rley 0.2.15 → 0.3.00
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 +4 -0
- data/lib/rley/constants.rb +1 -1
- data/lib/rley/gfg/call_edge.rb +30 -0
- data/lib/rley/gfg/edge.rb +4 -0
- data/lib/rley/gfg/end_vertex.rb +1 -1
- data/lib/rley/gfg/epsilon_edge.rb +0 -4
- data/lib/rley/gfg/grm_flow_graph.rb +32 -7
- data/lib/rley/gfg/item_vertex.rb +71 -25
- data/lib/rley/gfg/non_terminal_vertex.rb +10 -1
- data/lib/rley/gfg/return_edge.rb +31 -0
- data/lib/rley/gfg/scan_edge.rb +2 -1
- data/lib/rley/gfg/shortcut_edge.rb +26 -0
- data/lib/rley/gfg/start_vertex.rb +2 -2
- data/lib/rley/gfg/vertex.rb +27 -1
- data/lib/rley/parse_forest_visitor.rb +115 -0
- data/lib/rley/parser/base_parser.rb +27 -0
- data/lib/rley/parser/dotted_item.rb +11 -0
- data/lib/rley/parser/earley_parser.rb +3 -15
- data/lib/rley/parser/gfg_chart.rb +106 -0
- data/lib/rley/parser/gfg_earley_parser.rb +139 -0
- data/lib/rley/parser/gfg_parsing.rb +384 -0
- data/lib/rley/parser/parse_entry.rb +148 -0
- data/lib/rley/parser/parse_entry_set.rb +104 -0
- data/lib/rley/parser/parse_entry_tracker.rb +56 -0
- data/lib/rley/parser/parse_forest_builder.rb +229 -0
- data/lib/rley/parser/parse_forest_factory.rb +54 -0
- data/lib/rley/parser/parse_walker_factory.rb +237 -0
- data/lib/rley/ptree/token_range.rb +14 -1
- data/lib/rley/sppf/alternative_node.rb +34 -0
- data/lib/rley/sppf/composite_node.rb +27 -0
- data/lib/rley/sppf/epsilon_node.rb +27 -0
- data/lib/rley/sppf/leaf_node.rb +12 -0
- data/lib/rley/sppf/non_terminal_node.rb +38 -0
- data/lib/rley/sppf/parse_forest.rb +48 -0
- data/lib/rley/sppf/sppf_node.rb +24 -0
- data/lib/rley/sppf/token_node.rb +29 -0
- data/lib/rley/syntax/grammar_builder.rb +16 -12
- data/lib/rley/syntax/grm_symbol.rb +6 -0
- data/lib/rley/syntax/terminal.rb +5 -0
- data/spec/rley/gfg/call_edge_spec.rb +51 -0
- data/spec/rley/gfg/end_vertex_spec.rb +1 -0
- data/spec/rley/gfg/grm_flow_graph_spec.rb +24 -2
- data/spec/rley/gfg/item_vertex_spec.rb +75 -6
- data/spec/rley/gfg/non_terminal_vertex_spec.rb +14 -0
- data/spec/rley/gfg/return_edge_spec.rb +51 -0
- data/spec/rley/gfg/shortcut_edge_spec.rb +43 -0
- data/spec/rley/gfg/vertex_spec.rb +52 -37
- data/spec/rley/parse_forest_visitor_spec.rb +238 -0
- data/spec/rley/parser/dotted_item_spec.rb +29 -8
- data/spec/rley/parser/gfg_chart_spec.rb +138 -0
- data/spec/rley/parser/gfg_earley_parser_spec.rb +918 -0
- data/spec/rley/parser/gfg_parsing_spec.rb +565 -0
- data/spec/rley/parser/parse_entry_set_spec.rb +179 -0
- data/spec/rley/parser/parse_entry_spec.rb +208 -0
- data/spec/rley/parser/parse_forest_builder_spec.rb +382 -0
- data/spec/rley/parser/parse_forest_factory_spec.rb +81 -0
- data/spec/rley/parser/parse_walker_factory_spec.rb +235 -0
- data/spec/rley/parser/state_set_spec.rb +4 -0
- data/spec/rley/sppf/alternative_node_spec.rb +72 -0
- data/spec/rley/sppf/antecedence_graph.rb +87 -0
- data/spec/rley/sppf/forest_representation.rb +136 -0
- data/spec/rley/sppf/gfg_representation.rb +111 -0
- data/spec/rley/sppf/non_terminal_node_spec.rb +64 -0
- data/spec/rley/support/ambiguous_grammar_helper.rb +36 -36
- data/spec/rley/support/expectation_helper.rb +36 -0
- data/spec/rley/support/grammar_helper.rb +28 -0
- data/spec/rley/support/grammar_sppf_helper.rb +25 -0
- data/spec/rley/syntax/grammar_builder_spec.rb +5 -0
- data/spec/rley/syntax/non_terminal_spec.rb +4 -0
- data/spec/rley/syntax/terminal_spec.rb +4 -0
- metadata +58 -2
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
require_relative '../../spec_helper'
|
|
2
|
+
|
|
3
|
+
require_relative '../../../lib/rley/syntax/production'
|
|
4
|
+
require_relative '../../../lib/rley/parser/dotted_item'
|
|
5
|
+
require_relative '../../../lib/rley/gfg/item_vertex'
|
|
6
|
+
require_relative '../../../lib/rley/parser/parse_entry'
|
|
7
|
+
|
|
8
|
+
# Load the class under test
|
|
9
|
+
require_relative '../../../lib/rley/parser/parse_entry_set'
|
|
10
|
+
|
|
11
|
+
module Rley # Open this namespace to avoid module qualifier prefixes
|
|
12
|
+
module Parser # Open this namespace to avoid module qualifier prefixes
|
|
13
|
+
describe ParseEntrySet do
|
|
14
|
+
# Factory method. Builds a production with given left-hand side (LHS)
|
|
15
|
+
# and given RHS (right-hand side)
|
|
16
|
+
def build_prod(theLHS, *theRHSSymbols)
|
|
17
|
+
return Syntax::Production.new(theLHS, theRHSSymbols)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
let(:t_a) { Rley::Syntax::Terminal.new('a') }
|
|
21
|
+
let(:t_b) { Rley::Syntax::Terminal.new('b') }
|
|
22
|
+
let(:t_c) { Rley::Syntax::Terminal.new('c') }
|
|
23
|
+
let(:nt_repeated_c) { Rley::Syntax::NonTerminal.new('Repetition') }
|
|
24
|
+
let(:repeated_prod) { build_prod(nt_repeated_c, t_c, nt_repeated_c) }
|
|
25
|
+
let(:nt_sentence) { Rley::Syntax::NonTerminal.new('Sentence') }
|
|
26
|
+
let(:sample_prod) { build_prod(nt_sentence, t_a, t_b, t_b, nt_repeated_c) }
|
|
27
|
+
|
|
28
|
+
let(:sample_item1) { Parser::DottedItem.new(sample_prod, 1) }
|
|
29
|
+
let(:sample_item2) { Parser::DottedItem.new(sample_prod, 2) }
|
|
30
|
+
let(:sample_item3) { Parser::DottedItem.new(sample_prod, 3) }
|
|
31
|
+
|
|
32
|
+
let(:vertex1) { GFG::ItemVertex.new(sample_item1) }
|
|
33
|
+
let(:entry1) { ParseEntry.new(vertex1, 2) }
|
|
34
|
+
let(:vertex2) { GFG::ItemVertex.new(sample_item2) }
|
|
35
|
+
let(:entry2) { ParseEntry.new(vertex2, 3) }
|
|
36
|
+
let(:vertex3) { GFG::ItemVertex.new(sample_item3) }
|
|
37
|
+
let(:entry3) { ParseEntry.new(vertex3, 4) }
|
|
38
|
+
|
|
39
|
+
context 'Initialization:' do
|
|
40
|
+
it 'should be created without argument' do
|
|
41
|
+
expect { ParseEntrySet.new }.not_to raise_error
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it 'should be empty after creation' do
|
|
45
|
+
expect(subject.entries).to be_empty
|
|
46
|
+
end
|
|
47
|
+
end # context
|
|
48
|
+
|
|
49
|
+
context 'Provided services:' do
|
|
50
|
+
it 'should accept the addition of an entry' do
|
|
51
|
+
# Case: first time e,try addition
|
|
52
|
+
expect(subject.push_entry(entry1)).to eq(entry1)
|
|
53
|
+
expect(subject).not_to be_empty
|
|
54
|
+
|
|
55
|
+
# Case: duplicate entry
|
|
56
|
+
expect(subject.push_entry(entry1)).to eq(entry1)
|
|
57
|
+
|
|
58
|
+
# Yet another entry
|
|
59
|
+
expect(subject.push_entry(entry2)).to eq(entry2)
|
|
60
|
+
expect(subject.entries).to eq([entry1, entry2])
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
it 'should retrieve the entry at given position' do
|
|
64
|
+
subject.push_entry(entry1)
|
|
65
|
+
subject.push_entry(entry2)
|
|
66
|
+
expect(subject[0]).to eq(entry1)
|
|
67
|
+
expect(subject[1]).to eq(entry2)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
it 'should list the entries expecting a given terminal' do
|
|
71
|
+
# Case: an entry expecting a terminal
|
|
72
|
+
subject.push_entry(entry1)
|
|
73
|
+
expect(subject.entries4term(t_b)).to eq([entry1])
|
|
74
|
+
|
|
75
|
+
# Case: a second entry expecting same terminal
|
|
76
|
+
subject.push_entry(entry2)
|
|
77
|
+
expect(subject.entries4term(t_b)).to eq([entry1, entry2])
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
it 'should list the expected terminals' do
|
|
81
|
+
subject.push_entry(entry1)
|
|
82
|
+
subject.push_entry(entry2)
|
|
83
|
+
subject.push_entry(entry3)
|
|
84
|
+
|
|
85
|
+
expect(subject.expected_terminals).to eq([t_b])
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
it 'should list the entries expecting a given non-terminal' do
|
|
89
|
+
# Case: an entry expecting a non-terminal
|
|
90
|
+
subject.push_entry(entry3)
|
|
91
|
+
expect(subject.entries4n_term(nt_repeated_c)).to eq([entry3])
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
=begin
|
|
95
|
+
it 'should list of ambiguous states' do
|
|
96
|
+
prod1 = double('fake-production1')
|
|
97
|
+
prod2 = double('fake-production2')
|
|
98
|
+
expect(subject.ambiguities.size).to eq(0)
|
|
99
|
+
|
|
100
|
+
# Adding states
|
|
101
|
+
subject.push_entry(entry1)
|
|
102
|
+
allow(vertex1).to receive(:production).and_return(prod1)
|
|
103
|
+
allow(vertex1).to receive(:"reduce_item?").and_return(true)
|
|
104
|
+
allow(vertex1).to receive(:lhs).and_return(:something)
|
|
105
|
+
expect(subject.ambiguities.size).to eq(0)
|
|
106
|
+
allow(vertex2).to receive(:production).and_return(prod2)
|
|
107
|
+
allow(vertex2).to receive(:"reduce_item?").and_return(true)
|
|
108
|
+
allow(vertex2).to receive(:lhs).and_return(:something_else)
|
|
109
|
+
subject.push_entry(entry2)
|
|
110
|
+
expect(subject.ambiguities.size).to eq(0)
|
|
111
|
+
# dotted_rule3 = double('fake_dotted_rule3')
|
|
112
|
+
# allow(dotted_rule3).to receive(:production).and_return(prod2)
|
|
113
|
+
# allow(dotted_rule3).to receive(:"reduce_item?").and_return(true)
|
|
114
|
+
# allow(dotted_rule3).to receive(:lhs).and_return(:something_else)
|
|
115
|
+
# entry3 = ParseEntry.new(dotted_rule3, 5)
|
|
116
|
+
subject.push_entry(entry3)
|
|
117
|
+
expect(subject.ambiguities[0]).to eq([entry2, entry3])
|
|
118
|
+
end
|
|
119
|
+
=end
|
|
120
|
+
=begin
|
|
121
|
+
it 'should list the states expecting a given terminal' do
|
|
122
|
+
# Case of no state
|
|
123
|
+
expect(subject.states_expecting(:a)).to be_empty
|
|
124
|
+
|
|
125
|
+
# Adding states
|
|
126
|
+
subject.push_entry(entry1)
|
|
127
|
+
subject.push_entry(entry2)
|
|
128
|
+
allow(vertex1).to receive(:next_symbol).and_return(:b)
|
|
129
|
+
allow(vertex2).to receive(:next_symbol).and_return(:a)
|
|
130
|
+
expect(subject.states_expecting(:a)).to eq([entry2])
|
|
131
|
+
expect(subject.states_expecting(:b)).to eq([entry1])
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
it 'should list the states related to a production' do
|
|
135
|
+
a_prod = double('fake-production')
|
|
136
|
+
|
|
137
|
+
# Case of no state
|
|
138
|
+
expect(subject.states_for(a_prod)).to be_empty
|
|
139
|
+
|
|
140
|
+
# Adding states
|
|
141
|
+
subject.push_entry(entry1)
|
|
142
|
+
subject.push_entry(entry2)
|
|
143
|
+
allow(vertex1).to receive(:production).and_return(:dummy)
|
|
144
|
+
allow(vertex2).to receive(:production).and_return(a_prod)
|
|
145
|
+
expect(subject.states_for(a_prod)).to eq([entry2])
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
it 'should list the states that rewrite a given non-terminal' do
|
|
149
|
+
non_term = double('fake-non-terminal')
|
|
150
|
+
prod1 = double('fake-production1')
|
|
151
|
+
prod2 = double('fake-production2')
|
|
152
|
+
|
|
153
|
+
# Adding states
|
|
154
|
+
subject.push_entry(entry1)
|
|
155
|
+
subject.push_entry(entry2)
|
|
156
|
+
allow(vertex1).to receive(:production).and_return(prod1)
|
|
157
|
+
allow(prod1).to receive(:lhs).and_return(:dummy)
|
|
158
|
+
allow(vertex2).to receive(:production).and_return(prod2)
|
|
159
|
+
allow(vertex2).to receive(:reduce_item?).and_return(true)
|
|
160
|
+
allow(prod2).to receive(:lhs).and_return(non_term)
|
|
161
|
+
expect(subject.states_rewriting(non_term)).to eq([entry2])
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
it 'should complain when impossible predecessor of parse state' do
|
|
167
|
+
subject.push_entry(entry1)
|
|
168
|
+
subject.push_entry(entry2)
|
|
169
|
+
allow(vertex1).to receive(:prev_position).and_return(nil)
|
|
170
|
+
err = StandardError
|
|
171
|
+
expect { subject.predecessor_state(entry1) }.to raise_error(err)
|
|
172
|
+
end
|
|
173
|
+
=end
|
|
174
|
+
end # context
|
|
175
|
+
end # describe
|
|
176
|
+
end # module
|
|
177
|
+
end # module
|
|
178
|
+
|
|
179
|
+
# End of file
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
require_relative '../../spec_helper'
|
|
2
|
+
|
|
3
|
+
require_relative '../../../lib/rley/syntax/terminal'
|
|
4
|
+
require_relative '../../../lib/rley/syntax/non_terminal'
|
|
5
|
+
require_relative '../../../lib/rley/syntax/production'
|
|
6
|
+
require_relative '../../../lib/rley/parser/dotted_item'
|
|
7
|
+
|
|
8
|
+
# Load the class under test
|
|
9
|
+
require_relative '../../../lib/rley/parser/parse_entry'
|
|
10
|
+
|
|
11
|
+
module Rley # Open this namespace to avoid module qualifier prefixes
|
|
12
|
+
module Parser # Open this namespace to avoid module qualifier prefixes
|
|
13
|
+
describe ParseEntry do
|
|
14
|
+
|
|
15
|
+
let(:t_a) { Syntax::Terminal.new('A') }
|
|
16
|
+
let(:t_b) { Syntax::Terminal.new('B') }
|
|
17
|
+
let(:t_c) { Syntax::Terminal.new('C') }
|
|
18
|
+
let(:nt_sentence) { Syntax::NonTerminal.new('sentence') }
|
|
19
|
+
|
|
20
|
+
let(:sample_prod) do
|
|
21
|
+
Syntax::Production.new(nt_sentence, [t_a, t_b, t_c])
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
let(:empty_prod) do
|
|
25
|
+
Syntax::Production.new(nt_sentence, [])
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
let(:dotted_rule) { DottedItem.new(sample_prod, 2) }
|
|
30
|
+
let(:origin_val) { 3 }
|
|
31
|
+
let(:vertex_faked) { double('fake-vertex') }
|
|
32
|
+
let(:vertex2) { double('vertex-mock') }
|
|
33
|
+
# Default instantiation rule
|
|
34
|
+
subject { ParseEntry.new(vertex_faked, origin_val) }
|
|
35
|
+
|
|
36
|
+
context 'Initialization:' do
|
|
37
|
+
it 'should be created with a vertex and an origin position' do
|
|
38
|
+
args = [vertex_faked, origin_val]
|
|
39
|
+
expect { ParseEntry.new(*args) }.not_to raise_error
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it 'should complain when the vertex is nil' do
|
|
43
|
+
err = StandardError
|
|
44
|
+
msg = 'GFG vertex cannot be nil'
|
|
45
|
+
expect { ParseEntry.new(nil, 2) }.to raise_error(err, msg)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it 'should know the vertex' do
|
|
49
|
+
expect(subject.vertex).to eq(vertex_faked)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
it 'should know the origin value' do
|
|
53
|
+
expect(subject.origin).to eq(origin_val)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
it 'should have not antecedent at creation' do
|
|
57
|
+
expect(subject.antecedents).to be_empty
|
|
58
|
+
expect(subject).to be_orphan
|
|
59
|
+
end
|
|
60
|
+
end # context
|
|
61
|
+
|
|
62
|
+
context 'Provided services:' do
|
|
63
|
+
it 'should compare with itself' do
|
|
64
|
+
synonym = subject # Fool Rubocop
|
|
65
|
+
expect(subject == synonym).to eq(true)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
it 'should compare with another' do
|
|
69
|
+
equal = ParseEntry.new(vertex_faked, origin_val)
|
|
70
|
+
expect(subject == equal).to eq(true)
|
|
71
|
+
|
|
72
|
+
# Same vertex, different origin
|
|
73
|
+
diff_origin = ParseEntry.new(vertex_faked, 2)
|
|
74
|
+
expect(subject == diff_origin).to eq(false)
|
|
75
|
+
|
|
76
|
+
# Different vertices, same origin
|
|
77
|
+
diff_vertex = ParseEntry.new(double('other_vertex_faked'), 3)
|
|
78
|
+
expect(subject == diff_vertex).to eq(false)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
it 'should know if the vertex is a start vertex' do
|
|
82
|
+
expect(subject).not_to be_start_entry
|
|
83
|
+
|
|
84
|
+
instance = ParseEntry.new(GFG::StartVertex.new('.NT'), 3)
|
|
85
|
+
expect(instance).to be_start_entry
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
it 'should know if the vertex is an end vertex' do
|
|
89
|
+
expect(subject).not_to be_end_entry
|
|
90
|
+
|
|
91
|
+
instance = ParseEntry.new(GFG::EndVertex.new('NT.'), 3)
|
|
92
|
+
expect(instance).to be_end_entry
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
it 'should know if the entry is a dotted item vertex' do
|
|
96
|
+
expect(subject).not_to be_dotted_entry
|
|
97
|
+
|
|
98
|
+
instance = ParseEntry.new(GFG::ItemVertex.new('P => S.'), 3)
|
|
99
|
+
expect(instance).to be_dotted_entry
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
it 'should know if the vertex is at end of production (if any)' do
|
|
103
|
+
# Case: start vertex
|
|
104
|
+
instance1 = ParseEntry.new(GFG::StartVertex.new('.NT'), 3)
|
|
105
|
+
expect(instance1).not_to be_exit_entry
|
|
106
|
+
|
|
107
|
+
# Case: end vertex
|
|
108
|
+
instance2 = ParseEntry.new(GFG::EndVertex.new('NT.'), 3)
|
|
109
|
+
expect(instance2).not_to be_exit_entry
|
|
110
|
+
|
|
111
|
+
# Case: item vertex not at end of rhs
|
|
112
|
+
v1 = double('vertex-not-at-end')
|
|
113
|
+
allow(v1).to receive(:complete?).and_return(false)
|
|
114
|
+
instance3 = ParseEntry.new(v1, 3)
|
|
115
|
+
expect(instance3).not_to be_exit_entry
|
|
116
|
+
|
|
117
|
+
# Case: item vertex at end of rhs
|
|
118
|
+
v2 = double('vertex-at-end')
|
|
119
|
+
allow(v2).to receive(:complete?).and_return(true)
|
|
120
|
+
instance4 = ParseEntry.new(v2, 3)
|
|
121
|
+
expect(instance4).to be_exit_entry
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
it 'should know if the vertex is at begin of production (if any)' do
|
|
125
|
+
# Case: start vertex
|
|
126
|
+
instance1 = ParseEntry.new(GFG::StartVertex.new('.NT'), 3)
|
|
127
|
+
expect(instance1).not_to be_entry_entry
|
|
128
|
+
|
|
129
|
+
# Case: end vertex
|
|
130
|
+
instance2 = ParseEntry.new(GFG::EndVertex.new('NT.'), 3)
|
|
131
|
+
expect(instance2).not_to be_entry_entry
|
|
132
|
+
|
|
133
|
+
# Case: item vertex not at begin of rhs
|
|
134
|
+
d1 = DottedItem.new(sample_prod, 1)
|
|
135
|
+
v1 = GFG::ItemVertex.new(d1)
|
|
136
|
+
instance3 = ParseEntry.new(v1, 3)
|
|
137
|
+
expect(instance3).not_to be_entry_entry
|
|
138
|
+
|
|
139
|
+
# Case: item vertex at end of rhs
|
|
140
|
+
d2 = DottedItem.new(sample_prod, 0)
|
|
141
|
+
v2 = GFG::ItemVertex.new(d2)
|
|
142
|
+
instance4 = ParseEntry.new(v2, 3)
|
|
143
|
+
expect(instance4).to be_entry_entry
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
it 'should know the symbol before the dot (if any)' do
|
|
147
|
+
# Case: start vertex
|
|
148
|
+
instance1 = ParseEntry.new(GFG::StartVertex.new('.NT'), 3)
|
|
149
|
+
expect(instance1.prev_symbol).to be_nil
|
|
150
|
+
|
|
151
|
+
# Case: end vertex
|
|
152
|
+
instance2 = ParseEntry.new(GFG::EndVertex.new('NT.'), 3)
|
|
153
|
+
expect(instance2.prev_symbol).to be_nil # Really correct?
|
|
154
|
+
|
|
155
|
+
# Case: item vertex not at start of rhs
|
|
156
|
+
v1 = double('vertex-not-at-start')
|
|
157
|
+
allow(v1).to receive(:prev_symbol).and_return('symbol')
|
|
158
|
+
instance3 = ParseEntry.new(v1, 3)
|
|
159
|
+
expect(instance3.prev_symbol).to eq('symbol')
|
|
160
|
+
|
|
161
|
+
# Case: item vertex at start of rhs
|
|
162
|
+
v2 = double('vertex-at-start')
|
|
163
|
+
allow(v2).to receive(:prev_symbol).and_return(nil)
|
|
164
|
+
instance4 = ParseEntry.new(v2, 0)
|
|
165
|
+
expect(instance4.prev_symbol).to be_nil
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
it 'should know the next expected symbol (if any)' do
|
|
169
|
+
# Case: start vertex
|
|
170
|
+
instance1 = ParseEntry.new(GFG::StartVertex.new('.NT'), 3)
|
|
171
|
+
expect(instance1.next_symbol).to be_nil
|
|
172
|
+
|
|
173
|
+
# Case: end vertex
|
|
174
|
+
instance2 = ParseEntry.new(GFG::EndVertex.new('NT.'), 3)
|
|
175
|
+
expect(instance2.next_symbol).to be_nil
|
|
176
|
+
|
|
177
|
+
# Case: item vertex not at end of rhs
|
|
178
|
+
v1 = double('vertex-not-at-end')
|
|
179
|
+
allow(v1).to receive(:next_symbol).and_return('symbol')
|
|
180
|
+
instance3 = ParseEntry.new(v1, 3)
|
|
181
|
+
expect(instance3.next_symbol).to eq('symbol')
|
|
182
|
+
|
|
183
|
+
# Case: item vertex at end of rhs
|
|
184
|
+
v2 = double('vertex-at-end')
|
|
185
|
+
allow(v2).to receive(:next_symbol).and_return(nil)
|
|
186
|
+
instance4 = ParseEntry.new(v2, 3)
|
|
187
|
+
expect(instance4.next_symbol).to be_nil
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
it 'should accept antecedents' do
|
|
191
|
+
antecedent = ParseEntry.new(vertex2, origin_val)
|
|
192
|
+
subject.add_antecedent(antecedent)
|
|
193
|
+
expect(subject.antecedents).to eql([antecedent])
|
|
194
|
+
expect(subject).not_to be_orphan
|
|
195
|
+
end
|
|
196
|
+
=begin
|
|
197
|
+
|
|
198
|
+
it 'should know its text representation' do
|
|
199
|
+
expected = 'sentence => A B . C | 3'
|
|
200
|
+
expect(subject.to_s).to eq(expected)
|
|
201
|
+
end
|
|
202
|
+
=end
|
|
203
|
+
end # context
|
|
204
|
+
end # describe
|
|
205
|
+
end # module
|
|
206
|
+
end # module
|
|
207
|
+
|
|
208
|
+
# End of file
|
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
require_relative '../../spec_helper'
|
|
2
|
+
|
|
3
|
+
require_relative '../../../lib/rley/parser/gfg_earley_parser'
|
|
4
|
+
|
|
5
|
+
require_relative '../../../lib/rley/syntax/grammar_builder'
|
|
6
|
+
require_relative '../support/grammar_helper'
|
|
7
|
+
require_relative '../support/expectation_helper'
|
|
8
|
+
|
|
9
|
+
require_relative '../../../lib/rley/parser/parse_walker_factory'
|
|
10
|
+
|
|
11
|
+
# Load the class under test
|
|
12
|
+
require_relative '../../../lib/rley/parser/parse_forest_builder'
|
|
13
|
+
|
|
14
|
+
module Rley # Open this namespace to avoid module qualifier prefixes
|
|
15
|
+
module Parser
|
|
16
|
+
describe ParseForestBuilder do
|
|
17
|
+
include GrammarHelper # Mix-in with token factory method
|
|
18
|
+
include ExpectationHelper # Mix-in with expectation on parse entry sets
|
|
19
|
+
|
|
20
|
+
let(:sample_grammar) do
|
|
21
|
+
# Grammar based on paper from Elisabeth Scott
|
|
22
|
+
# "SPPF=Style Parsing From Earley Recognizers" in
|
|
23
|
+
# Notes in Theoretical Computer Science 203, (2008), pp. 53-67
|
|
24
|
+
# contains a hidden left recursion and a cycle
|
|
25
|
+
builder = Syntax::GrammarBuilder.new
|
|
26
|
+
builder.add_terminals('a', 'b')
|
|
27
|
+
builder.add_production('Phi' => %'S')
|
|
28
|
+
builder.add_production('S' => %w[A T])
|
|
29
|
+
builder.add_production('S' => %w[a T])
|
|
30
|
+
builder.add_production('A' => 'a')
|
|
31
|
+
builder.add_production('A' => %w[B A])
|
|
32
|
+
builder.add_production('B' => [])
|
|
33
|
+
builder.add_production('T' => %w( b b b))
|
|
34
|
+
builder.grammar
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
let(:sample_tokens) do
|
|
38
|
+
build_token_sequence(%w(a b b b), sample_grammar)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
let(:sample_result) do
|
|
42
|
+
parser = Parser::GFGEarleyParser.new(sample_grammar)
|
|
43
|
+
parser.parse(sample_tokens)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
let(:walker) do
|
|
47
|
+
factory = ParseWalkerFactory.new
|
|
48
|
+
factory.build_walker(sample_result)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
subject do
|
|
52
|
+
ParseForestBuilder.new(sample_result)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Emit a text representation of the current path.
|
|
56
|
+
def path_to_s()
|
|
57
|
+
text_parts = subject.curr_path.map { |path_element| path_element.to_string(0) }
|
|
58
|
+
return text_parts.join('/')
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
context 'Initialization:' do
|
|
63
|
+
it 'should be created with a GFGParsing' do
|
|
64
|
+
expect { ParseForestBuilder.new(sample_result) }.not_to raise_error
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
it 'should know the parse result' do
|
|
68
|
+
expect(subject.parsing).to eq(sample_result)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
it 'should have an empty path' do
|
|
72
|
+
expect(subject.curr_path).to be_empty
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
context 'Parse forest construction' do
|
|
77
|
+
|
|
78
|
+
it 'should initialize the root node' do
|
|
79
|
+
first_event = walker.next
|
|
80
|
+
subject.receive_event(*first_event)
|
|
81
|
+
forest = subject.forest
|
|
82
|
+
|
|
83
|
+
expect(forest.root.to_string(0)).to eq('Phi[0, 4]')
|
|
84
|
+
expect(subject.curr_path).to eq([forest.root])
|
|
85
|
+
expect(subject.entry2node[first_event[1]]).to eq(forest.root)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
it 'should initialize the first child of the root node' do
|
|
89
|
+
event1 = walker.next
|
|
90
|
+
subject.receive_event(*event1)
|
|
91
|
+
|
|
92
|
+
event2 = walker.next
|
|
93
|
+
subject.receive_event(*event2)
|
|
94
|
+
|
|
95
|
+
expect(subject.curr_parent.to_string(0)).to eq('S[?, 4]')
|
|
96
|
+
expected_path2 = 'Phi[0, 4]/S[?, 4]'
|
|
97
|
+
expect(path_to_s).to eq(expected_path2)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
it 'should build alternative node when detecting backtrack point' do
|
|
101
|
+
2.times do
|
|
102
|
+
event = walker.next
|
|
103
|
+
subject.receive_event(*event)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
event3 = walker.next
|
|
107
|
+
subject.receive_event(*event3)
|
|
108
|
+
|
|
109
|
+
expect(subject.curr_parent.to_string(0)).to eq('Alt(S => a T .)[0, 4]')
|
|
110
|
+
expected_path3 = 'Phi[0, 4]/S[0, 4]/Alt(S => a T .)[0, 4]'
|
|
111
|
+
expect(path_to_s).to eq(expected_path3)
|
|
112
|
+
expect(subject.curr_path[-2].refinement).to eq(:or)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
it 'should build token node when scan edge was detected' do
|
|
116
|
+
3.times do
|
|
117
|
+
event = walker.next
|
|
118
|
+
subject.receive_event(*event)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
event4 = walker.next
|
|
122
|
+
subject.receive_event(*event4)
|
|
123
|
+
expect(event4[1].to_s).to eq('S => a T . | 0')
|
|
124
|
+
expect(subject.curr_parent.to_string(0)).to eq('T[?, 4]')
|
|
125
|
+
expected_path4 = 'Phi[0, 4]/S[0, 4]/Alt(S => a T .)[0, 4]/T[?, 4]'
|
|
126
|
+
expect(path_to_s).to eq(expected_path4)
|
|
127
|
+
expect(subject.curr_parent.subnodes).to be_empty
|
|
128
|
+
|
|
129
|
+
event5 = walker.next
|
|
130
|
+
subject.receive_event(*event5)
|
|
131
|
+
expect(event5[1].to_s).to eq('T. | 1')
|
|
132
|
+
expect(subject.curr_parent.to_string(0)).to eq('T[1, 4]')
|
|
133
|
+
expected_path5 = 'Phi[0, 4]/S[0, 4]/Alt(S => a T .)[0, 4]/T[1, 4]'
|
|
134
|
+
expect(path_to_s).to eq(expected_path5)
|
|
135
|
+
expect(subject.curr_parent.subnodes).to be_empty
|
|
136
|
+
|
|
137
|
+
event6 = walker.next
|
|
138
|
+
subject.receive_event(*event6)
|
|
139
|
+
expect(event6[1].to_s).to eq('T => b b b . | 1')
|
|
140
|
+
expect(subject.curr_parent.to_string(0)).to eq('T[1, 4]')
|
|
141
|
+
expected_path6 = 'Phi[0, 4]/S[0, 4]/Alt(S => a T .)[0, 4]/T[1, 4]'
|
|
142
|
+
expect(path_to_s).to eq(expected_path6)
|
|
143
|
+
expect(subject.curr_parent.subnodes.size).to eq(1)
|
|
144
|
+
token_event6 = 'b[3, 4]'
|
|
145
|
+
expect(subject.curr_parent.subnodes.first.to_string(0)).to eq(token_event6)
|
|
146
|
+
|
|
147
|
+
event7 = walker.next
|
|
148
|
+
subject.receive_event(*event7)
|
|
149
|
+
expect(event7[1].to_s).to eq('T => b b . b | 1')
|
|
150
|
+
expect(subject.curr_parent.to_string(0)).to eq('T[1, 4]')
|
|
151
|
+
expected_path7 = 'Phi[0, 4]/S[0, 4]/Alt(S => a T .)[0, 4]/T[1, 4]'
|
|
152
|
+
expect(path_to_s).to eq(expected_path7)
|
|
153
|
+
expect(subject.curr_parent.subnodes.size).to eq(2)
|
|
154
|
+
token_event7 = 'b[2, 3]'
|
|
155
|
+
expect(subject.curr_parent.subnodes.first.to_string(0)).to eq(token_event7)
|
|
156
|
+
|
|
157
|
+
event8 = walker.next
|
|
158
|
+
subject.receive_event(*event8)
|
|
159
|
+
expect(event8[1].to_s).to eq('T => b . b b | 1')
|
|
160
|
+
expect(subject.curr_parent.to_string(0)).to eq('T[1, 4]')
|
|
161
|
+
expected_path8 = 'Phi[0, 4]/S[0, 4]/Alt(S => a T .)[0, 4]/T[1, 4]'
|
|
162
|
+
expect(path_to_s).to eq(expected_path8)
|
|
163
|
+
expect(subject.curr_parent.subnodes.size).to eq(3)
|
|
164
|
+
token_event8 = 'b[1, 2]'
|
|
165
|
+
expect(subject.curr_parent.subnodes.first.to_string(0)).to eq(token_event8)
|
|
166
|
+
|
|
167
|
+
event9 = walker.next
|
|
168
|
+
subject.receive_event(*event9)
|
|
169
|
+
expect(event9[1].to_s).to eq('T => . b b b | 1')
|
|
170
|
+
expect(subject.curr_parent.to_string(0)).to eq('T[1, 4]')
|
|
171
|
+
expected_path9 = 'Phi[0, 4]/S[0, 4]/Alt(S => a T .)[0, 4]/T[1, 4]'
|
|
172
|
+
expect(path_to_s).to eq(expected_path9)
|
|
173
|
+
|
|
174
|
+
event10 = walker.next
|
|
175
|
+
subject.receive_event(*event10)
|
|
176
|
+
expect(event10[1].to_s).to eq('.T | 1')
|
|
177
|
+
expect(subject.curr_parent.to_string(0)).to eq('Alt(S => a T .)[0, 4]')
|
|
178
|
+
expected_path10 = 'Phi[0, 4]/S[0, 4]/Alt(S => a T .)[0, 4]'
|
|
179
|
+
expect(path_to_s).to eq(expected_path10)
|
|
180
|
+
|
|
181
|
+
event11 = walker.next
|
|
182
|
+
subject.receive_event(*event11)
|
|
183
|
+
expect(event11[1].to_s).to eq('S => a . T | 0')
|
|
184
|
+
expect(subject.curr_parent.to_string(0)).to eq('Alt(S => a T .)[0, 4]')
|
|
185
|
+
expected_path11 = 'Phi[0, 4]/S[0, 4]/Alt(S => a T .)[0, 4]'
|
|
186
|
+
expect(path_to_s).to eq(expected_path11)
|
|
187
|
+
expect(subject.curr_parent.subnodes.size).to eq(2)
|
|
188
|
+
token_event11 = 'a[0, 1]'
|
|
189
|
+
expect(subject.curr_parent.subnodes.first.to_string(0)).to eq(token_event11)
|
|
190
|
+
|
|
191
|
+
event12 = walker.next
|
|
192
|
+
subject.receive_event(*event12)
|
|
193
|
+
expect(event12[1].to_s).to eq('S => . a T | 0')
|
|
194
|
+
expect(subject.curr_parent.to_string(0)).to eq('Alt(S => a T .)[0, 4]')
|
|
195
|
+
expected_path12 = 'Phi[0, 4]/S[0, 4]/Alt(S => a T .)[0, 4]'
|
|
196
|
+
expect(path_to_s).to eq(expected_path12)
|
|
197
|
+
expect(subject.curr_parent.subnodes.size).to eq(2) # Is this OK?
|
|
198
|
+
|
|
199
|
+
# Pop all Alternative nodes until no Alternative found, pop this last too
|
|
200
|
+
event13 = walker.next
|
|
201
|
+
subject.receive_event(*event13)
|
|
202
|
+
expect(event13[1].to_s).to eq('.S | 0')
|
|
203
|
+
expect(subject.curr_parent.to_string(0)).to eq('Phi[0, 4]')
|
|
204
|
+
expected_path13 = 'Phi[0, 4]'
|
|
205
|
+
expect(path_to_s).to eq(expected_path13)
|
|
206
|
+
|
|
207
|
+
event14 = walker.next
|
|
208
|
+
subject.receive_event(*event14)
|
|
209
|
+
expect(event14[1].to_s).to eq('Phi => . S | 0')
|
|
210
|
+
expect(subject.curr_parent.to_string(0)).to eq('Phi[0, 4]')
|
|
211
|
+
expected_path14 = 'Phi[0, 4]'
|
|
212
|
+
expect(path_to_s).to eq(expected_path14)
|
|
213
|
+
|
|
214
|
+
event15 = walker.next
|
|
215
|
+
subject.receive_event(*event15)
|
|
216
|
+
expect(event15[1].to_s).to eq('.Phi | 0')
|
|
217
|
+
expect(path_to_s).to be_empty
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
it 'should handle backtracking' do
|
|
221
|
+
15.times do
|
|
222
|
+
event = walker.next
|
|
223
|
+
subject.receive_event(*event)
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
event16 = walker.next
|
|
227
|
+
subject.receive_event(*event16)
|
|
228
|
+
expect(event16[0]).to eq(:backtrack) # Backtrack event!
|
|
229
|
+
expect(event16[1].to_s).to eq('S. | 0')
|
|
230
|
+
# A new alternative node must be created
|
|
231
|
+
expect(subject.curr_parent.to_string(0)).to eq('Alt(S => A T .)[0, 4]')
|
|
232
|
+
expected_path16 = 'Phi[0, 4]/S[0, 4]/Alt(S => A T .)[0, 4]'
|
|
233
|
+
expect(path_to_s).to eq(expected_path16)
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
event17 = walker.next
|
|
237
|
+
subject.receive_event(*event17)
|
|
238
|
+
expect(event17[1].to_s).to eq('S => A T . | 0')
|
|
239
|
+
expect(subject.curr_parent.to_string(0)).to eq('T[?, 4]')
|
|
240
|
+
expected_path17 = 'Phi[0, 4]/S[0, 4]/Alt(S => A T .)[0, 4]/T[?, 4]'
|
|
241
|
+
expect(path_to_s).to eq(expected_path17)
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
it 'should detect second time visit of an entry' do
|
|
245
|
+
17.times do
|
|
246
|
+
event = walker.next
|
|
247
|
+
subject.receive_event(*event)
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
event18 = walker.next
|
|
251
|
+
subject.receive_event(*event18)
|
|
252
|
+
expect(event18[0]).to eq(:revisit) # Revisit event!
|
|
253
|
+
expect(event18[1].to_s).to eq('T. | 1')
|
|
254
|
+
expect(subject.curr_parent.to_string(0)).to eq('Alt(S => A T .)[0, 4]')
|
|
255
|
+
expected_path18 = 'Phi[0, 4]/S[0, 4]/Alt(S => A T .)[0, 4]'
|
|
256
|
+
expect(path_to_s).to eq(expected_path18)
|
|
257
|
+
|
|
258
|
+
event19 = walker.next
|
|
259
|
+
subject.receive_event(*event19)
|
|
260
|
+
expect(event19[1].to_s).to eq('S => A . T | 0')
|
|
261
|
+
expect(subject.curr_parent.to_string(0)).to eq('A[?, 1]')
|
|
262
|
+
expected_path19 = 'Phi[0, 4]/S[0, 4]/Alt(S => A T .)[0, 4]/A[?, 1]'
|
|
263
|
+
expect(path_to_s).to eq(expected_path19)
|
|
264
|
+
|
|
265
|
+
event20 = walker.next
|
|
266
|
+
subject.receive_event(*event20)
|
|
267
|
+
# Next entry is an end entry...
|
|
268
|
+
expect(event20[1].to_s).to eq('A. | 0')
|
|
269
|
+
# ... with multiple antecedents => alternative nodes required
|
|
270
|
+
expect(subject.curr_parent.to_string(0)).to eq('Alt(A => a .)[0, 1]')
|
|
271
|
+
expected_path20 = 'Phi[0, 4]/S[0, 4]/Alt(S => A T .)[0, 4]/A[0, 1]/Alt(A => a .)[0, 1]'
|
|
272
|
+
expect(path_to_s).to eq(expected_path20)
|
|
273
|
+
expect(subject.curr_path[-2].refinement).to eq(:or)
|
|
274
|
+
|
|
275
|
+
event21 = walker.next
|
|
276
|
+
subject.receive_event(*event21)
|
|
277
|
+
expect(event21[1].to_s).to eq('A => a . | 0')
|
|
278
|
+
expect(subject.curr_parent.to_string(0)).to eq('Alt(A => a .)[0, 1]')
|
|
279
|
+
expected_path21 = expected_path20
|
|
280
|
+
expect(path_to_s).to eq(expected_path21)
|
|
281
|
+
|
|
282
|
+
event22 = walker.next
|
|
283
|
+
subject.receive_event(*event22)
|
|
284
|
+
expect(event22[1].to_s).to eq('A => . a | 0')
|
|
285
|
+
expect(subject.curr_parent.to_string(0)).to eq('Alt(A => a .)[0, 1]')
|
|
286
|
+
expected_path22 = expected_path20
|
|
287
|
+
expect(path_to_s).to eq(expected_path22)
|
|
288
|
+
|
|
289
|
+
event23 = walker.next
|
|
290
|
+
subject.receive_event(*event23)
|
|
291
|
+
# Next entry is an start entry...
|
|
292
|
+
expect(event23[1].to_s).to eq('.A | 0')
|
|
293
|
+
# ... with multiple antecedents => alternative nodes required
|
|
294
|
+
expect(subject.curr_parent.to_string(0)).to eq('Alt(S => A T .)[0, 4]')
|
|
295
|
+
expected_path23 = 'Phi[0, 4]/S[0, 4]/Alt(S => A T .)[0, 4]'
|
|
296
|
+
expect(path_to_s).to eq(expected_path23)
|
|
297
|
+
|
|
298
|
+
event24 = walker.next
|
|
299
|
+
subject.receive_event(*event24)
|
|
300
|
+
expect(event24[1].to_s).to eq('S => . A T | 0')
|
|
301
|
+
# ... with multiple antecedents => alternative nodes required
|
|
302
|
+
expect(subject.curr_parent.to_string(0)).to eq('Alt(S => A T .)[0, 4]')
|
|
303
|
+
expected_path24 = expected_path23
|
|
304
|
+
expect(path_to_s).to eq(expected_path24)
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
it 'should handle remaining events' do
|
|
308
|
+
24.times do
|
|
309
|
+
event = walker.next
|
|
310
|
+
subject.receive_event(*event)
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
event25 = walker.next
|
|
314
|
+
subject.receive_event(*event25)
|
|
315
|
+
expect(event25[0]).to eq(:backtrack) # Backtrack event!
|
|
316
|
+
expect(event25[1].to_s).to eq('A. | 0')
|
|
317
|
+
expect(subject.curr_parent.to_string(0)).to eq('Alt(A => B A .)[0, 1]')
|
|
318
|
+
expected_path25 = 'Phi[0, 4]/S[0, 4]/Alt(S => A T .)[0, 4]/A[0, 1]/Alt(A => B A .)[0, 1]'
|
|
319
|
+
expect(path_to_s).to eq(expected_path25)
|
|
320
|
+
|
|
321
|
+
event26 = walker.next
|
|
322
|
+
subject.receive_event(*event26)
|
|
323
|
+
expect(event26[1].to_s).to eq('A => B A . | 0')
|
|
324
|
+
expect(subject.curr_parent.to_string(0)).to eq('A[?, 1]')
|
|
325
|
+
expected_path26 = 'Phi[0, 4]/S[0, 4]/Alt(S => A T .)[0, 4]/A[0, 1]/Alt(A => B A .)[0, 1]/A[?, 1]'
|
|
326
|
+
expect(path_to_s).to eq(expected_path26)
|
|
327
|
+
|
|
328
|
+
event27 = walker.next
|
|
329
|
+
subject.receive_event(*event27)
|
|
330
|
+
expect(event27[0]).to eq(:revisit) # Revisit event!
|
|
331
|
+
expect(event27[1].to_s).to eq('A. | 0')
|
|
332
|
+
expect(subject.curr_parent.to_string(0)).to eq('Alt(A => B A .)[0, 1]')
|
|
333
|
+
expected_path27 = 'Phi[0, 4]/S[0, 4]/Alt(S => A T .)[0, 4]/A[0, 1]/Alt(A => B A .)[0, 1]'
|
|
334
|
+
expect(path_to_s).to eq(expected_path27)
|
|
335
|
+
|
|
336
|
+
event28 = walker.next
|
|
337
|
+
subject.receive_event(*event28)
|
|
338
|
+
expect(event28[0]).to eq(:visit)
|
|
339
|
+
expect(event28[1].to_s).to eq('A => B . A | 0')
|
|
340
|
+
expect(subject.curr_parent.to_string(0)).to eq('B[?, 0]')
|
|
341
|
+
expected_path28 = 'Phi[0, 4]/S[0, 4]/Alt(S => A T .)[0, 4]/A[0, 1]/Alt(A => B A .)[0, 1]/B[?, 0]'
|
|
342
|
+
expect(path_to_s).to eq(expected_path28)
|
|
343
|
+
|
|
344
|
+
event29 = walker.next
|
|
345
|
+
subject.receive_event(*event29)
|
|
346
|
+
expect(event29[0]).to eq(:visit)
|
|
347
|
+
expect(event29[1].to_s).to eq('B. | 0')
|
|
348
|
+
expect(subject.curr_parent.to_string(0)).to eq('B[0, 0]')
|
|
349
|
+
expected_path29 = 'Phi[0, 4]/S[0, 4]/Alt(S => A T .)[0, 4]/A[0, 1]/Alt(A => B A .)[0, 1]/B[0, 0]'
|
|
350
|
+
expect(path_to_s).to eq(expected_path29)
|
|
351
|
+
|
|
352
|
+
event30 = walker.next
|
|
353
|
+
subject.receive_event(*event30)
|
|
354
|
+
expect(event30[0]).to eq(:visit)
|
|
355
|
+
# Empty production!
|
|
356
|
+
expect(event30[1].to_s).to eq('B => . | 0')
|
|
357
|
+
expect(subject.curr_parent.to_string(0)).to eq('B[0, 0]')
|
|
358
|
+
expected_path30 = 'Phi[0, 4]/S[0, 4]/Alt(S => A T .)[0, 4]/A[0, 1]/Alt(A => B A .)[0, 1]/B[0, 0]'
|
|
359
|
+
expect(path_to_s).to eq(expected_path30)
|
|
360
|
+
|
|
361
|
+
event31 = walker.next
|
|
362
|
+
subject.receive_event(*event31)
|
|
363
|
+
expect(event31[0]).to eq(:visit)
|
|
364
|
+
expect(event31[1].to_s).to eq('.B | 0')
|
|
365
|
+
expect(subject.curr_parent.to_string(0)).to eq('Alt(A => B A .)[0, 1]')
|
|
366
|
+
expected_path31 = 'Phi[0, 4]/S[0, 4]/Alt(S => A T .)[0, 4]/A[0, 1]/Alt(A => B A .)[0, 1]'
|
|
367
|
+
expect(path_to_s).to eq(expected_path31)
|
|
368
|
+
|
|
369
|
+
event32 = walker.next
|
|
370
|
+
subject.receive_event(*event32)
|
|
371
|
+
expect(event32[0]).to eq(:visit)
|
|
372
|
+
expect(event32[1].to_s).to eq('A => . B A | 0')
|
|
373
|
+
expect(subject.curr_parent.to_string(0)).to eq('Alt(A => B A .)[0, 1]')
|
|
374
|
+
expected_path32 = 'Phi[0, 4]/S[0, 4]/Alt(S => A T .)[0, 4]/A[0, 1]/Alt(A => B A .)[0, 1]'
|
|
375
|
+
expect(path_to_s).to eq(expected_path32)
|
|
376
|
+
end
|
|
377
|
+
end # context
|
|
378
|
+
|
|
379
|
+
end # describe
|
|
380
|
+
end # module
|
|
381
|
+
end # module
|
|
382
|
+
# End of file
|