rley 0.4.01 → 0.4.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.
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