File Coverage

blib/lib/Parse/YALALR/Build.pm
Criterion Covered Total %
statement 38 654 5.8
branch 0 214 0.0
condition 0 57 0.0
subroutine 14 43 32.5
pod 0 26 0.0
total 52 994 5.2


line stmt bran cond sub pod time code
1             # -*- cperl -*-
2             # FYI: -*-mode: Lisp; fill-column: 75; comment-column: 50; -*-
3             #
4              
5             BEGIN {
6 1     1   885 $SIG{__WARN__} = sub { print STDERR shift; $DB::single = 1; };
  0         0  
  0         0  
7             };
8              
9             package chain;
10 1     1   1859 use fields qw(RULEIDX FIRSTLA);
  1         1629  
  1         7  
11              
12             package Parse::YALALR::Build;
13              
14 1     1   637 use Parse::YALALR::Common;
  1         3  
  1         6  
15 1     1   577 use Parse::YALALR::Read;
  1         5  
  1         39  
16 1     1   617 use Parse::YALALR::Vector;
  1         4  
  1         28  
17 1     1   508 use Parse::YALALR::Kernel;
  1         5  
  1         33  
18 1     1   6 use Parse::YALALR::Parser;
  1         2  
  1         20  
19 1     1   5 use Carp;
  1         2  
  1         70  
20              
21             # Load in the explanation extensions. The BEGIN {require} stuff is
22             # just to make it clear that this is not an independent module; it
23             # would work to say use instead.
24 1     1   792 BEGIN { require 'Parse/YALALR/Explain.pl'; };
25              
26             use fields
27 1         13 (parser =>
28              
29             # Lookup tables
30             item2state => # { stringified item ref => state that contains it }
31             itemmap => # { "statenum_itemidx" => item }
32             quickstate => # { 96-bit hash of kernel items in a state => state }
33              
34             FIRST => #
35             nullable => # { nullable symbol }
36             chainrules => # { A => { B => [ chainrules for A=>B ] }
37             # chainrule : [ grammar_index for rule => vec(??) ]
38             # misc & unclassified
39             why_nullable => #
40             chainreachable => #
41             chainfirst => #
42             WHY_FIRST => #
43              
44             why => # Whether to compute the WHY information
45              
46 1     1   8 'temp_tokmap'); #
  1         3  
47              
48 1     1   195 use strict;
  1         2  
  1         41  
49 1     1   4 use Carp qw(verbose croak);
  1         2  
  1         203  
50              
51             # Parse::YALALR::Build::new
52             #
53             # Reads the grammar file (Parse::YALALR::Read::read), collects all the
54             # interesting information (Parse::YALALR::Build::collect_grammar), and
55             # then builds the parser (Parse::YALALR::Build::build)
56             #
57             sub new {
58 0     0 0   my ($class, $lang, $data, %opts) = @_;
59 0 0         $data = Parse::YALALR::Read->read($lang, $data)
60             unless UNIVERSAL::isa($data, 'Parse::YALALR::Read');
61             # print "Done reading at ".time."= t0+".(time-$::t0)."\n";
62 0 0         $class = ref $class if ref $class;
63 1     1   6 no strict 'refs';
  1         1  
  1         2376  
64 0           my Parse::YALALR::Build $self = bless [\%{"$class\::FIELDS"}], $class;
  0            
65 0           $self->{why} = $opts{why};
66 0           $self->{parser} = Parse::YALALR::Parser->new(%opts);
67 0           $self->collect_grammar($data); # Remember to add START -> S
68 0           $self->build();
69 0           return $self;
70             }
71              
72 0     0 0   sub parser ($) { $_[0]->{parser} }
73              
74             sub build {
75 0     0 0   my ($self) = @_;
76 0           $self->compute_NULLABLE();
77 0           $self->compute_FIRST();
78 0           $self->compute_chainFIRSTs();
79 0           $self->compute_chains(); # Change this to demand-driven?
80 0           $self->generate_parser();
81 0           return $self;
82             }
83              
84             sub decide_token {
85 0     0 0   my Parse::YALALR::Build $self = shift;
86 0           my ($str) = @_;
87 0 0         return 0 if ref $str;
88 0 0         return 1 if defined $self->{temp_tokmap}->{$str};
89 0 0         return 1 if $str =~ /^'/;
90             # return 1 if $str =~ /^[A-Z_]+$/;
91 0           return 0;
92             }
93              
94             # collect_grammar
95             #
96             # INPUT:
97             # $data->{rules} : [ [ lhs, [ rhssym ], prec ] ]
98             # - rhssym is a SCALAR ref if it's an action. deref to get perl code.
99             # Will be blessed into CODE if normal code;
100             # CONDITION if a conditional ( is C or perl)
101             # - prec is a symbol to inherit precedence from, or ''
102             #
103             # OUTPUT:
104             # $self->{grammar} : array of all symbols in all rules, each separated by $nil
105             # $self->{code} : [ code_index => code_subroutine ]
106             # $self->{rule_code} : [ rulepos => code_subroutine ]
107             # $self->{ruletable} : [ nonterminal => [ grammar_index of lhs for rule ] ]
108             # $self->{epsilonrules}
109             # $self->{chainrules} : { A => { B => [ chainrules for A=>B ] }
110             # chainrule : [ grammar_index for rule => vec(FIRST??) ]
111             # $self->{nonterminals} : [ symbol ]
112             # $self->{tokens} : [ symbol ]
113             # $self->{ntflag} : [ symbol => boolean (is symmap[symbol] a nonterminal?) ]
114             # $self->{precedence} : [ token => ]
115             # $self->{rule_precedence} : [ rule => ]
116             #
117             # All symbols are converted to indexes in $self->{symmap}, which is built
118             # as a side effect.
119             #
120             sub collect_grammar {
121 0     0 0   my Parse::YALALR::Build $self = shift;
122 0           my ($data) = @_;
123 0           my $parser = $self->parser;
124              
125 0           my $nil = $parser->{nil};
126 0           my $end = $parser->{end};
127 0           my $error = $parser->{error};
128 0           $parser->register_token('error');
129             # Add the START -> S rule
130              
131 0           my $something;
132 0 0         if (exists $data->{start_symbol}) {
133 0           $something = $data->{start_symbol};
134             } else {
135 0           $something = $data->{rules}->[0]->[0];
136             }
137 0           unshift(@{$data->{rules}}, [ '', [ $something ] ]);
  0            
138 0           $parser->{startsym} = $parser->{symmap}->add_value('');
139 0           $parser->{startrule} = 1; # HACK
140              
141 0           foreach my $token (@{$data->{tokens}}) {
  0            
142 0           $parser->register_token($token);
143             }
144              
145 0           foreach my $precset (@{$data->{precedence}}) {
  0            
146 0           foreach my $token (@{$precset->[1]}) {
  0            
147 0           $parser->register_token($token);
148 0           $parser->{symmap}->add_value($token);
149             }
150             }
151              
152 0           my @rules;
153             my %rules; # { nt => [ rule ] }
154 0           my @epsilonrules;
155 0           my %chainrules;
156              
157 0           my @grammar;
158              
159 0           my @code;
160 0           my $code_ctr = 0;
161              
162 0           my $i = 0;
163 0           my %ruleprecs; # For rules with hardcoded %prec things
164 0           for my $rule (@{$data->{rules}}) {
  0            
165 0           my ($lhs, $rhs, $prec) = @$rule;
166 0           my $istok = $self->decide_token($lhs);
167 0           $lhs = $parser->{symmap}->get_index($lhs);
168              
169 0           my $rulepos = $i;
170 0           push(@rules, $rulepos);
171 0           push(@{$rules{$lhs}}, $rulepos);
  0            
172 0           $ruleprecs{$rulepos} = $prec;
173 0           $grammar[$i++] = $lhs;
174              
175 0           $parser->{ntflag}->[$lhs] = !$istok;
176              
177 0           my $epsilonrule_flag = 1;
178 0           foreach my $j (0..$#$rhs) {
179 0           my $sym = $rhs->[$j];
180 0           my $isnonterminal = ! $self->decide_token($sym);
181              
182 0 0         if (ref $sym) {
183 0           print "SYM=$sym\n";
184 0           print "ref=".(ref $sym)."\n";
185 0 0         print "yes\n" if (scalar(ref $sym) =~ /^perl/);
186 0 0         if (scalar(ref $sym) =~ /^perl/) {
187 0           $sym = eval "sub { my \@v = \@_; $$sym; }";
188             } else {
189 0     0     $sym = sub { print STDERR "Unrunnable ".(ref $sym)." called\n" };
  0            
190             }
191              
192             # Code
193 0           my $codesym = '@'.(++$code_ctr);
194 0           $code[$code_ctr] = $sym;
195 0           $parser->{rule_code}->{$rulepos} = $sym;
196 0           $sym = $parser->{symmap}->get_index($codesym);
197 0           $parser->{codesyms}->{$sym} = $codesym; # Used as boolean map
198              
199 0           $isnonterminal = '(code)';
200 0 0         if ($j != $#$rhs) {
201 0           push(@{$data->{rules}}, [ $codesym, [ ] ]);
  0            
202             } else {
203 0           $parser->{end_action_symbols}->{$sym} = 1;
204 0           $parser->{ntflag}->[$sym] = $isnonterminal;
205 0           next;
206             }
207              
208             } else {
209 0           $sym = $parser->{symmap}->get_index($sym);
210 0           $epsilonrule_flag = 0;
211             }
212              
213 0           $grammar[$i++] = $sym;
214 0           $parser->{ntflag}->[$sym] = $isnonterminal;
215             }
216              
217 0 0         push(@epsilonrules, $rulepos) if $epsilonrule_flag;
218              
219 0           $grammar[$i++] = $nil;
220             }
221              
222             # Must do this while we can still muck with the symmap
223 0           $parser->{grammar} = \@grammar;
224 0           $self->compute_precedence($data->{precedence}, \%ruleprecs);
225              
226 0           $parser->{nilvec} = $parser->{symmap}->make_onevec($nil);
227 0           my $endvec = $parser->{symmap}->make_onevec($end);
228              
229 0           my $bogus;
230 0           ($parser->{init_state}) =
231             $self->fetch_or_create_state([ [ $parser->new_item(1, $endvec), undef ] ], undef);
232              
233             # Compute chainrules
234 0           foreach my $rule (@rules) {
235 0           my $lhs = $grammar[$rule];
236 0           my $rhs0 = $grammar[$rule + 1];
237 0 0         if ($parser->is_nonterminal($rhs0)) {
238 0           push(@{$chainrules{$lhs}->{$rhs0}}, bless [ \%chain::FIELDS,
  0            
239             $rule,
240             undef ], 'chain');
241             }
242             }
243              
244             # For debugging: describe how to print out chainrules
245             $parser->register_dump('chain' => sub {
246 0     0     my ($self, $chain, $asXML) = @_;
247 0           $self->dump_rule($chain->{RULEIDX}, undef, $asXML)." F=".
248             $self->dump_symvec($chain->{FIRSTLA}, $asXML);
249 0           });
250              
251 0           $parser->{code} = \@code;
252 0           $parser->{rules} = \@rules;
253 0           $parser->{rulenum} = { map { $rules[$_] => $_ } 0 .. $#rules };
  0            
254 0           $parser->{ruletable} = \%rules; # { A => [ rule A -> ... ] }
255 0           $parser->{epsilonrules} = \@epsilonrules;
256 0           $self->{chainrules} = \%chainrules;
257 0           $parser->{nonterminals} =
258 0           [ grep { $parser->{ntflag}->[$_] } 0 .. $#{$parser->{ntflag}} ];
  0            
259 0           $parser->{tokens} =
260 0           [ grep { !$parser->{ntflag}->[$_] } 0 .. $#{$parser->{ntflag}} ];
  0            
261             }
262              
263             # compute_precedence
264             #
265             # INPUT:
266             # $precinfo : [ precedence layer ]
267             # $hardcoded : { rule => symbol to inherit precedence from or "" }
268             # precedence layer :
269             # $parser->{grammar} : see above
270             #
271             # OUTPUT:
272             # $parser->{precedence} : [ token => ]
273             # $parser->{rule_precedence} : [ rule => ]
274             #
275             sub compute_precedence {
276 0     0 0   my Parse::YALALR::Build $self = shift;
277 0           my Parse::YALALR::Parser $parser = $self->{parser};
278 0           my ($precinfo, $hardcoded) = @_;
279              
280             # Grab out the info from the precedence declarations
281 0           my $prec = 0;
282 0           foreach my $preclayer (@$precinfo) {
283 0           my ($assoc, $tokens) = @$preclayer;
284 0 0         $assoc = 'none' if $assoc eq 'token';
285 0           foreach my $token (@$tokens) {
286 0           $token = $parser->{symmap}->get_index($token);
287 0           $parser->{precedence}->[$token] = [ $prec, $assoc ];
288             # print "Token precedence($token) := $prec ($assoc)\n";
289             }
290             } continue {
291 0           $prec++;
292             };
293              
294             # Compute the rule precedences.
295             # It is the precedence of the %prec token, if given. Otherwise it
296             # is the precedence of the last terminal, if any. Otherwise it is
297             # undefined.
298 0           my $nil = $parser->{nil};
299 0           my $rule;
300             my $lastterm;
301 0           for (my $i = 0; $i < @{$parser->{grammar}}; $i++) {
  0            
302 0           my $sym = $parser->{grammar}->[$i];
303 0 0         if ($sym == $nil) {
    0          
304 0           my $hard = $hardcoded->{$rule};
305 0 0 0       if (defined $hard && $hard ne '') {
    0          
306 0           my $p = $parser->{rule_precedence}->[$rule] =
307             $parser->{precedence}->[$parser->{symmap}->get_index($hard)];
308             } elsif (defined $lastterm) {
309 0           my $p = $parser->{rule_precedence}->[$rule] =
310             $parser->{precedence}->[$lastterm];
311             }
312 0           undef $rule;
313 0           undef $lastterm;
314             } elsif (!defined $rule) {
315 0           $rule = $i;
316             } else {
317 0 0         $lastterm = $sym if $parser->is_token($sym);
318             }
319             }
320             }
321              
322             sub isdef {
323 0     0 0   my %x = @_;
324 0           while (my ($name, $val) = each %x) {
325 0 0         print "$name is ", (defined $val ? 'defined' : 'undefined'), "\n";
326             }
327             }
328              
329             # method FIRST(vec1 vec2 vec3...)
330             #
331             # Returns a vector of FIRST(vec1 vec2 vec3...)
332             # Will include nil if all vectors contain nil.
333             #
334             sub FIRST {
335 0     0 0   my $self = shift;
336 0           my Parse::YALALR::Parser $parser = $self->{parser};
337 0           my $nil = $parser->{nil};
338 0           my $first = shift;
339 0 0         if (ref $first) {
340 0           croak("FIRST(ref ".(ref $first).") called");
341             }
342              
343 0           my $second;
344 0   0       while (vec($first, $nil, 1) && defined($second = shift)) {
345 0           vec($first, $nil, 1) = 0; # Clear out the epsilon
346 0           $first |= $second; # first will only contain nil if second has it
347             }
348              
349 0           return $first;
350             }
351              
352             # method FIRST_nonvec(A B C...)
353             #
354             # Returns a vector of FIRST(A B C...)
355             # where the arguments are symbols. Will include nil if all symbols given
356             # are nullable.
357             #
358             sub FIRST_nonvec {
359 0     0 0   my Parse::YALALR::Build $self = shift;
360 0           my Parse::YALALR::Parser $parser = $self->{parser};
361 0           my $A = shift;
362 0           my $nil = $parser->{nil};
363 0           my $nilvec = $parser->{nilvec};
364 0           my $symmap = $parser->{symmap};
365              
366 0 0         return $nilvec if !defined $A;
367              
368 0           my $first;
369 0 0         if ($parser->is_nonterminal($A)) {
370 0           $first = $self->{FIRST}->{$A};
371             } else {
372 0           $first = $symmap->make_onevec($A);
373             }
374              
375 0           my $next;
376 0   0       while (vec($first, $nil, 1) && defined($next = shift)) {
377 0           vec($first, $nil, 1) = 0; # Clear out the epsilon
378 0 0         if ($parser->is_nonterminal($next)) {
379 0           $next = $self->{FIRST}->{$next};
380             } else {
381 0           $next = $symmap->make_onevec($next);
382             }
383 0           $first |= $next;
384             }
385              
386 0           return $first;
387             }
388              
389             # Could a (small) n^2 be removed by computing all of these at once?
390             # Or are few asked for? (Guess so; doesn't show up in profiling)
391             sub get_first_nextalpha {
392 0     0 0   my ($self, $I) = @_;
393 0           return $self->FIRST_nonvec($self->parser->get_dotalpha($I->{GRAMIDX} + 1));
394             }
395              
396             sub hidden_shift {
397 0     0 0   my ($self, $rule, $first) = @_;
398 0           return [ $rule + 1, $first ];
399             }
400              
401             # fetch_or_create_state
402             #
403             # Args:
404             # items: [ ]
405             # The source item is the Real item; the generated item is just a holder
406             # for the necessary information (specifically, a GRAMIDX and a lookahead set)
407             # and that reference will never be used inside any real state.
408             # source_state: The state that caused this set of items to be generated.
409             #
410             # $self->{quickstate} : [ item_ofs => state ]
411             # state : { id => id, items => [ item ],
412             # la_effects => [ items index => [ item ] ] }
413             # where item_ofs is the first item state->{items}->[0]
414             #
415             # la_effects is the set of outward edges from a kernel item to the
416             # kernel items of other states that the lookaheads should propagate to.
417             #
418             # Returns:
419             # state in scalar context, in list context
420             # - state is the state generated
421             # - changes is undefined if the state was created from scratch,
422             # otherwise a reference to a (probably empty) list of items
423             # whose lookaheads changed
424             #
425             sub fetch_or_create_state {
426 0     0 0   my Parse::YALALR::Build $self = shift;
427 0           my Parse::YALALR::Parser $parser = $self->{parser};
428 0           my ($edges, $source_state) = @_;
429 0 0         croak("must have at least one item") if @$edges == 0;
430              
431             # $DB::single = 1 if defined $source_state && $source_state->{id} == 4;
432              
433             # Canonicalize $edges -> @canon_items by removing duplicates
434              
435             # { GRAMIDX of generated item => lookahead for item }
436 0           my %canon_items;
437              
438             # { GRAMIDX of generated item => [ causing item, la, lawhy ] }
439             # lawhy : <'generated'|'propagated'|'epsilon-generated', causeidx, la>
440             my %causes;
441              
442             # { GRAMIDX of generating item that propagates its lookaheads => boolean }
443 0           my %propagating_cause;
444              
445 0           for my $edge (@$edges) {
446 0           my ($item, $cause) = @$edge;
447              
448 0 0         if (defined $cause) {
449 0           my $cause_restla = $self->get_first_nextalpha($cause);
450 0 0         $propagating_cause{$cause->{GRAMIDX}} = 1
451             if vec($cause_restla, $parser->{nil}, 1);
452             }
453              
454 0           my $idx = $item->{GRAMIDX};
455 0 0         if (exists $canon_items{$idx}) {
456 0           $canon_items{$idx} |= $item->{LA};
457             } else {
458 0           $canon_items{$idx} = $item->{LA};
459             }
460              
461 0 0         if ($self->{why}) {
462 0           while (my ($la, $lawhy) = each %{$item->{LA_WHY}}) {
  0            
463 0           push(@{$causes{$idx}}, [ $cause, $la, $lawhy ]);
  0            
464             }
465             } else {
466 0           push(@{$causes{$idx}}, [ $cause ] );
  0            
467             }
468             }
469              
470             # 96-bit hash value
471             # TODO: Compute a hash of the set of items in a state.
472             # It would be nice if the hash were insensitive to the order
473             # of items in the set. We don't need 96 bits if we do a pairwise
474             # comparison to check for sure, but we could get away with a
475             # simple hash -> state table instead of hash -> [ state ] if
476             # we use lots of bits. (96 bits means less than 1 chance in a million
477             # of getting a collision with 256,000 states. Assuming a truly random
478             # hash function, which this is nowhere close to.)
479 0           my $h1 = 0;
480 0           my $h2 = 0;
481 0           my $h3 = 0;
482              
483             # Order-independent hash
484             {
485 1     1   1590 use integer;
  1         13  
  1         5  
  0            
486 0           foreach (keys %canon_items) {
487 0           $h1 ^= (($_ + 1) * 149706587);
488 0           $h2 ^= (($_ + 1) * 4243838327);
489 0           $h3 ^= (($_ + 1) * 1347946109);
490             }
491             }
492              
493 0           my $hash = pack("LLL", $h1, $h2, $h3);
494 0           my $fetched = $self->{quickstate}->{$hash};
495              
496             # Found it!
497 0 0         if (defined $fetched) {
498              
499 0           for my $fitem (@{$fetched->{items}}) {
  0            
500              
501             # Merge lookaheads
502 0           my $merge = $canon_items{$fitem->{GRAMIDX}};
503 0           $fitem->{LA} |= $merge;
504              
505             # Add in the new edges to the item lookahead dependency graph
506 0           for my $cause (@{$causes{$fitem->{GRAMIDX}}}) {
  0            
507 0           my ($src_item, $la, $lawhy) = @$cause;
508              
509 0 0         if ($self->{why}) {
510 0           push(@{ $src_item->{DESTS} }, $fitem);
  0            
511 0           push(@{ $fitem->{SOURCES} }, $src_item);
  0            
512 0 0         $DB::single = 1 if $fitem->{GRAMIDX} == 35;
513 0 0         if ($fetched != $source_state) {
514 0           $fitem->{LA_WHY}->{$la} = $lawhy;
515             }
516             }
517              
518 0 0         next if ! $propagating_cause{$cause->[0]->{GRAMIDX}};
519 0           $lawhy->[1] = $src_item;
520 0           $self->add_item_edge($source_state, $src_item,
521             $fetched, $fitem,
522             $la => $lawhy);
523             }
524             }
525 0           return ($fetched);
526             }
527              
528             # Didn't find it, create a new state.
529              
530             # Create the items in the new state. These will be the Real items if
531             # the state is new, otherwise, they're just stores for the information
532             # to be merged into the fetched state.
533 0           my @canon_items;
534 0           while (my ($idx, $la) = each %canon_items) {
535 0           push(@canon_items, bless [ \%item::FIELDS, $idx, $la ], 'item');
536             }
537 0           @canon_items = sort { $a->{GRAMIDX} <=> $b->{GRAMIDX} } @canon_items;
  0            
538             # FIXME
539              
540             # Create the new state itself
541 0           my $state = Parse::YALALR::Kernel->new($parser, \@canon_items);
542              
543             # Register each item in the kernel with the causing kernel item
544             # in the source state.
545 0 0         if (defined $source_state) {
546 0           foreach my $item (@canon_items) {
547 0           foreach my $cause (@{$causes{$item->{GRAMIDX}}}) {
  0            
548 0           my ($src_item, $la, $lawhy) = @$cause;
549              
550 0 0         if ($self->{why}) {
551 0           push(@{ $src_item->{DESTS} }, $item);
  0            
552 0           push(@{ $item->{SOURCES} }, $src_item);
  0            
553 0 0         $DB::single = 1 if $item->{GRAMIDX} == 35;
554 0 0         die if $item == $lawhy->[1];
555 0           $item->{LA_WHY}->{$la} = $lawhy;
556             }
557              
558             # Check whether the source_state is A -> \alpha . X \beta,
559             # where \beta is nullable. If so, any change in the lookaheads
560             # of the source_state should be propagated to the state
561             # being created.
562 0 0         next unless $propagating_cause{$src_item->{GRAMIDX}};
563              
564 0           $lawhy->[1] = $src_item;
565             # print STDERR "$src_item->{GRAMIDX} $lawhy->[2]\n";
566 0           $self->add_item_edge($source_state, $src_item,
567             $state, $item,
568             $la => $lawhy);
569             }
570             }
571             }
572              
573 0 0         if ($self->{why}) {
574             # Fill in the map from GRAMIDX -> [ kernel item ]
575 0           for my $item (@canon_items) {
576 0           push(@{ $parser->{items}->{$item->{GRAMIDX}} }, $item);
  0            
577             }
578             }
579              
580 0           $self->{quickstate}->{$hash} = $state;
581 0           $parser->{states}->[$state->{id}] = $state;
582 0           return ($state, 1);
583             }
584              
585             sub compute_NULLABLE {
586 0     0 0   my Parse::YALALR::Build $self = shift;
587 0           my Parse::YALALR::Parser $parser = $self->{parser};
588 0           my $grammar = $parser->{grammar};
589 0           my $nil = $parser->{nil};
590              
591             # { B => [ A, rule A -> B..., item A -> . B... ] }
592 0           my %might_cause_nullable;
593 0           foreach my $nt (@{$parser->{nonterminals}}) {
  0            
594 0           $might_cause_nullable{$nt} = []; # Avoid @{undef}
595             }
596              
597             # Set up the might_cause_nullable cache
598 0           RULE: foreach my $rule (@{$parser->{rules}}) {
  0            
599 0           my $item = $rule;
600 0 0         next if $grammar->[$item + 1] == $nil;
601              
602 0           my $rhssym;
603 0           while (($rhssym = $grammar->[++$item]) != $nil) {
604 0 0         next RULE if $parser->is_token($rhssym);
605             }
606              
607 0           push(@{$might_cause_nullable{$grammar->[$rule + 1]}},
  0            
608             [ $grammar->[$rule], $rule, $rule + 1 ]);
609             }
610              
611             # Go through the epsilon rules and set the immediately nullable ones,
612             # but also push stuff on the queue
613 0           my @mightq;
614             my %nullable;
615 0           my %why_nullable;
616 0           foreach my $rule (@{$parser->{epsilonrules}}) {
  0            
617 0           my $lhs = $grammar->[$rule];
618 0 0         next if $nullable{$lhs};
619 0           $nullable{$lhs} = 1;
620 0           $why_nullable{$lhs} = $rule;
621 0           push(@mightq, @{$might_cause_nullable{$lhs}});
  0            
622 0           $might_cause_nullable{$lhs} = [];
623             }
624              
625 0           foreach my $nulsym (keys %{ $parser->{end_action_symbols} }) {
  0            
626 0           $nullable{$nulsym} = 1;
627 0           $why_nullable{$nulsym} = "is an action";
628 0           push(@mightq, @{$might_cause_nullable{$nulsym}});
  0            
629 0           $might_cause_nullable{$nulsym} = [];
630             }
631              
632 0           while (my $might = pop(@mightq)) {
633 0           my ($nullcand, $rule, $dot) = @$might;
634 0 0         next if $nullable{$nullcand};
635              
636             # Skip other nullable symbols
637 0           ++$dot;
638 0   0       ++$dot while ($grammar->[$dot] != $nil && $nullable{$grammar->[$dot]});
639              
640             # If still some non-nullable symbols left, put it back on the
641             # might_cause_nullable map.
642 0 0         if ($grammar->[$dot] != $nil) {
643 0           push(@{$might_cause_nullable{$grammar->[$dot]}},
  0            
644             [ $nullcand, $rule, $dot ]);
645             } else {
646             # Found new nullable symbol! Push its stuff onto the list
647 0           my $nulledsym = $grammar->[$rule];
648              
649             # Now wait a minute! We might have already figured this out from
650             # something else on the list! (Stupid kids...)
651 0 0         if (!$nullable{$nulledsym}) {
652 0           $nullable{$nulledsym} = 1;
653 0           $why_nullable{$nulledsym} = $rule;
654 0           push(@mightq, @{$might_cause_nullable{$nulledsym}});
  0            
655 0           $might_cause_nullable{$nulledsym} = [];
656             }
657             }
658             }
659              
660 0           $self->{nullable} = \%nullable;
661 0 0         $self->{why_nullable} = \%why_nullable if $self->{why};
662             }
663              
664             sub nullable_vec {
665 0     0 0   my ($self, $vec) = @_;
666 0           return vec($vec, $self->{nullable}, 1);
667             }
668              
669             # optimize by keeping only one A goesto B rule.
670             sub compute_FIRST {
671 0     0 0   my Parse::YALALR::Build $self = shift;
672 0           my Parse::YALALR::Parser $parser = $self->{parser};
673 0           my $grammar = $parser->{grammar};
674 0           my $nullable = $self->{nullable};
675 0           my $nil = $parser->{nil};
676              
677             # WHY_FIRST : { A => { t => } }
678             # where reason : 'nullable'|'propagated'
679             #
680             # reason = 'nullable':
681             # t is in FIRST(A) because rule A : \a1 t \a2 and NULLABLE(\a1)
682             # reason = 'propagated', parent = B
683             # t is in FIRST(A) because rule A : \a3 B \a4
684             # and NULLABLE(\a3) and t is in FIRST(B)
685             #
686 0           my %WHY_FIRST;
687             my %FIRST;
688              
689             my $add_to_first = sub {
690 0     0     my ($sym, $tok, $rule, $parent) = @_;
691 0 0         $FIRST{$sym} = "" if !defined $FIRST{$sym};
692 0           vec($FIRST{$sym}, $tok, 1) = 1;
693 0 0         if ($self->{why}) {
694 0 0         if (defined $parent) {
695 0 0         my $reason = ($tok == $parent) ? 'nullable' : 'propagated';
696 0           $WHY_FIRST{$sym}->{$tok} = [ $rule, $reason, $parent ];
697             # print "Set WHY_FIRST{".$parser->dump_sym($sym)."=>{".$parser->dump_sym($tok)."=> <".$parser->dump_rule($rule).",$reason,".$parser->dump_sym($parent).">}}\n";
698             } else {
699 0           $WHY_FIRST{$sym}->{$tok} = [ $rule ];
700             }
701             }
702 0           };
703              
704             # Initialize FIRST of all nonterminals to the empty set. This
705             # isn't used below, but will eliminate uses of undefined values
706             # later.
707              
708 0           foreach my $sym (@{$parser->{nonterminals}}) {
  0            
709 0           $FIRST{$sym} = '';
710             }
711              
712             # Set up the goesto graph.
713             # goesto{A} = [ B -> \alpha . A \beta ] means that
714             # FIRST(B) \contains FIRST(A) because \alpha is nullable.
715              
716 0           my %goesto;
717 0           foreach my $rule (@{$parser->{rules}}) {
  0            
718 0           my $item = $rule + 1;
719 0           while ($grammar->[$item] != $nil) {
720 0           my $sym = $grammar->[$item++];
721 0           push(@{$goesto{$sym}}, $rule);
  0            
722 0 0 0       last if $parser->is_token($sym) || !$nullable->{$sym};
723             }
724             }
725              
726             # Foreach token, do a BFS of the goesto graph, propagating the
727             # token to the FIRST sets of everything reached.
728             #
729             # Default all WHY_FIRSTs to 'propagated'
730 0           for my $tok (@{$parser->{tokens}}) {
  0            
731 0           my %visited;
732             my @queue;
733 0           push(@queue, \$tok); # Push a marker on
734 0 0         push(@queue, @{$goesto{$tok}}) if defined $goesto{$tok};
  0            
735 0           my $parent;
736 0           while (defined(my $x = shift(@queue))) {
737 0 0         if (ref $x) {
738 0           $parent = $$x;
739             } else {
740 0           my $rule = $x;
741 0           my $sym = $grammar->[$rule];
742 0 0         if (!$visited{$sym}) {
743 0           $visited{$sym} = 1;
744 0           $add_to_first->($sym, $tok, $rule, $parent);
745 0 0         if (defined $goesto{$sym}) {
746 0           push(@queue, \$sym);
747 0           push(@queue, @{$goesto{$sym}})
  0            
748             }
749             }
750             }
751             }
752             }
753              
754             # epsilons need to be in FIRST sets also. But they're trivial
755             # with NULLABLE.
756 0 0         if ($self->{why}) {
757 0           foreach (keys %$nullable) {
758 0           $add_to_first->($_, $nil, $self->{why_nullable}->{$_});
759             }
760             } else {
761 0           foreach (keys %$nullable) {
762 0           $add_to_first->($_, $nil);
763             }
764             }
765              
766 0           $self->{FIRST} = \%FIRST;
767 0           $self->{WHY_FIRST} = \%WHY_FIRST;
768             }
769              
770             # Chain rules
771             #
772             # $self->{chainrules} = { A => { B => [ B \alpha,FIRST(\alpha)> ] } }
773             #
774             # INCORRECT:
775             # $self->{chainreachable} = { A => { B => [ B \beta2, FIRST(\beta1)> ] }}
776             # where A ->* X \beta1
777             # X -> B \beta2 (\beta1 is the accumulation of symbols required to
778             # reach X, which produces B)
779             #
780             # CORRECT: See the description later
781             #
782              
783              
784             # $self->{chainrules} = { nt_A => { nt_B => [ < rule, first > ] } }
785             # aka { A => { B => [ < A -> B \alpha, FIRST(\alpha) > ] } }
786              
787             # Should really convert this to on-demand someday, too
788             sub compute_chainFIRSTs {
789 0     0 0   my Parse::YALALR::Build $self = shift;
790 0           my Parse::YALALR::Parser $parser = $self->{parser};
791 0           my $grammar = $parser->{grammar};
792 0           my $chainrules = $self->{chainrules};
793 0           my $nil = $parser->{nil};
794              
795 0           foreach my $X (values %$chainrules) {
796 0           foreach my $cruleset (values %$X) {
797 0           foreach my $crule (@$cruleset) {
798              
799             # Point to B in A -> B x y z, will incr to x before using
800 0           my $i = $crule->{RULEIDX} + 1;
801              
802 0           my @rhs;
803 0           while ($grammar->[++$i] != $nil) {
804 0           push(@rhs, $grammar->[$i]);
805             }
806 0           $crule->{FIRSTLA} = $self->FIRST_nonvec(@rhs);
807             }
808             }
809             }
810             }
811              
812             # chainreachable: {A => {B => rule} } means A ->* B \alpha, where
813             # no nonterminals died to get to B \alpha (== last rule in leftmost
814             # derivation was not epsilon rule, so no A -> C B x -> B x). The rule
815             # given is just some rule C -> B \beta, where C is reachable from A
816             # in zero or more steps. Mostly used as a boolean flag, but can be
817             # helpful for why.
818             #
819             # chainfirst: {A => {B => firstvec} } means firstvec is the union of the
820             # FIRST of all \alpha in A ->* B \alpha (no nonterminals die). It will
821             # be used for expanding X -> something1 . A something2, f1: this generates
822             # B -> ..., FIRST(\beta something2 f1) when A ->* B \beta (no dead nts).
823             #
824             sub compute_chain {
825 0     0 0   my Parse::YALALR::Build $self = shift;
826 0           my Parse::YALALR::Parser $parser = $self->{parser};
827 0           my ($A) = @_;
828 0           my $chainrules = $self->{chainrules};
829              
830 0           my @todo;
831             my %chainreachable;
832 0           my %first;
833              
834 0           my $nullfs = $parser->{symmap}->make_nullvec;
835              
836 0           push(@todo, $A);
837 0           while (my $X = pop(@todo)) {
838 0           my $pushed = 0;
839 0           foreach my $B (keys %{$chainrules->{$X}}) {
  0            
840 0 0         if (!exists $chainreachable{$B}) {
841 0           $chainreachable{$B} = $chainrules->{$X}{$B}->[0]->{RULEIDX};
842 0           push(@todo, $B);
843 0           $pushed = 1;
844             }
845              
846 0   0       my $oldfs = $first{$B} || $nullfs;
847 0           foreach my $crule (@{$chainrules->{$X}{$B}}) {
  0            
848 0           my $propfs = $self->FIRST($crule->{FIRSTLA}, $first{$X});
849 0           my $newfs = $propfs | $oldfs;
850 0 0         if (($newfs & ~$oldfs) !~ /^\0*$/s) {
851 0           $first{$B} = $newfs;
852 0 0         push(@todo, $B) unless $pushed;
853 0           $pushed = 1;
854             # why_chain_la(A)(B)(propfs & ~oldfs) = crule->{RULEIDX}
855             }
856             }
857             }
858             }
859              
860 0           $self->{chainreachable}->{$A} = \%chainreachable;
861 0           $self->{chainfirst}->{$A} = \%first;
862             }
863              
864             # This function should go away someday
865             sub compute_chains {
866 0     0 0   my Parse::YALALR::Build $self = shift;
867 0           my Parse::YALALR::Parser $parser = $self->{parser};
868 0           foreach my $nt (@{$parser->{nonterminals}}) {
  0            
869 0           $self->compute_chain($nt);
870             }
871             }
872              
873             # generate_parser
874             #
875             # Main entry point for creating a parser. Uses a bunch of precomputed data.
876             #
877             # Algorithm:
878             # Do a BFS creation of the state graph
879             # For each state (== kernel) during the BFS construction
880             # Foreach item in the kernel
881             # If it's a reduce, call add_reduce(kernel, lhs symbol, lookahead)
882             # If it's A => \a1 . t \a2, push(shifto[t], new kernel)
883             # If it's A => \a1 . B \a2, do the same as above,
884             # but also handle everything reachable from B => . \a3 (see below)
885             # Scan through the complete shifto sets and fetch or create the new
886             # kernel resulting from the shift (I'm including both terminals
887             # and nonterminals in shifto, as usual).
888             # If the kernel is new, enqueue it.
889             # The lookaheads for the reduces are tricky. SEE BELOW.
890             #
891             # The tricky part is handling the implicit kernel expansion. We have
892             # A => \a1 . B \a2, f1 (f1 is the lookahead)
893             #
894             # Let f2 = FIRST(\a2 with lookahead f1)
895             #
896             # Do a simple reduce|shift action, as above, for all rules
897             # B => \a3, f2
898             #
899             # Then, foreach X such that B =>+ X \a4 (use $self->{chainreachable} to find),
900             # let f = FIRST(\a4 f2) (FIRST(\a4) is $self->{chainfirst})
901             # Do the reduce|shift actions for each rule X -> \a5, using f as the lookahead
902             #
903             # To illustrate, we have something like:
904             # A => \a1 . B \a2, f1
905             # B => . X \a4, f2=FIRST(\a2 f1)
906             # X => \a5, FIRST(\a4 f2)
907             #
908             # (in general, B =>* . X \a4)
909             #
910             sub generate_parser {
911 0     0 0   my Parse::YALALR::Build $self = shift;
912 0           my Parse::YALALR::Parser $parser = $self->{parser};
913 0           my $grammar = $parser->{grammar};
914 0           my $nil = $parser->{nil};
915 0           my $nilvec = $parser->{nilvec};
916              
917 0           my %epsilon_items; # state id => [ generated item X -> . ]
918              
919             my @kq;
920 0           push(@kq, $parser->{init_state}); # START -> . S, $;
921              
922 0           while (defined(my $K = pop(@kq))) {
923 0           my @epsilon_items;
924              
925             my %shifto; # { symbol => [ item ] }
926 0           my %shifto_why; # { symbol => }
927              
928 0           KERNEL_ITEM:
929 0           foreach my $I (@{$K->{items}}) {
930 0           my $next = $parser->get_dot($I);
931             # If rule is A -> \a1 . then add a reduce and go to the next item
932 0 0         if ($next == $nil) {
933             # Off end. Reduce.
934 0           my $lhs = $I->{GRAMIDX};
935             # Find symbol to reduce to
936 0   0       while ($lhs > 0 && $grammar->[$lhs - 1] != $nil) { $lhs--; }
  0            
937             # FIXME: Add assertion that kernel item is not A -> .
938 0           $parser->add_reduce($K, $lhs, $I->{LA}, $I, 'kernel');
939 0           next KERNEL_ITEM;
940             }
941              
942             # Nope, so rule is A -> \a1 . X \a2
943 0           my $I2 = $parser->get_shift($I);
944 0           push(@{$shifto{$next}}, [ $I2, $I ]);
  0            
945 0 0         $DB::single = 1 if $I2->{GRAMIDX} == 35;
946 0 0         $I2->{LA_WHY}->{$I->{LA}} = [ 'propagated', $I ]
947             unless $I2->{GRAMIDX} == $I->{GRAMIDX};
948 0           $shifto_why{$next} = [ $I2, 'kernel' ];
949              
950             # If X is a terminal, no need to expand
951 0 0         next KERNEL_ITEM if $parser->is_token($next);
952              
953             # In fact, rule is A -> \a1 . B \a2, f1 (B is nonterminal)
954             # Oh boy. Chain rules.
955 0           my $B = $next; # Just renaming
956 0           my $f1 = $parser->get_la($I);
957 0           my $a2 = $self->get_first_nextalpha($I);
958 0           my $F_a2_f1 = $self->FIRST($a2, $f1);
959              
960             # $item_prop is the item to blame for lookaheads in the
961             # reduces that will be added. It's just $I or undef. We'll
962             # undef it as soon as we hit something non-nullable.
963 0           my $item_prop = $I;
964 0 0         undef $item_prop if !$self->nullable_vec($a2);
965              
966             # First, handle the rules for B (if B ->+ B..., then we'll
967             # visit B again in the following loop, but for now we
968             # just want B -> . \a3, FIRST(\a2 f1))
969 0           foreach my $rule ($parser->get_rules($B)) {
970 0           my $x = $grammar->[$rule+1];
971              
972 0 0         if ($x == $nil) {
973 0           my $eI = $parser->make_item($rule, 0, $F_a2_f1);
974 0 0         $DB::single = 1 if $eI->{GRAMIDX} == 35;
975 0 0         $eI->{LA_WHY}->{$F_a2_f1} =
976             [ 'epsilon-generated', $I, $parser->{nilvec}, $a2 ]
977             unless $eI->{GRAMIDX} == $I->{GRAMIDX};
978 0           push(@epsilon_items, $eI);
979 0           $parser->add_reduce($K, $rule, $F_a2_f1, $I, 'chained');
980             } else {
981             # I2 := B -> . \a3, FIRST(\a2 f1)
982 0           my $I2 = $parser->make_shift($rule + 1, $F_a2_f1);
983 0           push(@{$shifto{$x}}, [ $I2, $I ]);
  0            
984 0 0         $DB::single = 1 if $I2->{GRAMIDX} == 35;
985 0 0         $I2->{LA_WHY}->{$F_a2_f1} = [ 'generated', $I, $a2 ]
986             unless $I2 == $I;
987             # Don't use this explanation if there's a simpler
988 0 0 0       if ($self->{why} && !defined $shifto_why{$x}) {
989 0           $shifto_why{$x} = [ $I2, 'chained', $rule + 1 ];
990             }
991             }
992             }
993              
994 0           foreach my $X (keys %{$self->{chainreachable}{$B}}) {
  0            
995             # f3 = FIRST(everything up to just before \a2)
996 0   0       my $f3 = $self->{chainfirst}->{$B}{$X} || $nilvec;
997 0           my $f = $self->FIRST($f3, $F_a2_f1);
998             # undefine $item_prop if FIRST(f3 a2) doesn't contain
999             # epsilon. It'll already be undef if a2 is not
1000             # nullable, so just test f3
1001 0 0         undef $item_prop if !$self->nullable_vec($f3);
1002 0           foreach my $rule ($parser->get_rules($X)) {
1003 0           my $x = $grammar->[$rule+1];
1004              
1005 0 0         if ($x == $nil) {
1006 0           my $eI = $parser->make_item($rule, 0, $f);
1007 0 0         $DB::single = 1 if $eI->{GRAMIDX} == 35;
1008 0 0         $eI->{LA_WHY}->{$f} =
1009             [ 'epsilon-generated', $I, $f3, $a2 ]
1010             unless $eI->{GRAMIDX} == $I->{GRAMIDX};
1011              
1012 0           push(@epsilon_items, $eI);
1013 0           $parser->add_reduce($K, $rule, $f, $I, 'chained');
1014             } else {
1015 0           my $I2 = $parser->make_shift($rule + 1, $f);
1016 0           push(@{$shifto{$x}}, [ $I2, $I ]);
  0            
1017 0 0         $DB::single = 1 if $I2->{GRAMIDX} == 35;
1018 0 0         $I2->{LA_WHY}->{$f} = [ 'chain-generated', $I, $f3, $a2 ]
1019             unless $I2->{GRAMIDX} == $I->{GRAMIDX};
1020              
1021             # Don't use this explanation if there's a simpler
1022 0 0 0       if ($self->{why} && !defined $shifto_why{$x}) {
1023 0           $shifto_why{$x} = [ $I2, 'chained', $self->{chainreachable}{$B}{$X} + 1 ];
1024             }
1025             }
1026             }
1027             }
1028             } # foreach item I in kernel K
1029              
1030             # Merge epsilon items with the same core
1031 0           my %canonical; # { GRAMIDX => item }
1032 0           for my $item (@epsilon_items) {
1033 0 0         if (exists $canonical{$item->{GRAMIDX}}) {
1034             } else {
1035             }
1036             }
1037              
1038 0           $epsilon_items{$K->{id}} = \@epsilon_items;
1039              
1040             # Create all the new states and add shift actions
1041 0           while (my ($sym, $edges) = each %shifto) {
1042 0           my ($K2, $new) = $self->fetch_or_create_state($edges, $K);
1043 0           $parser->add_shift($K, $sym, $K2);
1044 0 0         $K->{SHIFT_WHY}->{$sym} = $shifto_why{$sym}
1045             if $self->{why};
1046              
1047             # Stick new states in the queue
1048 0 0         push(@kq, $K2) if $new;
1049             }
1050             }
1051              
1052 0 0         $self->create_item2state_map() if $self->{why};
1053 0 0         $self->effects_to_causes() if $self->{why};
1054 0           $self->propagate_lookaheads();
1055 0           $self->create_reduces();
1056             }
1057              
1058             sub add_item_edge {
1059 0     0 0   my Parse::YALALR::Build $self = shift;
1060 0           my Parse::YALALR::Parser $parser = $self->{parser};
1061 0           my ($K0, $I0, $K1, $I1, $la, $reason) = @_;
1062 0           $I0->{EFFECTS}->{$I1} = $I1;
1063 0 0 0       $I1->{LA_WHY}->{$la} ||= $reason
      0        
1064             if $self->{why} && $I0 != $I1;
1065             }
1066              
1067             sub propagate_lookaheads {
1068 0     0 0   my Parse::YALALR::Build $self = shift;
1069 0           my Parse::YALALR::Parser $parser = $self->{parser};
1070              
1071             # Start out search with all kernel items
1072 0           my @Q = map { @{$_->{items}} } @{$parser->{states}};
  0            
  0            
  0            
1073              
1074             # Keep track of what's already in the queue to avoid adding duplicates
1075 0           my %Q = map { $_ => 1 } @Q;
  0            
1076              
1077             # Keep propagating changes until equilibrium is reached
1078 0           while (my $change = shift(@Q)) {
1079 0           delete $Q{$change};
1080             # print "Propagating $change->{GRAMIDX}...\n";
1081 0           foreach (values %{$change->{EFFECTS}}) {
  0            
1082 0           (my $newla = $_->{LA}) |= $change->{LA};
1083 0 0         if ($newla ne $_->{LA}) {
1084 0 0         if ($self->{why}) {
1085 0           my $changela = $newla ^ $_->{LA};
1086 0 0 0       $DB::single = 1 if (vec($changela, $parser->{end}, 1) && $_->{GRAMIDX} == 27);
1087 0 0         if ($change->{GRAMIDX} + 1 == $_->{GRAMIDX}) {
1088 0 0         $_->{LA_WHY}{$changela} = [ 'propagated', $change ]
1089             unless $_->{GRAMIDX} == $change->{GRAMIDX};
1090             } else {
1091 0 0         $_->{LA_WHY}{$changela} = [ 'generated', $change ]
1092             unless $_->{GRAMIDX} == $change->{GRAMIDX};
1093             }
1094             }
1095 0           $_->{LA} = $newla;
1096 0 0         if (!exists $Q{$_}) {
1097 0           push(@Q, $_);
1098 0           $Q{$_} = 1;
1099             }
1100             }
1101             }
1102             }
1103             }
1104              
1105             # Update all reductions given the current lookaheads of the items they
1106             # depend upon.
1107             sub create_reduces {
1108 0     0 0   my Parse::YALALR::Build $self = shift;
1109 0           my Parse::YALALR::Parser $parser = $self->{parser};
1110 0           for my $K (@{$parser->{states}}) {
  0            
1111 0           for my $rinfo (@{$K->{reduces}}) {
  0            
1112 0           my ($la, $rule, $parent) = @$rinfo;
1113 0 0         if ($parent) {
1114 0           $rinfo->[0] |= $parent->{LA};
1115 0 0 0       if ($self->{why} && $rinfo->[0] ne $la) {
1116 0           $K->{REDUCE_WHY}->{$la ^ $rinfo->[0]} =
1117             [ $rule, $parent, 'propagated' ];
1118             }
1119             }
1120             }
1121             }
1122             }
1123              
1124             sub resolve_rr {
1125 0     0 0   my ($self, $state, $sym, $old, $new) = @_;
1126 0           my Parse::YALALR::Parser $parser = $self->{parser};
1127 0           my $id = $state->{id};
1128              
1129 0           my $prec1 = $parser->{rule_precedence}->[$old->[0]];
1130 0           my $prec2 = $parser->{rule_precedence}->[$new];
1131              
1132 0 0 0       if (defined $prec1 && defined $prec2 && $prec1->[0] != $prec2->[0]) {
      0        
1133 0           print "Precedence resolved reduce/reduce conflict in state $id on token ", $parser->dump_sym($sym), ": ";
1134 0 0         if ($prec1->[0] < $prec2->[0]) {
1135 0           print $parser->dump_rule($old->[0])."\n";
1136 0           return $old;
1137             } else {
1138 0           my $grammar = $parser->{grammar};
1139 0           print $parser->dump_rule($new)."\n";
1140 0           return bless [ $new, $grammar->[$new], $parser->rule_size($new) ],
1141             'reduce';
1142             }
1143             } else {
1144 0           print "Arbitrarily resolved reduce/reduce conflict in state $id on token ", $parser->dump_sym($sym), ": ",
1145             $parser->dump_rule($old->[0]), "\n";
1146 0           return $old;
1147             }
1148             }
1149              
1150             sub resolve_sr {
1151 0     0 0   my ($self, $state, $sym, $old, $new) = @_;
1152 0           my Parse::YALALR::Parser $parser = $self->{parser};
1153 0           my $id = $state->{id};
1154              
1155 0           my $prec1 = $parser->{precedence}->[$sym];
1156 0           my $prec2 = $parser->{rule_precedence}->[$new];
1157              
1158             # print "RESOLVING shift $sym vs rule $new\n";
1159              
1160 0           my $grammar = $parser->{grammar};
1161 0           my $reduce_rule = bless [ $new, $grammar->[$new], $parser->rule_size($new) ],
1162             'reduce';
1163              
1164 0 0 0       if (defined $prec1 && defined $prec2) {
1165 0 0         if ($prec1->[0] != $prec2->[0]) {
1166 0           print "Precedence resolved shift/reduce conflict in state $id on token ", $parser->dump_sym($sym), ": ";
1167 0 0         if ($prec1->[0] < $prec2->[0]) {
1168 0           print $parser->dump_action($old)."\n";
1169 0           return $old;
1170             } else {
1171 0           my $grammar = $parser->{grammar};
1172 0           print $parser->dump_rule($new)."\n";
1173 0           return $reduce_rule;
1174             }
1175             }
1176              
1177 0 0         if ($prec1->[1] eq 'left') {
    0          
    0          
1178 0           print "Left associativity resolved shift/reduce conflict in state $id on token ", $parser->dump_sym($sym), ": reduce\n";
1179 0           return $reduce_rule;
1180             } elsif ($prec1->[1] eq 'right') {
1181 0           print "Right associativity resolved shift/reduce conflict in state $id on token ", $parser->dump_sym($sym), ": shift\n";
1182 0           return $old;
1183             } elsif ($prec1->[1] eq 'nonassoc') {
1184 0           print "Nonassociative operator, resolved shift/reduce conflict in state $id on token ", $parser->dump_sym($sym), ": error\n";
1185 0           return undef;
1186             } else {
1187 0           die "What the hell is this?: $prec1->[1]";
1188             }
1189             }
1190              
1191 0           print "Arbitrarily resolved shift/reduce conflict in state $id on token ", $parser->dump_sym($sym), ": ",
1192             $parser->dump_action($old), "\n";
1193 0 0         print " (prec of ".$parser->dump_sym($sym)." is $prec1->[0] ($prec1->[1]))\n"
1194             if defined $prec1;
1195 0 0         print " (prec of rule is $prec2->[0] ($prec2->[1]))\n"
1196             if defined $prec2;
1197 0           return $old;
1198             }
1199              
1200             sub resolve {
1201 0     0 0   my ($self, $state, $sym, $old, $new) = @_;
1202 0           my Parse::YALALR::Parser $parser = $self->{parser};
1203 0 0 0       if ($old->[0] eq 'reduce' && $new->[0] eq 'reduce') {
    0 0        
1204 0           return $self->resolve_rr($state, $sym, $old->[1], $new->[1]);
1205             } elsif ($old->[0] eq 'shift' && $new->[0] eq 'reduce') {
1206 0           return $self->resolve_sr($state, $sym, $old->[1], $new->[1]);
1207             } else {
1208 0           return $self->resolve_sr($state, $sym, $new->[1], $old->[1]);
1209             }
1210             }
1211              
1212             # build_table
1213             #
1214             # INPUT:
1215             # $self->{states} : [ state ]
1216             # state : { 'id' => state number,
1217             # 'shifts' => { symbol => to_state },
1218             # 'reduces' => { lookahead => rule : grammar_index },
1219             # }
1220             #
1221             # OUTPUT:
1222             # $self->{states}[i]{actions} : [ symbol => shiftact|reduceact ]
1223             # (equiv, the above state += { 'actions' => [ symbol => shiftact|reduceact ] })
1224             # shiftact : to_state
1225             # reduceact : [ rule, lhs, number of elts in rhs ] : 'reduce'
1226             #
1227             sub build_table {
1228 0     0 0   my Parse::YALALR::Build $self = shift;
1229 0           my Parse::YALALR::Parser $parser = $self->parser;
1230 0           foreach my $state (@{$parser->{states}}) {
  0            
1231 0           my @actions;
1232 0           my $id = $state->{id};
1233 0           while (my ($sym, $dest) = each %{$state->{shifts}}) {
  0            
1234 0           $actions[$sym] = $dest;
1235             }
1236              
1237 0           foreach (@{$state->{reduces}}) {
  0            
1238 0           my ($la, $rule, $item) = @$_;
1239 0           foreach my $sym ($parser->{symmap}->get_indices($la)) {
1240 0 0         if (defined $actions[$sym]) {
1241 0 0         if (ref $actions[$sym] eq 'reduce') {
1242 0 0         if ($actions[$sym]->[0] != $rule) {
1243 0           $actions[$sym] =
1244             $self->resolve($state, $sym,
1245             [ 'reduce', $actions[$sym] ],
1246             [ 'reduce', $rule ]);
1247 0           next;
1248             } # else no conflict
1249             } else {
1250 0           $actions[$sym] =
1251             $self->resolve($state, $sym,
1252             [ 'shift', $actions[$sym] ],
1253             [ 'reduce', $rule ]);
1254 0           next;
1255             }
1256             }
1257              
1258 0           my $sz_rhs = $parser->rule_size($rule);
1259 0           my $lhs = $parser->{grammar}->[$rule];
1260 0           $actions[$sym] = bless [ $rule, $lhs, $sz_rhs ], 'reduce';
1261             }
1262             }
1263              
1264 0           $state->{actions} = \@actions;
1265             }
1266             }
1267              
1268             1;