rley 0.2.01 → 0.2.02
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 +8 -8
- data/CHANGELOG.md +5 -0
- data/README.md +9 -6
- data/examples/parsers/parsing_L0.rb +1 -1
- data/examples/parsers/parsing_L1.rb +136 -0
- data/examples/parsers/parsing_abc.rb +1 -2
- data/examples/parsers/parsing_ambig.rb +85 -0
- data/lib/rley/constants.rb +1 -1
- data/lib/rley/formatter/json.rb +1 -4
- data/lib/rley/parser/parse_state_tracker.rb +1 -1
- data/lib/rley/parser/parse_tree_builder.rb +12 -13
- data/lib/rley/parser/parsing.rb +61 -44
- data/lib/rley/ptree/non_terminal_node.rb +13 -0
- data/lib/rley/ptree/parse_tree_node.rb +7 -0
- data/lib/rley/ptree/terminal_node.rb +7 -0
- data/lib/rley/ptree/token_range.rb +18 -0
- data/spec/rley/formatter/json_spec.rb +1 -2
- data/spec/rley/parser/earley_parser_spec.rb +77 -4
- data/spec/rley/parser/parse_tree_builder_spec.rb +28 -27
- data/spec/rley/parser/parsing_spec.rb +230 -73
- data/spec/rley/ptree/non_terminal_node_spec.rb +42 -3
- data/spec/rley/ptree/terminal_node_spec.rb +39 -0
- data/spec/rley/ptree/token_range_spec.rb +45 -0
- data/spec/rley/support/ambiguous_grammar_helper.rb +36 -0
- data/spec/rley/support/grammar_b_expr_helper.rb +1 -1
- metadata +7 -2
@@ -16,6 +16,19 @@ module Rley # This module is used as a namespace
|
|
16
16
|
children << aChildNode
|
17
17
|
end
|
18
18
|
|
19
|
+
# Emit a (formatted) string representation of the node.
|
20
|
+
# Mainly used for diagnosis/debugging purposes.
|
21
|
+
def to_string(indentation)
|
22
|
+
connector = '+- '
|
23
|
+
selfie = super(indentation)
|
24
|
+
prefix = "\n" + (' ' * connector.size * indentation) + connector
|
25
|
+
children_repr = children.reduce('') do |sub_result, child|
|
26
|
+
sub_result << prefix + child.to_string(indentation + 1)
|
27
|
+
end
|
28
|
+
|
29
|
+
return selfie + children_repr
|
30
|
+
end
|
31
|
+
|
19
32
|
# Part of the 'visitee' role in Visitor design pattern.
|
20
33
|
# @param aVisitor[ParseTreeVisitor] the visitor
|
21
34
|
def accept(aVisitor)
|
@@ -17,6 +17,13 @@ module Rley # This module is used as a namespace
|
|
17
17
|
def range=(aRange)
|
18
18
|
range.assign(aRange)
|
19
19
|
end
|
20
|
+
|
21
|
+
# Emit a (formatted) string representation of the node.
|
22
|
+
# Mainly used for diagnosis/debugging purposes.
|
23
|
+
def to_string(indentation)
|
24
|
+
|
25
|
+
return "#{symbol.name}#{range.to_string(indentation)}"
|
26
|
+
end
|
20
27
|
end # class
|
21
28
|
end # module
|
22
29
|
end # module
|
@@ -10,6 +10,13 @@ module Rley # This module is used as a namespace
|
|
10
10
|
super(aTerminalSymbol, aRange)
|
11
11
|
end
|
12
12
|
|
13
|
+
# Emit a (formatted) string representation of the node.
|
14
|
+
# Mainly used for diagnosis/debugging purposes.
|
15
|
+
def to_string(indentation)
|
16
|
+
value = token.nil? ? '(nil)' : token.lexeme
|
17
|
+
super(indentation) + ": '#{value}'"
|
18
|
+
end
|
19
|
+
|
13
20
|
# Part of the 'visitee' role in Visitor design pattern.
|
14
21
|
# @param aVisitor[ParseTreeVisitor] the visitor
|
15
22
|
def accept(aVisitor)
|
@@ -42,6 +42,24 @@ module Rley # This module is used as a namespace
|
|
42
42
|
assign_high(aRange) if high.nil?
|
43
43
|
end
|
44
44
|
|
45
|
+
# Tell whether the given index value lies outside the range
|
46
|
+
def out_of_range?(index)
|
47
|
+
result = false
|
48
|
+
result = true if !low.nil? && index < low
|
49
|
+
result = true if !high.nil? && index > high
|
50
|
+
|
51
|
+
return result
|
52
|
+
end
|
53
|
+
|
54
|
+
# Emit a (formatted) string representation of the range.
|
55
|
+
# Mainly used for diagnosis/debugging purposes.
|
56
|
+
def to_string(_indentation)
|
57
|
+
low_text = low.nil? ? '?' : low.to_s
|
58
|
+
high_text = high.nil? ? '?' : high.to_s
|
59
|
+
|
60
|
+
return "[#{low_text}, #{high_text}]"
|
61
|
+
end
|
62
|
+
|
45
63
|
private
|
46
64
|
|
47
65
|
def assign_low(aRange)
|
@@ -6,6 +6,7 @@ require_relative '../../../lib/rley/syntax/production'
|
|
6
6
|
require_relative '../../../lib/rley/syntax/grammar_builder'
|
7
7
|
require_relative '../../../lib/rley/parser/token'
|
8
8
|
require_relative '../../../lib/rley/parser/dotted_item'
|
9
|
+
require_relative '../support/ambiguous_grammar_helper'
|
9
10
|
# Load the class under test
|
10
11
|
require_relative '../../../lib/rley/parser/earley_parser'
|
11
12
|
|
@@ -38,9 +39,9 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
38
39
|
|
39
40
|
# Grammar 1: A very simple language
|
40
41
|
# (based on example in N. Wirth "Compiler Construction" book, p. 6)
|
41
|
-
# S
|
42
|
-
# A
|
43
|
-
# A
|
42
|
+
# S => A.
|
43
|
+
# A => "a" A "c".
|
44
|
+
# A => "b".
|
44
45
|
# Let's create the grammar piece by piece
|
45
46
|
let(:nt_S) { Syntax::NonTerminal.new('S') }
|
46
47
|
let(:nt_A) { Syntax::NonTerminal.new('A') }
|
@@ -333,7 +334,7 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
333
334
|
compare_state_texts(parse_result.chart[1], expected)
|
334
335
|
end
|
335
336
|
|
336
|
-
it 'should parse an ambiguous grammar' do
|
337
|
+
it 'should parse an ambiguous grammar (I)' do
|
337
338
|
# Grammar 3: A ambiguous arithmetic expression language
|
338
339
|
# (based on example in article on Earley's algorithm in Wikipedia)
|
339
340
|
# P => S.
|
@@ -442,6 +443,78 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
442
443
|
compare_state_texts(parse_result.chart[5], expected)
|
443
444
|
end
|
444
445
|
|
446
|
+
it 'should parse an ambiguous grammar (II)' do
|
447
|
+
self.extend(AmbiguousGrammarHelper)
|
448
|
+
grammar = grammar_builder.grammar
|
449
|
+
instance = EarleyParser.new(grammar)
|
450
|
+
tokens = tokenize('abc + def + ghi', grammar)
|
451
|
+
expect { instance.parse(tokens) }.not_to raise_error
|
452
|
+
parse_result = instance.parse(tokens)
|
453
|
+
expect(parse_result.success?).to eq(true)
|
454
|
+
|
455
|
+
###################### S(0): . abc + def + ghi
|
456
|
+
# Expectation chart[0]:
|
457
|
+
expected = [
|
458
|
+
'S => . E | 0', # Start rule
|
459
|
+
'E => . E + E | 0', # predict from (1)
|
460
|
+
'E => . id | 0' # predict from (1)
|
461
|
+
]
|
462
|
+
compare_state_texts(parse_result.chart[0], expected)
|
463
|
+
|
464
|
+
###################### S(1): abc . + def + ghi
|
465
|
+
# Expectation chart[1]:
|
466
|
+
expected = [
|
467
|
+
'E => id . | 0', # scan from S(0) 3
|
468
|
+
'S => E . | 0', # complete from (1) and S(0) 2
|
469
|
+
'E => E . + E | 0' # complete from (1) and S(0) 3
|
470
|
+
]
|
471
|
+
compare_state_texts(parse_result.chart[1], expected)
|
472
|
+
|
473
|
+
###################### S(2): abc + . def + ghi
|
474
|
+
# Expectation chart[2]:
|
475
|
+
expected = [
|
476
|
+
'E => E + . E | 0', # Scan from S(1) 3
|
477
|
+
'E => . E + E | 2', # predict from (1)
|
478
|
+
'E => . id | 2' # predict from (1)
|
479
|
+
]
|
480
|
+
compare_state_texts(parse_result.chart[2], expected)
|
481
|
+
|
482
|
+
###################### S(3): abc + def . + ghi
|
483
|
+
# Expectation chart[3]:
|
484
|
+
expected = [
|
485
|
+
'E => id . | 2', # Scan from S(2) 3
|
486
|
+
'E => E + E . | 0', # complete from (1) and S(2) 1
|
487
|
+
'E => E . + E | 2', # complete from (1) and S(2) 2
|
488
|
+
'S => E . | 0', # complete from (1) and S(0) 1
|
489
|
+
'E => E . + E | 0' # complete from (1) and S(0) 2
|
490
|
+
]
|
491
|
+
compare_state_texts(parse_result.chart[3], expected)
|
492
|
+
|
493
|
+
###################### S(4): abc + def + . ghi
|
494
|
+
# Expectation chart[4]:
|
495
|
+
expected = [
|
496
|
+
'E => E + . E | 2', # Scan from S(3) 3
|
497
|
+
'E => E + . E | 0', # Scan from S(3) 5
|
498
|
+
'E => . E + E | 4', # predict from (1)
|
499
|
+
'E => . id | 4' # predict from (1)
|
500
|
+
]
|
501
|
+
compare_state_texts(parse_result.chart[4], expected)
|
502
|
+
|
503
|
+
###################### S(5): abc + def + ghi .
|
504
|
+
# Expectation chart[5]:
|
505
|
+
expected = [
|
506
|
+
'E => id . | 4', # Scan from S(4) 4
|
507
|
+
'E => E + E . | 2', # complete from (1) and S(4) 1
|
508
|
+
'E => E + E . | 0', # complete from (1) and S(4) 2
|
509
|
+
'E => E . + E | 4', # complete from (1) and S(4) 3
|
510
|
+
'E => E . + E | 2', # complete from (1) and S(2) 2
|
511
|
+
'S => E . | 0', # complete from (1) and S(0) 1
|
512
|
+
'E => E . + E | 0', # complete from (1) and S(0) 2
|
513
|
+
]
|
514
|
+
compare_state_texts(parse_result.chart[5], expected)
|
515
|
+
end
|
516
|
+
|
517
|
+
|
445
518
|
|
446
519
|
it 'should parse an invalid simple input' do
|
447
520
|
# Parse an erroneous input (b is missing)
|
@@ -21,8 +21,8 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
21
21
|
let(:small_a) { grammar_abc.name2symbol['a'] }
|
22
22
|
let(:small_b) { grammar_abc.name2symbol['b'] }
|
23
23
|
let(:small_c) { grammar_abc.name2symbol['c'] }
|
24
|
-
|
25
|
-
let(:start_prod) { grammar_abc.start_production }
|
24
|
+
|
25
|
+
let(:start_prod) { grammar_abc.start_production }
|
26
26
|
|
27
27
|
let(:tokens_abc) do
|
28
28
|
%w(a a b c c).map do |letter|
|
@@ -34,7 +34,7 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
34
34
|
parser = EarleyParser.new(grammar_abc)
|
35
35
|
parser.parse(tokens_abc)
|
36
36
|
end
|
37
|
-
|
37
|
+
|
38
38
|
subject { ParseTreeBuilder.new(start_prod, { low: 0, high: 5 }) }
|
39
39
|
|
40
40
|
context 'Initialization:' do
|
@@ -71,7 +71,7 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
71
71
|
# Add children to A
|
72
72
|
other_state = sample_parsing.chart.state_sets.last.states.first
|
73
73
|
subject.use_complete_state(other_state)
|
74
|
-
|
74
|
+
|
75
75
|
# Tree is:
|
76
76
|
# S[0,5]
|
77
77
|
# +- A[0,5]
|
@@ -85,7 +85,7 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
85
85
|
end
|
86
86
|
expect(child1.children[0].range.low).to eq(0)
|
87
87
|
expect(child1.children[-1].range.high).to eq(5)
|
88
|
-
|
88
|
+
|
89
89
|
subject.move_down # ... to c
|
90
90
|
subject.range = { low: 4 }
|
91
91
|
expect(child1.children[-1].range.low).to eq(4)
|
@@ -132,46 +132,47 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
132
132
|
subject.use_complete_state(other_state)
|
133
133
|
|
134
134
|
# Tree is:
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
135
|
+
tree_snapshot = <<-SNIPPET
|
136
|
+
S[0, 5]
|
137
|
+
+- A[0, 5]
|
138
|
+
+- a[0, ?]: '(nil)'
|
139
|
+
+- A[1, ?]
|
140
|
+
+- a[1, ?]: '(nil)'
|
141
|
+
+- A[?, ?]
|
142
|
+
+- c[?, ?]: '(nil)'
|
143
|
+
+- c[?, 5]: '(nil)'
|
144
|
+
SNIPPET
|
145
|
+
expect(subject.root.to_string(0)).to eq(tree_snapshot.chomp)
|
143
146
|
|
144
147
|
subject.move_down # ...to grand-grand-child c
|
145
|
-
expect(subject.current_node.
|
148
|
+
expect(subject.current_node.to_string(0)).to eq("c[?, ?]: '(nil)'")
|
146
149
|
|
147
150
|
subject.move_back # ...to grand-grand-child A
|
148
|
-
expect(subject.current_node.
|
151
|
+
expect(subject.current_node.to_string(0)).to eq('A[?, ?]')
|
149
152
|
|
150
153
|
subject.move_back # ...to grand-grand-child a
|
151
|
-
expect(subject.current_node.
|
154
|
+
expect(subject.current_node.to_string(0)).to eq("a[1, ?]: '(nil)'")
|
152
155
|
|
153
|
-
subject.move_back # ...to grand-child A
|
154
|
-
expect(subject.current_node.symbol).to eq(capital_a)
|
155
|
-
|
156
156
|
subject.move_back # ...to grand-child a
|
157
|
-
expect(subject.current_node.
|
158
|
-
|
159
|
-
subject.move_back # ...to child A
|
160
|
-
expect(subject.current_node.symbol).to eq(capital_a)
|
157
|
+
expect(subject.current_node.to_string(0)).to eq("a[0, ?]: '(nil)'")
|
161
158
|
|
162
159
|
subject.move_back # ...to S
|
163
|
-
expect(subject.current_node.symbol).to eq(capital_s)
|
160
|
+
expect(subject.current_node.symbol).to eq(capital_s)
|
161
|
+
end
|
162
|
+
|
163
|
+
it 'should move through deeply nested structure' do
|
164
|
+
|
164
165
|
end
|
165
166
|
end # context
|
166
|
-
|
167
|
-
context 'Parse tree building:' do
|
167
|
+
|
168
|
+
context 'Parse tree building:' do
|
168
169
|
it 'should build a parse tree' do
|
169
170
|
expect(subject.parse_tree).to be_kind_of(PTree::ParseTree)
|
170
171
|
actual = subject.parse_tree
|
171
172
|
expect(actual.root).to eq(subject.root)
|
172
173
|
end
|
173
174
|
end # context
|
174
|
-
|
175
|
+
|
175
176
|
end # describe
|
176
177
|
end # module
|
177
178
|
end # module
|
@@ -21,9 +21,9 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
21
21
|
include GrammarBExprHelper # Mix-in with builder for simple expressions
|
22
22
|
|
23
23
|
# Grammar 1: A very simple language
|
24
|
-
# S
|
25
|
-
# A
|
26
|
-
# A
|
24
|
+
# S => A.
|
25
|
+
# A => "a" A "c".
|
26
|
+
# A => "b".
|
27
27
|
let(:nt_S) { Syntax::NonTerminal.new('S') }
|
28
28
|
let(:nt_A) { Syntax::NonTerminal.new('A') }
|
29
29
|
let(:a_) { Syntax::VerbatimSymbol.new('a') }
|
@@ -139,6 +139,215 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
139
139
|
b_expr_grammar.name2symbol[aSymbolName]
|
140
140
|
end
|
141
141
|
|
142
|
+
subject do
|
143
|
+
parser = EarleyParser.new(b_expr_grammar)
|
144
|
+
tokens = expr_tokenizer('2 + 3 * 4', b_expr_grammar)
|
145
|
+
instance = parser.parse(tokens)
|
146
|
+
end
|
147
|
+
|
148
|
+
# Helper. Build a state tracker and a parse tree builder.
|
149
|
+
def prepare_parse_tree(aParsing)
|
150
|
+
# Accessing private methods by sending message
|
151
|
+
state_tracker = aParsing.send(:new_state_tracker)
|
152
|
+
builder = aParsing.send(:tree_builder, state_tracker.state_set_index)
|
153
|
+
return [state_tracker, builder]
|
154
|
+
end
|
155
|
+
|
156
|
+
|
157
|
+
it 'should create the root of a parse tree' do
|
158
|
+
(state_tracker, builder) = prepare_parse_tree(subject)
|
159
|
+
# The root node should correspond to the start symbol and
|
160
|
+
# its direct children should correspond to rhs of start production
|
161
|
+
expected_text = <<-SNIPPET
|
162
|
+
P[0, 5]
|
163
|
+
+- S[0, 5]
|
164
|
+
SNIPPET
|
165
|
+
root_text = builder.root.to_string(0)
|
166
|
+
expect(root_text).to eq(expected_text.chomp)
|
167
|
+
|
168
|
+
expect(state_tracker.state_set_index).to eq(subject.tokens.size)
|
169
|
+
expected_state = 'P => S . | 0'
|
170
|
+
expect(state_tracker.parse_state.to_s).to eq(expected_state)
|
171
|
+
expect(builder.current_node.to_string(0)).to eq('S[0, 5]')
|
172
|
+
end
|
173
|
+
|
174
|
+
it 'should use a reduce item for a matched non-terminal' do
|
175
|
+
# Setup
|
176
|
+
(state_tracker, builder) = prepare_parse_tree(subject)
|
177
|
+
# Same state as in previous example
|
178
|
+
|
179
|
+
# Given matched symbol is S[0, 5]
|
180
|
+
# And its reduce item is S => S + M . | 0
|
181
|
+
# Then add child nodes corresponding to the rhs symbols
|
182
|
+
# And make M[?, 5] the current symbol
|
183
|
+
subject.insert_matched_symbol(state_tracker, builder)
|
184
|
+
expected_text = <<-SNIPPET
|
185
|
+
P[0, 5]
|
186
|
+
+- S[0, 5]
|
187
|
+
+- S[0, ?]
|
188
|
+
+- +[?, ?]: '(nil)'
|
189
|
+
+- M[?, 5]
|
190
|
+
SNIPPET
|
191
|
+
root_text = builder.root.to_string(0)
|
192
|
+
expect(root_text).to eq(expected_text.chomp)
|
193
|
+
expected_state = 'S => S + M . | 0'
|
194
|
+
expect(state_tracker.parse_state.to_s).to eq(expected_state)
|
195
|
+
expect(state_tracker.state_set_index).to eq(5)
|
196
|
+
expect(builder.current_node.to_string(0)).to eq('M[?, 5]')
|
197
|
+
|
198
|
+
# Second similar test
|
199
|
+
|
200
|
+
# Given matched symbol is M[?, 5]
|
201
|
+
# And its reduce item is M => M * T . | 2
|
202
|
+
# Then add child nodes corresponding to the rhs symbols
|
203
|
+
# And make T[?, 5] the current symbol
|
204
|
+
subject.insert_matched_symbol(state_tracker, builder)
|
205
|
+
expected_text = <<-SNIPPET
|
206
|
+
P[0, 5]
|
207
|
+
+- S[0, 5]
|
208
|
+
+- S[0, ?]
|
209
|
+
+- +[?, ?]: '(nil)'
|
210
|
+
+- M[2, 5]
|
211
|
+
+- M[2, ?]
|
212
|
+
+- *[?, ?]: '(nil)'
|
213
|
+
+- T[?, 5]
|
214
|
+
SNIPPET
|
215
|
+
root_text = builder.root.to_string(0)
|
216
|
+
expect(root_text).to eq(expected_text.chomp)
|
217
|
+
expected_state = 'M => M * T . | 2'
|
218
|
+
expect(state_tracker.parse_state.to_s).to eq(expected_state)
|
219
|
+
expect(state_tracker.state_set_index).to eq(5)
|
220
|
+
expect(builder.current_node.to_string(0)).to eq('T[?, 5]')
|
221
|
+
end
|
222
|
+
|
223
|
+
|
224
|
+
|
225
|
+
it 'should use a previous item for a terminal symbol' do
|
226
|
+
# Setup
|
227
|
+
(state_tracker, builder) = prepare_parse_tree(subject)
|
228
|
+
3.times do
|
229
|
+
subject.insert_matched_symbol(state_tracker, builder)
|
230
|
+
end
|
231
|
+
|
232
|
+
# Given matched symbol is T[?, 5]
|
233
|
+
# And its reduce item is T => integer . | 4
|
234
|
+
# Then add child node corresponding to the rhs symbol
|
235
|
+
# And make integer[4, 5]: '(nil)' the current symbol
|
236
|
+
expected_text = <<-SNIPPET
|
237
|
+
P[0, 5]
|
238
|
+
+- S[0, 5]
|
239
|
+
+- S[0, ?]
|
240
|
+
+- +[?, ?]: '(nil)'
|
241
|
+
+- M[2, 5]
|
242
|
+
+- M[2, ?]
|
243
|
+
+- *[?, ?]: '(nil)'
|
244
|
+
+- T[4, 5]
|
245
|
+
+- integer[4, 5]: '(nil)'
|
246
|
+
SNIPPET
|
247
|
+
root_text = builder.root.to_string(0)
|
248
|
+
expect(root_text).to eq(expected_text.chomp)
|
249
|
+
expected_state = 'T => integer . | 4'
|
250
|
+
expect(state_tracker.parse_state.to_s).to eq(expected_state)
|
251
|
+
expect(state_tracker.state_set_index).to eq(5)
|
252
|
+
expect(builder.current_node.to_string(0)).to eq("integer[4, 5]: '(nil)'")
|
253
|
+
|
254
|
+
# Given current tree symbol is integer[4, 5]: '(nil)'
|
255
|
+
# And its previous item is T => . integer | 4
|
256
|
+
# Then attach the token to the terminal node
|
257
|
+
# And decrement the state index by one
|
258
|
+
# Make *[?, ?]: '(nil)' the current symbol
|
259
|
+
subject.insert_matched_symbol(state_tracker, builder)
|
260
|
+
expected_text = <<-SNIPPET
|
261
|
+
P[0, 5]
|
262
|
+
+- S[0, 5]
|
263
|
+
+- S[0, ?]
|
264
|
+
+- +[?, ?]: '(nil)'
|
265
|
+
+- M[2, 5]
|
266
|
+
+- M[2, ?]
|
267
|
+
+- *[?, ?]: '(nil)'
|
268
|
+
+- T[4, 5]
|
269
|
+
+- integer[4, 5]: '4'
|
270
|
+
SNIPPET
|
271
|
+
root_text = builder.root.to_string(0)
|
272
|
+
expect(root_text).to eq(expected_text.chomp)
|
273
|
+
expected_state = 'T => . integer | 4'
|
274
|
+
expect(state_tracker.parse_state.to_s).to eq(expected_state)
|
275
|
+
expect(state_tracker.state_set_index).to eq(4)
|
276
|
+
next_symbol = "*[?, ?]: '(nil)'"
|
277
|
+
expect(builder.current_node.to_string(0)).to eq(next_symbol)
|
278
|
+
end
|
279
|
+
|
280
|
+
it 'should handle [no symbol before dot, terminal tree node] case' do
|
281
|
+
# Setup
|
282
|
+
(state_tracker, builder) = prepare_parse_tree(subject)
|
283
|
+
4.times do
|
284
|
+
subject.insert_matched_symbol(state_tracker, builder)
|
285
|
+
end
|
286
|
+
|
287
|
+
# Given current tree symbol is *[?, ?]: '(nil)'
|
288
|
+
# And current dotted item is T => . integer | 4
|
289
|
+
# When one retrieves the parse state expecting the T
|
290
|
+
# Then new parse state is changed to: M => M * . T | 2
|
291
|
+
subject.insert_matched_symbol(state_tracker, builder)
|
292
|
+
|
293
|
+
expected_text = <<-SNIPPET
|
294
|
+
P[0, 5]
|
295
|
+
+- S[0, 5]
|
296
|
+
+- S[0, ?]
|
297
|
+
+- +[?, ?]: '(nil)'
|
298
|
+
+- M[2, 5]
|
299
|
+
+- M[2, ?]
|
300
|
+
+- *[?, ?]: '(nil)'
|
301
|
+
+- T[4, 5]
|
302
|
+
+- integer[4, 5]: '4'
|
303
|
+
SNIPPET
|
304
|
+
root_text = builder.root.to_string(0)
|
305
|
+
expect(root_text).to eq(expected_text.chomp)
|
306
|
+
expected_state = 'M => M * . T | 2'
|
307
|
+
expect(state_tracker.parse_state.to_s).to eq(expected_state)
|
308
|
+
expect(state_tracker.state_set_index).to eq(4)
|
309
|
+
next_symbol = "*[?, ?]: '(nil)'"
|
310
|
+
expect(builder.current_node.to_string(0)).to eq(next_symbol)
|
311
|
+
|
312
|
+
subject.insert_matched_symbol(state_tracker, builder)
|
313
|
+
next_symbol = 'M[2, ?]'
|
314
|
+
expect(builder.current_node.to_string(0)).to eq(next_symbol)
|
315
|
+
end
|
316
|
+
|
317
|
+
it 'should handle the end of parse tree generation' do
|
318
|
+
# Begin setup
|
319
|
+
is_done = false
|
320
|
+
(state_tracker, builder) = prepare_parse_tree(subject)
|
321
|
+
16.times do
|
322
|
+
is_done = subject.insert_matched_symbol(state_tracker, builder)
|
323
|
+
end
|
324
|
+
|
325
|
+
expected_text = <<-SNIPPET
|
326
|
+
P[0, 5]
|
327
|
+
+- S[0, 5]
|
328
|
+
+- S[0, 1]
|
329
|
+
+- M[0, 1]
|
330
|
+
+- T[0, 1]
|
331
|
+
+- integer[0, 1]: '2'
|
332
|
+
+- +[1, 2]: '+'
|
333
|
+
+- M[2, 5]
|
334
|
+
+- M[2, 3]
|
335
|
+
+- T[2, 3]
|
336
|
+
+- integer[2, 3]: '3'
|
337
|
+
+- *[3, 4]: '*'
|
338
|
+
+- T[4, 5]
|
339
|
+
+- integer[4, 5]: '4'
|
340
|
+
SNIPPET
|
341
|
+
root_text = builder.root.to_string(0)
|
342
|
+
expect(root_text).to eq(expected_text.chomp)
|
343
|
+
|
344
|
+
expected_state = 'T => . integer | 0'
|
345
|
+
expect(state_tracker.parse_state.to_s).to eq(expected_state)
|
346
|
+
expect(state_tracker.state_set_index).to eq(0)
|
347
|
+
expect(is_done).to eq(true)
|
348
|
+
end
|
349
|
+
|
350
|
+
|
142
351
|
|
143
352
|
it 'should build the parse tree for a simple non-ambiguous grammar' do
|
144
353
|
parser = EarleyParser.new(sample_grammar1)
|
@@ -155,76 +364,24 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
155
364
|
expect(ptree).to be_kind_of(PTree::ParseTree)
|
156
365
|
|
157
366
|
# Expect parse tree:
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
(
|
176
|
-
expect(node_s.symbol).to eq(grm_symbol('S'))
|
177
|
-
expect(node_s.range).to eq(low: 0, high: 1)
|
178
|
-
expect(node_s.children.size).to eq(1)
|
179
|
-
expect(node_plus.symbol).to eq(grm_symbol('+'))
|
180
|
-
expect(node_plus.range).to eq(low: 0, high: 1) # TODO: fix this
|
181
|
-
expect(node_plus.token.lexeme). to eq('+')
|
182
|
-
expect(node_m.symbol).to eq(grm_symbol('M'))
|
183
|
-
expect(node_m.range).to eq(low: 2, high: 5)
|
184
|
-
expect(node_m.children.size).to eq(3)
|
185
|
-
|
186
|
-
node = node_s.children[0] # M
|
187
|
-
expect(node.symbol).to eq(grm_symbol('M'))
|
188
|
-
expect(node.range).to eq([0, 1])
|
189
|
-
expect(node.children.size).to eq(1)
|
190
|
-
|
191
|
-
node = node.children[0] # T
|
192
|
-
expect(node.symbol).to eq(grm_symbol('T'))
|
193
|
-
expect(node.range).to eq([0, 1])
|
194
|
-
expect(node.children.size).to eq(1)
|
195
|
-
|
196
|
-
node = node.children[0] # integer(2)
|
197
|
-
expect(node.symbol).to eq(grm_symbol('integer'))
|
198
|
-
expect(node.range).to eq([0, 1])
|
199
|
-
expect(node.token.lexeme).to eq('2')
|
200
|
-
|
201
|
-
(node_m2, node_star, node_t3) = node_m.children
|
202
|
-
expect(node_m2.symbol).to eq(grm_symbol('M'))
|
203
|
-
expect(node_m2.range).to eq([2, 3])
|
204
|
-
expect(node_m2.children.size).to eq(1)
|
205
|
-
|
206
|
-
node_t2 = node_m2.children[0] # T
|
207
|
-
expect(node_t2.symbol).to eq(grm_symbol('T'))
|
208
|
-
expect(node_t2.range).to eq([2, 3])
|
209
|
-
expect(node_t2.children.size).to eq(1)
|
210
|
-
|
211
|
-
node = node_t2.children[0] # integer(3)
|
212
|
-
expect(node.symbol).to eq(grm_symbol('integer'))
|
213
|
-
expect(node.range).to eq([2, 3])
|
214
|
-
expect(node.token.lexeme).to eq('3')
|
215
|
-
|
216
|
-
expect(node_star.symbol).to eq(grm_symbol('*'))
|
217
|
-
expect(node_star.range).to eq([2, 3]) # Fix this
|
218
|
-
expect(node_star.token.lexeme). to eq('*')
|
219
|
-
|
220
|
-
expect(node_t3.symbol).to eq(grm_symbol('T'))
|
221
|
-
expect(node_t3.range).to eq([4, 5])
|
222
|
-
expect(node_t3.children.size).to eq(1)
|
223
|
-
|
224
|
-
node = node_t3.children[0] # integer(4)
|
225
|
-
expect(node.symbol).to eq(grm_symbol('integer'))
|
226
|
-
expect(node.range).to eq([4, 5])
|
227
|
-
expect(node.token.lexeme).to eq('4')
|
367
|
+
expected_text = <<-SNIPPET
|
368
|
+
P[0, 5]
|
369
|
+
+- S[0, 5]
|
370
|
+
+- S[0, 1]
|
371
|
+
+- M[0, 1]
|
372
|
+
+- T[0, 1]
|
373
|
+
+- integer[0, 1]: '2'
|
374
|
+
+- +[1, 2]: '+'
|
375
|
+
+- M[2, 5]
|
376
|
+
+- M[2, 3]
|
377
|
+
+- T[2, 3]
|
378
|
+
+- integer[2, 3]: '3'
|
379
|
+
+- *[3, 4]: '*'
|
380
|
+
+- T[4, 5]
|
381
|
+
+- integer[4, 5]: '4'
|
382
|
+
SNIPPET
|
383
|
+
actual = ptree.root.to_string(0)
|
384
|
+
expect(actual).to eq(expected_text.chomp)
|
228
385
|
end
|
229
386
|
end # context
|
230
387
|
end # describe
|