rley 0.2.15 → 0.3.00
Sign up to get free protection for your applications and to get access to all the features.
- 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,43 @@
|
|
1
|
+
require_relative '../../spec_helper'
|
2
|
+
require_relative '../../../lib/rley/syntax/non_terminal'
|
3
|
+
|
4
|
+
# Load the class under test
|
5
|
+
require_relative '../../../lib/rley/gfg/shortcut_edge'
|
6
|
+
|
7
|
+
module Rley # Open this namespace to avoid module qualifier prefixes
|
8
|
+
module GFG # Open this namespace to avoid module qualifier prefixes
|
9
|
+
describe Edge do
|
10
|
+
let(:nt_b_sequence) { Rley::Syntax::NonTerminal.new('b_sequence') }
|
11
|
+
|
12
|
+
let(:vertex1) { double('fake_vertex1') }
|
13
|
+
let(:vertex2) { double('fake_vertex2') }
|
14
|
+
subject { ShortcutEdge.new(vertex1, vertex2) }
|
15
|
+
|
16
|
+
context 'Initialization:' do
|
17
|
+
it 'should be created with two vertice arguments & a non-terminal' do
|
18
|
+
allow(vertex1).to receive(:shortcut=)
|
19
|
+
allow(vertex1).to receive(:next_symbol).and_return(nt_b_sequence)
|
20
|
+
|
21
|
+
expect { ShortcutEdge.new(vertex1, vertex2) }
|
22
|
+
.not_to raise_error
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'should know the successor vertex' do
|
26
|
+
allow(vertex1).to receive(:shortcut=)
|
27
|
+
allow(vertex1).to receive(:next_symbol).and_return(nt_b_sequence)
|
28
|
+
|
29
|
+
expect(subject.successor).to eq(vertex2)
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'should know the related terminal' do
|
33
|
+
allow(vertex1).to receive(:shortcut=)
|
34
|
+
allow(vertex1).to receive(:next_symbol).and_return(nt_b_sequence)
|
35
|
+
|
36
|
+
expect(subject.nonterminal).to eq(nt_b_sequence)
|
37
|
+
end
|
38
|
+
end # context
|
39
|
+
end # describe
|
40
|
+
end # module
|
41
|
+
end # module
|
42
|
+
|
43
|
+
# End of file
|
@@ -1,38 +1,53 @@
|
|
1
|
-
require_relative '../../spec_helper'
|
2
|
-
|
3
|
-
# Load the class under test
|
4
|
-
require_relative '../../../lib/rley/gfg/vertex'
|
5
|
-
|
6
|
-
module Rley # Open this namespace to avoid module qualifier prefixes
|
7
|
-
module GFG # Open this namespace to avoid module qualifier prefixes
|
8
|
-
describe Vertex do
|
9
|
-
subject { Vertex.new() }
|
10
|
-
|
11
|
-
context 'Initialization:' do
|
12
|
-
it 'should be created without argument' do
|
13
|
-
expect { Vertex.new() }.not_to raise_error
|
14
|
-
end
|
15
|
-
|
16
|
-
it "shouldn't have edges at start" do
|
17
|
-
expect(subject.edges.empty?).to eq(true)
|
18
|
-
end
|
19
|
-
end # context
|
20
|
-
|
21
|
-
context 'Provided services:' do
|
22
|
-
it 'should
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
expect(subject.
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
expect(subject.
|
32
|
-
end
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
1
|
+
require_relative '../../spec_helper'
|
2
|
+
|
3
|
+
# Load the class under test
|
4
|
+
require_relative '../../../lib/rley/gfg/vertex'
|
5
|
+
|
6
|
+
module Rley # Open this namespace to avoid module qualifier prefixes
|
7
|
+
module GFG # Open this namespace to avoid module qualifier prefixes
|
8
|
+
describe Vertex do
|
9
|
+
subject { Vertex.new() }
|
10
|
+
|
11
|
+
context 'Initialization:' do
|
12
|
+
it 'should be created without argument' do
|
13
|
+
expect { Vertex.new() }.not_to raise_error
|
14
|
+
end
|
15
|
+
|
16
|
+
it "shouldn't have edges at start" do
|
17
|
+
expect(subject.edges.empty?).to eq(true)
|
18
|
+
end
|
19
|
+
end # context
|
20
|
+
|
21
|
+
context 'Provided services:' do
|
22
|
+
it 'should know whether it has a dot at the end of the rhs' do
|
23
|
+
expect(subject).not_to be_complete
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should know the previous symbol (if any) in the rhs' do
|
27
|
+
expect(subject.prev_symbol).to be_nil
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'should know the next symbol (if any) in the rhs' do
|
31
|
+
expect(subject.next_symbol).to be_nil
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'should accept at most one new edge' do
|
35
|
+
edge1 = double('fake-edge1')
|
36
|
+
edge2 = double('fake-edge2')
|
37
|
+
|
38
|
+
expect { subject.add_edge(edge1) }.not_to raise_error
|
39
|
+
expect(subject.edges.size).to eq(1)
|
40
|
+
expect(subject.edges.last).to eq(edge1)
|
41
|
+
|
42
|
+
err = StandardError
|
43
|
+
msg = 'At most one edge accepted'
|
44
|
+
expect { subject.add_edge(edge2) }.to raise_error err, msg
|
45
|
+
expect(subject.edges.size).to eq(1)
|
46
|
+
expect(subject.edges.last).to eq(edge1)
|
47
|
+
end
|
48
|
+
end # context
|
49
|
+
end # describe
|
50
|
+
end # module
|
51
|
+
end # module
|
52
|
+
|
38
53
|
# End of file
|
@@ -0,0 +1,238 @@
|
|
1
|
+
require_relative '../spec_helper'
|
2
|
+
|
3
|
+
require_relative './support/grammar_helper'
|
4
|
+
require_relative './support/grammar_sppf_helper'
|
5
|
+
require_relative '../../lib/rley/parser/token'
|
6
|
+
require_relative '../../lib/rley/parser/gfg_earley_parser'
|
7
|
+
require_relative '../../lib/rley/sppf/non_terminal_node'
|
8
|
+
require_relative '../../lib/rley/sppf/parse_forest'
|
9
|
+
|
10
|
+
# Load the class under test
|
11
|
+
require_relative '../../lib/rley/parse_forest_visitor'
|
12
|
+
|
13
|
+
module Rley # Open this namespace to avoid module qualifier prefixes
|
14
|
+
describe ParseForestVisitor do
|
15
|
+
include GrammarSPPFHelper # Mix-in module with builder for grammar sppf
|
16
|
+
include GrammarHelper # Mix-in with token factory method
|
17
|
+
|
18
|
+
# Assumption the aParseEntry corresponds to an end GFG node
|
19
|
+
def create_non_terminal_node(aParseEntry, aRange)
|
20
|
+
a_vertex = aParseEntry.vertex
|
21
|
+
return Rley::SPPF::NonTerminalNode.new(a_vertex.non_terminal, aRange)
|
22
|
+
end
|
23
|
+
|
24
|
+
let(:grammar_sppf) do
|
25
|
+
builder = grammar_sppf_builder
|
26
|
+
builder.grammar
|
27
|
+
end
|
28
|
+
|
29
|
+
let(:sample_tokens) do
|
30
|
+
build_token_sequence(%w(a b b b), grammar_sppf)
|
31
|
+
end
|
32
|
+
|
33
|
+
# A forest with just a root node
|
34
|
+
let(:rooted_forest) do
|
35
|
+
parser = Parser::GFGEarleyParser.new(grammar_sppf)
|
36
|
+
parse_result = parser.parse(sample_tokens)
|
37
|
+
full_range = { low: 0, high: parse_result.chart.last_index }
|
38
|
+
root_node = create_non_terminal_node(parse_result.accepting_entry, full_range)
|
39
|
+
Rley::SPPF::ParseForest.new(root_node)
|
40
|
+
end
|
41
|
+
|
42
|
+
=begin
|
43
|
+
# Factory method that builds a sample parse forest.
|
44
|
+
# Generated forest has the following structure:
|
45
|
+
# S[0,5]
|
46
|
+
# +- A[0,5]
|
47
|
+
# +- a[0,0]
|
48
|
+
# +- A[1,4]
|
49
|
+
# | +- a[1,1]
|
50
|
+
# | +- A[2,3]
|
51
|
+
# | | +- b[2,3]
|
52
|
+
# | +- c[3,4]
|
53
|
+
# +- c[4,5]
|
54
|
+
# Capital letters represent non-terminal nodes
|
55
|
+
let(:grm_abc_pforest1) do
|
56
|
+
parser = Parser::EarleyParser.new(grammar_abc)
|
57
|
+
parse_result = parser.parse(grm_abc_tokens1)
|
58
|
+
parse_result.parse_forest
|
59
|
+
end
|
60
|
+
=end
|
61
|
+
|
62
|
+
# Default instantiation rule
|
63
|
+
subject { ParseForestVisitor.new(rooted_forest) }
|
64
|
+
|
65
|
+
|
66
|
+
context 'Standard creation & initialization:' do
|
67
|
+
it 'should be initialized with a parse forest argument' do
|
68
|
+
expect { ParseForestVisitor.new(rooted_forest) }.not_to raise_error
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'should know the parse forest to visit' do
|
72
|
+
expect(subject.pforest).to eq(rooted_forest)
|
73
|
+
end
|
74
|
+
|
75
|
+
it "shouldn't have subscribers at start" do
|
76
|
+
expect(subject.subscribers).to be_empty
|
77
|
+
end
|
78
|
+
end # context
|
79
|
+
|
80
|
+
|
81
|
+
context 'Subscribing:' do
|
82
|
+
let(:listener1) { double('fake-subscriber1') }
|
83
|
+
let(:listener2) { double('fake-subscriber2') }
|
84
|
+
|
85
|
+
it 'should allow subscriptions' do
|
86
|
+
subject.subscribe(listener1)
|
87
|
+
expect(subject.subscribers.size).to eq(1)
|
88
|
+
expect(subject.subscribers).to eq([listener1])
|
89
|
+
|
90
|
+
subject.subscribe(listener2)
|
91
|
+
expect(subject.subscribers.size).to eq(2)
|
92
|
+
expect(subject.subscribers).to eq([listener1, listener2])
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'should allow un-subcriptions' do
|
96
|
+
subject.subscribe(listener1)
|
97
|
+
subject.subscribe(listener2)
|
98
|
+
subject.unsubscribe(listener2)
|
99
|
+
expect(subject.subscribers.size).to eq(1)
|
100
|
+
expect(subject.subscribers).to eq([listener1])
|
101
|
+
subject.unsubscribe(listener1)
|
102
|
+
expect(subject.subscribers).to be_empty
|
103
|
+
end
|
104
|
+
end # context
|
105
|
+
|
106
|
+
|
107
|
+
context 'Notifying visit events:' do
|
108
|
+
# Use doubles/mocks to simulate subscribers
|
109
|
+
let(:listener1) { double('fake-subscriber1') }
|
110
|
+
let(:listener2) { double('fake-subscriber2') }
|
111
|
+
|
112
|
+
it 'should react to the start_visit_pforest message' do
|
113
|
+
subject.subscribe(listener1)
|
114
|
+
|
115
|
+
# Notify subscribers when start the visit of the pforest
|
116
|
+
expect(listener1).to receive(:before_pforest).with(rooted_forest)
|
117
|
+
subject.start_visit_pforest(rooted_forest)
|
118
|
+
end
|
119
|
+
=begin
|
120
|
+
# Default instantiation rule
|
121
|
+
subject do
|
122
|
+
instance = ParseForestVisitor.new(grm_abc_pforest1)
|
123
|
+
instance.subscribe(listener1)
|
124
|
+
instance
|
125
|
+
end
|
126
|
+
|
127
|
+
|
128
|
+
|
129
|
+
# Sample non-terminal node
|
130
|
+
let(:nterm_node) do
|
131
|
+
first_big_a = grm_abc_pforest1.root.children[0]
|
132
|
+
second_big_a = first_big_a.children[1]
|
133
|
+
second_big_a.children[1]
|
134
|
+
end
|
135
|
+
|
136
|
+
# Sample terminal node
|
137
|
+
let(:term_node) { nterm_node.children[0] }
|
138
|
+
|
139
|
+
it 'should react to the start_visit_pforest message' do
|
140
|
+
# Notify subscribers when start the visit of the pforest
|
141
|
+
expect(listener1).to receive(:before_pforest).with(grm_abc_pforest1)
|
142
|
+
subject.start_visit_pforest(grm_abc_pforest1)
|
143
|
+
end
|
144
|
+
|
145
|
+
it 'should react to the start_visit_nonterminal message' do
|
146
|
+
# Notify subscribers when start the visit of a non-terminal node
|
147
|
+
expect(listener1).to receive(:before_non_terminal).with(nterm_node)
|
148
|
+
subject.visit_nonterminal(nterm_node)
|
149
|
+
end
|
150
|
+
|
151
|
+
it 'should react to the visit_children message' do
|
152
|
+
# Notify subscribers when start the visit of children nodes
|
153
|
+
children = nterm_node.children
|
154
|
+
args = [nterm_node, children]
|
155
|
+
expect(listener1).to receive(:before_children).with(*args)
|
156
|
+
expect(listener1).to receive(:before_terminal).with(children[0])
|
157
|
+
expect(listener1).to receive(:after_terminal).with(children[0])
|
158
|
+
expect(listener1).to receive(:after_children).with(nterm_node, children)
|
159
|
+
subject.send(:traverse_children, nterm_node)
|
160
|
+
end
|
161
|
+
|
162
|
+
it 'should react to the end_visit_nonterminal message' do
|
163
|
+
# Notify subscribers when ending the visit of a non-terminal node
|
164
|
+
expect(listener1).to receive(:after_non_terminal).with(nterm_node)
|
165
|
+
subject.end_visit_nonterminal(nterm_node)
|
166
|
+
end
|
167
|
+
|
168
|
+
it 'should react to the visit_terminal message' do
|
169
|
+
# Notify subscribers when start & ending the visit of a terminal node
|
170
|
+
expect(listener1).to receive(:before_terminal).with(term_node)
|
171
|
+
expect(listener1).to receive(:after_terminal).with(term_node)
|
172
|
+
subject.visit_terminal(term_node)
|
173
|
+
end
|
174
|
+
|
175
|
+
it 'should react to the end_visit_pforest message' do
|
176
|
+
# Notify subscribers when ending the visit of the pforest
|
177
|
+
expect(listener1).to receive(:after_pforest).with(grm_abc_pforest1)
|
178
|
+
subject.end_visit_pforest(grm_abc_pforest1)
|
179
|
+
end
|
180
|
+
|
181
|
+
it 'should begin the visit when requested' do
|
182
|
+
# Reminder: parse forest structure is
|
183
|
+
# S[0,5]
|
184
|
+
# +- A[0,5]
|
185
|
+
# +- a[0,0]
|
186
|
+
# +- A[1,4]
|
187
|
+
# | +- a[1,1]
|
188
|
+
# | +- A[2,3]
|
189
|
+
# | | +- b[2,3]
|
190
|
+
# | +- c[3,4]
|
191
|
+
# +- c[4,5]
|
192
|
+
root = grm_abc_pforest1.root
|
193
|
+
children = root.children
|
194
|
+
big_a_1 = children[0]
|
195
|
+
big_a_1_children = big_a_1.children
|
196
|
+
big_a_2 = big_a_1_children[1]
|
197
|
+
big_a_2_children = big_a_2.children
|
198
|
+
big_a_3 = big_a_2_children[1]
|
199
|
+
big_a_3_children = big_a_3.children
|
200
|
+
expectations = [
|
201
|
+
[:before_pforest, [grm_abc_pforest1]],
|
202
|
+
[:before_non_terminal, [root]],
|
203
|
+
[:before_children, [root, children]],
|
204
|
+
[:before_non_terminal, [big_a_1]],
|
205
|
+
[:before_children, [big_a_1, big_a_1_children]],
|
206
|
+
[:before_terminal, [big_a_1_children[0]]],
|
207
|
+
[:after_terminal, [big_a_1_children[0]]],
|
208
|
+
[:before_non_terminal, [big_a_2]],
|
209
|
+
[:before_children, [big_a_2, big_a_2_children]],
|
210
|
+
[:before_terminal, [big_a_2_children[0]]],
|
211
|
+
[:after_terminal, [big_a_2_children[0]]],
|
212
|
+
[:before_non_terminal, [big_a_3]],
|
213
|
+
[:before_children, [big_a_3, big_a_3_children]],
|
214
|
+
[:before_terminal, [big_a_3_children[0]]],
|
215
|
+
[:after_terminal, [big_a_3_children[0]]],
|
216
|
+
[:after_children, [big_a_3, big_a_3_children]],
|
217
|
+
[:before_terminal, [big_a_2_children[2]]],
|
218
|
+
[:after_terminal, [big_a_2_children[2]]],
|
219
|
+
[:after_children, [big_a_2, big_a_2_children]],
|
220
|
+
[:before_terminal, [big_a_1_children[2]]],
|
221
|
+
[:after_terminal, [big_a_1_children[2]]],
|
222
|
+
[:after_children, [big_a_1, big_a_1_children]],
|
223
|
+
[:after_children, [root, children]],
|
224
|
+
[:after_pforest, [grm_abc_pforest1]]
|
225
|
+
]
|
226
|
+
expectations.each do |(msg, args)|
|
227
|
+
allow(listener1).to receive(msg).with(*args).ordered
|
228
|
+
end
|
229
|
+
|
230
|
+
# Here we go...
|
231
|
+
subject.start
|
232
|
+
end
|
233
|
+
=end
|
234
|
+
end # context
|
235
|
+
end # describe
|
236
|
+
end # module
|
237
|
+
|
238
|
+
# End of file
|
@@ -91,36 +91,57 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
91
91
|
|
92
92
|
it 'should know the symbol before the dot' do
|
93
93
|
expect(subject.prev_symbol).to eq(t_a)
|
94
|
-
|
94
|
+
|
95
95
|
# Case of an empty production
|
96
96
|
instance = DottedItem.new(empty_prod, 0)
|
97
97
|
expect(instance.prev_symbol).to be_nil
|
98
|
-
|
98
|
+
|
99
99
|
# Case of a dot at start position
|
100
100
|
instance = DottedItem.new(sample_prod, 0)
|
101
|
-
expect(instance.prev_symbol).to be_nil
|
101
|
+
expect(instance.prev_symbol).to be_nil
|
102
102
|
end
|
103
103
|
|
104
104
|
it 'should know the symbol after the dot' do
|
105
105
|
expect(subject.next_symbol).to eq(t_b)
|
106
106
|
end
|
107
|
-
|
107
|
+
|
108
108
|
it 'should calculate the previous position of the dot' do
|
109
109
|
expect(subject.prev_position).to eq(0)
|
110
|
-
|
110
|
+
|
111
111
|
# Case of an empty production
|
112
112
|
instance = DottedItem.new(empty_prod, 0)
|
113
113
|
expect(instance.prev_position).to be_nil
|
114
|
-
|
114
|
+
|
115
115
|
# Case of a dot at start position
|
116
116
|
instance = DottedItem.new(sample_prod, 0)
|
117
|
-
expect(instance.prev_position).to be_nil
|
117
|
+
expect(instance.prev_position).to be_nil
|
118
118
|
|
119
119
|
# Case of single symbol production
|
120
120
|
instance = DottedItem.new(other_prod, 1)
|
121
|
-
expect(instance.prev_position).to eq(0)
|
121
|
+
expect(instance.prev_position).to eq(0)
|
122
122
|
end
|
123
123
|
|
124
|
+
it 'should determine if it is a successor of another dotted item' do
|
125
|
+
expect(subject).not_to be_successor_of(subject)
|
126
|
+
|
127
|
+
# Case: different productions
|
128
|
+
instance = DottedItem.new(empty_prod, 0)
|
129
|
+
expect(subject).not_to be_successor_of(subject)
|
130
|
+
|
131
|
+
# Case: one position difference
|
132
|
+
instance = DottedItem.new(sample_prod, 0)
|
133
|
+
expect(subject).to be_successor_of(instance)
|
134
|
+
expect(instance).not_to be_successor_of(subject)
|
135
|
+
|
136
|
+
# Case: more than one position difference
|
137
|
+
instance2 = DottedItem.new(sample_prod, 2)
|
138
|
+
expect(instance).not_to be_successor_of(instance2)
|
139
|
+
expect(subject).not_to be_successor_of(instance2)
|
140
|
+
expect(instance2).to be_successor_of(subject)
|
141
|
+
end
|
142
|
+
|
143
|
+
|
144
|
+
|
124
145
|
it 'should give its text representation' do
|
125
146
|
expectation = 'sentence => A . B C'
|
126
147
|
expect(subject.to_s).to eq(expectation)
|
@@ -0,0 +1,138 @@
|
|
1
|
+
require_relative '../../spec_helper'
|
2
|
+
require 'stringio'
|
3
|
+
|
4
|
+
require_relative '../../../lib/rley/syntax/terminal'
|
5
|
+
require_relative '../../../lib/rley/parser/token'
|
6
|
+
require_relative '../../../lib/rley/gfg/start_vertex'
|
7
|
+
require_relative '../../../lib/rley/parser/parse_entry'
|
8
|
+
require_relative '../../../lib/rley/parser/parse_tracer'
|
9
|
+
require_relative '../../../lib/rley/parser/grm_items_builder'
|
10
|
+
require_relative '../../../lib/rley/gfg/grm_flow_graph'
|
11
|
+
require_relative '../support/grammar_abc_helper'
|
12
|
+
|
13
|
+
|
14
|
+
# Load the class under test
|
15
|
+
require_relative '../../../lib/rley/parser/gfg_chart'
|
16
|
+
|
17
|
+
module Rley # Open this namespace to avoid module qualifier prefixes
|
18
|
+
module Parser # Open this namespace to avoid module qualifier prefixes
|
19
|
+
describe GFGChart do
|
20
|
+
include GrammarABCHelper # Mix-in module with builder for grammar abc
|
21
|
+
|
22
|
+
# Helper method. Create an array of dotted items
|
23
|
+
# from the given grammar
|
24
|
+
def build_items_for_grammar(aGrammar)
|
25
|
+
helper = Object.new
|
26
|
+
helper.extend(Parser::GrmItemsBuilder)
|
27
|
+
return helper.build_dotted_items(aGrammar)
|
28
|
+
end
|
29
|
+
|
30
|
+
let(:count_token) { 20 }
|
31
|
+
let(:output) { StringIO.new('', 'w') }
|
32
|
+
|
33
|
+
# Factory method. Build a production with the given sequence
|
34
|
+
# of symbols as its rhs.
|
35
|
+
let(:grammar_abc) do
|
36
|
+
builder = grammar_abc_builder
|
37
|
+
builder.grammar
|
38
|
+
end
|
39
|
+
|
40
|
+
let(:token_seq) do
|
41
|
+
literals = %w(a a b c c)
|
42
|
+
literals.map { |lexeme| Token.new(lexeme, nil) }
|
43
|
+
end
|
44
|
+
|
45
|
+
# Helper method. Create an array of dotted items
|
46
|
+
# from the abc grammar
|
47
|
+
let(:items_from_grammar) { build_items_for_grammar(grammar_abc) }
|
48
|
+
let(:sample_gfg) { GFG::GrmFlowGraph.new(items_from_grammar) }
|
49
|
+
let(:sample_tracer) { ParseTracer.new(0, output, token_seq) }
|
50
|
+
let(:sample_start_symbol) { sample_gfg.start_vertex.non_terminal }
|
51
|
+
|
52
|
+
|
53
|
+
# Default instantiation rule
|
54
|
+
subject { GFGChart.new(count_token, sample_gfg, sample_tracer) }
|
55
|
+
|
56
|
+
|
57
|
+
context 'Initialization:' do
|
58
|
+
it 'should be created with start vertex, token count, tracer' do
|
59
|
+
expect { GFGChart.new(count_token, sample_gfg, sample_tracer) }
|
60
|
+
.not_to raise_error
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'should have correct entry set count' do
|
64
|
+
expect(subject.sets.size).to eq(count_token + 1)
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'should reference a tracer' do
|
68
|
+
expect(subject.tracer).to eq(sample_tracer)
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'should know the start symbol' do
|
72
|
+
expect(subject.start_symbol).to eq(sample_start_symbol)
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'should know the initial parse entry' do
|
76
|
+
expect(subject.initial_entry.vertex).to eq(sample_gfg.start_vertex)
|
77
|
+
expect(subject.initial_entry.origin).to eq(0)
|
78
|
+
end
|
79
|
+
=begin
|
80
|
+
|
81
|
+
it 'should know the start dotted rule' do
|
82
|
+
expect(subject.start_dotted_rule).to eq(dotted_rule)
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
=end
|
87
|
+
end # context
|
88
|
+
|
89
|
+
context 'Provided services:' do
|
90
|
+
=begin
|
91
|
+
let(:t_a) { Syntax::Terminal.new('a') }
|
92
|
+
let(:t_b) { Syntax::Terminal.new('b') }
|
93
|
+
let(:t_c) { Syntax::Terminal.new('c') }
|
94
|
+
let(:nt_sentence) { Syntax::NonTerminal.new('sentence') }
|
95
|
+
|
96
|
+
let(:sample_prod) do
|
97
|
+
Syntax::Production.new(nt_sentence, [t_a, t_b, t_c])
|
98
|
+
end
|
99
|
+
|
100
|
+
let(:origin_val) { 3 }
|
101
|
+
let(:dotted_rule) { DottedItem.new(sample_prod, 2) }
|
102
|
+
let(:complete_rule) { DottedItem.new(sample_prod, 3) }
|
103
|
+
let(:sample_parse_state) { ParseState.new(dotted_rule, origin_val) }
|
104
|
+
let(:sample_tracer) { ParseTracer.new(1, output, token_seq) }
|
105
|
+
|
106
|
+
# Factory method.
|
107
|
+
def parse_state(origin, aDottedRule)
|
108
|
+
ParseState.new(aDottedRule, origin)
|
109
|
+
end
|
110
|
+
|
111
|
+
|
112
|
+
it 'should trace its initialization' do
|
113
|
+
subject[0] # Force constructor call here
|
114
|
+
expectation = <<-SNIPPET
|
115
|
+
['I', 'saw', 'John', 'with', 'a', 'dog']
|
116
|
+
|. I . saw . John . with . a . dog .|
|
117
|
+
|> . . . . . .| [0:0] sentence => A B . C
|
118
|
+
SNIPPET
|
119
|
+
expect(output.string).to eq(expectation)
|
120
|
+
end
|
121
|
+
|
122
|
+
it 'should trace parse state pushing' do
|
123
|
+
subject[0] # Force constructor call here
|
124
|
+
output.string = ''
|
125
|
+
|
126
|
+
subject.push_state(dotted_rule, 3, 5, :prediction)
|
127
|
+
expectation = <<-SNIPPET
|
128
|
+
|. . . > .| [3:5] sentence => A B . C
|
129
|
+
SNIPPET
|
130
|
+
expect(output.string).to eq(expectation)
|
131
|
+
end
|
132
|
+
=end
|
133
|
+
end # context
|
134
|
+
end # describe
|
135
|
+
end # module
|
136
|
+
end # module
|
137
|
+
|
138
|
+
# End of file
|