rley 0.8.00 → 0.8.01
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 +10 -4
- data/lib/rley/constants.rb +1 -1
- data/lib/rley/notation/grammar_builder.rb +82 -29
- data/lib/rley/parse_rep/ast_base_builder.rb +31 -5
- data/lib/rley/syntax/base_grammar_builder.rb +8 -0
- data/spec/rley/notation/grammar_builder_spec.rb +49 -42
- data/spec/rley/parser/gfg_earley_parser_spec.rb +24 -4
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5652efd2016050e7a40a94c279fa545a03b69b705bdcc5089a479d72c4743325
|
4
|
+
data.tar.gz: c3f46ba853632a95e3c1fbe16f0f77f8e8a07ed3acd69a82297ca13eec9b6ccc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 532ca6d76a277e6fd234d66ec74e9ade425a91f8ace039e0dc0636547663ac52f64613001e9c36497d5a0e0d9fe67f09eb2fff80b77d8c2f4950095e58ac3b2e
|
7
|
+
data.tar.gz: b288a0345c1696b8987b362ab99dafa4354f94ece73c5c80c87d7a28aa3c3d9ccaf0f516f29cead1707727582e917812435e4254e372aa2d62c110af7aa99a99
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
### 0.8.01 / 2021-08-22
|
2
|
+
- Unused/redundant file removal. Fix in rule generation
|
3
|
+
|
4
|
+
* [CHANGE] Removal of files in repository that were redundant/useless.
|
5
|
+
* [FIX] The rule ordering was broken by the rules implicitly generated by Rlry
|
6
|
+
|
1
7
|
### 0.8.00 / 2021-08-15
|
2
8
|
- New grammar builder that accepts ? * + modifiers
|
3
9
|
|
@@ -15,10 +21,10 @@
|
|
15
21
|
- [FIX] Method `GFGParsing#nullable_rule`: issue with nullable productions having at least one member in their rhs.
|
16
22
|
|
17
23
|
### 0.7.04 / 2019-08-17
|
18
|
-
- Rley recognizer is about 25% faster than previous version. Kudos to the people
|
19
|
-
behind the *magic_frozen_string_literal* gem.
|
24
|
+
- Rley recognizer is about 25% faster than previous version. Kudos to the people
|
25
|
+
behind the *magic_frozen_string_literal* gem.
|
20
26
|
- Code refactoring to use string frozen magic comments (as a consequence, Rley runs only on Rubies 2.3 or newer).
|
21
|
-
- Code restyling to please rubocop 0.7.40.
|
27
|
+
- Code restyling to please rubocop 0.7.40.
|
22
28
|
- [CHANGE] Class `ParseEntrySet`: minor code optimization
|
23
29
|
- [CHANGE] File `README.md` removed allusion to Ruby 2.0.x up to 2.2.x.
|
24
30
|
- [CHANGE] File `README.md` added Ruby 2.6.x up as supported version.
|
@@ -103,7 +109,7 @@ behind the *magic_frozen_string_literal* gem.
|
|
103
109
|
* [FIX] Code re-styling to remove most style offenses found by Rubocop 0.52.1
|
104
110
|
|
105
111
|
### 0.6.00 / 2018-02-25
|
106
|
-
Version bump. Highlights: new programming interface through facade object, improved AST generation.
|
112
|
+
Version bump. Highlights: new programming interface through facade object, improved AST generation.
|
107
113
|
* [NEW] Class `Rley::Engine`: Implementation of Facade design pattern to reach more convenient interface.
|
108
114
|
* [NEW] Class `Rley::ParseRep::ASTBaseBuilder` Abstract class that simplifies the creation of custom AST (Abstract Syntax Tree)
|
109
115
|
* [NEW] Module `Rley::ParseRep` hosts the classes for building parse representations (parse trees and forests)
|
data/lib/rley/constants.rb
CHANGED
@@ -1,12 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'set'
|
4
|
+
|
4
5
|
require_relative 'parser'
|
5
6
|
require_relative 'ast_visitor'
|
6
7
|
require_relative '../syntax/match_closest'
|
7
8
|
|
8
9
|
module Rley # This module is used as a namespace
|
9
10
|
module Notation # This module is used as a namespace
|
11
|
+
# Structure used for production rules that are implicitly generated by Rley
|
12
|
+
RawRule = Struct.new(:lhs, :rhs, :tag, :simple, :constraints)
|
13
|
+
|
10
14
|
# Builder GoF pattern. Builder builds a complex object
|
11
15
|
# (say, a grammar) from simpler objects (terminals and productions)
|
12
16
|
# and using a step by step approach.
|
@@ -24,16 +28,19 @@ module Rley # This module is used as a namespace
|
|
24
28
|
# @return [Array<Production>] The list of production rules for
|
25
29
|
# the grammar to build.
|
26
30
|
attr_reader(:productions)
|
31
|
+
|
32
|
+
# @return [Hash{String, String}] The synthesized raw productions
|
33
|
+
attr_reader(:synthetized)
|
27
34
|
|
28
35
|
# Creates a new grammar builder.
|
29
36
|
# @param aBlock [Proc] code block used to build the grammar.
|
30
37
|
# @example Building a tiny English grammar
|
31
|
-
# builder = Rley::
|
38
|
+
# builder = Rley::Notation::GrammarBuilder.new do
|
32
39
|
# add_terminals('n', 'v', 'adj', 'det')
|
33
|
-
# rule 'S' =>
|
34
|
-
# rule 'VP' =>
|
35
|
-
# rule 'NP' =>
|
36
|
-
# rule 'NP' =>
|
40
|
+
# rule 'S' => 'NP VP'
|
41
|
+
# rule 'VP' => 'v NP'
|
42
|
+
# rule 'NP' => 'det n'
|
43
|
+
# rule 'NP' => 'adj NP'
|
37
44
|
# end
|
38
45
|
# tiny_eng = builder.grammar
|
39
46
|
def initialize(&aBlock)
|
@@ -41,8 +48,12 @@ module Rley # This module is used as a namespace
|
|
41
48
|
@productions = []
|
42
49
|
@parser = Notation::Parser.new
|
43
50
|
@visitor2rhs = {}
|
51
|
+
@synthetized = {}
|
44
52
|
|
45
|
-
|
53
|
+
if block_given?
|
54
|
+
instance_exec(&aBlock)
|
55
|
+
grammar_complete!
|
56
|
+
end
|
46
57
|
end
|
47
58
|
|
48
59
|
# Retrieve a grammar symbol from its name.
|
@@ -60,6 +71,14 @@ module Rley # This module is used as a namespace
|
|
60
71
|
new_symbs = build_symbols(Syntax::Terminal, terminalSymbols)
|
61
72
|
symbols.merge!(new_symbs)
|
62
73
|
end
|
74
|
+
|
75
|
+
# Add the given marker symbol to the grammar of the language
|
76
|
+
# @param aMarkerSymbol [String] A mazker symbol
|
77
|
+
# @return [void]
|
78
|
+
def add_marker(aMarkerSymbol)
|
79
|
+
new_symb = build_symbol(Syntax::Marker, aMarkerSymbol)
|
80
|
+
symbols[new_symb.name] = new_symb
|
81
|
+
end
|
63
82
|
|
64
83
|
# Add a production rule in the grammar given one
|
65
84
|
# key-value pair of the form: String => String.
|
@@ -204,7 +223,7 @@ module Rley # This module is used as a namespace
|
|
204
223
|
end
|
205
224
|
|
206
225
|
##################################
|
207
|
-
# AST visit notification events
|
226
|
+
# RGN's AST visit notification events
|
208
227
|
# ################################
|
209
228
|
def after_symbol_node(aSymbolNode, aVisitor)
|
210
229
|
symb_name = aSymbolNode.name
|
@@ -215,9 +234,9 @@ module Rley # This module is used as a namespace
|
|
215
234
|
# implicitly called: rule('symb_name_qmark' => '').tag suffix_qmark_none
|
216
235
|
name_modified = "#{symb_name}#{suffix_qmark}"
|
217
236
|
unless symbols.include? name_modified
|
218
|
-
|
219
|
-
|
220
|
-
|
237
|
+
add_nonterminal(name_modified)
|
238
|
+
add_raw_rule(name_modified, "#{symb_name}", suffix_qmark_one)
|
239
|
+
add_raw_rule(name_modified, '', suffix_qmark_none)
|
221
240
|
end
|
222
241
|
symb_name = name_modified
|
223
242
|
|
@@ -226,9 +245,9 @@ module Rley # This module is used as a namespace
|
|
226
245
|
# implicitly called: rule('symb_name_star' => '').tag suffix_star_none
|
227
246
|
name_modified = "#{symb_name}#{suffix_star}"
|
228
247
|
unless symbols.include? name_modified
|
229
|
-
|
230
|
-
|
231
|
-
|
248
|
+
add_nonterminal(name_modified)
|
249
|
+
add_raw_rule(name_modified, "#{name_modified} #{symb_name}", suffix_star_more)
|
250
|
+
add_raw_rule(name_modified, [], suffix_star_none)
|
232
251
|
end
|
233
252
|
symb_name = name_modified
|
234
253
|
|
@@ -238,9 +257,9 @@ module Rley # This module is used as a namespace
|
|
238
257
|
when :one_or_more
|
239
258
|
name_modified = "#{symb_name}#{suffix_plus}"
|
240
259
|
unless symbols.include? name_modified
|
241
|
-
|
242
|
-
|
243
|
-
|
260
|
+
add_nonterminal(name_modified)
|
261
|
+
add_raw_rule(name_modified, "#{name_modified} #{symb_name}", suffix_plus_more)
|
262
|
+
add_raw_rule(name_modified, symb_name, suffix_plus_one)
|
244
263
|
end
|
245
264
|
symb_name = name_modified
|
246
265
|
else
|
@@ -264,10 +283,9 @@ module Rley # This module is used as a namespace
|
|
264
283
|
symb_name = sequence_name(aGroupingNode)
|
265
284
|
|
266
285
|
unless symbols.include?(symb_name) || aGroupingNode.repetition == :exactly_one
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
prod.constraints = aGroupingNode.constraints
|
286
|
+
add_nonterminal(symb_name)
|
287
|
+
rhs = serialize_sequence(aGroupingNode)
|
288
|
+
add_raw_rule(symb_name, rhs, 'return_children', true, aGroupingNode.constraints)
|
271
289
|
end
|
272
290
|
name_modified = "#{symb_name}#{repetition2suffix(aGroupingNode.repetition)}"
|
273
291
|
|
@@ -276,18 +294,18 @@ module Rley # This module is used as a namespace
|
|
276
294
|
# implicitly called: rule('symb_name_qmark' => 'symb_name_qmark').tag suffix_qmark_one
|
277
295
|
# implicitly called: rule('symb_name_qmark' => '').tag suffix_qmark_none
|
278
296
|
unless symbols.include? name_modified
|
279
|
-
|
280
|
-
|
281
|
-
|
297
|
+
add_nonterminal(name_modified)
|
298
|
+
add_raw_rule(name_modified, symb_name, suffix_qmark_one, true)
|
299
|
+
add_raw_rule(name_modified, [], suffix_qmark_none, true)
|
282
300
|
end
|
283
301
|
|
284
302
|
when :zero_or_more
|
285
303
|
# implicitly called: rule('symb_name_star' => 'symb_name_star symb_name').tag suffix_star_more
|
286
304
|
# implicitly called: rule('symb_name_star' => '').tag suffix_star_none
|
287
305
|
unless symbols.include? name_modified
|
288
|
-
|
289
|
-
|
290
|
-
|
306
|
+
add_nonterminal(name_modified)
|
307
|
+
add_raw_rule(name_modified, "#{name_modified} #{symb_name}", suffix_star_more)
|
308
|
+
add_raw_rule(name_modified, '', suffix_star_none)
|
291
309
|
end
|
292
310
|
|
293
311
|
when :exactly_one
|
@@ -295,9 +313,9 @@ module Rley # This module is used as a namespace
|
|
295
313
|
|
296
314
|
when :one_or_more
|
297
315
|
unless symbols.include? name_modified
|
298
|
-
|
299
|
-
|
300
|
-
|
316
|
+
add_nonterminal(name_modified)
|
317
|
+
add_raw_rule(name_modified, "#{name_modified} #{symb_name}", suffix_plus_more)
|
318
|
+
add_raw_rule(name_modified, symb_name, suffix_plus_one)
|
301
319
|
end
|
302
320
|
else
|
303
321
|
raise StandardError, 'Unhandled multiplicity'
|
@@ -309,7 +327,17 @@ module Rley # This module is used as a namespace
|
|
309
327
|
end
|
310
328
|
end
|
311
329
|
|
330
|
+
# A notification to the builderobject that the programmer
|
331
|
+
# has completed the entry of terminals and production rules
|
332
|
+
def grammar_complete!
|
333
|
+
process_raw_rules()
|
334
|
+
end
|
335
|
+
|
312
336
|
private
|
337
|
+
|
338
|
+
def add_nonterminal(aName)
|
339
|
+
symbols[aName] = Syntax::NonTerminal.new(aName)
|
340
|
+
end
|
313
341
|
|
314
342
|
def simple_rule(aProductionRepr)
|
315
343
|
aProductionRepr.each_pair do |(lhs_name, rhs_repr)|
|
@@ -444,6 +472,31 @@ module Rley # This module is used as a namespace
|
|
444
472
|
|
445
473
|
text.strip
|
446
474
|
end
|
475
|
+
|
476
|
+
def add_raw_rule(aSymbol, aRHS, aTag, simplified = false, constraints = [])
|
477
|
+
raw_rule = RawRule.new(aSymbol, aRHS, aTag, simplified, constraints)
|
478
|
+
if synthetized.include?(aSymbol)
|
479
|
+
@synthetized[aSymbol] << raw_rule
|
480
|
+
else
|
481
|
+
@synthetized[aSymbol] = [raw_rule]
|
482
|
+
end
|
483
|
+
end
|
484
|
+
|
485
|
+
def process_raw_rules
|
486
|
+
until synthetized.empty? do
|
487
|
+
raw_rules = synthetized.delete(synthetized.keys.first)
|
488
|
+
raw_rules.each do |raw|
|
489
|
+
new_prod = nil
|
490
|
+
if raw.simple
|
491
|
+
new_prod = simple_rule(raw.lhs => raw.rhs)
|
492
|
+
else
|
493
|
+
new_prod = rule(raw.lhs => raw.rhs)
|
494
|
+
end
|
495
|
+
new_prod.tag(raw.tag)
|
496
|
+
new_prod.constraints = raw.constraints
|
497
|
+
end
|
498
|
+
end
|
499
|
+
end
|
447
500
|
end # class
|
448
501
|
end # module
|
449
502
|
end # module
|
@@ -126,16 +126,42 @@ module Rley # This module is used as a namespace
|
|
126
126
|
|
127
127
|
# Standard method for handling one or more modifier: symbol+
|
128
128
|
# rule('symbol_plus' => 'symbol_plus symbol')
|
129
|
-
def reduce_base_plus_more(_production, _range, _tokens, theChildren)
|
130
|
-
theChildren[0] << theChildren[1]
|
131
|
-
end
|
129
|
+
# def reduce_base_plus_more(_production, _range, _tokens, theChildren)
|
130
|
+
# theChildren[0] << theChildren[1]
|
131
|
+
# end
|
132
132
|
|
133
133
|
# Standard rule method handling one or more modifier: symbol+
|
134
134
|
# rule('symbol_plus' => 'symbol')
|
135
|
-
def reduce_base_plus_last(_production, _range, _tokens, theChildren)
|
136
|
-
[theChildren[0]]
|
135
|
+
# def reduce_base_plus_last(_production, _range, _tokens, theChildren)
|
136
|
+
# [theChildren[0]]
|
137
|
+
# end
|
138
|
+
|
139
|
+
# Implicit rule generated for * modifier
|
140
|
+
# rule('X') => 'X item'.as '_star_more'
|
141
|
+
def reduce__star_more(_production, _range, _tokens, theChildren)
|
142
|
+
theChildren[0] << theChildren[1]
|
143
|
+
theChildren[0]
|
144
|
+
end
|
145
|
+
|
146
|
+
# Implicit rule generated for * modifier
|
147
|
+
# rule('X') => ''.as '_star_none'
|
148
|
+
def reduce__star_none(_production, _range, _tokens, theChildren)
|
149
|
+
[]
|
137
150
|
end
|
138
151
|
|
152
|
+
# Implicit rule generated for + modifier
|
153
|
+
# rule('X') => 'X item'.as '_plus_more'
|
154
|
+
def reduce__plus_more(_production, _range, _tokens, theChildren)
|
155
|
+
theChildren[0] << theChildren[1]
|
156
|
+
theChildren[0]
|
157
|
+
end
|
158
|
+
|
159
|
+
# Implicit rule generated for + modifier
|
160
|
+
# rule('X') => 'item'.as '_plus_one'
|
161
|
+
def reduce__plus_one(_production, _range, _tokens, theChildren)
|
162
|
+
[theChildren[0]]
|
163
|
+
end
|
164
|
+
|
139
165
|
end # class
|
140
166
|
end # module
|
141
167
|
end # module
|
@@ -55,6 +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
|
+
|
59
|
+
# Add the given marker symbol to the grammar of the language
|
60
|
+
# @param aMarkerSymbol [Syntax::Marker] A mazker symbol
|
61
|
+
# @return [void]
|
62
|
+
def add_marker(aMarkerSymbol)
|
63
|
+
new_symb = build_symbol(Marker, aMarkerSymbol)
|
64
|
+
symbols[aMarkerSymbol.name] = new_symb
|
65
|
+
end
|
58
66
|
|
59
67
|
# Add a production rule in the grammar given one
|
60
68
|
# key-value pair of the form: String => Array.
|
@@ -113,70 +113,78 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
113
113
|
expect(new_prod.constraints[0].closest_symb).to eq('IF')
|
114
114
|
end
|
115
115
|
|
116
|
-
it
|
116
|
+
it 'should support optional symbol' do
|
117
117
|
instance = GrammarBuilder.new
|
118
118
|
instance.add_terminals('LPAREN', 'RPAREN')
|
119
119
|
|
120
|
-
instance.
|
120
|
+
instance.rule 'argument_list' => 'LPAREN arguments? RPAREN'
|
121
|
+
instance.grammar_complete!
|
121
122
|
|
122
123
|
# implicitly called: rule('arguments_qmark' => 'arguments').tag suffix_qmark_one
|
123
124
|
# implicitly called: rule('arguments_qmark' => '').tag suffix_qmark_none
|
124
125
|
expect(instance.productions.size).to eq(3)
|
125
126
|
prod_star = instance.productions.select { |prod| prod.lhs.name == 'arguments_qmark' }
|
126
127
|
expect(prod_star.size).to eq(2)
|
127
|
-
|
128
|
-
expect(
|
129
|
-
expect(
|
128
|
+
first_prod = instance.productions.first
|
129
|
+
expect(first_prod.lhs.name).to eq('argument_list')
|
130
|
+
expect(first_prod.rhs.members[1].name).to eq('arguments_qmark')
|
130
131
|
end
|
131
132
|
|
132
133
|
it "should support Kleene's star" do
|
133
134
|
instance = GrammarBuilder.new
|
134
135
|
instance.add_terminals('EOF')
|
135
136
|
|
136
|
-
instance.
|
137
|
+
instance.rule 'program' => 'declaration* EOF'
|
138
|
+
instance.grammar_complete!
|
137
139
|
|
138
140
|
# implicitly called: rule('declaration_star' => 'declaration_star declaration').tag suffix_star_more
|
139
141
|
# implicitly called: rule('declaration_star' => '').tag suffix_star_last
|
140
142
|
expect(instance.productions.size).to eq(3)
|
141
143
|
prod_star = instance.productions.select { |prod| prod.lhs.name == 'declaration_star' }
|
142
144
|
expect(prod_star.size).to eq(2)
|
143
|
-
|
144
|
-
expect(
|
145
|
-
expect(
|
145
|
+
first_prod = instance.productions.first
|
146
|
+
expect(first_prod.lhs.name).to eq('program')
|
147
|
+
expect(first_prod.rhs.members[0].name).to eq('declaration_star')
|
146
148
|
end
|
147
149
|
|
148
150
|
it "should support symbols decorated with Kleene's plus" do
|
149
151
|
instance = GrammarBuilder.new
|
150
152
|
instance.add_terminals('plus', 'minus', 'digit')
|
151
153
|
|
152
|
-
instance.
|
153
|
-
instance.
|
154
|
-
instance.
|
155
|
-
instance.
|
156
|
-
|
157
|
-
instance.
|
154
|
+
instance.rule 'integer' => 'value'
|
155
|
+
instance.rule 'integer' => 'sign value'
|
156
|
+
instance.rule 'sign' => 'plus'
|
157
|
+
instance.rule 'sign' => 'minus'
|
158
|
+
instance.rule 'value' => 'digit+'
|
159
|
+
expect(instance.productions.size).to eq(5)
|
160
|
+
instance.grammar_complete!
|
161
|
+
expect(instance.productions.size).to eq(7)
|
158
162
|
|
159
163
|
# implicitly called: rule('digit_plus' => 'digit_plus digit').tag suffix_plus_more
|
160
164
|
# implicitly called: rule('digit_plus' => 'digit').tag suffix_plus_last
|
161
165
|
expect(instance.productions.size).to eq(7) # Two additional rules generated
|
162
166
|
prod_plus = instance.productions.select { |prod| prod.lhs.name == 'digit_plus' }
|
163
167
|
expect(prod_plus.size).to eq(2)
|
164
|
-
|
165
|
-
expect(
|
166
|
-
expect(
|
168
|
+
val_prod = instance.productions[4]
|
169
|
+
expect(val_prod.lhs.name).to eq('value')
|
170
|
+
expect(val_prod.rhs.members[0].name).to eq('digit_plus')
|
167
171
|
end
|
168
172
|
|
169
173
|
it "should support optional grouping" do
|
170
174
|
instance = GrammarBuilder.new
|
171
175
|
instance.add_terminals('EQUAL', 'IDENTIFIER', 'VAR')
|
172
176
|
|
173
|
-
instance.
|
177
|
+
instance.rule 'var_decl' => 'VAR IDENTIFIER (EQUAL expression)?'
|
178
|
+
instance.grammar_complete!
|
174
179
|
|
175
180
|
# implicitly called: rule('seq_EQUAL_expression' => 'EQUAL expression').tag 'return_children'
|
176
181
|
# implicitly called: rule('seq_EQUAL_expression_qmark' => 'seq_EQUAL_expression').tag suffix_qmark_one
|
177
182
|
# implicitly called: rule('seq_EQUAL_expression_qmark' => '').tag suffix_qmark_none
|
178
183
|
expect(instance.productions.size).to eq(4)
|
179
|
-
|
184
|
+
first_prod = instance.productions.first
|
185
|
+
expect(first_prod.lhs.name).to eq('var_decl')
|
186
|
+
expect(first_prod.rhs.members[2].name).to eq('seq_EQUAL_expression_qmark')
|
187
|
+
(p0, p1, p2) = instance.productions[1..3]
|
180
188
|
expect(p0.lhs.name).to eq('seq_EQUAL_expression')
|
181
189
|
expect(p0.rhs[0].name).to eq('EQUAL')
|
182
190
|
expect(p0.rhs[1].name).to eq('expression')
|
@@ -189,23 +197,24 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
189
197
|
expect(p2.lhs.name).to eq('seq_EQUAL_expression_qmark')
|
190
198
|
expect(p2.rhs).to be_empty
|
191
199
|
expect(p2.name).to eq('_qmark_none')
|
192
|
-
|
193
|
-
last_prod = instance.productions.last
|
194
|
-
expect(last_prod.lhs.name).to eq('var_decl')
|
195
|
-
expect(last_prod.rhs.members[2].name).to eq('seq_EQUAL_expression_qmark')
|
196
200
|
end
|
197
201
|
|
198
202
|
it "should support grouping with star modifier" do
|
199
203
|
instance = GrammarBuilder.new
|
200
204
|
instance.add_terminals('OR')
|
201
205
|
|
202
|
-
instance.
|
206
|
+
instance.rule 'logic_or' => 'logic_and (OR logic_and)*'
|
207
|
+
instance.grammar_complete!
|
203
208
|
|
204
209
|
# implicitly called: rule('seq_OR_logic_and' => 'OR logic_and').tag 'return_children'
|
205
210
|
# implicitly called: rule('seq_EQUAL_expression_star' => 'seq_EQUAL_expression_star seq_EQUAL_expression').tag suffix_star_more
|
206
211
|
# implicitly called: rule('seq_EQUAL_expression_star' => '').tag suffix_star_none
|
207
212
|
expect(instance.productions.size).to eq(4)
|
208
|
-
|
213
|
+
first_prod = instance.productions.first
|
214
|
+
expect(first_prod.lhs.name).to eq('logic_or')
|
215
|
+
expect(first_prod.rhs.members[1].name).to eq('seq_OR_logic_and_star')
|
216
|
+
|
217
|
+
(p0, p1, p2) = instance.productions[1..3]
|
209
218
|
expect(p0.lhs.name).to eq('seq_OR_logic_and')
|
210
219
|
expect(p0.rhs[0].name).to eq('OR')
|
211
220
|
expect(p0.rhs[1].name).to eq('logic_and')
|
@@ -219,23 +228,24 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
219
228
|
expect(p2.lhs.name).to eq('seq_OR_logic_and_star')
|
220
229
|
expect(p2.rhs).to be_empty
|
221
230
|
expect(p2.name).to eq('_star_none')
|
222
|
-
|
223
|
-
last_prod = instance.productions.last
|
224
|
-
expect(last_prod.lhs.name).to eq('logic_or')
|
225
|
-
expect(last_prod.rhs.members[1].name).to eq('seq_OR_logic_and_star')
|
226
231
|
end
|
227
232
|
|
228
233
|
it "should support grouping with plus modifier" do
|
229
234
|
instance = GrammarBuilder.new
|
230
235
|
instance.add_terminals('POINT TO SEMI_COLON')
|
231
236
|
|
232
|
-
instance.
|
237
|
+
instance.rule 'path' => 'POINT (TO POINT)+ SEMI_COLON'
|
238
|
+
instance.grammar_complete!
|
233
239
|
|
234
240
|
# implicitly called: rule('seq_TO_POINT' => 'TO POINT').tag 'return_children'
|
235
241
|
# implicitly called: rule('seq_TO_POINT_plus' => 'seq_TO_POINT_plus seq_TO_POINT').tag suffix_plus_more
|
236
242
|
# implicitly called: rule('seq_TO_POINT_plus' => 'seq_TO_POINT').tag suffix_plus_one
|
237
243
|
expect(instance.productions.size).to eq(4)
|
238
|
-
|
244
|
+
first_prod = instance.productions.first
|
245
|
+
expect(first_prod.lhs.name).to eq('path')
|
246
|
+
expect(first_prod.rhs.members[1].name).to eq('seq_TO_POINT_plus')
|
247
|
+
|
248
|
+
(p0, p1, p2) = instance.productions[1..3]
|
239
249
|
expect(p0.lhs.name).to eq('seq_TO_POINT')
|
240
250
|
expect(p0.rhs[0].name).to eq('TO')
|
241
251
|
expect(p0.rhs[1].name).to eq('POINT')
|
@@ -249,23 +259,24 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
249
259
|
expect(p2.lhs.name).to eq('seq_TO_POINT_plus')
|
250
260
|
expect(p2.rhs[0].name).to eq('seq_TO_POINT')
|
251
261
|
expect(p2.name).to eq('_plus_one')
|
252
|
-
|
253
|
-
last_prod = instance.productions.last
|
254
|
-
expect(last_prod.lhs.name).to eq('path')
|
255
|
-
expect(last_prod.rhs.members[1].name).to eq('seq_TO_POINT_plus')
|
256
262
|
end
|
257
263
|
|
258
264
|
it "should support grouping with nested annotation" do
|
259
265
|
instance = GrammarBuilder.new
|
260
266
|
instance.add_terminals('IF ELSE LPAREN RPAREN')
|
261
267
|
st = "IF LPAREN expr RPAREN stmt (ELSE { match_closest: 'IF' } stmt)?"
|
262
|
-
instance.
|
268
|
+
instance.rule('if_stmt' => st)
|
269
|
+
instance.grammar_complete!
|
263
270
|
|
264
271
|
# implicitly called: rule('seq_ELSE_stmt' => 'ELSE stmt').tag 'return_children'
|
265
272
|
# implicitly called: rule('seq_ELSE_stmt_qmark' => 'seq_ELSE_stmt ').tag suffix_plus_more
|
266
273
|
# implicitly called: rule('seq_ELSE_stmt_qmark' => '').tag suffix_plus_one
|
267
274
|
expect(instance.productions.size).to eq(4)
|
268
|
-
|
275
|
+
first_prod = instance.productions.first
|
276
|
+
expect(first_prod.lhs.name).to eq('if_stmt')
|
277
|
+
expect(first_prod.rhs.members[5].name).to eq('seq_ELSE_stmt_qmark')
|
278
|
+
|
279
|
+
(p0, p1, p2) = instance.productions[1..3]
|
269
280
|
expect(p0.lhs.name).to eq('seq_ELSE_stmt')
|
270
281
|
expect(p0.rhs[0].name).to eq('ELSE')
|
271
282
|
expect(p0.rhs[1].name).to eq('stmt')
|
@@ -282,10 +293,6 @@ module Rley # Open this namespace to avoid module qualifier prefixes
|
|
282
293
|
expect(p2.lhs.name).to eq('seq_ELSE_stmt_qmark')
|
283
294
|
expect(p2.rhs).to be_empty
|
284
295
|
expect(p2.name).to eq('_qmark_none')
|
285
|
-
|
286
|
-
last_prod = instance.productions.last
|
287
|
-
expect(last_prod.lhs.name).to eq('if_stmt')
|
288
|
-
expect(last_prod.rhs.members[5].name).to eq('seq_ELSE_stmt_qmark')
|
289
296
|
end
|
290
297
|
end # context
|
291
298
|
end # describe
|
@@ -934,10 +934,11 @@ MSG
|
|
934
934
|
# S => ;
|
935
935
|
# This grammar requires a time that is quadratic in the number of
|
936
936
|
# input tokens
|
937
|
-
builder = Notation::GrammarBuilder.new
|
938
|
-
|
939
|
-
|
940
|
-
|
937
|
+
builder = Notation::GrammarBuilder.new do
|
938
|
+
add_terminals('a')
|
939
|
+
rule('S' => 'a S')
|
940
|
+
rule('S' => '')
|
941
|
+
end
|
941
942
|
grammar = builder.grammar
|
942
943
|
tokens = build_token_sequence(%w[a a a a], grammar)
|
943
944
|
|
@@ -1017,6 +1018,25 @@ MSG
|
|
1017
1018
|
]
|
1018
1019
|
compare_entry_texts(parse_result.chart[4], expected)
|
1019
1020
|
end
|
1021
|
+
|
1022
|
+
it 'should support modifier(s) in start rule' do
|
1023
|
+
# An implicit EOF marker is a special terminal
|
1024
|
+
# that denotes the end of input string but doesn't
|
1025
|
+
# appear explicitly as some character or text in the input.
|
1026
|
+
builder = Notation::GrammarBuilder.new do
|
1027
|
+
add_terminals('a', 'b', 'EOF')
|
1028
|
+
|
1029
|
+
rule('S' => 'a_or_b* EOF')
|
1030
|
+
rule('a_or_b' => 'a')
|
1031
|
+
rule('a_or_b' => 'b')
|
1032
|
+
end
|
1033
|
+
grammar = builder.grammar
|
1034
|
+
tokens = build_token_sequence(%w[EOF], grammar)
|
1035
|
+
tokens[0].instance_variable_set(:@lexeme, '')
|
1036
|
+
instance = GFGEarleyParser.new(grammar)
|
1037
|
+
parse_result = instance.parse(tokens)
|
1038
|
+
expect(parse_result.success?).to eq(true)
|
1039
|
+
end
|
1020
1040
|
end # context
|
1021
1041
|
end # describe
|
1022
1042
|
end # module
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rley
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.8.
|
4
|
+
version: 0.8.01
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dimitri Geshef
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-08-
|
11
|
+
date: 2021-08-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|