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