rley 0.3.04 → 0.3.05
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +7 -3
- data/CHANGELOG.md +3 -0
- data/Rakefile +30 -30
- data/examples/parsers/parsing_L0.rb +1 -1
- data/examples/parsers/parsing_L1.rb +1 -1
- data/examples/parsers/parsing_abc.rb +1 -1
- data/examples/parsers/parsing_ambig.rb +1 -1
- data/examples/parsers/parsing_another.rb +1 -1
- data/examples/parsers/parsing_b_expr.rb +1 -1
- data/examples/parsers/parsing_err_expr.rb +1 -1
- data/examples/parsers/parsing_groucho.rb +1 -1
- data/examples/parsers/parsing_right_recursive.rb +1 -1
- data/examples/parsers/parsing_tricky.rb +1 -1
- data/lib/rley/constants.rb +2 -2
- data/lib/rley/formatter/base_formatter.rb +0 -2
- data/lib/rley/formatter/debug.rb +0 -2
- data/lib/rley/formatter/json.rb +1 -3
- data/lib/rley/gfg/call_edge.rb +31 -30
- data/lib/rley/gfg/edge.rb +22 -23
- data/lib/rley/gfg/end_vertex.rb +22 -24
- data/lib/rley/gfg/epsilon_edge.rb +20 -21
- data/lib/rley/gfg/grm_flow_graph.rb +39 -39
- data/lib/rley/gfg/item_vertex.rb +16 -17
- data/lib/rley/gfg/non_terminal_vertex.rb +3 -4
- data/lib/rley/gfg/return_edge.rb +32 -31
- data/lib/rley/gfg/scan_edge.rb +25 -26
- data/lib/rley/gfg/shortcut_edge.rb +25 -26
- data/lib/rley/gfg/start_vertex.rb +0 -2
- data/lib/rley/gfg/vertex.rb +8 -8
- data/lib/rley/parse_forest_visitor.rb +113 -115
- data/lib/rley/parse_tree_visitor.rb +0 -2
- data/lib/rley/parser/base_parser.rb +27 -27
- data/lib/rley/parser/chart.rb +14 -14
- data/lib/rley/parser/dotted_item.rb +33 -33
- data/lib/rley/parser/earley_parser.rb +6 -6
- data/lib/rley/parser/gfg_chart.rb +8 -15
- data/lib/rley/parser/gfg_earley_parser.rb +15 -13
- data/lib/rley/parser/gfg_parsing.rb +26 -22
- data/lib/rley/parser/grm_items_builder.rb +3 -2
- data/lib/rley/parser/parse_entry.rb +3 -9
- data/lib/rley/parser/parse_entry_set.rb +14 -19
- data/lib/rley/parser/parse_entry_tracker.rb +56 -56
- data/lib/rley/parser/parse_forest_builder.rb +215 -214
- data/lib/rley/parser/parse_forest_factory.rb +57 -56
- data/lib/rley/parser/parse_state.rb +8 -11
- data/lib/rley/parser/parse_state_tracker.rb +56 -56
- data/lib/rley/parser/parse_tracer.rb +3 -3
- data/lib/rley/parser/parse_tree_builder.rb +10 -10
- data/lib/rley/parser/parse_walker_factory.rb +30 -33
- data/lib/rley/parser/parsing.rb +8 -8
- data/lib/rley/parser/state_set.rb +23 -26
- data/lib/rley/ptree/non_terminal_node.rb +1 -1
- data/lib/rley/ptree/token_range.rb +2 -2
- data/lib/rley/sppf/alternative_node.rb +32 -34
- data/lib/rley/sppf/composite_node.rb +27 -27
- data/lib/rley/sppf/epsilon_node.rb +26 -27
- data/lib/rley/sppf/leaf_node.rb +11 -12
- data/lib/rley/sppf/non_terminal_node.rb +37 -38
- data/lib/rley/sppf/sppf_node.rb +1 -1
- data/lib/rley/sppf/token_node.rb +29 -29
- data/lib/rley/syntax/grammar.rb +1 -3
- data/lib/rley/syntax/grammar_builder.rb +8 -8
- data/lib/rley/syntax/non_terminal.rb +2 -4
- data/lib/rley/syntax/production.rb +3 -3
- data/lib/rley/syntax/symbol_seq.rb +1 -1
- data/spec/rley/gfg/call_edge_spec.rb +50 -51
- data/spec/rley/gfg/edge_spec.rb +33 -33
- data/spec/rley/gfg/end_vertex_spec.rb +26 -27
- data/spec/rley/gfg/epsilon_edge_spec.rb +25 -25
- data/spec/rley/gfg/grm_flow_graph_spec.rb +1 -1
- data/spec/rley/gfg/item_vertex_spec.rb +3 -4
- data/spec/rley/gfg/return_edge_spec.rb +51 -51
- data/spec/rley/gfg/scan_edge_spec.rb +32 -30
- data/spec/rley/gfg/shortcut_edge_spec.rb +1 -1
- data/spec/rley/gfg/vertex_spec.rb +3 -3
- data/spec/rley/parse_forest_visitor_spec.rb +239 -238
- data/spec/rley/parser/dotted_item_spec.rb +1 -1
- data/spec/rley/parser/earley_parser_spec.rb +16 -16
- data/spec/rley/parser/gfg_earley_parser_spec.rb +30 -31
- data/spec/rley/parser/gfg_parsing_spec.rb +11 -10
- data/spec/rley/parser/grm_items_builder_spec.rb +2 -2
- data/spec/rley/parser/parse_entry_set_spec.rb +4 -4
- data/spec/rley/parser/parse_entry_spec.rb +0 -2
- data/spec/rley/parser/parse_forest_builder_spec.rb +82 -57
- data/spec/rley/parser/parse_forest_factory_spec.rb +84 -82
- data/spec/rley/parser/parse_walker_factory_spec.rb +10 -9
- data/spec/rley/parser/parsing_spec.rb +0 -1
- data/spec/rley/sppf/alternative_node_spec.rb +2 -2
- data/spec/rley/sppf/non_terminal_node_spec.rb +0 -1
- data/spec/rley/support/ambiguous_grammar_helper.rb +1 -1
- data/spec/rley/support/expectation_helper.rb +37 -36
- data/spec/rley/support/grammar_abc_helper.rb +17 -17
- data/spec/rley/support/grammar_b_expr_helper.rb +40 -39
- data/spec/rley/support/grammar_helper.rb +2 -1
- data/spec/rley/support/{grammar_L0_helper.rb → grammar_l0_helper.rb} +82 -81
- data/spec/rley/support/grammar_sppf_helper.rb +24 -25
- data/spec/rley/syntax/grammar_spec.rb +1 -1
- metadata +2 -2
@@ -126,7 +126,7 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
126
126
|
|
127
127
|
# Case: different productions
|
128
128
|
instance = DottedItem.new(empty_prod, 0)
|
129
|
-
expect(subject).not_to be_successor_of(
|
129
|
+
expect(subject).not_to be_successor_of(instance)
|
130
130
|
|
131
131
|
# Case: one position difference
|
132
132
|
instance = DottedItem.new(sample_prod, 0)
|
@@ -220,8 +220,8 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
220
220
|
######################
|
221
221
|
# Expectation chart[5]:
|
222
222
|
expected = [
|
223
|
-
"A => 'a' A 'c' . | 0",
|
224
|
-
'S => A . | 0'
|
223
|
+
"A => 'a' A 'c' . | 0", # scan from S(4) 1
|
224
|
+
'S => A . | 0' # complete from 0 and S(0) 0
|
225
225
|
]
|
226
226
|
state_set_5 = parse_result.chart[5]
|
227
227
|
expect(state_set_5.states.size).to eq(2)
|
@@ -325,7 +325,7 @@ SNIPPET
|
|
325
325
|
###################### S(5): 2 + 3 * 4 .
|
326
326
|
# Expectation chart[5]:
|
327
327
|
expected = [
|
328
|
-
'T => integer . | 4',
|
328
|
+
'T => integer . | 4', # scan from S(4) 2
|
329
329
|
"M => M '*' T . | 2", # complete from (1) and S(4) 1
|
330
330
|
"S => S '+' M . | 0", # complete from (2) and S(2) 1
|
331
331
|
"M => M . '*' T | 2", # complete from (2) and S(2) 2
|
@@ -357,14 +357,14 @@ SNIPPET
|
|
357
357
|
"Ss => . A A 'x' | 0", # Start rule
|
358
358
|
'A => . | 0', # predict from (1)
|
359
359
|
"Ss => A . A 'x' | 0", # modified predict from (1)
|
360
|
-
"Ss => A A . 'x' | 0"
|
360
|
+
"Ss => A A . 'x' | 0" # modified predict from (1)
|
361
361
|
]
|
362
362
|
compare_state_texts(parse_result.chart[0], expected)
|
363
363
|
|
364
364
|
###################### S(1): x .
|
365
365
|
# Expectation chart[1]:
|
366
366
|
expected = [
|
367
|
-
"Ss => A A 'x' . | 0"
|
367
|
+
"Ss => A A 'x' . | 0" # scan from S(0) 4
|
368
368
|
]
|
369
369
|
compare_state_texts(parse_result.chart[1], expected)
|
370
370
|
end
|
@@ -415,7 +415,7 @@ SNIPPET
|
|
415
415
|
###################### S(1): 2 . + 3 * 4
|
416
416
|
# Expectation chart[1]:
|
417
417
|
expected = [
|
418
|
-
'L => integer . | 0',
|
418
|
+
'L => integer . | 0', # scan from S(0) 4
|
419
419
|
'S => L . | 0', # complete from (1) and S(0) 4
|
420
420
|
'P => S . | 0', # complete from (2) and S(0) 1
|
421
421
|
"S => S . '+' S | 0", # complete from (2) and S(0) 2
|
@@ -439,7 +439,7 @@ SNIPPET
|
|
439
439
|
expected = [
|
440
440
|
'L => integer . | 2', # scan from S(2) 5
|
441
441
|
'S => L . | 2', # complete from (1) and S(2) 4
|
442
|
-
"S => S '+' S . | 0",
|
442
|
+
"S => S '+' S . | 0", # complete from (2) and S(2) 1
|
443
443
|
"S => S . '+' S | 2", # complete from (2) and S(2) 2
|
444
444
|
"S => S . '*' S | 2", # complete from (2) and S(2) 3
|
445
445
|
'P => S . | 0', # complete from (2) and S(0) 1
|
@@ -649,15 +649,15 @@ MSG
|
|
649
649
|
###################### S(1) == a . a / a
|
650
650
|
# Expectation chart[1]:
|
651
651
|
expected = [
|
652
|
-
"F => 'a' . | 0",
|
653
|
-
'E => F . | 0',
|
654
|
-
'Z => E . | 0',
|
655
|
-
'E => E . Q F | 0',
|
656
|
-
"Q => . '*' | 1",
|
657
|
-
"Q => . '/' | 1",
|
658
|
-
'Q => . | 1',
|
659
|
-
'E => E Q . F | 0',
|
660
|
-
"F => . 'a' | 1"
|
652
|
+
"F => 'a' . | 0", # scan from S(0) 4
|
653
|
+
'E => F . | 0', # complete from (1) and S(0) 3
|
654
|
+
'Z => E . | 0', # complete from (2) and S(0) 1
|
655
|
+
'E => E . Q F | 0', # complete from (2) and S(0) 2
|
656
|
+
"Q => . '*' | 1", # Predict from (4)
|
657
|
+
"Q => . '/' | 1", # Predict from (4)
|
658
|
+
'Q => . | 1', # Predict from (4)
|
659
|
+
'E => E Q . F | 0', # Modified predict from (4)
|
660
|
+
"F => . 'a' | 1" # Predict from (8)
|
661
661
|
]
|
662
662
|
compare_state_texts(parse_result.chart[1], expected)
|
663
663
|
|
@@ -66,11 +66,15 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
66
66
|
|
67
67
|
# Helper method that mimicks the output of a tokenizer
|
68
68
|
# for the language specified by grammar_expr
|
69
|
-
def grm2_tokens()
|
70
|
-
input_sequence = [
|
71
|
-
|
69
|
+
def grm2_tokens()
|
70
|
+
input_sequence = [
|
71
|
+
{ '2' => 'integer' },
|
72
|
+
'+',
|
73
|
+
{ '3' => 'integer' },
|
74
|
+
'*',
|
75
|
+
{ '4' => 'integer' }
|
72
76
|
]
|
73
|
-
return
|
77
|
+
return build_token_sequence(input_sequence, grammar_expr)
|
74
78
|
end
|
75
79
|
|
76
80
|
# Default instantiation rule
|
@@ -272,7 +276,7 @@ SNIPPET
|
|
272
276
|
"M => M . '*' T | 2", # end rule
|
273
277
|
'S. | 0', # exit rule
|
274
278
|
'P => S . | 0', # end rule
|
275
|
-
"S => S . '+' M | 0",
|
279
|
+
"S => S . '+' M | 0", # end rule
|
276
280
|
'P. | 0' # exit rule
|
277
281
|
]
|
278
282
|
compare_entry_texts(parse_result.chart[3], expected)
|
@@ -361,12 +365,12 @@ SNIPPET
|
|
361
365
|
builder.add_production('S' => %w(S * S))
|
362
366
|
builder.add_production('S' => 'L')
|
363
367
|
builder.add_production('L' => 'integer')
|
364
|
-
input_sequence = [
|
365
|
-
{'2' => 'integer'},
|
366
|
-
'+',
|
367
|
-
{'3' => 'integer'},
|
368
|
-
'*',
|
369
|
-
{'4' => 'integer'}
|
368
|
+
input_sequence = [
|
369
|
+
{ '2' => 'integer' },
|
370
|
+
'+',
|
371
|
+
{ '3' => 'integer' },
|
372
|
+
'*',
|
373
|
+
{ '4' => 'integer' }
|
370
374
|
]
|
371
375
|
tokens = build_token_sequence(input_sequence, builder.grammar)
|
372
376
|
instance = GFGEarleyParser.new(builder.grammar)
|
@@ -470,7 +474,7 @@ SNIPPET
|
|
470
474
|
'P. | 0' # exit rule
|
471
475
|
]
|
472
476
|
compare_entry_texts(parse_result.chart[5], expected)
|
473
|
-
|
477
|
+
|
474
478
|
expected_antecedents = {
|
475
479
|
'L => integer . | 4' => ['L => . integer | 4'],
|
476
480
|
'L. | 4' => ['L => integer . | 4'],
|
@@ -478,14 +482,14 @@ SNIPPET
|
|
478
482
|
'S. | 4' => ['S => L . | 4'],
|
479
483
|
"S => S '*' S . | 2" => ['S. | 4'],
|
480
484
|
"S => S '*' S . | 0" => ['S. | 4'],
|
481
|
-
"S => S . '+' S | 4" => ['S. | 4'],
|
485
|
+
"S => S . '+' S | 4" => ['S. | 4'],
|
482
486
|
"S => S . '*' S | 4" => ['S. | 4'],
|
483
487
|
'S. | 2' => ["S => S '*' S . | 2"],
|
484
488
|
'S. | 0' => ["S => S '*' S . | 0", "S => S '+' S . | 0"],
|
485
489
|
"S => S '+' S . | 0" => ['S. | 2'],
|
486
490
|
"S => S . '+' S | 2" => ['S. | 2'],
|
487
491
|
"S => S . '*' S | 2" => ['S. | 2'],
|
488
|
-
'P => S . | 0'
|
492
|
+
'P => S . | 0' => ['S. | 0'],
|
489
493
|
"S => S . '+' S | 0" => ['S. | 0'],
|
490
494
|
"S => S . '*' S | 0" => ['S. | 0'],
|
491
495
|
'P. | 0' => ['P => S . | 0']
|
@@ -518,7 +522,7 @@ SNIPPET
|
|
518
522
|
# Expectation chart[1]:
|
519
523
|
expected = [
|
520
524
|
'E => id . | 0', # scan 'abc'
|
521
|
-
'E. | 0',
|
525
|
+
'E. | 0', # exit rule
|
522
526
|
'S => E . | 0', # end rule
|
523
527
|
'E => E . + E | 0', # end rule
|
524
528
|
'S. | 0' # exit rule
|
@@ -603,14 +607,14 @@ MSG
|
|
603
607
|
builder.add_terminals(t_int, t_plus, t_lparen, t_rparen)
|
604
608
|
builder.add_production('S' => 'E')
|
605
609
|
builder.add_production('E' => 'int')
|
606
|
-
builder.add_production('E' => %w
|
607
|
-
builder.add_production('E' => %w(
|
608
|
-
input_sequence = [
|
609
|
-
{'7' => 'int'},
|
610
|
-
'+',
|
611
|
-
{'8' => 'int'},
|
612
|
-
'+',
|
613
|
-
{'9' => 'int'}
|
610
|
+
builder.add_production('E' => %w(( E + E )))
|
611
|
+
builder.add_production('E' => %w(E + E))
|
612
|
+
input_sequence = [
|
613
|
+
{ '7' => 'int' },
|
614
|
+
'+',
|
615
|
+
{ '8' => 'int' },
|
616
|
+
'+',
|
617
|
+
{ '9' => 'int' }
|
614
618
|
]
|
615
619
|
tokens = build_token_sequence(input_sequence, builder.grammar)
|
616
620
|
instance = GFGEarleyParser.new(builder.grammar)
|
@@ -644,7 +648,7 @@ MSG
|
|
644
648
|
expected = [
|
645
649
|
"E => E '+' . E | 0", # scan '+'
|
646
650
|
'.E | 2', # exit rule
|
647
|
-
|
651
|
+
'E => . int | 2', # start rule
|
648
652
|
"E => . '(' E '+' E ')' | 2", # start rule
|
649
653
|
"E => . E '+' E | 2" # start rule
|
650
654
|
]
|
@@ -718,7 +722,7 @@ MSG
|
|
718
722
|
builder.add_production('Q' => t_star)
|
719
723
|
builder.add_production('Q' => t_slash)
|
720
724
|
builder.add_production('Q' => []) # Empty production
|
721
|
-
|
725
|
+
|
722
726
|
tokens = build_token_sequence(%w(a a / a), builder.grammar)
|
723
727
|
instance = GFGEarleyParser.new(builder.grammar)
|
724
728
|
expect { instance.parse(tokens) }.not_to raise_error
|
@@ -823,9 +827,6 @@ MSG
|
|
823
827
|
# S => ;
|
824
828
|
# This grammar requires a time that is quadratic in the number of
|
825
829
|
# input tokens
|
826
|
-
|
827
|
-
t_x = Syntax::VerbatimSymbol.new('x')
|
828
|
-
|
829
830
|
builder = Syntax::GrammarBuilder.new
|
830
831
|
builder.add_terminals('a')
|
831
832
|
builder.add_production('S' => %w(a S))
|
@@ -909,10 +910,8 @@ MSG
|
|
909
910
|
]
|
910
911
|
compare_entry_texts(parse_result.chart[4], expected)
|
911
912
|
end
|
912
|
-
|
913
913
|
end # context
|
914
914
|
end # describe
|
915
915
|
end # module
|
916
916
|
end # module
|
917
|
-
|
918
|
-
# End of module
|
917
|
+
# End of file
|
@@ -79,8 +79,9 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
79
79
|
end
|
80
80
|
|
81
81
|
it 'should have no antecedence for the initial parse entry' do
|
82
|
-
|
83
|
-
expect(
|
82
|
+
antecedence = subject.antecedence
|
83
|
+
expect(antecedence.size).to eq(1)
|
84
|
+
expect(antecedence.fetch(subject.initial_entry)).to be_empty
|
84
85
|
end
|
85
86
|
|
86
87
|
=begin
|
@@ -186,7 +187,7 @@ SNIPPET
|
|
186
187
|
|
187
188
|
it 'should apply the scan rule correctly' do
|
188
189
|
# Filling manually first entry set...
|
189
|
-
fill_first_set
|
190
|
+
fill_first_set
|
190
191
|
# There are two entries expecting a terminal:
|
191
192
|
# ['A => . a A c', 'A => . b']
|
192
193
|
fourth_entry = subject.chart[0].entries[3] # 'A => . a A c'
|
@@ -201,15 +202,16 @@ SNIPPET
|
|
201
202
|
# Entry must be past the terminal symbol
|
202
203
|
expect(last_entry.vertex.label).to eq('A => a . A c')
|
203
204
|
expect(last_entry.origin).to eq(0)
|
204
|
-
|
205
|
+
antecedence = subject.antecedence
|
206
|
+
expect(antecedence.fetch(last_entry)).to eq([fourth_entry])
|
205
207
|
end
|
206
208
|
|
207
209
|
it 'should apply the exit rule correctly' do
|
208
210
|
# Filling manually first entry set...
|
209
|
-
fill_first_set
|
211
|
+
fill_first_set
|
210
212
|
|
211
213
|
# Initial manually first entry set...
|
212
|
-
seed_second_set
|
214
|
+
seed_second_set
|
213
215
|
|
214
216
|
# Given that the scanned token is 'b'...
|
215
217
|
# Then a new entry is added in next entry set
|
@@ -231,10 +233,10 @@ SNIPPET
|
|
231
233
|
|
232
234
|
it 'should apply the end rule correctly' do
|
233
235
|
# Filling manually first entry set...
|
234
|
-
fill_first_set
|
236
|
+
fill_first_set
|
235
237
|
|
236
238
|
# Initial manually first entry set...
|
237
|
-
seed_second_set
|
239
|
+
seed_second_set
|
238
240
|
last_entry = subject.chart[1].last
|
239
241
|
|
240
242
|
# Given that the scanned token is 'b'...
|
@@ -290,7 +292,6 @@ SNIPPET
|
|
290
292
|
end # context
|
291
293
|
|
292
294
|
context 'Parse forest building:' do
|
293
|
-
|
294
295
|
let(:sample_grammar1) do
|
295
296
|
builder = grammar_abc_builder
|
296
297
|
builder.grammar
|
@@ -318,7 +319,7 @@ SNIPPET
|
|
318
319
|
end
|
319
320
|
|
320
321
|
it 'should build a parse forest' do
|
321
|
-
expect{subject.parse_forest }.not_to raise_error
|
322
|
+
expect { subject.parse_forest }.not_to raise_error
|
322
323
|
|
323
324
|
end
|
324
325
|
=begin
|
@@ -31,7 +31,7 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
31
31
|
'A => a A . c',
|
32
32
|
'A => a A c .',
|
33
33
|
'A => . b',
|
34
|
-
'A => b .'
|
34
|
+
'A => b .'
|
35
35
|
]
|
36
36
|
expect(items.map(&:to_s)).to eq(expectations)
|
37
37
|
end
|
@@ -40,4 +40,4 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
40
40
|
end # module
|
41
41
|
end # module
|
42
42
|
|
43
|
-
# End of file
|
43
|
+
# End of file
|
@@ -20,10 +20,10 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
20
20
|
let(:t_a) { Rley::Syntax::Terminal.new('a') }
|
21
21
|
let(:t_b) { Rley::Syntax::Terminal.new('b') }
|
22
22
|
let(:t_c) { Rley::Syntax::Terminal.new('c') }
|
23
|
-
let(:
|
24
|
-
let(:repeated_prod) { build_prod(
|
23
|
+
let(:nt_rep_c) { Rley::Syntax::NonTerminal.new('Repetition') }
|
24
|
+
let(:repeated_prod) { build_prod(nt_rep_c, t_c, nt_rep_c) }
|
25
25
|
let(:nt_sentence) { Rley::Syntax::NonTerminal.new('Sentence') }
|
26
|
-
let(:sample_prod) { build_prod(nt_sentence, t_a, t_b, t_b,
|
26
|
+
let(:sample_prod) { build_prod(nt_sentence, t_a, t_b, t_b, nt_rep_c) }
|
27
27
|
|
28
28
|
let(:sample_item1) { Parser::DottedItem.new(sample_prod, 1) }
|
29
29
|
let(:sample_item2) { Parser::DottedItem.new(sample_prod, 2) }
|
@@ -88,7 +88,7 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
88
88
|
it 'should list the entries expecting a given non-terminal' do
|
89
89
|
# Case: an entry expecting a non-terminal
|
90
90
|
subject.push_entry(entry3)
|
91
|
-
expect(subject.entries4n_term(
|
91
|
+
expect(subject.entries4n_term(nt_rep_c)).to eq([entry3])
|
92
92
|
end
|
93
93
|
|
94
94
|
=begin
|
@@ -11,7 +11,6 @@ require_relative '../../../lib/rley/parser/parse_entry'
|
|
11
11
|
module Rley # Open this namespace to avoid module qualifier prefixes
|
12
12
|
module Parser # Open this namespace to avoid module qualifier prefixes
|
13
13
|
describe ParseEntry do
|
14
|
-
|
15
14
|
let(:t_a) { Syntax::Terminal.new('A') }
|
16
15
|
let(:t_b) { Syntax::Terminal.new('B') }
|
17
16
|
let(:t_c) { Syntax::Terminal.new('C') }
|
@@ -25,7 +24,6 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
25
24
|
Syntax::Production.new(nt_sentence, [])
|
26
25
|
end
|
27
26
|
|
28
|
-
|
29
27
|
let(:dotted_rule) { DottedItem.new(sample_prod, 2) }
|
30
28
|
let(:origin_val) { 3 }
|
31
29
|
let(:vertex_faked) { double('fake-vertex') }
|
@@ -5,7 +5,7 @@ require_relative '../../../lib/rley/parser/parse_walker_factory'
|
|
5
5
|
|
6
6
|
require_relative '../support/grammar_helper'
|
7
7
|
require_relative '../support/expectation_helper'
|
8
|
-
require_relative '../support/
|
8
|
+
require_relative '../support/grammar_l0_helper'
|
9
9
|
|
10
10
|
# Load the class under test
|
11
11
|
require_relative '../../../lib/rley/parser/parse_forest_builder'
|
@@ -23,13 +23,13 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
23
23
|
# contains a hidden left recursion and a cycle
|
24
24
|
builder = Syntax::GrammarBuilder.new
|
25
25
|
builder.add_terminals('a', 'b')
|
26
|
-
builder.add_production('Phi' =>
|
27
|
-
builder.add_production('S' => %w
|
28
|
-
builder.add_production('S' => %w
|
26
|
+
builder.add_production('Phi' => 'S')
|
27
|
+
builder.add_production('S' => %w(A T))
|
28
|
+
builder.add_production('S' => %w(a T))
|
29
29
|
builder.add_production('A' => 'a')
|
30
|
-
builder.add_production('A' => %w
|
30
|
+
builder.add_production('A' => %w(B A))
|
31
31
|
builder.add_production('B' => [])
|
32
|
-
builder.add_production('T' => %w(
|
32
|
+
builder.add_production('T' => %w(b b b))
|
33
33
|
builder.grammar
|
34
34
|
end
|
35
35
|
|
@@ -53,7 +53,9 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
53
53
|
|
54
54
|
# Emit a text representation of the current path.
|
55
55
|
def path_to_s()
|
56
|
-
text_parts = subject.curr_path.map
|
56
|
+
text_parts = subject.curr_path.map do |path_element|
|
57
|
+
path_element.to_string(0)
|
58
|
+
end
|
57
59
|
return text_parts.join('/')
|
58
60
|
end
|
59
61
|
|
@@ -70,10 +72,9 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
70
72
|
it 'should have an empty path' do
|
71
73
|
expect(subject.curr_path).to be_empty
|
72
74
|
end
|
73
|
-
end
|
75
|
+
end # context
|
74
76
|
|
75
77
|
context 'Parse forest construction' do
|
76
|
-
|
77
78
|
it 'should initialize the root node' do
|
78
79
|
first_event = walker.next
|
79
80
|
subject.receive_event(*first_event)
|
@@ -108,7 +109,8 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
108
109
|
event4 = walker.next
|
109
110
|
subject.receive_event(*event4)
|
110
111
|
|
111
|
-
|
112
|
+
parent_as_text = subject.curr_parent.to_string(0)
|
113
|
+
expect(parent_as_text).to eq('Alt(S => a T .)[0, 4]')
|
112
114
|
expected_path4 = 'Phi[0, 4]/S[0, 4]/Alt(S => a T .)[0, 4]'
|
113
115
|
expect(path_to_s).to eq(expected_path4)
|
114
116
|
expect(subject.curr_path[-2].refinement).to eq(:or)
|
@@ -136,7 +138,8 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
136
138
|
expect(path_to_s).to eq(expected_path6)
|
137
139
|
expect(subject.curr_parent.subnodes.size).to eq(1)
|
138
140
|
token_event6 = 'b[3, 4]'
|
139
|
-
|
141
|
+
child = subject.curr_parent.subnodes.first
|
142
|
+
expect(child.to_string(0)).to eq(token_event6)
|
140
143
|
|
141
144
|
event7 = walker.next
|
142
145
|
subject.receive_event(*event7)
|
@@ -146,7 +149,8 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
146
149
|
expect(path_to_s).to eq(expected_path7)
|
147
150
|
expect(subject.curr_parent.subnodes.size).to eq(2)
|
148
151
|
token_event7 = 'b[2, 3]'
|
149
|
-
|
152
|
+
child = subject.curr_parent.subnodes.first
|
153
|
+
expect(child.to_string(0)).to eq(token_event7)
|
150
154
|
|
151
155
|
event8 = walker.next
|
152
156
|
subject.receive_event(*event8)
|
@@ -156,7 +160,8 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
156
160
|
expect(path_to_s).to eq(expected_path8)
|
157
161
|
expect(subject.curr_parent.subnodes.size).to eq(3)
|
158
162
|
token_event8 = 'b[1, 2]'
|
159
|
-
|
163
|
+
child = subject.curr_parent.subnodes.first
|
164
|
+
expect(child.to_string(0)).to eq(token_event8)
|
160
165
|
|
161
166
|
event9 = walker.next
|
162
167
|
subject.receive_event(*event9)
|
@@ -168,19 +173,22 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
168
173
|
event10 = walker.next
|
169
174
|
subject.receive_event(*event10)
|
170
175
|
expect(event10[1].to_s).to eq('.T | 1')
|
171
|
-
|
176
|
+
parent_as_text = subject.curr_parent.to_string(0)
|
177
|
+
expect(parent_as_text).to eq('Alt(S => a T .)[0, 4]')
|
172
178
|
expected_path10 = 'Phi[0, 4]/S[0, 4]/Alt(S => a T .)[0, 4]'
|
173
179
|
expect(path_to_s).to eq(expected_path10)
|
174
180
|
|
175
181
|
event11 = walker.next
|
176
182
|
subject.receive_event(*event11)
|
177
183
|
expect(event11[1].to_s).to eq('S => a . T | 0')
|
178
|
-
|
184
|
+
parent_as_text = subject.curr_parent.to_string(0)
|
185
|
+
expect(parent_as_text).to eq('Alt(S => a T .)[0, 4]')
|
179
186
|
expected_path11 = 'Phi[0, 4]/S[0, 4]/Alt(S => a T .)[0, 4]'
|
180
187
|
expect(path_to_s).to eq(expected_path11)
|
181
188
|
expect(subject.curr_parent.subnodes.size).to eq(2)
|
182
189
|
token_event11 = 'a[0, 1]'
|
183
|
-
|
190
|
+
child = subject.curr_parent.subnodes.first
|
191
|
+
expect(child.to_string(0)).to eq(token_event11)
|
184
192
|
|
185
193
|
event12 = walker.next
|
186
194
|
subject.receive_event(*event12)
|
@@ -219,7 +227,7 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
219
227
|
|
220
228
|
event16 = walker.next
|
221
229
|
subject.receive_event(*event16)
|
222
|
-
expect(event16[0]).to eq(:backtrack)
|
230
|
+
expect(event16[0]).to eq(:backtrack) # Backtrack event!
|
223
231
|
expect(event16[1].to_s).to eq('S. | 0')
|
224
232
|
expect(subject.curr_parent.to_string(0)).to eq('S[0, 4]')
|
225
233
|
expected_path16 = 'Phi[0, 4]/S[0, 4]'
|
@@ -229,7 +237,8 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
229
237
|
event17 = walker.next
|
230
238
|
subject.receive_event(*event17)
|
231
239
|
expect(event17[1].to_s).to eq('S => A T . | 0')
|
232
|
-
|
240
|
+
parent_as_text = subject.curr_parent.to_string(0)
|
241
|
+
expect(parent_as_text).to eq('Alt(S => A T .)[0, 4]')
|
233
242
|
expected_path17 = 'Phi[0, 4]/S[0, 4]/Alt(S => A T .)[0, 4]'
|
234
243
|
expect(path_to_s).to eq(expected_path17)
|
235
244
|
expect(subject.curr_path[-2].refinement).to eq(:or)
|
@@ -243,16 +252,18 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
243
252
|
|
244
253
|
event18 = walker.next
|
245
254
|
subject.receive_event(*event18)
|
246
|
-
expect(event18[0]).to eq(:revisit)
|
255
|
+
expect(event18[0]).to eq(:revisit) # Revisit event!
|
247
256
|
expect(event18[1].to_s).to eq('T. | 1')
|
248
|
-
|
257
|
+
parent_as_text = subject.curr_parent.to_string(0)
|
258
|
+
expect(parent_as_text).to eq('Alt(S => A T .)[0, 4]')
|
249
259
|
expected_path18 = 'Phi[0, 4]/S[0, 4]/Alt(S => A T .)[0, 4]'
|
250
260
|
expect(path_to_s).to eq(expected_path18)
|
251
261
|
|
252
262
|
event19 = walker.next
|
253
263
|
subject.receive_event(*event19)
|
254
264
|
expect(event19[1].to_s).to eq('S => A . T | 0')
|
255
|
-
|
265
|
+
parent_as_text = subject.curr_parent.to_string(0)
|
266
|
+
expect(parent_as_text).to eq('Alt(S => A T .)[0, 4]')
|
256
267
|
expected_path19 = 'Phi[0, 4]/S[0, 4]/Alt(S => A T .)[0, 4]'
|
257
268
|
expect(path_to_s).to eq(expected_path19)
|
258
269
|
|
@@ -268,8 +279,10 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
268
279
|
event21 = walker.next
|
269
280
|
subject.receive_event(*event21)
|
270
281
|
expect(event21[1].to_s).to eq('A => a . | 0')
|
271
|
-
|
272
|
-
|
282
|
+
parent_as_text = subject.curr_parent.to_string(0)
|
283
|
+
expect(parent_as_text).to eq('Alt(A => a .)[0, 1]')
|
284
|
+
path_prefix = 'Phi[0, 4]/S[0, 4]/Alt(S => A T .)[0, 4]/A[0, 1]/'
|
285
|
+
expected_path21 = path_prefix + 'Alt(A => a .)[0, 1]'
|
273
286
|
expect(path_to_s).to eq(expected_path21)
|
274
287
|
expect(subject.curr_path[-2].refinement).to eq(:or)
|
275
288
|
|
@@ -283,7 +296,8 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
283
296
|
event23 = walker.next
|
284
297
|
subject.receive_event(*event23)
|
285
298
|
expect(event23[1].to_s).to eq('.A | 0')
|
286
|
-
|
299
|
+
parent_as_text = subject.curr_parent.to_string(0)
|
300
|
+
expect(parent_as_text).to eq('Alt(S => A T .)[0, 4]')
|
287
301
|
expected_path23 = 'Phi[0, 4]/S[0, 4]/Alt(S => A T .)[0, 4]'
|
288
302
|
expect(path_to_s).to eq(expected_path23)
|
289
303
|
|
@@ -318,7 +332,7 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
318
332
|
|
319
333
|
event27 = walker.next
|
320
334
|
subject.receive_event(*event27)
|
321
|
-
expect(event27[0]).to eq(:backtrack)
|
335
|
+
expect(event27[0]).to eq(:backtrack) # Backtrack event!
|
322
336
|
expect(event27[1].to_s).to eq('A. | 0')
|
323
337
|
expect(subject.curr_parent.to_string(0)).to eq('A[0, 1]')
|
324
338
|
expected_path27 = 'Phi[0, 4]/S[0, 4]/Alt(S => A T .)[0, 4]/A[0, 1]'
|
@@ -328,24 +342,28 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
328
342
|
subject.receive_event(*event28)
|
329
343
|
expect(event28[0]).to eq(:visit)
|
330
344
|
expect(event28[1].to_s).to eq('A => B A . | 0')
|
331
|
-
|
332
|
-
|
345
|
+
parent_as_text = subject.curr_parent.to_string(0)
|
346
|
+
expect(parent_as_text).to eq('Alt(A => B A .)[0, 1]')
|
347
|
+
path_prefix = 'Phi[0, 4]/S[0, 4]/Alt(S => A T .)[0, 4]/A[0, 1]/'
|
348
|
+
expected_path28 = path_prefix + 'Alt(A => B A .)[0, 1]'
|
333
349
|
expect(path_to_s).to eq(expected_path28)
|
334
350
|
|
335
351
|
event29 = walker.next
|
336
352
|
subject.receive_event(*event29)
|
337
|
-
expect(event29[0]).to eq(:revisit)
|
353
|
+
expect(event29[0]).to eq(:revisit) # Revisit event!
|
338
354
|
expect(event29[1].to_s).to eq('A. | 0')
|
339
|
-
|
340
|
-
|
355
|
+
parent_as_text = subject.curr_parent.to_string(0)
|
356
|
+
expect(parent_as_text).to eq('Alt(A => B A .)[0, 1]')
|
357
|
+
expected_path29 = path_prefix + 'Alt(A => B A .)[0, 1]'
|
341
358
|
expect(path_to_s).to eq(expected_path29)
|
342
359
|
|
343
360
|
event30 = walker.next
|
344
361
|
subject.receive_event(*event30)
|
345
362
|
expect(event30[0]).to eq(:visit)
|
346
363
|
expect(event30[1].to_s).to eq('A => B . A | 0')
|
347
|
-
|
348
|
-
|
364
|
+
parent_as_text = subject.curr_parent.to_string(0)
|
365
|
+
expect(parent_as_text).to eq('Alt(A => B A .)[0, 1]')
|
366
|
+
expected_path30 = path_prefix + 'Alt(A => B A .)[0, 1]'
|
349
367
|
expect(path_to_s).to eq(expected_path30)
|
350
368
|
|
351
369
|
event31 = walker.next
|
@@ -353,7 +371,7 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
353
371
|
expect(event31[0]).to eq(:visit)
|
354
372
|
expect(event31[1].to_s).to eq('B. | 0')
|
355
373
|
expect(subject.curr_parent.to_string(0)).to eq('B[0, 0]')
|
356
|
-
expected_path31 =
|
374
|
+
expected_path31 = path_prefix + 'Alt(A => B A .)[0, 1]/B[0, 0]'
|
357
375
|
expect(path_to_s).to eq(expected_path31)
|
358
376
|
|
359
377
|
event32 = walker.next
|
@@ -362,15 +380,16 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
362
380
|
# Empty production!
|
363
381
|
expect(event32[1].to_s).to eq('B => . | 0')
|
364
382
|
expect(subject.curr_parent.to_string(0)).to eq('B[0, 0]')
|
365
|
-
expected_path30 =
|
383
|
+
expected_path30 = path_prefix + 'Alt(A => B A .)[0, 1]/B[0, 0]'
|
366
384
|
expect(path_to_s).to eq(expected_path30)
|
367
385
|
|
368
386
|
event33 = walker.next
|
369
387
|
subject.receive_event(*event33)
|
370
388
|
expect(event33[0]).to eq(:visit)
|
371
389
|
expect(event33[1].to_s).to eq('.B | 0')
|
372
|
-
|
373
|
-
|
390
|
+
parent_as_text = subject.curr_parent.to_string(0)
|
391
|
+
expect(parent_as_text).to eq('Alt(A => B A .)[0, 1]')
|
392
|
+
expected_path33 = path_prefix + 'Alt(A => B A .)[0, 1]'
|
374
393
|
expect(path_to_s).to eq(expected_path33)
|
375
394
|
|
376
395
|
event34 = walker.next
|
@@ -378,14 +397,15 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
378
397
|
expect(event34[0]).to eq(:visit)
|
379
398
|
expect(event34[1].to_s).to eq('A => . B A | 0')
|
380
399
|
expect(subject.curr_parent.to_string(0)).to eq('A[0, 1]')
|
381
|
-
|
382
|
-
expect(path_to_s).to eq(
|
400
|
+
path34 = 'Phi[0, 4]/S[0, 4]/Alt(S => A T .)[0, 4]/A[0, 1]'
|
401
|
+
expect(path_to_s).to eq(path34)
|
383
402
|
|
384
403
|
event35 = walker.next
|
385
404
|
subject.receive_event(*event35)
|
386
405
|
expect(event35[0]).to eq(:revisit)
|
387
406
|
expect(event35[1].to_s).to eq('.A | 0')
|
388
|
-
|
407
|
+
parent_as_text = subject.curr_parent.to_string(0)
|
408
|
+
expect(parent_as_text).to eq('Alt(S => A T .)[0, 4]')
|
389
409
|
expected_path35 = 'Phi[0, 4]/S[0, 4]/Alt(S => A T .)[0, 4]'
|
390
410
|
expect(path_to_s).to eq(expected_path35)
|
391
411
|
|
@@ -410,18 +430,18 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
410
430
|
context 'Natural language processing' do
|
411
431
|
include GrammarL0Helper
|
412
432
|
|
413
|
-
let(:
|
414
|
-
builder =
|
433
|
+
let(:grammar_l0) do
|
434
|
+
builder = grammar_l0_builder
|
415
435
|
builder.grammar
|
416
436
|
end
|
417
437
|
|
418
438
|
let(:sentence_tokens) do
|
419
439
|
sentence = 'I prefer a morning flight'
|
420
|
-
|
440
|
+
tokenizer_l0(sentence, grammar_l0)
|
421
441
|
end
|
422
442
|
|
423
443
|
let(:sentence_result) do
|
424
|
-
parser = Parser::GFGEarleyParser.new(
|
444
|
+
parser = Parser::GFGEarleyParser.new(grammar_l0)
|
425
445
|
parser.parse(sentence_tokens)
|
426
446
|
end
|
427
447
|
|
@@ -500,7 +520,8 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
500
520
|
expect(path_to_s).to eq(expected_path8)
|
501
521
|
expect(subject.curr_parent.subnodes.size).to eq(1)
|
502
522
|
token_event8 = 'Noun[4, 5]'
|
503
|
-
|
523
|
+
child = subject.curr_parent.subnodes.first
|
524
|
+
expect(child.to_string(0)).to eq(token_event8)
|
504
525
|
|
505
526
|
event9 = walker.next
|
506
527
|
subject.receive_event(*event9)
|
@@ -515,27 +536,28 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
515
536
|
expect(event10[0]).to eq(:visit)
|
516
537
|
expect(event10[1].to_s).to eq('Nominal. | 3')
|
517
538
|
expect(subject.curr_parent.to_string(0)).to eq('Nominal[3, 4]')
|
518
|
-
|
519
|
-
expect(path_to_s).to eq(
|
539
|
+
path10 = 'S[0, 5]/VP[1, 5]/NP[2, 5]/Nominal[3, 5]/Nominal[3, 4]'
|
540
|
+
expect(path_to_s).to eq(path10)
|
520
541
|
|
521
542
|
event11 = walker.next
|
522
543
|
subject.receive_event(*event11)
|
523
544
|
expect(event11[0]).to eq(:visit)
|
524
545
|
expect(event11[1].to_s).to eq('Nominal => Noun . | 3')
|
525
546
|
expect(subject.curr_parent.to_string(0)).to eq('Nominal[3, 4]')
|
526
|
-
|
527
|
-
expect(path_to_s).to eq(
|
547
|
+
path11 = 'S[0, 5]/VP[1, 5]/NP[2, 5]/Nominal[3, 5]/Nominal[3, 4]'
|
548
|
+
expect(path_to_s).to eq(path11)
|
528
549
|
expect(subject.curr_parent.subnodes.size).to eq(1)
|
529
550
|
token_event11 = 'Noun[3, 4]'
|
530
|
-
|
551
|
+
child = subject.curr_parent.subnodes.first
|
552
|
+
expect(child.to_string(0)).to eq(token_event11)
|
531
553
|
|
532
554
|
event12 = walker.next
|
533
555
|
subject.receive_event(*event12)
|
534
556
|
expect(event12[0]).to eq(:visit)
|
535
557
|
expect(event12[1].to_s).to eq('Nominal => . Noun | 3')
|
536
558
|
expect(subject.curr_parent.to_string(0)).to eq('Nominal[3, 4]')
|
537
|
-
|
538
|
-
expect(path_to_s).to eq(
|
559
|
+
path12 = 'S[0, 5]/VP[1, 5]/NP[2, 5]/Nominal[3, 5]/Nominal[3, 4]'
|
560
|
+
expect(path_to_s).to eq(path12)
|
539
561
|
|
540
562
|
event13 = walker.next
|
541
563
|
subject.receive_event(*event13)
|
@@ -569,7 +591,8 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
569
591
|
expected_path16 = 'S[0, 5]/VP[1, 5]/NP[2, 5]'
|
570
592
|
expect(path_to_s).to eq(expected_path16)
|
571
593
|
token_event16 = 'Determiner[2, 3]'
|
572
|
-
|
594
|
+
child = subject.curr_parent.subnodes.first
|
595
|
+
expect(child.to_string(0)).to eq(token_event16)
|
573
596
|
|
574
597
|
event17 = walker.next
|
575
598
|
subject.receive_event(*event17)
|
@@ -595,7 +618,8 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
595
618
|
expected_path19 = 'S[0, 5]/VP[1, 5]'
|
596
619
|
expect(path_to_s).to eq(expected_path19)
|
597
620
|
token_event19 = 'Verb[1, 2]'
|
598
|
-
|
621
|
+
child = subject.curr_parent.subnodes.first
|
622
|
+
expect(child.to_string(0)).to eq(token_event19)
|
599
623
|
|
600
624
|
event20 = walker.next
|
601
625
|
subject.receive_event(*event20)
|
@@ -637,8 +661,9 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
637
661
|
expected_path24 = 'S[0, 5]/NP[0, 1]'
|
638
662
|
expect(path_to_s).to eq(expected_path24)
|
639
663
|
token_event24 = 'Pronoun[0, 1]'
|
640
|
-
|
641
|
-
|
664
|
+
child = subject.curr_parent.subnodes.first
|
665
|
+
expect(child.to_string(0)).to eq(token_event24)
|
666
|
+
|
642
667
|
event25 = walker.next
|
643
668
|
subject.receive_event(*event25)
|
644
669
|
expect(event25[0]).to eq(:visit)
|
@@ -668,10 +693,10 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
668
693
|
expect(event28[0]).to eq(:visit)
|
669
694
|
expect(event28[1].to_s).to eq('.S | 0')
|
670
695
|
expected_path28 = ''
|
671
|
-
expect(path_to_s).to eq(expected_path28)
|
696
|
+
expect(path_to_s).to eq(expected_path28)
|
672
697
|
end
|
673
698
|
end # context
|
674
699
|
end # describe
|
675
700
|
end # module
|
676
701
|
end # module
|
677
|
-
# End of file
|
702
|
+
# End of file
|