rley 0.8.11 → 0.8.13

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: f7ad228834b0f81ead8bc7ba0276c46b15d1d26e6c041bca00737bc5e224642b
4
- data.tar.gz: 8877d597137a6aa3548abe6bdeeb5c51d9e94479094c78db377680657131a7d2
3
+ metadata.gz: b1ad581d39b568b76013e43a7c1a04177845226c8d6a65cf49314a4897ad1e59
4
+ data.tar.gz: 6b626ec3a1f32e886a057075d90fc42fb07ddc0abe3a9d6abe357ccf934902dd
5
5
  SHA512:
6
- metadata.gz: b7f4da7fafc790cd5c737310b811b4b5e51d5f37c37373ce04089c73e8ade7c318d3638ee1b345e165b7cbd59cc7636038ad866f19352f8208d8de759c47006d
7
- data.tar.gz: 611344a1d84c111e3280cac654082670eecc27f4da781e9f32e5bfdc21f52a1ff3c17cb812de2b346cfbf29247c5976321368cebcf55e0463860f5f7281ab61a
6
+ metadata.gz: 9185b89a40e09b17648415df3ade7d9a2948eee552e938d3452b0abc06082914cc48a4bd67ba389b36d9bf20c09ad42e3bf132487c8ce8d402687a2fa302d1d5
7
+ data.tar.gz: 5b2b5e1b4abf2f233e21edae90e2ee7a197b5bc9cedbaee5b2690b57562201e331dfff21b4115ffea48d5bd7086b9673cac2985aa9d7942fdac4d199ec1f43d9
data/.rubocop.yml CHANGED
@@ -2,8 +2,8 @@ AllCops:
2
2
  Exclude:
3
3
  - 'exp/**/*'
4
4
 
5
- Gemspec/DateAssignment:
6
- Enabled: true
5
+ #Gemspec/DateAssignment:
6
+ # Enabled: true
7
7
 
8
8
  Gemspec/RequireMFA: # new in 1.23
9
9
  Enabled: false
data/.yardopts CHANGED
@@ -2,5 +2,5 @@
2
2
  --no-private
3
3
  --markup markdown
4
4
  -
5
- Changelog.md
6
- License.txt
5
+ CHANGELOG.md
6
+ LICENSE.txt
data/CHANGELOG.md CHANGED
@@ -1,3 +1,13 @@
1
+ ### 0.8.13 / 2025-02-16
2
+ - Tested against MRI Ruby 3.4.1.
3
+ - [FIX] Fixed most "offenses" reported by Rubocop 1.72.0
4
+
5
+ ### 0.8.11 / 2022-04-17
6
+ - Moved dependency on `prime` to gem.
7
+
8
+ * [CHANGE] File `rley.gemspec` added runtime dependency on `prime` gem (rationale: was part of stdlib, from 3.1 it is demoted to a bundled gem)
9
+ * [CHANGE] File `rgn\tokenizer.rb` minor style refactoring to please Rubocop 1.27
10
+
1
11
  ### 0.8.10 / 2022-04-08
2
12
  - Refactoring of `RGN::Tokenizer` class.
3
13
  * [CHANGE] Class `RGN::Tokenizer` changes.
data/README.md CHANGED
@@ -51,9 +51,9 @@ application range such as:
51
51
 
52
52
  ### Compatibility
53
53
  Rley supports the following Ruby implementations:
54
- - MRI 2.5
55
- - MRI 2.6
56
- - MRI 2.7
54
+ - MRI 3.2
55
+ - MRI 3.3
56
+ - MRI 3.4
57
57
  - JRuby 9.1+
58
58
 
59
59
  ---
@@ -72,5 +72,5 @@ print_tree('Abstract Syntax Tree (AST)', ast_ptree)
72
72
  # Now perform the computation of math expression
73
73
  root = ast_ptree.root
74
74
  print_title('Result:')
75
- puts root.interpret.to_s # Output the expression result
75
+ puts root.interpret # Output the expression result
76
76
  # End of file
@@ -0,0 +1,601 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rley'
4
+
5
+ # Simple right recursive grammar
6
+ # based on example in D. Grune, C. Jacobs "Parsing Techniques" book
7
+ # pp. 224 et sq.
8
+ # S => a S;
9
+ # S => ;
10
+ # This grammar requires a time that is quadratic in the number of
11
+ # input tokens
12
+ # Similar grammar is also considered here: https://loup-vaillant.fr/tutorials/earley-parsing/right-recursion
13
+ builder = Rley::grammar_builder do
14
+ # Define first the terminal symbols...
15
+ add_terminals('a')
16
+
17
+ # ... then with syntax rules
18
+ # First found rule is considered to be the top-level rule
19
+ rule('S' => 'a S')
20
+ rule('S' => [])
21
+ end
22
+
23
+ # Highly simplified tokenizer implementation.
24
+ def tokenizer(aText, aGrammar)
25
+ index = 0
26
+ tokens = aText.scan(/a/).map do |letter_a|
27
+ terminal = aGrammar.name2symbol['a']
28
+ index += 1
29
+ pos = Rley::Lexical::Position.new(1, index)
30
+ Rley::Lexical::Token.new(letter_a, terminal, pos)
31
+ end
32
+
33
+ return tokens
34
+ end
35
+
36
+ right_recursive_grammar = builder.grammar.freeze
37
+
38
+
39
+ input_to_parse = 'a' * 5
40
+
41
+ parser = Rley::Parser::GFGEarleyParser.new(right_recursive_grammar)
42
+ tokens = tokenizer(input_to_parse, right_recursive_grammar)
43
+ result = parser.parse(tokens)
44
+
45
+ # p result.chart.to_s
46
+
47
+
48
+ # If we remove the .X and X. entries, then we have exactly the same output than Loup Vaillant
49
+
50
+ =begin
51
+ State[0]
52
+ .S | 0
53
+ S => . a S | 0
54
+ S => . | 0
55
+ S. | 0
56
+ State[1]
57
+ S => a . S | 0
58
+ .S | 1
59
+ S => . a S | 1
60
+ S => . | 1
61
+ S. | 1
62
+ S => a S . | 0
63
+ S. | 0
64
+ State[2]
65
+ S => a . S | 1
66
+ .S | 2
67
+ S => . a S | 2
68
+ S => . | 2
69
+ S. | 2
70
+ S => a S . | 1
71
+ S. | 1
72
+ S => a S . | 0
73
+ S. | 0
74
+ State[3]
75
+ S => a . S | 2
76
+ .S | 3
77
+ S => . a S | 3
78
+ S => . | 3
79
+ S. | 3
80
+ S => a S . | 2
81
+ S. | 2
82
+ S => a S . | 1
83
+ S. | 1
84
+ S => a S . | 0
85
+ S. | 0
86
+ State[4]
87
+ S => a . S | 3
88
+ .S | 4
89
+ S => . a S | 4
90
+ S => . | 4
91
+ S. | 4
92
+ S => a S . | 3
93
+ S. | 3
94
+ S => a S . | 2
95
+ S. | 2
96
+ S => a S . | 1
97
+ S. | 1
98
+ S => a S . | 0
99
+ S. | 0
100
+ State[5]
101
+ S => a . S | 4
102
+ .S | 5
103
+ S => . a S | 5
104
+ S => . | 5
105
+ S. | 5
106
+ S => a S . | 4
107
+ S. | 4
108
+ S => a S . | 3
109
+ S. | 3
110
+ S => a S . | 2
111
+ S. | 2
112
+ S => a S . | 1
113
+ S. | 1
114
+ S => a S . | 0
115
+ S. | 0
116
+ =end
117
+
118
+ =begin
119
+ from the right recursive grammar:
120
+ Terminals: a
121
+ Rules:
122
+ S => a S
123
+ S => []
124
+
125
+ ... the following table is generated:
126
+ | | .S | S. | (S -> .a S) |
127
+ |:-------------:|:--:|:--:|-------------|
128
+ | .S start | | Ta | Tb |
129
+ | S. end | | | |
130
+ | (S -> .a S) | | Tc | Td |
131
+
132
+ Two first rows are start and end nodes
133
+ Remaining rows are for nodes with outgoing scan edge
134
+
135
+ Algorithm to build the table
136
+ start node
137
+ end node
138
+ find every scan edge
139
+
140
+ =end
141
+
142
+
143
+ def build_gfg(aGrammar)
144
+ items_builder = Object.new.extend(Rley::Base::GrmItemsBuilder)
145
+ items = items_builder.build_dotted_items(aGrammar)
146
+ gfg = Rley::GFG::GrmFlowGraph.new(items)
147
+ end
148
+
149
+ graph = build_gfg(right_recursive_grammar)
150
+
151
+ =begin
152
+ Execution simulation table construction
153
+ Do a df traversal from start vertex
154
+ current_from: .S
155
+
156
+ Visit .S ; stack = [.S]
157
+
158
+ Visit S => .a S ; stack = [.S, S => . a S]
159
+ scan edge detected
160
+ table[.S] = {(S => . a S) => [[S => . a S]]}
161
+ current_from: (S => . a S)
162
+
163
+
164
+ Visit S => a . S; stack = [.S, S => . a S, S => a . S]
165
+ call edge to .S
166
+ current_from: (S => . a S)
167
+ prov_table[(S => . a S)] => { .S => [S => a . S] }
168
+
169
+ Visit .S; [.S, S => . a S, S => a . S, .S]
170
+ .S is a table heading
171
+ prov_table[(S => . a S)] => { .S => [S => a . S, .S] }
172
+ current_from: .S
173
+
174
+ Visit S => . ; stack = [.S, S => . a S, S => a . S, .S, S => .]
175
+ table[.S] = {..., S. => [[S => .]]}
176
+ current_from: .S
177
+
178
+ Visit S. ; stack = [.S, S => . a S, S => a . S, .S, S => ., S.]
179
+ S. is a table heading
180
+ table[.S] = {..., S. => [[S => ., S.]]}
181
+ current_from: S.
182
+
183
+ Visit S => a S .; stack = [.S, S => . a S, S => a . S, .S, S => ., S., S => a S .]
184
+ return edge to .S detected
185
+ table[S.] = { S. => [[S => a S ., S.]]}
186
+
187
+ Visit S.; stack = [.S, S => . a S, S => a . S, .S, S => ., S., S => a S ., S.]
188
+ S. fully visited; pop it from stack
189
+
190
+ Visit S => a S .; stack = [.S, S => . a S, S => a . S, .S, S => ., S., S => a S .]
191
+ S => a S . fully visited; pop it from stack
192
+
193
+ Visit S.; stack = [.S, S => . a S, S => a . S, .S, S => ., S.]
194
+ S. fully visited; pop it from stack
195
+
196
+ Visit S => .; stack = [.S, S => . a S, S => a . S, .S, S => .]
197
+ S => . fully visited; pop it from stack
198
+
199
+ Visit .S; stack = [.S, S => . a S, S => a . S, .S]
200
+ .S fully visited; pop it from stack
201
+
202
+ Visit S => a . S; stack = [.S, S => . a S, S => a . S]
203
+ S => a . S fully visited; pop it from stack
204
+
205
+ Visit S => . a S; stack = [.S, S => . a S]
206
+ S => . a Sfully visited; pop it from stack
207
+
208
+ Visit .S; stack = [.S]
209
+ .S fully visited; pop it from stack
210
+
211
+ Stack contains couples of the type (node, node-from)
212
+ Stack empty; table is complete
213
+ table[.S] = {(S => . a S) => [[S => . a S, .S], S. => [[S => ., S.]]}
214
+ table[S.] = { S. => [[S => a S ., S.]]}
215
+ prov_table[(S => . a S)] = { .S => [S => a . S] }
216
+ table[(S => . a S] = { (S => . a S) => [S => a . S, .S, S => . a S], S. =>[[S => a S ., S., S => ., S.]] }
217
+ =end
218
+
219
+ module GraphMixin
220
+ def build_table
221
+ table = { start_vertex => {} }
222
+ end_vertex = end_vertex_for[start_vertex.non_terminal]
223
+ table[end_vertex] = {}
224
+ term2scan_edge = {}
225
+
226
+ vertices.each do |vx|
227
+ next if vx.kind_of?(Rley::GFG::NonTerminalVertex)
228
+
229
+ edge = vx.edges[0]
230
+ next unless edge.kind_of?(Rley::GFG::ScanEdge)
231
+
232
+ table[edge] = {}
233
+ terminal = edge.terminal
234
+ term2scan_edge[terminal] = [] unless term2scan_edge.include? terminal
235
+ term2scan_edge[terminal] << edge
236
+ end
237
+
238
+ table
239
+ end
240
+
241
+ def build_raw_table
242
+ table = { start_vertex => {} }
243
+ end_vertex = end_vertex_for[start_vertex.non_terminal]
244
+ table[end_vertex] = {} # table: vertex => { to_vertex => [path to_vertex] }
245
+ visit = [start_vertex] # visit: stack of vertex to visit
246
+ from_indices = [0] # Array of indices. Each index refers to one visitee
247
+ vtx2count = { start_vertex => 0 }
248
+
249
+ loop do
250
+ break if visit.empty?
251
+
252
+ current_from = visit[from_indices.last]
253
+ visitee = visit.last
254
+ visit_count = vtx2count[visitee]
255
+ if visit_count < visitee.edges.size
256
+ # puts visitee.label
257
+ vtx2count[visitee] = visit_count + 1
258
+ edge = visitee.edges[visit_count]
259
+ # Do processing
260
+ if edge.kind_of?(Rley::GFG::ScanEdge) || ((visitee == start_vertex) && visit_count.positive?)
261
+ table[visitee] = {} unless table.include? visitee
262
+ current_row = table[current_from]
263
+ current_row[visitee] = [] unless current_row.include? visitee
264
+ current_row[visitee].concat(visit[from_indices.last..(visit.size - 1)])
265
+ from_indices << (visit.size - 1)
266
+ elsif (visitee == end_vertex) && visit_count.zero?
267
+ current_row = table[current_from]
268
+ current_row[visitee] = [] unless current_row.include? visitee
269
+ current_row[visitee].concat(visit[from_indices.last..(visit.size - 1)])
270
+ from_indices << (visit.size - 1)
271
+ end
272
+ successor = edge.successor
273
+ visit << successor
274
+ vtx2count[successor] = 0 unless vtx2count.include?(successor)
275
+ else
276
+ if (visitee == end_vertex) && visit_count == visitee.edges.size
277
+ current_row = table[current_from]
278
+ current_row[visitee] = [] unless current_row.include? visitee
279
+ slice = visit[from_indices.last..(visit.size - 1)]
280
+ if slice.size > 1
281
+ current_row[visitee].concat(slice)
282
+ from_indices << (visit.size - 1)
283
+ end
284
+ end
285
+ visit.pop
286
+ from_indices.pop if from_indices.last >= visit.size
287
+ end
288
+ end
289
+
290
+ table
291
+ end
292
+
293
+ def build_table2
294
+ raw_table = build_raw_table
295
+ refined_table = {}
296
+
297
+ # To get table right
298
+ # for every non start or end vertex:
299
+ # Check if there is an entry for start_vertex, if yes
300
+ # prefix path = raw_table[curr_vertex][start_vertex]
301
+ # remove last vertex in prefix path (it's start vertex)
302
+ # then for each entry from raw_table[start_vertex]:
303
+ # create a keyval pair: key, value = prefix + original value
304
+ end_vertex = end_vertex_for[start_vertex.non_terminal]
305
+ raw_table.each_pair do |key, val|
306
+ if key == start_vertex || key == end_vertex
307
+ refined_table[key] = val.dup
308
+ next
309
+ end
310
+
311
+ if val.include? start_vertex
312
+ prefix_path = val[start_vertex]
313
+ prefix_path.pop
314
+ start_row = raw_table[start_vertex]
315
+ new_row = val.dup
316
+ new_row.delete(start_vertex)
317
+ start_row.each_pair do |to_vertex, to_path|
318
+ new_row[to_vertex] = prefix_path + to_path
319
+ end
320
+ refined_table[key] = new_row
321
+ end
322
+ end
323
+
324
+ refined_table
325
+ end
326
+
327
+ # visit_action takes two arguments: a vertex and an edge
328
+ # return true/false if false stop traversal
329
+ def df_traversal(aVertex, &visit_action)
330
+ visit = [aVertex]
331
+ vtx2count = { aVertex => 0 }
332
+
333
+ loop do
334
+ break if visit.empty?
335
+
336
+ visitee = visit.last
337
+ visit_count = vtx2count[visitee]
338
+ if visit_count < visitee.edges.size
339
+ # puts visitee.label
340
+ vtx2count[visitee] = visit_count + 1
341
+ edge = visitee.edges[visit_count]
342
+ resume = block_given? ? yield(visitee, edge) : true
343
+ if resume
344
+ successor = edge.successor
345
+ visit << successor
346
+ vtx2count[successor] = 0 unless vtx2count.include?(successor)
347
+ end
348
+ else
349
+ visit.pop
350
+ end
351
+ end
352
+ end
353
+
354
+ def row_to_s(aRow)
355
+ result = +''
356
+ aRow.each_pair do |to_vertex, to_path|
357
+ path_text = to_path.map(&:label).join(', ')
358
+ result << "#{to_vertex.label} => [#{path_text}]\n"
359
+ end
360
+
361
+ result
362
+ end
363
+ end # module
364
+
365
+ graph.extend(GraphMixin)
366
+ # graph.df_traversal(graph.start_vertex) { |n, _edge| puts n.label; true }
367
+ # p graph.build_table.size
368
+ raw_table = graph.build_raw_table
369
+ p raw_table[graph.start_vertex]
370
+ # (S. => .a S) => [.S, S => .a S], S. => [.S, S => ., S.]
371
+
372
+ p raw_table[raw_table.keys[1]]
373
+ # S. => [S., S=> a S., S.]
374
+
375
+ p raw_table[raw_table.keys[2]]
376
+ # .S => [S => .a S, S => a .S, .S]
377
+
378
+ # raw_table[.S] = { (S. => .a S) => [.S, S => .a S], S. => [.S, S => ., S.] }
379
+ # raw_table[.S] = { S. => [S., S=> a S., S.] }
380
+ # raw_table[S => .a S] = { .S => [S => .a S, S => a .S, .S] }
381
+
382
+ refined_table = graph.build_table2
383
+ puts '=='
384
+ puts graph.row_to_s(refined_table[graph.start_vertex])
385
+ # refined_table[start_vertex] = { (S => .a S) => [.S, S => .a S], S. => [S., S => ., S.] }
386
+ puts '=='
387
+ puts graph.row_to_s(refined_table[refined_table.keys[1]])
388
+ # refined_table[S.] = { S. => [S., S => a S., S.] }
389
+ puts '=='
390
+ puts graph.row_to_s(refined_table[refined_table.keys[2]])
391
+ # refined_table[S => .a S] = { (S => .a S) => [S => .a S, S => a .S, .S, S -> .a S],
392
+ # S. => [S => .a S, S => a .S, .S, S => ., S.] }
393
+
394
+
395
+ # | | .S | S. | (S -> .a S) |
396
+ # |:-------------:|:--:|:--:|-------------|
397
+ # | .S start | | Ta | Tb |
398
+ # | S. end | | ?? | |
399
+ # | (S -> .a S) | | Tc | Td |
400
+
401
+
402
+ class TransitionTable
403
+ attr_reader(:table)
404
+
405
+ def initialize(aTable)
406
+ @table = aTable
407
+ end
408
+
409
+ def paths_from_start(aTerminalName)
410
+ destinations = table.values.first
411
+ result = []
412
+ destinations.each_pair do |to_vx, path|
413
+ next unless Rley::GFG::ItemVertex
414
+
415
+ result << path if to_vx.next_symbol&.name == aTerminalName
416
+ end
417
+
418
+ result
419
+ end
420
+
421
+ def paths_from(from_terminal, to_terminal)
422
+ from_vertices = table.keys.select do |vx|
423
+ vx.kind_of?(Rley::GFG::ItemVertex) && vx.next_symbol&.name == from_terminal
424
+ end
425
+
426
+ result = []
427
+ from_vertices.each do |fr_vx|
428
+ destinations = table[fr_vx]
429
+ destinations.each_pair do |to_vx, path|
430
+ next unless Rley::GFG::ItemVertex
431
+
432
+ result << path if to_vx.next_symbol&.name == to_terminal
433
+ end
434
+ end
435
+
436
+ result
437
+ end
438
+
439
+ def paths_to_end(from_terminal)
440
+ from_vertices = table.keys.select do |vx|
441
+ vx.kind_of?(Rley::GFG::ItemVertex) && vx.next_symbol&.name == from_terminal
442
+ end
443
+
444
+ result = []
445
+ from_vertices.each do |fr_vx|
446
+ destinations = table[fr_vx]
447
+ destinations.each_pair do |to_vx, path|
448
+ result << path if to_vx.kind_of?(Rley::GFG::EndVertex)
449
+ end
450
+ end
451
+
452
+ result
453
+ end
454
+ end # class
455
+
456
+ =begin
457
+ Simulation of recognizer algorithm
458
+ input: aaaaa
459
+ Reminder:
460
+ # refined_table[start_vertex] = { (S => .a S) => [.S, S => .a S], S. => [S., S => ., S.] }
461
+ # refined_table[S.] = { S. => [S., S => a S., S.] }
462
+ # refined_table[S => .a S] = { (S => .a S) => [S => .a S, S => a .S, .S, S -> .a S],
463
+ # S. => [S => .a S, S => a .S, .S, S => ., S.] }
464
+
465
+ Maybe one needs to transform the above table into terminal => terminal table
466
+
467
+ Computing state 0
468
+ What is next terminal? a
469
+ We push P1 == (S => .a S) => [.S, S => .a S]
470
+
471
+ Computing state 1
472
+ What is next terminal? a
473
+ We push P2 == (S => .a S) => [S => .a S, S => a .S, .S, S -> .a S]
474
+
475
+ Computing state 2
476
+ What is next terminal? a
477
+ We push P2 == (S => .a S) => [S => .a S, S => a .S, .S, S -> .a S]
478
+
479
+ Computing state 3
480
+ What is next terminal? a
481
+ We push P2 == (S => .a S) => [S => .a S, S => a .S, .S, S -> .a S]
482
+
483
+ Computing state 4
484
+ What is next terminal? a
485
+ We push P2 == (S => .a S) => [S => .a S, S => a .S, .S, S -> .a S]
486
+
487
+ Computing state 5
488
+ What is next terminal? EOS
489
+ We push P3 == S. => [S => .a S, S => a .S, .S, S => ., S.]
490
+
491
+ Recognizer terminates here.
492
+ =end
493
+
494
+ class Recognizer
495
+ attr_reader(:table)
496
+
497
+ def initialize(aTable)
498
+ @table = TransitionTable.new(aTable)
499
+ end
500
+
501
+ # @param enumerator [Enumerator] Enumerator that yields Terminal symbols
502
+ def run(enum_tokens)
503
+ states = []
504
+ prev_terminal = nil
505
+ paths = nil
506
+ enum_tokens.each_with_index do |token, index|
507
+ terminal_name = token.terminal.name
508
+ if index.zero?
509
+ paths = table.paths_from_start(terminal_name)
510
+ else
511
+ paths = table.paths_from(prev_terminal, terminal_name)
512
+ end
513
+ prev_terminal = terminal_name
514
+ raise StandardError, "Error at position #{index} with #{token.lexeme}." if paths.empty?
515
+
516
+ states << paths
517
+ end
518
+
519
+ paths = table.paths_to_end(prev_terminal)
520
+ raise StandardError, "Error at position #{index} with #{token.lexeme}." if paths.empty?
521
+
522
+ states << paths
523
+
524
+ states
525
+ end
526
+ end # class
527
+
528
+
529
+ recog = Recognizer.new(refined_table)
530
+ states = recog.run(tokens)
531
+ puts 'Done!'
532
+ p states[5]
533
+ # states[0] = [[.S, S => .a S]]
534
+ # states[1] = [[S => .a S, S => a .S, .S, S => .a S]]
535
+ # states[4] = [[S => .a S, S => a .S, .S, S => .a S]]
536
+ # states[5] = [[S => .a S, S => a .S, .S, S => a S., S.]]
537
+
538
+ # Simulation for second recognizer pass
539
+ # Every entry edge has an identifier
540
+ # Corresponding exit edge has same identifier negated
541
+ # Call and return edges are ambiguous when a non-terminal is lhs of multiple productions
542
+ # Creating statechart[0]:
543
+ # for each path do
544
+ # for each item vertex do
545
+ # push on current statechart rank, the item vertices
546
+ # statechart[0] # next token is 'a'. There is only one path from .S to S => .a S
547
+ # push S => .a S @ 0
548
+ # stack:
549
+ # S => .a S @ 0
550
+
551
+ # statechart[1] # next to token is 'a'. There is only one path from S => a .S to S => .a S
552
+ # Remove vertex from previous state
553
+ # push S => a . S @ 0
554
+ # push S => .a S @ 1 # Start vertex discarded
555
+ # stack:
556
+ # S => .a S @ 1
557
+ # S => .a S @ 0
558
+ #
559
+ # statechart[2] # next to token is 'a'. There is only one path from S => a .S to S => .a S
560
+ # Remove vertex from previous state
561
+ # push S => a . S @ 1
562
+ # push S => .a S @ 2 # Start vertex discarded
563
+ # stack:
564
+ # S => .a S @ 2
565
+ # S => .a S @ 1
566
+ # S => .a S @ 0
567
+ #
568
+ # statechart[3] # next to token is 'a'. There is only one path from S => a .S to S => .a S
569
+ # Remove vertex from previous state
570
+ # push S => a . S @ 2
571
+ # push S => .a S @ 3 # Start vertex discarded
572
+ # stack:
573
+ # S => .a S @ 3
574
+ # S => .a S @ 2
575
+ # S => .a S @ 1
576
+ # S => .a S @ 0
577
+ #
578
+ # statechart[4] # next to token is 'a'. There is only one path from S => a .S to S => .a S
579
+ # Remove vertex from previous state
580
+ # push S => a . S @ 3
581
+ # push S => .a S @ 4 # Start vertex discarded
582
+ # stack:
583
+ # S => .a S @ 4
584
+ # S => .a S @ 3
585
+ # S => .a S @ 2
586
+ # S => .a S @ 1
587
+ # S => .a S @ 0
588
+ #
589
+ # statechart[5] # next to token is EOS. There is only one path from S => a .S to S.
590
+ # Remove vertex from previous state
591
+ # push S => a . S @ 4
592
+ # push .S @ 5 ?
593
+ # push S => . @5
594
+ # push S. possibly add 0..n events S => a S. @ xx
595
+ # stack:
596
+ # S => .a S @ 4 expecting S => a S @ 4 event
597
+ # S => .a S @ 3
598
+ # S => .a S @ 2
599
+ # S => .a S @ 1
600
+ # S => .a S @ 0
601
+ #
@@ -1,14 +1,17 @@
1
- # As Rubocop shouts about "offences" in the generated code,
1
+ # As Rubocop shouts about "offences" in the generated code,
2
2
  # we disable the detection of most of them...
3
3
  # rubocop: disable Style/MutableConstant
4
4
  # rubocop: disable Layout/SpaceBeforeSemicolon
5
5
  # rubocop: disable Style/Alias
6
6
  # rubocop: disable Style/AndOr
7
7
  # rubocop: disable Style/MultilineIfModifier
8
+ # rubocop: disable Style/MultilineIfThen
8
9
  # rubocop: disable Style/StringLiterals
9
10
  # rubocop: disable Style/MethodDefParentheses
10
- # rubocop: disable Security/Open
11
+ # rubocop: disable Style/RedundantParentheses
12
+ # rubocop: disable Style/SlicingWithRange
11
13
  # rubocop: disable Style/TrailingCommaInArrayLiteral
14
+ # rubocop: disable Security/Open#
12
15
  # rubocop: disable Layout/EmptyLinesAroundMethodBody
13
16
  # rubocop: disable Style/WhileUntilDo
14
17
  # rubocop: disable Style/MultilineWhenThen
@@ -94,6 +97,9 @@ end
94
97
  # rubocop: enable Style/MultilineIfModifier
95
98
  # rubocop: enable Style/StringLiterals
96
99
  # rubocop: enable Style/MethodDefParentheses
100
+ # rubocop: enable Style/MultilineIfThen:
101
+ # rubocop: enable Style/RedundantParentheses
102
+ # rubocop: enable Style/SlicingWithRange
97
103
  # rubocop: enable Security/Open
98
104
  # rubocop: enable Style/TrailingCommaInArrayLiteral
99
105
  # rubocop: enable Layout/EmptyLinesAroundMethodBody
@@ -1,10 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # encoding: UTF-8
4
-
5
3
  #--
6
4
  # This file is automatically generated. Do not modify it.
7
- # Generated by: oedipus_lex version 2.5.3.
5
+ # Generated by: oedipus_lex version 2.6.2.
8
6
  # Source: loxxy_raw_scanner.rex
9
7
  #++
10
8
 
@@ -15,10 +13,13 @@
15
13
  # rubocop: disable Style/Alias
16
14
  # rubocop: disable Style/AndOr
17
15
  # rubocop: disable Style/MultilineIfModifier
16
+ # rubocop: disable Style/MultilineIfThen
18
17
  # rubocop: disable Style/StringLiterals
19
18
  # rubocop: disable Style/MethodDefParentheses
20
- # rubocop: disable Security/Open
19
+ # rubocop: disable Style/RedundantParentheses
20
+ # rubocop: disable Style/SlicingWithRange
21
21
  # rubocop: disable Style/TrailingCommaInArrayLiteral
22
+ # rubocop: disable Security/Open#
22
23
  # rubocop: disable Layout/EmptyLinesAroundMethodBody
23
24
  # rubocop: disable Style/WhileUntilDo
24
25
  # rubocop: disable Style/MultilineWhenThen
@@ -104,7 +105,6 @@ class LoxxyRawScanner
104
105
  old_pos - start_of_current_line_pos
105
106
  end
106
107
 
107
-
108
108
  ##
109
109
  # The current scanner class. Must be overridden in subclasses.
110
110
 
@@ -153,7 +153,7 @@ class LoxxyRawScanner
153
153
  token = nil
154
154
 
155
155
  until ss.eos? or token do
156
- if ss.peek(1) == "\n"
156
+ if ss.check(/\n/) then
157
157
  self.lineno += 1
158
158
  # line starts 1 position after the newline
159
159
  self.start_of_current_line_pos = ss.pos + 1
@@ -246,6 +246,9 @@ end # class
246
246
  # rubocop: enable Style/MultilineIfModifier
247
247
  # rubocop: enable Style/StringLiterals
248
248
  # rubocop: enable Style/MethodDefParentheses
249
+ # rubocop: enable Style/MultilineIfThen:
250
+ # rubocop: enable Style/RedundantParentheses
251
+ # rubocop: enable Style/SlicingWithRange
249
252
  # rubocop: enable Security/Open
250
253
  # rubocop: enable Style/TrailingCommaInArrayLiteral
251
254
  # rubocop: enable Layout/EmptyLinesAroundMethodBody
@@ -23,7 +23,7 @@ class LoxxyTokenizer
23
23
  lookup = %w[
24
24
  and class else false fun for if nil or
25
25
  print return super this true var while
26
- ].map { |x| [x, x.upcase] }.to_h
26
+ ].to_h { |x| [x, x.upcase] }
27
27
  lookup.default = 'IDENTIFIER'
28
28
  lookup.freeze
29
29
  end
@@ -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.11'
8
+ Version = '0.8.13'
9
9
 
10
10
  # Brief description of the gem.
11
11
  Description = "Ruby implementation of the Earley's parsing algorithm"
data/lib/rley/engine.rb CHANGED
@@ -80,9 +80,11 @@ module Rley # This module is used as a namespace
80
80
  aTokenizer.each do |a_token|
81
81
  next unless a_token
82
82
 
83
- term_name = a_token.terminal
84
- term_symb = grammar.name2symbol[term_name]
85
- a_token.instance_variable_set(:@terminal, term_symb)
83
+ if a_token.terminal.kind_of?(String)
84
+ term_name = a_token.terminal
85
+ term_symb = grammar.name2symbol[term_name]
86
+ a_token.instance_variable_set(:@terminal, term_symb)
87
+ end
86
88
  tokens << a_token
87
89
  end
88
90
  parser = build_parser(grammar)
@@ -386,23 +386,6 @@ module Rley # This module is used as a namespace
386
386
  end
387
387
  end
388
388
 
389
- # def sequence_name(aSequenceNode)
390
- # subnode_names = +''
391
- # aSequenceNode.subnodes.each do |subn|
392
- # case subn
393
- # when SymbolNode
394
- # subnode_names << "_#{subn.name}"
395
- # when SequenceNode
396
- # subnode_names << "_#{sequence_name(subn)}"
397
- # when RepetitionNode
398
- # suffix = repetition2suffix(subn.repetition)
399
- # subnode_names << suffix
400
- # end
401
- # end
402
- #
403
- # "seq#{subnode_names}"
404
- # end
405
-
406
389
  def node_base_name(aNode)
407
390
  if aNode.kind_of?(SymbolNode)
408
391
  aNode.name
@@ -418,24 +401,6 @@ module Rley # This module is used as a namespace
418
401
  "#{base_name}#{suffix}"
419
402
  end
420
403
 
421
- # def serialize_sequence(aSequenceNode)
422
- # text = +''
423
- # aSequenceNode.subnodes.each do |sn|
424
- # text << ' '
425
- # case sn
426
- # when SymbolNode
427
- # text << sn.name
428
- # when SequenceNode
429
- # text << sequence_name(sn)
430
- # when RepetitionNode
431
- # suffix = repetition2suffix(sn.repetition)
432
- # text << suffix
433
- # end
434
- # end
435
- #
436
- # text.strip
437
- # end
438
-
439
404
  def add_raw_rule(aSymbol, aRHS, aTag, simplified = false, constraints = [])
440
405
  raw_rule = RawRule.new(aSymbol, aRHS, aTag, simplified, constraints)
441
406
  if synthetized.include?(aSymbol)
@@ -49,7 +49,7 @@ module Rley # Open this namespace to avoid module qualifier prefixes
49
49
  term_name = 'INTEGER'
50
50
  else
51
51
  err_msg = "Unknown token '#{lexeme}'"
52
- raise StandardError, err_msg
52
+ raise StandardError, err_msg
53
53
  end
54
54
  end
55
55
  pos = Rley::Lexical::Position.new(1, curr_pos + 1)
@@ -104,9 +104,9 @@ module Rley # Open this namespace to avoid module qualifier prefixes
104
104
  context 'Parsing: ' do
105
105
  # rubocop: disable Naming/VariableNumber
106
106
  it 'should parse a valid simple input' do
107
- parse_result = subject.parse(grm1_tokens)
107
+ parse_result = subject.parse(build_token_sequence(%w[a a b c c], grammar_abc))
108
108
  expect(parse_result.success?).to eq(true)
109
- # expect(parse_result.ambiguous?).to eq(false)
109
+ expect(parse_result.ambiguous?).to eq(false)
110
110
  ######################
111
111
  # Expectation chart[0]:
112
112
  expected = [
data/spec/spec_helper.rb CHANGED
@@ -3,7 +3,6 @@
3
3
  # File: spec_helper.rb
4
4
  # Purpose: utility file that is loaded by all our RSpec files
5
5
 
6
- require 'pp' # Use pretty-print for debugging purposes
7
6
  require 'rspec' # Use the RSpec framework
8
7
 
9
8
 
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rley
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.11
4
+ version: 0.8.13
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dimitri Geshef
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2022-04-17 00:00:00.000000000 Z
10
+ date: 2025-02-16 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: prime
@@ -16,74 +15,74 @@ dependencies:
16
15
  requirements:
17
16
  - - "~>"
18
17
  - !ruby/object:Gem::Version
19
- version: 0.1.0
18
+ version: 0.1.2
20
19
  type: :runtime
21
20
  prerelease: false
22
21
  version_requirements: !ruby/object:Gem::Requirement
23
22
  requirements:
24
23
  - - "~>"
25
24
  - !ruby/object:Gem::Version
26
- version: 0.1.0
25
+ version: 0.1.2
27
26
  - !ruby/object:Gem::Dependency
28
27
  name: rake
29
28
  requirement: !ruby/object:Gem::Requirement
30
29
  requirements:
31
30
  - - "~>"
32
31
  - !ruby/object:Gem::Version
33
- version: '13.0'
32
+ version: 13.1.0
34
33
  - - ">="
35
34
  - !ruby/object:Gem::Version
36
- version: 13.0.0
35
+ version: 13.1.0
37
36
  type: :development
38
37
  prerelease: false
39
38
  version_requirements: !ruby/object:Gem::Requirement
40
39
  requirements:
41
40
  - - "~>"
42
41
  - !ruby/object:Gem::Version
43
- version: '13.0'
42
+ version: 13.1.0
44
43
  - - ">="
45
44
  - !ruby/object:Gem::Version
46
- version: 13.0.0
45
+ version: 13.1.0
47
46
  - !ruby/object:Gem::Dependency
48
47
  name: rspec
49
48
  requirement: !ruby/object:Gem::Requirement
50
49
  requirements:
51
50
  - - "~>"
52
51
  - !ruby/object:Gem::Version
53
- version: '3.5'
52
+ version: 3.12.0
54
53
  - - ">="
55
54
  - !ruby/object:Gem::Version
56
- version: 3.5.0
55
+ version: 3.12.0
57
56
  type: :development
58
57
  prerelease: false
59
58
  version_requirements: !ruby/object:Gem::Requirement
60
59
  requirements:
61
60
  - - "~>"
62
61
  - !ruby/object:Gem::Version
63
- version: '3.5'
62
+ version: 3.12.0
64
63
  - - ">="
65
64
  - !ruby/object:Gem::Version
66
- version: 3.5.0
65
+ version: 3.12.0
67
66
  - !ruby/object:Gem::Dependency
68
- name: rubygems
67
+ name: yard
69
68
  requirement: !ruby/object:Gem::Requirement
70
69
  requirements:
71
70
  - - "~>"
72
71
  - !ruby/object:Gem::Version
73
- version: '2.0'
72
+ version: 0.9.34
74
73
  - - ">="
75
74
  - !ruby/object:Gem::Version
76
- version: 2.0.0
75
+ version: 0.9.34
77
76
  type: :development
78
77
  prerelease: false
79
78
  version_requirements: !ruby/object:Gem::Requirement
80
79
  requirements:
81
80
  - - "~>"
82
81
  - !ruby/object:Gem::Version
83
- version: '2.0'
82
+ version: 0.9.34
84
83
  - - ">="
85
84
  - !ruby/object:Gem::Version
86
- version: 2.0.0
85
+ version: 0.9.34
87
86
  description: A general parser using the Earley algorithm.
88
87
  email: famished.tiger@yahoo.com
89
88
  executables: []
@@ -94,7 +93,6 @@ files:
94
93
  - ".rspec"
95
94
  - ".rubocop.yml"
96
95
  - ".ruby-gemset"
97
- - ".travis.yml"
98
96
  - ".yardopts"
99
97
  - CHANGELOG.md
100
98
  - LICENSE.txt
@@ -136,6 +134,7 @@ files:
136
134
  - examples/general/calc_iter2/spec/calculator_spec.rb
137
135
  - examples/general/general_examples.md
138
136
  - examples/general/left.rb
137
+ - examples/general/recursive_right.rb
139
138
  - examples/general/right.rb
140
139
  - examples/tokenizer/README.md
141
140
  - examples/tokenizer/loxxy_raw_scanner.rex
@@ -310,15 +309,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
310
309
  requirements:
311
310
  - - ">="
312
311
  - !ruby/object:Gem::Version
313
- version: 2.6.0
312
+ version: 3.2.0
314
313
  required_rubygems_version: !ruby/object:Gem::Requirement
315
314
  requirements:
316
315
  - - ">="
317
316
  - !ruby/object:Gem::Version
318
317
  version: '0'
319
318
  requirements: []
320
- rubygems_version: 3.3.7
321
- signing_key:
319
+ rubygems_version: 3.6.2
322
320
  specification_version: 4
323
321
  summary: Ruby implementation of the Earley's parsing algorithm
324
322
  test_files:
data/.travis.yml DELETED
@@ -1,29 +0,0 @@
1
- language: ruby
2
- dist: trusty
3
-
4
- before_install:
5
- - gem update --system
6
- - gem install bundler
7
-
8
- script:
9
- - bundle exec rake
10
-
11
- rvm:
12
- - 2.7.1
13
- - 2.6.6
14
- - 2.5.8
15
- - 2.4.10
16
- - ruby-head
17
- - jruby-head
18
- before_install: gem install bundler -v 2.0.2
19
-
20
- matrix:
21
- allow_failures:
22
- - rvm: ruby-head
23
- - rvm: jruby-head
24
-
25
-
26
- # whitelist
27
- branches:
28
- only:
29
- - master