line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# This is really a -*- cperl -*- extension package to Parse::YALALR::Build |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
package Parse::YALALR::Build; |
4
|
1
|
|
|
1
|
|
5
|
use Parse::YALALR::Common; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
10
|
|
5
|
1
|
|
|
1
|
|
6
|
use strict; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
4689
|
|
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
######################## EXPLANATIONS ########################### |
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
sub effects_to_causes { |
10
|
0
|
|
|
0
|
0
|
|
my Parse::YALALR::Build $self = shift; |
11
|
0
|
|
|
|
|
|
my Parse::YALALR::Parser $parser = $self->parser; |
12
|
|
|
|
|
|
|
|
13
|
0
|
|
|
|
|
|
for my $cause (map { @{$_->{items}} } @{$parser->{states}}) { |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
14
|
0
|
|
|
|
|
|
for my $effect (values %{$cause->{EFFECTS}}) { |
|
0
|
|
|
|
|
|
|
15
|
0
|
|
|
|
|
|
push(@{$effect->{CAUSES}}, $cause); |
|
0
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
} |
17
|
|
|
|
|
|
|
} |
18
|
|
|
|
|
|
|
} |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
# Explain why $symbol ->* B, where $item is B -> \alpha (with a dot somewhere) |
21
|
|
|
|
|
|
|
sub explain_sym_chain { |
22
|
0
|
|
|
0
|
0
|
|
my Parse::YALALR::Build $self = shift; |
23
|
0
|
|
|
|
|
|
my ($symbol, $item, $asXML) = @_; |
24
|
0
|
|
|
|
|
|
my Parse::YALALR::Parser $parser = $self->parser; |
25
|
0
|
|
|
|
|
|
my $nil = $parser->{nil}; |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
# print "Called explain_sym_chain(".$parser->dump_sym($symbol).", ".$parser->dump_item($item)."\n"; |
28
|
|
|
|
|
|
|
|
29
|
0
|
|
|
|
|
|
my @chain; |
30
|
|
|
|
|
|
|
|
31
|
0
|
0
|
|
|
|
|
return undef if ($parser->is_token($symbol)); |
32
|
0
|
|
|
|
|
|
my $target; |
33
|
0
|
|
|
|
|
|
while (1) { |
34
|
0
|
|
|
|
|
|
--$item while ($parser->{grammar}[$item] != $nil); |
35
|
0
|
|
|
|
|
|
$item++; |
36
|
0
|
|
|
|
|
|
my $target = $parser->{grammar}[$item]; |
37
|
0
|
|
|
|
|
|
push(@chain, $parser->dump_item($item+1, $asXML)); |
38
|
0
|
0
|
|
|
|
|
last if ($symbol == $target); |
39
|
0
|
|
|
|
|
|
$item = $self->{chainreachable}{$symbol}{$target}; |
40
|
0
|
0
|
|
|
|
|
return undef if !defined $item; |
41
|
|
|
|
|
|
|
} |
42
|
|
|
|
|
|
|
|
43
|
0
|
|
|
|
|
|
my $desc = ''; |
44
|
0
|
|
|
|
|
|
foreach (reverse @chain) { |
45
|
0
|
|
|
|
|
|
$desc .= "generates $_\n"; |
46
|
|
|
|
|
|
|
} |
47
|
0
|
|
|
|
|
|
chomp($desc); |
48
|
|
|
|
|
|
|
|
49
|
0
|
|
|
|
|
|
return $desc; |
50
|
|
|
|
|
|
|
} |
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
# Explain how STATE came to have an item X -> . SYMBOL \alpha |
53
|
|
|
|
|
|
|
sub explain_chain { |
54
|
0
|
|
|
0
|
0
|
|
my Parse::YALALR::Build $self = shift; |
55
|
0
|
|
|
|
|
|
my ($state, $symbol, $asXML) = @_; |
56
|
0
|
|
|
|
|
|
my Parse::YALALR::Parser $parser = $self->parser; |
57
|
0
|
|
|
|
|
|
my $desc; |
58
|
0
|
|
|
|
|
|
foreach my $kitem (@{$state->{items}}) { |
|
0
|
|
|
|
|
|
|
59
|
0
|
|
|
|
|
|
$desc = $self->explain_sym_chain($parser->{grammar}[$kitem->{GRAMIDX}], |
60
|
|
|
|
|
|
|
$symbol, $asXML); |
61
|
0
|
0
|
|
|
|
|
return $desc if defined $desc; |
62
|
|
|
|
|
|
|
} |
63
|
0
|
|
|
|
|
|
return undef; |
64
|
|
|
|
|
|
|
} |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
sub explain_FIRST { |
67
|
0
|
|
|
0
|
0
|
|
my Parse::YALALR::Build $self = shift; |
68
|
0
|
|
|
|
|
|
my ($token, $symbol, $asXML) = @_; |
69
|
0
|
|
|
|
|
|
my Parse::YALALR::Parser $parser = $self->parser; |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
# WHY_FIRST : { A => { t => } } |
72
|
0
|
|
|
|
|
|
my ($rule, $reason, $parent) = @{$self->{WHY_FIRST}->{$symbol}->{$token}}; |
|
0
|
|
|
|
|
|
|
73
|
0
|
0
|
|
|
|
|
die unless defined $rule; |
74
|
|
|
|
|
|
|
|
75
|
0
|
|
|
|
|
|
my $str; |
76
|
0
|
0
|
|
|
|
|
$str .= "" if $asXML; |
77
|
0
|
|
|
|
|
|
$str .= "rule ".$parser->dump_rule($rule, undef, $asXML); |
78
|
0
|
0
|
|
|
|
|
$str .= "" if $asXML; |
79
|
0
|
|
|
|
|
|
$str .= "\n"; |
80
|
|
|
|
|
|
|
|
81
|
0
|
|
|
|
|
|
my $idx = $rule+1; |
82
|
0
|
|
|
|
|
|
while ((my $A = $parser->{grammar}->[$idx++]) != $parser->{nil}) { |
83
|
0
|
|
|
|
|
|
print "A=".$parser->dump_sym($A)." reason=$reason"; |
84
|
0
|
0
|
|
|
|
|
if ($reason eq 'propagated') { |
85
|
0
|
|
|
|
|
|
print " from ".$parser->dump_sym($parent); |
86
|
|
|
|
|
|
|
} |
87
|
0
|
|
|
|
|
|
print "\n"; |
88
|
0
|
0
|
0
|
|
|
|
if ($A == $token) { |
|
|
0
|
|
|
|
|
|
89
|
0
|
|
|
|
|
|
chomp($str); |
90
|
0
|
|
|
|
|
|
return (undef, $str); |
91
|
|
|
|
|
|
|
} elsif ($reason eq 'propagated' && $A == $parent) { |
92
|
0
|
|
|
|
|
|
$str .= "and "; |
93
|
0
|
|
|
|
|
|
my (undef, $substr) = |
94
|
|
|
|
|
|
|
$self->explain_FIRST($token, $parent, $asXML); |
95
|
0
|
|
|
|
|
|
chomp($substr); |
96
|
0
|
|
|
|
|
|
return (undef, $str.$substr); |
97
|
|
|
|
|
|
|
} else { |
98
|
0
|
|
|
|
|
|
$str .= "and "; |
99
|
0
|
0
|
|
|
|
|
$str .= "" if $asXML; |
100
|
0
|
|
|
|
|
|
$str .= $parser->dump_sym($A, $asXML)." is nullable"; |
101
|
0
|
0
|
|
|
|
|
$str .= "" if $asXML; |
102
|
0
|
|
|
|
|
|
$str .= "\n"; |
103
|
|
|
|
|
|
|
} |
104
|
|
|
|
|
|
|
} |
105
|
|
|
|
|
|
|
|
106
|
0
|
|
|
|
|
|
die "Can't get here! tok=$ID{$parser->dump_sym($token)} symbol=$ID{$parser->dump_sym($symbol)} str=$str"; |
107
|
|
|
|
|
|
|
} |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
sub explain_nullable { |
110
|
0
|
|
|
0
|
0
|
|
my Parse::YALALR::Build $self = shift; |
111
|
0
|
|
|
|
|
|
my ($symbol, $asXML, $visited) = @_; |
112
|
0
|
|
|
|
|
|
my Parse::YALALR::Parser $parser = $self->parser; |
113
|
0
|
|
|
|
|
|
my $grammar = $parser->{grammar}; |
114
|
|
|
|
|
|
|
|
115
|
0
|
|
0
|
|
|
|
$visited ||= {}; |
116
|
0
|
|
|
|
|
|
$visited->{$symbol} = 1; |
117
|
|
|
|
|
|
|
|
118
|
0
|
|
|
|
|
|
my $str; |
119
|
0
|
|
|
|
|
|
my $rule = $self->{why_nullable}->{$symbol}; |
120
|
0
|
|
|
|
|
|
$str .= $parser->dump_rule($rule, undef, $asXML); |
121
|
|
|
|
|
|
|
|
122
|
0
|
|
|
|
|
|
my $idx = $rule; |
123
|
0
|
|
|
|
|
|
while ((my $A = $grammar->[++$idx]) != $parser->{nil}) { |
124
|
0
|
0
|
|
|
|
|
next if $visited->{$A}; |
125
|
0
|
|
|
|
|
|
my (undef, $substr) = $self->explain_nullable($A, $asXML); |
126
|
0
|
|
|
|
|
|
$str .= "\n$substr"; |
127
|
|
|
|
|
|
|
} |
128
|
|
|
|
|
|
|
|
129
|
0
|
|
|
|
|
|
return (undef, $str); |
130
|
|
|
|
|
|
|
} |
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
# When in state $state, why shift on token/nonterminal $symbol? |
133
|
|
|
|
|
|
|
sub explain_shift { |
134
|
0
|
|
|
0
|
0
|
|
my Parse::YALALR::Build $self = shift; |
135
|
0
|
|
|
|
|
|
my ($state, $symbol, $action, $actions, $asXML) = @_; |
136
|
0
|
|
|
|
|
|
my Parse::YALALR::Parser $parser = $self->parser; |
137
|
|
|
|
|
|
|
|
138
|
0
|
0
|
|
|
|
|
if (!ref $actions->[$symbol]) { |
139
|
|
|
|
|
|
|
# Usual explanation of a shift: state n has A -> \a1 . t \a2 |
140
|
|
|
|
|
|
|
# in it. This might actually be because the kernel has |
141
|
|
|
|
|
|
|
# X -> \a3 . A \a4, though. |
142
|
|
|
|
|
|
|
|
143
|
0
|
|
|
|
|
|
my ($item, $reason, $chainfrom) = @{$state->{SHIFT_WHY}->{$symbol}}; |
|
0
|
|
|
|
|
|
|
144
|
0
|
0
|
|
|
|
|
my $where = ($reason eq 'kernel' ? 'kernel ' : 'chained '); |
145
|
0
|
|
|
|
|
|
my $desc; |
146
|
0
|
0
|
|
|
|
|
if ($reason eq 'chained') { |
147
|
|
|
|
|
|
|
# No need to dump out the generation list if ...? |
148
|
0
|
0
|
|
|
|
|
if ($item->{GRAMIDX} != $chainfrom + 1) { |
149
|
0
|
|
|
|
|
|
$desc .= "\n".$self->explain_chain($state, $chainfrom, $asXML); |
150
|
|
|
|
|
|
|
} |
151
|
0
|
|
|
|
|
|
$desc .= "\ngenerates "; |
152
|
0
|
|
|
|
|
|
$desc .= $parser->dump_item($item->{GRAMIDX}-1, $asXML); |
153
|
|
|
|
|
|
|
} else { |
154
|
0
|
0
|
|
|
|
|
if (@{ $parser->{states}->[$state] } > 1) { |
|
0
|
|
|
|
|
|
|
155
|
0
|
|
|
|
|
|
$desc .= "in particular, item "; |
156
|
0
|
|
|
|
|
|
$desc .= $parser->dump_item($item->{GRAMIDX}-1, $asXML); |
157
|
|
|
|
|
|
|
} |
158
|
|
|
|
|
|
|
} |
159
|
0
|
|
|
|
|
|
return ($state->{SHIFT_WHY}->{$symbol}, $desc); |
160
|
|
|
|
|
|
|
} else { |
161
|
|
|
|
|
|
|
# Hm. Some strange reason. |
162
|
0
|
|
|
|
|
|
return (undef, 'dunno(shift)(internal error)'); |
163
|
|
|
|
|
|
|
} |
164
|
|
|
|
|
|
|
} |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
sub create_item2state_map { |
167
|
0
|
|
|
0
|
0
|
|
my Parse::YALALR::Build $self = shift; |
168
|
0
|
|
|
|
|
|
my Parse::YALALR::Parser $parser = $self->parser; |
169
|
0
|
|
|
|
|
|
for my $state (@{$parser->{states}}) { |
|
0
|
|
|
|
|
|
|
170
|
0
|
|
|
|
|
|
for my $item (@{$state->{items}}) { |
|
0
|
|
|
|
|
|
|
171
|
0
|
|
|
|
|
|
$self->{item2state}->{$item} = $state; |
172
|
0
|
|
|
|
|
|
$self->{itemmap}->{"$state->{id}_$item->{GRAMIDX}"} = $item; |
173
|
|
|
|
|
|
|
} |
174
|
|
|
|
|
|
|
} |
175
|
|
|
|
|
|
|
} |
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
sub lookup_lookahead_why ($$) { |
178
|
0
|
|
|
0
|
0
|
|
my ($why, $token) = @_; |
179
|
0
|
|
|
|
|
|
while (my ($vec, $reason) = each %$why) { |
180
|
0
|
0
|
|
|
|
|
return $reason if vec($vec, $token, 1); |
181
|
|
|
|
|
|
|
} |
182
|
0
|
|
|
|
|
|
warn("Failed to figure out why token is in lookahead of item"); |
183
|
0
|
|
|
|
|
|
return undef; |
184
|
|
|
|
|
|
|
} |
185
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
# Figure out which (possibly generated) item propagated lookahead TOKEN |
187
|
|
|
|
|
|
|
# to the item EFFECT. Favor items which were themselves generated rather |
188
|
|
|
|
|
|
|
# than propagated (to avoid propagation cycles). |
189
|
|
|
|
|
|
|
# |
190
|
|
|
|
|
|
|
# -> |
191
|
|
|
|
|
|
|
# |
192
|
|
|
|
|
|
|
sub search_for_cause { |
193
|
0
|
|
|
0
|
0
|
|
my Parse::YALALR::Build $self = shift; |
194
|
0
|
|
|
|
|
|
my ($effect, $token) = @_; |
195
|
0
|
|
|
|
|
|
my Parse::YALALR::Parser $parser = $self->parser; |
196
|
0
|
|
|
|
|
|
my $effect_idx = $effect->{GRAMIDX}; |
197
|
|
|
|
|
|
|
|
198
|
0
|
|
|
|
|
|
my $cand_state; |
199
|
|
|
|
|
|
|
my $cand_item; |
200
|
0
|
|
|
|
|
|
for my $cause (@{ $effect->{SOURCES} }) { |
|
0
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
# $DB::single = 1 if $effect->{GRAMIDX} == 245; |
202
|
0
|
|
|
|
|
|
my $state = $self->{item2state}->{$cause}; |
203
|
0
|
|
|
|
|
|
my $xstate = $self->expand_state($state); |
204
|
0
|
|
|
|
|
|
my $cause_xitem = $xstate->{$effect_idx - 1}; |
205
|
0
|
0
|
|
|
|
|
next if !exists $cause_xitem->{$token}; |
206
|
0
|
|
|
|
|
|
$cand_item = $cause_xitem->{item}; |
207
|
0
|
0
|
|
|
|
|
return ($state, $cand_item) |
208
|
|
|
|
|
|
|
if $cause_xitem->{$token}->[0] eq 'generated'; |
209
|
0
|
0
|
|
|
|
|
return ($state, $cand_item) |
210
|
|
|
|
|
|
|
if $cause_xitem->{$token}->[0] eq 'kernel'; |
211
|
0
|
|
|
|
|
|
$cand_state = $state; |
212
|
|
|
|
|
|
|
} |
213
|
|
|
|
|
|
|
|
214
|
0
|
0
|
|
|
|
|
$DB::single = 1 if !defined $cand_state; |
215
|
0
|
0
|
|
|
|
|
die "No cause found!" if !defined $cand_state; |
216
|
0
|
|
|
|
|
|
return ($cand_state, $cand_item); |
217
|
|
|
|
|
|
|
} |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
sub find_ultimate { |
220
|
0
|
|
|
0
|
0
|
|
my Parse::YALALR::Build $self = shift; |
221
|
0
|
|
|
|
|
|
my ($state, $symbol) = @_; |
222
|
0
|
|
|
|
|
|
my Parse::YALALR::Parser $parser = $self->parser; |
223
|
0
|
|
|
|
|
|
my $reasons; |
224
|
0
|
|
|
|
|
|
while (my ($la, $r) = each %{$state->{REDUCE_WHY}}) { |
|
0
|
|
|
|
|
|
|
225
|
0
|
0
|
|
|
|
|
$reasons = $r, last if vec($la, $symbol, 1); |
226
|
|
|
|
|
|
|
} |
227
|
0
|
|
|
|
|
|
return $reasons->[1]; |
228
|
|
|
|
|
|
|
} |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
sub explain_reduce { |
231
|
0
|
|
|
0
|
0
|
|
my Parse::YALALR::Build $self = shift; |
232
|
0
|
|
|
|
|
|
my ($state, $symbol, $action, $actions, $asXML) = @_; |
233
|
0
|
|
|
|
|
|
my Parse::YALALR::Parser $parser = $self->parser; |
234
|
0
|
|
|
|
|
|
my $str = ''; |
235
|
|
|
|
|
|
|
|
236
|
0
|
|
|
|
|
|
my $reasons; |
237
|
0
|
|
|
|
|
|
while (my ($la, $r) = each %{$state->{REDUCE_WHY}}) { |
|
0
|
|
|
|
|
|
|
238
|
0
|
0
|
|
|
|
|
$reasons = $r, last if vec($la, $symbol, 1); |
239
|
|
|
|
|
|
|
} |
240
|
|
|
|
|
|
|
|
241
|
0
|
|
|
|
|
|
my $index = $reasons->[0]; |
242
|
0
|
|
|
|
|
|
++$index while $parser->{grammar}->[$index] != $parser->{nil}; |
243
|
|
|
|
|
|
|
|
244
|
0
|
|
|
|
|
|
my ($xml, $reason); |
245
|
0
|
|
|
|
|
|
$xml .= "has item ".$parser->dump_item($index, $asXML); |
246
|
0
|
|
|
|
|
|
$xml .= "\nwith lookahead " |
247
|
|
|
|
|
|
|
."{id} item=$index token=$symbol ultimate=$reasons->[1]->{GRAMIDX}>" |
248
|
|
|
|
|
|
|
.$parser->dump_sym($symbol, $asXML) |
249
|
|
|
|
|
|
|
.""; |
250
|
0
|
|
|
|
|
|
$reason = bless [ $state, $index, $symbol, $reasons->[1] ], 'reduce_reason'; |
251
|
0
|
|
|
|
|
|
print "WOULD HAVE CALLED exp_la($state=$state->{id}, $index, ".$parser->dump_sym($symbol).", $reasons->[1]=$reasons->[1]->{GRAMIDX}, $asXML\n"; |
252
|
0
|
|
|
|
|
|
return ($reason, $xml); |
253
|
|
|
|
|
|
|
} |
254
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
# Why was conflict resolved to $action? |
256
|
0
|
|
|
0
|
0
|
|
sub explain_conflict { |
257
|
|
|
|
|
|
|
} |
258
|
|
|
|
|
|
|
|
259
|
|
|
|
|
|
|
sub explain { |
260
|
0
|
|
|
0
|
0
|
|
my Parse::YALALR::Build $self = shift; |
261
|
0
|
|
|
|
|
|
my ($state0, $cause, $action) = @_; |
262
|
0
|
|
|
|
|
|
my Parse::YALALR::Parser $parser = $self->parser; |
263
|
0
|
|
|
|
|
|
$state0 = $parser->{states}->[$state0]; |
264
|
0
|
|
|
|
|
|
my $actions = $state0->{actions}; |
265
|
0
|
|
|
|
|
|
my $desc = "state $state0->{id} is ".$parser->dump_kernel($state0)."\nand in particular "; |
266
|
0
|
|
|
|
|
|
my ($exp, $reason); |
267
|
0
|
0
|
|
|
|
|
if ($action eq 'shift') { |
|
|
0
|
|
|
|
|
|
268
|
0
|
|
|
|
|
|
($exp, $reason) = $self->explain_shift($state0, $cause, $action, $actions); |
269
|
|
|
|
|
|
|
} elsif ($action eq 'reduce') { |
270
|
0
|
|
|
|
|
|
($exp, $reason) = $self->explain_reduce($state0, $cause, $action, $actions); |
271
|
|
|
|
|
|
|
} |
272
|
|
|
|
|
|
|
|
273
|
0
|
|
|
|
|
|
return ($exp, $desc.$reason); |
274
|
|
|
|
|
|
|
} |
275
|
|
|
|
|
|
|
|
276
|
|
|
|
|
|
|
# DESTRUCTION TRACKING (no practical purpose yet) |
277
|
|
|
|
|
|
|
# sub xitem::DESTROY { print "xitem::DESTROY\n"; } |
278
|
|
|
|
|
|
|
# sub item::DESTROY { print "item::DESTROY\n"; } |
279
|
|
|
|
|
|
|
# sub kernel::DESTROY { print "kernel::DESTROY\n"; } |
280
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
# Create a graph of xitem : { 'item' => grammar index of item B -> . \beta, |
282
|
|
|
|
|
|
|
# token => } |
283
|
|
|
|
|
|
|
# where whylookahead is 'generated' | 'propagated' | 'kernel' |
284
|
|
|
|
|
|
|
# and the xitem ref is the xitem containing item A -> \a1 . B \a2, f1 |
285
|
|
|
|
|
|
|
# if 'generated', then token is in FIRST(\a2) |
286
|
|
|
|
|
|
|
# if 'propagated', then token is in f1 |
287
|
|
|
|
|
|
|
# if 'kernel', then item is a kernel item and token is in the lookahead |
288
|
|
|
|
|
|
|
# |
289
|
|
|
|
|
|
|
# Note that this routine is very slow and produces a huge amount of data. |
290
|
|
|
|
|
|
|
# Should probably destroy the whole thing afterwards. (This routine is NOT |
291
|
|
|
|
|
|
|
# used to build a parser, only to explain why a lookahead is in an item if |
292
|
|
|
|
|
|
|
# the user asks.) |
293
|
|
|
|
|
|
|
# |
294
|
|
|
|
|
|
|
sub expand_state { |
295
|
0
|
|
|
0
|
0
|
|
my Parse::YALALR::Build $self = shift; |
296
|
0
|
|
|
|
|
|
my Parse::YALALR::Parser $parser = $self->parser; |
297
|
0
|
|
|
|
|
|
my ($state) = @_; |
298
|
0
|
|
|
|
|
|
return $self->expand_items(@{ $state->{items} }); |
|
0
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
} |
300
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
sub expand_items { |
302
|
0
|
|
|
0
|
0
|
|
my Parse::YALALR::Build $self = shift; |
303
|
0
|
|
|
|
|
|
my Parse::YALALR::Parser $parser = $self->parser; |
304
|
0
|
|
|
|
|
|
my (@kitems) = @_; |
305
|
|
|
|
|
|
|
|
306
|
0
|
|
|
|
|
|
my @xitems; |
307
|
|
|
|
|
|
|
my %visited; # { grammar index => xitem } |
308
|
0
|
|
|
|
|
|
my @Q; |
309
|
|
|
|
|
|
|
|
310
|
0
|
|
|
|
|
|
for my $kitem (@kitems) { |
311
|
0
|
|
|
|
|
|
my $xitem = bless { item => $kitem->{GRAMIDX}, |
312
|
0
|
|
|
|
|
|
map { ($_ => [ 'kernel' ]) } |
313
|
|
|
|
|
|
|
($parser->{symmap}->get_indices($kitem->{LA})) }, |
314
|
|
|
|
|
|
|
'xitem'; |
315
|
0
|
|
|
|
|
|
push @xitems, $xitem; |
316
|
0
|
|
|
|
|
|
$visited{$kitem->{GRAMIDX}} = $xitem; |
317
|
0
|
|
|
|
|
|
push(@Q, $xitem); |
318
|
|
|
|
|
|
|
} |
319
|
|
|
|
|
|
|
|
320
|
0
|
|
|
|
|
|
while (@Q) { |
321
|
0
|
|
|
|
|
|
my $node = shift(@Q); |
322
|
|
|
|
|
|
|
# $node : { 'item' => B -> \gamma . \beta1, t1=>..., t2=>... } |
323
|
|
|
|
|
|
|
# (t1,t2 are the lookahead) |
324
|
|
|
|
|
|
|
# (\gamma is empty unless it's a kernel item) |
325
|
|
|
|
|
|
|
# |
326
|
|
|
|
|
|
|
# If we make it past the upcoming 'next's, we'll know that |
327
|
|
|
|
|
|
|
# the item is actually -> \gamma . C \beta2 |
328
|
|
|
|
|
|
|
# (i.e., \beta1 = C \beta2) |
329
|
|
|
|
|
|
|
|
330
|
0
|
|
|
|
|
|
my $C = $parser->{grammar}->[$node->{item}]; |
331
|
0
|
0
|
|
|
|
|
next if $C == $parser->{nil}; |
332
|
0
|
0
|
|
|
|
|
next if $parser->is_token($C); |
333
|
|
|
|
|
|
|
|
334
|
|
|
|
|
|
|
# $F_beta2 := FIRST(\beta2) |
335
|
0
|
|
|
|
|
|
my @beta2 = $parser->get_dotalpha($node->{item} + 1); |
336
|
0
|
|
|
|
|
|
my $F_beta2 = $self->FIRST_nonvec(@beta2); |
337
|
|
|
|
|
|
|
# Gather up everything that will be passed to the children, either |
338
|
|
|
|
|
|
|
# by being generated by FIRST(\beta2) or propagated from the lookahead |
339
|
|
|
|
|
|
|
# of $node. |
340
|
0
|
|
|
|
|
|
my %generations; |
341
|
|
|
|
|
|
|
my %propagations; |
342
|
0
|
|
|
|
|
|
for my $t ($parser->{symmap}->get_indices($F_beta2)) { |
343
|
0
|
0
|
|
|
|
|
if ($t == $parser->{nil}) { |
344
|
0
|
|
|
|
|
|
foreach (keys %$node) { |
345
|
0
|
0
|
|
|
|
|
next if $_ eq 'item'; |
346
|
0
|
0
|
|
|
|
|
next if $_ eq 'parent0'; |
347
|
0
|
0
|
|
|
|
|
next if $_ == $parser->{nil}; |
348
|
0
|
|
|
|
|
|
$propagations{$_} = [ 'propagated', $node ]; |
349
|
|
|
|
|
|
|
} |
350
|
|
|
|
|
|
|
} else { |
351
|
0
|
|
|
|
|
|
$generations{$t} = [ 'generated', $node ]; |
352
|
|
|
|
|
|
|
} |
353
|
|
|
|
|
|
|
} |
354
|
|
|
|
|
|
|
|
355
|
0
|
|
|
|
|
|
for my $rule ($parser->get_rules($C)) { |
356
|
|
|
|
|
|
|
# $rule : grammar index of . C -> \alpha |
357
|
0
|
|
|
|
|
|
my $child = $rule + 1; # C -> . \alpha |
358
|
|
|
|
|
|
|
|
359
|
0
|
|
|
|
|
|
my $newXitem; |
360
|
0
|
0
|
|
|
|
|
if ($visited{$child}) { |
361
|
0
|
|
|
|
|
|
$newXitem = $visited{$child}; |
362
|
0
|
|
|
|
|
|
my $old_number_of_lookaheads = keys %$newXitem; |
363
|
0
|
|
|
|
|
|
%$newXitem = (%propagations, |
364
|
|
|
|
|
|
|
%generations, |
365
|
0
|
|
|
|
|
|
%{$visited{$child}}); |
366
|
0
|
0
|
|
|
|
|
next if keys %$newXitem == $old_number_of_lookaheads; |
367
|
|
|
|
|
|
|
} else { |
368
|
0
|
|
|
|
|
|
$newXitem = bless { item => $child, |
369
|
|
|
|
|
|
|
parent0 => $node, |
370
|
|
|
|
|
|
|
%generations, |
371
|
|
|
|
|
|
|
%propagations }, 'xitem'; |
372
|
|
|
|
|
|
|
} |
373
|
|
|
|
|
|
|
|
374
|
0
|
|
|
|
|
|
$visited{$child} = $newXitem; |
375
|
0
|
|
|
|
|
|
push(@Q, $newXitem); |
376
|
|
|
|
|
|
|
} |
377
|
|
|
|
|
|
|
} |
378
|
|
|
|
|
|
|
|
379
|
0
|
|
|
|
|
|
return \%visited; |
380
|
|
|
|
|
|
|
} |
381
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
sub dump_xreason { |
383
|
0
|
|
|
0
|
0
|
|
my Parse::YALALR::Build $self = shift; |
384
|
0
|
|
|
|
|
|
my Parse::YALALR::Parser $parser = $self->parser; |
385
|
0
|
|
|
|
|
|
my $reason = shift; |
386
|
0
|
|
|
|
|
|
my $str = $parser->dump_item($reason->[0]); |
387
|
0
|
0
|
|
|
|
|
if ($reason->[1] eq 'kernel') { |
388
|
0
|
|
|
|
|
|
return $str." (kernel item)"; |
389
|
|
|
|
|
|
|
} else { |
390
|
0
|
|
|
|
|
|
return $str." <-$reason->[1]-- ".$parser->dump_item($reason->[2]->{item}); |
391
|
|
|
|
|
|
|
} |
392
|
|
|
|
|
|
|
} |
393
|
|
|
|
|
|
|
|
394
|
|
|
|
|
|
|
# 1. LA_WHY trace to an item in the correct state |
395
|
|
|
|
|
|
|
sub LA_WHY_chain_explstr { |
396
|
0
|
|
|
0
|
0
|
|
my Parse::YALALR::Build $self = shift; |
397
|
0
|
|
|
|
|
|
my Parse::YALALR::Parser $parser = $self->parser; |
398
|
|
|
|
|
|
|
|
399
|
0
|
|
|
|
|
|
my $asXML; |
400
|
0
|
0
|
|
|
|
|
if (!ref $_[-1]) { |
401
|
0
|
|
|
|
|
|
$asXML = pop(@_); |
402
|
|
|
|
|
|
|
} |
403
|
|
|
|
|
|
|
|
404
|
0
|
|
|
|
|
|
my $str = ''; |
405
|
0
|
|
|
|
|
|
foreach (reverse @_) { |
406
|
0
|
|
|
|
|
|
my ($reason, $cause_item, $f1, $f2) = @$_; |
407
|
0
|
|
|
|
|
|
my $itemdesc = $parser->dump_item($cause_item, $asXML); |
408
|
|
|
|
|
|
|
# if ($reason eq 'generated') { |
409
|
|
|
|
|
|
|
# $str .= "generated by $itemdesc\n"; |
410
|
|
|
|
|
|
|
# } elsif ($reason eq 'propagated') { |
411
|
|
|
|
|
|
|
# $str .= "propagated from $itemdesc\n"; |
412
|
|
|
|
|
|
|
# } elsif ($reason eq 'chain-generated') { |
413
|
|
|
|
|
|
|
# $str .= "chain-generated from $itemdesc\n"; |
414
|
|
|
|
|
|
|
# } elsif ($reason eq 'epsilon-generated') { |
415
|
|
|
|
|
|
|
# $str .= "epsilon-generated from $itemdesc\n"; |
416
|
|
|
|
|
|
|
# } |
417
|
0
|
|
|
|
|
|
$str .= "propagates the lookahead to $itemdesc\n"; |
418
|
|
|
|
|
|
|
} |
419
|
|
|
|
|
|
|
|
420
|
0
|
|
|
|
|
|
chomp($str); |
421
|
0
|
|
|
|
|
|
return $str; |
422
|
|
|
|
|
|
|
} |
423
|
|
|
|
|
|
|
|
424
|
|
|
|
|
|
|
sub xreason_chain_explstr { |
425
|
0
|
|
|
0
|
0
|
|
my Parse::YALALR::Build $self = shift; |
426
|
0
|
|
|
|
|
|
my Parse::YALALR::Parser $parser = $self->parser; |
427
|
0
|
|
|
|
|
|
my ($token, $chain, $ultimate_state, $asXML) = @_; |
428
|
0
|
0
|
|
|
|
|
return '' if @$chain == 0; |
429
|
|
|
|
|
|
|
|
430
|
0
|
|
|
|
|
|
my $str; |
431
|
|
|
|
|
|
|
|
432
|
0
|
0
|
|
|
|
|
print "asXML: ".((defined $asXML) ? $asXML : "(undef)")."\n"; |
433
|
0
|
|
|
|
|
|
print "ITEMS: ", join(";; ", map { $parser->dump_item($_->[0]) } @$chain), "\n"; |
|
0
|
|
|
|
|
|
|
434
|
0
|
|
|
|
|
|
print "REASONS: ", join(" , ", map { $_->[1] } @$chain), "\n"; |
|
0
|
|
|
|
|
|
|
435
|
0
|
0
|
|
|
|
|
print "CAUSES: ", join(" , ", map { (defined $_->[2]) ? $parser->dump_item($_->[2]{item}) : "(undef)" } @$chain), "\n"; |
|
0
|
|
|
|
|
|
|
436
|
|
|
|
|
|
|
|
437
|
0
|
|
|
|
|
|
@$chain = reverse @$chain; |
438
|
|
|
|
|
|
|
|
439
|
0
|
|
|
|
|
|
my $lastitem; |
440
|
|
|
|
|
|
|
|
441
|
0
|
0
|
|
|
|
|
if ($chain->[0]->[1] eq 'kernel') { |
442
|
0
|
|
|
|
|
|
$lastitem = $chain->[0]->[0]; |
443
|
0
|
0
|
|
|
|
|
if ($lastitem == 1) { |
444
|
0
|
|
|
|
|
|
$str .= ":automatically generated item "; |
445
|
|
|
|
|
|
|
# $str .= $parser->dump_item($lastitem, $asXML)."\n"; |
446
|
|
|
|
|
|
|
} else { |
447
|
0
|
|
|
|
|
|
my (undef, $tmp) = |
448
|
|
|
|
|
|
|
$self->lookahead_inherit_explanation($ultimate_state, $lastitem, |
449
|
|
|
|
|
|
|
$token, $asXML); |
450
|
0
|
|
|
|
|
|
$tmp =~ s/\n/\n:/g; |
451
|
0
|
|
|
|
|
|
$str .= ":$tmp\n"; |
452
|
|
|
|
|
|
|
} |
453
|
|
|
|
|
|
|
} else { |
454
|
0
|
|
|
|
|
|
$lastitem = $chain->[0]->[2]->{item}; |
455
|
0
|
|
|
|
|
|
$str .= "generates ".$parser->dump_item($lastitem, $asXML)."\n"; |
456
|
|
|
|
|
|
|
} |
457
|
|
|
|
|
|
|
|
458
|
0
|
|
|
|
|
|
foreach (@$chain) { |
459
|
0
|
|
|
|
|
|
my ($item, $reason, $cause) = @$_; |
460
|
|
|
|
|
|
|
|
461
|
|
|
|
|
|
|
# if ($reason eq 'generated') { |
462
|
|
|
|
|
|
|
# $str .= "with la ".$parser->dump_sym($token, $asXML); |
463
|
|
|
|
|
|
|
# $str .= "generates ".$parser->dump_item($cause->{item}, $asXML)."\n"; |
464
|
|
|
|
|
|
|
# } elsif ($reason eq 'propagated') { |
465
|
|
|
|
|
|
|
# $str .= "with la ".$parser->dump_sym($token, $asXML); |
466
|
|
|
|
|
|
|
# $str .= "generates ".$parser->dump_item($cause->{item}, $asXML)."\n"; |
467
|
|
|
|
|
|
|
# } else { |
468
|
|
|
|
|
|
|
# $str .= "which is a kernel item\n"; |
469
|
|
|
|
|
|
|
# } |
470
|
|
|
|
|
|
|
|
471
|
0
|
0
|
|
|
|
|
if ($reason eq 'generated') { |
|
|
0
|
|
|
|
|
|
472
|
|
|
|
|
|
|
# That means $lastitem generated the lookahead. Examine why. |
473
|
0
|
|
|
|
|
|
$str .= $self->lookahead_generation_explanation($lastitem, $token, $asXML, " ")."\n"; |
474
|
|
|
|
|
|
|
} elsif ($reason eq 'kernel') { |
475
|
0
|
|
|
|
|
|
$str .= $self->lookahead_generation_explanation($item, $token, $asXML, "")."\n"; |
476
|
|
|
|
|
|
|
} |
477
|
|
|
|
|
|
|
|
478
|
0
|
|
|
|
|
|
$str .= "generates ".$parser->dump_item($item, $asXML)."\n"; |
479
|
|
|
|
|
|
|
|
480
|
0
|
|
|
|
|
|
print "LASTITEM turning over from ".$parser->dump_item($lastitem)." TO ".$parser->dump_item($item)."\n"; |
481
|
|
|
|
|
|
|
|
482
|
0
|
|
|
|
|
|
$lastitem = $item; |
483
|
|
|
|
|
|
|
} |
484
|
|
|
|
|
|
|
|
485
|
0
|
|
|
|
|
|
$str =~ s/\n+$//; |
486
|
0
|
|
|
|
|
|
return $str; |
487
|
|
|
|
|
|
|
} |
488
|
|
|
|
|
|
|
|
489
|
|
|
|
|
|
|
sub lookahead_generation_explanation { |
490
|
0
|
|
|
0
|
0
|
|
my Parse::YALALR::Build $self = shift; |
491
|
0
|
|
|
|
|
|
my Parse::YALALR::Parser $parser = $self->parser; |
492
|
0
|
|
|
|
|
|
my ($item, $lookahead, $asXML, $tab) = @_; |
493
|
0
|
|
|
|
|
|
my @alpha = $parser->get_dotalpha($item+1); |
494
|
0
|
|
|
|
|
|
print "ALPHA=".join(" ", $parser->dump_sym(@alpha))."\n"; |
495
|
0
|
|
|
|
|
|
my $firstalpha = $self->FIRST_nonvec(@alpha); |
496
|
0
|
|
|
|
|
|
my $str = ''; |
497
|
0
|
0
|
|
|
|
|
if (vec($firstalpha, $lookahead, 1)) { |
498
|
0
|
|
|
|
|
|
my $expl = $self->explain_first_alpha($lookahead, \@alpha, $asXML); |
499
|
0
|
|
|
|
|
|
$str .= "generates the lookahead "; |
500
|
0
|
|
|
|
|
|
$str .= $parser->dump_sym($lookahead, $asXML)."\n"; |
501
|
0
|
|
|
|
|
|
$str .= "because $expl"; |
502
|
|
|
|
|
|
|
} else { |
503
|
|
|
|
|
|
|
# $str = "inherits the lookahead ".$parser->dump_sym($lookahead, $asXML); |
504
|
|
|
|
|
|
|
} |
505
|
|
|
|
|
|
|
|
506
|
0
|
|
|
|
|
|
chomp($str); |
507
|
0
|
|
|
|
|
|
$str =~ s/\n/\n$tab/g; |
508
|
0
|
|
|
|
|
|
return $tab.$str; |
509
|
|
|
|
|
|
|
} |
510
|
|
|
|
|
|
|
|
511
|
|
|
|
|
|
|
sub lookahead_inherit_explanation { |
512
|
0
|
|
|
0
|
0
|
|
my Parse::YALALR::Build $self = shift; |
513
|
0
|
|
|
|
|
|
my Parse::YALALR::Parser $parser = $self->parser; |
514
|
0
|
|
|
|
|
|
my ($ultimate_state, $lastitem, $token, $asXML) = @_; |
515
|
0
|
|
|
|
|
|
return $self->explain_lookahead($ultimate_state, $lastitem, $token, |
516
|
|
|
|
|
|
|
undef, $asXML); |
517
|
|
|
|
|
|
|
} |
518
|
|
|
|
|
|
|
|
519
|
|
|
|
|
|
|
sub explain_first_alpha { |
520
|
0
|
|
|
0
|
0
|
|
my Parse::YALALR::Build $self = shift; |
521
|
0
|
|
|
|
|
|
my Parse::YALALR::Parser $parser = $self->parser; |
522
|
0
|
|
|
|
|
|
my ($la, $alpha, $asXML) = @_; |
523
|
|
|
|
|
|
|
|
524
|
0
|
|
|
|
|
|
my $str; |
525
|
0
|
|
|
|
|
|
foreach (@$alpha) { |
526
|
0
|
0
|
|
|
|
|
if ($self->FIRST_nonvec($_)) { |
|
|
0
|
|
|
|
|
|
527
|
0
|
0
|
|
|
|
|
if ($parser->is_nonterminal($_)) { |
528
|
0
|
|
|
|
|
|
$str .= $parser->dump_sym($la, $asXML)." is in "; |
529
|
0
|
0
|
|
|
|
|
$str .= "" if $asXML; |
530
|
0
|
|
|
|
|
|
$str .= "FIRST(".$parser->dump_sym($_).")"; |
531
|
0
|
0
|
|
|
|
|
$str .= "" if $asXML; |
532
|
|
|
|
|
|
|
} else { |
533
|
0
|
|
|
|
|
|
$str .= $parser->dump_sym($la, $asXML)." immediately follows the expanded nonterminal"; |
534
|
|
|
|
|
|
|
} |
535
|
0
|
|
|
|
|
|
return $str; |
536
|
|
|
|
|
|
|
} elsif ($parser->is_nonterminal($_)) { |
537
|
0
|
0
|
|
|
|
|
$str .= "" if $asXML; |
538
|
0
|
|
|
|
|
|
$str .= $parser->dump_sym($_, $asXML)." derives the empty string"; |
539
|
0
|
0
|
|
|
|
|
$str .= "" if $asXML; |
540
|
0
|
|
|
|
|
|
$str .= ", and\n"; |
541
|
|
|
|
|
|
|
} |
542
|
|
|
|
|
|
|
} |
543
|
|
|
|
|
|
|
|
544
|
0
|
|
|
|
|
|
die "Hey! Never found lookahead in alpha!"; |
545
|
|
|
|
|
|
|
} |
546
|
|
|
|
|
|
|
|
547
|
|
|
|
|
|
|
sub xitem_chain_explstr { |
548
|
0
|
|
|
0
|
0
|
|
my Parse::YALALR::Build $self = shift; |
549
|
0
|
|
|
|
|
|
my Parse::YALALR::Parser $parser = $self->parser; |
550
|
|
|
|
|
|
|
|
551
|
0
|
|
|
|
|
|
my $asXML; |
552
|
0
|
0
|
|
|
|
|
if (!ref $_[-1]) { |
553
|
0
|
|
|
|
|
|
$asXML = pop(@_); |
554
|
|
|
|
|
|
|
} |
555
|
|
|
|
|
|
|
|
556
|
0
|
0
|
|
|
|
|
return '' if @_ == 0; |
557
|
|
|
|
|
|
|
|
558
|
0
|
0
|
|
|
|
|
my $xformat = ($asXML ? "briefxml" : "brief"); |
559
|
0
|
|
|
|
|
|
my $str = ''; |
560
|
0
|
|
|
|
|
|
my @xitems = reverse @_; |
561
|
0
|
|
|
|
|
|
my $kernel = shift(@xitems); |
562
|
0
|
|
|
|
|
|
$str .= "kernel item ".$parser->dump_item($kernel->{item}, $xformat)."\n"; |
563
|
|
|
|
|
|
|
|
564
|
0
|
|
|
|
|
|
for my $xitem (@xitems) { |
565
|
0
|
|
|
|
|
|
$str .= "generates ".$parser->dump_item($xitem->{item}, $xformat)."\n"; |
566
|
|
|
|
|
|
|
} |
567
|
|
|
|
|
|
|
|
568
|
0
|
|
|
|
|
|
chomp($str); |
569
|
0
|
|
|
|
|
|
return $str; |
570
|
|
|
|
|
|
|
} |
571
|
|
|
|
|
|
|
|
572
|
|
|
|
|
|
|
# Tie the chains together |
573
|
|
|
|
|
|
|
# $ultimate_chain |
574
|
|
|
|
|
|
|
# $xreason_chain |
575
|
|
|
|
|
|
|
# $lawhy_chain |
576
|
|
|
|
|
|
|
# |
577
|
|
|
|
|
|
|
# $lawhy_chain is in reverse order |
578
|
|
|
|
|
|
|
# $xreason_chain is in reverse order |
579
|
|
|
|
|
|
|
# $ultimate_chain is in reverse order |
580
|
|
|
|
|
|
|
# |
581
|
|
|
|
|
|
|
sub explain_lookahead { |
582
|
0
|
|
|
0
|
0
|
|
my Parse::YALALR::Build $self = shift; |
583
|
0
|
|
|
|
|
|
my Parse::YALALR::Parser $parser = $self->parser; |
584
|
0
|
|
|
|
|
|
my ($state, $idx, $token, $ultimate_kitem, $asXML) = @_; |
585
|
|
|
|
|
|
|
|
586
|
0
|
|
|
|
|
|
$DB::single = 1; |
587
|
|
|
|
|
|
|
|
588
|
0
|
|
|
|
|
|
my ($lawhy_chain, $xreason_chain, $ultimate_chain, $ultimate_state) = |
589
|
|
|
|
|
|
|
$self->lookahead_explanation($state, $idx, $token, $ultimate_kitem); |
590
|
|
|
|
|
|
|
|
591
|
0
|
|
|
|
|
|
my $str; |
592
|
|
|
|
|
|
|
|
593
|
|
|
|
|
|
|
# $str .= "--Kernel to just before cause--\n"; |
594
|
0
|
|
|
|
|
|
$str .= $self->xitem_chain_explstr(@$ultimate_chain, $asXML); |
595
|
0
|
|
|
|
|
|
$str =~ s/^\n//; |
596
|
|
|
|
|
|
|
# $str .= "\n--Cause to A : . b x y--"; |
597
|
0
|
|
|
|
|
|
$str .= "\n".$self->xreason_chain_explstr($token, $xreason_chain, $ultimate_state, $asXML); |
598
|
0
|
|
|
|
|
|
$str =~ s/^\n//; |
599
|
|
|
|
|
|
|
# $str .= "\n--Propagation chain--"; |
600
|
0
|
|
|
|
|
|
pop(@$lawhy_chain); # Get rid of kernel item (printed above) |
601
|
0
|
|
|
|
|
|
$str .= "\n".$self->LA_WHY_chain_explstr(@$lawhy_chain, $asXML); |
602
|
|
|
|
|
|
|
# $str .= "\n--done--"; |
603
|
|
|
|
|
|
|
|
604
|
0
|
|
|
|
|
|
return [ $lawhy_chain, $xreason_chain, $ultimate_chain ], $str; |
605
|
|
|
|
|
|
|
} |
606
|
|
|
|
|
|
|
|
607
|
|
|
|
|
|
|
# lookahead_explanation |
608
|
|
|
|
|
|
|
# |
609
|
|
|
|
|
|
|
# Explaining lookaheads is a 3-step process: |
610
|
|
|
|
|
|
|
# |
611
|
|
|
|
|
|
|
# 1. Use item->{LA_WHY} to trace to the ultimately generating state |
612
|
|
|
|
|
|
|
# and kernel item. |
613
|
|
|
|
|
|
|
# |
614
|
|
|
|
|
|
|
# 2. Look back at the path found in #1 and find the second-to-last |
615
|
|
|
|
|
|
|
# item (it will be the one the was propagated from an item generated |
616
|
|
|
|
|
|
|
# by the ultimate kernel item.) Expand the ultimate state and use the |
617
|
|
|
|
|
|
|
# xitem lookahead links to get to the generating xitem for that 2nd to |
618
|
|
|
|
|
|
|
# last item respecting that particular lookahead. |
619
|
|
|
|
|
|
|
# |
620
|
|
|
|
|
|
|
# 3. To get to the kernel, expand just the ultimately generating |
621
|
|
|
|
|
|
|
# kernel item to find the chain of GRAMIDXes that lead from the xitem |
622
|
|
|
|
|
|
|
# found in the previous step to the kernel item. |
623
|
|
|
|
|
|
|
# |
624
|
|
|
|
|
|
|
# Example: |
625
|
|
|
|
|
|
|
# A -> X D C y |
626
|
|
|
|
|
|
|
# D -> B d |
627
|
|
|
|
|
|
|
# B -> a b |
628
|
|
|
|
|
|
|
# C -> |
629
|
|
|
|
|
|
|
# X -> |
630
|
|
|
|
|
|
|
# |
631
|
|
|
|
|
|
|
# Consider X -> a b ., d |
632
|
|
|
|
|
|
|
# |
633
|
|
|
|
|
|
|
# Step 1 finds |
634
|
|
|
|
|
|
|
# D -> X . D C y (kernel) |
635
|
|
|
|
|
|
|
# B -> a . b, d (propagated) |
636
|
|
|
|
|
|
|
# B -> a b ., d (propagated) |
637
|
|
|
|
|
|
|
# Step 2 finds the generating xitem |
638
|
|
|
|
|
|
|
# D -> . B d, ... |
639
|
|
|
|
|
|
|
# B -> . a b, d (generated) |
640
|
|
|
|
|
|
|
# Step 3 finds the path from the kernel |
641
|
|
|
|
|
|
|
# A -> X . D C y (kernel) |
642
|
|
|
|
|
|
|
# D -> . B d, ... (generated) |
643
|
|
|
|
|
|
|
# |
644
|
|
|
|
|
|
|
# Sewing those together in the correct order results in: |
645
|
|
|
|
|
|
|
# A -> X . D C y (kernel) |
646
|
|
|
|
|
|
|
# D -> . B d, ... (generated) *** source of the lookahead |
647
|
|
|
|
|
|
|
# B -> . a b, d (generated) |
648
|
|
|
|
|
|
|
# B -> a . b, d (propagated) |
649
|
|
|
|
|
|
|
# B -> a b ., d (propagated) |
650
|
|
|
|
|
|
|
# |
651
|
|
|
|
|
|
|
sub lookahead_explanation { |
652
|
0
|
|
|
0
|
0
|
|
my Parse::YALALR::Build $self = shift; |
653
|
0
|
|
|
|
|
|
my Parse::YALALR::Parser $parser = $self->{parser}; |
654
|
0
|
|
|
|
|
|
my ($state, $idx, $token, $ultimate_kitem) = @_; |
655
|
|
|
|
|
|
|
|
656
|
|
|
|
|
|
|
# Output |
657
|
0
|
|
|
|
|
|
my (@lawhy_chain, @xreason_chain, @ultimate_chain); |
658
|
|
|
|
|
|
|
|
659
|
|
|
|
|
|
|
# First, find the item we're talking about |
660
|
0
|
|
|
|
|
|
my ($item) = grep { $_->{GRAMIDX} == $idx } @{ $state->{items} }; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
661
|
0
|
0
|
|
|
|
|
undef $ultimate_kitem if defined $item; |
662
|
|
|
|
|
|
|
|
663
|
|
|
|
|
|
|
# Step 1 is unnecessary if the caller gave us the ultimate kernel item |
664
|
0
|
|
|
|
|
|
my $lastidx; |
665
|
0
|
0
|
|
|
|
|
if (!defined $ultimate_kitem) { |
666
|
1
|
|
|
1
|
|
9
|
use Carp; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
739
|
|
667
|
0
|
0
|
|
|
|
|
confess "No ultimate kernel item given and item not found" |
668
|
|
|
|
|
|
|
if !defined $item; |
669
|
|
|
|
|
|
|
|
670
|
|
|
|
|
|
|
# Step 1: item->{LA_WHY} chain |
671
|
0
|
|
|
|
|
|
@lawhy_chain = $self->get_LA_WHY_chain($token, $item); |
672
|
0
|
|
|
|
|
|
$ultimate_kitem = $lawhy_chain[-1]->[1]; |
673
|
0
|
0
|
|
|
|
|
if ($lawhy_chain[-1]->[0] eq 'init') { |
674
|
0
|
|
|
|
|
|
print "Ran afoul of autogenerated item\n"; |
675
|
0
|
|
|
|
|
|
$DB::single = 1; |
676
|
0
|
|
|
|
|
|
$ultimate_kitem = $parser->{states}->[0]->{items}->[0]; |
677
|
0
|
|
|
|
|
|
$lastidx = 1; |
678
|
|
|
|
|
|
|
} else { |
679
|
0
|
|
|
|
|
|
$state = $self->{item2state}->{$ultimate_kitem}; |
680
|
0
|
|
|
|
|
|
$lastidx = $lawhy_chain[-2]->[1]->{GRAMIDX}-1; |
681
|
|
|
|
|
|
|
} |
682
|
|
|
|
|
|
|
} else { |
683
|
0
|
|
|
|
|
|
$lastidx = $idx; |
684
|
|
|
|
|
|
|
} |
685
|
|
|
|
|
|
|
|
686
|
|
|
|
|
|
|
# Step 2: expand the state and find the generating xitem |
687
|
0
|
|
|
|
|
|
my $xstate = $self->expand_state($state); |
688
|
0
|
|
|
|
|
|
{ local $^W = 0; print $parser->dump_xstate($xstate); } |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
689
|
0
|
|
|
|
|
|
my $xitem = $xstate->{$lastidx}; |
690
|
0
|
|
|
|
|
|
$DB::single = 1; |
691
|
0
|
|
|
|
|
|
@xreason_chain = $self->get_xreason_chain($xitem, $token); |
692
|
0
|
|
|
|
|
|
my $generating_reason = $xreason_chain[-1]; |
693
|
|
|
|
|
|
|
|
694
|
|
|
|
|
|
|
# Check whether step 3 makes sense |
695
|
0
|
0
|
|
|
|
|
if ($generating_reason->[1] ne 'kernel') { |
696
|
0
|
|
|
|
|
|
@ultimate_chain = |
697
|
|
|
|
|
|
|
$self->get_any_chain($generating_reason->[2]->{item}, |
698
|
|
|
|
|
|
|
$ultimate_kitem); |
699
|
|
|
|
|
|
|
} else { |
700
|
0
|
0
|
|
|
|
|
warn "Hm. Found an alternate reason?" |
701
|
|
|
|
|
|
|
if $generating_reason->[0] != $ultimate_kitem->{GRAMIDX}; |
702
|
|
|
|
|
|
|
} |
703
|
|
|
|
|
|
|
|
704
|
0
|
|
|
|
|
|
return \@lawhy_chain, \@xreason_chain, \@ultimate_chain, $state; |
705
|
|
|
|
|
|
|
} |
706
|
|
|
|
|
|
|
|
707
|
|
|
|
|
|
|
# Given an item A -> x y z . \alpha, t|other0 and a token t |
708
|
|
|
|
|
|
|
# return the sequence |
709
|
|
|
|
|
|
|
# <'myself', A -> x y z . \alpha, t|other0> |
710
|
|
|
|
|
|
|
# <'propagated', A -> x y . z \alpha, t|other1> |
711
|
|
|
|
|
|
|
# <'propagated', A -> x . y z \alpha, t|other2> |
712
|
|
|
|
|
|
|
# <'generated', Q -> Z t P . A t> |
713
|
|
|
|
|
|
|
# |
714
|
|
|
|
|
|
|
# Could this be changed to produce shorter chains by favoring generated |
715
|
|
|
|
|
|
|
# links? (I don't think this is necessary to avoid cycles) |
716
|
|
|
|
|
|
|
sub get_LA_WHY_chain { |
717
|
0
|
|
|
0
|
0
|
|
my Parse::YALALR::Build $self = shift; |
718
|
0
|
|
|
|
|
|
my Parse::YALALR::Parser $parser = $self->parser; |
719
|
0
|
|
|
|
|
|
my ($token, $item) = @_; |
720
|
|
|
|
|
|
|
|
721
|
0
|
|
|
|
|
|
my @chain = ([ 'myself', $item ]); |
722
|
|
|
|
|
|
|
|
723
|
0
|
0
|
|
|
|
|
if ($item->{GRAMIDX} == 1) { |
724
|
0
|
|
|
|
|
|
return ([ 'init', $item ]); |
725
|
|
|
|
|
|
|
} |
726
|
|
|
|
|
|
|
|
727
|
0
|
|
|
|
|
|
my $lawhy; |
728
|
0
|
|
|
|
|
|
do { |
729
|
0
|
|
|
|
|
|
my @la = grep { vec($_, $token, 1) } (keys %{ $item->{LA_WHY} }); |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
730
|
0
|
0
|
|
|
|
|
die "Unable to find LA_WHY for ".$parser->dump_sym($token) |
731
|
|
|
|
|
|
|
if @la == 0; |
732
|
0
|
|
|
|
|
|
$lawhy = $item->{LA_WHY}->{$la[0]}; |
733
|
0
|
|
|
|
|
|
push(@chain, $lawhy); |
734
|
0
|
|
|
|
|
|
$item = $lawhy->[1]; |
735
|
|
|
|
|
|
|
} while ($lawhy->[0] !~ /generated/); |
736
|
|
|
|
|
|
|
|
737
|
0
|
|
|
|
|
|
return @chain; |
738
|
|
|
|
|
|
|
} |
739
|
|
|
|
|
|
|
|
740
|
|
|
|
|
|
|
# Given an xitem C -> . \alpha and token a token d, produce a chain |
741
|
|
|
|
|
|
|
# C -> . \alpha, d |
742
|
|
|
|
|
|
|
# B -> . C D |
743
|
|
|
|
|
|
|
# A -> X . B d |
744
|
|
|
|
|
|
|
# |
745
|
|
|
|
|
|
|
# saying that d is generated by A -> . B d (because D is nullable) |
746
|
|
|
|
|
|
|
# |
747
|
|
|
|
|
|
|
sub get_xreason_chain { |
748
|
0
|
|
|
0
|
0
|
|
my Parse::YALALR::Build $self = shift; |
749
|
0
|
|
|
|
|
|
my Parse::YALALR::Parser $parser = $self->parser; |
750
|
0
|
|
|
|
|
|
my ($xitem, $token) = @_; |
751
|
|
|
|
|
|
|
|
752
|
0
|
|
|
|
|
|
my %visited; # { grammar index } |
753
|
|
|
|
|
|
|
|
754
|
1
|
|
|
1
|
|
7
|
use Carp; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
7060
|
|
755
|
0
|
0
|
0
|
|
|
|
confess ($token || "false") if !defined $xitem->{$token}; |
756
|
0
|
|
|
|
|
|
my @chain = (bless [ $xitem->{item}, @{$xitem->{$token}} ], 'xreason'); |
|
0
|
|
|
|
|
|
|
757
|
|
|
|
|
|
|
|
758
|
|
|
|
|
|
|
# Traverse upwards from the given item until either a kernel item |
759
|
|
|
|
|
|
|
# is reached or the requested token is generated. |
760
|
0
|
|
|
|
|
|
while (1) { |
761
|
0
|
|
|
|
|
|
my ($last_item, $last_reason, $last_cause) = @{$chain[-1]}; |
|
0
|
|
|
|
|
|
|
762
|
0
|
0
|
|
|
|
|
return @chain if $last_reason ne 'propagated'; |
763
|
0
|
0
|
|
|
|
|
die "Infinite loop!" if $visited{$last_cause->{item}}; # DBG |
764
|
0
|
|
|
|
|
|
$visited{$last_cause->{item}} = 1; |
765
|
0
|
|
|
|
|
|
push(@chain, |
766
|
0
|
|
|
|
|
|
bless [ $last_cause->{item}, @{$last_cause->{$token}} ], |
767
|
|
|
|
|
|
|
'xreason'); |
768
|
0
|
0
|
|
|
|
|
return @chain if $chain[-1]->[1] eq 'kernel'; |
769
|
|
|
|
|
|
|
} |
770
|
|
|
|
|
|
|
} |
771
|
|
|
|
|
|
|
|
772
|
|
|
|
|
|
|
# from_xitem |
773
|
|
|
|
|
|
|
# . |
774
|
|
|
|
|
|
|
# . |
775
|
|
|
|
|
|
|
# . |
776
|
|
|
|
|
|
|
# to_xitem |
777
|
|
|
|
|
|
|
sub get_any_chain { |
778
|
0
|
|
|
0
|
0
|
|
my Parse::YALALR::Build $self = shift; |
779
|
0
|
|
|
|
|
|
my Parse::YALALR::Parser $parser = $self->parser; |
780
|
0
|
|
|
|
|
|
my ($from_idx, $to_kitem) = @_; |
781
|
|
|
|
|
|
|
|
782
|
0
|
|
|
|
|
|
my $xwad = $self->expand_items($to_kitem); |
783
|
0
|
|
|
|
|
|
my @chain; |
784
|
0
|
|
|
|
|
|
my $xitem = $xwad->{$from_idx}; |
785
|
0
|
|
|
|
|
|
while (1) { |
786
|
0
|
|
|
|
|
|
push(@chain, $xitem); |
787
|
0
|
0
|
|
|
|
|
last if $xitem->{item} == $to_kitem->{GRAMIDX}; |
788
|
0
|
|
|
|
|
|
$xitem = $xitem->{parent0}; |
789
|
|
|
|
|
|
|
} |
790
|
|
|
|
|
|
|
|
791
|
0
|
|
|
|
|
|
shift(@chain); # Really just want the explanation starting after from_idx |
792
|
0
|
|
|
|
|
|
return @chain; |
793
|
|
|
|
|
|
|
} |
794
|
|
|
|
|
|
|
|
795
|
|
|
|
|
|
|
sub get_lookahead_chain { |
796
|
0
|
|
|
0
|
0
|
|
my Parse::YALALR::Build $self = shift; |
797
|
0
|
|
|
|
|
|
my Parse::YALALR::Parser $parser = $self->parser; |
798
|
0
|
|
|
|
|
|
my ($state, $token, $item) = @_; |
799
|
|
|
|
|
|
|
|
800
|
0
|
|
|
|
|
|
my $lawhy; |
801
|
|
|
|
|
|
|
# Foreach lavec in LA_WHY = { la => lawhy } that contains TOKEN |
802
|
0
|
|
|
|
|
|
for my $lavec (grep { vec($_, $token, 1) } (keys %{$item->{LA_WHY}})) { |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
803
|
0
|
|
|
|
|
|
$lawhy = $item->{LA_WHY}->{$lavec}; |
804
|
0
|
0
|
|
|
|
|
last if $lawhy->[0] eq 'generated'; |
805
|
|
|
|
|
|
|
} |
806
|
|
|
|
|
|
|
|
807
|
0
|
0
|
|
|
|
|
$DB::single = 1 if !defined $lawhy; |
808
|
0
|
0
|
|
|
|
|
die "Unable to find lookahead chain for token ".$parser->dump_sym($token)." in item ".$parser->dump_item($item) if !defined $lawhy; |
809
|
|
|
|
|
|
|
|
810
|
0
|
0
|
|
|
|
|
return ($lawhy) if $lawhy->[0] eq 'generated'; |
811
|
0
|
|
|
|
|
|
my $K0 = $self->{item2state}->{$lawhy->[1]}; |
812
|
0
|
|
|
|
|
|
my @chain = $self->get_lookahead_chain($K0, $token, $lawhy->[1]->{GRAMIDX}); |
813
|
0
|
|
|
|
|
|
return ($lawhy, @chain); |
814
|
|
|
|
|
|
|
} |
815
|
|
|
|
|
|
|
|
816
|
|
|
|
|
|
|
sub get_lookahead_chain3 { |
817
|
0
|
|
|
0
|
0
|
|
my Parse::YALALR::Build $self = shift; |
818
|
0
|
|
|
|
|
|
my Parse::YALALR::Parser $parser = $self->parser; |
819
|
0
|
|
|
|
|
|
my ($state, $token, $item) = @_; |
820
|
|
|
|
|
|
|
|
821
|
0
|
|
|
|
|
|
my $expansion = $self->expand_state($state); |
822
|
0
|
|
|
|
|
|
my $xitem = $expansion->{$item}; |
823
|
|
|
|
|
|
|
|
824
|
|
|
|
|
|
|
# @chain : ( ) |
825
|
|
|
|
|
|
|
# item ITEMIDX was created because REASON by xitem CAUSE |
826
|
0
|
|
|
|
|
|
my @chain = (bless [ $item, @{$xitem->{$token}} ], 'xreason'); |
|
0
|
|
|
|
|
|
|
827
|
0
|
|
|
|
|
|
my %visited; |
828
|
0
|
|
|
|
|
|
$visited{$item} = 1; |
829
|
|
|
|
|
|
|
|
830
|
|
|
|
|
|
|
# Traverse upwards from the given item until either a kernel item |
831
|
|
|
|
|
|
|
# is reached or the requested token is generated. |
832
|
0
|
|
|
|
|
|
while (1) { |
833
|
0
|
|
|
|
|
|
my ($last_item, $last_reason, $last_cause) = @{$chain[-1]}; |
|
0
|
|
|
|
|
|
|
834
|
0
|
|
|
|
|
|
$visited{$last_cause->{item}} = 1; |
835
|
0
|
0
|
|
|
|
|
last if $last_reason ne 'propagated'; |
836
|
0
|
|
|
|
|
|
push(@chain, |
837
|
0
|
|
|
|
|
|
bless [ $last_cause->{item}, @{$last_cause->{$token}} ], 'xreason'); |
838
|
0
|
0
|
|
|
|
|
return \@chain if $chain[-1]->[1] eq 'kernel'; |
839
|
|
|
|
|
|
|
} |
840
|
|
|
|
|
|
|
|
841
|
|
|
|
|
|
|
# Then keep traversing upward along randomly chosen unvisited |
842
|
|
|
|
|
|
|
# links until a kernel item is reached. |
843
|
0
|
|
|
|
|
|
LINK: while (1) { |
844
|
0
|
|
|
|
|
|
my ($last_item, $last_reason, $last_cause) = @{$chain[-1]}; |
|
0
|
|
|
|
|
|
|
845
|
0
|
|
|
|
|
|
$visited{$last_cause->{item}} = 1; |
846
|
|
|
|
|
|
|
|
847
|
|
|
|
|
|
|
# Pick randomly (grab the first one reached) |
848
|
0
|
|
|
|
|
|
while (my ($t, $r) = each %$last_cause) { |
849
|
0
|
0
|
|
|
|
|
next if $t eq 'item'; |
850
|
0
|
0
|
|
|
|
|
next if $t eq 'parent0'; |
851
|
0
|
0
|
|
|
|
|
last LINK if $r->[0] eq 'kernel'; |
852
|
0
|
0
|
|
|
|
|
next if $visited{$r->[1]->{item}}; |
853
|
0
|
|
|
|
|
|
push(@chain, bless [ $last_cause->{item}, @$r ], 'xreason'); |
854
|
0
|
|
|
|
|
|
next LINK; |
855
|
|
|
|
|
|
|
} |
856
|
|
|
|
|
|
|
|
857
|
0
|
|
|
|
|
|
print STDERR "Failed to find path to kernel item\n"; |
858
|
0
|
|
|
|
|
|
print STDERR "Visited items:\n"; |
859
|
0
|
|
|
|
|
|
foreach (keys %visited) { |
860
|
0
|
|
|
|
|
|
print STDERR $parser->dump_item($_)."\n"; |
861
|
|
|
|
|
|
|
} |
862
|
0
|
|
|
|
|
|
die "Bye bye\n"; |
863
|
|
|
|
|
|
|
} |
864
|
|
|
|
|
|
|
|
865
|
0
|
|
|
|
|
|
return \@chain; |
866
|
|
|
|
|
|
|
} |
867
|
|
|
|
|
|
|
|
868
|
|
|
|
|
|
|
1; # I am not a module! |