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