rley 0.8.01 → 0.8.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.
- checksums.yaml +4 -4
- data/.rubocop.yml +29 -5
- data/CHANGELOG.md +7 -0
- data/examples/NLP/pico_en_demo.rb +2 -2
- data/lib/rley/constants.rb +1 -1
- data/lib/rley/notation/all_notation_nodes.rb +3 -1
- data/lib/rley/notation/ast_builder.rb +185 -191
- data/lib/rley/notation/ast_node.rb +5 -5
- data/lib/rley/notation/ast_visitor.rb +3 -1
- data/lib/rley/notation/grammar.rb +1 -1
- data/lib/rley/notation/grammar_builder.rb +16 -15
- data/lib/rley/notation/grouping_node.rb +1 -1
- data/lib/rley/notation/parser.rb +56 -56
- data/lib/rley/notation/sequence_node.rb +3 -3
- data/lib/rley/notation/symbol_node.rb +2 -2
- data/lib/rley/notation/tokenizer.rb +3 -15
- data/lib/rley/parse_rep/ast_base_builder.rb +5 -6
- data/lib/rley/parser/gfg_chart.rb +5 -4
- data/lib/rley/parser/gfg_earley_parser.rb +1 -1
- data/lib/rley/syntax/base_grammar_builder.rb +3 -3
- data/lib/rley/syntax/match_closest.rb +7 -7
- data/spec/rley/notation/grammar_builder_spec.rb +6 -6
- data/spec/rley/notation/parser_spec.rb +183 -184
- data/spec/rley/notation/tokenizer_spec.rb +98 -104
- data/spec/rley/parser/dangling_else_spec.rb +15 -13
- data/spec/rley/parser/gfg_earley_parser_spec.rb +11 -9
- data/spec/rley/parser/gfg_parsing_spec.rb +1 -0
- data/spec/rley/syntax/base_grammar_builder_spec.rb +0 -1
- data/spec/rley/syntax/match_closest_spec.rb +4 -4
- metadata +2 -2
@@ -51,7 +51,7 @@ module Rley # This module is used as a namespace
|
|
51
51
|
result.chart[index].each do |entry|
|
52
52
|
# Is entry of the form? [A => alpha . B beta, k]...
|
53
53
|
next_symbol = entry.next_symbol
|
54
|
-
if next_symbol
|
54
|
+
if next_symbol.kind_of?(Syntax::NonTerminal)
|
55
55
|
# ...apply the Call rule
|
56
56
|
call_rule(result, entry, index)
|
57
57
|
end
|
@@ -55,14 +55,14 @@ module Rley # This module is used as a namespace
|
|
55
55
|
new_symbs = build_symbols(Terminal, terminalSymbols)
|
56
56
|
symbols.merge!(new_symbs)
|
57
57
|
end
|
58
|
-
|
58
|
+
|
59
59
|
# Add the given marker symbol to the grammar of the language
|
60
60
|
# @param aMarkerSymbol [Syntax::Marker] A mazker symbol
|
61
|
-
# @return [void]
|
61
|
+
# @return [void]
|
62
62
|
def add_marker(aMarkerSymbol)
|
63
63
|
new_symb = build_symbol(Marker, aMarkerSymbol)
|
64
64
|
symbols[aMarkerSymbol.name] = new_symb
|
65
|
-
end
|
65
|
+
end
|
66
66
|
|
67
67
|
# Add a production rule in the grammar given one
|
68
68
|
# key-value pair of the form: String => Array.
|
@@ -7,13 +7,13 @@ module Rley # This module is used as a namespace
|
|
7
7
|
class MatchClosest
|
8
8
|
# @return [Integer] index of constrained symbol to match
|
9
9
|
attr_reader(:idx_symbol)
|
10
|
-
|
10
|
+
|
11
11
|
# @return [String] name of closest preceding symbol to pair
|
12
12
|
attr_reader(:closest_symb)
|
13
13
|
|
14
14
|
# @return [NilClass, Array<Parser::ParseEntry>] set of entries with closest symbol
|
15
15
|
attr_accessor(:entries)
|
16
|
-
|
16
|
+
|
17
17
|
# @param aSymbolSeq [Rley::Syntax::SymbolSeq] a sequence of grammar symbols
|
18
18
|
# @param idxSymbol [Integer] index of symbol
|
19
19
|
# @param nameClosest [String] Terminal symbol name
|
@@ -21,18 +21,18 @@ module Rley # This module is used as a namespace
|
|
21
21
|
@idx_symbol = valid_idx_symbol(idxSymbol, aSymbolSeq)
|
22
22
|
@closest_symb = valid_name_closest(nameClosest)
|
23
23
|
end
|
24
|
-
|
24
|
+
|
25
25
|
private
|
26
|
-
|
26
|
+
|
27
27
|
# Check that the provided index is within plausible bounds
|
28
28
|
def valid_idx_symbol(idxSymbol, aSymbolSeq)
|
29
|
-
bounds = 0
|
29
|
+
bounds = 0..aSymbolSeq.size - 1
|
30
30
|
err_msg_outbound = 'Index of symbol out of bound'
|
31
31
|
raise StandardError, err_msg_outbound unless bounds.include? idxSymbol
|
32
|
-
|
32
|
+
|
33
33
|
idxSymbol
|
34
34
|
end
|
35
|
-
|
35
|
+
|
36
36
|
def valid_name_closest(nameClosest)
|
37
37
|
nameClosest
|
38
38
|
end
|
@@ -170,7 +170,7 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
170
170
|
expect(val_prod.rhs.members[0].name).to eq('digit_plus')
|
171
171
|
end
|
172
172
|
|
173
|
-
it
|
173
|
+
it 'should support optional grouping' do
|
174
174
|
instance = GrammarBuilder.new
|
175
175
|
instance.add_terminals('EQUAL', 'IDENTIFIER', 'VAR')
|
176
176
|
|
@@ -199,7 +199,7 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
199
199
|
expect(p2.name).to eq('_qmark_none')
|
200
200
|
end
|
201
201
|
|
202
|
-
it
|
202
|
+
it 'should support grouping with star modifier' do
|
203
203
|
instance = GrammarBuilder.new
|
204
204
|
instance.add_terminals('OR')
|
205
205
|
|
@@ -230,7 +230,7 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
230
230
|
expect(p2.name).to eq('_star_none')
|
231
231
|
end
|
232
232
|
|
233
|
-
it
|
233
|
+
it 'should support grouping with plus modifier' do
|
234
234
|
instance = GrammarBuilder.new
|
235
235
|
instance.add_terminals('POINT TO SEMI_COLON')
|
236
236
|
|
@@ -261,7 +261,7 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
261
261
|
expect(p2.name).to eq('_plus_one')
|
262
262
|
end
|
263
263
|
|
264
|
-
it
|
264
|
+
it 'should support grouping with nested annotation' do
|
265
265
|
instance = GrammarBuilder.new
|
266
266
|
instance.add_terminals('IF ELSE LPAREN RPAREN')
|
267
267
|
st = "IF LPAREN expr RPAREN stmt (ELSE { match_closest: 'IF' } stmt)?"
|
@@ -281,7 +281,7 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
281
281
|
expect(p0.rhs[0].name).to eq('ELSE')
|
282
282
|
expect(p0.rhs[1].name).to eq('stmt')
|
283
283
|
expect(p0.name).to eq('return_children')
|
284
|
-
expect(p0.constraints.size).
|
284
|
+
expect(p0.constraints.size).to eq(1)
|
285
285
|
expect(p0.constraints[0]).to be_kind_of(Syntax::MatchClosest)
|
286
286
|
expect(p0.constraints[0].idx_symbol).to eq(0) # ELSE is on position 0
|
287
287
|
expect(p0.constraints[0].closest_symb).to eq('IF')
|
@@ -299,4 +299,4 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
299
299
|
end # module
|
300
300
|
end # module
|
301
301
|
|
302
|
-
# End of file
|
302
|
+
# End of file
|
@@ -1,184 +1,183 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative '../../spec_helper' # Use the RSpec framework
|
4
|
-
|
5
|
-
require_relative '../../../lib/rley/notation/ast_builder'
|
6
|
-
# Load the class under test
|
7
|
-
require_relative '../../../lib/rley/notation/parser'
|
8
|
-
|
9
|
-
module Rley
|
10
|
-
module Notation
|
11
|
-
describe Parser do
|
12
|
-
subject { Parser.new }
|
13
|
-
|
14
|
-
# Utility method to walk towards deeply nested node
|
15
|
-
# @param aNTNode [Rley::PTree::NonTerminalNode]
|
16
|
-
# @param subnodePath[Array<Integer>] An Array of subnode indices
|
17
|
-
def walk_subnodes(aNTNode, subnodePath)
|
18
|
-
curr_node = aNTNode
|
19
|
-
subnodePath.each do |index|
|
20
|
-
curr_node = curr_node.subnodes[index]
|
21
|
-
end
|
22
|
-
|
23
|
-
curr_node
|
24
|
-
end
|
25
|
-
|
26
|
-
context 'Initialization:' do
|
27
|
-
it 'should be initialized without argument' do
|
28
|
-
expect { Parser.new }.not_to raise_error
|
29
|
-
end
|
30
|
-
|
31
|
-
it 'should have its parse engine initialized' do
|
32
|
-
expect(subject.engine).to be_kind_of(Rley::Engine)
|
33
|
-
end
|
34
|
-
end # context
|
35
|
-
|
36
|
-
context 'Parsing into CST:' do
|
37
|
-
subject do
|
38
|
-
instance = Parser.new
|
39
|
-
instance.engine.configuration.repr_builder = Rley::ParseRep::CSTBuilder
|
40
|
-
|
41
|
-
instance
|
42
|
-
end
|
43
|
-
|
44
|
-
it 'should parse single symbol names' do
|
45
|
-
samples = %w[IF ifCondition statement]
|
46
|
-
|
47
|
-
# One drawback od CSTs: they have a deeply nested structure
|
48
|
-
samples.each do |source|
|
49
|
-
ptree = subject.parse(source)
|
50
|
-
expect(ptree.root).to be_kind_of(Rley::PTree::NonTerminalNode)
|
51
|
-
expect(ptree.root.symbol.name).to eq('notation')
|
52
|
-
expect(ptree.root.subnodes[0]).to be_kind_of(Rley::PTree::NonTerminalNode)
|
53
|
-
expect(ptree.root.subnodes[0].symbol.name).to eq('rhs')
|
54
|
-
expect(ptree.root.subnodes[0].subnodes[0]).to be_kind_of(Rley::PTree::NonTerminalNode)
|
55
|
-
member_seq = ptree.root.subnodes[0].subnodes[0]
|
56
|
-
expect(member_seq.symbol.name).to eq('member_seq')
|
57
|
-
expect(member_seq.subnodes[0]).to be_kind_of(Rley::PTree::NonTerminalNode)
|
58
|
-
expect(member_seq.subnodes[0].symbol.name).to eq('member')
|
59
|
-
expect(member_seq.subnodes[0].subnodes[0]).to be_kind_of(Rley::PTree::NonTerminalNode)
|
60
|
-
expect(member_seq.subnodes[0].subnodes[0].symbol.name).to eq('strait_member')
|
61
|
-
strait_member = member_seq.subnodes[0].subnodes[0]
|
62
|
-
expect(strait_member.subnodes[0]).to be_kind_of(Rley::PTree::NonTerminalNode)
|
63
|
-
expect(strait_member.subnodes[0].symbol.name).to eq('base_member')
|
64
|
-
expect(strait_member.subnodes[0].subnodes[0]).to be_kind_of(Rley::PTree::TerminalNode)
|
65
|
-
expect(strait_member.subnodes[0].subnodes[0].token.lexeme).to eq(source)
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
instance =
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
ptree
|
84
|
-
expect(ptree.root).to
|
85
|
-
expect(ptree.root.
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
ptree
|
94
|
-
expect(ptree.root).to be_kind_of(
|
95
|
-
expect(ptree.root.subnodes[0]).to
|
96
|
-
expect(ptree.root.subnodes[
|
97
|
-
expect(ptree.root.subnodes[1]).to
|
98
|
-
expect(ptree.root.subnodes[
|
99
|
-
expect(ptree.root.subnodes[2]).to
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
ptree
|
107
|
-
expect(ptree.root).to
|
108
|
-
expect(ptree.root.
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
ptree
|
116
|
-
expect(ptree.root).to
|
117
|
-
expect(ptree.root.
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
ptree
|
125
|
-
expect(ptree.root).to be_kind_of(
|
126
|
-
expect(ptree.root.subnodes[0]).to
|
127
|
-
expect(ptree.root.subnodes[0].
|
128
|
-
expect(ptree.root.subnodes[
|
129
|
-
expect(ptree.root.subnodes[1]).to
|
130
|
-
expect(ptree.root.subnodes[1].
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
ptree
|
138
|
-
expect(ptree.root).to be_kind_of(
|
139
|
-
expect(ptree.root.subnodes[0]).to
|
140
|
-
expect(ptree.root.subnodes[
|
141
|
-
expect(ptree.root.subnodes[1]).to
|
142
|
-
expect(ptree.root.subnodes[
|
143
|
-
expect(ptree.root.subnodes[2]).to
|
144
|
-
expect(ptree.root.subnodes[
|
145
|
-
expect(ptree.root.subnodes[3]).to
|
146
|
-
expect(ptree.root.subnodes[3].
|
147
|
-
expect(ptree.root.subnodes[3].subnodes[0]).to
|
148
|
-
expect(ptree.root.subnodes[3].subnodes[
|
149
|
-
expect(ptree.root.subnodes[3].subnodes[1]).to
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
ptree
|
157
|
-
expect(ptree.root).to
|
158
|
-
expect(ptree.root.
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
ptree
|
166
|
-
expect(ptree.root).to be_kind_of(
|
167
|
-
expect(ptree.root.subnodes[0]).to
|
168
|
-
expect(ptree.root.subnodes[
|
169
|
-
expect(ptree.root.subnodes[1]).to
|
170
|
-
expect(ptree.root.subnodes[
|
171
|
-
expect(ptree.root.subnodes[2]).to
|
172
|
-
|
173
|
-
optional
|
174
|
-
expect(optional).to
|
175
|
-
expect(optional.
|
176
|
-
expect(optional.subnodes[0]).to
|
177
|
-
expect(optional.subnodes[0].
|
178
|
-
expect(optional.subnodes[
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
end # module
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../../spec_helper' # Use the RSpec framework
|
4
|
+
|
5
|
+
require_relative '../../../lib/rley/notation/ast_builder'
|
6
|
+
# Load the class under test
|
7
|
+
require_relative '../../../lib/rley/notation/parser'
|
8
|
+
|
9
|
+
module Rley
|
10
|
+
module Notation
|
11
|
+
describe Parser do
|
12
|
+
subject { Parser.new }
|
13
|
+
|
14
|
+
# Utility method to walk towards deeply nested node
|
15
|
+
# @param aNTNode [Rley::PTree::NonTerminalNode]
|
16
|
+
# @param subnodePath[Array<Integer>] An Array of subnode indices
|
17
|
+
def walk_subnodes(aNTNode, subnodePath)
|
18
|
+
curr_node = aNTNode
|
19
|
+
subnodePath.each do |index|
|
20
|
+
curr_node = curr_node.subnodes[index]
|
21
|
+
end
|
22
|
+
|
23
|
+
curr_node
|
24
|
+
end
|
25
|
+
|
26
|
+
context 'Initialization:' do
|
27
|
+
it 'should be initialized without argument' do
|
28
|
+
expect { Parser.new }.not_to raise_error
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'should have its parse engine initialized' do
|
32
|
+
expect(subject.engine).to be_kind_of(Rley::Engine)
|
33
|
+
end
|
34
|
+
end # context
|
35
|
+
|
36
|
+
context 'Parsing into CST:' do
|
37
|
+
subject do
|
38
|
+
instance = Parser.new
|
39
|
+
instance.engine.configuration.repr_builder = Rley::ParseRep::CSTBuilder
|
40
|
+
|
41
|
+
instance
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'should parse single symbol names' do
|
45
|
+
samples = %w[IF ifCondition statement]
|
46
|
+
|
47
|
+
# One drawback od CSTs: they have a deeply nested structure
|
48
|
+
samples.each do |source|
|
49
|
+
ptree = subject.parse(source)
|
50
|
+
expect(ptree.root).to be_kind_of(Rley::PTree::NonTerminalNode)
|
51
|
+
expect(ptree.root.symbol.name).to eq('notation')
|
52
|
+
expect(ptree.root.subnodes[0]).to be_kind_of(Rley::PTree::NonTerminalNode)
|
53
|
+
expect(ptree.root.subnodes[0].symbol.name).to eq('rhs')
|
54
|
+
expect(ptree.root.subnodes[0].subnodes[0]).to be_kind_of(Rley::PTree::NonTerminalNode)
|
55
|
+
member_seq = ptree.root.subnodes[0].subnodes[0]
|
56
|
+
expect(member_seq.symbol.name).to eq('member_seq')
|
57
|
+
expect(member_seq.subnodes[0]).to be_kind_of(Rley::PTree::NonTerminalNode)
|
58
|
+
expect(member_seq.subnodes[0].symbol.name).to eq('member')
|
59
|
+
expect(member_seq.subnodes[0].subnodes[0]).to be_kind_of(Rley::PTree::NonTerminalNode)
|
60
|
+
expect(member_seq.subnodes[0].subnodes[0].symbol.name).to eq('strait_member')
|
61
|
+
strait_member = member_seq.subnodes[0].subnodes[0]
|
62
|
+
expect(strait_member.subnodes[0]).to be_kind_of(Rley::PTree::NonTerminalNode)
|
63
|
+
expect(strait_member.subnodes[0].symbol.name).to eq('base_member')
|
64
|
+
expect(strait_member.subnodes[0].subnodes[0]).to be_kind_of(Rley::PTree::TerminalNode)
|
65
|
+
expect(strait_member.subnodes[0].subnodes[0].token.lexeme).to eq(source)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end # context
|
69
|
+
|
70
|
+
context 'Parsing into AST:' do
|
71
|
+
subject do
|
72
|
+
instance = Parser.new
|
73
|
+
instance.engine.configuration.repr_builder = ASTBuilder
|
74
|
+
|
75
|
+
instance
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'should parse single symbol names' do
|
79
|
+
samples = %w[IF ifCondition statement]
|
80
|
+
|
81
|
+
samples.each do |source|
|
82
|
+
ptree = subject.parse(source)
|
83
|
+
expect(ptree.root).to be_kind_of(SymbolNode)
|
84
|
+
expect(ptree.root.name).to eq(source)
|
85
|
+
expect(ptree.root.repetition).to eq(:exactly_one)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'should parse a sequence of symbols' do
|
90
|
+
sequence = 'INT_LIT ELLIPSIS INT_LIT'
|
91
|
+
|
92
|
+
ptree = subject.parse(sequence)
|
93
|
+
expect(ptree.root).to be_kind_of(SequenceNode)
|
94
|
+
expect(ptree.root.subnodes[0]).to be_kind_of(SymbolNode)
|
95
|
+
expect(ptree.root.subnodes[0].name).to eq('INT_LIT')
|
96
|
+
expect(ptree.root.subnodes[1]).to be_kind_of(SymbolNode)
|
97
|
+
expect(ptree.root.subnodes[1].name).to eq('ELLIPSIS')
|
98
|
+
expect(ptree.root.subnodes[2]).to be_kind_of(SymbolNode)
|
99
|
+
expect(ptree.root.subnodes[2].name).to eq('INT_LIT')
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'should parse an optional symbol' do
|
103
|
+
optional = 'member_seq?'
|
104
|
+
|
105
|
+
ptree = subject.parse(optional)
|
106
|
+
expect(ptree.root).to be_kind_of(SymbolNode)
|
107
|
+
expect(ptree.root.name).to eq('member_seq')
|
108
|
+
expect(ptree.root.repetition).to eq(:zero_or_one)
|
109
|
+
end
|
110
|
+
|
111
|
+
it 'should parse a symbol with a + modifier' do
|
112
|
+
one_or_more = 'member+'
|
113
|
+
|
114
|
+
ptree = subject.parse(one_or_more)
|
115
|
+
expect(ptree.root).to be_kind_of(SymbolNode)
|
116
|
+
expect(ptree.root.name).to eq('member')
|
117
|
+
expect(ptree.root.repetition).to eq(:one_or_more)
|
118
|
+
end
|
119
|
+
|
120
|
+
it 'should parse a symbol with a * modifier' do
|
121
|
+
zero_or_more = 'declaration* EOF'
|
122
|
+
|
123
|
+
ptree = subject.parse(zero_or_more)
|
124
|
+
expect(ptree.root).to be_kind_of(SequenceNode)
|
125
|
+
expect(ptree.root.subnodes[0]).to be_kind_of(SymbolNode)
|
126
|
+
expect(ptree.root.subnodes[0].name).to eq('declaration')
|
127
|
+
expect(ptree.root.subnodes[0].repetition).to eq(:zero_or_more)
|
128
|
+
expect(ptree.root.subnodes[1]).to be_kind_of(SymbolNode)
|
129
|
+
expect(ptree.root.subnodes[1].name).to eq('EOF')
|
130
|
+
expect(ptree.root.subnodes[1].repetition).to eq(:exactly_one)
|
131
|
+
end
|
132
|
+
|
133
|
+
it 'should parse a grouping with a modifier' do
|
134
|
+
input = 'IF ifCondition statement (ELSE statement)?'
|
135
|
+
|
136
|
+
ptree = subject.parse(input)
|
137
|
+
expect(ptree.root).to be_kind_of(SequenceNode)
|
138
|
+
expect(ptree.root.subnodes[0]).to be_kind_of(SymbolNode)
|
139
|
+
expect(ptree.root.subnodes[0].name).to eq('IF')
|
140
|
+
expect(ptree.root.subnodes[1]).to be_kind_of(SymbolNode)
|
141
|
+
expect(ptree.root.subnodes[1].name).to eq('ifCondition')
|
142
|
+
expect(ptree.root.subnodes[2]).to be_kind_of(SymbolNode)
|
143
|
+
expect(ptree.root.subnodes[2].name).to eq('statement')
|
144
|
+
expect(ptree.root.subnodes[3]).to be_kind_of(SequenceNode)
|
145
|
+
expect(ptree.root.subnodes[3].repetition).to eq(:zero_or_one)
|
146
|
+
expect(ptree.root.subnodes[3].subnodes[0]).to be_kind_of(SymbolNode)
|
147
|
+
expect(ptree.root.subnodes[3].subnodes[0].name).to eq('ELSE')
|
148
|
+
expect(ptree.root.subnodes[3].subnodes[1]).to be_kind_of(SymbolNode)
|
149
|
+
expect(ptree.root.subnodes[3].subnodes[1].name).to eq('statement')
|
150
|
+
end
|
151
|
+
|
152
|
+
it 'should parse an annotated symbol' do
|
153
|
+
optional = 'member_seq{repeat: 0..1}'
|
154
|
+
|
155
|
+
ptree = subject.parse(optional)
|
156
|
+
expect(ptree.root).to be_kind_of(SymbolNode)
|
157
|
+
expect(ptree.root.name).to eq('member_seq')
|
158
|
+
expect(ptree.root.repetition).to eq(:zero_or_one)
|
159
|
+
end
|
160
|
+
|
161
|
+
it 'should parse a grouping with embedded annotation' do
|
162
|
+
if_stmt = "IF ifCondition statement ( ELSE { match_closest: 'IF' } statement )?"
|
163
|
+
|
164
|
+
ptree = subject.parse(if_stmt)
|
165
|
+
expect(ptree.root).to be_kind_of(SequenceNode)
|
166
|
+
expect(ptree.root.subnodes[0]).to be_kind_of(SymbolNode)
|
167
|
+
expect(ptree.root.subnodes[0].name).to eq('IF')
|
168
|
+
expect(ptree.root.subnodes[1]).to be_kind_of(SymbolNode)
|
169
|
+
expect(ptree.root.subnodes[1].name).to eq('ifCondition')
|
170
|
+
expect(ptree.root.subnodes[2]).to be_kind_of(SymbolNode)
|
171
|
+
expect(ptree.root.subnodes[2].name).to eq('statement')
|
172
|
+
optional = ptree.root.subnodes[3]
|
173
|
+
expect(optional).to be_kind_of(SequenceNode)
|
174
|
+
expect(optional.repetition).to eq(:zero_or_one)
|
175
|
+
expect(optional.subnodes[0]).to be_kind_of(SymbolNode)
|
176
|
+
expect(optional.subnodes[0].name).to eq('ELSE')
|
177
|
+
expect(optional.subnodes[0].annotation).to eq({ 'match_closest' => 'IF' })
|
178
|
+
expect(optional.subnodes[1].name).to eq('statement')
|
179
|
+
end
|
180
|
+
end # context
|
181
|
+
end # describe
|
182
|
+
end # module
|
183
|
+
end # module
|