rley 0.4.01 → 0.4.02

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +4 -0
  3. data/LICENSE.txt +2 -2
  4. data/README.md +3 -3
  5. data/examples/NLP/mini_en_demo.rb +1 -1
  6. data/examples/data_formats/JSON/JSON_demo.rb +1 -0
  7. data/examples/data_formats/JSON/JSON_lexer.rb +4 -4
  8. data/examples/general/calc/calc_lexer.rb +2 -2
  9. data/lib/rley.rb +1 -1
  10. data/lib/rley/constants.rb +1 -1
  11. data/lib/rley/formatter/debug.rb +2 -2
  12. data/lib/rley/formatter/json.rb +4 -4
  13. data/lib/rley/parse_tree_visitor.rb +9 -9
  14. data/lib/rley/parser/base_parser.rb +1 -1
  15. data/lib/rley/parser/gfg_parsing.rb +9 -0
  16. data/lib/rley/parser/parse_tree_builder.rb +176 -126
  17. data/lib/rley/parser/parse_tree_factory.rb +57 -0
  18. data/lib/rley/ptree/non_terminal_node.rb +10 -9
  19. data/lib/rley/ptree/parse_tree_node.rb +10 -5
  20. data/lib/rley/ptree/terminal_node.rb +14 -6
  21. data/lib/rley/sppf/sppf_node.rb +2 -2
  22. data/lib/rley/{parser → tokens}/token.rb +1 -4
  23. data/lib/rley/{ptree → tokens}/token_range.rb +1 -1
  24. data/spec/rley/formatter/debug_spec.rb +16 -16
  25. data/spec/rley/formatter/json_spec.rb +8 -8
  26. data/spec/rley/parse_forest_visitor_spec.rb +1 -1
  27. data/spec/rley/parse_tree_visitor_spec.rb +28 -28
  28. data/spec/rley/parser/error_reason_spec.rb +3 -3
  29. data/spec/rley/parser/gfg_chart_spec.rb +2 -2
  30. data/spec/rley/parser/gfg_earley_parser_spec.rb +2 -2
  31. data/spec/rley/parser/gfg_parsing_spec.rb +2 -2
  32. data/spec/rley/parser/groucho_spec.rb +1 -1
  33. data/spec/rley/parser/parse_tracer_spec.rb +2 -2
  34. data/spec/rley/parser/parse_tree_builder_spec.rb +213 -140
  35. data/spec/rley/parser/parse_tree_factory_spec.rb +85 -0
  36. data/spec/rley/parser/parse_walker_factory_spec.rb +11 -10
  37. data/spec/rley/ptree/non_terminal_node_spec.rb +23 -20
  38. data/spec/rley/ptree/terminal_node_spec.rb +7 -12
  39. data/spec/rley/sppf/alternative_node_spec.rb +2 -2
  40. data/spec/rley/sppf/non_terminal_node_spec.rb +2 -2
  41. data/spec/rley/support/ambiguous_grammar_helper.rb +2 -2
  42. data/spec/rley/support/expectation_helper.rb +1 -1
  43. data/spec/rley/support/grammar_ambig01_helper.rb +2 -2
  44. data/spec/rley/support/grammar_b_expr_helper.rb +2 -2
  45. data/spec/rley/support/grammar_helper.rb +3 -3
  46. data/spec/rley/support/grammar_l0_helper.rb +2 -2
  47. data/spec/rley/support/grammar_pb_helper.rb +2 -2
  48. data/spec/rley/{ptree → tokens}/token_range_spec.rb +2 -2
  49. data/spec/rley/{parser → tokens}/token_spec.rb +2 -2
  50. metadata +11 -17
  51. data/lib/rley/parser/chart.rb +0 -82
  52. data/lib/rley/parser/earley_parser.rb +0 -203
  53. data/lib/rley/parser/parsing.rb +0 -265
  54. data/spec/rley/parser/chart_spec.rb +0 -120
  55. data/spec/rley/parser/earley_parser_spec.rb +0 -710
  56. data/spec/rley/parser/parsing_spec.rb +0 -408
@@ -1,5 +1,5 @@
1
1
  require_relative '../../spec_helper'
2
- require_relative '../../../lib/rley/parser/token'
2
+ require_relative '../../../lib/rley/tokens/token'
3
3
 
4
4
  # Load the class under test
5
5
  require_relative '../../../lib/rley/parser/error_reason'
@@ -61,7 +61,7 @@ module Rley # Open this namespace to avoid module qualifier prefixes
61
61
  describe UnexpectedToken do
62
62
  let(:err_lexeme) { '-'}
63
63
  let(:err_terminal) { Syntax::Terminal.new('MINUS') }
64
- let(:err_token) { Token.new(err_lexeme, err_terminal) }
64
+ let(:err_token) { Tokens::Token.new(err_lexeme, err_terminal) }
65
65
  let(:terminals) do
66
66
  ['PLUS', 'LPAREN'].map { |name| Syntax::Terminal.new(name) }
67
67
  end
@@ -90,7 +90,7 @@ MSG_END
90
90
  describe PrematureInputEnd do
91
91
  let(:err_lexeme) { '+'}
92
92
  let(:err_terminal) { Syntax::Terminal.new('PLUS') }
93
- let(:err_token) { Token.new(err_lexeme, err_terminal) }
93
+ let(:err_token) { Tokens::Token.new(err_lexeme, err_terminal) }
94
94
  let(:terminals) do
95
95
  ['INT', 'LPAREN'].map { |name| Syntax::Terminal.new(name) }
96
96
  end
@@ -2,7 +2,7 @@ require_relative '../../spec_helper'
2
2
  require 'stringio'
3
3
 
4
4
  require_relative '../../../lib/rley/syntax/terminal'
5
- require_relative '../../../lib/rley/parser/token'
5
+ require_relative '../../../lib/rley/tokens/token'
6
6
  require_relative '../../../lib/rley/gfg/start_vertex'
7
7
  require_relative '../../../lib/rley/parser/parse_entry'
8
8
  require_relative '../../../lib/rley/parser/parse_tracer'
@@ -39,7 +39,7 @@ module Rley # Open this namespace to avoid module qualifier prefixes
39
39
 
40
40
  let(:token_seq) do
41
41
  literals = %w(a a b c c)
42
- literals.map { |lexeme| Token.new(lexeme, nil) }
42
+ literals.map { |lexeme| Tokens::Token.new(lexeme, nil) }
43
43
  end
44
44
 
45
45
  # Helper method. Create an array of dotted items
@@ -4,7 +4,7 @@ require_relative '../../../lib/rley/syntax/verbatim_symbol'
4
4
  require_relative '../../../lib/rley/syntax/non_terminal'
5
5
  require_relative '../../../lib/rley/syntax/production'
6
6
  require_relative '../../../lib/rley/syntax/grammar_builder'
7
- require_relative '../../../lib/rley/parser/token'
7
+ require_relative '../../../lib/rley/tokens/token'
8
8
  require_relative '../../../lib/rley/parser/dotted_item'
9
9
  require_relative '../../../lib/rley/parser/gfg_parsing'
10
10
 
@@ -288,7 +288,7 @@ module Rley # Open this namespace to avoid module qualifier prefixes
288
288
  builder.add_terminals(t_x)
289
289
  builder.add_production('Ss' => %w(A A x))
290
290
  builder.add_production('A' => [])
291
- tokens = [ Token.new('x', t_x) ]
291
+ tokens = [ Tokens::Token.new('x', t_x) ]
292
292
 
293
293
  instance = GFGEarleyParser.new(builder.grammar)
294
294
  expect { instance.parse(tokens) }.not_to raise_error
@@ -6,7 +6,7 @@ require_relative '../../../lib/rley/syntax/verbatim_symbol'
6
6
  require_relative '../../../lib/rley/syntax/production'
7
7
  require_relative '../../../lib/rley/syntax/grammar_builder'
8
8
  require_relative '../../../lib/rley/parser/dotted_item'
9
- require_relative '../../../lib/rley/parser/token'
9
+ require_relative '../../../lib/rley/tokens/token'
10
10
  require_relative '../../../lib/rley/parser/parse_tracer'
11
11
  require_relative '../../../lib/rley/gfg/grm_flow_graph'
12
12
  require_relative '../../../lib/rley/parser/grm_items_builder'
@@ -298,7 +298,7 @@ SNIPPET
298
298
 
299
299
  let(:token_seq1) do
300
300
  %w(a a b c c).map do |letter|
301
- Token.new(letter, sample_grammar1.name2symbol[letter])
301
+ Tokens::Token.new(letter, sample_grammar1.name2symbol[letter])
302
302
  end
303
303
  end
304
304
 
@@ -54,7 +54,7 @@ module Rley # Open this namespace to avoid module qualifier prefixes
54
54
  raise StandardError, "Word '#{word}' not found in lexicon"
55
55
  end
56
56
  terminal = aGrammar.name2symbol[term_name]
57
- Rley::Parser::Token.new(word, terminal)
57
+ Rley::Tokens::Token.new(word, terminal)
58
58
  end
59
59
 
60
60
  return tokens
@@ -6,7 +6,7 @@ require_relative '../../../lib/rley/syntax/non_terminal'
6
6
  require_relative '../../../lib/rley/syntax/production'
7
7
  require_relative '../../../lib/rley/parser/dotted_item'
8
8
  require_relative '../../../lib/rley/parser/parse_state'
9
- require_relative '../../../lib/rley/parser/token'
9
+ require_relative '../../../lib/rley/tokens/token'
10
10
 
11
11
  # Load the class under test
12
12
  require_relative '../../../lib/rley/parser/parse_tracer'
@@ -18,7 +18,7 @@ module Rley # Open this namespace to avoid module qualifier prefixes
18
18
 
19
19
  let(:token_seq) do
20
20
  literals = %w(I saw John with a dog)
21
- literals.map { |lexeme| Token.new(lexeme, nil) }
21
+ literals.map { |lexeme| Tokens::Token.new(lexeme, nil) }
22
22
  end
23
23
 
24
24
  subject { ParseTracer.new(1, output, token_seq) }
@@ -1,178 +1,251 @@
1
1
  require_relative '../../spec_helper'
2
- require_relative '../../../lib/rley/parser/token'
3
- require_relative '../../../lib/rley/parser/earley_parser'
4
- require_relative '../../../lib/rley/parser/parsing'
2
+
3
+ require_relative '../../../lib/rley/parser/gfg_earley_parser'
4
+ require_relative '../../../lib/rley/parser/parse_walker_factory'
5
+
6
+ require_relative '../support/expectation_helper'
7
+ require_relative '../support/grammar_b_expr_helper'
8
+
5
9
  # Load the class under test
6
10
  require_relative '../../../lib/rley/parser/parse_tree_builder'
7
- require_relative '../support/grammar_abc_helper'
8
11
 
9
12
  module Rley # Open this namespace to avoid module qualifier prefixes
10
- module Parser # Open this namespace to avoid module qualifier prefixes
13
+ module Parser
11
14
  describe ParseTreeBuilder do
12
- include GrammarABCHelper # Mix-in module with builder for grammar abc
15
+ include ExpectationHelper # Mix-in with expectation on parse entry sets
16
+ include GrammarBExprHelper # Mix-in for basic arithmetic language
13
17
 
14
- let(:grammar_abc) do
15
- builder = grammar_abc_builder
16
- builder.grammar
18
+ let(:sample_grammar) do
19
+ builder = grammar_expr_builder()
20
+ builder.grammar
17
21
  end
18
22
 
19
- let(:capital_a) { grammar_abc.name2symbol['A'] }
20
- let(:capital_s) { grammar_abc.name2symbol['S'] }
21
- let(:small_a) { grammar_abc.name2symbol['a'] }
22
- let(:small_b) { grammar_abc.name2symbol['b'] }
23
- let(:small_c) { grammar_abc.name2symbol['c'] }
23
+ let(:sample_tokens) do
24
+ expr_tokenizer('2 + 3 * 4', sample_grammar)
25
+ end
24
26
 
25
- let(:start_prod) { grammar_abc.start_production }
27
+ let(:sample_result) do
28
+ parser = Parser::GFGEarleyParser.new(sample_grammar)
29
+ parser.parse(sample_tokens)
30
+ end
31
+
32
+ subject { ParseTreeBuilder.new(sample_tokens) }
26
33
 
27
- let(:tokens_abc) do
28
- %w(a a b c c).map do |letter|
29
- Token.new(letter, grammar_abc.name2symbol[letter])
34
+ # Emit a text representation of the current path.
35
+ def path_to_s()
36
+ text_parts = subject.curr_path.map do |path_element|
37
+ path_element.to_s
30
38
  end
39
+ return text_parts.join('/')
31
40
  end
32
41
 
33
- let(:sample_parsing) do
34
- parser = EarleyParser.new(grammar_abc)
35
- parser.parse(tokens_abc)
42
+ def next_event(eventType, anEntryText)
43
+ event = @walker.next
44
+ subject.receive_event(*event)
45
+ expect(event[0]).to eq(eventType)
46
+ expect(event[1].to_s).to eq(anEntryText)
36
47
  end
37
48
 
38
- subject { ParseTreeBuilder.new(start_prod, low: 0, high: 5) }
49
+ def expected_curr_parent(anExpectation)
50
+ expect(subject.curr_parent.to_string(0)).to eq(anExpectation)
51
+ end
52
+
53
+ def expected_curr_path(anExpectation)
54
+ expect(path_to_s).to eq(anExpectation)
55
+ end
56
+
57
+ def expected_first_child(anExpectation)
58
+ child = subject.curr_parent.subnodes.first
59
+ expect(child.to_string(0)).to eq(anExpectation)
60
+ end
39
61
 
40
62
  context 'Initialization:' do
41
- it 'should be created with a proposition and a range' do
42
- expect { ParseTreeBuilder.new(start_prod, {}) }.not_to raise_error
63
+ it 'should be created with a sequence of tokens' do
64
+ expect { ParseTreeBuilder.new(sample_tokens) }.not_to raise_error
43
65
  end
44
66
 
45
- it 'should have a root node at start' do
46
- expect(subject.root.symbol).to eq(capital_s)
67
+ it 'should know the input tokens' do
68
+ expect(subject.tokens).to eq(sample_tokens)
47
69
  end
48
70
 
49
- it 'should have current path at start' do
50
- expect(subject.current_path).not_to be_empty
71
+ it 'should have an empty path' do
72
+ expect(subject.curr_path).to be_empty
51
73
  end
74
+ end # context
52
75
 
53
- it 'should have current node at start' do
54
- expect(subject.current_node.symbol).to eq(capital_a)
76
+ context 'Parse tree construction:' do
77
+ before(:each) do
78
+ factory = ParseWalkerFactory.new
79
+ accept_entry = sample_result.accepting_entry
80
+ accept_index = sample_result.chart.last_index
81
+ @walker = factory.build_walker(accept_entry, accept_index)
55
82
  end
56
- end # context
57
83
 
58
- context 'Adding nodes to parse tree:' do
59
- it 'should process parse state for a non-terminal node' do
60
- # Expectation:
61
- # S[0, 5]
62
- # +- A[0,5]
63
- expect(subject.root.symbol).to eq(capital_s)
64
- expect(subject.root.children.size).to eq(1)
65
- child1 = subject.root.children[0]
66
- expect(child1.symbol).to eq(capital_a)
67
- expect(child1.range.low).to eq(0)
68
- expect(child1.range.high).to eq(5)
69
- expect(subject.current_node).to eq(child1)
70
-
71
- # Add children to A
72
- other_state = sample_parsing.chart.state_sets.last.states.first
73
- subject.use_complete_state(other_state)
74
-
75
- # Tree is:
76
- # S[0,5]
77
- # +- A[0,5]
78
- # +- a[0, ?]
79
- # +- A[?, ?]
80
- # +- c[?, 5]
81
- expect(child1.children.size).to eq(3) # a A c
82
- %w(a A c).each_with_index do |letter, i|
83
- grm_symbol = grammar_abc.name2symbol[letter]
84
- expect(child1.children[i].symbol).to eq(grm_symbol)
85
- end
86
- expect(child1.children[0].range.low).to eq(0)
87
- expect(child1.children[-1].range.high).to eq(5)
88
-
89
- subject.move_down # ... to c
90
- subject.range = { low: 4 }
91
- expect(child1.children[-1].range.low).to eq(4)
92
- expect(child1.children.last).to eq(subject.current_node)
93
- subject.move_back # ... to A
94
- expect(subject.current_node).to eq(child1.children[1])
95
- grand_child_A = subject.current_node
96
-
97
- other_state = sample_parsing.chart.state_sets[4].first
98
- subject.use_complete_state(other_state)
99
- expect(grand_child_A.children.size).to eq(3) # a A c
100
- %w(a A c).each_with_index do |letter, i|
101
- grm_symbol = grammar_abc.name2symbol[letter]
102
- expect(grand_child_A.children[i].symbol).to eq(grm_symbol)
103
- end
84
+ it 'should initialize the root node' do
85
+ next_event(:visit, 'P. | 0')
86
+ tree = subject.tree
87
+
88
+ expect(tree.root.to_string(0)).to eq('P[0, 5]')
89
+ expected_curr_path('P[0, 5]')
104
90
  end
105
- end # context
106
91
 
107
- context 'Moving the current node:' do
108
- it 'should move down to last child' do
109
- # Tree is:
110
- # S[0,?]
111
- # +- A[0,?]
112
-
113
- # Add children to A
114
- parse_state = sample_parsing.chart.state_sets.last.states.first
115
- subject.use_complete_state(parse_state)
116
-
117
- # Tree is:
118
- # S[0,?]
119
- # +- A[0,?]
120
- # +- a[0, ?]
121
- # +- A[?, ?]
122
- # +- c[?, ?]
123
- subject.move_down # ...to grand-child c
124
- expect(subject.current_node.symbol).to eq(small_c)
125
-
126
-
127
- subject.move_back # ...to grand-child A
128
- expect(subject.current_node.symbol).to eq(capital_a)
129
-
130
- # Add more children
131
- other_state = sample_parsing.chart.state_sets[4].states.first
132
- subject.use_complete_state(other_state)
133
-
134
- # Tree is:
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)
146
-
147
- subject.move_down # ...to grand-grand-child c
148
- expect(subject.current_node.to_string(0)).to eq("c[?, ?]: '(nil)'")
149
-
150
- subject.move_back # ...to grand-grand-child A
151
- expect(subject.current_node.to_string(0)).to eq('A[?, ?]')
152
-
153
- subject.move_back # ...to grand-grand-child a
154
- expect(subject.current_node.to_string(0)).to eq("a[1, ?]: '(nil)'")
155
-
156
- subject.move_back # ...to grand-child a
157
- expect(subject.current_node.to_string(0)).to eq("a[0, ?]: '(nil)'")
158
-
159
- subject.move_back # ...to S
160
- expect(subject.current_node.symbol).to eq(capital_s)
92
+ it 'should initialize the first child of the root node' do
93
+ next_event(:visit, 'P. | 0') # Event 1
94
+ next_event(:visit, 'P => S . | 0') # Event 2
95
+ next_event(:visit, 'S. | 0') # Event 3
96
+ next_event(:visit, 'S => S + M . | 0') # Event 4
97
+ expected_curr_path('P[0, 5]/S[0, 5]')
98
+ next_event(:visit, 'M. | 2') # Event 5
99
+ expected_curr_path('P[0, 5]/S[0, 5]/M[2, 5]')
100
+ next_event(:visit, 'M => M * T . | 2') # Event 6
101
+ next_event(:visit, 'T. | 4') # Event 7
102
+ expected_curr_path('P[0, 5]/S[0, 5]/M[2, 5]/T[4, 5]')
103
+ next_event(:visit, 'T => integer . | 4') # Event 8
161
104
  end
162
105
 
163
- it 'should move through deeply nested structure' do
106
+ it 'should build token node when scan edge was detected' do
107
+ 8.times do
108
+ event = @walker.next
109
+ subject.receive_event(*event)
110
+ end
111
+
112
+ next_event(:visit, 'T => . integer | 4') # Event 9
113
+ expected_curr_path('P[0, 5]/S[0, 5]/M[2, 5]/T[4, 5]')
114
+ expected_first_child("integer[4, 5]: '4'")
115
+ expect(subject.curr_parent.subnodes.size).to eq(1)
164
116
  end
165
- end # context
166
117
 
167
- context 'Parse tree building:' do
168
- it 'should build a parse tree' do
169
- expect(subject.parse_tree).to be_kind_of(PTree::ParseTree)
170
- actual = subject.parse_tree
171
- expect(actual.root).to eq(subject.root)
118
+ it 'should handle the remaining events' do
119
+ 9.times do
120
+ event = @walker.next
121
+ subject.receive_event(*event)
122
+ end
123
+
124
+ next_event(:visit, '.T | 4') # Event 10
125
+ expected_curr_path('P[0, 5]/S[0, 5]/M[2, 5]')
126
+
127
+ next_event(:visit, 'M => M * . T | 2') # Event 11
128
+
129
+ next_event(:visit, 'M => M . * T | 2') # Event 12
130
+ expected_curr_path('P[0, 5]/S[0, 5]/M[2, 5]')
131
+ expect(subject.curr_parent.subnodes.size).to eq(2)
132
+ expected_first_child("*[3, 4]: '*'")
133
+
134
+ next_event(:visit, 'M. | 2') # Event 13
135
+ expected_curr_path('P[0, 5]/S[0, 5]/M[2, 5]/M[2, 3]')
136
+
137
+ next_event(:visit, 'M => T . | 2') # Event 14
138
+ expected_curr_path('P[0, 5]/S[0, 5]/M[2, 5]/M[2, 3]')
139
+
140
+ next_event(:visit, 'T. | 2') # Event 15
141
+ expected_curr_path('P[0, 5]/S[0, 5]/M[2, 5]/M[2, 3]/T[2, 3]')
142
+
143
+ next_event(:visit, 'T => integer . | 2') # Event 16
144
+ expected_curr_path('P[0, 5]/S[0, 5]/M[2, 5]/M[2, 3]/T[2, 3]')
145
+ expect(subject.curr_parent.subnodes.size).to eq(1)
146
+ expected_first_child("integer[2, 3]: '3'")
147
+
148
+ next_event(:visit, 'T => . integer | 2') # Event 17
149
+
150
+ next_event(:visit, '.T | 2') # Event 18
151
+ expected_curr_path('P[0, 5]/S[0, 5]/M[2, 5]/M[2, 3]')
152
+
153
+ next_event(:visit, 'M => . T | 2') # Event 19
154
+ expected_curr_path('P[0, 5]/S[0, 5]/M[2, 5]/M[2, 3]')
155
+
156
+ next_event(:visit, '.M | 2') # Event 20
157
+ expected_curr_path('P[0, 5]/S[0, 5]/M[2, 5]')
158
+
159
+ next_event(:visit, 'M => . M * T | 2') # Event 21
160
+ expected_curr_path('P[0, 5]/S[0, 5]/M[2, 5]')
161
+
162
+ next_event(:revisit, '.M | 2') # Revisit Event 22
163
+ expected_curr_path('P[0, 5]/S[0, 5]')
164
+
165
+ next_event(:visit, 'S => S + . M | 0') # Event 23
166
+ expected_curr_path('P[0, 5]/S[0, 5]')
167
+
168
+ next_event(:visit, 'S => S . + M | 0') # Event 24
169
+ expected_curr_path('P[0, 5]/S[0, 5]')
170
+ expect(subject.curr_parent.subnodes.size).to eq(2)
171
+ expected_first_child("+[1, 2]: '+'")
172
+
173
+ next_event(:visit, 'S. | 0') # Event 25
174
+ expected_curr_path('P[0, 5]/S[0, 5]/S[0, 1]')
175
+
176
+ next_event(:visit, 'S => M . | 0') # Event 26
177
+ expected_curr_path('P[0, 5]/S[0, 5]/S[0, 1]')
178
+
179
+ next_event(:visit, 'M. | 0') # Event 27
180
+ expected_curr_path('P[0, 5]/S[0, 5]/S[0, 1]/M[0, 1]')
181
+
182
+ next_event(:visit, 'M => T . | 0') # Event 28
183
+ expected_curr_path('P[0, 5]/S[0, 5]/S[0, 1]/M[0, 1]')
184
+
185
+ next_event(:visit, 'T. | 0') # Event 29
186
+ expected_curr_path('P[0, 5]/S[0, 5]/S[0, 1]/M[0, 1]/T[0, 1]')
187
+
188
+ next_event(:visit, 'T => integer . | 0') # Event 30
189
+ expected_curr_path('P[0, 5]/S[0, 5]/S[0, 1]/M[0, 1]/T[0, 1]')
190
+
191
+ next_event(:visit, 'T => . integer | 0') # Event 31
192
+ expected_curr_path('P[0, 5]/S[0, 5]/S[0, 1]/M[0, 1]/T[0, 1]')
193
+ expect(subject.curr_parent.subnodes.size).to eq(1)
194
+ expected_first_child("integer[0, 1]: '2'")
195
+
196
+ next_event(:visit, '.T | 0') # Event 32
197
+ expected_curr_path('P[0, 5]/S[0, 5]/S[0, 1]/M[0, 1]')
198
+
199
+ next_event(:visit, 'M => . T | 0') # Event 33
200
+ expected_curr_path('P[0, 5]/S[0, 5]/S[0, 1]/M[0, 1]')
201
+
202
+ next_event(:visit, '.M | 0') # Event 34
203
+ expected_curr_path('P[0, 5]/S[0, 5]/S[0, 1]')
204
+
205
+ next_event(:visit, 'S => . M | 0') # Event 35
206
+ expected_curr_path('P[0, 5]/S[0, 5]/S[0, 1]')
207
+
208
+ next_event(:visit, '.S | 0') # Event 36
209
+ expected_curr_path('P[0, 5]/S[0, 5]')
210
+
211
+ next_event(:visit, 'S => . S + M | 0') # Event 37
212
+ expected_curr_path('P[0, 5]/S[0, 5]')
213
+
214
+ next_event(:revisit, '.S | 0') # Event 38
215
+ expected_curr_path('P[0, 5]')
216
+
217
+ next_event(:visit, 'P => . S | 0') # Event 39
218
+ expected_curr_path('P[0, 5]')
219
+
220
+ next_event(:visit, '.P | 0') # Event 39
221
+ expect(path_to_s).to be_empty
222
+ end
223
+
224
+ it 'should build parse trees' do
225
+ loop do
226
+ event = @walker.next
227
+ subject.receive_event(*event)
228
+ break if path_to_s.empty?
229
+ end
230
+
231
+ # Lightweight sanity check
232
+ expect(subject.tree).not_to be_nil
233
+ expect(subject.tree).to be_kind_of(PTree::ParseTree)
234
+ expect(subject.tree.root.to_s).to eq('P[0, 5]')
235
+ expect(subject.tree.root.subnodes.size).to eq(1)
236
+ child_node = subject.tree.root.subnodes[0]
237
+ expect(child_node.to_s).to eq('S[0, 5]')
238
+ expect(child_node.subnodes.size).to eq(3)
239
+ first_grandchild = child_node.subnodes[0]
240
+ expect(first_grandchild.to_s).to eq('S[0, 1]')
241
+ second_grandchild = child_node.subnodes[1]
242
+ expect(second_grandchild.to_s).to eq("+[1, 2]: '+'")
243
+ third_grandchild = child_node.subnodes[2]
244
+ expect(third_grandchild.to_s).to eq('M[2, 5]')
172
245
  end
173
246
  end # context
247
+
174
248
  end # describe
175
249
  end # module
176
250
  end # module
177
-
178
251
  # End of file