lrama 0.5.2 → 0.5.4
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/test.yaml +10 -1
- data/.gitignore +1 -0
- data/Gemfile +1 -0
- data/LEGAL.md +1 -16
- data/README.md +11 -1
- data/Steepfile +2 -1
- data/doc/TODO.md +8 -3
- data/exe/lrama +1 -1
- data/lib/lrama/command.rb +91 -72
- data/lib/lrama/context.rb +11 -1
- data/lib/lrama/counterexamples/derivation.rb +63 -0
- data/lib/lrama/counterexamples/example.rb +124 -0
- data/lib/lrama/counterexamples/path.rb +69 -0
- data/lib/lrama/counterexamples/state_item.rb +6 -0
- data/lib/lrama/counterexamples/triple.rb +21 -0
- data/lib/lrama/counterexamples.rb +285 -0
- data/lib/lrama/digraph.rb +2 -3
- data/lib/lrama/grammar/auxiliary.rb +7 -0
- data/lib/lrama/grammar/code.rb +123 -0
- data/lib/lrama/grammar/error_token.rb +9 -0
- data/lib/lrama/grammar/precedence.rb +11 -0
- data/lib/lrama/grammar/printer.rb +9 -0
- data/lib/lrama/grammar/reference.rb +22 -0
- data/lib/lrama/grammar/rule.rb +39 -0
- data/lib/lrama/grammar/symbol.rb +87 -0
- data/lib/lrama/grammar/union.rb +10 -0
- data/lib/lrama/grammar.rb +89 -282
- data/lib/lrama/lexer/token/type.rb +8 -0
- data/lib/lrama/lexer/token.rb +77 -0
- data/lib/lrama/lexer.rb +4 -74
- data/lib/lrama/output.rb +32 -4
- data/lib/lrama/parser/token_scanner.rb +3 -6
- data/lib/lrama/parser.rb +9 -1
- data/lib/lrama/report/duration.rb +25 -0
- data/lib/lrama/report/profile.rb +25 -0
- data/lib/lrama/report.rb +2 -47
- data/lib/lrama/state/reduce_reduce_conflict.rb +9 -0
- data/lib/lrama/state/resolved_conflict.rb +29 -0
- data/lib/lrama/state/shift_reduce_conflict.rb +9 -0
- data/lib/lrama/state.rb +13 -30
- data/lib/lrama/states/item.rb +79 -0
- data/lib/lrama/states.rb +24 -73
- data/lib/lrama/states_reporter.rb +28 -3
- data/lib/lrama/type.rb +4 -0
- data/lib/lrama/version.rb +1 -1
- data/lib/lrama.rb +2 -0
- data/lrama.gemspec +1 -1
- data/sig/lrama/{report.rbs → report/duration.rbs} +0 -4
- data/sig/lrama/report/profile.rbs +7 -0
- data/template/bison/yacc.c +371 -0
- metadata +30 -5
@@ -0,0 +1,285 @@
|
|
1
|
+
require "set"
|
2
|
+
|
3
|
+
require "lrama/counterexamples/derivation"
|
4
|
+
require "lrama/counterexamples/example"
|
5
|
+
require "lrama/counterexamples/path"
|
6
|
+
require "lrama/counterexamples/state_item"
|
7
|
+
require "lrama/counterexamples/triple"
|
8
|
+
|
9
|
+
module Lrama
|
10
|
+
# See: https://www.cs.cornell.edu/andru/papers/cupex/cupex.pdf
|
11
|
+
# 4. Constructing Nonunifying Counterexamples
|
12
|
+
class Counterexamples
|
13
|
+
attr_reader :transitions, :productions
|
14
|
+
|
15
|
+
def initialize(states)
|
16
|
+
@states = states
|
17
|
+
setup_transitions
|
18
|
+
setup_productions
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_s
|
22
|
+
"#<Counterexamples>"
|
23
|
+
end
|
24
|
+
alias :inspect :to_s
|
25
|
+
|
26
|
+
def compute(conflict_state)
|
27
|
+
conflict_state.conflicts.flat_map do |conflict|
|
28
|
+
case conflict.type
|
29
|
+
when :shift_reduce
|
30
|
+
shift_reduce_example(conflict_state, conflict)
|
31
|
+
when :reduce_reduce
|
32
|
+
reduce_reduce_examples(conflict_state, conflict)
|
33
|
+
end
|
34
|
+
end.compact
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def setup_transitions
|
40
|
+
# Hash [StateItem, Symbol] => StateItem
|
41
|
+
@transitions = {}
|
42
|
+
# Hash [StateItem, Symbol] => Set(StateItem)
|
43
|
+
@reverse_transitions = {}
|
44
|
+
|
45
|
+
@states.states.each do |src_state|
|
46
|
+
trans = {}
|
47
|
+
|
48
|
+
src_state.transitions.each do |shift, next_state|
|
49
|
+
trans[shift.next_sym] = next_state
|
50
|
+
end
|
51
|
+
|
52
|
+
src_state.items.each do |src_item|
|
53
|
+
next if src_item.end_of_rule?
|
54
|
+
sym = src_item.next_sym
|
55
|
+
dest_state = trans[sym]
|
56
|
+
|
57
|
+
dest_state.kernels.each do |dest_item|
|
58
|
+
next unless (src_item.rule == dest_item.rule) && (src_item.position + 1 == dest_item.position)
|
59
|
+
src_state_item = StateItem.new(src_state, src_item)
|
60
|
+
dest_state_item = StateItem.new(dest_state, dest_item)
|
61
|
+
|
62
|
+
@transitions[[src_state_item, sym]] = dest_state_item
|
63
|
+
|
64
|
+
key = [dest_state_item, sym]
|
65
|
+
@reverse_transitions[key] ||= Set.new
|
66
|
+
@reverse_transitions[key] << src_state_item
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def setup_productions
|
73
|
+
# Hash [StateItem] => Set(Item)
|
74
|
+
@productions = {}
|
75
|
+
# Hash [State, Symbol] => Set(Item). Symbol is nterm
|
76
|
+
@reverse_productions = {}
|
77
|
+
|
78
|
+
@states.states.each do |state|
|
79
|
+
# LHS => Set(Item)
|
80
|
+
h = {}
|
81
|
+
|
82
|
+
state.closure.each do |item|
|
83
|
+
sym = item.lhs
|
84
|
+
|
85
|
+
h[sym] ||= Set.new
|
86
|
+
h[sym] << item
|
87
|
+
end
|
88
|
+
|
89
|
+
state.items.each do |item|
|
90
|
+
next if item.end_of_rule?
|
91
|
+
next if item.next_sym.term?
|
92
|
+
|
93
|
+
sym = item.next_sym
|
94
|
+
state_item = StateItem.new(state, item)
|
95
|
+
key = [state, sym]
|
96
|
+
|
97
|
+
@productions[state_item] = h[sym]
|
98
|
+
|
99
|
+
@reverse_productions[key] ||= Set.new
|
100
|
+
@reverse_productions[key] << item
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def shift_reduce_example(conflict_state, conflict)
|
106
|
+
conflict_symbol = conflict.symbols.first
|
107
|
+
shift_conflict_item = conflict_state.items.find { |item| item.next_sym == conflict_symbol }
|
108
|
+
path2 = shortest_path(conflict_state, conflict.reduce.item, conflict_symbol)
|
109
|
+
path1 = find_shift_conflict_shortest_path(path2, conflict_state, shift_conflict_item)
|
110
|
+
|
111
|
+
Example.new(path1, path2, conflict, conflict_symbol, self)
|
112
|
+
end
|
113
|
+
|
114
|
+
def reduce_reduce_examples(conflict_state, conflict)
|
115
|
+
conflict_symbol = conflict.symbols.first
|
116
|
+
path1 = shortest_path(conflict_state, conflict.reduce1.item, conflict_symbol)
|
117
|
+
path2 = shortest_path(conflict_state, conflict.reduce2.item, conflict_symbol)
|
118
|
+
|
119
|
+
Example.new(path1, path2, conflict, conflict_symbol, self)
|
120
|
+
end
|
121
|
+
|
122
|
+
def find_shift_conflict_shortest_path(reduce_path, conflict_state, conflict_item)
|
123
|
+
state_items = find_shift_conflict_shortest_state_items(reduce_path, conflict_state, conflict_item)
|
124
|
+
build_paths_from_state_items(state_items)
|
125
|
+
end
|
126
|
+
|
127
|
+
def find_shift_conflict_shortest_state_items(reduce_path, conflict_state, conflict_item)
|
128
|
+
target_state_item = StateItem.new(conflict_state, conflict_item)
|
129
|
+
result = [target_state_item]
|
130
|
+
reversed_reduce_path = reduce_path.to_a.reverse
|
131
|
+
# Index for state_item
|
132
|
+
i = 0
|
133
|
+
|
134
|
+
while (path = reversed_reduce_path[i])
|
135
|
+
# Index for prev_state_item
|
136
|
+
j = i + 1
|
137
|
+
_j = j
|
138
|
+
|
139
|
+
while (prev_path = reversed_reduce_path[j])
|
140
|
+
if prev_path.production?
|
141
|
+
j += 1
|
142
|
+
else
|
143
|
+
break
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
state_item = path.to
|
148
|
+
prev_state_item = prev_path&.to
|
149
|
+
|
150
|
+
if target_state_item == state_item || target_state_item.item.start_item?
|
151
|
+
result.concat(reversed_reduce_path[_j..-1].map(&:to))
|
152
|
+
break
|
153
|
+
end
|
154
|
+
|
155
|
+
if target_state_item.item.beginning_of_rule?
|
156
|
+
queue = []
|
157
|
+
queue << [target_state_item]
|
158
|
+
|
159
|
+
# Find reverse production
|
160
|
+
while (sis = queue.shift)
|
161
|
+
si = sis.last
|
162
|
+
|
163
|
+
# Reach to start state
|
164
|
+
if si.item.start_item?
|
165
|
+
sis.shift
|
166
|
+
result.concat(sis)
|
167
|
+
target_state_item = si
|
168
|
+
break
|
169
|
+
end
|
170
|
+
|
171
|
+
if !si.item.beginning_of_rule?
|
172
|
+
key = [si, si.item.previous_sym]
|
173
|
+
@reverse_transitions[key].each do |prev_target_state_item|
|
174
|
+
next if prev_target_state_item.state != prev_state_item.state
|
175
|
+
sis.shift
|
176
|
+
result.concat(sis)
|
177
|
+
result << prev_target_state_item
|
178
|
+
target_state_item = prev_target_state_item
|
179
|
+
i = j
|
180
|
+
queue.clear
|
181
|
+
break
|
182
|
+
end
|
183
|
+
else
|
184
|
+
key = [si.state, si.item.lhs]
|
185
|
+
@reverse_productions[key].each do |item|
|
186
|
+
state_item = StateItem.new(si.state, item)
|
187
|
+
queue << (sis + [state_item])
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
else
|
192
|
+
# Find reverse transition
|
193
|
+
key = [target_state_item, target_state_item.item.previous_sym]
|
194
|
+
@reverse_transitions[key].each do |prev_target_state_item|
|
195
|
+
next if prev_target_state_item.state != prev_state_item.state
|
196
|
+
result << prev_target_state_item
|
197
|
+
target_state_item = prev_target_state_item
|
198
|
+
i = j
|
199
|
+
break
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
result.reverse
|
205
|
+
end
|
206
|
+
|
207
|
+
def build_paths_from_state_items(state_items)
|
208
|
+
paths = state_items.zip([nil] + state_items).map do |si, prev_si|
|
209
|
+
case
|
210
|
+
when prev_si.nil?
|
211
|
+
StartPath.new(si)
|
212
|
+
when si.item.beginning_of_rule?
|
213
|
+
ProductionPath.new(prev_si, si)
|
214
|
+
else
|
215
|
+
TransitionPath.new(prev_si, si)
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
paths
|
220
|
+
end
|
221
|
+
|
222
|
+
def shortest_path(conflict_state, conflict_reduce_item, conflict_term)
|
223
|
+
# queue: is an array of [Triple, [Path]]
|
224
|
+
queue = []
|
225
|
+
visited = {}
|
226
|
+
start_state = @states.states.first
|
227
|
+
raise "BUG: Start state should be just one kernel." if start_state.kernels.count != 1
|
228
|
+
|
229
|
+
start = Triple.new(start_state, start_state.kernels.first, Set.new([@states.eof_symbol]))
|
230
|
+
|
231
|
+
queue << [start, [StartPath.new(start.state_item)]]
|
232
|
+
|
233
|
+
while true
|
234
|
+
triple, paths = queue.shift
|
235
|
+
|
236
|
+
next if visited[triple]
|
237
|
+
visited[triple] = true
|
238
|
+
|
239
|
+
# Found
|
240
|
+
if triple.state == conflict_state && triple.item == conflict_reduce_item && triple.l.include?(conflict_term)
|
241
|
+
return paths
|
242
|
+
end
|
243
|
+
|
244
|
+
# transition
|
245
|
+
triple.state.transitions.each do |shift, next_state|
|
246
|
+
next unless triple.item.next_sym && triple.item.next_sym == shift.next_sym
|
247
|
+
next_state.kernels.each do |kernel|
|
248
|
+
next if kernel.rule != triple.item.rule
|
249
|
+
t = Triple.new(next_state, kernel, triple.l)
|
250
|
+
queue << [t, paths + [TransitionPath.new(triple.state_item, t.state_item)]]
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
# production step
|
255
|
+
triple.state.closure.each do |item|
|
256
|
+
next unless triple.item.next_sym && triple.item.next_sym == item.lhs
|
257
|
+
l = follow_l(triple.item, triple.l)
|
258
|
+
t = Triple.new(triple.state, item, l)
|
259
|
+
queue << [t, paths + [ProductionPath.new(triple.state_item, t.state_item)]]
|
260
|
+
end
|
261
|
+
|
262
|
+
break if queue.empty?
|
263
|
+
end
|
264
|
+
|
265
|
+
return nil
|
266
|
+
end
|
267
|
+
|
268
|
+
def follow_l(item, current_l)
|
269
|
+
# 1. follow_L (A -> X1 ... Xn-1 • Xn) = L
|
270
|
+
# 2. follow_L (A -> X1 ... Xk • Xk+1 Xk+2 ... Xn) = {Xk+2} if Xk+2 is a terminal
|
271
|
+
# 3. follow_L (A -> X1 ... Xk • Xk+1 Xk+2 ... Xn) = FIRST(Xk+2) if Xk+2 is a nonnullable nonterminal
|
272
|
+
# 4. follow_L (A -> X1 ... Xk • Xk+1 Xk+2 ... Xn) = FIRST(Xk+2) + follow_L (A -> X1 ... Xk+1 • Xk+2 ... Xn) if Xk+2 is a nullable nonterminal
|
273
|
+
case
|
274
|
+
when item.number_of_rest_symbols == 1
|
275
|
+
current_l
|
276
|
+
when item.next_next_sym.term?
|
277
|
+
Set.new([item.next_next_sym])
|
278
|
+
when !item.next_next_sym.nullable
|
279
|
+
item.next_next_sym.first_set
|
280
|
+
else
|
281
|
+
item.next_next_sym.first_set + follow_l(item.new_by_next_position, current_l)
|
282
|
+
end
|
283
|
+
end
|
284
|
+
end
|
285
|
+
end
|
data/lib/lrama/digraph.rb
CHANGED
@@ -33,7 +33,7 @@ module Lrama
|
|
33
33
|
@h[x] = d
|
34
34
|
@result[x] = @base_function[x] # F x = F' x
|
35
35
|
|
36
|
-
@relation[x]
|
36
|
+
@relation[x]&.each do |y|
|
37
37
|
traverse(y) if @h[y] == 0
|
38
38
|
@h[x] = [@h[x], @h[y]].min
|
39
39
|
@result[x] |= @result[y] # F x = F x + F y
|
@@ -43,9 +43,8 @@ module Lrama
|
|
43
43
|
while true do
|
44
44
|
z = @stack.pop
|
45
45
|
@h[z] = Float::INFINITY
|
46
|
-
@result[z] = @result[x] # F (Top of S) = F x
|
47
|
-
|
48
46
|
break if z == x
|
47
|
+
@result[z] = @result[x] # F (Top of S) = F x
|
49
48
|
end
|
50
49
|
end
|
51
50
|
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
require "forwardable"
|
2
|
+
|
3
|
+
module Lrama
|
4
|
+
class Grammar
|
5
|
+
class Code < Struct.new(:type, :token_code, keyword_init: true)
|
6
|
+
extend Forwardable
|
7
|
+
|
8
|
+
def_delegators "token_code", :s_value, :line, :column, :references
|
9
|
+
|
10
|
+
# $$, $n, @$, @n is translated to C code
|
11
|
+
def translated_code
|
12
|
+
case type
|
13
|
+
when :user_code
|
14
|
+
translated_user_code
|
15
|
+
when :initial_action
|
16
|
+
translated_initial_action_code
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# * ($1) error
|
21
|
+
# * ($$) *yyvaluep
|
22
|
+
# * (@1) error
|
23
|
+
# * (@$) *yylocationp
|
24
|
+
def translated_printer_code(tag)
|
25
|
+
t_code = s_value.dup
|
26
|
+
|
27
|
+
references.reverse.each do |ref|
|
28
|
+
first_column = ref.first_column
|
29
|
+
last_column = ref.last_column
|
30
|
+
|
31
|
+
case
|
32
|
+
when ref.value == "$" && ref.type == :dollar # $$
|
33
|
+
# Omit "<>"
|
34
|
+
member = tag.s_value[1..-2]
|
35
|
+
str = "((*yyvaluep).#{member})"
|
36
|
+
when ref.value == "$" && ref.type == :at # @$
|
37
|
+
str = "(*yylocationp)"
|
38
|
+
when ref.type == :dollar # $n
|
39
|
+
raise "$#{ref.value} can not be used in %printer."
|
40
|
+
when ref.type == :at # @n
|
41
|
+
raise "@#{ref.value} can not be used in %printer."
|
42
|
+
else
|
43
|
+
raise "Unexpected. #{self}, #{ref}"
|
44
|
+
end
|
45
|
+
|
46
|
+
t_code[first_column..last_column] = str
|
47
|
+
end
|
48
|
+
|
49
|
+
return t_code
|
50
|
+
end
|
51
|
+
alias :translated_error_token_code :translated_printer_code
|
52
|
+
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
# * ($1) yyvsp[i]
|
57
|
+
# * ($$) yyval
|
58
|
+
# * (@1) yylsp[i]
|
59
|
+
# * (@$) yyloc
|
60
|
+
def translated_user_code
|
61
|
+
t_code = s_value.dup
|
62
|
+
|
63
|
+
references.reverse.each do |ref|
|
64
|
+
first_column = ref.first_column
|
65
|
+
last_column = ref.last_column
|
66
|
+
|
67
|
+
case
|
68
|
+
when ref.value == "$" && ref.type == :dollar # $$
|
69
|
+
# Omit "<>"
|
70
|
+
member = ref.tag.s_value[1..-2]
|
71
|
+
str = "(yyval.#{member})"
|
72
|
+
when ref.value == "$" && ref.type == :at # @$
|
73
|
+
str = "(yyloc)"
|
74
|
+
when ref.type == :dollar # $n
|
75
|
+
i = -ref.position_in_rhs + ref.value
|
76
|
+
# Omit "<>"
|
77
|
+
member = ref.tag.s_value[1..-2]
|
78
|
+
str = "(yyvsp[#{i}].#{member})"
|
79
|
+
when ref.type == :at # @n
|
80
|
+
i = -ref.position_in_rhs + ref.value
|
81
|
+
str = "(yylsp[#{i}])"
|
82
|
+
else
|
83
|
+
raise "Unexpected. #{self}, #{ref}"
|
84
|
+
end
|
85
|
+
|
86
|
+
t_code[first_column..last_column] = str
|
87
|
+
end
|
88
|
+
|
89
|
+
return t_code
|
90
|
+
end
|
91
|
+
|
92
|
+
# * ($1) error
|
93
|
+
# * ($$) yylval
|
94
|
+
# * (@1) error
|
95
|
+
# * (@$) yylloc
|
96
|
+
def translated_initial_action_code
|
97
|
+
t_code = s_value.dup
|
98
|
+
|
99
|
+
references.reverse.each do |ref|
|
100
|
+
first_column = ref.first_column
|
101
|
+
last_column = ref.last_column
|
102
|
+
|
103
|
+
case
|
104
|
+
when ref.value == "$" && ref.type == :dollar # $$
|
105
|
+
str = "yylval"
|
106
|
+
when ref.value == "$" && ref.type == :at # @$
|
107
|
+
str = "yylloc"
|
108
|
+
when ref.type == :dollar # $n
|
109
|
+
raise "$#{ref.value} can not be used in initial_action."
|
110
|
+
when ref.type == :at # @n
|
111
|
+
raise "@#{ref.value} can not be used in initial_action."
|
112
|
+
else
|
113
|
+
raise "Unexpected. #{self}, #{ref}"
|
114
|
+
end
|
115
|
+
|
116
|
+
t_code[first_column..last_column] = str
|
117
|
+
end
|
118
|
+
|
119
|
+
return t_code
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# type: :dollar or :at
|
2
|
+
# ex_tag: "$<tag>1" (Optional)
|
3
|
+
|
4
|
+
module Lrama
|
5
|
+
class Grammar
|
6
|
+
class Reference < Struct.new(:type, :value, :ex_tag, :first_column, :last_column, :referring_symbol, :position_in_rhs, keyword_init: true)
|
7
|
+
def tag
|
8
|
+
if ex_tag
|
9
|
+
ex_tag
|
10
|
+
else
|
11
|
+
# FIXME: Remove this class check
|
12
|
+
if referring_symbol.is_a?(Symbol)
|
13
|
+
referring_symbol.tag
|
14
|
+
else
|
15
|
+
# Lrama::Lexer::Token (User_code) case
|
16
|
+
nil
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Lrama
|
2
|
+
class Grammar
|
3
|
+
class Rule < Struct.new(:id, :lhs, :rhs, :code, :nullable, :precedence_sym, :lineno, keyword_init: true)
|
4
|
+
# TODO: Change this to display_name
|
5
|
+
def to_s
|
6
|
+
l = lhs.id.s_value
|
7
|
+
r = rhs.empty? ? "ε" : rhs.map {|r| r.id.s_value }.join(", ")
|
8
|
+
|
9
|
+
"#{l} -> #{r}"
|
10
|
+
end
|
11
|
+
|
12
|
+
# Used by #user_actions
|
13
|
+
def as_comment
|
14
|
+
l = lhs.id.s_value
|
15
|
+
r = rhs.empty? ? "%empty" : rhs.map(&:display_name).join(" ")
|
16
|
+
|
17
|
+
"#{l}: #{r}"
|
18
|
+
end
|
19
|
+
|
20
|
+
# opt_nl: ε <-- empty_rule
|
21
|
+
# | '\n' <-- not empty_rule
|
22
|
+
def empty_rule?
|
23
|
+
rhs.empty?
|
24
|
+
end
|
25
|
+
|
26
|
+
def precedence
|
27
|
+
precedence_sym&.precedence
|
28
|
+
end
|
29
|
+
|
30
|
+
def initial_rule?
|
31
|
+
id == 0
|
32
|
+
end
|
33
|
+
|
34
|
+
def translated_code
|
35
|
+
code&.translated_code
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
# Symbol is both of nterm and term
|
2
|
+
# `number` is both for nterm and term
|
3
|
+
# `token_id` is tokentype for term, internal sequence number for nterm
|
4
|
+
#
|
5
|
+
# TODO: Add validation for ASCII code range for Token::Char
|
6
|
+
|
7
|
+
module Lrama
|
8
|
+
class Grammar
|
9
|
+
class Symbol < Struct.new(:id, :alias_name, :number, :tag, :term, :token_id, :nullable, :precedence, :printer, :error_token, keyword_init: true)
|
10
|
+
attr_accessor :first_set, :first_set_bitmap
|
11
|
+
attr_writer :eof_symbol, :error_symbol, :undef_symbol, :accept_symbol
|
12
|
+
|
13
|
+
def term?
|
14
|
+
term
|
15
|
+
end
|
16
|
+
|
17
|
+
def nterm?
|
18
|
+
!term
|
19
|
+
end
|
20
|
+
|
21
|
+
def eof_symbol?
|
22
|
+
!!@eof_symbol
|
23
|
+
end
|
24
|
+
|
25
|
+
def error_symbol?
|
26
|
+
!!@error_symbol
|
27
|
+
end
|
28
|
+
|
29
|
+
def undef_symbol?
|
30
|
+
!!@undef_symbol
|
31
|
+
end
|
32
|
+
|
33
|
+
def accept_symbol?
|
34
|
+
!!@accept_symbol
|
35
|
+
end
|
36
|
+
|
37
|
+
def display_name
|
38
|
+
alias_name || id.s_value
|
39
|
+
end
|
40
|
+
|
41
|
+
# name for yysymbol_kind_t
|
42
|
+
#
|
43
|
+
# See: b4_symbol_kind_base
|
44
|
+
def enum_name
|
45
|
+
case
|
46
|
+
when accept_symbol?
|
47
|
+
name = "YYACCEPT"
|
48
|
+
when eof_symbol?
|
49
|
+
name = "YYEOF"
|
50
|
+
when term? && id.type == Token::Char
|
51
|
+
name = number.to_s + display_name
|
52
|
+
when term? && id.type == Token::Ident
|
53
|
+
name = id.s_value
|
54
|
+
when nterm? && (id.s_value.include?("$") || id.s_value.include?("@"))
|
55
|
+
name = number.to_s + id.s_value
|
56
|
+
when nterm?
|
57
|
+
name = id.s_value
|
58
|
+
else
|
59
|
+
raise "Unexpected #{self}"
|
60
|
+
end
|
61
|
+
|
62
|
+
"YYSYMBOL_" + name.gsub(/\W+/, "_")
|
63
|
+
end
|
64
|
+
|
65
|
+
# comment for yysymbol_kind_t
|
66
|
+
def comment
|
67
|
+
case
|
68
|
+
when accept_symbol?
|
69
|
+
# YYSYMBOL_YYACCEPT
|
70
|
+
id.s_value
|
71
|
+
when eof_symbol?
|
72
|
+
# YYEOF
|
73
|
+
alias_name
|
74
|
+
when (term? && 0 < token_id && token_id < 128)
|
75
|
+
# YYSYMBOL_3_backslash_, YYSYMBOL_14_
|
76
|
+
alias_name || id.s_value
|
77
|
+
when id.s_value.include?("$") || id.s_value.include?("@")
|
78
|
+
# YYSYMBOL_21_1
|
79
|
+
id.s_value
|
80
|
+
else
|
81
|
+
# YYSYMBOL_keyword_class, YYSYMBOL_strings_1
|
82
|
+
alias_name || id.s_value
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|