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.
@@ -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