dendroid 0.2.02 → 0.2.03
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +11 -0
- data/lib/dendroid/formatters/bracket_notation.rb +1 -1
- data/lib/dendroid/grm_analysis/dotted_item.rb +4 -4
- data/lib/dendroid/lexical/literal.rb +5 -0
- data/lib/dendroid/lexical/token.rb +5 -0
- data/lib/dendroid/parsing/and_node.rb +25 -3
- data/lib/dendroid/parsing/chart_walker.rb +3 -3
- data/lib/dendroid/parsing/composite_parse_node.rb +5 -0
- data/lib/dendroid/parsing/empty_rule_node.rb +11 -1
- data/lib/dendroid/parsing/or_node.rb +22 -2
- data/lib/dendroid/parsing/parse_node.rb +17 -4
- data/lib/dendroid/parsing/parse_tree_visitor.rb +1 -1
- data/lib/dendroid/parsing/terminal_node.rb +5 -2
- data/lib/dendroid/parsing/walk_progress.rb +46 -5
- data/lib/dendroid/recognizer/e_item.rb +3 -1
- data/lib/dendroid/syntax/rule.rb +1 -1
- data/spec/dendroid/lexical/literal_spec.rb +5 -1
- data/spec/dendroid/lexical/token_spec.rb +4 -0
- data/spec/dendroid/parsing/chart_walker_spec.rb +76 -76
- data/spec/dendroid/parsing/composite_parse_node_spec.rb +37 -0
- data/spec/dendroid/parsing/empty_rule_node_spec.rb +50 -0
- data/spec/dendroid/parsing/parse_node_spec.rb +25 -0
- data/spec/dendroid/parsing/terminal_node_spec.rb +4 -4
- data/version.txt +1 -1
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ba78964528c386b4be6024fdd2f1f76edc87478e83013ac40933d183e0d04d02
|
4
|
+
data.tar.gz: 1a5840b2d92a8b8e525bdf1313122ae3f3160fbb9676700c8e4d3edede71928b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 241163fd9a9f7ab036a1d750ef30844f538b40e2fea401af86929798a98f515ec9a82d136f2db7e61c3253ce7c88e9166db55ef24373a47acc0a6dd043e84c61
|
7
|
+
data.tar.gz: ee0faca7992fa087ca100b0de21b7e85464828a832462cded6593688d20b6b834a11d87284f7a3fcce137dbc670db98499e5601587119e32d25f5ab2f61a4134
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,17 @@
|
|
2
2
|
|
3
3
|
## [Unreleased]
|
4
4
|
|
5
|
+
## [0.2.03] - 2023-12-21
|
6
|
+
|
7
|
+
### Added
|
8
|
+
- YARD documentation for the `ParseNode` hierarchy.
|
9
|
+
- Classes `Token` and `Literal` added the predicate method `literal?`
|
10
|
+
|
11
|
+
### Changed
|
12
|
+
- Class rename: `ANDNode` became `AndNode`
|
13
|
+
- Class `ParseNode`: method rename: `ParseNode#to_s` became `ParseNode#range_to_s`
|
14
|
+
- Class `ParseNode`: attribute `range` is now of type `Range`
|
15
|
+
|
5
16
|
## [0.2.02] - 2023-12-18
|
6
17
|
Code re-styling to fix most Rubocop offenses.
|
7
18
|
|
@@ -6,7 +6,7 @@ class BracketNotation < BaseFormatter
|
|
6
6
|
# Method called by a ParseTreeVisitor to which the formatter subscribed.
|
7
7
|
# Notification of a visit event: the visitor is about to visit
|
8
8
|
# a non-terminal node
|
9
|
-
# @param and_node [
|
9
|
+
# @param and_node [AndNode]
|
10
10
|
def before_and_node(and_node)
|
11
11
|
write("[#{and_node.rule.lhs.name} ")
|
12
12
|
end
|
@@ -21,7 +21,7 @@ module Dendroid
|
|
21
21
|
# An item with the dot not at the beginning is sometimes referred to as a kernel item
|
22
22
|
class DottedItem
|
23
23
|
# (Weak) reference to the production rule
|
24
|
-
# @return [Dendroid::Syntax::
|
24
|
+
# @return [Dendroid::Syntax::Rule]
|
25
25
|
attr_reader :rule
|
26
26
|
|
27
27
|
# @return [Integer] the dot position
|
@@ -31,12 +31,12 @@ module Dendroid
|
|
31
31
|
attr_reader :alt_index
|
32
32
|
|
33
33
|
# Constructor.
|
34
|
-
# @param
|
34
|
+
# @param aRule[Dendroid::Syntax::Rule]
|
35
35
|
# @param aPosition [Integer] Position of the dot in rhs of production.
|
36
36
|
# @param index [Integer] the rank of the alternative at hand
|
37
|
-
def initialize(
|
37
|
+
def initialize(aRule, aPosition, index)
|
38
38
|
@alt_index = index
|
39
|
-
@rule = WeakRef.new(
|
39
|
+
@rule = WeakRef.new(aRule)
|
40
40
|
@position = valid_position(aPosition)
|
41
41
|
end
|
42
42
|
|
@@ -4,10 +4,18 @@ require_relative 'composite_parse_node'
|
|
4
4
|
|
5
5
|
module Dendroid
|
6
6
|
module Parsing
|
7
|
-
|
7
|
+
# A composite parse node that matches the sequence of grammar symbols from
|
8
|
+
# a right-hand side of a rule to a range of input tokens. The child nodes
|
9
|
+
# correspond to the grammar symbols in the RHS of the rule.
|
10
|
+
class AndNode < CompositeParseNode
|
11
|
+
# @return [WeakRef<Dendroid::Syntax::Rule>] Grammar rule
|
8
12
|
attr_reader :rule
|
13
|
+
|
14
|
+
# @return [Integer] Index of the rule alternative.
|
9
15
|
attr_reader :alt_index
|
10
16
|
|
17
|
+
# @param anEItem [Dendroid::Recognizer::EItem] An entry from the chart.
|
18
|
+
# @param rank [Integer] rank of the last input token matched by this node
|
11
19
|
def initialize(anEItem, rank)
|
12
20
|
@rule = WeakRef.new(anEItem.dotted_item.rule)
|
13
21
|
@alt_index = anEItem.dotted_item.alt_index
|
@@ -15,14 +23,22 @@ module Dendroid
|
|
15
23
|
super(anEItem.origin, upper_bound, rule.rhs[alt_index].size)
|
16
24
|
end
|
17
25
|
|
26
|
+
# Add a child a given available position.
|
27
|
+
# @param child_node [Dendroid::Parsing::ParseNode] Node to add as a child
|
28
|
+
# @param index [Integer] position of the child node in the `children` array
|
18
29
|
def add_child(child_node, index)
|
19
30
|
raise StandardError unless children[index].nil? # Is slot available?
|
20
31
|
|
21
32
|
super(child_node, index)
|
22
33
|
end
|
23
34
|
|
35
|
+
# Is the given chart entry matching this node?
|
36
|
+
# The chart entry matches this node if:
|
37
|
+
# - its origin equals to the start of the range; and,
|
38
|
+
# - both rules are the same; and,
|
39
|
+
# @return [Boolean] true if the entry corresponds to this node.
|
24
40
|
def match(anEItem)
|
25
|
-
return false if range
|
41
|
+
return false if range.begin != anEItem.origin
|
26
42
|
|
27
43
|
dotted = anEItem.dotted_item
|
28
44
|
same_rule = (rule.lhs == dotted.rule.lhs) && (alt_index == dotted.alt_index)
|
@@ -31,17 +47,23 @@ module Dendroid
|
|
31
47
|
dotted.initial_pos? ? true : partial?
|
32
48
|
end
|
33
49
|
|
50
|
+
# Is this node expecting at given RHS index, the given symbol?
|
51
|
+
# @param symbol [Dendroid::Syntax::GrmSymbol]
|
52
|
+
# @param position [Integer] index of given member in RHS of the rule
|
34
53
|
def expecting?(symbol, position)
|
35
54
|
symb_seq = rule.rhs[alt_index]
|
36
55
|
symb_seq[position] == symbol
|
37
56
|
end
|
38
57
|
|
58
|
+
# @return [Boolean] true if at least one of the children slots is free.
|
39
59
|
def partial?
|
40
60
|
children.any?(&:nil?)
|
41
61
|
end
|
42
62
|
|
63
|
+
# Return a String representation of itself
|
64
|
+
# @return [String] text representation of itself
|
43
65
|
def to_s
|
44
|
-
"#{rule.lhs} => #{rule.rhs[alt_index]} #{
|
66
|
+
"#{rule.lhs} => #{rule.rhs[alt_index]} #{range_to_s}"
|
45
67
|
end
|
46
68
|
|
47
69
|
# Part of the 'visitee' role in Visitor design pattern.
|
@@ -27,11 +27,11 @@ module Dendroid
|
|
27
27
|
# Create n times start_item as predecessors, then for each path initialize to its unique own predecessor
|
28
28
|
forerunners = disambiguate(progress, start_item.predecessors)
|
29
29
|
if forerunners.size == 1
|
30
|
-
parents <<
|
30
|
+
parents << AndNode.new(start_item, curr_rank)
|
31
31
|
else
|
32
32
|
preds = sort_predecessors(forerunners)
|
33
33
|
if start_item.rule.rhs.size == 1
|
34
|
-
parents <<
|
34
|
+
parents << AndNode.new(start_item, curr_rank)
|
35
35
|
progress.push_or_node(start_item.origin, preds.size)
|
36
36
|
else
|
37
37
|
parents << OrNode.new(start_item.lhs, start_item.origin, curr_rank, preds.size)
|
@@ -40,7 +40,7 @@ module Dendroid
|
|
40
40
|
fork(progress, paths, preds)
|
41
41
|
end
|
42
42
|
else
|
43
|
-
parents <<
|
43
|
+
parents << AndNode.new(start_item, curr_rank)
|
44
44
|
end
|
45
45
|
token2node = {}
|
46
46
|
entry2node = {}
|
@@ -4,9 +4,14 @@ require_relative 'parse_node'
|
|
4
4
|
|
5
5
|
module Dendroid
|
6
6
|
module Parsing
|
7
|
+
# Composite Pattern. A specialization of parse nodes that have themselves children nodes.
|
7
8
|
class CompositeParseNode < ParseNode
|
9
|
+
# @return [Array<Dendroid::Parsing::ParseNode|NilClass>] Sub-nodes. Nil values represent available slots
|
8
10
|
attr_reader :children
|
9
11
|
|
12
|
+
# @param lowerBound [Integer] Rank of first input token that is matched by this node
|
13
|
+
# @param upperBound [Integer] Rank of last input token that is matched by this node
|
14
|
+
# @param child_count [Integer] The expected number of child nodes
|
10
15
|
def initialize(lowerBound, upperBound, child_count)
|
11
16
|
super(lowerBound, upperBound)
|
12
17
|
@children = Array.new(child_count, nil)
|
@@ -1,21 +1,31 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'weakref'
|
3
4
|
require_relative 'parse_node'
|
4
5
|
|
5
6
|
module Dendroid
|
6
7
|
module Parsing
|
8
|
+
# A parse tree/forest node that is related to a production rule with an empty
|
9
|
+
# RHS (right-hand side).
|
7
10
|
class EmptyRuleNode < ParseNode
|
11
|
+
# @return [WeakRef<Dendroid::Syntax::Rule>] Grammar rule
|
8
12
|
attr_reader :rule
|
13
|
+
|
14
|
+
# @return [Integer] Index of the rule alternative.
|
9
15
|
attr_reader :alt_index
|
10
16
|
|
17
|
+
# @param anEItem [Dendroid::Recognizer::EItem] An entry from the chart.
|
18
|
+
# @param rank [Integer] rank of the last input token matched by this node
|
11
19
|
def initialize(anEItem, rank)
|
12
20
|
super(rank, rank)
|
13
21
|
@rule = WeakRef.new(anEItem.dotted_item.rule)
|
14
22
|
@alt_index = anEItem.dotted_item.alt_index
|
15
23
|
end
|
16
24
|
|
25
|
+
# Return a String representation of itself
|
26
|
+
# @return [String] text representation of itself
|
17
27
|
def to_s
|
18
|
-
"_ #{
|
28
|
+
"_ #{range_to_s}"
|
19
29
|
end
|
20
30
|
|
21
31
|
# Part of the 'visitee' role in Visitor design pattern.
|
@@ -4,14 +4,25 @@ require_relative 'composite_parse_node'
|
|
4
4
|
|
5
5
|
module Dendroid
|
6
6
|
module Parsing
|
7
|
+
# A composite parse node that embodies multiple syntactical derivations of a right-hand side of a rule
|
8
|
+
# to a range of input tokens. Each child node corresponds to a distinct derivation.
|
7
9
|
class OrNode < CompositeParseNode
|
10
|
+
# @return [Dendroid::Syntax::NonTerminal] The non-terminal symbol at LHS of rule
|
8
11
|
attr_reader :symbol
|
9
12
|
|
13
|
+
# @param sym [Dendroid::Syntax::NonTerminal]
|
14
|
+
# @param lower [Integer] lowest token rank matching start of the rule
|
15
|
+
# @param upper [Integer] largest token rank matching start of the rule
|
16
|
+
# @param arity [Integer] Number of derivations of the given rule
|
10
17
|
def initialize(sym, lower, upper, arity)
|
11
18
|
@symbol = sym
|
12
19
|
super(lower, upper, arity)
|
13
20
|
end
|
14
21
|
|
22
|
+
# Add a child node as root of one derivation.
|
23
|
+
# Place it in an available child slot.
|
24
|
+
# @param child_node [Dendroid::Parsing::ParseNode]
|
25
|
+
# @param _index [Integer] Unused
|
15
26
|
def add_child(child_node, _index)
|
16
27
|
idx = children.find_index(&:nil?)
|
17
28
|
raise StandardError unless idx
|
@@ -20,20 +31,29 @@ module Dendroid
|
|
20
31
|
super(child_node, idx)
|
21
32
|
end
|
22
33
|
|
34
|
+
# Is the given chart entry matching this node?
|
35
|
+
# The chart entry matches this node if:
|
36
|
+
# - its origin equals to the start of the range; and,
|
37
|
+
# - both rules are the same; and,
|
38
|
+
# - each child matches this chart entry
|
39
|
+
# @return [Boolean] true if the entry corresponds to this node.
|
23
40
|
def match(anEItem)
|
24
|
-
return false if range
|
41
|
+
return false if range.begin != anEItem.origin
|
25
42
|
|
26
43
|
dotted = anEItem.dotted_item
|
27
44
|
(symbol == dotted.rule.lhs) && children.any? { |ch| ch.match(anEItem) }
|
28
45
|
end
|
29
46
|
|
47
|
+
# @return [FalseClass]
|
30
48
|
def partial?
|
31
49
|
# children.any?(&:nil?)
|
32
50
|
false
|
33
51
|
end
|
34
52
|
|
53
|
+
# Return a String representation of itself
|
54
|
+
# @return [String] text representation of itself
|
35
55
|
def to_s
|
36
|
-
"OR: #{symbol.name} #{
|
56
|
+
"OR: #{symbol.name} #{range_to_s}"
|
37
57
|
end
|
38
58
|
|
39
59
|
# Part of the 'visitee' role in Visitor design pattern.
|
@@ -1,17 +1,30 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Dendroid
|
4
|
+
# Namespace for all classes needed for implementing a generic parser.
|
5
|
+
# The goal is to take the output from the Earley recognizer (i.e. a chart object),
|
6
|
+
# visit it and build a data structure (a parse tree or a shared parse forest) that is much
|
7
|
+
# more convenient for subsequent processing (e.g. semantic analysis of a compiler/interpreter).
|
4
8
|
module Parsing
|
9
|
+
# An abstract class (i.e. a generalization) for elements forming a parse tree (forest).
|
10
|
+
# A parse tree is a graph data structure that represents the parsed input text into a tree-like hierarchy
|
11
|
+
# of elements constructed by applying syntax rules of the language at hand.
|
12
|
+
# A parse forest is a data structure that merges a number parse trees into one graph.
|
13
|
+
# Contrary to parse trees, a parse forests can represent the results of an ambiguous parsing.
|
5
14
|
class ParseNode
|
6
|
-
# @return [
|
15
|
+
# @return [Range] The range of indexes of the input tokens that match this node.
|
7
16
|
attr_reader :range
|
8
17
|
|
18
|
+
# @param lowerBound [Integer] Rank of first input token that is matched by this node
|
19
|
+
# @param upperBound [Integer] Rank of last input token that is matched by this node
|
9
20
|
def initialize(lowerBound, upperBound)
|
10
21
|
@range = valid_range(lowerBound, upperBound)
|
11
22
|
end
|
12
23
|
|
13
|
-
|
14
|
-
|
24
|
+
protected
|
25
|
+
|
26
|
+
def range_to_s
|
27
|
+
"[#{range}]"
|
15
28
|
end
|
16
29
|
|
17
30
|
private
|
@@ -19,7 +32,7 @@ module Dendroid
|
|
19
32
|
def valid_range(lowerBound, upperBound)
|
20
33
|
raise StandardError unless lowerBound.is_a?(Integer) && upperBound.is_a?(Integer)
|
21
34
|
|
22
|
-
|
35
|
+
lowerBound..upperBound
|
23
36
|
end
|
24
37
|
end # class
|
25
38
|
end # module
|
@@ -45,7 +45,7 @@ class ParseTreeVisitor
|
|
45
45
|
end
|
46
46
|
|
47
47
|
# Visit event. The visitor is about to visit the given non terminal node.
|
48
|
-
# @param aNonTerminalNode [
|
48
|
+
# @param aNonTerminalNode [AndNode] the node to visit.
|
49
49
|
def visit_and_node(aNonTerminalNode)
|
50
50
|
if @traversal == :post_order
|
51
51
|
broadcast(:before_and_node, aNonTerminalNode)
|
@@ -4,6 +4,7 @@ require_relative 'parse_node'
|
|
4
4
|
|
5
5
|
module Dendroid
|
6
6
|
module Parsing
|
7
|
+
# A parse tree/forest node that is related to one input token.
|
7
8
|
class TerminalNode < ParseNode
|
8
9
|
# @return [Dendroid::Syntax::Terminal] Terminal symbol of matching token.
|
9
10
|
attr_reader :symbol
|
@@ -23,9 +24,11 @@ module Dendroid
|
|
23
24
|
aVisitor.visit_terminal(self)
|
24
25
|
end
|
25
26
|
|
27
|
+
# Render a String representation of itself
|
28
|
+
# @return [String]
|
26
29
|
def to_s
|
27
|
-
display_val = token.
|
28
|
-
"#{symbol.name}#{display_val} #{
|
30
|
+
display_val = token.literal? ? ": #{token.value}" : ''
|
31
|
+
"#{symbol.name}#{display_val} #{range_to_s}"
|
29
32
|
end
|
30
33
|
end # class
|
31
34
|
end # module
|
@@ -1,22 +1,39 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative 'and_node'
|
3
|
+
require_relative 'and_node.rb'
|
4
4
|
require_relative 'or_node'
|
5
5
|
require_relative 'terminal_node'
|
6
6
|
require_relative 'empty_rule_node'
|
7
7
|
|
8
8
|
module Dendroid
|
9
9
|
module Parsing
|
10
|
+
# This object holds the current state of the visit of a Chart by one
|
11
|
+
# ChartWalker through one single visit path. A path corresponds to a
|
12
|
+
# chain from the current item back to the initial item(s) through the predecessors links.
|
13
|
+
# It is used to construct (part of) the parse tree from the root node.
|
10
14
|
class WalkProgress
|
15
|
+
# @return [Symbol] One of: :New, :Forking,
|
11
16
|
attr_accessor :state
|
17
|
+
|
18
|
+
# @return [Integer] rank of the item set from the chart being visited
|
12
19
|
attr_accessor :curr_rank
|
20
|
+
|
21
|
+
# @return [Dendroid::Recognizer::EItem] the chart entry being visited
|
13
22
|
attr_reader :curr_item
|
23
|
+
|
24
|
+
# When not nil, override the predecessors links of the current item
|
25
|
+
# @return [Dendroid::Recognizer::EItem|NilClass]
|
14
26
|
attr_accessor :predecessor
|
27
|
+
|
28
|
+
# @return [Array<Dendroid::Parsing::CompositeParseNode>] The ancestry of current parse node.
|
15
29
|
attr_reader :parents
|
16
30
|
|
17
31
|
# rubocop: disable Metrics/CyclomaticComplexity
|
18
32
|
# rubocop: disable Metrics/PerceivedComplexity
|
19
33
|
|
34
|
+
# @param start_rank [Integer] Initial rank at the start of the visit
|
35
|
+
# @param start_item [Dendroid::Recognizer::EItem] Initial chart entry to visit
|
36
|
+
# @param parents [Array<Dendroid::Parsing::CompositeParseNode>]
|
20
37
|
def initialize(start_rank, start_item, parents)
|
21
38
|
@state = :New
|
22
39
|
@curr_rank = start_rank
|
@@ -34,32 +51,46 @@ module Dendroid
|
|
34
51
|
@parents = orig.parents.dup
|
35
52
|
end
|
36
53
|
|
54
|
+
# Current item has multiple predecessors: set the state to Forking and
|
55
|
+
# force one of the predecessor to be the next entry to visit.
|
56
|
+
# @param thePredecessor [Dendroid::Recognizer::EItem]
|
37
57
|
def fork(thePredecessor)
|
38
58
|
@state = :Forking
|
39
59
|
@predecessor = thePredecessor
|
40
60
|
end
|
41
61
|
|
62
|
+
# Set the current entry being visited to the given one
|
63
|
+
# @param anEntry [Dendroid::Recognizer::EItem]
|
42
64
|
def curr_item=(anEntry)
|
43
65
|
raise StandardError if anEntry.nil?
|
44
66
|
|
45
67
|
@curr_item = anEntry
|
46
68
|
end
|
47
69
|
|
70
|
+
# Add a child leaf node for the given chart entry that corresponds
|
71
|
+
# to an empty rule.
|
72
|
+
# @param anEntry [Dendroid::Recognizer::EItem]
|
73
|
+
# @return [Dendroid::Parsing::EmptyRuleNode]
|
48
74
|
def add_node_empty(anEntry)
|
49
75
|
node_empty = EmptyRuleNode.new(anEntry, curr_rank)
|
50
76
|
add_child_node(node_empty)
|
51
77
|
end
|
52
78
|
|
53
|
-
# Add a terminal node for
|
79
|
+
# Add a leaf terminal node for the token at current rank as a child of last parent.
|
80
|
+
# @param token [Dendroid::Lexical::Token]
|
81
|
+
# @return [Dendroid::Parsing::TerminalNode]
|
54
82
|
def add_terminal_node(token)
|
55
83
|
@curr_rank -= 1
|
56
84
|
term_node = TerminalNode.new(curr_item.prev_symbol, token, curr_rank)
|
57
85
|
add_child_node(term_node)
|
58
86
|
end
|
59
87
|
|
60
|
-
#
|
88
|
+
# Make an AND node for the given entry as a child of last parent and
|
89
|
+
# push this node in the ancestry
|
90
|
+
# @param anEntry [Dendroid::Recognizer::EItem]
|
91
|
+
# @return [Dendroid::Parsing::AndNode]
|
61
92
|
def push_and_node(anEntry)
|
62
|
-
node =
|
93
|
+
node = AndNode.new(anEntry, curr_rank)
|
63
94
|
raise StandardError unless anEntry.rule == node.rule # Fails
|
64
95
|
|
65
96
|
add_child_node(node)
|
@@ -68,6 +99,11 @@ module Dendroid
|
|
68
99
|
node
|
69
100
|
end
|
70
101
|
|
102
|
+
# Make an OR node as a child of last parent and
|
103
|
+
# push this node in the ancestry.
|
104
|
+
# @param origin [Integer] Start rank
|
105
|
+
# #param arity [Integer] The number of alternative derivations
|
106
|
+
# @return [Dendroid::Parsing::OrNode]
|
71
107
|
def push_or_node(origin, arity)
|
72
108
|
node = OrNode.new(curr_item.prev_symbol, origin, curr_rank, arity)
|
73
109
|
add_child_node(node)
|
@@ -76,6 +112,9 @@ module Dendroid
|
|
76
112
|
node
|
77
113
|
end
|
78
114
|
|
115
|
+
# Add the given node as a child of the last parent node.
|
116
|
+
# @param aNode [Dendroid::Parsing::ParseNode]
|
117
|
+
# @return [Dendroid::Parsing::ParseNode]
|
79
118
|
def add_child_node(aNode)
|
80
119
|
parents.last.add_child(aNode, curr_item.position - 1)
|
81
120
|
aNode
|
@@ -83,6 +122,8 @@ module Dendroid
|
|
83
122
|
|
84
123
|
# Do the given EItems match one of the parent?
|
85
124
|
# Matching = corresponds to the same rule and range
|
125
|
+
# @param entries [Dendroid::Recognizer::EItem]
|
126
|
+
# @param stop_at_first [Boolean] Must be true
|
86
127
|
# @return [Array<EItem>]
|
87
128
|
def match_parent?(entries, stop_at_first)
|
88
129
|
matching = []
|
@@ -106,7 +147,7 @@ module Dendroid
|
|
106
147
|
break if stop_at_first && !matching.empty?
|
107
148
|
|
108
149
|
# Stop loop when parent.origin < min(entries.origin)
|
109
|
-
break if node.range
|
150
|
+
break if node.range.begin < min_origin
|
110
151
|
|
111
152
|
offset += 1
|
112
153
|
end
|
@@ -8,6 +8,7 @@ module Dendroid
|
|
8
8
|
# An Earley item is essentially a pair consisting of a dotted item and the rank of a token.
|
9
9
|
# It helps to keep track the progress of an Earley recognizer.
|
10
10
|
class EItem
|
11
|
+
# Mix-in module used to forward some method calls to the related dotted item.
|
11
12
|
extend Forwardable
|
12
13
|
|
13
14
|
# (Weak) reference to the dotted item
|
@@ -17,7 +18,8 @@ module Dendroid
|
|
17
18
|
# @return [Integer] the rank of the token that correspond to the start of the rule.
|
18
19
|
attr_reader :origin
|
19
20
|
|
20
|
-
#
|
21
|
+
# Specifies the algorithm with which this entry can be derived from its predecessor(s).
|
22
|
+
# @return [Symbol] of one: :predictor, :completer, :scanner
|
21
23
|
attr_accessor :algo
|
22
24
|
|
23
25
|
# @return [Array<WeakRef>] predecessors sorted by decreasing origin value
|
data/lib/dendroid/syntax/rule.rb
CHANGED
@@ -12,7 +12,7 @@ module Dendroid
|
|
12
12
|
# @return [Array<Dendroid::Syntax::SymbolSeq>]
|
13
13
|
attr_reader :alternatives
|
14
14
|
|
15
|
-
# Create a
|
15
|
+
# Create a Rule instance.
|
16
16
|
# @param theLhs [Dendroid::Syntax::NonTerminal] The left-hand side of the rule.
|
17
17
|
# @param alt [Array<Dendroid::Syntax::SymbolSeq>] the alternatives (each as a sequence of symbols).
|
18
18
|
def initialize(theLhs, alt)
|
@@ -16,7 +16,11 @@ describe Dendroid::Lexical::Literal do
|
|
16
16
|
expect { described_class.new(ex_source, ex_pos, ex_terminal, ex_value) }.not_to raise_error
|
17
17
|
end
|
18
18
|
|
19
|
-
it 'knows
|
19
|
+
it 'knows it is a token with a literal value attached to it' do
|
20
|
+
expect(subject).to be_literal
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'knows its literal value' do
|
20
24
|
expect(subject.value).to eq(ex_value)
|
21
25
|
end
|
22
26
|
end # context
|
@@ -45,37 +45,37 @@ RSpec.describe Dendroid::Parsing::ChartWalker do
|
|
45
45
|
walker = described_class.new(chart)
|
46
46
|
root = walker.walk(success_entry(chart, recognizer))
|
47
47
|
|
48
|
-
expect(root.to_s).to eq('p => s [0
|
48
|
+
expect(root.to_s).to eq('p => s [0..5]')
|
49
49
|
expect(root.children.size).to eq(1)
|
50
|
-
expect(root.children[-1].to_s).to eq('s => s PLUS m [0
|
50
|
+
expect(root.children[-1].to_s).to eq('s => s PLUS m [0..5]')
|
51
51
|
plus_expr = root.children[-1]
|
52
52
|
expect(plus_expr.children.size).to eq(3)
|
53
|
-
expect(plus_expr.children[0].to_s).to eq('s => m [0
|
54
|
-
expect(plus_expr.children[1].to_s).to eq('PLUS [1
|
55
|
-
expect(plus_expr.children[2].to_s).to eq('m => m STAR t [2
|
53
|
+
expect(plus_expr.children[0].to_s).to eq('s => m [0..1]')
|
54
|
+
expect(plus_expr.children[1].to_s).to eq('PLUS [1..2]')
|
55
|
+
expect(plus_expr.children[2].to_s).to eq('m => m STAR t [2..5]')
|
56
56
|
|
57
57
|
operand_plus = plus_expr.children[0]
|
58
58
|
expect(operand_plus.children.size).to eq(1)
|
59
|
-
expect(operand_plus.children[0].to_s).to eq('m => t [0
|
59
|
+
expect(operand_plus.children[0].to_s).to eq('m => t [0..1]')
|
60
60
|
expect(operand_plus.children[0].children.size).to eq(1)
|
61
|
-
expect(operand_plus.children[0].children[0].to_s).to eq('t => INTEGER [0
|
62
|
-
expect(operand_plus.children[0].children[0].children[0].to_s).to eq('INTEGER: 2 [0
|
61
|
+
expect(operand_plus.children[0].children[0].to_s).to eq('t => INTEGER [0..1]')
|
62
|
+
expect(operand_plus.children[0].children[0].children[0].to_s).to eq('INTEGER: 2 [0..1]')
|
63
63
|
|
64
|
-
expect(plus_expr.children[1].to_s).to eq('PLUS [1
|
64
|
+
expect(plus_expr.children[1].to_s).to eq('PLUS [1..2]')
|
65
65
|
|
66
66
|
star_expr = plus_expr.children[2]
|
67
67
|
expect(star_expr.children.size).to eq(3)
|
68
|
-
expect(star_expr.children[0].to_s).to eq('m => t [2
|
69
|
-
expect(star_expr.children[1].to_s).to eq('STAR [3
|
70
|
-
expect(star_expr.children[2].to_s).to eq('t => INTEGER [4
|
68
|
+
expect(star_expr.children[0].to_s).to eq('m => t [2..3]')
|
69
|
+
expect(star_expr.children[1].to_s).to eq('STAR [3..4]')
|
70
|
+
expect(star_expr.children[2].to_s).to eq('t => INTEGER [4..5]')
|
71
71
|
|
72
72
|
operand_star = star_expr.children[0]
|
73
73
|
expect(operand_star.children.size).to eq(1)
|
74
|
-
expect(operand_star.children[0].to_s).to eq('t => INTEGER [2
|
75
|
-
expect(operand_star.children[0].children[0].to_s).to eq('INTEGER: 3 [2
|
74
|
+
expect(operand_star.children[0].to_s).to eq('t => INTEGER [2..3]')
|
75
|
+
expect(operand_star.children[0].children[0].to_s).to eq('INTEGER: 3 [2..3]')
|
76
76
|
|
77
77
|
expect(star_expr.children[2].children.size).to eq(1)
|
78
|
-
expect(star_expr.children[2].children[0].to_s).to eq('INTEGER: 4 [4
|
78
|
+
expect(star_expr.children[2].children[0].to_s).to eq('INTEGER: 4 [4..5]')
|
79
79
|
end
|
80
80
|
|
81
81
|
it 'generates a parse tree for grammar l10 (with left recursive rule)' do
|
@@ -84,27 +84,27 @@ RSpec.describe Dendroid::Parsing::ChartWalker do
|
|
84
84
|
walker = described_class.new(chart)
|
85
85
|
root = walker.walk(success_entry(chart, recognizer))
|
86
86
|
|
87
|
-
expect(root.to_s).to eq('A => A a [0
|
87
|
+
expect(root.to_s).to eq('A => A a [0..5]')
|
88
88
|
expect(root.children.size).to eq(2)
|
89
|
-
expect(root.children[0].to_s).to eq('A => A a [0
|
90
|
-
expect(root.children[1].to_s).to eq('a [4
|
89
|
+
expect(root.children[0].to_s).to eq('A => A a [0..4]')
|
90
|
+
expect(root.children[1].to_s).to eq('a [4..5]')
|
91
91
|
|
92
92
|
expect(root.children[0].children.size).to eq(2)
|
93
|
-
expect(root.children[0].children[0].to_s).to eq('A => A a [0
|
94
|
-
expect(root.children[0].children[1].to_s).to eq('a [3
|
93
|
+
expect(root.children[0].children[0].to_s).to eq('A => A a [0..3]')
|
94
|
+
expect(root.children[0].children[1].to_s).to eq('a [3..4]')
|
95
95
|
|
96
96
|
grand_child = root.children[0].children[0]
|
97
97
|
expect(grand_child.children.size).to eq(2)
|
98
|
-
expect(grand_child.children[0].to_s).to eq('A => A a [0
|
99
|
-
expect(grand_child.children[1].to_s).to eq('a [2
|
98
|
+
expect(grand_child.children[0].to_s).to eq('A => A a [0..2]')
|
99
|
+
expect(grand_child.children[1].to_s).to eq('a [2..3]')
|
100
100
|
|
101
101
|
expect(grand_child.children[0].children.size).to eq(2)
|
102
|
-
expect(grand_child.children[0].children[0].to_s).to eq('A => A a [0
|
103
|
-
expect(grand_child.children[0].children[1].to_s).to eq('a [1
|
102
|
+
expect(grand_child.children[0].children[0].to_s).to eq('A => A a [0..1]')
|
103
|
+
expect(grand_child.children[0].children[1].to_s).to eq('a [1..2]')
|
104
104
|
|
105
105
|
expect(grand_child.children[0].children[0].children.size).to eq(2)
|
106
|
-
expect(grand_child.children[0].children[0].children[0].to_s).to eq('_ [0
|
107
|
-
expect(grand_child.children[0].children[0].children[1].to_s).to eq('a [0
|
106
|
+
expect(grand_child.children[0].children[0].children[0].to_s).to eq('_ [0..0]')
|
107
|
+
expect(grand_child.children[0].children[0].children[1].to_s).to eq('a [0..1]')
|
108
108
|
end
|
109
109
|
|
110
110
|
it 'generates a parse tree for grammar l11 (with right recursive rule)' do
|
@@ -113,27 +113,27 @@ RSpec.describe Dendroid::Parsing::ChartWalker do
|
|
113
113
|
walker = described_class.new(chart)
|
114
114
|
root = walker.walk(success_entry(chart, recognizer))
|
115
115
|
|
116
|
-
expect(root.to_s).to eq('A => a A [0
|
116
|
+
expect(root.to_s).to eq('A => a A [0..5]')
|
117
117
|
expect(root.children.size).to eq(2)
|
118
|
-
expect(root.children[0].to_s).to eq('a [0
|
119
|
-
expect(root.children[1].to_s).to eq('A => a A [1
|
118
|
+
expect(root.children[0].to_s).to eq('a [0..1]')
|
119
|
+
expect(root.children[1].to_s).to eq('A => a A [1..5]')
|
120
120
|
|
121
121
|
expect(root.children[1].children.size).to eq(2)
|
122
|
-
expect(root.children[1].children[0].to_s).to eq('a [1
|
123
|
-
expect(root.children[1].children[1].to_s).to eq('A => a A [2
|
122
|
+
expect(root.children[1].children[0].to_s).to eq('a [1..2]')
|
123
|
+
expect(root.children[1].children[1].to_s).to eq('A => a A [2..5]')
|
124
124
|
|
125
125
|
grand_child = root.children[1].children[1]
|
126
126
|
expect(grand_child.children.size).to eq(2)
|
127
|
-
expect(grand_child.children[0].to_s).to eq('a [2
|
128
|
-
expect(grand_child.children[1].to_s).to eq('A => a A [3
|
127
|
+
expect(grand_child.children[0].to_s).to eq('a [2..3]')
|
128
|
+
expect(grand_child.children[1].to_s).to eq('A => a A [3..5]')
|
129
129
|
|
130
130
|
expect(grand_child.children[1].children.size).to eq(2)
|
131
|
-
expect(grand_child.children[1].children[0].to_s).to eq('a [3
|
132
|
-
expect(grand_child.children[1].children[1].to_s).to eq('A => a A [4
|
131
|
+
expect(grand_child.children[1].children[0].to_s).to eq('a [3..4]')
|
132
|
+
expect(grand_child.children[1].children[1].to_s).to eq('A => a A [4..5]')
|
133
133
|
|
134
134
|
expect(grand_child.children[1].children[1].children.size).to eq(2)
|
135
|
-
expect(grand_child.children[1].children[1].children[0].to_s).to eq('a [4
|
136
|
-
expect(grand_child.children[1].children[1].children[1].to_s).to eq('_ [5
|
135
|
+
expect(grand_child.children[1].children[1].children[0].to_s).to eq('a [4..5]')
|
136
|
+
expect(grand_child.children[1].children[1].children[1].to_s).to eq('_ [5..5]')
|
137
137
|
end
|
138
138
|
end # context
|
139
139
|
|
@@ -144,78 +144,78 @@ RSpec.describe Dendroid::Parsing::ChartWalker do
|
|
144
144
|
walker = described_class.new(chart)
|
145
145
|
root = walker.walk(success_entry(chart, recognizer))
|
146
146
|
|
147
|
-
expect(root.to_s).to eq('OR: S [0
|
147
|
+
expect(root.to_s).to eq('OR: S [0..4]')
|
148
148
|
expect(root.children.size).to eq(3)
|
149
149
|
root.children.each do |child|
|
150
150
|
expect(child.children.size).to eq(2)
|
151
|
-
expect(child.to_s).to eq('S => S S [0
|
151
|
+
expect(child.to_s).to eq('S => S S [0..4]')
|
152
152
|
end
|
153
153
|
(a, b, c) = root.children
|
154
154
|
|
155
155
|
# Test structure of tree a
|
156
156
|
(child_a_0, child_a_1) = a.children
|
157
|
-
expect(child_a_0.to_s).to eq('S => S S [0
|
158
|
-
expect(child_a_1.to_s).to eq('S => S S [2
|
157
|
+
expect(child_a_0.to_s).to eq('S => S S [0..2]')
|
158
|
+
expect(child_a_1.to_s).to eq('S => S S [2..4]')
|
159
159
|
expect(child_a_0.children.size).to eq(2)
|
160
160
|
(child_a_0_0, child_a_0_1) = child_a_0.children
|
161
|
-
expect(child_a_0_0.to_s).to eq('S => x [0
|
162
|
-
expect(child_a_0_1.to_s).to eq('S => x [1
|
163
|
-
expect(child_a_0_0.children[0].to_s).to eq('x [0
|
164
|
-
expect(child_a_0_1.children[0].to_s).to eq('x [1
|
161
|
+
expect(child_a_0_0.to_s).to eq('S => x [0..1]')
|
162
|
+
expect(child_a_0_1.to_s).to eq('S => x [1..2]')
|
163
|
+
expect(child_a_0_0.children[0].to_s).to eq('x [0..1]')
|
164
|
+
expect(child_a_0_1.children[0].to_s).to eq('x [1..2]')
|
165
165
|
|
166
166
|
expect(child_a_1.children.size).to eq(2)
|
167
167
|
(child_a_1_0, child_a_1_1) = child_a_1.children
|
168
|
-
expect(child_a_1_0.to_s).to eq('S => x [2
|
169
|
-
expect(child_a_1_1.to_s).to eq('S => x [3
|
170
|
-
expect(child_a_1_0.children[0].to_s).to eq('x [2
|
171
|
-
expect(child_a_1_1.children[0].to_s).to eq('x [3
|
168
|
+
expect(child_a_1_0.to_s).to eq('S => x [2..3]')
|
169
|
+
expect(child_a_1_1.to_s).to eq('S => x [3..4]')
|
170
|
+
expect(child_a_1_0.children[0].to_s).to eq('x [2..3]')
|
171
|
+
expect(child_a_1_1.children[0].to_s).to eq('x [3..4]')
|
172
172
|
|
173
173
|
# Test structure of forest b
|
174
174
|
(child_b_0, child_b_1) = b.children
|
175
|
-
expect(child_b_0.to_s).to eq('OR: S [0
|
176
|
-
expect(child_b_1.to_s).to eq('S => x [3
|
175
|
+
expect(child_b_0.to_s).to eq('OR: S [0..3]')
|
176
|
+
expect(child_b_1.to_s).to eq('S => x [3..4]')
|
177
177
|
expect(child_b_1.equal?(child_a_1_1)).to be_truthy # Sharing
|
178
178
|
expect(child_b_0.children.size).to eq(2)
|
179
179
|
(child_b_0_0, child_b_0_1) = child_b_0.children
|
180
|
-
expect(child_b_0_0.to_s).to eq('S => S S [0
|
181
|
-
expect(child_b_0_1.to_s).to eq('S => S S [0
|
180
|
+
expect(child_b_0_0.to_s).to eq('S => S S [0..3]')
|
181
|
+
expect(child_b_0_1.to_s).to eq('S => S S [0..3]')
|
182
182
|
expect(child_b_0_0.children.size).to eq(2)
|
183
183
|
(child_b_0_0_0, child_b_0_0_1) = child_b_0_0.children
|
184
|
-
expect(child_b_0_0_0.to_s).to eq('S => x [0
|
184
|
+
expect(child_b_0_0_0.to_s).to eq('S => x [0..1]')
|
185
185
|
expect(child_b_0_0_0.equal?(child_a_0_0)).to be_truthy # Sharing
|
186
|
-
expect(child_b_0_0_1.to_s).to eq('S => S S [1
|
186
|
+
expect(child_b_0_0_1.to_s).to eq('S => S S [1..3]')
|
187
187
|
expect(child_b_0_0_1.children.size).to eq(2)
|
188
|
-
expect(child_b_0_0_1.children[0].to_s).to eq('S => x [1
|
188
|
+
expect(child_b_0_0_1.children[0].to_s).to eq('S => x [1..2]')
|
189
189
|
expect(child_b_0_0_1.children[0].equal?(child_a_0_1)).to be_truthy # Sharing
|
190
|
-
expect(child_b_0_0_1.children[1].to_s).to eq('S => x [2
|
190
|
+
expect(child_b_0_0_1.children[1].to_s).to eq('S => x [2..3]')
|
191
191
|
expect(child_b_0_0_1.children[1].equal?(child_a_1_0)).to be_truthy # Sharing
|
192
192
|
|
193
193
|
expect(child_b_0_1.children.size).to eq(2)
|
194
194
|
(child_b_0_1_0, child_b_0_1_1) = child_b_0_1.children
|
195
|
-
expect(child_b_0_1_0.to_s).to eq('S => S S [0
|
195
|
+
expect(child_b_0_1_0.to_s).to eq('S => S S [0..2]')
|
196
196
|
expect(child_b_0_1_0.equal?(child_a_0)).to be_truthy # Sharing
|
197
|
-
expect(child_b_0_1_1.to_s).to eq('S => x [2
|
197
|
+
expect(child_b_0_1_1.to_s).to eq('S => x [2..3]')
|
198
198
|
expect(child_b_0_1_1.equal?(child_a_1_0)).to be_truthy # Sharing
|
199
199
|
|
200
200
|
# Test structure of forest c
|
201
201
|
(child_c_0, child_c_1) = c.children
|
202
|
-
expect(child_c_0.to_s).to eq('S => x [0
|
202
|
+
expect(child_c_0.to_s).to eq('S => x [0..1]')
|
203
203
|
expect(child_c_0.equal?(child_a_0_0)).to be_truthy # Sharing
|
204
|
-
expect(child_c_1.to_s).to eq('OR: S [1
|
204
|
+
expect(child_c_1.to_s).to eq('OR: S [1..4]')
|
205
205
|
expect(child_c_1.children.size).to eq(2)
|
206
206
|
(child_c_1_0, child_c_1_1) = child_c_1.children
|
207
|
-
expect(child_c_1_0.to_s).to eq('S => S S [1
|
208
|
-
expect(child_c_1_1.to_s).to eq('S => S S [1
|
207
|
+
expect(child_c_1_0.to_s).to eq('S => S S [1..4]')
|
208
|
+
expect(child_c_1_1.to_s).to eq('S => S S [1..4]')
|
209
209
|
expect(child_c_1_0.children.size).to eq(2)
|
210
210
|
(child_c_1_0_0, child_c_1_0_1) = child_c_1_0.children
|
211
|
-
expect(child_c_1_0_0.to_s).to eq('S => x [1
|
211
|
+
expect(child_c_1_0_0.to_s).to eq('S => x [1..2]')
|
212
212
|
expect(child_c_1_0_0.equal?(child_a_0_1)).to be_truthy # Sharing
|
213
|
-
expect(child_c_1_0_1.to_s).to eq('S => S S [2
|
213
|
+
expect(child_c_1_0_1.to_s).to eq('S => S S [2..4]')
|
214
214
|
expect(child_c_1_0_1.equal?(child_a_1)).to be_truthy # Sharing
|
215
215
|
(child_c_1_1_0, child_c_1_1_1) = child_c_1_1.children
|
216
|
-
expect(child_c_1_1_0.to_s).to eq('S => S S [1
|
216
|
+
expect(child_c_1_1_0.to_s).to eq('S => S S [1..3]')
|
217
217
|
expect(child_c_1_1_0.equal?(child_b_0_0_1)).to be_truthy # Sharing
|
218
|
-
expect(child_c_1_1_1.to_s).to eq('S => x [3
|
218
|
+
expect(child_c_1_1_1.to_s).to eq('S => x [3..4]')
|
219
219
|
expect(child_c_1_1_1.equal?(child_b_1)).to be_truthy # Sharing
|
220
220
|
end
|
221
221
|
|
@@ -225,24 +225,24 @@ RSpec.describe Dendroid::Parsing::ChartWalker do
|
|
225
225
|
walker = described_class.new(chart)
|
226
226
|
root = walker.walk(success_entry(chart, recognizer))
|
227
227
|
|
228
|
-
expect(root.to_s).to eq('OR: S [0
|
228
|
+
expect(root.to_s).to eq('OR: S [0..2]')
|
229
229
|
expect(root.children.size).to eq(2)
|
230
230
|
root.children.each do |ch|
|
231
|
-
expect(ch.to_s).to eq('S => S T [0
|
231
|
+
expect(ch.to_s).to eq('S => S T [0..2]')
|
232
232
|
expect(ch.children.size).to eq(2)
|
233
233
|
end
|
234
234
|
(child_0_0, child_0_1) = root.children[0].children
|
235
|
-
expect(child_0_0.to_s).to eq('S => a [0
|
236
|
-
expect(child_0_1.to_s).to eq('T => a B [1
|
235
|
+
expect(child_0_0.to_s).to eq('S => a [0..1]')
|
236
|
+
expect(child_0_1.to_s).to eq('T => a B [1..2]')
|
237
237
|
expect(child_0_1.children.size).to eq(2)
|
238
|
-
expect(child_0_1.children[0].to_s).to eq('a [1
|
239
|
-
expect(child_0_1.children[1].to_s).to eq('_ [2
|
238
|
+
expect(child_0_1.children[0].to_s).to eq('a [1..2]')
|
239
|
+
expect(child_0_1.children[1].to_s).to eq('_ [2..2]')
|
240
240
|
|
241
241
|
(child_1_0, child_1_1) = root.children[1].children
|
242
|
-
expect(child_1_0.to_s).to eq('S => a [0
|
243
|
-
expect(child_1_1.to_s).to eq('T => a [1
|
242
|
+
expect(child_1_0.to_s).to eq('S => a [0..1]')
|
243
|
+
expect(child_1_1.to_s).to eq('T => a [1..2]')
|
244
244
|
expect(child_1_0.equal?(child_0_0)).to be_truthy # Sharing
|
245
|
-
expect(child_1_1.children[0].to_s).to eq('a [1
|
245
|
+
expect(child_1_1.children[0].to_s).to eq('a [1..2]')
|
246
246
|
expect(child_1_1.children[0].equal?(child_0_1.children[0])).to be_truthy # Sharing
|
247
247
|
end
|
248
248
|
end # context
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../../spec_helper'
|
4
|
+
|
5
|
+
require_relative '../../../lib/dendroid/parsing/composite_parse_node'
|
6
|
+
|
7
|
+
RSpec.describe Dendroid::Parsing::CompositeParseNode do
|
8
|
+
let(:sample_range) { 3..5 }
|
9
|
+
let(:sample_child_count) { 7 }
|
10
|
+
subject { described_class.new(sample_range.begin, sample_range.end, sample_child_count) }
|
11
|
+
|
12
|
+
context 'Initialization:' do
|
13
|
+
it 'should be initialized with two token positions and a child count' do
|
14
|
+
expect { described_class.new(sample_range.begin, sample_range.end, sample_child_count) }.not_to raise_error
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'has its children array filled with nils' do
|
18
|
+
# Check number of elements...
|
19
|
+
expect(subject.children.size).to eq(sample_child_count)
|
20
|
+
|
21
|
+
# All elements are nil
|
22
|
+
expect(subject.children.all?(&:nil?)).to be_truthy
|
23
|
+
end
|
24
|
+
end # context
|
25
|
+
|
26
|
+
context 'provided services:' do
|
27
|
+
it 'adds a child node at a specific slot' do
|
28
|
+
child3 = Dendroid::Parsing::ParseNode.new(3, 4)
|
29
|
+
child5 = Dendroid::Parsing::ParseNode.new(5, 6)
|
30
|
+
subject.add_child(child3, 3)
|
31
|
+
subject.add_child(child5, 5)
|
32
|
+
expect(subject.children.reject(&:nil?).size).to eq(2)
|
33
|
+
expect(subject.children[3]).to eq(child3)
|
34
|
+
expect(subject.children[5]).to eq(child5)
|
35
|
+
end
|
36
|
+
end # context
|
37
|
+
end # describe
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../../spec_helper'
|
4
|
+
require_relative '../../../lib/dendroid/grm_dsl/base_grm_builder'
|
5
|
+
require_relative '../../../lib/dendroid/grm_analysis/dotted_item'
|
6
|
+
require_relative '../../../lib/dendroid/recognizer/e_item'
|
7
|
+
require_relative '../../../lib/dendroid/parsing/empty_rule_node'
|
8
|
+
|
9
|
+
RSpec.describe Dendroid::Parsing::EmptyRuleNode do
|
10
|
+
let(:sample_rule) { sample_grammar.rules[0] }
|
11
|
+
let(:sample_dotted_item) { Dendroid::GrmAnalysis::DottedItem.new(sample_rule, 0, 1) }
|
12
|
+
let(:sample_entry) { Dendroid::Recognizer::EItem.new(sample_dotted_item, 5) }
|
13
|
+
|
14
|
+
subject { described_class.new(sample_entry, 5) }
|
15
|
+
|
16
|
+
let(:sample_grammar) do
|
17
|
+
builder = Dendroid::GrmDSL::BaseGrmBuilder.new do
|
18
|
+
declare_terminals('INTEGER')
|
19
|
+
|
20
|
+
rule('s' => ['INTEGER', ''])
|
21
|
+
end
|
22
|
+
|
23
|
+
builder.grammar
|
24
|
+
end
|
25
|
+
|
26
|
+
context 'Initialization:' do
|
27
|
+
it 'should be initialized with a chart entry and an end rank number' do
|
28
|
+
expect { described_class.new(sample_entry, 5) }.not_to raise_error
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'knows the rule it is related to' do
|
32
|
+
expect(subject.rule).to eq(sample_grammar.rules[0])
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'knows the alternative index it is related to' do
|
36
|
+
expect(subject.alt_index).to eq(1)
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'knows its origin and end positions' do
|
40
|
+
expect(subject.range.begin).to eq(subject.range.end)
|
41
|
+
expect(subject.range.begin).to eq(5)
|
42
|
+
end
|
43
|
+
end # context
|
44
|
+
|
45
|
+
context 'Provided services:' do
|
46
|
+
it 'renders a String representation of its range' do
|
47
|
+
expect(subject.to_s).to eq('_ [5..5]')
|
48
|
+
end
|
49
|
+
end # context
|
50
|
+
end # describe
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../../spec_helper'
|
4
|
+
require_relative '../../../lib/dendroid/parsing/parse_node'
|
5
|
+
|
6
|
+
RSpec.describe Dendroid::Parsing::ParseNode do
|
7
|
+
let(:sample_range) { 3..5 }
|
8
|
+
subject { described_class.new(3, 5) }
|
9
|
+
|
10
|
+
context 'Initialization:' do
|
11
|
+
it 'should be initialized with two token positions' do
|
12
|
+
expect { described_class.new(3, 5) }.not_to raise_error
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'knows its origin and end positions' do
|
16
|
+
expect(subject.range).to eq(sample_range)
|
17
|
+
end
|
18
|
+
end # context
|
19
|
+
|
20
|
+
context 'Provided services:' do
|
21
|
+
it 'renders a String representation of its range' do
|
22
|
+
expect(subject.send(:range_to_s)).to eq('[3..5]')
|
23
|
+
end
|
24
|
+
end # context
|
25
|
+
end # describe
|
@@ -22,15 +22,15 @@ RSpec.describe Dendroid::Parsing::TerminalNode do
|
|
22
22
|
it 'should be initialized with a symbol, terminal and a rank' do
|
23
23
|
expect { described_class.new(ex_terminal, plus_token, 3) }.not_to raise_error
|
24
24
|
end
|
25
|
-
end
|
25
|
+
end # context
|
26
26
|
|
27
|
-
context 'provided services' do
|
27
|
+
context 'provided services:' do
|
28
28
|
it 'renders a String representation of itself' do
|
29
|
-
expect(plus_node.to_s).to eq('PLUS [3
|
29
|
+
expect(plus_node.to_s).to eq('PLUS [3..4]')
|
30
30
|
end
|
31
31
|
|
32
32
|
it 'renders also the token value (if any)' do
|
33
|
-
expect(int_node.to_s).to eq('INTEGER: 2 [5
|
33
|
+
expect(int_node.to_s).to eq('INTEGER: 2 [5..6]')
|
34
34
|
end
|
35
35
|
end
|
36
36
|
end
|
data/version.txt
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.2.
|
1
|
+
0.2.03
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dendroid
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.03
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dimitri Geshef
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-12-
|
11
|
+
date: 2023-12-22 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: WIP. A Ruby implementation of an Earley parser
|
14
14
|
email: famished.tiger@yahoo.com
|
@@ -62,6 +62,9 @@ files:
|
|
62
62
|
- spec/dendroid/lexical/token_position_spec.rb
|
63
63
|
- spec/dendroid/lexical/token_spec.rb
|
64
64
|
- spec/dendroid/parsing/chart_walker_spec.rb
|
65
|
+
- spec/dendroid/parsing/composite_parse_node_spec.rb
|
66
|
+
- spec/dendroid/parsing/empty_rule_node_spec.rb
|
67
|
+
- spec/dendroid/parsing/parse_node_spec.rb
|
65
68
|
- spec/dendroid/parsing/terminal_node_spec.rb
|
66
69
|
- spec/dendroid/recognizer/chart_spec.rb
|
67
70
|
- spec/dendroid/recognizer/e_item_spec.rb
|