rley 0.8.00 → 0.8.01

Sign up to get free protection for your applications and to get access to all the features.
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