lrama 0.5.3 → 0.5.5
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 +24 -1
- data/Gemfile +3 -2
- data/README.md +11 -1
- data/doc/TODO.md +5 -1
- data/exe/lrama +0 -1
- data/lib/lrama/command.rb +5 -10
- data/lib/lrama/context.rb +0 -2
- 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 +283 -0
- data/lib/lrama/digraph.rb +2 -3
- data/lib/lrama/grammar/auxiliary.rb +7 -0
- data/lib/lrama/grammar/code.rb +0 -1
- data/lib/lrama/grammar/rule.rb +6 -0
- data/lib/lrama/grammar/symbol.rb +4 -11
- data/lib/lrama/grammar.rb +44 -8
- data/lib/lrama/lexer/token/type.rb +8 -0
- data/lib/lrama/lexer/token.rb +4 -2
- data/lib/lrama/lexer.rb +3 -4
- data/lib/lrama/output.rb +1 -1
- data/lib/lrama/parser/token_scanner.rb +3 -6
- data/lib/lrama/parser.rb +9 -0
- data/lib/lrama/state/reduce_reduce_conflict.rb +9 -0
- data/lib/lrama/state/shift_reduce_conflict.rb +9 -0
- data/lib/lrama/state.rb +11 -4
- data/lib/lrama/states/item.rb +38 -2
- data/lib/lrama/states.rb +28 -34
- data/lib/lrama/states_reporter.rb +29 -16
- data/lib/lrama/type.rb +4 -0
- data/lib/lrama/version.rb +1 -1
- data/lib/lrama.rb +2 -0
- data/template/bison/yacc.c +103 -95
- metadata +13 -2
@@ -0,0 +1,283 @@
|
|
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
|
+
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
|
+
end
|
219
|
+
|
220
|
+
def shortest_path(conflict_state, conflict_reduce_item, conflict_term)
|
221
|
+
# queue: is an array of [Triple, [Path]]
|
222
|
+
queue = []
|
223
|
+
visited = {}
|
224
|
+
start_state = @states.states.first
|
225
|
+
raise "BUG: Start state should be just one kernel." if start_state.kernels.count != 1
|
226
|
+
|
227
|
+
start = Triple.new(start_state, start_state.kernels.first, Set.new([@states.eof_symbol]))
|
228
|
+
|
229
|
+
queue << [start, [StartPath.new(start.state_item)]]
|
230
|
+
|
231
|
+
while true
|
232
|
+
triple, paths = queue.shift
|
233
|
+
|
234
|
+
next if visited[triple]
|
235
|
+
visited[triple] = true
|
236
|
+
|
237
|
+
# Found
|
238
|
+
if triple.state == conflict_state && triple.item == conflict_reduce_item && triple.l.include?(conflict_term)
|
239
|
+
return paths
|
240
|
+
end
|
241
|
+
|
242
|
+
# transition
|
243
|
+
triple.state.transitions.each do |shift, next_state|
|
244
|
+
next unless triple.item.next_sym && triple.item.next_sym == shift.next_sym
|
245
|
+
next_state.kernels.each do |kernel|
|
246
|
+
next if kernel.rule != triple.item.rule
|
247
|
+
t = Triple.new(next_state, kernel, triple.l)
|
248
|
+
queue << [t, paths + [TransitionPath.new(triple.state_item, t.state_item)]]
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
# production step
|
253
|
+
triple.state.closure.each do |item|
|
254
|
+
next unless triple.item.next_sym && triple.item.next_sym == item.lhs
|
255
|
+
l = follow_l(triple.item, triple.l)
|
256
|
+
t = Triple.new(triple.state, item, l)
|
257
|
+
queue << [t, paths + [ProductionPath.new(triple.state_item, t.state_item)]]
|
258
|
+
end
|
259
|
+
|
260
|
+
break if queue.empty?
|
261
|
+
end
|
262
|
+
|
263
|
+
return nil
|
264
|
+
end
|
265
|
+
|
266
|
+
def follow_l(item, current_l)
|
267
|
+
# 1. follow_L (A -> X1 ... Xn-1 • Xn) = L
|
268
|
+
# 2. follow_L (A -> X1 ... Xk • Xk+1 Xk+2 ... Xn) = {Xk+2} if Xk+2 is a terminal
|
269
|
+
# 3. follow_L (A -> X1 ... Xk • Xk+1 Xk+2 ... Xn) = FIRST(Xk+2) if Xk+2 is a nonnullable nonterminal
|
270
|
+
# 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
|
271
|
+
case
|
272
|
+
when item.number_of_rest_symbols == 1
|
273
|
+
current_l
|
274
|
+
when item.next_next_sym.term?
|
275
|
+
Set.new([item.next_next_sym])
|
276
|
+
when !item.next_next_sym.nullable
|
277
|
+
item.next_next_sym.first_set
|
278
|
+
else
|
279
|
+
item.next_next_sym.first_set + follow_l(item.new_by_next_position, current_l)
|
280
|
+
end
|
281
|
+
end
|
282
|
+
end
|
283
|
+
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
|
data/lib/lrama/grammar/code.rb
CHANGED
data/lib/lrama/grammar/rule.rb
CHANGED
data/lib/lrama/grammar/symbol.rb
CHANGED
@@ -7,6 +7,7 @@
|
|
7
7
|
module Lrama
|
8
8
|
class Grammar
|
9
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
|
10
11
|
attr_writer :eof_symbol, :error_symbol, :undef_symbol, :accept_symbol
|
11
12
|
|
12
13
|
def term?
|
@@ -34,11 +35,7 @@ module Lrama
|
|
34
35
|
end
|
35
36
|
|
36
37
|
def display_name
|
37
|
-
|
38
|
-
alias_name
|
39
|
-
else
|
40
|
-
id.s_value
|
41
|
-
end
|
38
|
+
alias_name || id.s_value
|
42
39
|
end
|
43
40
|
|
44
41
|
# name for yysymbol_kind_t
|
@@ -51,11 +48,7 @@ module Lrama
|
|
51
48
|
when eof_symbol?
|
52
49
|
name = "YYEOF"
|
53
50
|
when term? && id.type == Token::Char
|
54
|
-
|
55
|
-
name = number.to_s + alias_name
|
56
|
-
else
|
57
|
-
name = number.to_s + id.s_value
|
58
|
-
end
|
51
|
+
name = number.to_s + display_name
|
59
52
|
when term? && id.type == Token::Ident
|
60
53
|
name = id.s_value
|
61
54
|
when nterm? && (id.s_value.include?("$") || id.s_value.include?("@"))
|
@@ -66,7 +59,7 @@ module Lrama
|
|
66
59
|
raise "Unexpected #{self}"
|
67
60
|
end
|
68
61
|
|
69
|
-
"YYSYMBOL_" + name.gsub(
|
62
|
+
"YYSYMBOL_" + name.gsub(/\W+/, "_")
|
70
63
|
end
|
71
64
|
|
72
65
|
# comment for yysymbol_kind_t
|
data/lib/lrama/grammar.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require "lrama/grammar/auxiliary"
|
1
2
|
require "lrama/grammar/code"
|
2
3
|
require "lrama/grammar/error_token"
|
3
4
|
require "lrama/grammar/precedence"
|
@@ -7,16 +8,13 @@ require "lrama/grammar/rule"
|
|
7
8
|
require "lrama/grammar/symbol"
|
8
9
|
require "lrama/grammar/union"
|
9
10
|
require "lrama/lexer"
|
11
|
+
require "lrama/type"
|
10
12
|
|
11
13
|
module Lrama
|
12
|
-
Type = Struct.new(:id, :tag, keyword_init: true)
|
13
14
|
Token = Lrama::Lexer::Token
|
14
15
|
|
15
16
|
# Grammar is the result of parsing an input grammar file
|
16
17
|
class Grammar
|
17
|
-
# Grammar file information not used by States but by Output
|
18
|
-
Aux = Struct.new(:prologue_first_lineno, :prologue, :epilogue_first_lineno, :epilogue, keyword_init: true)
|
19
|
-
|
20
18
|
attr_reader :eof_symbol, :error_symbol, :undef_symbol, :accept_symbol, :aux
|
21
19
|
attr_accessor :union, :expect,
|
22
20
|
:printers, :error_tokens,
|
@@ -38,7 +36,7 @@ module Lrama
|
|
38
36
|
@error_symbol = nil
|
39
37
|
@undef_symbol = nil
|
40
38
|
@accept_symbol = nil
|
41
|
-
@aux =
|
39
|
+
@aux = Auxiliary.new
|
42
40
|
|
43
41
|
append_special_symbols
|
44
42
|
end
|
@@ -48,7 +46,7 @@ module Lrama
|
|
48
46
|
end
|
49
47
|
|
50
48
|
def add_error_token(ident_or_tags:, code:, lineno:)
|
51
|
-
@error_tokens << ErrorToken.new(ident_or_tags, code, lineno)
|
49
|
+
@error_tokens << ErrorToken.new(ident_or_tags: ident_or_tags, code: code, lineno: lineno)
|
52
50
|
end
|
53
51
|
|
54
52
|
def add_term(id:, alias_name: nil, tag: nil, token_id: nil, replace: false)
|
@@ -105,6 +103,10 @@ module Lrama
|
|
105
103
|
set_precedence(sym, Precedence.new(type: :right, precedence: precedence))
|
106
104
|
end
|
107
105
|
|
106
|
+
def add_precedence(sym, precedence)
|
107
|
+
set_precedence(sym, Precedence.new(type: :precedence, precedence: precedence))
|
108
|
+
end
|
109
|
+
|
108
110
|
def set_precedence(sym, precedence)
|
109
111
|
raise "" if sym.nterm?
|
110
112
|
sym.precedence = precedence
|
@@ -215,6 +217,41 @@ module Lrama
|
|
215
217
|
end
|
216
218
|
end
|
217
219
|
|
220
|
+
def compute_first_set
|
221
|
+
terms.each do |term|
|
222
|
+
term.first_set = Set.new([term]).freeze
|
223
|
+
term.first_set_bitmap = Lrama::Bitmap.from_array([term.number])
|
224
|
+
end
|
225
|
+
|
226
|
+
nterms.each do |nterm|
|
227
|
+
nterm.first_set = Set.new([]).freeze
|
228
|
+
nterm.first_set_bitmap = Lrama::Bitmap.from_array([])
|
229
|
+
end
|
230
|
+
|
231
|
+
while true do
|
232
|
+
changed = false
|
233
|
+
|
234
|
+
@rules.each do |rule|
|
235
|
+
rule.rhs.each do |r|
|
236
|
+
if rule.lhs.first_set_bitmap | r.first_set_bitmap != rule.lhs.first_set_bitmap
|
237
|
+
changed = true
|
238
|
+
rule.lhs.first_set_bitmap = rule.lhs.first_set_bitmap | r.first_set_bitmap
|
239
|
+
end
|
240
|
+
|
241
|
+
break unless r.nullable
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
break unless changed
|
246
|
+
end
|
247
|
+
|
248
|
+
nterms.each do |nterm|
|
249
|
+
nterm.first_set = Lrama::Bitmap.to_array(nterm.first_set_bitmap).map do |number|
|
250
|
+
find_symbol_by_number!(number)
|
251
|
+
end.to_set
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
218
255
|
def find_symbol_by_s_value(s_value)
|
219
256
|
@symbols.find do |sym|
|
220
257
|
sym.id.s_value == s_value
|
@@ -277,7 +314,6 @@ module Lrama
|
|
277
314
|
end || (raise "Nterm not found: #{id}")
|
278
315
|
end
|
279
316
|
|
280
|
-
|
281
317
|
def append_special_symbols
|
282
318
|
# YYEMPTY (token_id: -2, number: -2) is added when a template is evaluated
|
283
319
|
# term = add_term(id: Token.new(Token::Ident, "YYEMPTY"), token_id: -2)
|
@@ -479,7 +515,7 @@ module Lrama
|
|
479
515
|
sym.token_id = 11
|
480
516
|
when "\""
|
481
517
|
sym.token_id = 34
|
482
|
-
when "
|
518
|
+
when "'"
|
483
519
|
sym.token_id = 39
|
484
520
|
when "\\\\"
|
485
521
|
sym.token_id = 92
|
data/lib/lrama/lexer/token.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
|
+
require 'lrama/lexer/token/type'
|
2
|
+
|
1
3
|
module Lrama
|
2
4
|
class Lexer
|
3
|
-
class Token
|
4
|
-
Type = Struct.new(:id, :name, keyword_init: true)
|
5
|
+
class Token
|
5
6
|
|
6
7
|
attr_accessor :line, :column, :referred
|
7
8
|
# For User_code
|
@@ -60,6 +61,7 @@ module Lrama
|
|
60
61
|
define_type(:P_nonassoc) # %nonassoc
|
61
62
|
define_type(:P_left) # %left
|
62
63
|
define_type(:P_right) # %right
|
64
|
+
define_type(:P_precedence) # %precedence
|
63
65
|
define_type(:P_prec) # %prec
|
64
66
|
define_type(:User_code) # { ... }
|
65
67
|
define_type(:Tag) # <int>
|
data/lib/lrama/lexer.rb
CHANGED
@@ -30,7 +30,6 @@ module Lrama
|
|
30
30
|
@grammar_rules = []
|
31
31
|
@epilogue = []
|
32
32
|
|
33
|
-
#
|
34
33
|
@bison_declarations_tokens = []
|
35
34
|
@grammar_rules_tokens = []
|
36
35
|
|
@@ -155,6 +154,8 @@ module Lrama
|
|
155
154
|
tokens << create_token(Token::P_left, ss[0], line, ss.pos - column)
|
156
155
|
when ss.scan(/%right/)
|
157
156
|
tokens << create_token(Token::P_right, ss[0], line, ss.pos - column)
|
157
|
+
when ss.scan(/%precedence/)
|
158
|
+
tokens << create_token(Token::P_precedence, ss[0], line, ss.pos - column)
|
158
159
|
when ss.scan(/%prec/)
|
159
160
|
tokens << create_token(Token::P_prec, ss[0], line, ss.pos - column)
|
160
161
|
when ss.scan(/{/)
|
@@ -223,7 +224,7 @@ module Lrama
|
|
223
224
|
references << [:dollar, ss[2], tag, str.length, str.length + ss[0].length - 1]
|
224
225
|
when ss.scan(/@\$/) # @$
|
225
226
|
references << [:at, "$", nil, str.length, str.length + ss[0].length - 1]
|
226
|
-
when ss.scan(/@(\d)
|
227
|
+
when ss.scan(/@(\d+)/) # @1
|
227
228
|
references << [:at, Integer(ss[1]), nil, str.length, str.length + ss[0].length - 1]
|
228
229
|
when ss.scan(/{/)
|
229
230
|
brace_count += 1
|
@@ -314,8 +315,6 @@ module Lrama
|
|
314
315
|
str << ss.getch
|
315
316
|
next
|
316
317
|
end
|
317
|
-
|
318
|
-
str << ss[0]
|
319
318
|
end
|
320
319
|
|
321
320
|
line # Reach to end of input
|
data/lib/lrama/output.rb
CHANGED
@@ -11,7 +11,7 @@ module Lrama
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def current_type
|
14
|
-
current_token
|
14
|
+
current_token&.type
|
15
15
|
end
|
16
16
|
|
17
17
|
def previous_token
|
@@ -26,9 +26,7 @@ module Lrama
|
|
26
26
|
|
27
27
|
def consume(*token_types)
|
28
28
|
if token_types.include?(current_type)
|
29
|
-
|
30
|
-
self.next
|
31
|
-
return token
|
29
|
+
return self.next
|
32
30
|
end
|
33
31
|
|
34
32
|
return nil
|
@@ -42,8 +40,7 @@ module Lrama
|
|
42
40
|
a = []
|
43
41
|
|
44
42
|
while token_types.include?(current_type)
|
45
|
-
a <<
|
46
|
-
self.next
|
43
|
+
a << self.next
|
47
44
|
end
|
48
45
|
|
49
46
|
raise "No token is consumed. #{token_types}" if a.empty?
|
data/lib/lrama/parser.rb
CHANGED
@@ -22,6 +22,7 @@ module Lrama
|
|
22
22
|
process_epilogue(grammar, lexer)
|
23
23
|
grammar.prepare
|
24
24
|
grammar.compute_nullable
|
25
|
+
grammar.compute_first_set
|
25
26
|
grammar.validate!
|
26
27
|
|
27
28
|
grammar
|
@@ -158,6 +159,14 @@ module Lrama
|
|
158
159
|
grammar.add_right(sym, precedence_number)
|
159
160
|
end
|
160
161
|
precedence_number += 1
|
162
|
+
when T::P_precedence
|
163
|
+
# %precedence (ident|char|string)+
|
164
|
+
ts.next
|
165
|
+
while (id = ts.consume(T::Ident, T::Char, T::String)) do
|
166
|
+
sym = grammar.add_term(id: id)
|
167
|
+
grammar.add_precedence(sym, precedence_number)
|
168
|
+
end
|
169
|
+
precedence_number += 1
|
161
170
|
when nil
|
162
171
|
# end of input
|
163
172
|
raise "Reach to end of input within declarations"
|
data/lib/lrama/state.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
require "lrama/state/reduce"
|
2
|
-
require "lrama/state/
|
2
|
+
require "lrama/state/reduce_reduce_conflict"
|
3
3
|
require "lrama/state/resolved_conflict"
|
4
|
+
require "lrama/state/shift"
|
5
|
+
require "lrama/state/shift_reduce_conflict"
|
4
6
|
|
5
7
|
module Lrama
|
6
8
|
class State
|
7
|
-
Conflict = Struct.new(:symbols, :reduce, :type, keyword_init: true)
|
8
|
-
|
9
9
|
attr_reader :id, :accessing_symbol, :kernels, :conflicts, :resolved_conflicts,
|
10
10
|
:default_reduction_rule, :closure, :items
|
11
11
|
attr_accessor :shifts, :reduces
|
@@ -62,7 +62,6 @@ module Lrama
|
|
62
62
|
@items_to_state[items] = next_state
|
63
63
|
end
|
64
64
|
|
65
|
-
#
|
66
65
|
def set_look_ahead(rule, look_ahead)
|
67
66
|
reduce = reduces.find do |r|
|
68
67
|
r.rule == rule
|
@@ -101,6 +100,10 @@ module Lrama
|
|
101
100
|
@term_transitions
|
102
101
|
end
|
103
102
|
|
103
|
+
def transitions
|
104
|
+
term_transitions + nterm_transitions
|
105
|
+
end
|
106
|
+
|
104
107
|
def selected_term_transitions
|
105
108
|
term_transitions.select do |shift, next_state|
|
106
109
|
!shift.not_selected
|
@@ -144,6 +147,10 @@ module Lrama
|
|
144
147
|
end
|
145
148
|
end
|
146
149
|
|
150
|
+
def has_conflicts?
|
151
|
+
!@conflicts.empty?
|
152
|
+
end
|
153
|
+
|
147
154
|
def sr_conflicts
|
148
155
|
@conflicts.select do |conflict|
|
149
156
|
conflict.type == :shift_reduce
|