lrama 0.1.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.
@@ -0,0 +1,510 @@
1
+ require "lrama/report"
2
+
3
+ module Lrama
4
+ # This is passed to a template
5
+ class Context
6
+ include Report::Duration
7
+
8
+ ErrorActionNumber = -Float::INFINITY
9
+ BaseMin = -Float::INFINITY
10
+
11
+ # TODO: It might be better to pass `states` to Output directly?
12
+ attr_reader :states
13
+
14
+ def initialize(states)
15
+ @states = states
16
+ @yydefact = nil
17
+ @yydefgoto = nil
18
+ # Array of array
19
+ @_actions = []
20
+
21
+ report_duration(:compute_tables) { compute_tables }
22
+ end
23
+
24
+ # enum yytokentype
25
+ def yytokentype
26
+ @states.terms.reject do |term|
27
+ 0 < term.token_id && term.token_id < 128
28
+ end.map do |term|
29
+ [term.id.s_value, term.token_id, term.display_name]
30
+ end.unshift(["YYEMPTY", -2, nil])
31
+ end
32
+
33
+ # enum yysymbol_kind_t
34
+ def yysymbol_kind_t
35
+ @states.symbols.map do |sym|
36
+ [sym.enum_name, sym.number, sym.comment]
37
+ end.unshift(["YYSYMBOL_YYEMPTY", -2, nil])
38
+ end
39
+
40
+ # State number of final (accepted) state
41
+ def yyfinal
42
+ @states.states.find do |state|
43
+ state.items.find do |item|
44
+ item.rule.lhs.id.s_value == "$accept" && item.end_of_rule?
45
+ end
46
+ end.id
47
+ end
48
+
49
+ def yylast
50
+ @yylast
51
+ end
52
+
53
+ # Number of terms
54
+ def yyntokens
55
+ @states.terms.count
56
+ end
57
+
58
+ # Number of nterms
59
+ def yynnts
60
+ @states.nterms.count
61
+ end
62
+
63
+ # Number of rules
64
+ def yynrules
65
+ @states.rules.count
66
+ end
67
+
68
+ # Number of states
69
+ def yynstates
70
+ @states.states.count
71
+ end
72
+
73
+ # Last token number
74
+ def yymaxutok
75
+ @states.terms.map(&:token_id).max
76
+ end
77
+
78
+ # YYTRANSLATE
79
+ #
80
+ # yytranslate is a mapping from token id to symbol number
81
+ def yytranslate
82
+ # 2 is YYSYMBOL_YYUNDEF
83
+ a = Array.new(yymaxutok, 2)
84
+
85
+ @states.terms.each do |term|
86
+ a[term.token_id] = term.number
87
+ end
88
+
89
+ return a
90
+ end
91
+
92
+ # Mapping from rule number to line number of the rule is defined.
93
+ # Dummy rule is appended as the first element whose value is 0
94
+ # because 0 means error in yydefact.
95
+ def yyrline
96
+ a = [0]
97
+
98
+ @states.rules.each do |rule|
99
+ a << rule.lineno
100
+ end
101
+
102
+ return a
103
+ end
104
+
105
+ # Mapping from symbol number to its name
106
+ def yytname
107
+ @states.symbols.sort_by(&:number).map do |sym|
108
+ sym.display_name
109
+ end
110
+ end
111
+
112
+ def yypact_ninf
113
+ @yypact_ninf
114
+ end
115
+
116
+ def yytable_ninf
117
+ @yytable_ninf
118
+ end
119
+
120
+ def yypact
121
+ @base[0...yynstates]
122
+ end
123
+
124
+ def yydefact
125
+ @yydefact
126
+ end
127
+
128
+ def yypgoto
129
+ @base[yynstates..-1]
130
+ end
131
+
132
+ def yydefgoto
133
+ @yydefgoto
134
+ end
135
+
136
+ def yytable
137
+ @table
138
+ end
139
+
140
+ def yycheck
141
+ @check
142
+ end
143
+
144
+ def yystos
145
+ @states.states.map do |state|
146
+ state.accessing_symbol.number
147
+ end
148
+ end
149
+
150
+ # Mapping from rule number to symbol number of LHS.
151
+ # Dummy rule is appended as the first element whose value is 0
152
+ # because 0 means error in yydefact.
153
+ def yyr1
154
+ a = [0]
155
+
156
+ @states.rules.each do |rule|
157
+ a << rule.lhs.number
158
+ end
159
+
160
+ return a
161
+ end
162
+
163
+ # Mapping from rule number to lenght of RHS.
164
+ # Dummy rule is appended as the first element whose value is 0
165
+ # because 0 means error in yydefact.
166
+ def yyr2
167
+ a = [0]
168
+
169
+ @states.rules.each do |rule|
170
+ a << rule.rhs.count
171
+ end
172
+
173
+ return a
174
+ end
175
+
176
+ private
177
+
178
+ # Compute these
179
+ #
180
+ # See also: "src/tables.c" of Bison.
181
+ #
182
+ # * yydefact
183
+ # * yydefgoto
184
+ # * yypact and yypgoto
185
+ # * yytable
186
+ # * yycheck
187
+ # * yypact_ninf
188
+ # * yytable_ninf
189
+ def compute_tables
190
+ compute_yydefact
191
+ compute_yydefgoto
192
+ sort_actions
193
+ # debug_sorted_actions
194
+ compute_packed_table
195
+ end
196
+
197
+ def vectors_count
198
+ @states.states.count + @states.nterms.count
199
+ end
200
+
201
+ # In compressed table, rule 0 is appended as an error case
202
+ # and reduce is represented as minus number.
203
+ def rule_id_to_action_number(rule_id)
204
+ (rule_id + 1) * -1
205
+ end
206
+
207
+ # Symbol number is assinged to term first then nterm.
208
+ # This method calculates sequence_number for nterm.
209
+ def nterm_number_to_sequence_number(nterm_number)
210
+ nterm_number - @states.terms.count
211
+ end
212
+
213
+ # Vector is states + nterms
214
+ def nterm_number_to_vector_number(nterm_number)
215
+ @states.states.count + (nterm_number - @states.terms.count)
216
+ end
217
+
218
+ def compute_yydefact
219
+ # Default action (shift/reduce/error) for each state.
220
+ # Index is state id, value is `rule id + 1` of a default reduction.
221
+ @yydefact = Array.new(@states.states.count, 0)
222
+
223
+ @states.states.each do |state|
224
+ # Action number means
225
+ #
226
+ # * number = 0, default action
227
+ # * number = -Float::INFINITY, error by %nonassoc
228
+ # * number > 0, shift then move to state "number"
229
+ # * number < 0, reduce by "-number" rule. Rule "number" is already added by 1.
230
+ actions = Array.new(@states.terms.count, 0)
231
+
232
+ if state.reduces.map(&:selected_look_ahead).any? {|la| !la.empty? }
233
+ # Iterate reduces with reverse order so that first rule is used.
234
+ state.reduces.reverse.each do |reduce|
235
+ reduce.look_ahead.each do |term|
236
+ actions[term.number] = rule_id_to_action_number(reduce.rule.id)
237
+ end
238
+ end
239
+ end
240
+
241
+ # Shift is selected when S/R conflict exists.
242
+ state.selected_term_transitions.each do |shift, next_state|
243
+ actions[shift.next_sym.number] = next_state.id
244
+ end
245
+
246
+ state.resolved_conflicts.select do |conflict|
247
+ conflict.which == :error
248
+ end.each do |conflict|
249
+ actions[conflict.symbol.number] = ErrorActionNumber
250
+ end
251
+
252
+ # If default_reduction_rule, replase default_reduction_rule in
253
+ # actions with zero.
254
+ if state.default_reduction_rule
255
+ actions.map! do |e|
256
+ if e == rule_id_to_action_number(state.default_reduction_rule.id)
257
+ 0
258
+ else
259
+ e
260
+ end
261
+ end
262
+ end
263
+
264
+ # If no default_reduction_rule, default behavior is an
265
+ # error then replase ErrorActionNumber with zero.
266
+ if !state.default_reduction_rule
267
+ actions.map! do |e|
268
+ if e == ErrorActionNumber
269
+ 0
270
+ else
271
+ e
272
+ end
273
+ end
274
+ end
275
+
276
+ s = actions.each_with_index.map do |n, i|
277
+ [i, n]
278
+ end.select do |i, n|
279
+ # Remove default_reduction_rule entries
280
+ n != 0
281
+ end
282
+
283
+ if s.count != 0
284
+ # Entry of @_actions is an array of
285
+ #
286
+ # * State id
287
+ # * Array of tuple, [from, to] where from is term number and to is action.
288
+ # * The number of "Array of tuple" used by sort_actions
289
+ # * "width" used by sort_actions
290
+ @_actions << [state.id, s, s.count, s.last[0] - s.first[0] + 1]
291
+ end
292
+
293
+ @yydefact[state.id] = state.default_reduction_rule ? state.default_reduction_rule.id + 1 : 0
294
+ end
295
+ end
296
+
297
+ def compute_yydefgoto
298
+ # Default GOTO (nterm transition) for each nterm.
299
+ # Index is sequence number of nterm, value is state id
300
+ # of a default nterm transition destination.
301
+ @yydefgoto = Array.new(@states.nterms.count, 0)
302
+ h = {}
303
+ # Mapping from nterm to next_states
304
+ nterm_to_next_states = {}
305
+ terms_count = @states.terms.count
306
+
307
+ @states.states.each do |state|
308
+ state.nterm_transitions.each do |shift, next_state|
309
+ key = shift.next_sym
310
+ nterm_to_next_states[key] ||= []
311
+ nterm_to_next_states[key] << [state, next_state] # [from_state, to_state]
312
+ end
313
+ end
314
+
315
+ @states.nterms.each do |nterm|
316
+ if !(states = nterm_to_next_states[nterm])
317
+ default_goto = 0
318
+ not_default_gotos = []
319
+ else
320
+ default_state = states.map(&:last).group_by {|s| s }.max_by {|_, v| v.count }.first
321
+ default_goto = default_state.id
322
+ not_default_gotos = []
323
+ states.each do |from_state, to_state|
324
+ next if to_state.id == default_goto
325
+ not_default_gotos << [from_state.id, to_state.id]
326
+ end
327
+ end
328
+
329
+ k = nterm_number_to_sequence_number(nterm.number)
330
+ @yydefgoto[k] = default_goto
331
+
332
+ if not_default_gotos.count != 0
333
+ v = nterm_number_to_vector_number(nterm.number)
334
+
335
+ # Entry of @_actions is an array of
336
+ #
337
+ # * Nterm number as vector number
338
+ # * Array of tuple, [from, to] where from is state number and to is state number.
339
+ # * The number of "Array of tuple" used by sort_actions
340
+ # * "width" used by sort_actions
341
+ @_actions << [v, not_default_gotos, not_default_gotos.count, not_default_gotos.last[0] - not_default_gotos.first[0] + 1]
342
+ end
343
+ end
344
+ end
345
+
346
+ def sort_actions
347
+ # This is not same with #sort_actions
348
+ #
349
+ # @sorted_actions = @_actions.sort_by do |_, _, count, width|
350
+ # [-width, -count]
351
+ # end
352
+
353
+ @sorted_actions = []
354
+
355
+ @_actions.each do |action|
356
+ if @sorted_actions.empty?
357
+ @sorted_actions << action
358
+ next
359
+ end
360
+
361
+ j = @sorted_actions.count - 1
362
+ state_id, froms_and_tos, count, width = action
363
+
364
+ while (j >= 0) do
365
+ case
366
+ when @sorted_actions[j][3] < width
367
+ j -= 1
368
+ when @sorted_actions[j][3] == width && @sorted_actions[j][2] < count
369
+ j -= 1
370
+ else
371
+ break
372
+ end
373
+ end
374
+
375
+ @sorted_actions.insert(j + 1, action)
376
+ end
377
+ end
378
+
379
+ def debug_sorted_actions
380
+ ary = Array.new
381
+ @sorted_actions.each do |state_id, froms_and_tos, count, width|
382
+ ary[state_id] = [state_id, froms_and_tos, count, width]
383
+ end
384
+
385
+ print sprintf("table_print:\n\n")
386
+
387
+ print sprintf("order [\n")
388
+ vectors_count.times do |i|
389
+ print sprintf("%d, ", @sorted_actions[i] ? @sorted_actions[i][0] : 0)
390
+ print "\n" if i % 10 == 9
391
+ end
392
+ print sprintf("]\n\n")
393
+
394
+
395
+ print sprintf("width [\n")
396
+ vectors_count.times do |i|
397
+ print sprintf("%d, ", ary[i] ? ary[i][3] : 0)
398
+ print "\n" if i % 10 == 9
399
+ end
400
+ print sprintf("]\n\n")
401
+
402
+
403
+ print sprintf("tally [\n")
404
+ vectors_count.times do |i|
405
+ print sprintf("%d, ", ary[i] ? ary[i][2] : 0)
406
+ print "\n" if i % 10 == 9
407
+ end
408
+ print sprintf("]\n\n")
409
+ end
410
+
411
+ def compute_packed_table
412
+ # yypact and yypgoto
413
+ @base = Array.new(vectors_count, BaseMin)
414
+ # yytable
415
+ @table = []
416
+ # yycheck
417
+ @check = []
418
+ # Key is froms_and_tos, value is index position
419
+ pushed = {}
420
+ userd_res = {}
421
+ lowzero = 0
422
+ high = 0
423
+
424
+ @sorted_actions.each do |state_id, froms_and_tos, _, _|
425
+ if (res = pushed[froms_and_tos])
426
+ @base[state_id] = res
427
+ next
428
+ end
429
+
430
+ res = lowzero - froms_and_tos.first[0]
431
+
432
+ while true do
433
+ ok = true
434
+
435
+ froms_and_tos.each do |from, to|
436
+ loc = res + from
437
+
438
+ if @table[loc]
439
+ # If the cell of table is set, can not use the cell.
440
+ ok = false
441
+ end
442
+ end
443
+
444
+ if userd_res[res]
445
+ ok = false
446
+ end
447
+
448
+ if ok
449
+ break
450
+ else
451
+ res += 1
452
+ end
453
+ end
454
+
455
+ loc = 0
456
+
457
+ froms_and_tos.each do |from, to|
458
+ loc = res + from
459
+
460
+ @table[loc] = to
461
+ @check[loc] = from
462
+ end
463
+
464
+ while (@table[lowzero]) do
465
+ lowzero += 1
466
+ end
467
+
468
+ high = loc if high < loc
469
+
470
+ @base[state_id] = res
471
+ pushed[froms_and_tos] = res
472
+ userd_res[res] = true
473
+ end
474
+
475
+ @yylast = high
476
+
477
+ # replace_ninf
478
+ @yypact_ninf = (@base.select {|i| i != BaseMin } + [0]).min - 1
479
+ @base.map! do |i|
480
+ case i
481
+ when BaseMin
482
+ @yypact_ninf
483
+ else
484
+ i
485
+ end
486
+ end
487
+
488
+ @yytable_ninf = (@table.compact.select {|i| i != ErrorActionNumber } + [0]).min - 1
489
+ @table.map! do |i|
490
+ case i
491
+ when nil
492
+ 0
493
+ when ErrorActionNumber
494
+ @yytable_ninf
495
+ else
496
+ i
497
+ end
498
+ end
499
+
500
+ @check.map! do |i|
501
+ case i
502
+ when nil
503
+ -1
504
+ else
505
+ i
506
+ end
507
+ end
508
+ end
509
+ end
510
+ end