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,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
|