dendroid 0.2.02 → 0.2.04
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 +21 -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 +278 -184
- 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 +50 -9
- data/lib/dendroid/recognizer/e_item.rb +3 -1
- data/lib/dendroid/syntax/rule.rb +1 -1
- data/lib/dendroid.rb +0 -2
- 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 -84
- 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/spec/dendroid/parsing/walk_progress_spec.rb +61 -0
- data/version.txt +1 -1
- metadata +6 -2
@@ -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
|
@@ -7,16 +7,30 @@ 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 beginning from the root node.
|
10
14
|
class WalkProgress
|
15
|
+
# @return [Symbol] One of: :New, :Running, :Waiting, :Complete, :Forking, :Delegating
|
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
|
15
|
-
attr_reader :parents
|
16
27
|
|
17
|
-
#
|
18
|
-
|
28
|
+
# @return [Array<Dendroid::Parsing::CompositeParseNode>] The ancestry of current parse node.
|
29
|
+
attr_reader :parents
|
19
30
|
|
31
|
+
# @param start_rank [Integer] Initial rank at the start of the visit
|
32
|
+
# @param start_item [Dendroid::Recognizer::EItem] Initial chart entry to visit
|
33
|
+
# @param parents [Array<Dendroid::Parsing::CompositeParseNode>]
|
20
34
|
def initialize(start_rank, start_item, parents)
|
21
35
|
@state = :New
|
22
36
|
@curr_rank = start_rank
|
@@ -34,32 +48,46 @@ module Dendroid
|
|
34
48
|
@parents = orig.parents.dup
|
35
49
|
end
|
36
50
|
|
51
|
+
# Current item has multiple predecessors: set the state to Forking and
|
52
|
+
# force one of the predecessor to be the next entry to visit.
|
53
|
+
# @param thePredecessor [Dendroid::Recognizer::EItem]
|
37
54
|
def fork(thePredecessor)
|
38
55
|
@state = :Forking
|
39
56
|
@predecessor = thePredecessor
|
40
57
|
end
|
41
58
|
|
59
|
+
# Set the current entry being visited to the given one
|
60
|
+
# @param anEntry [Dendroid::Recognizer::EItem]
|
42
61
|
def curr_item=(anEntry)
|
43
62
|
raise StandardError if anEntry.nil?
|
44
63
|
|
45
64
|
@curr_item = anEntry
|
46
65
|
end
|
47
66
|
|
67
|
+
# Add a child leaf node for the given chart entry that corresponds
|
68
|
+
# to an empty rule.
|
69
|
+
# @param anEntry [Dendroid::Recognizer::EItem]
|
70
|
+
# @return [Dendroid::Parsing::EmptyRuleNode]
|
48
71
|
def add_node_empty(anEntry)
|
49
72
|
node_empty = EmptyRuleNode.new(anEntry, curr_rank)
|
50
73
|
add_child_node(node_empty)
|
51
74
|
end
|
52
75
|
|
53
|
-
# Add a terminal node for
|
76
|
+
# Add a leaf terminal node for the token at current rank as a child of last parent.
|
77
|
+
# @param token [Dendroid::Lexical::Token]
|
78
|
+
# @return [Dendroid::Parsing::TerminalNode]
|
54
79
|
def add_terminal_node(token)
|
55
80
|
@curr_rank -= 1
|
56
81
|
term_node = TerminalNode.new(curr_item.prev_symbol, token, curr_rank)
|
57
82
|
add_child_node(term_node)
|
58
83
|
end
|
59
84
|
|
60
|
-
#
|
85
|
+
# Make an AND node for the given entry as a child of last parent and
|
86
|
+
# push this node in the ancestry
|
87
|
+
# @param anEntry [Dendroid::Recognizer::EItem]
|
88
|
+
# @return [Dendroid::Parsing::AndNode]
|
61
89
|
def push_and_node(anEntry)
|
62
|
-
node =
|
90
|
+
node = AndNode.new(anEntry, curr_rank)
|
63
91
|
raise StandardError unless anEntry.rule == node.rule # Fails
|
64
92
|
|
65
93
|
add_child_node(node)
|
@@ -68,6 +96,11 @@ module Dendroid
|
|
68
96
|
node
|
69
97
|
end
|
70
98
|
|
99
|
+
# Make an OR node as a child of last parent and
|
100
|
+
# push this node in the ancestry.
|
101
|
+
# @param origin [Integer] Start rank
|
102
|
+
# #param arity [Integer] The number of alternative derivations
|
103
|
+
# @return [Dendroid::Parsing::OrNode]
|
71
104
|
def push_or_node(origin, arity)
|
72
105
|
node = OrNode.new(curr_item.prev_symbol, origin, curr_rank, arity)
|
73
106
|
add_child_node(node)
|
@@ -76,14 +109,22 @@ module Dendroid
|
|
76
109
|
node
|
77
110
|
end
|
78
111
|
|
112
|
+
# Add the given node as a child of the last parent node.
|
113
|
+
# @param aNode [Dendroid::Parsing::ParseNode]
|
114
|
+
# @return [Dendroid::Parsing::ParseNode]
|
79
115
|
def add_child_node(aNode)
|
80
|
-
parents.last.add_child(aNode, curr_item.position - 1)
|
116
|
+
parents.last.add_child(aNode, curr_item.position - 1) unless parents.empty?
|
81
117
|
aNode
|
82
118
|
end
|
83
119
|
|
120
|
+
# rubocop: disable Metrics/CyclomaticComplexity
|
121
|
+
# rubocop: disable Metrics/PerceivedComplexity
|
122
|
+
|
84
123
|
# Do the given EItems match one of the parent?
|
85
124
|
# Matching = corresponds to the same rule and range
|
86
|
-
# @
|
125
|
+
# @param entries [Dendroid::Recognizer::EItem]
|
126
|
+
# @param stop_at_first [Boolean] Must be true
|
127
|
+
# @return [Array<Array<EItem, Integer>>]
|
87
128
|
def match_parent?(entries, stop_at_first)
|
88
129
|
matching = []
|
89
130
|
min_origin = entries[0].origin
|
@@ -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)
|
data/lib/dendroid.rb
CHANGED
@@ -6,10 +6,8 @@
|
|
6
6
|
module Dendroid
|
7
7
|
end # module
|
8
8
|
|
9
|
-
|
10
9
|
# This file acts as a jumping-off point for loading dependencies expected
|
11
10
|
# for a Dendroid client.
|
12
|
-
|
13
11
|
require_relative './dendroid/grm_dsl/base_grm_builder'
|
14
12
|
require_relative './dendroid/utils/base_tokenizer'
|
15
13
|
require_relative './dendroid/recognizer/recognizer'
|
@@ -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
|
@@ -5,14 +5,6 @@ require_relative '../support/sample_grammars'
|
|
5
5
|
require_relative '../../../lib/dendroid/recognizer/recognizer'
|
6
6
|
require_relative '../../../lib/dendroid/parsing/chart_walker'
|
7
7
|
|
8
|
-
# require_relative '../grm_dsl/base_grm_builder'
|
9
|
-
# require_relative '../utils/base_tokenizer'
|
10
|
-
# require_relative '../recognizer/recognizer'
|
11
|
-
# require_relative 'chart_walker'
|
12
|
-
# require_relative 'parse_tree_visitor'
|
13
|
-
# require_relative '../formatters/bracket_notation'
|
14
|
-
# require_relative '../formatters/ascii_tree'
|
15
|
-
|
16
8
|
RSpec.describe Dendroid::Parsing::ChartWalker do
|
17
9
|
include SampleGrammars
|
18
10
|
|
@@ -45,37 +37,37 @@ RSpec.describe Dendroid::Parsing::ChartWalker do
|
|
45
37
|
walker = described_class.new(chart)
|
46
38
|
root = walker.walk(success_entry(chart, recognizer))
|
47
39
|
|
48
|
-
expect(root.to_s).to eq('p => s [0
|
40
|
+
expect(root.to_s).to eq('p => s [0..5]')
|
49
41
|
expect(root.children.size).to eq(1)
|
50
|
-
expect(root.children[-1].to_s).to eq('s => s PLUS m [0
|
42
|
+
expect(root.children[-1].to_s).to eq('s => s PLUS m [0..5]')
|
51
43
|
plus_expr = root.children[-1]
|
52
44
|
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
|
45
|
+
expect(plus_expr.children[0].to_s).to eq('s => m [0..1]')
|
46
|
+
expect(plus_expr.children[1].to_s).to eq('PLUS [1..2]')
|
47
|
+
expect(plus_expr.children[2].to_s).to eq('m => m STAR t [2..5]')
|
56
48
|
|
57
49
|
operand_plus = plus_expr.children[0]
|
58
50
|
expect(operand_plus.children.size).to eq(1)
|
59
|
-
expect(operand_plus.children[0].to_s).to eq('m => t [0
|
51
|
+
expect(operand_plus.children[0].to_s).to eq('m => t [0..1]')
|
60
52
|
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
|
53
|
+
expect(operand_plus.children[0].children[0].to_s).to eq('t => INTEGER [0..1]')
|
54
|
+
expect(operand_plus.children[0].children[0].children[0].to_s).to eq('INTEGER: 2 [0..1]')
|
63
55
|
|
64
|
-
expect(plus_expr.children[1].to_s).to eq('PLUS [1
|
56
|
+
expect(plus_expr.children[1].to_s).to eq('PLUS [1..2]')
|
65
57
|
|
66
58
|
star_expr = plus_expr.children[2]
|
67
59
|
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
|
60
|
+
expect(star_expr.children[0].to_s).to eq('m => t [2..3]')
|
61
|
+
expect(star_expr.children[1].to_s).to eq('STAR [3..4]')
|
62
|
+
expect(star_expr.children[2].to_s).to eq('t => INTEGER [4..5]')
|
71
63
|
|
72
64
|
operand_star = star_expr.children[0]
|
73
65
|
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
|
66
|
+
expect(operand_star.children[0].to_s).to eq('t => INTEGER [2..3]')
|
67
|
+
expect(operand_star.children[0].children[0].to_s).to eq('INTEGER: 3 [2..3]')
|
76
68
|
|
77
69
|
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
|
70
|
+
expect(star_expr.children[2].children[0].to_s).to eq('INTEGER: 4 [4..5]')
|
79
71
|
end
|
80
72
|
|
81
73
|
it 'generates a parse tree for grammar l10 (with left recursive rule)' do
|
@@ -84,27 +76,27 @@ RSpec.describe Dendroid::Parsing::ChartWalker do
|
|
84
76
|
walker = described_class.new(chart)
|
85
77
|
root = walker.walk(success_entry(chart, recognizer))
|
86
78
|
|
87
|
-
expect(root.to_s).to eq('A => A a [0
|
79
|
+
expect(root.to_s).to eq('A => A a [0..5]')
|
88
80
|
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
|
81
|
+
expect(root.children[0].to_s).to eq('A => A a [0..4]')
|
82
|
+
expect(root.children[1].to_s).to eq('a [4..5]')
|
91
83
|
|
92
84
|
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
|
85
|
+
expect(root.children[0].children[0].to_s).to eq('A => A a [0..3]')
|
86
|
+
expect(root.children[0].children[1].to_s).to eq('a [3..4]')
|
95
87
|
|
96
88
|
grand_child = root.children[0].children[0]
|
97
89
|
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
|
90
|
+
expect(grand_child.children[0].to_s).to eq('A => A a [0..2]')
|
91
|
+
expect(grand_child.children[1].to_s).to eq('a [2..3]')
|
100
92
|
|
101
93
|
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
|
94
|
+
expect(grand_child.children[0].children[0].to_s).to eq('A => A a [0..1]')
|
95
|
+
expect(grand_child.children[0].children[1].to_s).to eq('a [1..2]')
|
104
96
|
|
105
97
|
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
|
98
|
+
expect(grand_child.children[0].children[0].children[0].to_s).to eq('_ [0..0]')
|
99
|
+
expect(grand_child.children[0].children[0].children[1].to_s).to eq('a [0..1]')
|
108
100
|
end
|
109
101
|
|
110
102
|
it 'generates a parse tree for grammar l11 (with right recursive rule)' do
|
@@ -113,27 +105,27 @@ RSpec.describe Dendroid::Parsing::ChartWalker do
|
|
113
105
|
walker = described_class.new(chart)
|
114
106
|
root = walker.walk(success_entry(chart, recognizer))
|
115
107
|
|
116
|
-
expect(root.to_s).to eq('A => a A [0
|
108
|
+
expect(root.to_s).to eq('A => a A [0..5]')
|
117
109
|
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
|
110
|
+
expect(root.children[0].to_s).to eq('a [0..1]')
|
111
|
+
expect(root.children[1].to_s).to eq('A => a A [1..5]')
|
120
112
|
|
121
113
|
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
|
114
|
+
expect(root.children[1].children[0].to_s).to eq('a [1..2]')
|
115
|
+
expect(root.children[1].children[1].to_s).to eq('A => a A [2..5]')
|
124
116
|
|
125
117
|
grand_child = root.children[1].children[1]
|
126
118
|
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
|
119
|
+
expect(grand_child.children[0].to_s).to eq('a [2..3]')
|
120
|
+
expect(grand_child.children[1].to_s).to eq('A => a A [3..5]')
|
129
121
|
|
130
122
|
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
|
123
|
+
expect(grand_child.children[1].children[0].to_s).to eq('a [3..4]')
|
124
|
+
expect(grand_child.children[1].children[1].to_s).to eq('A => a A [4..5]')
|
133
125
|
|
134
126
|
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
|
127
|
+
expect(grand_child.children[1].children[1].children[0].to_s).to eq('a [4..5]')
|
128
|
+
expect(grand_child.children[1].children[1].children[1].to_s).to eq('_ [5..5]')
|
137
129
|
end
|
138
130
|
end # context
|
139
131
|
|
@@ -144,78 +136,78 @@ RSpec.describe Dendroid::Parsing::ChartWalker do
|
|
144
136
|
walker = described_class.new(chart)
|
145
137
|
root = walker.walk(success_entry(chart, recognizer))
|
146
138
|
|
147
|
-
expect(root.to_s).to eq('OR: S [0
|
139
|
+
expect(root.to_s).to eq('OR: S [0..4]')
|
148
140
|
expect(root.children.size).to eq(3)
|
149
141
|
root.children.each do |child|
|
150
142
|
expect(child.children.size).to eq(2)
|
151
|
-
expect(child.to_s).to eq('S => S S [0
|
143
|
+
expect(child.to_s).to eq('S => S S [0..4]')
|
152
144
|
end
|
153
145
|
(a, b, c) = root.children
|
154
146
|
|
155
147
|
# Test structure of tree a
|
156
148
|
(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
|
149
|
+
expect(child_a_0.to_s).to eq('S => S S [0..2]')
|
150
|
+
expect(child_a_1.to_s).to eq('S => S S [2..4]')
|
159
151
|
expect(child_a_0.children.size).to eq(2)
|
160
152
|
(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
|
153
|
+
expect(child_a_0_0.to_s).to eq('S => x [0..1]')
|
154
|
+
expect(child_a_0_1.to_s).to eq('S => x [1..2]')
|
155
|
+
expect(child_a_0_0.children[0].to_s).to eq('x [0..1]')
|
156
|
+
expect(child_a_0_1.children[0].to_s).to eq('x [1..2]')
|
165
157
|
|
166
158
|
expect(child_a_1.children.size).to eq(2)
|
167
159
|
(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
|
160
|
+
expect(child_a_1_0.to_s).to eq('S => x [2..3]')
|
161
|
+
expect(child_a_1_1.to_s).to eq('S => x [3..4]')
|
162
|
+
expect(child_a_1_0.children[0].to_s).to eq('x [2..3]')
|
163
|
+
expect(child_a_1_1.children[0].to_s).to eq('x [3..4]')
|
172
164
|
|
173
165
|
# Test structure of forest b
|
174
166
|
(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
|
167
|
+
expect(child_b_0.to_s).to eq('OR: S [0..3]')
|
168
|
+
expect(child_b_1.to_s).to eq('S => x [3..4]')
|
177
169
|
expect(child_b_1.equal?(child_a_1_1)).to be_truthy # Sharing
|
178
170
|
expect(child_b_0.children.size).to eq(2)
|
179
171
|
(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
|
172
|
+
expect(child_b_0_0.to_s).to eq('S => S S [0..3]')
|
173
|
+
expect(child_b_0_1.to_s).to eq('S => S S [0..3]')
|
182
174
|
expect(child_b_0_0.children.size).to eq(2)
|
183
175
|
(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
|
176
|
+
expect(child_b_0_0_0.to_s).to eq('S => x [0..1]')
|
185
177
|
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
|
178
|
+
expect(child_b_0_0_1.to_s).to eq('S => S S [1..3]')
|
187
179
|
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
|
180
|
+
expect(child_b_0_0_1.children[0].to_s).to eq('S => x [1..2]')
|
189
181
|
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
|
182
|
+
expect(child_b_0_0_1.children[1].to_s).to eq('S => x [2..3]')
|
191
183
|
expect(child_b_0_0_1.children[1].equal?(child_a_1_0)).to be_truthy # Sharing
|
192
184
|
|
193
185
|
expect(child_b_0_1.children.size).to eq(2)
|
194
186
|
(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
|
187
|
+
expect(child_b_0_1_0.to_s).to eq('S => S S [0..2]')
|
196
188
|
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
|
189
|
+
expect(child_b_0_1_1.to_s).to eq('S => x [2..3]')
|
198
190
|
expect(child_b_0_1_1.equal?(child_a_1_0)).to be_truthy # Sharing
|
199
191
|
|
200
192
|
# Test structure of forest c
|
201
193
|
(child_c_0, child_c_1) = c.children
|
202
|
-
expect(child_c_0.to_s).to eq('S => x [0
|
194
|
+
expect(child_c_0.to_s).to eq('S => x [0..1]')
|
203
195
|
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
|
196
|
+
expect(child_c_1.to_s).to eq('OR: S [1..4]')
|
205
197
|
expect(child_c_1.children.size).to eq(2)
|
206
198
|
(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
|
199
|
+
expect(child_c_1_0.to_s).to eq('S => S S [1..4]')
|
200
|
+
expect(child_c_1_1.to_s).to eq('S => S S [1..4]')
|
209
201
|
expect(child_c_1_0.children.size).to eq(2)
|
210
202
|
(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
|
203
|
+
expect(child_c_1_0_0.to_s).to eq('S => x [1..2]')
|
212
204
|
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
|
205
|
+
expect(child_c_1_0_1.to_s).to eq('S => S S [2..4]')
|
214
206
|
expect(child_c_1_0_1.equal?(child_a_1)).to be_truthy # Sharing
|
215
207
|
(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
|
208
|
+
expect(child_c_1_1_0.to_s).to eq('S => S S [1..3]')
|
217
209
|
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
|
210
|
+
expect(child_c_1_1_1.to_s).to eq('S => x [3..4]')
|
219
211
|
expect(child_c_1_1_1.equal?(child_b_1)).to be_truthy # Sharing
|
220
212
|
end
|
221
213
|
|
@@ -225,24 +217,24 @@ RSpec.describe Dendroid::Parsing::ChartWalker do
|
|
225
217
|
walker = described_class.new(chart)
|
226
218
|
root = walker.walk(success_entry(chart, recognizer))
|
227
219
|
|
228
|
-
expect(root.to_s).to eq('OR: S [0
|
220
|
+
expect(root.to_s).to eq('OR: S [0..2]')
|
229
221
|
expect(root.children.size).to eq(2)
|
230
222
|
root.children.each do |ch|
|
231
|
-
expect(ch.to_s).to eq('S => S T [0
|
223
|
+
expect(ch.to_s).to eq('S => S T [0..2]')
|
232
224
|
expect(ch.children.size).to eq(2)
|
233
225
|
end
|
234
226
|
(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
|
227
|
+
expect(child_0_0.to_s).to eq('S => a [0..1]')
|
228
|
+
expect(child_0_1.to_s).to eq('T => a B [1..2]')
|
237
229
|
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
|
230
|
+
expect(child_0_1.children[0].to_s).to eq('a [1..2]')
|
231
|
+
expect(child_0_1.children[1].to_s).to eq('_ [2..2]')
|
240
232
|
|
241
233
|
(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
|
234
|
+
expect(child_1_0.to_s).to eq('S => a [0..1]')
|
235
|
+
expect(child_1_1.to_s).to eq('T => a [1..2]')
|
244
236
|
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
|
237
|
+
expect(child_1_1.children[0].to_s).to eq('a [1..2]')
|
246
238
|
expect(child_1_1.children[0].equal?(child_0_1.children[0])).to be_truthy # Sharing
|
247
239
|
end
|
248
240
|
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
|