lrama 0.6.10 → 0.6.11
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 +5 -1
- data/Gemfile +2 -2
- data/NEWS.md +65 -30
- data/Steepfile +3 -0
- data/doc/development/compressed_state_table/main.md +635 -0
- data/doc/development/compressed_state_table/parse.output +174 -0
- data/doc/development/compressed_state_table/parse.y +22 -0
- data/doc/development/compressed_state_table/parser.rb +282 -0
- data/lib/lrama/bitmap.rb +1 -1
- data/lib/lrama/context.rb +3 -3
- data/lib/lrama/counterexamples/derivation.rb +6 -5
- data/lib/lrama/counterexamples/example.rb +7 -4
- data/lib/lrama/counterexamples/path.rb +4 -0
- data/lib/lrama/counterexamples.rb +19 -9
- data/lib/lrama/grammar/parameterizing_rule/rhs.rb +1 -1
- data/lib/lrama/grammar/rule_builder.rb +1 -1
- data/lib/lrama/grammar/symbols/resolver.rb +4 -0
- data/lib/lrama/grammar.rb +2 -2
- data/lib/lrama/lexer/token/user_code.rb +1 -1
- data/lib/lrama/lexer.rb +1 -0
- data/lib/lrama/parser.rb +520 -487
- data/lib/lrama/state/reduce.rb +2 -3
- data/lib/lrama/version.rb +1 -1
- data/parser.y +38 -27
- data/rbs_collection.lock.yaml +10 -2
- data/sig/lrama/counterexamples/derivation.rbs +33 -0
- data/sig/lrama/counterexamples/example.rbs +45 -0
- data/sig/lrama/counterexamples/path.rbs +21 -0
- data/sig/lrama/counterexamples/production_path.rbs +11 -0
- data/sig/lrama/counterexamples/start_path.rbs +13 -0
- data/sig/lrama/counterexamples/state_item.rbs +10 -0
- data/sig/lrama/counterexamples/transition_path.rbs +11 -0
- data/sig/lrama/counterexamples/triple.rbs +20 -0
- data/sig/lrama/counterexamples.rbs +29 -0
- data/sig/lrama/grammar/symbol.rbs +1 -1
- data/sig/lrama/grammar/symbols/resolver.rbs +3 -3
- data/sig/lrama/grammar.rbs +13 -0
- data/sig/lrama/state/reduce_reduce_conflict.rbs +2 -2
- data/sig/lrama/state.rbs +79 -0
- data/sig/lrama/states.rbs +101 -0
- 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
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
|
-
|
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 &&
|
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
|
-
|
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
|
-
|
55
|
-
|
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
|
-
|
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)
|
@@ -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(
|
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
|
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
|
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]))
|