dendroid 0.1.00 → 0.2.00

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +3 -0
  3. data/lib/dendroid/formatters/ascii_tree.rb +142 -0
  4. data/lib/dendroid/formatters/base_formatter.rb +25 -0
  5. data/lib/dendroid/formatters/bracket_notation.rb +50 -0
  6. data/lib/dendroid/grm_analysis/dotted_item.rb +46 -30
  7. data/lib/dendroid/grm_analysis/grm_analyzer.rb +2 -4
  8. data/lib/dendroid/grm_analysis/{choice_items.rb → rule_items.rb} +10 -10
  9. data/lib/dendroid/grm_dsl/base_grm_builder.rb +3 -4
  10. data/lib/dendroid/parsing/and_node.rb +56 -0
  11. data/lib/dendroid/parsing/chart_walker.rb +293 -0
  12. data/lib/dendroid/parsing/composite_parse_node.rb +21 -0
  13. data/lib/dendroid/parsing/empty_rule_node.rb +28 -0
  14. data/lib/dendroid/parsing/or_node.rb +51 -0
  15. data/lib/dendroid/parsing/parse_node.rb +26 -0
  16. data/lib/dendroid/parsing/parse_tree_visitor.rb +127 -0
  17. data/lib/dendroid/parsing/parser.rb +185 -0
  18. data/lib/dendroid/parsing/terminal_node.rb +32 -0
  19. data/lib/dendroid/parsing/walk_progress.rb +117 -0
  20. data/lib/dendroid/recognizer/chart.rb +8 -0
  21. data/lib/dendroid/recognizer/e_item.rb +21 -2
  22. data/lib/dendroid/recognizer/item_set.rb +7 -2
  23. data/lib/dendroid/recognizer/recognizer.rb +33 -20
  24. data/lib/dendroid/syntax/grammar.rb +1 -1
  25. data/lib/dendroid/syntax/rule.rb +71 -13
  26. data/spec/dendroid/grm_analysis/dotted_item_spec.rb +59 -47
  27. data/spec/dendroid/grm_analysis/{choice_items_spec.rb → rule_items_spec.rb} +5 -6
  28. data/spec/dendroid/parsing/chart_walker_spec.rb +223 -0
  29. data/spec/dendroid/parsing/terminal_node_spec.rb +36 -0
  30. data/spec/dendroid/recognizer/e_item_spec.rb +5 -5
  31. data/spec/dendroid/recognizer/item_set_spec.rb +16 -8
  32. data/spec/dendroid/recognizer/recognizer_spec.rb +57 -5
  33. data/spec/dendroid/support/sample_grammars.rb +2 -0
  34. data/spec/dendroid/syntax/grammar_spec.rb +16 -21
  35. data/spec/dendroid/syntax/rule_spec.rb +56 -7
  36. data/version.txt +1 -1
  37. metadata +20 -13
  38. data/lib/dendroid/grm_analysis/alternative_item.rb +0 -70
  39. data/lib/dendroid/grm_analysis/production_items.rb +0 -55
  40. data/lib/dendroid/syntax/choice.rb +0 -95
  41. data/lib/dendroid/syntax/production.rb +0 -82
  42. data/spec/dendroid/grm_analysis/alternative_item_spec.rb +0 -12
  43. data/spec/dendroid/grm_analysis/production_items_spec.rb +0 -68
  44. data/spec/dendroid/syntax/choice_spec.rb +0 -68
  45. data/spec/dendroid/syntax/production_spec.rb +0 -92
@@ -4,28 +4,29 @@ require_relative '..\..\spec_helper'
4
4
  require_relative '..\..\..\lib\dendroid\syntax\terminal'
5
5
  require_relative '..\..\..\lib\dendroid\syntax\non_terminal'
6
6
  require_relative '..\..\..\lib\dendroid\syntax\symbol_seq'
7
- require_relative '..\..\..\lib\dendroid\syntax\production'
7
+ require_relative '..\..\..\lib\dendroid\syntax\rule'
8
8
  require_relative '..\..\..\lib\dendroid\grm_analysis\dotted_item'
9
9
 
10
10
  describe Dendroid::GrmAnalysis::DottedItem do
11
11
  let(:num_symb) { Dendroid::Syntax::Terminal.new('NUMBER') }
12
12
  let(:plus_symb) { Dendroid::Syntax::Terminal.new('PLUS') }
13
+ let(:minus_symb) { Dendroid::Syntax::Terminal.new('MINUS') }
13
14
  let(:expr_symb) { Dendroid::Syntax::NonTerminal.new('expression') }
14
- let(:rhs) { Dendroid::Syntax::SymbolSeq.new([num_symb, plus_symb, num_symb]) }
15
+ let(:rhs1) { Dendroid::Syntax::SymbolSeq.new([num_symb, plus_symb, num_symb]) }
16
+ let(:rhs2) { Dendroid::Syntax::SymbolSeq.new([num_symb, minus_symb, num_symb]) }
15
17
  let(:empty_body) { Dendroid::Syntax::SymbolSeq.new([]) }
16
- let(:prod) { Dendroid::Syntax::Production.new(expr_symb, rhs) }
17
- let(:empty_prod) { Dendroid::Syntax::Production.new(expr_symb, empty_body) }
18
+ let(:choice) { Dendroid::Syntax::Rule.new(expr_symb, [rhs1, rhs2, empty_body]) }
18
19
 
19
- # Implements a dotted item: expression => NUMBER . PLUS NUMBER
20
- subject { described_class.new(prod, 1) }
20
+ # Implements a dotted item: expression => NUMBER . MINUS NUMBER
21
+ subject { described_class.new(choice, 1, 1) }
21
22
 
22
23
  context 'Initialization:' do
23
24
  it 'is initialized with a production and a dot position' do
24
- expect { described_class.new(prod, 1) }.not_to raise_error
25
+ expect { described_class.new(choice, 1, 1) }.not_to raise_error
25
26
  end
26
27
 
27
28
  it 'knows its related production' do
28
- expect(subject.rule).to eq(prod)
29
+ expect(subject.rule).to eq(choice)
29
30
  end
30
31
 
31
32
  it 'knows its position' do
@@ -35,67 +36,78 @@ describe Dendroid::GrmAnalysis::DottedItem do
35
36
 
36
37
  context 'Provided services:' do
37
38
  it 'renders a String representation of itself' do
38
- expect(subject.to_s).to eq('expression => NUMBER . PLUS NUMBER')
39
+ expect(subject.to_s).to eq('expression => NUMBER . MINUS NUMBER')
39
40
  end
40
41
 
41
42
  it 'knows its state' do
42
- expect(described_class.new(prod, 0).state).to eq(:initial)
43
- expect(described_class.new(prod, 1).state).to eq(:partial)
44
- expect(described_class.new(prod, 3).state).to eq(:completed)
43
+ expect(described_class.new(choice, 0, 1).state).to eq(:initial)
44
+ expect(described_class.new(choice, 1, 1).state).to eq(:partial)
45
+ expect(described_class.new(choice, 3, 1).state).to eq(:completed)
45
46
 
46
- # Case of an empty production
47
- expect(described_class.new(empty_prod, 0).state).to eq(:initial_and_completed)
47
+ # Case of an empty alternative
48
+ expect(described_class.new(choice, 0, 2).state).to eq(:initial_and_completed)
48
49
  end
49
50
 
50
51
  it 'knows whether it is in the initial position' do
51
- expect(described_class.new(prod, 0)).to be_initial_pos
52
- expect(described_class.new(prod, 2)).not_to be_initial_pos
53
- expect(described_class.new(prod, 3)).not_to be_initial_pos
52
+ expect(described_class.new(choice, 0, 0)).to be_initial_pos
53
+ expect(described_class.new(choice, 2, 0)).not_to be_initial_pos
54
+ expect(described_class.new(choice, 3, 0)).not_to be_initial_pos
54
55
 
55
- # Case of an empty production
56
- expect(described_class.new(empty_prod, 0)).to be_initial_pos
56
+ # Case of an empty alternative
57
+ expect(described_class.new(choice, 0, 2)).to be_initial_pos
57
58
  end
58
59
 
59
60
  it 'knows whether it is in the final position' do
60
- expect(described_class.new(prod, 0)).not_to be_final_pos
61
- expect(described_class.new(prod, 2)).not_to be_final_pos
62
- expect(described_class.new(prod, 3)).to be_final_pos
63
- expect(described_class.new(prod, 3)).to be_completed
61
+ expect(described_class.new(choice, 0, 1)).not_to be_final_pos
62
+ expect(described_class.new(choice, 2, 1)).not_to be_final_pos
63
+ expect(described_class.new(choice, 3, 1)).to be_final_pos
64
+ expect(described_class.new(choice, 3,1)).to be_completed
64
65
 
65
- # Case of an empty production
66
- expect(described_class.new(empty_prod, 0)).to be_final_pos
66
+ # Case of an empty alternative
67
+ expect(described_class.new(choice, 0, 2)).to be_final_pos
67
68
  end
68
69
 
69
70
  it 'knows whether it is in an intermediate position' do
70
- expect(described_class.new(prod, 0)).not_to be_intermediate_pos
71
- expect(described_class.new(prod, 2)).to be_intermediate_pos
72
- expect(described_class.new(prod, 3)).not_to be_intermediate_pos
71
+ expect(described_class.new(choice, 0, 0)).not_to be_intermediate_pos
72
+ expect(described_class.new(choice, 2, 0)).to be_intermediate_pos
73
+ expect(described_class.new(choice, 3, 0)).not_to be_intermediate_pos
73
74
 
74
- # Case of an empty production
75
- expect(described_class.new(empty_prod, 0)).not_to be_intermediate_pos
75
+ # Case of an empty alternative
76
+ expect(described_class.new(choice, 0, 2)).not_to be_intermediate_pos
76
77
  end
77
78
 
78
79
  it 'knows the symbol after the dot (if any)' do
79
- expect(described_class.new(prod, 0).next_symbol.name).to eq(:NUMBER)
80
- expect(described_class.new(prod, 1).next_symbol.name).to eq(:PLUS)
81
- expect(described_class.new(prod, 2).next_symbol.name).to eq(:NUMBER)
82
- expect(described_class.new(prod, 3).next_symbol).to be_nil
80
+ expect(described_class.new(choice, 0, 1).next_symbol.name).to eq(:NUMBER)
81
+ expect(described_class.new(choice, 1, 1).next_symbol.name).to eq(:MINUS)
82
+ expect(described_class.new(choice, 2, 1).next_symbol.name).to eq(:NUMBER)
83
+ expect(described_class.new(choice, 3, 1).next_symbol).to be_nil
83
84
 
84
- # Case of an empty production
85
- expect(described_class.new(empty_prod, 0).next_symbol).to be_nil
85
+ # Case of an empty alternative
86
+ expect(described_class.new(choice, 0, 2).next_symbol).to be_nil
86
87
  end
87
88
 
88
- it 'can compare a given symbol to the expected one' do
89
- expect(described_class.new(prod, 0)).to be_expecting(num_symb)
90
- expect(described_class.new(prod, 0)).not_to be_expecting(plus_symb)
91
- expect(described_class.new(prod, 1)).to be_expecting(plus_symb)
92
- expect(described_class.new(prod, 2)).to be_expecting(num_symb)
93
- expect(described_class.new(prod, 3)).not_to be_expecting(num_symb)
94
- expect(described_class.new(prod, 3)).not_to be_expecting(plus_symb)
95
-
96
- # Case of an empty production
97
- expect(described_class.new(empty_prod, 0)).not_to be_expecting(num_symb)
98
- expect(described_class.new(empty_prod, 0)).not_to be_expecting(plus_symb)
89
+ it 'knows the symbol before the dot (if any)' do
90
+ expect(described_class.new(choice, 0, 1).prev_symbol).to be_nil
91
+ expect(described_class.new(choice, 1, 1).prev_symbol.name).to eq(:NUMBER)
92
+ expect(described_class.new(choice, 2, 1).prev_symbol.name).to eq(:MINUS)
93
+ expect(described_class.new(choice, 3, 1).prev_symbol.name).to eq(:NUMBER)
94
+
95
+ # Case of an empty alternative
96
+ expect(described_class.new(choice, 0, 1).prev_symbol).to be_nil
97
+ end
98
+
99
+ it 'can compare a given symbol to the one expected' do
100
+ expect(described_class.new(choice, 0, 1)).to be_expecting(num_symb)
101
+ expect(described_class.new(choice, 0, 1)).not_to be_expecting(plus_symb)
102
+ expect(described_class.new(choice, 1, 0)).to be_expecting(plus_symb)
103
+ expect(described_class.new(choice, 1, 1)).to be_expecting(minus_symb)
104
+ expect(described_class.new(choice, 2, 0)).to be_expecting(num_symb)
105
+ expect(described_class.new(choice, 3, 1)).not_to be_expecting(num_symb)
106
+ expect(described_class.new(choice, 3, 1)).not_to be_expecting(plus_symb)
107
+
108
+ # Case of an empty alternative
109
+ expect(described_class.new(choice, 0, 2)).not_to be_expecting(num_symb)
110
+ expect(described_class.new(choice, 0, 2)).not_to be_expecting(plus_symb)
99
111
  end
100
112
  end # context
101
113
  end # describe
@@ -4,11 +4,10 @@ require_relative '..\..\spec_helper'
4
4
  require_relative '..\..\..\lib\dendroid\syntax\terminal'
5
5
  require_relative '..\..\..\lib\dendroid\syntax\non_terminal'
6
6
  require_relative '..\..\..\lib\dendroid\syntax\symbol_seq'
7
- require_relative '..\..\..\lib\dendroid\syntax\choice'
8
- # require_relative '..\..\..\lib\dendroid\grm_analysis\alternative_item'
9
- require_relative '..\..\..\lib\dendroid\grm_analysis\choice_items'
7
+ require_relative '..\..\..\lib\dendroid\syntax\rule'
8
+ require_relative '..\..\..\lib\dendroid\grm_analysis\rule_items'
10
9
 
11
- describe Dendroid::GrmAnalysis::ChoiceItems do
10
+ describe Dendroid::GrmAnalysis::RuleItems do
12
11
  let(:num_symb) { Dendroid::Syntax::Terminal.new('NUMBER') }
13
12
  let(:plus_symb) { Dendroid::Syntax::Terminal.new('PLUS') }
14
13
  let(:star_symb) { Dendroid::Syntax::Terminal.new('STAR') }
@@ -17,8 +16,8 @@ describe Dendroid::GrmAnalysis::ChoiceItems do
17
16
  let(:alt2) { Dendroid::Syntax::SymbolSeq.new([num_symb, star_symb, num_symb]) }
18
17
  let(:alt3) { Dendroid::Syntax::SymbolSeq.new([]) }
19
18
  subject do
20
- choice = Dendroid::Syntax::Choice.new(expr_symb, [alt1, alt2, alt3])
21
- choice.extend(Dendroid::GrmAnalysis::ChoiceItems)
19
+ choice = Dendroid::Syntax::Rule.new(expr_symb, [alt1, alt2, alt3])
20
+ choice.extend(Dendroid::GrmAnalysis::RuleItems)
22
21
  choice.build_items
23
22
  choice
24
23
  end
@@ -0,0 +1,223 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../spec_helper'
4
+ require_relative '../support/sample_grammars'
5
+ require_relative '../../../lib/dendroid/recognizer/recognizer'
6
+ require_relative '../../../lib/dendroid/parsing/chart_walker'
7
+
8
+ # require_relative '../grm_dsl/base_grm_builder'
9
+ # require_relative '../utils/base_tokenizer'
10
+ # require_relative '../recognizer/recognizer'
11
+ # require_relative 'chart_walker'
12
+ # require_relative 'parse_tree_visitor'
13
+ # require_relative '../formatters/bracket_notation'
14
+ # require_relative '../formatters/ascii_tree'
15
+
16
+ RSpec.describe Dendroid::Parsing::ChartWalker do
17
+ include SampleGrammars
18
+
19
+ def retrieve_success_item(chart, grammar)
20
+ last_item_set = chart.item_sets.last
21
+ result = nil
22
+ last_item_set.items.reverse_each do |itm|
23
+ if itm.origin.zero? && itm.dotted_item.completed? && itm.dotted_item.rule.lhs == grammar.start_symbol
24
+ result = itm
25
+ break
26
+ end
27
+ end
28
+
29
+ result
30
+ end
31
+
32
+ def recognizer_for(grammar, tokenizer)
33
+ Dendroid::Recognizer::Recognizer.new(grammar, tokenizer)
34
+ end
35
+
36
+ def success_entry(chart, recognizer)
37
+ retrieve_success_item(chart, recognizer.grm_analysis.grammar)
38
+ end
39
+
40
+ context 'Parsing non-ambiguous grammars' do
41
+ it 'generates a parse tree for the example from Wikipedia' do
42
+ recognizer = recognizer_for(grammar_l1, tokenizer_l1)
43
+ chart = recognizer.run('2 + 3 * 4')
44
+ walker = described_class.new(chart)
45
+ root = walker.walk(success_entry(chart, recognizer))
46
+
47
+ expect(root.to_s).to eq('p => s [0, 5]')
48
+ expect(root.children.size). to eq(1)
49
+ expect(root.children[-1].to_s).to eq('s => s PLUS m [0, 5]')
50
+ plus_expr = root.children[-1]
51
+ expect(plus_expr.children.size).to eq(3)
52
+ expect(plus_expr.children[0].to_s).to eq('s => m [0, 1]')
53
+ expect(plus_expr.children[1].to_s).to eq('PLUS [1, 2]')
54
+ expect(plus_expr.children[2].to_s).to eq('m => m STAR t [2, 5]')
55
+
56
+ operand_plus = plus_expr.children[0]
57
+ expect(operand_plus.children.size).to eq(1)
58
+ expect(operand_plus.children[0].to_s).to eq('m => t [0, 1]')
59
+ expect(operand_plus.children[0].children.size).to eq(1)
60
+ expect(operand_plus.children[0].children[0].to_s).to eq('t => INTEGER [0, 1]')
61
+ expect(operand_plus.children[0].children[0].children[0].to_s).to eq('INTEGER: 2 [0, 1]')
62
+
63
+ expect(plus_expr.children[1].to_s).to eq('PLUS [1, 2]')
64
+
65
+ star_expr = plus_expr.children[2]
66
+ expect(star_expr.children.size).to eq(3)
67
+ expect(star_expr.children[0].to_s).to eq('m => t [2, 3]')
68
+ expect(star_expr.children[1].to_s).to eq('STAR [3, 4]')
69
+ expect(star_expr.children[2].to_s).to eq('t => INTEGER [4, 5]')
70
+
71
+ operand_star = star_expr.children[0]
72
+ expect(operand_star.children.size).to eq(1)
73
+ expect(operand_star.children[0].to_s).to eq('t => INTEGER [2, 3]')
74
+ expect(operand_star.children[0].children[0].to_s).to eq('INTEGER: 3 [2, 3]')
75
+
76
+ expect(star_expr.children[2].children.size).to eq(1)
77
+ expect(star_expr.children[2].children[0].to_s).to eq('INTEGER: 4 [4, 5]')
78
+ end
79
+
80
+ it 'generates a parse tree for grammar l10 (with left recursive rule)' do
81
+ recognizer = recognizer_for(grammar_l10, tokenizer_l10)
82
+ chart = recognizer.run('a a a a a')
83
+ walker = described_class.new(chart)
84
+ root = walker.walk(success_entry(chart, recognizer))
85
+
86
+ expect(root.to_s).to eq('A => A a [0, 5]')
87
+ expect(root.children.size). to eq(2)
88
+ expect(root.children[0].to_s).to eq('A => A a [0, 4]')
89
+ expect(root.children[1].to_s).to eq('a [4, 5]')
90
+
91
+ expect(root.children[0].children.size).to eq(2)
92
+ expect(root.children[0].children[0].to_s).to eq('A => A a [0, 3]')
93
+ expect(root.children[0].children[1].to_s).to eq('a [3, 4]')
94
+
95
+ grand_child = root.children[0].children[0]
96
+ expect(grand_child.children.size).to eq(2)
97
+ expect(grand_child.children[0].to_s).to eq('A => A a [0, 2]')
98
+ expect(grand_child.children[1].to_s).to eq('a [2, 3]')
99
+
100
+ expect(grand_child.children[0].children.size).to eq(2)
101
+ expect(grand_child.children[0].children[0].to_s).to eq('A => A a [0, 1]')
102
+ expect(grand_child.children[0].children[1].to_s).to eq('a [1, 2]')
103
+
104
+ expect(grand_child.children[0].children[0].children.size).to eq(2)
105
+ expect(grand_child.children[0].children[0].children[0].to_s).to eq('_ [0, 0]')
106
+ expect(grand_child.children[0].children[0].children[1].to_s).to eq('a [0, 1]')
107
+ end
108
+
109
+ it 'generates a parse tree for grammar l11 (with right recursive rule)' do
110
+ recognizer = recognizer_for(grammar_l11, tokenizer_l11)
111
+ chart = recognizer.run('a a a a a')
112
+ walker = described_class.new(chart)
113
+ root = walker.walk(success_entry(chart, recognizer))
114
+
115
+ expect(root.to_s).to eq('A => a A [0, 5]')
116
+ expect(root.children.size). to eq(2)
117
+ expect(root.children[0].to_s).to eq('a [0, 1]')
118
+ expect(root.children[1].to_s).to eq('A => a A [1, 5]')
119
+
120
+ expect(root.children[1].children.size).to eq(2)
121
+ expect(root.children[1].children[0].to_s).to eq('a [1, 2]')
122
+ expect(root.children[1].children[1].to_s).to eq('A => a A [2, 5]')
123
+
124
+ grand_child = root.children[1].children[1]
125
+ expect(grand_child.children.size).to eq(2)
126
+ expect(grand_child.children[0].to_s).to eq('a [2, 3]')
127
+ expect(grand_child.children[1].to_s).to eq('A => a A [3, 5]')
128
+
129
+ expect(grand_child.children[1].children.size).to eq(2)
130
+ expect(grand_child.children[1].children[0].to_s).to eq('a [3, 4]')
131
+ expect(grand_child.children[1].children[1].to_s).to eq('A => a A [4, 5]')
132
+
133
+ expect(grand_child.children[1].children[1].children.size).to eq(2)
134
+ expect(grand_child.children[1].children[1].children[0].to_s).to eq('a [4, 5]')
135
+ expect(grand_child.children[1].children[1].children[1].to_s).to eq('_ [5, 5]')
136
+ end
137
+ end # context
138
+
139
+ context 'Parsing ambiguous grammars' do
140
+ it "generates a parse forest for the G2 grammar that choked Earley's parsing algorithm" do
141
+ recognizer = recognizer_for(grammar_l8, tokenizer_l8)
142
+ chart = recognizer.run('x x x x')
143
+ walker = described_class.new(chart)
144
+ root = walker.walk(success_entry(chart, recognizer))
145
+
146
+ expect(root.to_s).to eq('OR: S [0, 4]')
147
+ expect(root.children.size). to eq(3)
148
+ root.children.each do |child|
149
+ expect(child.children.size).to eq(2)
150
+ expect(child.to_s).to eq('S => S S [0, 4]')
151
+ end
152
+ (a, b, c) = root.children
153
+
154
+ # Test structure of tree a
155
+ (child_a_0, child_a_1) = a.children
156
+ expect(child_a_0.to_s).to eq('S => S S [0, 2]')
157
+ expect(child_a_1.to_s).to eq('S => S S [2, 4]')
158
+ expect(child_a_0.children.size).to eq(2)
159
+ (child_a_0_0, child_a_0_1) = child_a_0.children
160
+ expect(child_a_0_0.to_s).to eq('S => x [0, 1]')
161
+ expect(child_a_0_1.to_s).to eq('S => x [1, 2]')
162
+ expect(child_a_0_0.children[0].to_s).to eq('x [0, 1]')
163
+ expect(child_a_0_1.children[0].to_s).to eq('x [1, 2]')
164
+
165
+ expect(child_a_1.children.size).to eq(2)
166
+ (child_a_1_0, child_a_1_1) = child_a_1.children
167
+ expect(child_a_1_0.to_s).to eq('S => x [2, 3]')
168
+ expect(child_a_1_1.to_s).to eq('S => x [3, 4]')
169
+ expect(child_a_1_0.children[0].to_s).to eq('x [2, 3]')
170
+ expect(child_a_1_1.children[0].to_s).to eq('x [3, 4]')
171
+
172
+ # Test structure of forest b
173
+ (child_b_0, child_b_1) = b.children
174
+ expect(child_b_0.to_s).to eq('OR: S [0, 3]')
175
+ expect(child_b_1.to_s).to eq('S => x [3, 4]')
176
+ expect(child_b_1.equal?(child_a_1_1)).to be_truthy # Sharing
177
+ expect(child_b_0.children.size).to eq(2)
178
+ (child_b_0_0, child_b_0_1) = child_b_0.children
179
+ expect(child_b_0_0.to_s).to eq('S => S S [0, 3]')
180
+ expect(child_b_0_1.to_s).to eq('S => S S [0, 3]')
181
+ expect(child_b_0_0.children.size).to eq(2)
182
+ (child_b_0_0_0, child_b_0_0_1) = child_b_0_0.children
183
+ expect(child_b_0_0_0.to_s).to eq('S => x [0, 1]')
184
+ expect(child_b_0_0_0.equal?(child_a_0_0)).to be_truthy # Sharing
185
+ expect(child_b_0_0_1.to_s).to eq('S => S S [1, 3]')
186
+ expect(child_b_0_0_1.children.size).to eq(2)
187
+ expect(child_b_0_0_1.children[0].to_s).to eq('S => x [1, 2]')
188
+ expect(child_b_0_0_1.children[0].equal?(child_a_0_1)).to be_truthy # Sharing
189
+ expect(child_b_0_0_1.children[1].to_s).to eq('S => x [2, 3]')
190
+ expect(child_b_0_0_1.children[1].equal?(child_a_1_0)).to be_truthy # Sharing
191
+
192
+ expect(child_b_0_1.children.size).to eq(2)
193
+ (child_b_0_1_0, child_b_0_1_1) = child_b_0_1.children
194
+ expect(child_b_0_1_0.to_s).to eq('S => S S [0, 2]')
195
+ expect(child_b_0_1_0.equal?(child_a_0)).to be_truthy # Sharing
196
+ expect(child_b_0_1_1.to_s).to eq('S => x [2, 3]')
197
+ expect(child_b_0_1_1.equal?(child_a_1_0)).to be_truthy # Sharing
198
+
199
+ # Test structure of forest c
200
+ (child_c_0, child_c_1) = c.children
201
+ expect(child_c_0.to_s).to eq('S => x [0, 1]')
202
+ expect(child_c_0.equal?(child_a_0_0)).to be_truthy # Sharing
203
+ expect(child_c_1.to_s).to eq('OR: S [1, 4]')
204
+ expect(child_c_1.children.size).to eq(2)
205
+ (child_c_1_0, child_c_1_1) = child_c_1.children
206
+ expect(child_c_1_0.to_s).to eq('S => S S [1, 4]')
207
+ expect(child_c_1_1.to_s).to eq('S => S S [1, 4]')
208
+ expect(child_c_1_0.children.size).to eq(2)
209
+ (child_c_1_0_0, child_c_1_0_1) = child_c_1_0.children
210
+ expect(child_c_1_0_0.to_s).to eq('S => x [1, 2]')
211
+ expect(child_c_1_0_0.equal?(child_a_0_1)).to be_truthy # Sharing
212
+ expect(child_c_1_0_1.to_s).to eq('S => S S [2, 4]')
213
+ expect(child_c_1_0_1.equal?(child_a_1)).to be_truthy # Sharing
214
+ (child_c_1_1_0, child_c_1_1_1) = child_c_1_1.children
215
+ expect(child_c_1_1_0.to_s).to eq('S => S S [1, 3]')
216
+ expect(child_c_1_1_0.equal?(child_b_0_0_1)).to be_truthy # Sharing
217
+ expect(child_c_1_1_1.to_s).to eq('S => x [3, 4]')
218
+ expect(child_c_1_1_1.equal?(child_b_1)).to be_truthy # Sharing
219
+ end
220
+ end # context
221
+ end # describe
222
+
223
+
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../spec_helper'
4
+ require_relative '../../../lib\dendroid/syntax/terminal'
5
+ require_relative '../../../lib/dendroid/lexical/token_position'
6
+ require_relative '../../../lib/dendroid/lexical/literal'
7
+ require_relative '../../../lib/dendroid/parsing/terminal_node'
8
+
9
+ RSpec.describe Dendroid::Parsing::TerminalNode do
10
+ let(:ex_source) { '+' }
11
+ let(:ex_pos) { Dendroid::Lexical::TokenPosition.new(2, 5) }
12
+ let(:ex_terminal) { Dendroid::Syntax::Terminal.new('PLUS') }
13
+ let(:plus_token) { Dendroid::Lexical::Token.new(ex_source, ex_pos, ex_terminal) }
14
+ let(:plus_node) { described_class.new(ex_terminal, plus_token, 3) }
15
+
16
+ let(:int_source) { '2' }
17
+ let(:int_symbol) { Dendroid::Syntax::Terminal.new('INTEGER') }
18
+ let(:int_token) { Dendroid::Lexical::Literal.new(int_source, ex_pos, int_symbol, 2) }
19
+ let(:int_node) { described_class.new(int_symbol, int_token, 5) }
20
+
21
+ context 'Initialization:' do
22
+ it 'should be initialized with a symbol, terminal and a rank' do
23
+ expect { described_class.new(ex_terminal, plus_token, 3) }.not_to raise_error
24
+ end
25
+ end
26
+
27
+ context 'provided services' do
28
+ it 'renders a String representation of itself' do
29
+ expect(plus_node.to_s).to eq('PLUS [3, 4]')
30
+ end
31
+
32
+ it 'renders also the token value (if any)' do
33
+ expect(int_node.to_s).to eq('INTEGER: 2 [5, 6]')
34
+ end
35
+ end
36
+ end
@@ -4,7 +4,7 @@ require_relative '../../spec_helper'
4
4
  require_relative '../../../lib/dendroid/syntax/terminal'
5
5
  require_relative '../../../lib/dendroid/syntax/non_terminal'
6
6
  require_relative '../../../lib/dendroid/syntax/symbol_seq'
7
- require_relative '../../../lib/dendroid/syntax/production'
7
+ require_relative '../../../lib/dendroid/syntax/rule'
8
8
  require_relative '../../../lib/dendroid/grm_analysis/dotted_item'
9
9
  require_relative '../../../lib/dendroid/recognizer/e_item'
10
10
 
@@ -14,10 +14,10 @@ describe Dendroid::Recognizer::EItem do
14
14
  let(:expr_symb) { Dendroid::Syntax::NonTerminal.new('expression') }
15
15
  let(:rhs) { Dendroid::Syntax::SymbolSeq.new([num_symb, plus_symb, num_symb]) }
16
16
  let(:empty_body) { Dendroid::Syntax::SymbolSeq.new([]) }
17
- let(:prod) { Dendroid::Syntax::Production.new(expr_symb, rhs) }
18
- let(:empty_prod) { Dendroid::Syntax::Production.new(expr_symb, empty_body) }
19
- let(:sample_dotted) { Dendroid::GrmAnalysis::DottedItem.new(prod, 1) }
20
- let(:other_dotted) { Dendroid::GrmAnalysis::DottedItem.new(empty_prod, 0) }
17
+ let(:prod) { Dendroid::Syntax::Rule.new(expr_symb, [rhs]) }
18
+ let(:empty_prod) { Dendroid::Syntax::Rule.new(expr_symb, [empty_body]) }
19
+ let(:sample_dotted) { Dendroid::GrmAnalysis::DottedItem.new(prod, 1, 0) }
20
+ let(:other_dotted) { Dendroid::GrmAnalysis::DottedItem.new(empty_prod, 0, 0) }
21
21
  let(:sample_origin) { 3 }
22
22
 
23
23
  subject { described_class.new(sample_dotted, sample_origin) }
@@ -4,7 +4,7 @@ require_relative '../../spec_helper'
4
4
  require_relative '../../../lib/dendroid/syntax/terminal'
5
5
  require_relative '../../../lib/dendroid/syntax/non_terminal'
6
6
  require_relative '../../../lib/dendroid/syntax/symbol_seq'
7
- require_relative '../../../lib/dendroid/syntax/production'
7
+ require_relative '../../../lib/dendroid/syntax/rule'
8
8
  require_relative '../../../lib/dendroid/grm_analysis/dotted_item'
9
9
  require_relative '../../../lib/dendroid/recognizer/e_item'
10
10
  require_relative '../../../lib/dendroid/recognizer/item_set'
@@ -15,11 +15,11 @@ describe Dendroid::Recognizer::ItemSet do
15
15
  let(:expr_symb) { Dendroid::Syntax::NonTerminal.new('expression') }
16
16
  let(:rhs) { Dendroid::Syntax::SymbolSeq.new([num_symb, plus_symb, num_symb]) }
17
17
  let(:empty_body) { Dendroid::Syntax::SymbolSeq.new([]) }
18
- let(:prod) { Dendroid::Syntax::Production.new(expr_symb, rhs) }
19
- let(:empty_prod) { Dendroid::Syntax::Production.new(expr_symb, empty_body) }
20
- let(:sample_dotted) { Dendroid::GrmAnalysis::DottedItem.new(prod, 1) }
18
+ let(:prod) { Dendroid::Syntax::Rule.new(expr_symb, [rhs]) }
19
+ let(:empty_prod) { Dendroid::Syntax::Rule.new(expr_symb, [empty_body]) }
20
+ let(:sample_dotted) { Dendroid::GrmAnalysis::DottedItem.new(prod, 1, 0) }
21
21
  let(:sample_origin) { 3 }
22
- let(:other_dotted) { Dendroid::GrmAnalysis::DottedItem.new(empty_prod, 0) }
22
+ let(:other_dotted) { Dendroid::GrmAnalysis::DottedItem.new(empty_prod, 0, 0) }
23
23
  let(:first_element) { Dendroid::Recognizer::EItem.new(sample_dotted, sample_origin) }
24
24
  let(:second_element) { Dendroid::Recognizer::EItem.new(other_dotted, 5) }
25
25
 
@@ -37,15 +37,23 @@ describe Dendroid::Recognizer::ItemSet do
37
37
 
38
38
  context 'Provided services:' do
39
39
  it 'adds a new element' do
40
- subject.add_item(first_element)
40
+ added = subject.add_item(first_element)
41
41
  expect(subject.size).to eq(1)
42
+ expect(added).to eq(first_element)
42
43
 
43
- # Trying a second time, doesn't change the set
44
+ # Trying a second time with itentical item, doesn't change the set
44
45
  subject.add_item(first_element)
45
46
  expect(subject.size).to eq(1)
46
47
 
47
- subject.add_item(second_element)
48
+ # Trying a third time with equal item, doesn't change the set
49
+ similar = first_element.dup
50
+ added = subject.add_item(similar)
51
+ expect(subject.size).to eq(1)
52
+ expect(added).to eq(first_element)
53
+
54
+ added = subject.add_item(second_element)
48
55
  expect(subject.size).to eq(2)
56
+ expect(added).to eq(second_element)
49
57
  end
50
58
 
51
59
  it 'can render a String representation of itself' do
@@ -51,7 +51,7 @@ describe Dendroid::Recognizer::Recognizer do
51
51
  'm => t . @ 0',
52
52
  's => m . @ 0',
53
53
  # 'm => m . STAR t @ 0',
54
- 'p => s . @ 0',
54
+ 'p => s . @ 0', # Can be ruled out (next token != eos)
55
55
  's => s . PLUS m @ 0'
56
56
  ]
57
57
  set2 = [ # 2 + . 3 * 4'
@@ -65,7 +65,7 @@ describe Dendroid::Recognizer::Recognizer do
65
65
  'm => t . @ 2',
66
66
  's => s PLUS m . @ 0',
67
67
  'm => m . STAR t @ 2',
68
- 'p => s . @ 0'
68
+ 'p => s . @ 0' # Can be ruled out (next token != eos)
69
69
  # 's => s . PLUS m @ 0'
70
70
  ]
71
71
  set4 = [ # 2 + 3 * . 4'
@@ -422,6 +422,55 @@ describe Dendroid::Recognizer::Recognizer do
422
422
  comp_expected_actuals(chart, expectations)
423
423
  end
424
424
 
425
+
426
+ it 'accepts an input with multiple levels of ambiguity' do
427
+ recognizer = described_class.new(grammar_l8, tokenizer_l8)
428
+ chart = recognizer.run('x x x x')
429
+ expect(chart).to be_successful
430
+
431
+ set0 = [ # . x x x x
432
+ 'S => . S S @ 0',
433
+ 'S => . x @ 0'
434
+ ]
435
+ set1 = [ # x . x x x
436
+ 'S => x . @ 0',
437
+ 'S => S . S @ 0',
438
+ 'S => . S S @ 1',
439
+ 'S => . x @ 1'
440
+ ]
441
+ set2 = [ # x x . x x
442
+ 'S => x . @ 1',
443
+ 'S => S S . @ 0',
444
+ 'S => S . S @ 1',
445
+ 'S => S . S @ 0',
446
+ 'S => . S S @ 2',
447
+ 'S => . x @ 2'
448
+ ]
449
+ set3 = [ # x x x . x
450
+ 'S => x . @ 2',
451
+ 'S => S S . @ 1',
452
+ 'S => S S . @ 0',
453
+ 'S => S . S @ 2',
454
+ 'S => S . S @ 1',
455
+ 'S => S . S @ 0',
456
+ 'S => . S S @ 3',
457
+ 'S => . x @ 3'
458
+ ]
459
+ set4 = [ # x x x x .
460
+ 'S => x . @ 3',
461
+ 'S => S S . @ 2',
462
+ 'S => S S . @ 1',
463
+ 'S => S S . @ 0', # Success entry
464
+ 'S => S . S @ 3',
465
+ 'S => S . S @ 2',
466
+ 'S => S . S @ 1',
467
+ 'S => S . S @ 0',
468
+ 'S => . S S @ 4'
469
+ ]
470
+ expectations = [set0, set1, set2, set3, set4]
471
+ comp_expected_actuals(chart, expectations)
472
+ end
473
+
425
474
  it 'swallows the input from an infinite ambiguity grammar' do
426
475
  recognizer = described_class.new(grammar_l9, tokenizer_l9)
427
476
  chart = recognizer.run('x x x')
@@ -441,7 +490,8 @@ describe Dendroid::Recognizer::Recognizer do
441
490
  'S => . S S @ 1',
442
491
  'S => . @ 1',
443
492
  'S => . x @ 1',
444
- 'S => S . S @ 1'
493
+ 'S => S . S @ 1',
494
+ 'S => S S . @ 1'
445
495
  ]
446
496
  set2 = [ # x x . x
447
497
  'S => x . @ 1',
@@ -452,7 +502,8 @@ describe Dendroid::Recognizer::Recognizer do
452
502
  'S => . S S @ 2',
453
503
  'S => . @ 2',
454
504
  'S => . x @ 2',
455
- 'S => S . S @ 2'
505
+ 'S => S . S @ 2',
506
+ 'S => S S . @ 2'
456
507
  ]
457
508
  set3 = [ # x x x .
458
509
  'S => x . @ 2',
@@ -465,7 +516,8 @@ describe Dendroid::Recognizer::Recognizer do
465
516
  'S => . S S @ 3',
466
517
  'S => . @ 3',
467
518
  # 'S => . x @ 3',
468
- 'S => S . S @ 3'
519
+ 'S => S . S @ 3',
520
+ 'S => S S . @ 3'
469
521
  ]
470
522
  expectations = [set0, set1, set2, set3]
471
523
  comp_expected_actuals(chart, expectations)
@@ -211,6 +211,7 @@ module SampleGrammars
211
211
  end
212
212
 
213
213
  def grammar_l10
214
+ # Grammar with left recursive rule
214
215
  builder = Dendroid::GrmDSL::BaseGrmBuilder.new do
215
216
  declare_terminals('a')
216
217
 
@@ -230,6 +231,7 @@ module SampleGrammars
230
231
 
231
232
  def grammar_l11
232
233
  builder = Dendroid::GrmDSL::BaseGrmBuilder.new do
234
+ # Grammar with right-recursive rule
233
235
  declare_terminals('a')
234
236
 
235
237
  rule 'A' => ['a A', '']