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.
- checksums.yaml +4 -4
- data/.github/workflows/gh-pages.yml +46 -0
- data/.github/workflows/test.yaml +40 -8
- data/.gitignore +1 -0
- data/.rdoc_options +2 -0
- data/Gemfile +4 -2
- data/NEWS.md +125 -30
- data/README.md +44 -15
- data/Rakefile +13 -1
- data/Steepfile +5 -0
- data/doc/Index.md +58 -0
- data/doc/development/compressed_state_table/main.md +635 -0
- data/doc/development/compressed_state_table/parse.output +174 -0
- data/doc/development/compressed_state_table/parse.y +22 -0
- data/doc/development/compressed_state_table/parser.rb +282 -0
- data/lib/lrama/bitmap.rb +4 -1
- data/lib/lrama/command.rb +2 -1
- data/lib/lrama/context.rb +3 -3
- data/lib/lrama/counterexamples/derivation.rb +6 -5
- data/lib/lrama/counterexamples/example.rb +7 -4
- data/lib/lrama/counterexamples/path.rb +4 -0
- data/lib/lrama/counterexamples.rb +19 -9
- data/lib/lrama/digraph.rb +30 -0
- data/lib/lrama/grammar/binding.rb +47 -15
- data/lib/lrama/grammar/parameterizing_rule/rhs.rb +1 -1
- data/lib/lrama/grammar/rule.rb +8 -0
- data/lib/lrama/grammar/rule_builder.rb +4 -16
- data/lib/lrama/grammar/symbols/resolver.rb +4 -0
- data/lib/lrama/grammar.rb +10 -5
- data/lib/lrama/lexer/grammar_file.rb +8 -1
- data/lib/lrama/lexer/location.rb +17 -1
- data/lib/lrama/lexer/token/char.rb +1 -0
- data/lib/lrama/lexer/token/ident.rb +1 -0
- data/lib/lrama/lexer/token/instantiate_rule.rb +6 -1
- data/lib/lrama/lexer/token/tag.rb +3 -1
- data/lib/lrama/lexer/token/user_code.rb +6 -2
- data/lib/lrama/lexer/token.rb +14 -2
- data/lib/lrama/lexer.rb +5 -5
- data/lib/lrama/logger.rb +4 -0
- data/lib/lrama/option_parser.rb +10 -8
- data/lib/lrama/options.rb +2 -1
- data/lib/lrama/parser.rb +529 -490
- data/lib/lrama/state/reduce.rb +2 -3
- data/lib/lrama/state.rb +288 -1
- data/lib/lrama/states/item.rb +8 -0
- data/lib/lrama/states.rb +69 -2
- data/lib/lrama/trace_reporter.rb +17 -2
- data/lib/lrama/version.rb +1 -1
- data/lrama.gemspec +1 -1
- data/parser.y +42 -30
- data/rbs_collection.lock.yaml +10 -2
- data/sig/generated/lrama/bitmap.rbs +11 -0
- data/sig/generated/lrama/digraph.rbs +39 -0
- data/sig/generated/lrama/grammar/binding.rbs +34 -0
- data/sig/generated/lrama/lexer/grammar_file.rbs +28 -0
- data/sig/generated/lrama/lexer/location.rbs +52 -0
- data/sig/{lrama → generated/lrama}/lexer/token/char.rbs +2 -0
- data/sig/{lrama → generated/lrama}/lexer/token/ident.rbs +2 -0
- data/sig/{lrama → generated/lrama}/lexer/token/instantiate_rule.rbs +8 -0
- data/sig/{lrama → generated/lrama}/lexer/token/tag.rbs +3 -0
- data/sig/{lrama → generated/lrama}/lexer/token/user_code.rbs +6 -1
- data/sig/{lrama → generated/lrama}/lexer/token.rbs +26 -3
- data/sig/generated/lrama/logger.rbs +14 -0
- data/sig/generated/lrama/trace_reporter.rbs +25 -0
- data/sig/lrama/counterexamples/derivation.rbs +33 -0
- data/sig/lrama/counterexamples/example.rbs +45 -0
- data/sig/lrama/counterexamples/path.rbs +21 -0
- data/sig/lrama/counterexamples/production_path.rbs +11 -0
- data/sig/lrama/counterexamples/start_path.rbs +13 -0
- data/sig/lrama/counterexamples/state_item.rbs +10 -0
- data/sig/lrama/counterexamples/transition_path.rbs +11 -0
- data/sig/lrama/counterexamples/triple.rbs +20 -0
- data/sig/lrama/counterexamples.rbs +29 -0
- data/sig/lrama/grammar/rule_builder.rbs +0 -1
- data/sig/lrama/grammar/symbol.rbs +1 -1
- data/sig/lrama/grammar/symbols/resolver.rbs +3 -3
- data/sig/lrama/grammar.rbs +13 -0
- data/sig/lrama/options.rbs +1 -0
- data/sig/lrama/state/reduce_reduce_conflict.rbs +2 -2
- data/sig/lrama/state.rbs +79 -0
- data/sig/lrama/states.rbs +101 -0
- metadata +34 -14
- data/sig/lrama/bitmap.rbs +0 -7
- data/sig/lrama/digraph.rbs +0 -23
- data/sig/lrama/grammar/binding.rbs +0 -19
- data/sig/lrama/lexer/grammar_file.rbs +0 -17
- data/sig/lrama/lexer/location.rbs +0 -26
data/lib/lrama/state/reduce.rb
CHANGED
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
|
data/lib/lrama/states/item.rb
CHANGED
@@ -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
|
-
|
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
|
data/lib/lrama/trace_reporter.rb
CHANGED
@@ -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
|
-
|
16
|
-
|
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
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"] =
|
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
|
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
|
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
|
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:
|
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+
|
257
|
-
|
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
|
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
|