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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +3 -0
- data/lib/dendroid/formatters/ascii_tree.rb +142 -0
- data/lib/dendroid/formatters/base_formatter.rb +25 -0
- data/lib/dendroid/formatters/bracket_notation.rb +50 -0
- data/lib/dendroid/grm_analysis/dotted_item.rb +46 -30
- data/lib/dendroid/grm_analysis/grm_analyzer.rb +2 -4
- data/lib/dendroid/grm_analysis/{choice_items.rb → rule_items.rb} +10 -10
- data/lib/dendroid/grm_dsl/base_grm_builder.rb +3 -4
- data/lib/dendroid/parsing/and_node.rb +56 -0
- data/lib/dendroid/parsing/chart_walker.rb +293 -0
- data/lib/dendroid/parsing/composite_parse_node.rb +21 -0
- data/lib/dendroid/parsing/empty_rule_node.rb +28 -0
- data/lib/dendroid/parsing/or_node.rb +51 -0
- data/lib/dendroid/parsing/parse_node.rb +26 -0
- data/lib/dendroid/parsing/parse_tree_visitor.rb +127 -0
- data/lib/dendroid/parsing/parser.rb +185 -0
- data/lib/dendroid/parsing/terminal_node.rb +32 -0
- data/lib/dendroid/parsing/walk_progress.rb +117 -0
- data/lib/dendroid/recognizer/chart.rb +8 -0
- data/lib/dendroid/recognizer/e_item.rb +21 -2
- data/lib/dendroid/recognizer/item_set.rb +7 -2
- data/lib/dendroid/recognizer/recognizer.rb +33 -20
- data/lib/dendroid/syntax/grammar.rb +1 -1
- data/lib/dendroid/syntax/rule.rb +71 -13
- data/spec/dendroid/grm_analysis/dotted_item_spec.rb +59 -47
- data/spec/dendroid/grm_analysis/{choice_items_spec.rb → rule_items_spec.rb} +5 -6
- data/spec/dendroid/parsing/chart_walker_spec.rb +223 -0
- data/spec/dendroid/parsing/terminal_node_spec.rb +36 -0
- data/spec/dendroid/recognizer/e_item_spec.rb +5 -5
- data/spec/dendroid/recognizer/item_set_spec.rb +16 -8
- data/spec/dendroid/recognizer/recognizer_spec.rb +57 -5
- data/spec/dendroid/support/sample_grammars.rb +2 -0
- data/spec/dendroid/syntax/grammar_spec.rb +16 -21
- data/spec/dendroid/syntax/rule_spec.rb +56 -7
- data/version.txt +1 -1
- metadata +20 -13
- data/lib/dendroid/grm_analysis/alternative_item.rb +0 -70
- data/lib/dendroid/grm_analysis/production_items.rb +0 -55
- data/lib/dendroid/syntax/choice.rb +0 -95
- data/lib/dendroid/syntax/production.rb +0 -82
- data/spec/dendroid/grm_analysis/alternative_item_spec.rb +0 -12
- data/spec/dendroid/grm_analysis/production_items_spec.rb +0 -68
- data/spec/dendroid/syntax/choice_spec.rb +0 -68
- 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\
|
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(:
|
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(:
|
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 .
|
20
|
-
subject { described_class.new(
|
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(
|
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(
|
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 .
|
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(
|
43
|
-
expect(described_class.new(
|
44
|
-
expect(described_class.new(
|
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
|
47
|
-
expect(described_class.new(
|
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(
|
52
|
-
expect(described_class.new(
|
53
|
-
expect(described_class.new(
|
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
|
56
|
-
expect(described_class.new(
|
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(
|
61
|
-
expect(described_class.new(
|
62
|
-
expect(described_class.new(
|
63
|
-
expect(described_class.new(
|
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
|
66
|
-
expect(described_class.new(
|
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(
|
71
|
-
expect(described_class.new(
|
72
|
-
expect(described_class.new(
|
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
|
75
|
-
expect(described_class.new(
|
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(
|
80
|
-
expect(described_class.new(
|
81
|
-
expect(described_class.new(
|
82
|
-
expect(described_class.new(
|
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
|
85
|
-
expect(described_class.new(
|
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 '
|
89
|
-
expect(described_class.new(
|
90
|
-
expect(described_class.new(
|
91
|
-
expect(described_class.new(
|
92
|
-
expect(described_class.new(
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
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\
|
8
|
-
|
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::
|
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::
|
21
|
-
choice.extend(Dendroid::GrmAnalysis::
|
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/
|
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::
|
18
|
-
let(:empty_prod) { Dendroid::Syntax::
|
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/
|
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::
|
19
|
-
let(:empty_prod) { Dendroid::Syntax::
|
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
|
-
|
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', '']
|