lrama 0.5.3 → 0.5.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -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] && @relation[x].each do |y|
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,7 @@
1
+ module Lrama
2
+ class Grammar
3
+ # Grammar file information not used by States but by Output
4
+ class Auxiliary < Struct.new(:prologue_first_lineno, :prologue, :epilogue_first_lineno, :epilogue, keyword_init: true)
5
+ end
6
+ end
7
+ end
@@ -50,7 +50,6 @@ module Lrama
50
50
  end
51
51
  alias :translated_error_token_code :translated_printer_code
52
52
 
53
-
54
53
  private
55
54
 
56
55
  # * ($1) yyvsp[i]
@@ -17,6 +17,12 @@ module Lrama
17
17
  "#{l}: #{r}"
18
18
  end
19
19
 
20
+ # opt_nl: ε <-- empty_rule
21
+ # | '\n' <-- not empty_rule
22
+ def empty_rule?
23
+ rhs.empty?
24
+ end
25
+
20
26
  def precedence
21
27
  precedence_sym&.precedence
22
28
  end
@@ -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
- if alias_name
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
- if alias_name
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(/[^a-zA-Z_0-9]+/, "_")
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 = Aux.new
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
@@ -0,0 +1,8 @@
1
+ module Lrama
2
+ class Lexer
3
+ class Token < Struct.new(:type, :s_value, :alias, keyword_init: true)
4
+ class Type < Struct.new(:id, :name, keyword_init: true)
5
+ end
6
+ end
7
+ end
8
+ end
@@ -1,7 +1,8 @@
1
+ require 'lrama/lexer/token/type'
2
+
1
3
  module Lrama
2
4
  class Lexer
3
- class Token < Struct.new(:type, :s_value, :alias, keyword_init: true)
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)+/) # @1
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
@@ -252,7 +252,7 @@ module Lrama
252
252
  end
253
253
 
254
254
  def extract_param_name(param)
255
- /\A(.)+([a-zA-Z0-9_]+)\z/.match(param)[2]
255
+ param[/\b([a-zA-Z0-9_]+)(?=\s*\z)/]
256
256
  end
257
257
 
258
258
  def parse_param_name
@@ -11,7 +11,7 @@ module Lrama
11
11
  end
12
12
 
13
13
  def current_type
14
- current_token && current_token.type
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
- token = current_token
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 << current_token
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"
@@ -0,0 +1,9 @@
1
+ module Lrama
2
+ class State
3
+ class ReduceReduceConflict < Struct.new(:symbols, :reduce1, :reduce2, keyword_init: true)
4
+ def type
5
+ :reduce_reduce
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module Lrama
2
+ class State
3
+ class ShiftReduceConflict < Struct.new(:symbols, :shift, :reduce, keyword_init: true)
4
+ def type
5
+ :shift_reduce
6
+ end
7
+ end
8
+ end
9
+ end
data/lib/lrama/state.rb CHANGED
@@ -1,11 +1,11 @@
1
1
  require "lrama/state/reduce"
2
- require "lrama/state/shift"
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