| 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! |