lrama 0.6.10 → 0.6.11

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yaml +5 -1
  3. data/Gemfile +2 -2
  4. data/NEWS.md +65 -30
  5. data/Steepfile +3 -0
  6. data/doc/development/compressed_state_table/main.md +635 -0
  7. data/doc/development/compressed_state_table/parse.output +174 -0
  8. data/doc/development/compressed_state_table/parse.y +22 -0
  9. data/doc/development/compressed_state_table/parser.rb +282 -0
  10. data/lib/lrama/bitmap.rb +1 -1
  11. data/lib/lrama/context.rb +3 -3
  12. data/lib/lrama/counterexamples/derivation.rb +6 -5
  13. data/lib/lrama/counterexamples/example.rb +7 -4
  14. data/lib/lrama/counterexamples/path.rb +4 -0
  15. data/lib/lrama/counterexamples.rb +19 -9
  16. data/lib/lrama/grammar/parameterizing_rule/rhs.rb +1 -1
  17. data/lib/lrama/grammar/rule_builder.rb +1 -1
  18. data/lib/lrama/grammar/symbols/resolver.rb +4 -0
  19. data/lib/lrama/grammar.rb +2 -2
  20. data/lib/lrama/lexer/token/user_code.rb +1 -1
  21. data/lib/lrama/lexer.rb +1 -0
  22. data/lib/lrama/parser.rb +520 -487
  23. data/lib/lrama/state/reduce.rb +2 -3
  24. data/lib/lrama/version.rb +1 -1
  25. data/parser.y +38 -27
  26. data/rbs_collection.lock.yaml +10 -2
  27. data/sig/lrama/counterexamples/derivation.rbs +33 -0
  28. data/sig/lrama/counterexamples/example.rbs +45 -0
  29. data/sig/lrama/counterexamples/path.rbs +21 -0
  30. data/sig/lrama/counterexamples/production_path.rbs +11 -0
  31. data/sig/lrama/counterexamples/start_path.rbs +13 -0
  32. data/sig/lrama/counterexamples/state_item.rbs +10 -0
  33. data/sig/lrama/counterexamples/transition_path.rbs +11 -0
  34. data/sig/lrama/counterexamples/triple.rbs +20 -0
  35. data/sig/lrama/counterexamples.rbs +29 -0
  36. data/sig/lrama/grammar/symbol.rbs +1 -1
  37. data/sig/lrama/grammar/symbols/resolver.rbs +3 -3
  38. data/sig/lrama/grammar.rbs +13 -0
  39. data/sig/lrama/state/reduce_reduce_conflict.rbs +2 -2
  40. data/sig/lrama/state.rbs +79 -0
  41. data/sig/lrama/states.rbs +101 -0
  42. metadata +17 -2
@@ -0,0 +1,174 @@
1
+ Symbol
2
+
3
+ -2 EMPTY
4
+ 0 "end of file"
5
+ 1 error
6
+ 2 "invalid token" (undef)
7
+ 3 LF
8
+ 4 NUM
9
+ 5 '+'
10
+ 6 '*'
11
+ 7 '('
12
+ 8 ')'
13
+ 9 $accept # Start of nonterminal
14
+ 10 program
15
+ 11 expr
16
+
17
+
18
+ Grammar
19
+
20
+ 0 $accept: program "end of file"
21
+
22
+ 1 program: ε
23
+ 2 | expr LF
24
+
25
+ 3 expr: NUM
26
+ 4 | expr '+' expr
27
+ 5 | expr '*' expr
28
+ 6 | '(' expr ')'
29
+
30
+
31
+ State 0
32
+
33
+ 0 $accept: • program "end of file"
34
+ 1 program: ε • ["end of file"]
35
+ 2 | • expr LF
36
+ 3 expr: • NUM
37
+ 4 | • expr '+' expr
38
+ 5 | • expr '*' expr
39
+ 6 | • '(' expr ')'
40
+
41
+ NUM shift, and go to state 1
42
+ '(' shift, and go to state 2
43
+
44
+ $default reduce using rule 1 (program)
45
+
46
+ program go to state 3
47
+ expr go to state 4
48
+
49
+
50
+ State 1
51
+
52
+ 3 expr: NUM •
53
+
54
+ $default reduce using rule 3 (expr)
55
+
56
+
57
+ State 2
58
+
59
+ 3 expr: • NUM
60
+ 4 | • expr '+' expr
61
+ 5 | • expr '*' expr
62
+ 6 | • '(' expr ')'
63
+ 6 | '(' • expr ')'
64
+
65
+ NUM shift, and go to state 1
66
+ '(' shift, and go to state 2
67
+
68
+ expr go to state 5
69
+
70
+
71
+ State 3
72
+
73
+ 0 $accept: program • "end of file"
74
+
75
+ "end of file" shift, and go to state 6
76
+
77
+
78
+ State 4
79
+
80
+ 2 program: expr • LF
81
+ 4 expr: expr • '+' expr
82
+ 5 | expr • '*' expr
83
+
84
+ LF shift, and go to state 7
85
+ '+' shift, and go to state 8
86
+ '*' shift, and go to state 9
87
+
88
+
89
+ State 5
90
+
91
+ 4 expr: expr • '+' expr
92
+ 5 | expr • '*' expr
93
+ 6 | '(' expr • ')'
94
+
95
+ '+' shift, and go to state 8
96
+ '*' shift, and go to state 9
97
+ ')' shift, and go to state 10
98
+
99
+
100
+ State 6
101
+
102
+ 0 $accept: program "end of file" •
103
+
104
+ $default accept
105
+
106
+
107
+ State 7
108
+
109
+ 2 program: expr LF •
110
+
111
+ $default reduce using rule 2 (program)
112
+
113
+
114
+ State 8
115
+
116
+ 3 expr: • NUM
117
+ 4 | • expr '+' expr
118
+ 4 | expr '+' • expr
119
+ 5 | • expr '*' expr
120
+ 6 | • '(' expr ')'
121
+
122
+ NUM shift, and go to state 1
123
+ '(' shift, and go to state 2
124
+
125
+ expr go to state 11
126
+
127
+
128
+ State 9
129
+
130
+ 3 expr: • NUM
131
+ 4 | • expr '+' expr
132
+ 5 | • expr '*' expr
133
+ 5 | expr '*' • expr
134
+ 6 | • '(' expr ')'
135
+
136
+ NUM shift, and go to state 1
137
+ '(' shift, and go to state 2
138
+
139
+ expr go to state 12
140
+
141
+
142
+ State 10
143
+
144
+ 6 expr: '(' expr ')' •
145
+
146
+ $default reduce using rule 6 (expr)
147
+
148
+
149
+ State 11
150
+
151
+ 4 expr: expr • '+' expr
152
+ 4 | expr '+' expr • [LF, '+', ')']
153
+ 5 | expr • '*' expr
154
+
155
+ '*' shift, and go to state 9
156
+
157
+ $default reduce using rule 4 (expr)
158
+
159
+ Conflict between rule 4 and token '+' resolved as reduce (%left '+').
160
+ Conflict between rule 4 and token '*' resolved as shift ('+' < '*').
161
+
162
+
163
+ State 12
164
+
165
+ 4 expr: expr • '+' expr
166
+ 5 | expr • '*' expr
167
+ 5 | expr '*' expr • [LF, '+', '*', ')']
168
+
169
+ $default reduce using rule 5 (expr)
170
+
171
+ Conflict between rule 5 and token '+' resolved as reduce ('+' < '*').
172
+ Conflict between rule 5 and token '*' resolved as reduce (%left '*').
173
+
174
+
@@ -0,0 +1,22 @@
1
+ %union {
2
+ int val;
3
+ }
4
+ %token LF
5
+ %token <val> NUM
6
+ %type <val> expr
7
+ %left '+'
8
+ %left '*'
9
+
10
+ %%
11
+
12
+ program : /* empty */
13
+ | expr LF { printf("=> %d\n", $1); }
14
+ ;
15
+
16
+ expr : NUM
17
+ | expr '+' expr { $$ = $1 + $3; }
18
+ | expr '*' expr { $$ = $1 * $3; }
19
+ | '(' expr ')' { $$ = $2; }
20
+ ;
21
+
22
+ %%
@@ -0,0 +1,282 @@
1
+ class Parser
2
+ YYNTOKENS = 9
3
+ YYLAST = 13
4
+ YYTABLE_NINF = -1
5
+ YYTABLE = [ 5, 6, 7, 9, 8, 9, 11, 12, 8, 9, 1, 10, 0, 2]
6
+ YYCHECK = [ 2, 0, 3, 6, 5, 6, 8, 9, 5, 6, 4, 8, -1, 7]
7
+
8
+ YYPACT_NINF = -4
9
+ YYPACT = [ 6, -4, 6, 1, -1, 3, -4, -4, 6, 6, -4, -3, -4]
10
+ YYPGOTO = [ -4, -4, -2]
11
+
12
+ YYDEFACT = [ 2, 4, 0, 0, 0, 0, 1, 3, 0, 0, 7, 5, 6]
13
+ YYDEFGOTO = [ 0, 3, 4]
14
+
15
+ YYR1 = [ 0, 9, 10, 10, 11, 11, 11, 11]
16
+ YYR2 = [ 0, 2, 0, 2, 1, 3, 3, 3]
17
+
18
+ YYFINAL = 6
19
+
20
+ # Symbols
21
+ SYM_EMPTY = -2
22
+ SYM_EOF = 0 # "end of file"
23
+ SYM_ERROR = 1 # error
24
+ SYM_UNDEF = 2 # Invalid Token
25
+ SYM_LF = 3 # LF
26
+ SYM_NUM = 4 # NUM
27
+ SYM_PLUS = 5 # '+'
28
+ SYM_ASTER = 6 # '*'
29
+ SYM_LPAREN = 7 # '('
30
+ SYM_RPAREN = 8 # ')'
31
+ # Start of nonterminal
32
+ SYM_ACCEPT = 9 # $accept
33
+ SYM_PROGRAM = 10 # program
34
+ SYM_EXPR = 11 # expr
35
+
36
+ def initialize(debug = false)
37
+ @debug = debug
38
+ end
39
+
40
+ def parse(lexer)
41
+ state = 0
42
+ stack = []
43
+ yytoken = SYM_EMPTY
44
+ parser_action = :push_state
45
+ next_state = nil
46
+ rule = nil
47
+
48
+ while true
49
+ _parser_action = parser_action
50
+ parser_action = nil
51
+
52
+ case _parser_action
53
+ when :syntax_error
54
+ debug_print("Entering :syntax_error")
55
+
56
+ return 1
57
+ when :accept
58
+ debug_print("Entering :accept")
59
+
60
+ return 0
61
+ when :push_state
62
+ # Precondition: `state` is set to new state
63
+ debug_print("Entering :push_state")
64
+
65
+ debug_print("Push state #{state}")
66
+ stack.push(state)
67
+ debug_print("Current stack #{stack}")
68
+
69
+ if state == YYFINAL
70
+ parser_action = :accept
71
+ next
72
+ end
73
+
74
+ parser_action = :decide_parser_action
75
+ next
76
+ when :decide_parser_action
77
+ debug_print("Entering :decide_parser_action")
78
+
79
+ offset = yypact[state]
80
+ if offset == YYPACT_NINF
81
+ parser_action = :yydefault
82
+ next
83
+ end
84
+
85
+ # Ensure next token
86
+ if yytoken == SYM_EMPTY
87
+ debug_print("Reading a token")
88
+
89
+ yytoken = lexer.next_token
90
+ end
91
+
92
+ case yytoken
93
+ when SYM_EOF
94
+ debug_print("Now at end of input.")
95
+ when SYM_ERROR
96
+ parser_action = :syntax_error
97
+ next
98
+ else
99
+ debug_print("Next token is #{yytoken}")
100
+ end
101
+
102
+ idx = offset + yytoken
103
+ if idx < 0 || YYLAST < idx
104
+ debug_print("Decide next parser action as :yydefault")
105
+
106
+ parser_action = :yydefault
107
+ next
108
+ end
109
+ if yycheck[idx] != yytoken
110
+ debug_print("Decide next parser action as :yydefault")
111
+
112
+ parser_action = :yydefault
113
+ next
114
+ end
115
+
116
+ action = yytable[idx]
117
+ if action == YYTABLE_NINF
118
+ parser_action = :syntax_error
119
+ next
120
+ end
121
+ if action > 0
122
+ # Shift
123
+ debug_print("Decide next parser action as :yyshift")
124
+
125
+ next_state = action
126
+ parser_action = :yyshift
127
+ next
128
+ else
129
+ # Reduce
130
+ debug_print("Decide next parser action as :yyreduce")
131
+
132
+ rule = -action
133
+ parser_action = :yyreduce
134
+ next
135
+ end
136
+ when :yyshift
137
+ # Precondition: `next_state` is set
138
+ debug_print("Entering :yyshift")
139
+ raise "next_state is not set" unless next_state
140
+
141
+ yytoken = SYM_EMPTY
142
+ state = next_state
143
+ next_state = nil
144
+ parser_action = :push_state
145
+ next
146
+ when :yydefault
147
+ debug_print("Entering :yydefault")
148
+
149
+ rule = yydefact[state]
150
+ if rule == 0
151
+ parser_action = :syntax_error
152
+ next
153
+ end
154
+
155
+ parser_action = :yyreduce
156
+ next
157
+ when :yyreduce
158
+ # Precondition: `rule`, used for reduce, is set
159
+ debug_print("Entering :yyreduce")
160
+ raise "rule is not set" unless rule
161
+
162
+ rhs_length = yyr2[rule]
163
+ lhs_nterm = yyr1[rule]
164
+ lhs_nterm_id = lhs_nterm - YYNTOKENS
165
+
166
+ text = "Execute action for Rule (#{rule}) "
167
+ case rule
168
+ when 1
169
+ text << "$accept: program \"end of file\""
170
+ when 2
171
+ text << "program: ε"
172
+ when 3
173
+ text << "program: expr LF"
174
+ when 4
175
+ text << "expr: NUM"
176
+ when 5
177
+ text << "expr: expr '+' expr"
178
+ when 6
179
+ text << "expr: expr '*' expr"
180
+ when 7
181
+ text << "expr: '(' expr ')'"
182
+ end
183
+ debug_print(text)
184
+
185
+ debug_print("Pop #{rhs_length} elements")
186
+ debug_print("Stack before pop: #{stack}")
187
+ stack.pop(rhs_length)
188
+ debug_print("Stack after pop: #{stack}")
189
+ state = stack[-1]
190
+
191
+ # "Shift" LHS nonterminal
192
+ offset = yypgoto[lhs_nterm_id]
193
+ if offset == YYPACT_NINF
194
+ state = yydefgoto[lhs_nterm_id]
195
+ else
196
+ idx = offset + state
197
+ if idx < 0 || YYLAST < idx
198
+ state = yydefgoto[lhs_nterm_id]
199
+ elsif yycheck[idx] != state
200
+ state = yydefgoto[lhs_nterm_id]
201
+ else
202
+ state = yytable[idx]
203
+ end
204
+ end
205
+
206
+ rule = nil
207
+ parser_action = :push_state
208
+ next
209
+ else
210
+ raise "Unknown parser_action: #{parser_action}"
211
+ end
212
+ end
213
+ end
214
+
215
+ private
216
+
217
+ def debug_print(str)
218
+ if @debug
219
+ $stderr.puts str
220
+ end
221
+ end
222
+
223
+ def yytable
224
+ YYTABLE
225
+ end
226
+
227
+ def yycheck
228
+ YYCHECK
229
+ end
230
+
231
+ def yypact
232
+ YYPACT
233
+ end
234
+
235
+ def yypgoto
236
+ YYPGOTO
237
+ end
238
+
239
+ def yydefact
240
+ YYDEFACT
241
+ end
242
+
243
+ def yydefgoto
244
+ YYDEFGOTO
245
+ end
246
+
247
+ def yyr1
248
+ YYR1
249
+ end
250
+
251
+ def yyr2
252
+ YYR2
253
+ end
254
+ end
255
+
256
+ class Lexer
257
+ def initialize(tokens)
258
+ @tokens = tokens
259
+ @index = 0
260
+ end
261
+
262
+ def next_token
263
+ if @tokens.length > @index
264
+ token = @tokens[@index]
265
+ @index += 1
266
+ return token
267
+ else
268
+ return Parser::SYM_EOF
269
+ end
270
+ end
271
+ end
272
+
273
+ lexer = Lexer.new([
274
+ # 1 + 2 + 3 LF
275
+ Parser::SYM_NUM,
276
+ Parser::SYM_PLUS,
277
+ Parser::SYM_NUM,
278
+ Parser::SYM_PLUS,
279
+ Parser::SYM_NUM,
280
+ Parser::SYM_LF,
281
+ ])
282
+ Parser.new(debug: true).parse(lexer)
data/lib/lrama/bitmap.rb CHANGED
@@ -13,7 +13,7 @@ module Lrama
13
13
  end
14
14
 
15
15
  def self.to_array(int)
16
- a = []
16
+ a = [] #: Array[Integer]
17
17
  i = 0
18
18
 
19
19
  while int > 0 do
data/lib/lrama/context.rb CHANGED
@@ -405,7 +405,7 @@ module Lrama
405
405
  @check = []
406
406
  # Key is froms_and_tos, value is index position
407
407
  pushed = {}
408
- userd_res = {}
408
+ used_res = {}
409
409
  lowzero = 0
410
410
  high = 0
411
411
 
@@ -430,7 +430,7 @@ module Lrama
430
430
  end
431
431
  end
432
432
 
433
- if ok && userd_res[res]
433
+ if ok && used_res[res]
434
434
  ok = false
435
435
  end
436
436
 
@@ -458,7 +458,7 @@ module Lrama
458
458
 
459
459
  @base[state_id] = res
460
460
  pushed[froms_and_tos] = res
461
- userd_res[res] = true
461
+ used_res[res] = true
462
462
  end
463
463
 
464
464
  @yylast = high
@@ -18,7 +18,7 @@ module Lrama
18
18
  alias :inspect :to_s
19
19
 
20
20
  def render_strings_for_report
21
- result = []
21
+ result = [] #: Array[String]
22
22
  _render_for_report(self, 0, result, 0)
23
23
  result.map(&:rstrip)
24
24
  end
@@ -44,18 +44,19 @@ module Lrama
44
44
  str << "#{item.next_sym.display_name}"
45
45
  length = _render_for_report(derivation.left, len, strings, index + 1)
46
46
  # I want String#ljust!
47
- str << " " * (length - str.length)
47
+ str << " " * (length - str.length) if length > str.length
48
48
  else
49
49
  str << " • #{item.symbols_after_dot.map(&:display_name).join(" ")} "
50
50
  return str.length
51
51
  end
52
52
 
53
53
  if derivation.right&.left
54
- length = _render_for_report(derivation.right.left, str.length, strings, index + 1)
55
- str << "#{item.symbols_after_dot[1..-1].map(&:display_name).join(" ")} "
54
+ left = derivation.right&.left #: Derivation
55
+ length = _render_for_report(left, str.length, strings, index + 1)
56
+ str << "#{item.symbols_after_dot[1..-1].map(&:display_name).join(" ")} " # steep:ignore
56
57
  str << " " * (length - str.length) if length > str.length
57
58
  elsif item.next_next_sym
58
- str << "#{item.symbols_after_dot[1..-1].map(&:display_name).join(" ")} "
59
+ str << "#{item.symbols_after_dot[1..-1].map(&:display_name).join(" ")} " # steep:ignore
59
60
  end
60
61
 
61
62
  return str.length
@@ -38,9 +38,10 @@ module Lrama
38
38
  private
39
39
 
40
40
  def _derivations(paths)
41
- derivation = nil
41
+ derivation = nil #: Derivation
42
42
  current = :production
43
- lookahead_sym = paths.last.to.item.end_of_rule? ? @conflict_symbol : nil
43
+ last_path = paths.last #: Path
44
+ lookahead_sym = last_path.to.item.end_of_rule? ? @conflict_symbol : nil
44
45
 
45
46
  paths.reverse_each do |path|
46
47
  item = path.to.item
@@ -57,12 +58,14 @@ module Lrama
57
58
  when ProductionPath
58
59
  derivation = Derivation.new(item, derivation)
59
60
  current = :production
61
+ else
62
+ raise "Unexpected. #{path}"
60
63
  end
61
64
 
62
65
  if lookahead_sym && item.next_next_sym && item.next_next_sym.first_set.include?(lookahead_sym)
63
66
  state_item = @counterexamples.transitions[[path.to, item.next_sym]]
64
67
  derivation2 = find_derivation_for_symbol(state_item, lookahead_sym)
65
- derivation.right = derivation2
68
+ derivation.right = derivation2 # steep:ignore
66
69
  lookahead_sym = nil
67
70
  end
68
71
 
@@ -89,7 +92,7 @@ module Lrama
89
92
  end
90
93
 
91
94
  def find_derivation_for_symbol(state_item, sym)
92
- queue = []
95
+ queue = [] #: Array[Array[StateItem]]
93
96
  queue << [state_item]
94
97
 
95
98
  while (sis = queue.shift)
@@ -20,6 +20,10 @@ module Lrama
20
20
  "#<Path(#{type})>"
21
21
  end
22
22
  alias :inspect :to_s
23
+
24
+ def type
25
+ raise NotImplementedError
26
+ end
23
27
  end
24
28
  end
25
29
  end
@@ -32,8 +32,10 @@ module Lrama
32
32
  conflict_state.conflicts.flat_map do |conflict|
33
33
  case conflict.type
34
34
  when :shift_reduce
35
+ # @type var conflict: State::ShiftReduceConflict
35
36
  shift_reduce_example(conflict_state, conflict)
36
37
  when :reduce_reduce
38
+ # @type var conflict: State::ReduceReduceConflict
37
39
  reduce_reduce_examples(conflict_state, conflict)
38
40
  end
39
41
  end.compact
@@ -48,7 +50,7 @@ module Lrama
48
50
  @reverse_transitions = {}
49
51
 
50
52
  @states.states.each do |src_state|
51
- trans = {}
53
+ trans = {} #: Hash[Grammar::Symbol, State]
52
54
 
53
55
  src_state.transitions.each do |shift, next_state|
54
56
  trans[shift.next_sym] = next_state
@@ -66,6 +68,7 @@ module Lrama
66
68
 
67
69
  @transitions[[src_state_item, sym]] = dest_state_item
68
70
 
71
+ # @type var key: [StateItem, Grammar::Symbol]
69
72
  key = [dest_state_item, sym]
70
73
  @reverse_transitions[key] ||= Set.new
71
74
  @reverse_transitions[key] << src_state_item
@@ -82,7 +85,7 @@ module Lrama
82
85
 
83
86
  @states.states.each do |state|
84
87
  # LHS => Set(Item)
85
- h = {}
88
+ h = {} #: Hash[Grammar::Symbol, Set[States::Item]]
86
89
 
87
90
  state.closure.each do |item|
88
91
  sym = item.lhs
@@ -97,6 +100,7 @@ module Lrama
97
100
 
98
101
  sym = item.next_sym
99
102
  state_item = StateItem.new(state, item)
103
+ # @type var key: [State, Grammar::Symbol]
100
104
  key = [state, sym]
101
105
 
102
106
  @productions[state_item] = h[sym]
@@ -109,6 +113,7 @@ module Lrama
109
113
 
110
114
  def shift_reduce_example(conflict_state, conflict)
111
115
  conflict_symbol = conflict.symbols.first
116
+ # @type var shift_conflict_item: ::Lrama::States::Item
112
117
  shift_conflict_item = conflict_state.items.find { |item| item.next_sym == conflict_symbol }
113
118
  path2 = shortest_path(conflict_state, conflict.reduce.item, conflict_symbol)
114
119
  path1 = find_shift_conflict_shortest_path(path2, conflict_state, shift_conflict_item)
@@ -153,12 +158,14 @@ module Lrama
153
158
  prev_state_item = prev_path&.to
154
159
 
155
160
  if target_state_item == state_item || target_state_item.item.start_item?
156
- result.concat(reversed_reduce_path[_j..-1].map(&:to))
161
+ result.concat(
162
+ reversed_reduce_path[_j..-1] #: Array[StartPath|TransitionPath|ProductionPath]
163
+ .map(&:to))
157
164
  break
158
165
  end
159
166
 
160
167
  if target_state_item.item.beginning_of_rule?
161
- queue = []
168
+ queue = [] #: Array[Array[StateItem]]
162
169
  queue << [target_state_item]
163
170
 
164
171
  # Find reverse production
@@ -174,15 +181,17 @@ module Lrama
174
181
  end
175
182
 
176
183
  if si.item.beginning_of_rule?
184
+ # @type var key: [State, Grammar::Symbol]
177
185
  key = [si.state, si.item.lhs]
178
186
  @reverse_productions[key].each do |item|
179
187
  state_item = StateItem.new(si.state, item)
180
188
  queue << (sis + [state_item])
181
189
  end
182
190
  else
191
+ # @type var key: [StateItem, Grammar::Symbol]
183
192
  key = [si, si.item.previous_sym]
184
193
  @reverse_transitions[key].each do |prev_target_state_item|
185
- next if prev_target_state_item.state != prev_state_item.state
194
+ next if prev_target_state_item.state != prev_state_item&.state
186
195
  sis.shift
187
196
  result.concat(sis)
188
197
  result << prev_target_state_item
@@ -195,9 +204,10 @@ module Lrama
195
204
  end
196
205
  else
197
206
  # Find reverse transition
207
+ # @type var key: [StateItem, Grammar::Symbol]
198
208
  key = [target_state_item, target_state_item.item.previous_sym]
199
209
  @reverse_transitions[key].each do |prev_target_state_item|
200
- next if prev_target_state_item.state != prev_state_item.state
210
+ next if prev_target_state_item.state != prev_state_item&.state
201
211
  result << prev_target_state_item
202
212
  target_state_item = prev_target_state_item
203
213
  i = j
@@ -224,9 +234,9 @@ module Lrama
224
234
 
225
235
  def shortest_path(conflict_state, conflict_reduce_item, conflict_term)
226
236
  # queue: is an array of [Triple, [Path]]
227
- queue = []
228
- visited = {}
229
- start_state = @states.states.first
237
+ queue = [] #: Array[[Triple, Array[StartPath|TransitionPath|ProductionPath]]]
238
+ visited = {} #: Hash[Triple, true]
239
+ start_state = @states.states.first #: Lrama::State
230
240
  raise "BUG: Start state should be just one kernel." if start_state.kernels.count != 1
231
241
 
232
242
  start = Triple.new(start_state, start_state.kernels.first, Set.new([@states.eof_symbol]))