lrama 0.6.10 → 0.7.0

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.
Files changed (87) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/gh-pages.yml +46 -0
  3. data/.github/workflows/test.yaml +40 -8
  4. data/.gitignore +1 -0
  5. data/.rdoc_options +2 -0
  6. data/Gemfile +4 -2
  7. data/NEWS.md +125 -30
  8. data/README.md +44 -15
  9. data/Rakefile +13 -1
  10. data/Steepfile +5 -0
  11. data/doc/Index.md +58 -0
  12. data/doc/development/compressed_state_table/main.md +635 -0
  13. data/doc/development/compressed_state_table/parse.output +174 -0
  14. data/doc/development/compressed_state_table/parse.y +22 -0
  15. data/doc/development/compressed_state_table/parser.rb +282 -0
  16. data/lib/lrama/bitmap.rb +4 -1
  17. data/lib/lrama/command.rb +2 -1
  18. data/lib/lrama/context.rb +3 -3
  19. data/lib/lrama/counterexamples/derivation.rb +6 -5
  20. data/lib/lrama/counterexamples/example.rb +7 -4
  21. data/lib/lrama/counterexamples/path.rb +4 -0
  22. data/lib/lrama/counterexamples.rb +19 -9
  23. data/lib/lrama/digraph.rb +30 -0
  24. data/lib/lrama/grammar/binding.rb +47 -15
  25. data/lib/lrama/grammar/parameterizing_rule/rhs.rb +1 -1
  26. data/lib/lrama/grammar/rule.rb +8 -0
  27. data/lib/lrama/grammar/rule_builder.rb +4 -16
  28. data/lib/lrama/grammar/symbols/resolver.rb +4 -0
  29. data/lib/lrama/grammar.rb +10 -5
  30. data/lib/lrama/lexer/grammar_file.rb +8 -1
  31. data/lib/lrama/lexer/location.rb +17 -1
  32. data/lib/lrama/lexer/token/char.rb +1 -0
  33. data/lib/lrama/lexer/token/ident.rb +1 -0
  34. data/lib/lrama/lexer/token/instantiate_rule.rb +6 -1
  35. data/lib/lrama/lexer/token/tag.rb +3 -1
  36. data/lib/lrama/lexer/token/user_code.rb +6 -2
  37. data/lib/lrama/lexer/token.rb +14 -2
  38. data/lib/lrama/lexer.rb +5 -5
  39. data/lib/lrama/logger.rb +4 -0
  40. data/lib/lrama/option_parser.rb +10 -8
  41. data/lib/lrama/options.rb +2 -1
  42. data/lib/lrama/parser.rb +529 -490
  43. data/lib/lrama/state/reduce.rb +2 -3
  44. data/lib/lrama/state.rb +288 -1
  45. data/lib/lrama/states/item.rb +8 -0
  46. data/lib/lrama/states.rb +69 -2
  47. data/lib/lrama/trace_reporter.rb +17 -2
  48. data/lib/lrama/version.rb +1 -1
  49. data/lrama.gemspec +1 -1
  50. data/parser.y +42 -30
  51. data/rbs_collection.lock.yaml +10 -2
  52. data/sig/generated/lrama/bitmap.rbs +11 -0
  53. data/sig/generated/lrama/digraph.rbs +39 -0
  54. data/sig/generated/lrama/grammar/binding.rbs +34 -0
  55. data/sig/generated/lrama/lexer/grammar_file.rbs +28 -0
  56. data/sig/generated/lrama/lexer/location.rbs +52 -0
  57. data/sig/{lrama → generated/lrama}/lexer/token/char.rbs +2 -0
  58. data/sig/{lrama → generated/lrama}/lexer/token/ident.rbs +2 -0
  59. data/sig/{lrama → generated/lrama}/lexer/token/instantiate_rule.rbs +8 -0
  60. data/sig/{lrama → generated/lrama}/lexer/token/tag.rbs +3 -0
  61. data/sig/{lrama → generated/lrama}/lexer/token/user_code.rbs +6 -1
  62. data/sig/{lrama → generated/lrama}/lexer/token.rbs +26 -3
  63. data/sig/generated/lrama/logger.rbs +14 -0
  64. data/sig/generated/lrama/trace_reporter.rbs +25 -0
  65. data/sig/lrama/counterexamples/derivation.rbs +33 -0
  66. data/sig/lrama/counterexamples/example.rbs +45 -0
  67. data/sig/lrama/counterexamples/path.rbs +21 -0
  68. data/sig/lrama/counterexamples/production_path.rbs +11 -0
  69. data/sig/lrama/counterexamples/start_path.rbs +13 -0
  70. data/sig/lrama/counterexamples/state_item.rbs +10 -0
  71. data/sig/lrama/counterexamples/transition_path.rbs +11 -0
  72. data/sig/lrama/counterexamples/triple.rbs +20 -0
  73. data/sig/lrama/counterexamples.rbs +29 -0
  74. data/sig/lrama/grammar/rule_builder.rbs +0 -1
  75. data/sig/lrama/grammar/symbol.rbs +1 -1
  76. data/sig/lrama/grammar/symbols/resolver.rbs +3 -3
  77. data/sig/lrama/grammar.rbs +13 -0
  78. data/sig/lrama/options.rbs +1 -0
  79. data/sig/lrama/state/reduce_reduce_conflict.rbs +2 -2
  80. data/sig/lrama/state.rbs +79 -0
  81. data/sig/lrama/states.rbs +101 -0
  82. metadata +34 -14
  83. data/sig/lrama/bitmap.rbs +0 -7
  84. data/sig/lrama/digraph.rbs +0 -23
  85. data/sig/lrama/grammar/binding.rbs +0 -19
  86. data/sig/lrama/lexer/grammar_file.rbs +0 -17
  87. data/sig/lrama/lexer/location.rbs +0 -26
@@ -26,9 +26,8 @@ module Lrama
26
26
  end
27
27
 
28
28
  def selected_look_ahead
29
- if @look_ahead
30
- # @type ivar @look_ahead: Array<Grammar::Symbol>
31
- @look_ahead - @not_selected_symbols
29
+ if look_ahead
30
+ look_ahead - @not_selected_symbols
32
31
  else
33
32
  []
34
33
  end
data/lib/lrama/state.rb CHANGED
@@ -10,7 +10,7 @@ module Lrama
10
10
  class State
11
11
  attr_reader :id, :accessing_symbol, :kernels, :conflicts, :resolved_conflicts,
12
12
  :default_reduction_rule, :closure, :items
13
- attr_accessor :shifts, :reduces
13
+ attr_accessor :shifts, :reduces, :ielr_isocores, :lalr_isocore
14
14
 
15
15
  def initialize(id, accessing_symbol, kernels)
16
16
  @id = id
@@ -23,6 +23,12 @@ module Lrama
23
23
  @conflicts = []
24
24
  @resolved_conflicts = []
25
25
  @default_reduction_rule = nil
26
+ @predecessors = []
27
+ @lalr_isocore = self
28
+ @ielr_isocores = [self]
29
+ @internal_dependencies = {}
30
+ @successor_dependencies = {}
31
+ @always_follows = {}
26
32
  end
27
33
 
28
34
  def closure=(closure)
@@ -84,6 +90,18 @@ module Lrama
84
90
  @transitions ||= shifts.map {|shift| [shift, @items_to_state[shift.next_items]] }
85
91
  end
86
92
 
93
+ def update_transition(shift, next_state)
94
+ set_items_to_state(shift.next_items, next_state)
95
+ next_state.append_predecessor(self)
96
+ clear_transitions_cache
97
+ end
98
+
99
+ def clear_transitions_cache
100
+ @nterm_transitions = nil
101
+ @term_transitions = nil
102
+ @transitions = nil
103
+ end
104
+
87
105
  def selected_term_transitions
88
106
  term_transitions.reject do |shift, next_state|
89
107
  shift.not_selected
@@ -142,5 +160,274 @@ module Lrama
142
160
  conflict.type == :reduce_reduce
143
161
  end
144
162
  end
163
+
164
+ def propagate_lookaheads(next_state)
165
+ next_state.kernels.map {|item|
166
+ lookahead_sets =
167
+ if item.position == 1
168
+ goto_follow_set(item.lhs)
169
+ else
170
+ kernel = kernels.find {|k| k.predecessor_item_of?(item) }
171
+ item_lookahead_set[kernel]
172
+ end
173
+
174
+ [item, lookahead_sets & next_state.lookahead_set_filters[item]]
175
+ }.to_h
176
+ end
177
+
178
+ def lookaheads_recomputed
179
+ !@item_lookahead_set.nil?
180
+ end
181
+
182
+ def compatible_lookahead?(filtered_lookahead)
183
+ !lookaheads_recomputed ||
184
+ @lalr_isocore.annotation_list.all? {|token, actions|
185
+ a = dominant_contribution(token, actions, item_lookahead_set)
186
+ b = dominant_contribution(token, actions, filtered_lookahead)
187
+ a.nil? || b.nil? || a == b
188
+ }
189
+ end
190
+
191
+ def lookahead_set_filters
192
+ kernels.map {|kernel|
193
+ [kernel,
194
+ @lalr_isocore.annotation_list.select {|token, actions|
195
+ token.term? && actions.any? {|action, contributions|
196
+ !contributions.nil? && contributions.key?(kernel) && contributions[kernel]
197
+ }
198
+ }.map {|token, _| token }
199
+ ]
200
+ }.to_h
201
+ end
202
+
203
+ def dominant_contribution(token, actions, lookaheads)
204
+ a = actions.select {|action, contributions|
205
+ contributions.nil? || contributions.any? {|item, contributed| contributed && lookaheads[item].include?(token) }
206
+ }.map {|action, _| action }
207
+ return nil if a.empty?
208
+ a.reject {|action|
209
+ if action.is_a?(State::Shift)
210
+ action.not_selected
211
+ elsif action.is_a?(State::Reduce)
212
+ action.not_selected_symbols.include?(token)
213
+ end
214
+ }
215
+ end
216
+
217
+ def inadequacy_list
218
+ return @inadequacy_list if @inadequacy_list
219
+
220
+ shift_contributions = shifts.map {|shift|
221
+ [shift.next_sym, [shift]]
222
+ }.to_h
223
+ reduce_contributions = reduces.map {|reduce|
224
+ (reduce.look_ahead || []).map {|sym|
225
+ [sym, [reduce]]
226
+ }.to_h
227
+ }.reduce(Hash.new([])) {|hash, cont|
228
+ hash.merge(cont) {|_, a, b| a | b }
229
+ }
230
+
231
+ list = shift_contributions.merge(reduce_contributions) {|_, a, b| a | b }
232
+ @inadequacy_list = list.select {|token, actions| token.term? && actions.size > 1 }
233
+ end
234
+
235
+ def annotation_list
236
+ return @annotation_list if @annotation_list
237
+
238
+ @annotation_list = annotate_manifestation
239
+ @annotation_list = @items_to_state.values.map {|next_state| next_state.annotate_predecessor(self) }
240
+ .reduce(@annotation_list) {|result, annotations|
241
+ result.merge(annotations) {|_, actions_a, actions_b|
242
+ if actions_a.nil? || actions_b.nil?
243
+ actions_a || actions_b
244
+ else
245
+ actions_a.merge(actions_b) {|_, contributions_a, contributions_b|
246
+ if contributions_a.nil? || contributions_b.nil?
247
+ next contributions_a || contributions_b
248
+ end
249
+
250
+ contributions_a.merge(contributions_b) {|_, contributed_a, contributed_b|
251
+ contributed_a || contributed_b
252
+ }
253
+ }
254
+ end
255
+ }
256
+ }
257
+ end
258
+
259
+ def annotate_manifestation
260
+ inadequacy_list.transform_values {|actions|
261
+ actions.map {|action|
262
+ if action.is_a?(Shift)
263
+ [action, nil]
264
+ elsif action.is_a?(Reduce)
265
+ if action.rule.empty_rule?
266
+ [action, lhs_contributions(action.rule.lhs, inadequacy_list.key(actions))]
267
+ else
268
+ contributions = kernels.map {|kernel| [kernel, kernel.rule == action.rule && kernel.end_of_rule?] }.to_h
269
+ [action, contributions]
270
+ end
271
+ end
272
+ }.to_h
273
+ }
274
+ end
275
+
276
+ def annotate_predecessor(predecessor)
277
+ annotation_list.transform_values {|actions|
278
+ token = annotation_list.key(actions)
279
+ actions.transform_values {|inadequacy|
280
+ next nil if inadequacy.nil?
281
+ lhs_adequacy = kernels.any? {|kernel|
282
+ inadequacy[kernel] && kernel.position == 1 && predecessor.lhs_contributions(kernel.lhs, token).nil?
283
+ }
284
+ if lhs_adequacy
285
+ next nil
286
+ else
287
+ predecessor.kernels.map {|pred_k|
288
+ [pred_k, kernels.any? {|k|
289
+ inadequacy[k] && (
290
+ pred_k.predecessor_item_of?(k) && predecessor.item_lookahead_set[pred_k].include?(token) ||
291
+ k.position == 1 && predecessor.lhs_contributions(k.lhs, token)[pred_k]
292
+ )
293
+ }]
294
+ }.to_h
295
+ end
296
+ }
297
+ }
298
+ end
299
+
300
+ def lhs_contributions(sym, token)
301
+ shift, next_state = nterm_transitions.find {|sh, _| sh.next_sym == sym }
302
+ if always_follows(shift, next_state).include?(token)
303
+ nil
304
+ else
305
+ kernels.map {|kernel| [kernel, follow_kernel_items(shift, next_state, kernel) && item_lookahead_set[kernel].include?(token)] }.to_h
306
+ end
307
+ end
308
+
309
+ def follow_kernel_items(shift, next_state, kernel)
310
+ queue = [[self, shift, next_state]]
311
+ until queue.empty?
312
+ st, sh, next_st = queue.pop
313
+ return true if kernel.next_sym == sh.next_sym && kernel.symbols_after_transition.all?(&:nullable)
314
+ st.internal_dependencies(sh, next_st).each {|v| queue << v }
315
+ end
316
+ false
317
+ end
318
+
319
+ def item_lookahead_set
320
+ return @item_lookahead_set if @item_lookahead_set
321
+
322
+ kernels.map {|item|
323
+ value =
324
+ if item.lhs.accept_symbol?
325
+ []
326
+ elsif item.position > 1
327
+ prev_items = predecessors_with_item(item)
328
+ prev_items.map {|st, i| st.item_lookahead_set[i] }.reduce([]) {|acc, syms| acc |= syms }
329
+ elsif item.position == 1
330
+ prev_state = @predecessors.find {|p| p.shifts.any? {|shift| shift.next_sym == item.lhs } }
331
+ shift, next_state = prev_state.nterm_transitions.find {|shift, _| shift.next_sym == item.lhs }
332
+ prev_state.goto_follows(shift, next_state)
333
+ end
334
+ [item, value]
335
+ }.to_h
336
+ end
337
+
338
+ def item_lookahead_set=(k)
339
+ @item_lookahead_set = k
340
+ end
341
+
342
+ def predecessors_with_item(item)
343
+ result = []
344
+ @predecessors.each do |pre|
345
+ pre.items.each do |i|
346
+ result << [pre, i] if i.predecessor_item_of?(item)
347
+ end
348
+ end
349
+ result
350
+ end
351
+
352
+ def append_predecessor(prev_state)
353
+ @predecessors << prev_state
354
+ @predecessors.uniq!
355
+ end
356
+
357
+ def goto_follow_set(nterm_token)
358
+ return [] if nterm_token.accept_symbol?
359
+ shift, next_state = @lalr_isocore.nterm_transitions.find {|sh, _| sh.next_sym == nterm_token }
360
+
361
+ @kernels
362
+ .select {|kernel| follow_kernel_items(shift, next_state, kernel) }
363
+ .map {|kernel| item_lookahead_set[kernel] }
364
+ .reduce(always_follows(shift, next_state)) {|result, terms| result |= terms }
365
+ end
366
+
367
+ def goto_follows(shift, next_state)
368
+ queue = internal_dependencies(shift, next_state) + predecessor_dependencies(shift, next_state)
369
+ terms = always_follows(shift, next_state)
370
+ until queue.empty?
371
+ st, sh, next_st = queue.pop
372
+ terms |= st.always_follows(sh, next_st)
373
+ st.internal_dependencies(sh, next_st).each {|v| queue << v }
374
+ st.predecessor_dependencies(sh, next_st).each {|v| queue << v }
375
+ end
376
+ terms
377
+ end
378
+
379
+ def always_follows(shift, next_state)
380
+ return @always_follows[[shift, next_state]] if @always_follows[[shift, next_state]]
381
+
382
+ queue = internal_dependencies(shift, next_state) + successor_dependencies(shift, next_state)
383
+ terms = []
384
+ until queue.empty?
385
+ st, sh, next_st = queue.pop
386
+ terms |= next_st.term_transitions.map {|sh, _| sh.next_sym }
387
+ st.internal_dependencies(sh, next_st).each {|v| queue << v }
388
+ st.successor_dependencies(sh, next_st).each {|v| queue << v }
389
+ end
390
+ @always_follows[[shift, next_state]] = terms
391
+ end
392
+
393
+ def internal_dependencies(shift, next_state)
394
+ return @internal_dependencies[[shift, next_state]] if @internal_dependencies[[shift, next_state]]
395
+
396
+ syms = @items.select {|i|
397
+ i.next_sym == shift.next_sym && i.symbols_after_transition.all?(&:nullable) && i.position == 0
398
+ }.map(&:lhs).uniq
399
+ @internal_dependencies[[shift, next_state]] = nterm_transitions.select {|sh, _| syms.include?(sh.next_sym) }.map {|goto| [self, *goto] }
400
+ end
401
+
402
+ def successor_dependencies(shift, next_state)
403
+ return @successor_dependencies[[shift, next_state]] if @successor_dependencies[[shift, next_state]]
404
+
405
+ @successor_dependencies[[shift, next_state]] =
406
+ next_state.nterm_transitions
407
+ .select {|next_shift, _| next_shift.next_sym.nullable }
408
+ .map {|transition| [next_state, *transition] }
409
+ end
410
+
411
+ def predecessor_dependencies(shift, next_state)
412
+ state_items = []
413
+ @kernels.select {|kernel|
414
+ kernel.next_sym == shift.next_sym && kernel.symbols_after_transition.all?(&:nullable)
415
+ }.each do |item|
416
+ queue = predecessors_with_item(item)
417
+ until queue.empty?
418
+ st, i = queue.pop
419
+ if i.position == 0
420
+ state_items << [st, i]
421
+ else
422
+ st.predecessors_with_item(i).each {|v| queue << v }
423
+ end
424
+ end
425
+ end
426
+
427
+ state_items.map {|state, item|
428
+ sh, next_st = state.nterm_transitions.find {|shi, _| shi.next_sym == item.lhs }
429
+ [state, sh, next_st]
430
+ }
431
+ end
145
432
  end
146
433
  end
@@ -64,6 +64,10 @@ module Lrama
64
64
  rhs[position..-1]
65
65
  end
66
66
 
67
+ def symbols_after_transition
68
+ rhs[position+1..-1]
69
+ end
70
+
67
71
  def to_s
68
72
  "#{lhs.id.s_value}: #{display_name}"
69
73
  end
@@ -78,6 +82,10 @@ module Lrama
78
82
  r = symbols_after_dot.map(&:display_name).join(" ")
79
83
  ". #{r} (rule #{rule_id})"
80
84
  end
85
+
86
+ def predecessor_item_of?(other_item)
87
+ rule == other_item.rule && position == other_item.position - 1
88
+ end
81
89
  end
82
90
  end
83
91
  end
data/lib/lrama/states.rb CHANGED
@@ -41,6 +41,8 @@ module Lrama
41
41
  # value is array of [state.id, nterm.token_id].
42
42
  @reads_relation = {}
43
43
 
44
+ # `Read(p, A) =s DR(p, A) ∪ ∪{Read(r, C) | (p, A) reads (r, C)}`
45
+ #
44
46
  # `@read_sets` is a hash whose
45
47
  # key is [state.id, nterm.token_id],
46
48
  # value is bitmap of term.
@@ -62,6 +64,8 @@ module Lrama
62
64
  # value is array of [state.id, nterm.token_id].
63
65
  @lookback_relation = {}
64
66
 
67
+ # `Follow(p, A) =s Read(p, A) ∪ ∪{Follow(p', B) | (p, A) includes (p', B)}`
68
+ #
65
69
  # `@follow_sets` is a hash whose
66
70
  # key is [state.id, rule.id],
67
71
  # value is bitmap of term.
@@ -92,6 +96,20 @@ module Lrama
92
96
  report_duration(:compute_default_reduction) { compute_default_reduction }
93
97
  end
94
98
 
99
+ def compute_ielr
100
+ report_duration(:split_states) { split_states }
101
+ report_duration(:compute_direct_read_sets) { compute_direct_read_sets }
102
+ report_duration(:compute_reads_relation) { compute_reads_relation }
103
+ report_duration(:compute_read_sets) { compute_read_sets }
104
+ report_duration(:compute_includes_relation) { compute_includes_relation }
105
+ report_duration(:compute_lookback_relation) { compute_lookback_relation }
106
+ report_duration(:compute_follow_sets) { compute_follow_sets }
107
+ report_duration(:compute_look_ahead_sets) { compute_look_ahead_sets }
108
+ report_duration(:compute_conflicts) { compute_conflicts }
109
+
110
+ report_duration(:compute_default_reduction) { compute_default_reduction }
111
+ end
112
+
95
113
  def reporter
96
114
  StatesReporter.new(self)
97
115
  end
@@ -235,7 +253,7 @@ module Lrama
235
253
  # Trace
236
254
  previous = state.kernels.first.previous_sym
237
255
  trace_state do |out|
238
- out << sprintf("state_list_append (state = %d, symbol = %d (%s))",
256
+ out << sprintf("state_list_append (state = %d, symbol = %d (%s))\n",
239
257
  @states.count, previous.number, previous.display_name)
240
258
  end
241
259
 
@@ -265,7 +283,10 @@ module Lrama
265
283
  state.shifts.each do |shift|
266
284
  new_state, created = create_state(shift.next_sym, shift.next_items, states_created)
267
285
  state.set_items_to_state(shift.next_items, new_state)
268
- enqueue_state(states, new_state) if created
286
+ if created
287
+ enqueue_state(states, new_state)
288
+ new_state.append_predecessor(state)
289
+ end
269
290
  end
270
291
  end
271
292
  end
@@ -524,5 +545,51 @@ module Lrama
524
545
  end.first
525
546
  end
526
547
  end
548
+
549
+ def split_states
550
+ @states.each do |state|
551
+ state.transitions.each do |shift, next_state|
552
+ compute_state(state, shift, next_state)
553
+ end
554
+ end
555
+ end
556
+
557
+ def merge_lookaheads(state, filtered_lookaheads)
558
+ return if state.kernels.all? {|item| (filtered_lookaheads[item] - state.item_lookahead_set[item]).empty? }
559
+
560
+ state.item_lookahead_set = state.item_lookahead_set.merge {|_, v1, v2| v1 | v2 }
561
+ state.transitions.each do |shift, next_state|
562
+ next if next_state.lookaheads_recomputed
563
+ compute_state(state, shift, next_state)
564
+ end
565
+ end
566
+
567
+ def compute_state(state, shift, next_state)
568
+ filtered_lookaheads = state.propagate_lookaheads(next_state)
569
+ s = next_state.ielr_isocores.find {|st| st.compatible_lookahead?(filtered_lookaheads) }
570
+
571
+ if s.nil?
572
+ s = next_state.ielr_isocores.last
573
+ new_state = State.new(@states.count, s.accessing_symbol, s.kernels)
574
+ new_state.closure = s.closure
575
+ new_state.compute_shifts_reduces
576
+ s.transitions.each do |sh, next_state|
577
+ new_state.set_items_to_state(sh.next_items, next_state)
578
+ end
579
+ @states << new_state
580
+ new_state.lalr_isocore = s
581
+ s.ielr_isocores << new_state
582
+ s.ielr_isocores.each do |st|
583
+ st.ielr_isocores = s.ielr_isocores
584
+ end
585
+ new_state.item_lookahead_set = filtered_lookaheads
586
+ state.update_transition(shift, new_state)
587
+ elsif(!s.lookaheads_recomputed)
588
+ s.item_lookahead_set = filtered_lookaheads
589
+ else
590
+ state.update_transition(shift, s)
591
+ merge_lookaheads(s, filtered_lookaheads)
592
+ end
593
+ end
527
594
  end
528
595
  end
@@ -1,27 +1,42 @@
1
+ # rbs_inline: enabled
1
2
  # frozen_string_literal: true
2
3
 
3
4
  module Lrama
4
5
  class TraceReporter
6
+ # @rbs (Lrama::Grammar grammar) -> void
5
7
  def initialize(grammar)
6
8
  @grammar = grammar
7
9
  end
8
10
 
11
+ # @rbs (**Hash[Symbol, bool] options) -> void
9
12
  def report(**options)
10
13
  _report(**options)
11
14
  end
12
15
 
13
16
  private
14
17
 
15
- def _report(rules: false, actions: false, **_)
16
- report_rules if rules
18
+ # @rbs rules: (bool rules, bool actions, bool only_explicit_rules, **untyped _) -> void
19
+ def _report(rules: false, actions: false, only_explicit_rules: false, **_)
20
+ report_rules if rules && !only_explicit_rules
21
+ report_only_explicit_rules if only_explicit_rules
17
22
  report_actions if actions
18
23
  end
19
24
 
25
+ # @rbs () -> void
20
26
  def report_rules
21
27
  puts "Grammar rules:"
22
28
  @grammar.rules.each { |rule| puts rule.display_name }
23
29
  end
24
30
 
31
+ # @rbs () -> void
32
+ def report_only_explicit_rules
33
+ puts "Grammar rules:"
34
+ @grammar.rules.each do |rule|
35
+ puts rule.display_name_without_action if rule.lhs.first_set.any?
36
+ end
37
+ end
38
+
39
+ # @rbs () -> void
25
40
  def report_actions
26
41
  puts "Grammar rules with actions:"
27
42
  @grammar.rules.each { |rule| puts rule.with_actions }
data/lib/lrama/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Lrama
4
- VERSION = "0.6.10".freeze
4
+ VERSION = "0.7.0".freeze
5
5
  end
data/lrama.gemspec CHANGED
@@ -21,7 +21,7 @@ Gem::Specification.new do |spec|
21
21
 
22
22
  spec.metadata["homepage_uri"] = spec.homepage
23
23
  spec.metadata["source_code_uri"] = spec.homepage
24
- spec.metadata["documentation_uri"] = spec.homepage
24
+ spec.metadata["documentation_uri"] = "https://ruby.github.io/lrama/"
25
25
  spec.metadata["changelog_uri"] = "#{spec.homepage}/releases"
26
26
  spec.metadata["bug_tracker_uri"] = "#{spec.homepage}/issues"
27
27
 
data/parser.y CHANGED
@@ -25,7 +25,7 @@ rule
25
25
 
26
26
  bison_declaration: grammar_declaration
27
27
  | "%expect" INTEGER { @grammar.expect = val[1] }
28
- | "%define" variable value
28
+ | "%define" variable value { @grammar.define[val[1].s_value] = val[2]&.s_value }
29
29
  | "%param" param+
30
30
  | "%lex-param" param+
31
31
  {
@@ -61,26 +61,26 @@ rule
61
61
  | symbol_declaration
62
62
  | rule_declaration
63
63
  | inline_declaration
64
- | "%destructor" param generic_symbol+
64
+ | "%destructor" param (symbol | TAG)+
65
65
  {
66
66
  @grammar.add_destructor(
67
- ident_or_tags: val[2],
67
+ ident_or_tags: val[2].flatten,
68
68
  token_code: val[1],
69
69
  lineno: val[1].line
70
70
  )
71
71
  }
72
- | "%printer" param generic_symbol+
72
+ | "%printer" param (symbol | TAG)+
73
73
  {
74
74
  @grammar.add_printer(
75
- ident_or_tags: val[2],
75
+ ident_or_tags: val[2].flatten,
76
76
  token_code: val[1],
77
77
  lineno: val[1].line
78
78
  )
79
79
  }
80
- | "%error-token" param generic_symbol+
80
+ | "%error-token" param (symbol | TAG)+
81
81
  {
82
82
  @grammar.add_error_token(
83
- ident_or_tags: val[2],
83
+ ident_or_tags: val[2].flatten,
84
84
  token_code: val[1],
85
85
  lineno: val[1].line
86
86
  )
@@ -115,6 +115,18 @@ rule
115
115
  }
116
116
  }
117
117
  }
118
+ | "%nterm" symbol_declarations
119
+ {
120
+ val[1].each {|hash|
121
+ hash[:tokens].each {|id|
122
+ if @grammar.find_term_by_s_value(id.s_value)
123
+ on_action_error("symbol #{id.s_value} redeclared as a nonterminal", id)
124
+ else
125
+ @grammar.add_type(id: id, tag: hash[:tag])
126
+ end
127
+ }
128
+ }
129
+ }
118
130
  | "%left" token_declarations_for_precedence
119
131
  {
120
132
  val[1].each {|hash|
@@ -156,13 +168,7 @@ rule
156
168
  @precedence_number += 1
157
169
  }
158
170
 
159
- token_declarations: token_declaration+
160
- {
161
- val[0].each {|token_declaration|
162
- @grammar.add_term(id: token_declaration[0], alias_name: token_declaration[2], token_id: token_declaration[1], tag: nil, replace: true)
163
- }
164
- }
165
- | TAG token_declaration+
171
+ token_declarations: TAG? token_declaration+
166
172
  {
167
173
  val[1].each {|token_declaration|
168
174
  @grammar.add_term(id: token_declaration[0], alias_name: token_declaration[2], token_id: token_declaration[1], tag: val[0], replace: true)
@@ -208,7 +214,7 @@ rule
208
214
  result = val[0].append(builder)
209
215
  }
210
216
 
211
- rule_rhs: empty
217
+ rule_rhs: "%empty"?
212
218
  {
213
219
  reset_precs
214
220
  result = Grammar::ParameterizingRule::Rhs.new
@@ -250,11 +256,16 @@ rule
250
256
  result = builder
251
257
  }
252
258
 
253
- alias: # empty
254
- | string_as_id { result = val[0].s_value }
259
+ alias: string_as_id? { result = val[0].s_value if val[0] }
255
260
 
256
- symbol_declarations: symbol+ { result = [{tag: nil, tokens: val[0]}] }
257
- | TAG symbol+ { result = [{tag: val[0], tokens: val[1]}] }
261
+ symbol_declarations: TAG? symbol+
262
+ {
263
+ result = if val[0]
264
+ [{tag: val[0], tokens: val[1]}]
265
+ else
266
+ [{tag: nil, tokens: val[1]}]
267
+ end
268
+ }
258
269
  | symbol_declarations TAG symbol+ { result = val[0].append({tag: val[1], tokens: val[2]}) }
259
270
 
260
271
  symbol: id
@@ -311,7 +322,7 @@ rule
311
322
  result = val[0].append(builder)
312
323
  }
313
324
 
314
- rhs: empty
325
+ rhs: "%empty"?
315
326
  {
316
327
  reset_precs
317
328
  result = @grammar.create_rule_builder(@rule_counter, @midrule_action_counter)
@@ -362,9 +373,15 @@ rule
362
373
  | "+" { result = "nonempty_list" }
363
374
  | "*" { result = "list" }
364
375
 
365
- parameterizing_args: symbol { result = [val[0]] }
376
+ parameterizing_args: symbol parameterizing_suffix?
377
+ {
378
+ result = if val[1]
379
+ [Lrama::Lexer::Token::InstantiateRule.new(s_value: val[1].s_value, location: @lexer.location, args: val[0])]
380
+ else
381
+ [val[0]]
382
+ end
383
+ }
366
384
  | parameterizing_args ',' symbol { result = val[0].append(val[2]) }
367
- | symbol parameterizing_suffix { result = [Lrama::Lexer::Token::InstantiateRule.new(s_value: val[1].s_value, location: @lexer.location, args: val[0])] }
368
385
  | IDENTIFIER "(" parameterizing_args ")" { result = [Lrama::Lexer::Token::InstantiateRule.new(s_value: val[0].s_value, location: @lexer.location, args: val[2])] }
369
386
 
370
387
  midrule_action: "{"
@@ -404,12 +421,6 @@ rule
404
421
  | STRING
405
422
  | "{...}"
406
423
 
407
- generic_symbol: symbol
408
- | TAG
409
-
410
- empty: /* empty */
411
- | "%empty"
412
-
413
424
  string_as_id: STRING { result = Lrama::Lexer::Token::Ident.new(s_value: val[0]) }
414
425
  end
415
426
 
@@ -417,17 +428,18 @@ end
417
428
 
418
429
  include Lrama::Report::Duration
419
430
 
420
- def initialize(text, path, debug = false)
431
+ def initialize(text, path, debug = false, define = {})
421
432
  @grammar_file = Lrama::Lexer::GrammarFile.new(path, text)
422
433
  @yydebug = debug
423
434
  @rule_counter = Lrama::Grammar::Counter.new(0)
424
435
  @midrule_action_counter = Lrama::Grammar::Counter.new(1)
436
+ @define = define
425
437
  end
426
438
 
427
439
  def parse
428
440
  report_duration(:parse) do
429
441
  @lexer = Lrama::Lexer.new(@grammar_file)
430
- @grammar = Lrama::Grammar.new(@rule_counter)
442
+ @grammar = Lrama::Grammar.new(@rule_counter, @define)
431
443
  @precedence_number = 0
432
444
  reset_precs
433
445
  do_parse