lrama 0.1.0

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