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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ee99e1b9803a447d18a3f82e91353659e211109a5fb99d48a834ddebf572998f
4
- data.tar.gz: f4719d9fa301a0de2be143ca0aa5d0c261cf1562194a40b038c79b1faa1c04dc
3
+ metadata.gz: 5652efd2016050e7a40a94c279fa545a03b69b705bdcc5089a479d72c4743325
4
+ data.tar.gz: c3f46ba853632a95e3c1fbe16f0f77f8e8a07ed3acd69a82297ca13eec9b6ccc
5
5
  SHA512:
6
- metadata.gz: 9810a2d3884f86d2d437e1ba0911681f53895ff60ef500e4d19bd5d9429ff433f46c1dd57d6e185eb0bce3fcf869b1e4884acd09aaff72aee0323d00a781e763
7
- data.tar.gz: e7d49393824bbbeaff70bc7d57137d1fe82f86a98bfda52fc9144dbafa3e5568e51ce7e9da8c15fc2f51438b29100801e8ea145f613165ba4369093f07bfad23
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)
@@ -5,7 +5,7 @@
5
5
 
6
6
  module Rley # Module used as a namespace
7
7
  # The version number of the gem.
8
- Version = '0.8.00'
8
+ Version = '0.8.01'
9
9
 
10
10
  # Brief description of the gem.
11
11
  Description = "Ruby implementation of the Earley's parsing algorithm"
@@ -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::Syntax::GrammarBuilder.new do
38
+ # builder = Rley::Notation::GrammarBuilder.new do
32
39
  # add_terminals('n', 'v', 'adj', 'det')
33
- # rule 'S' => %w[NP VP]
34
- # rule 'VP' => %w[v NP]
35
- # rule 'NP' => %w[det n]
36
- # rule 'NP' => %w[adj 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
- instance_exec(&aBlock) if block_given?
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
- symbols[name_modified] = Syntax::NonTerminal.new(name_modified)
219
- rule(name_modified => "#{symb_name}" ).tag suffix_qmark_one
220
- rule(name_modified => '').tag suffix_qmark_none
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
- symbols[name_modified] = Syntax::NonTerminal.new(name_modified)
230
- rule(name_modified => "#{name_modified} #{symb_name}").tag suffix_star_more
231
- rule(name_modified => '').tag suffix_star_none
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
- symbols[name_modified] = Syntax::NonTerminal.new(name_modified)
242
- rule(name_modified => "#{name_modified} #{symb_name}").tag suffix_plus_more
243
- rule(name_modified => symb_name).tag suffix_plus_one
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
- symbols[symb_name] = Syntax::NonTerminal.new(symb_name)
268
- simple_rule(symb_name => serialize_sequence(aGroupingNode) ).tag 'return_children'
269
- prod = productions.last
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
- symbols[name_modified] = Syntax::NonTerminal.new(name_modified)
280
- simple_rule(name_modified => symb_name).tag suffix_qmark_one
281
- simple_rule(name_modified => []).tag suffix_qmark_none
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
- symbols[name_modified] = Syntax::NonTerminal.new(name_modified)
289
- rule(name_modified => "#{name_modified} #{symb_name}").tag suffix_star_more
290
- rule(name_modified => '').tag suffix_star_none
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
- symbols[name_modified] = Syntax::NonTerminal.new(name_modified)
299
- rule(name_modified => "#{name_modified} #{symb_name}").tag suffix_plus_more
300
- rule(name_modified => symb_name).tag suffix_plus_one
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 "should support optional symbol" do
116
+ it 'should support optional symbol' do
117
117
  instance = GrammarBuilder.new
118
118
  instance.add_terminals('LPAREN', 'RPAREN')
119
119
 
120
- instance.add_production('argument_list' => 'LPAREN arguments? RPAREN')
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
- last_prod = instance.productions.last
128
- expect(last_prod.lhs.name).to eq('argument_list')
129
- expect(last_prod.rhs.members[1].name).to eq('arguments_qmark')
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.add_production('program' => 'declaration* EOF')
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
- last_prod = instance.productions.last
144
- expect(last_prod.lhs.name).to eq('program')
145
- expect(last_prod.rhs.members[0].name).to eq('declaration_star')
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.add_production('integer' => 'value')
153
- instance.add_production('integer' => 'sign value')
154
- instance.add_production('sign' => 'plus')
155
- instance.add_production('sign' => 'minus')
156
- expect(instance.productions.size).to eq(4)
157
- instance.add_production('value' => 'digit+')
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
- last_prod = instance.productions.last
165
- expect(last_prod.lhs.name).to eq('value')
166
- expect(last_prod.rhs.members[0].name).to eq('digit_plus')
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.add_production('var_decl' => 'VAR IDENTIFIER (EQUAL expression)?')
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
- (p0, p1, p2) = instance.productions[0..2]
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.add_production('logic_or' => 'logic_and (OR logic_and)*')
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
- (p0, p1, p2) = instance.productions[0..2]
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.add_production('path' => 'POINT (TO POINT)+ SEMI_COLON')
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
- (p0, p1, p2) = instance.productions[0..2]
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.add_production('if_stmt' => st)
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
- (p0, p1, p2) = instance.productions[0..2]
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
- builder.add_terminals('a')
939
- builder.add_production('S' => 'a S')
940
- builder.add_production('S' => '')
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.00
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-15 00:00:00.000000000 Z
11
+ date: 2021-08-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake