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,918 @@
|
|
|
1
|
+
require_relative '../../spec_helper'
|
|
2
|
+
require 'stringio'
|
|
3
|
+
require_relative '../../../lib/rley/syntax/verbatim_symbol'
|
|
4
|
+
require_relative '../../../lib/rley/syntax/non_terminal'
|
|
5
|
+
require_relative '../../../lib/rley/syntax/production'
|
|
6
|
+
require_relative '../../../lib/rley/syntax/grammar_builder'
|
|
7
|
+
require_relative '../../../lib/rley/parser/token'
|
|
8
|
+
require_relative '../../../lib/rley/parser/dotted_item'
|
|
9
|
+
require_relative '../../../lib/rley/parser/gfg_parsing'
|
|
10
|
+
require_relative '../support/grammar_abc_helper'
|
|
11
|
+
require_relative '../support/ambiguous_grammar_helper'
|
|
12
|
+
require_relative '../support/grammar_helper'
|
|
13
|
+
require_relative '../support/expectation_helper'
|
|
14
|
+
|
|
15
|
+
# Load the class under test
|
|
16
|
+
require_relative '../../../lib/rley/parser/gfg_earley_parser'
|
|
17
|
+
|
|
18
|
+
module Rley # Open this namespace to avoid module qualifier prefixes
|
|
19
|
+
module Parser # Open this namespace to avoid module qualifier prefixes
|
|
20
|
+
describe GFGEarleyParser do
|
|
21
|
+
include GrammarABCHelper # Mix-in module with builder for grammar abc
|
|
22
|
+
include GrammarHelper # Mix-in with method for creating token sequence
|
|
23
|
+
include ExpectationHelper # Mix-in with expectation on parse entry sets
|
|
24
|
+
|
|
25
|
+
# Factory method. Build a production with the given sequence
|
|
26
|
+
# of symbols as its rhs.
|
|
27
|
+
let(:grammar_abc) do
|
|
28
|
+
builder = grammar_abc_builder
|
|
29
|
+
builder.grammar
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
let(:grm1_tokens) do
|
|
33
|
+
build_token_sequence(%w(a a b c c), grammar_abc)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
# Grammar 2: A simple arithmetic expression language
|
|
38
|
+
# (based on example in article on Earley's algorithm in Wikipedia)
|
|
39
|
+
# P ::= S.
|
|
40
|
+
# S ::= S "+" M.
|
|
41
|
+
# S ::= M.
|
|
42
|
+
# M ::= M "*" T.
|
|
43
|
+
# M ::= T.
|
|
44
|
+
# T ::= an integer number token.
|
|
45
|
+
# Let's create the grammar piece by piece
|
|
46
|
+
let(:nt_P) { Syntax::NonTerminal.new('P') }
|
|
47
|
+
let(:nt_S) { Syntax::NonTerminal.new('S') }
|
|
48
|
+
let(:nt_M) { Syntax::NonTerminal.new('M') }
|
|
49
|
+
let(:nt_T) { Syntax::NonTerminal.new('T') }
|
|
50
|
+
let(:plus) { Syntax::VerbatimSymbol.new('+') }
|
|
51
|
+
let(:star) { Syntax::VerbatimSymbol.new('*') }
|
|
52
|
+
let(:integer) do
|
|
53
|
+
integer_pattern = /[-+]?[0-9]+/ # Decimal notation
|
|
54
|
+
Syntax::Literal.new('integer', integer_pattern)
|
|
55
|
+
end
|
|
56
|
+
let(:prod_P) { Syntax::Production.new(nt_P, [nt_S]) }
|
|
57
|
+
let(:prod_S1) { Syntax::Production.new(nt_S, [nt_S, plus, nt_M]) }
|
|
58
|
+
let(:prod_S2) { Syntax::Production.new(nt_S, [nt_M]) }
|
|
59
|
+
let(:prod_M1) { Syntax::Production.new(nt_M, [nt_M, star, nt_T]) }
|
|
60
|
+
let(:prod_M2) { Syntax::Production.new(nt_M, [nt_T]) }
|
|
61
|
+
let(:prod_T) { Syntax::Production.new(nt_T, [integer]) }
|
|
62
|
+
let(:grammar_expr) do
|
|
63
|
+
all_prods = [prod_P, prod_S1, prod_S2, prod_M1, prod_M2, prod_T]
|
|
64
|
+
Syntax::Grammar.new(all_prods)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Helper method that mimicks the output of a tokenizer
|
|
68
|
+
# for the language specified by grammar_expr
|
|
69
|
+
def grm2_tokens()
|
|
70
|
+
input_sequence = [ {'2' => 'integer'}, '+', {'3' => 'integer'},
|
|
71
|
+
'*', {'4' => 'integer'}
|
|
72
|
+
]
|
|
73
|
+
return build_token_sequence(input_sequence, grammar_expr)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Default instantiation rule
|
|
77
|
+
subject { GFGEarleyParser.new(grammar_abc) }
|
|
78
|
+
|
|
79
|
+
context 'Initialization:' do
|
|
80
|
+
it 'should be created with a grammar' do
|
|
81
|
+
expect { GFGEarleyParser.new(grammar_abc) }.not_to raise_error
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
it 'should know its grammar' do
|
|
85
|
+
expect(subject.grammar).to eq(grammar_abc)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
it 'should know its dotted items' do
|
|
89
|
+
expect(subject.dotted_items.size).to eq(8)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
it 'should know its flow graph' do
|
|
93
|
+
expect(subject.gf_graph).to be_kind_of(GFG::GrmFlowGraph)
|
|
94
|
+
end
|
|
95
|
+
end # context
|
|
96
|
+
|
|
97
|
+
context 'Parsing: ' do
|
|
98
|
+
it 'should parse a valid simple input' do
|
|
99
|
+
parse_result = subject.parse(grm1_tokens)
|
|
100
|
+
expect(parse_result.success?).to eq(true)
|
|
101
|
+
# expect(parse_result.ambiguous?).to eq(false)
|
|
102
|
+
######################
|
|
103
|
+
# Expectation chart[0]:
|
|
104
|
+
expected = [
|
|
105
|
+
'.S | 0', # initialization
|
|
106
|
+
'S => . A | 0', # start rule
|
|
107
|
+
'.A | 0', # call rule
|
|
108
|
+
'A => . a A c | 0', # start rule
|
|
109
|
+
'A => . b | 0' # start rule
|
|
110
|
+
]
|
|
111
|
+
compare_entry_texts(parse_result.chart[0], expected)
|
|
112
|
+
expected_terminals(parse_result.chart[0], %w(a b))
|
|
113
|
+
|
|
114
|
+
######################
|
|
115
|
+
# Expectation chart[1]:
|
|
116
|
+
expected = [
|
|
117
|
+
'A => a . A c | 0', # scan 'a'
|
|
118
|
+
'.A | 1', # call rule
|
|
119
|
+
'A => . a A c | 1', # start rule
|
|
120
|
+
'A => . b | 1' # start rule
|
|
121
|
+
]
|
|
122
|
+
entry_set_1 = parse_result.chart[1]
|
|
123
|
+
expect(entry_set_1.entries.size).to eq(4)
|
|
124
|
+
compare_entry_texts(entry_set_1, expected)
|
|
125
|
+
expected_terminals(parse_result.chart[1], %w(a b))
|
|
126
|
+
|
|
127
|
+
######################
|
|
128
|
+
# Expectation chart[2]:
|
|
129
|
+
expected = [
|
|
130
|
+
'A => a . A c | 1', # scan 'a'
|
|
131
|
+
'.A | 2', # call rule
|
|
132
|
+
'A => . a A c | 2', # start rule
|
|
133
|
+
'A => . b | 2' # start rule
|
|
134
|
+
]
|
|
135
|
+
entry_set_2 = parse_result.chart[2]
|
|
136
|
+
expect(entry_set_2.entries.size).to eq(4)
|
|
137
|
+
compare_entry_texts(entry_set_2, expected)
|
|
138
|
+
expected_terminals(parse_result.chart[2], %w(a b))
|
|
139
|
+
|
|
140
|
+
######################
|
|
141
|
+
# Expectation chart[3]:
|
|
142
|
+
expected = [
|
|
143
|
+
'A => b . | 2', # scan 'b'
|
|
144
|
+
'A. | 2', # exit rule
|
|
145
|
+
'A => a A . c | 1', # end rule
|
|
146
|
+
]
|
|
147
|
+
entry_set_3 = parse_result.chart[3]
|
|
148
|
+
expect(entry_set_3.entries.size).to eq(3)
|
|
149
|
+
compare_entry_texts(entry_set_3, expected)
|
|
150
|
+
expected_terminals(parse_result.chart[3], %w(c))
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
######################
|
|
154
|
+
# Expectation chart[4]:
|
|
155
|
+
expected = [
|
|
156
|
+
'A => a A c . | 1', # scan 'c'
|
|
157
|
+
'A. | 1', # exit rule
|
|
158
|
+
'A => a A . c | 0' # end rule
|
|
159
|
+
]
|
|
160
|
+
entry_set_4 = parse_result.chart[4]
|
|
161
|
+
expect(entry_set_4.entries.size).to eq(3)
|
|
162
|
+
compare_entry_texts(entry_set_4, expected)
|
|
163
|
+
expected_terminals(parse_result.chart[4], %w(c))
|
|
164
|
+
|
|
165
|
+
######################
|
|
166
|
+
# Expectation chart[5]:
|
|
167
|
+
expected = [
|
|
168
|
+
'A => a A c . | 0', # scan 'c'
|
|
169
|
+
'A. | 0', # exit rule
|
|
170
|
+
'S => A . | 0', # end rule
|
|
171
|
+
'S. | 0' # exit rule
|
|
172
|
+
]
|
|
173
|
+
entry_set_5 = parse_result.chart[5]
|
|
174
|
+
expect(entry_set_5.entries.size).to eq(4)
|
|
175
|
+
compare_entry_texts(entry_set_5, expected)
|
|
176
|
+
end
|
|
177
|
+
=begin
|
|
178
|
+
it 'should trace a parse with level 1' do
|
|
179
|
+
# Substitute temporarily $stdout by a StringIO
|
|
180
|
+
prev_ostream = $stdout
|
|
181
|
+
$stdout = StringIO.new('', 'w')
|
|
182
|
+
|
|
183
|
+
trace_level = 1
|
|
184
|
+
subject.parse(grm1_tokens, trace_level)
|
|
185
|
+
expectations = <<-SNIPPET
|
|
186
|
+
['a', 'a', 'b', 'c', 'c']
|
|
187
|
+
|. a . a . b . c . c .|
|
|
188
|
+
|> . . . . .| [0:0] S => . A
|
|
189
|
+
|> . . . . .| [0:0] A => . 'a' A 'c'
|
|
190
|
+
|> . . . . .| [0:0] A => . 'b'
|
|
191
|
+
|[---] . . . .| [0:1] A => 'a' . A 'c'
|
|
192
|
+
|. > . . . .| [1:1] A => . 'a' A 'c'
|
|
193
|
+
|. > . . . .| [1:1] A => . 'b'
|
|
194
|
+
|. [---] . . .| [1:2] A => 'a' . A 'c'
|
|
195
|
+
|. . > . . .| [2:2] A => . 'a' A 'c'
|
|
196
|
+
|. . > . . .| [2:2] A => . 'b'
|
|
197
|
+
|. . [---] . .| [2:3] A => 'b' .
|
|
198
|
+
|. [-------> . .| [1:3] A => 'a' A . 'c'
|
|
199
|
+
|. . . [---] .| [3:4] A => 'a' A 'c' .
|
|
200
|
+
|[---------------> .| [0:4] A => 'a' A . 'c'
|
|
201
|
+
|. . . . [---]| [4:5] A => 'a' A 'c' .
|
|
202
|
+
|[===================]| [0:5] S => A .
|
|
203
|
+
SNIPPET
|
|
204
|
+
expect($stdout.string).to eq(expectations)
|
|
205
|
+
|
|
206
|
+
# Restore standard ouput stream
|
|
207
|
+
$stdout = prev_ostream
|
|
208
|
+
end
|
|
209
|
+
=end
|
|
210
|
+
|
|
211
|
+
it 'should parse a valid simple expression' do
|
|
212
|
+
instance = GFGEarleyParser.new(grammar_expr)
|
|
213
|
+
parse_result = instance.parse(grm2_tokens)
|
|
214
|
+
expect(parse_result.success?).to eq(true)
|
|
215
|
+
# expect(parse_result.ambiguous?).to eq(false)
|
|
216
|
+
|
|
217
|
+
###################### S(0): . 2 + 3 * 4
|
|
218
|
+
# Expectation chart[0]:
|
|
219
|
+
expected = [
|
|
220
|
+
'.P | 0', # Initialization
|
|
221
|
+
'P => . S | 0', # start rule
|
|
222
|
+
'.S | 0', # call rule
|
|
223
|
+
"S => . S '+' M | 0", # start rule
|
|
224
|
+
'S => . M | 0', # start rule
|
|
225
|
+
'.M | 0', # call rule
|
|
226
|
+
"M => . M '*' T | 0", # start rule
|
|
227
|
+
'M => . T | 0', # start rule
|
|
228
|
+
'.T | 0', # call rule
|
|
229
|
+
'T => . integer | 0' # start rule
|
|
230
|
+
]
|
|
231
|
+
compare_entry_texts(parse_result.chart[0], expected)
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
###################### S(1): 2 . + 3 * 4
|
|
235
|
+
# Expectation chart[1]:
|
|
236
|
+
expected = [
|
|
237
|
+
'T => integer . | 0', # scan '2'
|
|
238
|
+
'T. | 0', # exit rule
|
|
239
|
+
'M => T . | 0', # end rule
|
|
240
|
+
'M. | 0', # exit rule
|
|
241
|
+
'S => M . | 0', # end rule
|
|
242
|
+
"M => M . '*' T | 0", # end rule
|
|
243
|
+
'S. | 0', # exit rule
|
|
244
|
+
'P => S . | 0', # end rule
|
|
245
|
+
"S => S . '+' M | 0", # end rule
|
|
246
|
+
'P. | 0' # exit rule
|
|
247
|
+
]
|
|
248
|
+
compare_entry_texts(parse_result.chart[1], expected)
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
###################### S(2): 2 + . 3 * 4
|
|
252
|
+
# Expectation chart[2]:
|
|
253
|
+
expected = [
|
|
254
|
+
"S => S '+' . M | 0", # scan '+'
|
|
255
|
+
'.M | 2', # call rule
|
|
256
|
+
"M => . M '*' T | 2", # start rule
|
|
257
|
+
'M => . T | 2', # start rule
|
|
258
|
+
'.T | 2', # call rule
|
|
259
|
+
'T => . integer | 2' # start rule
|
|
260
|
+
]
|
|
261
|
+
compare_entry_texts(parse_result.chart[2], expected)
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
###################### S(3): 2 + 3 . * 4
|
|
265
|
+
# Expectation chart[3]:
|
|
266
|
+
expected = [
|
|
267
|
+
'T => integer . | 2', # scan '3'
|
|
268
|
+
'T. | 2', # exit rule
|
|
269
|
+
'M => T . | 2', # end rule
|
|
270
|
+
'M. | 2', # exit rule
|
|
271
|
+
"S => S '+' M . | 0", # end rule
|
|
272
|
+
"M => M . '*' T | 2", # end rule
|
|
273
|
+
'S. | 0', # exit rule
|
|
274
|
+
'P => S . | 0', # end rule
|
|
275
|
+
"S => S . '+' M | 0", # end rule
|
|
276
|
+
'P. | 0' # exit rule
|
|
277
|
+
]
|
|
278
|
+
compare_entry_texts(parse_result.chart[3], expected)
|
|
279
|
+
|
|
280
|
+
###################### S(4): 2 + 3 * . 4
|
|
281
|
+
# Expectation chart[4]:
|
|
282
|
+
expected = [
|
|
283
|
+
"M => M '*' . T | 2", # scan '*'
|
|
284
|
+
'.T | 4', # call rule
|
|
285
|
+
'T => . integer | 4' # entry rule
|
|
286
|
+
]
|
|
287
|
+
compare_entry_texts(parse_result.chart[4], expected)
|
|
288
|
+
|
|
289
|
+
###################### S(5): 2 + 3 * 4 .
|
|
290
|
+
# Expectation chart[5]:
|
|
291
|
+
expected = [
|
|
292
|
+
'T => integer . | 4', # scan '4'
|
|
293
|
+
'T. | 4', # exit rule
|
|
294
|
+
"M => M '*' T . | 2", # end rule
|
|
295
|
+
'M. | 2', # exit rule
|
|
296
|
+
"S => S '+' M . | 0", # end rule
|
|
297
|
+
"M => M . '*' T | 2", # end rule
|
|
298
|
+
'S. | 0', # exit rule
|
|
299
|
+
'P => S . | 0', # end rule
|
|
300
|
+
"S => S . '+' M | 0", # end rule
|
|
301
|
+
'P. | 0' # end rule
|
|
302
|
+
]
|
|
303
|
+
compare_entry_texts(parse_result.chart[5], expected)
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
it 'should parse a nullable grammar' do
|
|
307
|
+
# Simple but problematic grammar for the original Earley parser
|
|
308
|
+
# (based on example in D. Grune, C. Jacobs "Parsing Techniques" book)
|
|
309
|
+
# Ss => A A 'x';
|
|
310
|
+
# A => ;
|
|
311
|
+
t_x = Syntax::VerbatimSymbol.new('x')
|
|
312
|
+
|
|
313
|
+
builder = Syntax::GrammarBuilder.new
|
|
314
|
+
builder.add_terminals(t_x)
|
|
315
|
+
builder.add_production('Ss' => %w(A A x))
|
|
316
|
+
builder.add_production('A' => [])
|
|
317
|
+
tokens = [ Token.new('x', t_x) ]
|
|
318
|
+
|
|
319
|
+
instance = GFGEarleyParser.new(builder.grammar)
|
|
320
|
+
expect { instance.parse(tokens) }.not_to raise_error
|
|
321
|
+
parse_result = instance.parse(tokens)
|
|
322
|
+
expect(parse_result.success?).to eq(true)
|
|
323
|
+
###################### S(0): . x
|
|
324
|
+
# Expectation chart[0]:
|
|
325
|
+
expected = [
|
|
326
|
+
'.Ss | 0', # Initialization
|
|
327
|
+
"Ss => . A A 'x' | 0", # start rule
|
|
328
|
+
'.A | 0', # call rule
|
|
329
|
+
'A => . | 0', # start rule
|
|
330
|
+
'A. | 0', # exit rule
|
|
331
|
+
"Ss => A . A 'x' | 0", # end rule
|
|
332
|
+
"Ss => A A . 'x' | 0" # end rule
|
|
333
|
+
]
|
|
334
|
+
compare_entry_texts(parse_result.chart[0], expected)
|
|
335
|
+
|
|
336
|
+
###################### S(1): x .
|
|
337
|
+
# Expectation chart[1]:
|
|
338
|
+
expected = [
|
|
339
|
+
"Ss => A A 'x' . | 0", # scan 'x'
|
|
340
|
+
'Ss. | 0' # exit rule
|
|
341
|
+
]
|
|
342
|
+
compare_entry_texts(parse_result.chart[1], expected)
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
it 'should parse an ambiguous grammar (I)' do
|
|
346
|
+
# Grammar 3: A ambiguous arithmetic expression language
|
|
347
|
+
# (based on example in article on Earley's algorithm in Wikipedia)
|
|
348
|
+
# P => S.
|
|
349
|
+
# S => S "+" S.
|
|
350
|
+
# S => S "*" S.
|
|
351
|
+
# S => L.
|
|
352
|
+
# L => an integer number token.
|
|
353
|
+
t_int = Syntax::Literal.new('integer', /[-+]?\d+/)
|
|
354
|
+
t_plus = Syntax::VerbatimSymbol.new('+')
|
|
355
|
+
t_star = Syntax::VerbatimSymbol.new('*')
|
|
356
|
+
|
|
357
|
+
builder = Syntax::GrammarBuilder.new
|
|
358
|
+
builder.add_terminals(t_int, t_plus, t_star)
|
|
359
|
+
builder.add_production('P' => 'S')
|
|
360
|
+
builder.add_production('S' => %w(S + S))
|
|
361
|
+
builder.add_production('S' => %w(S * S))
|
|
362
|
+
builder.add_production('S' => 'L')
|
|
363
|
+
builder.add_production('L' => 'integer')
|
|
364
|
+
input_sequence = [
|
|
365
|
+
{'2' => 'integer'},
|
|
366
|
+
'+',
|
|
367
|
+
{'3' => 'integer'},
|
|
368
|
+
'*',
|
|
369
|
+
{'4' => 'integer'}
|
|
370
|
+
]
|
|
371
|
+
tokens = build_token_sequence(input_sequence, builder.grammar)
|
|
372
|
+
instance = GFGEarleyParser.new(builder.grammar)
|
|
373
|
+
expect { instance.parse(tokens) }.not_to raise_error
|
|
374
|
+
parse_result = instance.parse(tokens)
|
|
375
|
+
expect(parse_result.success?).to eq(true)
|
|
376
|
+
# expect(parse_result.ambiguous?).to eq(true)
|
|
377
|
+
|
|
378
|
+
###################### S(0): . 2 + 3 * 4
|
|
379
|
+
# Expectation chart[0]:
|
|
380
|
+
expected = [
|
|
381
|
+
'.P | 0', # Initialization
|
|
382
|
+
'P => . S | 0', # start rule
|
|
383
|
+
'.S | 0', # call rule
|
|
384
|
+
"S => . S '+' S | 0", # entry rule
|
|
385
|
+
"S => . S '*' S | 0", # entry rule
|
|
386
|
+
'S => . L | 0', # entry rule
|
|
387
|
+
'.L | 0', # call rule
|
|
388
|
+
'L => . integer | 0' # entry rule
|
|
389
|
+
]
|
|
390
|
+
compare_entry_texts(parse_result.chart[0], expected)
|
|
391
|
+
|
|
392
|
+
###################### S(1): 2 . + 3 * 4
|
|
393
|
+
# Expectation chart[1]:
|
|
394
|
+
expected = [
|
|
395
|
+
'L => integer . | 0', # scan '2'
|
|
396
|
+
'L. | 0', # exit rule
|
|
397
|
+
'S => L . | 0', # end rule
|
|
398
|
+
'S. | 0', # exit rule
|
|
399
|
+
'P => S . | 0', # end rule
|
|
400
|
+
"S => S . '+' S | 0", # end rule
|
|
401
|
+
"S => S . '*' S | 0", # end rule
|
|
402
|
+
'P. | 0' # exit rule
|
|
403
|
+
]
|
|
404
|
+
compare_entry_texts(parse_result.chart[1], expected)
|
|
405
|
+
|
|
406
|
+
###################### S(2): 2 + . 3 * 4
|
|
407
|
+
# Expectation chart[2]:
|
|
408
|
+
expected = [
|
|
409
|
+
"S => S '+' . S | 0", # scan '+'
|
|
410
|
+
'.S | 2', # call rule
|
|
411
|
+
"S => . S '+' S | 2", # entry rule
|
|
412
|
+
"S => . S '*' S | 2", # entry rule
|
|
413
|
+
'S => . L | 2', # entry rule
|
|
414
|
+
'.L | 2', # call rule
|
|
415
|
+
'L => . integer | 2' # entry rule
|
|
416
|
+
]
|
|
417
|
+
compare_entry_texts(parse_result.chart[2], expected)
|
|
418
|
+
|
|
419
|
+
###################### S(3): 2 + 3 . * 4
|
|
420
|
+
# Expectation chart[3]:
|
|
421
|
+
expected = [
|
|
422
|
+
'L => integer . | 2', # scan '3'
|
|
423
|
+
'L. | 2', # exit rule
|
|
424
|
+
'S => L . | 2', # end rule
|
|
425
|
+
'S. | 2', # exit rule
|
|
426
|
+
"S => S '+' S . | 0", # end rule
|
|
427
|
+
"S => S . '+' S | 2", # end rule
|
|
428
|
+
"S => S . '*' S | 2", # end rule
|
|
429
|
+
'S. | 0', # exit rule
|
|
430
|
+
'P => S . | 0', # end rule
|
|
431
|
+
"S => S . '+' S | 0", # end rule
|
|
432
|
+
"S => S . '*' S | 0", # end rule
|
|
433
|
+
'P. | 0', # exit rule
|
|
434
|
+
]
|
|
435
|
+
compare_entry_texts(parse_result.chart[3], expected)
|
|
436
|
+
|
|
437
|
+
###################### S(4): 2 + 3 * . 4
|
|
438
|
+
# Expectation chart[4]:
|
|
439
|
+
expected = [
|
|
440
|
+
"S => S '*' . S | 2", # scan '*'
|
|
441
|
+
"S => S '*' . S | 0", # scan '*'
|
|
442
|
+
'.S | 4', # call rule
|
|
443
|
+
"S => . S '+' S | 4", # entry rule
|
|
444
|
+
"S => . S '*' S | 4", # entry rule
|
|
445
|
+
'S => . L | 4', # entry rule
|
|
446
|
+
'.L | 4', # call rule
|
|
447
|
+
'L => . integer | 4' # entry rule
|
|
448
|
+
]
|
|
449
|
+
compare_entry_texts(parse_result.chart[4], expected)
|
|
450
|
+
|
|
451
|
+
###################### S(5): 2 + 3 * 4 .
|
|
452
|
+
# Expectation chart[5]:
|
|
453
|
+
expected = [
|
|
454
|
+
'L => integer . | 4', # scan '4'
|
|
455
|
+
'L. | 4', # exit rule
|
|
456
|
+
'S => L . | 4', # end rule
|
|
457
|
+
'S. | 4', # exit rule
|
|
458
|
+
"S => S '*' S . | 2", # end rule
|
|
459
|
+
"S => S '*' S . | 0", # end rule
|
|
460
|
+
"S => S . '+' S | 4", # end rule
|
|
461
|
+
"S => S . '*' S | 4", # end rule
|
|
462
|
+
'S. | 2', # exit rule
|
|
463
|
+
'S. | 0', # exit rule
|
|
464
|
+
"S => S '+' S . | 0", # end rule
|
|
465
|
+
"S => S . '+' S | 2", # end rule
|
|
466
|
+
"S => S . '*' S | 2", # end rule
|
|
467
|
+
'P => S . | 0', # end rule
|
|
468
|
+
"S => S . '+' S | 0", # end rule
|
|
469
|
+
"S => S . '*' S | 0", # end rule
|
|
470
|
+
'P. | 0' # exit rule
|
|
471
|
+
]
|
|
472
|
+
compare_entry_texts(parse_result.chart[5], expected)
|
|
473
|
+
|
|
474
|
+
expected_antecedents = {
|
|
475
|
+
'L => integer . | 4' => ['L => . integer | 4'],
|
|
476
|
+
'L. | 4' => ['L => integer . | 4'],
|
|
477
|
+
'S => L . | 4' => ['L. | 4'],
|
|
478
|
+
'S. | 4' => ['S => L . | 4'],
|
|
479
|
+
"S => S '*' S . | 2" => ['S. | 4'],
|
|
480
|
+
"S => S '*' S . | 0" => ['S. | 4'],
|
|
481
|
+
"S => S . '+' S | 4" => ['S. | 4'],
|
|
482
|
+
"S => S . '*' S | 4" => ['S. | 4'],
|
|
483
|
+
'S. | 2' => ["S => S '*' S . | 2"],
|
|
484
|
+
'S. | 0' => ["S => S '*' S . | 0", "S => S '+' S . | 0"],
|
|
485
|
+
"S => S '+' S . | 0" => ['S. | 2'],
|
|
486
|
+
"S => S . '+' S | 2" => ['S. | 2'],
|
|
487
|
+
"S => S . '*' S | 2" => ['S. | 2'],
|
|
488
|
+
'P => S . | 0' => ['S. | 0'],
|
|
489
|
+
"S => S . '+' S | 0" => ['S. | 0'],
|
|
490
|
+
"S => S . '*' S | 0" => ['S. | 0'],
|
|
491
|
+
'P. | 0' => ['P => S . | 0']
|
|
492
|
+
}
|
|
493
|
+
check_antecedence(parse_result, 5, expected_antecedents)
|
|
494
|
+
end
|
|
495
|
+
|
|
496
|
+
it 'should parse an ambiguous grammar (II)' do
|
|
497
|
+
extend(AmbiguousGrammarHelper)
|
|
498
|
+
grammar = grammar_builder.grammar
|
|
499
|
+
instance = GFGEarleyParser.new(grammar)
|
|
500
|
+
tokens = tokenize('abc + def + ghi', grammar)
|
|
501
|
+
expect { instance.parse(tokens) }.not_to raise_error
|
|
502
|
+
parse_result = instance.parse(tokens)
|
|
503
|
+
expect(parse_result.success?).to eq(true)
|
|
504
|
+
# expect(parse_result.ambiguous?).to eq(true)
|
|
505
|
+
|
|
506
|
+
###################### S(0): . abc + def + ghi
|
|
507
|
+
# Expectation chart[0]:
|
|
508
|
+
expected = [
|
|
509
|
+
'.S | 0', # Initialization
|
|
510
|
+
'S => . E | 0', # start rule
|
|
511
|
+
'.E | 0', # call rule
|
|
512
|
+
'E => . E + E | 0', # start rule
|
|
513
|
+
'E => . id | 0' # start rule
|
|
514
|
+
]
|
|
515
|
+
compare_entry_texts(parse_result.chart[0], expected)
|
|
516
|
+
|
|
517
|
+
###################### S(1): abc . + def + ghi
|
|
518
|
+
# Expectation chart[1]:
|
|
519
|
+
expected = [
|
|
520
|
+
'E => id . | 0', # scan 'abc'
|
|
521
|
+
'E. | 0', # exit rule
|
|
522
|
+
'S => E . | 0', # end rule
|
|
523
|
+
'E => E . + E | 0', # end rule
|
|
524
|
+
'S. | 0' # exit rule
|
|
525
|
+
]
|
|
526
|
+
compare_entry_texts(parse_result.chart[1], expected)
|
|
527
|
+
|
|
528
|
+
###################### S(2): abc + . def + ghi
|
|
529
|
+
# Expectation chart[2]:
|
|
530
|
+
expected = [
|
|
531
|
+
'E => E + . E | 0', # Scan '+'
|
|
532
|
+
'.E | 2', # call rule
|
|
533
|
+
'E => . E + E | 2', # entry rule
|
|
534
|
+
'E => . id | 2' # entry rule
|
|
535
|
+
]
|
|
536
|
+
compare_entry_texts(parse_result.chart[2], expected)
|
|
537
|
+
|
|
538
|
+
###################### S(3): abc + def . + ghi
|
|
539
|
+
# Expectation chart[3]:
|
|
540
|
+
expected = [
|
|
541
|
+
'E => id . | 2', # Scan 'def'
|
|
542
|
+
'E. | 2', # exit rule
|
|
543
|
+
'E => E + E . | 0', # end rule
|
|
544
|
+
'E => E . + E | 2', # end rule
|
|
545
|
+
'E. | 0', # exit rule
|
|
546
|
+
'S => E . | 0', # end rule
|
|
547
|
+
'E => E . + E | 0', # end rule
|
|
548
|
+
'S. | 0' # exit rule
|
|
549
|
+
]
|
|
550
|
+
compare_entry_texts(parse_result.chart[3], expected)
|
|
551
|
+
|
|
552
|
+
###################### S(4): abc + def + . ghi
|
|
553
|
+
# Expectation chart[4]:
|
|
554
|
+
expected = [
|
|
555
|
+
'E => E + . E | 2', # Scan '+'
|
|
556
|
+
'E => E + . E | 0', # Scan '+'
|
|
557
|
+
'.E | 4', # call rule
|
|
558
|
+
'E => . E + E | 4', # start rule
|
|
559
|
+
'E => . id | 4' # start rule
|
|
560
|
+
]
|
|
561
|
+
compare_entry_texts(parse_result.chart[4], expected)
|
|
562
|
+
|
|
563
|
+
###################### S(5): abc + def + ghi .
|
|
564
|
+
# Expectation chart[5]:
|
|
565
|
+
expected = [
|
|
566
|
+
'E => id . | 4', # Scan 'ghi'
|
|
567
|
+
'E. | 4', # exit rule
|
|
568
|
+
'E => E + E . | 2', # end rule
|
|
569
|
+
'E => E + E . | 0', # end rule
|
|
570
|
+
'E => E . + E | 4', # end rule
|
|
571
|
+
'E. | 2', # exit rule
|
|
572
|
+
'E. | 0', # exit rule
|
|
573
|
+
'E => E . + E | 2', # end rule
|
|
574
|
+
'S => E . | 0', # end rule
|
|
575
|
+
'E => E . + E | 0', # end rule
|
|
576
|
+
'S. | 0', # exit rule
|
|
577
|
+
]
|
|
578
|
+
compare_entry_texts(parse_result.chart[5], expected)
|
|
579
|
+
end
|
|
580
|
+
|
|
581
|
+
it 'should parse an invalid simple input' do
|
|
582
|
+
# Parse an erroneous input (b is missing)
|
|
583
|
+
wrong = build_token_sequence(%w(a a c c), grammar_abc)
|
|
584
|
+
|
|
585
|
+
err_msg = <<-MSG
|
|
586
|
+
Syntax error at or near token 3>>>c<<<:
|
|
587
|
+
Expected one of: ['a', 'b'], found a 'c' instead.
|
|
588
|
+
MSG
|
|
589
|
+
err = StandardError
|
|
590
|
+
expect { subject.parse(wrong) }
|
|
591
|
+
.to raise_error(err, err_msg.chomp)
|
|
592
|
+
end
|
|
593
|
+
|
|
594
|
+
it 'should parse a common sample' do
|
|
595
|
+
# Grammar based on example found in paper of K. Pingali, G. Bilardi:
|
|
596
|
+
# "A Graphical Model for Context-Free Gammar Parsing"
|
|
597
|
+
t_int = Syntax::Literal.new('int', /[-+]?\d+/)
|
|
598
|
+
t_plus = Syntax::VerbatimSymbol.new('+')
|
|
599
|
+
t_lparen = Syntax::VerbatimSymbol.new('(')
|
|
600
|
+
t_rparen = Syntax::VerbatimSymbol.new(')')
|
|
601
|
+
|
|
602
|
+
builder = Syntax::GrammarBuilder.new
|
|
603
|
+
builder.add_terminals(t_int, t_plus, t_lparen, t_rparen)
|
|
604
|
+
builder.add_production('S' => 'E')
|
|
605
|
+
builder.add_production('E' => 'int')
|
|
606
|
+
builder.add_production('E' => %w[( E + E )])
|
|
607
|
+
builder.add_production('E' => %w( E + E ))
|
|
608
|
+
input_sequence = [
|
|
609
|
+
{'7' => 'int'},
|
|
610
|
+
'+',
|
|
611
|
+
{'8' => 'int'},
|
|
612
|
+
'+',
|
|
613
|
+
{'9' => 'int'}
|
|
614
|
+
]
|
|
615
|
+
tokens = build_token_sequence(input_sequence, builder.grammar)
|
|
616
|
+
instance = GFGEarleyParser.new(builder.grammar)
|
|
617
|
+
parse_result = instance.parse(tokens)
|
|
618
|
+
expect(parse_result.success?).to eq(true)
|
|
619
|
+
###################### S(0) == . 7 + 8 + 9
|
|
620
|
+
# Expectation chart[0]:
|
|
621
|
+
expected = [
|
|
622
|
+
'.S | 0', # initialization
|
|
623
|
+
'S => . E | 0', # start rule
|
|
624
|
+
'.E | 0', # call rule
|
|
625
|
+
'E => . int | 0', # start rule
|
|
626
|
+
"E => . '(' E '+' E ')' | 0", # start rule
|
|
627
|
+
"E => . E '+' E | 0" # start rule
|
|
628
|
+
]
|
|
629
|
+
compare_entry_texts(parse_result.chart[0], expected)
|
|
630
|
+
|
|
631
|
+
###################### S(1) == 7 . + 8 + 9
|
|
632
|
+
# Expectation chart[1]:
|
|
633
|
+
expected = [
|
|
634
|
+
'E => int . | 0', # scan '7'
|
|
635
|
+
'E. | 0', # exit rule
|
|
636
|
+
'S => E . | 0', # end rule
|
|
637
|
+
"E => E . '+' E | 0", # end rule
|
|
638
|
+
'S. | 0' # exit rule
|
|
639
|
+
]
|
|
640
|
+
compare_entry_texts(parse_result.chart[1], expected)
|
|
641
|
+
|
|
642
|
+
###################### S(2) == 7 + . 8 + 9
|
|
643
|
+
# Expectation chart[2]:
|
|
644
|
+
expected = [
|
|
645
|
+
"E => E '+' . E | 0", # scan '+'
|
|
646
|
+
'.E | 2', # exit rule
|
|
647
|
+
'E => . int | 2', # start rule
|
|
648
|
+
"E => . '(' E '+' E ')' | 2", # start rule
|
|
649
|
+
"E => . E '+' E | 2" # start rule
|
|
650
|
+
]
|
|
651
|
+
compare_entry_texts(parse_result.chart[2], expected)
|
|
652
|
+
|
|
653
|
+
###################### S(3) == 7 + 8 . + 9
|
|
654
|
+
# Expectation chart[3]:
|
|
655
|
+
expected = [
|
|
656
|
+
'E => int . | 2', # scan '8'
|
|
657
|
+
'E. | 2', # exit rule
|
|
658
|
+
"E => E '+' E . | 0", # end rule
|
|
659
|
+
"E => E . '+' E | 2", # end rule
|
|
660
|
+
'E. | 0', # exit rule
|
|
661
|
+
'S => E . | 0', # end rule
|
|
662
|
+
"E => E . '+' E | 0", # end rule
|
|
663
|
+
'S. | 0' # exit rule
|
|
664
|
+
]
|
|
665
|
+
compare_entry_texts(parse_result.chart[3], expected)
|
|
666
|
+
|
|
667
|
+
###################### S(4) == 7 + 8 + . 9
|
|
668
|
+
# Expectation chart[4]:
|
|
669
|
+
expected = [
|
|
670
|
+
"E => E '+' . E | 2", # scan '+'
|
|
671
|
+
"E => E '+' . E | 0", # scan '+'
|
|
672
|
+
'.E | 4', # exit rule
|
|
673
|
+
'E => . int | 4', # start rule
|
|
674
|
+
"E => . '(' E '+' E ')' | 4", # start rule
|
|
675
|
+
"E => . E '+' E | 4" # start rule
|
|
676
|
+
]
|
|
677
|
+
compare_entry_texts(parse_result.chart[4], expected)
|
|
678
|
+
|
|
679
|
+
###################### S(5) == 7 + 8 + 9 .
|
|
680
|
+
# Expectation chart[5]:
|
|
681
|
+
expected = [
|
|
682
|
+
'E => int . | 4', # scan '9'
|
|
683
|
+
'E. | 4', # exit rule
|
|
684
|
+
"E => E '+' E . | 2", # end rule
|
|
685
|
+
"E => E '+' E . | 0", # end rule
|
|
686
|
+
"E => E . '+' E | 4", # exit rule (not shown in paper)
|
|
687
|
+
'E. | 2', # exit rule
|
|
688
|
+
'E. | 0', # exit rule
|
|
689
|
+
"E => E . '+' E | 2", # end rule
|
|
690
|
+
'S => E . | 0', # end rule
|
|
691
|
+
"E => E . '+' E | 0", # end rule
|
|
692
|
+
'S. | 0'
|
|
693
|
+
]
|
|
694
|
+
compare_entry_texts(parse_result.chart[5], expected)
|
|
695
|
+
end
|
|
696
|
+
|
|
697
|
+
it 'should parse a grammar with nullable nonterminals' do
|
|
698
|
+
# Grammar 4: A grammar with nullable nonterminal
|
|
699
|
+
# based on example from "Parsing Techniques" book
|
|
700
|
+
# (D. Grune, C. Jabobs)
|
|
701
|
+
# Z ::= E.
|
|
702
|
+
# E ::= E Q F.
|
|
703
|
+
# E ::= F.
|
|
704
|
+
# F ::= a.
|
|
705
|
+
# Q ::= *.
|
|
706
|
+
# Q ::= /.
|
|
707
|
+
# Q ::=.
|
|
708
|
+
t_a = Syntax::VerbatimSymbol.new('a')
|
|
709
|
+
t_star = Syntax::VerbatimSymbol.new('*')
|
|
710
|
+
t_slash = Syntax::VerbatimSymbol.new('/')
|
|
711
|
+
|
|
712
|
+
builder = Syntax::GrammarBuilder.new
|
|
713
|
+
builder.add_terminals(t_a, t_star, t_slash)
|
|
714
|
+
builder.add_production('Z' => 'E')
|
|
715
|
+
builder.add_production('E' => %w(E Q F))
|
|
716
|
+
builder.add_production('E' => 'F')
|
|
717
|
+
builder.add_production('F' => t_a)
|
|
718
|
+
builder.add_production('Q' => t_star)
|
|
719
|
+
builder.add_production('Q' => t_slash)
|
|
720
|
+
builder.add_production('Q' => []) # Empty production
|
|
721
|
+
|
|
722
|
+
tokens = build_token_sequence(%w(a a / a), builder.grammar)
|
|
723
|
+
instance = GFGEarleyParser.new(builder.grammar)
|
|
724
|
+
expect { instance.parse(tokens) }.not_to raise_error
|
|
725
|
+
parse_result = instance.parse(tokens)
|
|
726
|
+
expect(parse_result.success?).to eq(true)
|
|
727
|
+
|
|
728
|
+
###################### S(0) == . a a / a
|
|
729
|
+
# Expectation chart[0]:
|
|
730
|
+
expected = [
|
|
731
|
+
'.Z | 0', # initialization
|
|
732
|
+
'Z => . E | 0', # start rule
|
|
733
|
+
'.E | 0', # call rule
|
|
734
|
+
'E => . E Q F | 0', # start rule
|
|
735
|
+
'E => . F | 0', # start rule
|
|
736
|
+
'.F | 0', # call rule
|
|
737
|
+
"F => . 'a' | 0" # start rule
|
|
738
|
+
]
|
|
739
|
+
compare_entry_texts(parse_result.chart[0], expected)
|
|
740
|
+
|
|
741
|
+
###################### S(1) == a . a / a
|
|
742
|
+
# Expectation chart[1]:
|
|
743
|
+
expected = [
|
|
744
|
+
"F => 'a' . | 0", # scan 'a'
|
|
745
|
+
'F. | 0', # exit rule
|
|
746
|
+
'E => F . | 0', # end rule
|
|
747
|
+
'E. | 0', # exit rule
|
|
748
|
+
'Z => E . | 0', # end rule
|
|
749
|
+
'E => E . Q F | 0', # end rule
|
|
750
|
+
'Z. | 0', # exit rule
|
|
751
|
+
'.Q | 1', # call rule
|
|
752
|
+
"Q => . '*' | 1", # start rule
|
|
753
|
+
"Q => . '/' | 1", # start rule
|
|
754
|
+
'Q => . | 1', # start rule
|
|
755
|
+
'Q. | 1', # exit rule
|
|
756
|
+
'E => E Q . F | 0', # end rule
|
|
757
|
+
'.F | 1', # call rule
|
|
758
|
+
"F => . 'a' | 1" # start rule
|
|
759
|
+
]
|
|
760
|
+
compare_entry_texts(parse_result.chart[1], expected)
|
|
761
|
+
|
|
762
|
+
###################### S(2) == a a . / a
|
|
763
|
+
# Expectation chart[2]:
|
|
764
|
+
expected = [
|
|
765
|
+
"F => 'a' . | 1", # scan 'a'
|
|
766
|
+
'F. | 1', # exit rule
|
|
767
|
+
'E => E Q F . | 0', # end rule
|
|
768
|
+
'E. | 0', # exit rule
|
|
769
|
+
'Z => E . | 0', # end rule
|
|
770
|
+
'E => E . Q F | 0', # end rule
|
|
771
|
+
'Z. | 0', # exit rule
|
|
772
|
+
'.Q | 2', # call rule
|
|
773
|
+
"Q => . '*' | 2", # start rule
|
|
774
|
+
"Q => . '/' | 2", # start rule
|
|
775
|
+
'Q => . | 2', # start rule
|
|
776
|
+
'Q. | 2', # exit rule
|
|
777
|
+
'E => E Q . F | 0', # end rule
|
|
778
|
+
'.F | 2', # call rule
|
|
779
|
+
"F => . 'a' | 2" # start rule
|
|
780
|
+
]
|
|
781
|
+
compare_entry_texts(parse_result.chart[2], expected)
|
|
782
|
+
|
|
783
|
+
|
|
784
|
+
###################### S(3) == a a / . a
|
|
785
|
+
# Expectation chart[3]:
|
|
786
|
+
expected = [
|
|
787
|
+
"Q => '/' . | 2", # scan '/'
|
|
788
|
+
'Q. | 2', # exit rule
|
|
789
|
+
'E => E Q . F | 0', # end rule
|
|
790
|
+
'.F | 3', # call rule
|
|
791
|
+
"F => . 'a' | 3" # entry rule
|
|
792
|
+
]
|
|
793
|
+
compare_entry_texts(parse_result.chart[3], expected)
|
|
794
|
+
|
|
795
|
+
|
|
796
|
+
###################### S(4) == a a / a .
|
|
797
|
+
# Expectation chart[4]:
|
|
798
|
+
expected = [
|
|
799
|
+
"F => 'a' . | 3", # scan 'a'
|
|
800
|
+
'F. | 3', # exit rule
|
|
801
|
+
'E => E Q F . | 0', # end rule
|
|
802
|
+
'E. | 0', # exit rule
|
|
803
|
+
'Z => E . | 0', # end rule
|
|
804
|
+
'E => E . Q F | 0', # end rule
|
|
805
|
+
'Z. | 0', # exit rule
|
|
806
|
+
'.Q | 4', # call rule
|
|
807
|
+
"Q => . '*' | 4", # start rule
|
|
808
|
+
"Q => . '/' | 4", # start rule
|
|
809
|
+
'Q => . | 4', # start rule
|
|
810
|
+
'Q. | 4', # exit rule
|
|
811
|
+
'E => E Q . F | 0', # end rule
|
|
812
|
+
'.F | 4', # call rule
|
|
813
|
+
"F => . 'a' | 4" # entry rule
|
|
814
|
+
]
|
|
815
|
+
compare_entry_texts(parse_result.chart[4], expected)
|
|
816
|
+
end
|
|
817
|
+
|
|
818
|
+
it 'should parse a right recursive grammar' do
|
|
819
|
+
# Simple right recursive grammar
|
|
820
|
+
# based on example in D. Grune, C. Jacobs "Parsing Techniques" book
|
|
821
|
+
# pp. 224 et sq.
|
|
822
|
+
# S => a S;
|
|
823
|
+
# S => ;
|
|
824
|
+
# This grammar requires a time that is quadratic in the number of
|
|
825
|
+
# input tokens
|
|
826
|
+
|
|
827
|
+
t_x = Syntax::VerbatimSymbol.new('x')
|
|
828
|
+
|
|
829
|
+
builder = Syntax::GrammarBuilder.new
|
|
830
|
+
builder.add_terminals('a')
|
|
831
|
+
builder.add_production('S' => %w(a S))
|
|
832
|
+
builder.add_production('S' => [])
|
|
833
|
+
grammar = builder.grammar
|
|
834
|
+
tokens = build_token_sequence(%w(a a a a), grammar)
|
|
835
|
+
|
|
836
|
+
instance = GFGEarleyParser.new(grammar)
|
|
837
|
+
parse_result = instance.parse(tokens)
|
|
838
|
+
expect(parse_result.success?).to eq(true)
|
|
839
|
+
###################### S(0): . a a a a
|
|
840
|
+
# Expectation chart[0]:
|
|
841
|
+
expected = [
|
|
842
|
+
'.S | 0', # Initialization
|
|
843
|
+
'S => . a S | 0', # start rule
|
|
844
|
+
'S => . | 0', # start rule
|
|
845
|
+
'S. | 0' # exit rule
|
|
846
|
+
]
|
|
847
|
+
compare_entry_texts(parse_result.chart[0], expected)
|
|
848
|
+
|
|
849
|
+
###################### S(1): a . a a a
|
|
850
|
+
# Expectation chart[1]:
|
|
851
|
+
expected = [
|
|
852
|
+
'S => a . S | 0', # scan 'a'
|
|
853
|
+
'.S | 1', # call rule
|
|
854
|
+
'S => . a S | 1', # start rule
|
|
855
|
+
'S => . | 1', # start rule
|
|
856
|
+
'S. | 1', # exit rule
|
|
857
|
+
'S => a S . | 0' # end rule
|
|
858
|
+
]
|
|
859
|
+
compare_entry_texts(parse_result.chart[1], expected)
|
|
860
|
+
|
|
861
|
+
###################### S(2): a a . a a
|
|
862
|
+
# Expectation chart[2]:
|
|
863
|
+
expected = [
|
|
864
|
+
'S => a . S | 1', # scan 'a'
|
|
865
|
+
'.S | 2', # call rule
|
|
866
|
+
'S => . a S | 2', # start rule
|
|
867
|
+
'S => . | 2', # start rule
|
|
868
|
+
'S. | 2', # exit rule
|
|
869
|
+
'S => a S . | 1', # end rule
|
|
870
|
+
'S. | 1', # exit rule
|
|
871
|
+
'S => a S . | 0', # end rule
|
|
872
|
+
'S. | 0' # exit rule
|
|
873
|
+
]
|
|
874
|
+
compare_entry_texts(parse_result.chart[2], expected)
|
|
875
|
+
|
|
876
|
+
###################### S(3): a a a . a
|
|
877
|
+
# Expectation chart[3]:
|
|
878
|
+
expected = [
|
|
879
|
+
'S => a . S | 2', # scan 'a'
|
|
880
|
+
'.S | 3', # call rule
|
|
881
|
+
'S => . a S | 3', # start rule
|
|
882
|
+
'S => . | 3', # start rule
|
|
883
|
+
'S. | 3', # exit rule
|
|
884
|
+
'S => a S . | 2', # end rule
|
|
885
|
+
'S. | 2', # exit rule
|
|
886
|
+
'S => a S . | 1', # end rule
|
|
887
|
+
'S. | 1', # exit rule
|
|
888
|
+
'S => a S . | 0', # end rule
|
|
889
|
+
'S. | 0' # exit rule
|
|
890
|
+
]
|
|
891
|
+
compare_entry_texts(parse_result.chart[3], expected)
|
|
892
|
+
|
|
893
|
+
###################### S(4): a a a a .
|
|
894
|
+
# Expectation chart[4]:
|
|
895
|
+
expected = [
|
|
896
|
+
'S => a . S | 3', # scan 'a'
|
|
897
|
+
'.S | 4', # call rule
|
|
898
|
+
'S => . a S | 4', # start rule
|
|
899
|
+
'S => . | 4', # start rule
|
|
900
|
+
'S. | 4', # exit rule
|
|
901
|
+
'S => a S . | 3', # end rule
|
|
902
|
+
'S. | 3', # exit rule
|
|
903
|
+
'S => a S . | 2', # end rule
|
|
904
|
+
'S. | 2', # exit rule
|
|
905
|
+
'S => a S . | 1', # end rule
|
|
906
|
+
'S. | 1', # exit rule
|
|
907
|
+
'S => a S . | 0', # end rule
|
|
908
|
+
'S. | 0' # exit rule
|
|
909
|
+
]
|
|
910
|
+
compare_entry_texts(parse_result.chart[4], expected)
|
|
911
|
+
end
|
|
912
|
+
|
|
913
|
+
end # context
|
|
914
|
+
end # describe
|
|
915
|
+
end # module
|
|
916
|
+
end # module
|
|
917
|
+
|
|
918
|
+
# End of module
|