File Coverage

blib/lib/Parse/Yapp/Lalr.pm
Criterion Covered Total %
statement 339 447 75.8
branch 101 164 61.5
condition 38 42 90.4
subroutine 21 25 84.0
pod 0 6 0.0
total 499 684 72.9


line stmt bran cond sub pod time code
1             #
2             # Module Parse::Yapp::Lalr
3             #
4             # Copyright © 1998, 1999, 2000, 2001, Francois Desarmenien.
5             # Copyright © 2017 William N. Braswell, Jr.
6             # (see the pod text in Parse::Yapp module for use and distribution rights)
7             #
8             package Parse::Yapp::Lalr;
9             @ISA=qw( Parse::Yapp::Grammar );
10              
11             require 5.004;
12              
13 3     3   916 use Parse::Yapp::Grammar;
  3         10  
  3         113  
14              
15             =for nobody
16              
17             Parse::Yapp::Compile Object Structure:
18             --------------------------------------
19             {
20             GRAMMAR => Parse::Yapp::Grammar,
21             STATES => [ { CORE => [ items... ],
22             ACTIONS => { term => action }
23             GOTOS => { nterm => stateno }
24             }... ]
25             CONFLICTS=>{ SOLVED => { stateno => [ ruleno, token, solved ] },
26             FORCED => { TOTAL => [ nbsr, nbrr ],
27             DETAIL => { stateno => { TOTAL => [ nbsr, nbrr ] }
28             LIST => [ ruleno, token ]
29             }
30             }
31             }
32             }
33              
34             'items' are of form: [ ruleno, dotpos ]
35             'term' in ACTIONS is '' means default action
36             'action' may be:
37             undef: explicit error (nonassociativity)
38             0 : accept
39             >0 : shift and go to state 'action'
40             <0 : reduce using rule -'action'
41             'solved' may have values of:
42             'shift' if solved as Shift
43             'reduce' if solved as Reduce
44             'error' if solved by discarding both Shift and Reduce (nonassoc)
45              
46             SOLVED is a set of states containing Solved conflicts
47             FORCED are forced conflict resolutions
48              
49             nbsr and nbrr are number of shift/reduce and reduce/reduce conflicts
50              
51             TOTAL is the total number of SR/RR conflicts for the parser
52              
53             DETAIL is the detail of conflicts for each state
54             TOTAL is the total number of SR/RR conflicts for a state
55             LIST is the list of discarded reductions (for display purpose only)
56              
57              
58             =cut
59              
60 3     3   17 use strict;
  3         7  
  3         71  
61              
62 3     3   17 use Carp;
  3         9  
  3         13663  
63              
64             ###############
65             # Constructor #
66             ###############
67             sub new {
68 10     10 0 1801 my($class)=shift;
69              
70 10 50       33 ref($class)
71             and $class=ref($class);
72              
73 10         61 my($self)=$class->SUPER::new(@_);
74 10         68 $self->_Compile();
75 10         50 bless($self,$class);
76             }
77             ###########
78             # Methods #
79             ###########
80              
81             ###########################
82             # Method To View Warnings #
83             ###########################
84             sub Warnings {
85 0     0 0 0 my($self)=shift;
86 0         0 my($text);
87 0         0 my($nbsr,$nbrr)=@{$$self{CONFLICTS}{FORCED}{TOTAL}};
  0         0  
88              
89 0         0 $text=$self->SUPER::Warnings();
90              
91             $nbsr != $$self{GRAMMAR}{EXPECT}
92 0 0       0 and $text.="$nbsr shift/reduce conflict".($nbsr > 1 ? "s" : "");
    0          
93              
94             $nbrr
95 0 0       0 and do {
96 0 0       0 $nbsr
97             and $text.=" and ";
98 0 0       0 $text.="$nbrr reduce/reduce conflict".($nbrr > 1 ? "s" : "");
99             };
100              
101             ( $nbsr != $$self{GRAMMAR}{EXPECT}
102 0 0 0     0 or $nbrr)
103             and $text.="\n";
104              
105 0         0 $text;
106             }
107             #############################
108             # Method To View DFA States #
109             #############################
110             sub ShowDfa {
111 0     0 0 0 my($self)=shift;
112 0         0 my($text);
113 0         0 my($grammar,$states)=($$self{GRAMMAR}, $$self{STATES});
114              
115 0         0 for my $stateno (0..$#$states) {
116 0         0 my(@shifts,@reduces,@errors,$default);
117              
118 0         0 $text.="State $stateno:\n\n";
119              
120             #Dump Kernel Items
121 0 0       0 for (sort { $$a[0] <=> $$b[0]
  0         0  
122 0         0 or $$a[1] <=> $$b[1] } @{$$states[$stateno]{'CORE'}}) {
123 0         0 my($ruleno,$pos)=@$_;
124 0         0 my($lhs,$rhs)=@{$$grammar{RULES}[$ruleno]}[0,1];
  0         0  
125 0         0 my(@rhscopy)=@$rhs;
126            
127 0 0       0 $ruleno
128             or $rhscopy[-1] = '$end';
129              
130 0         0 splice(@rhscopy,$pos,0,'.');
131 0         0 $text.= "\t$lhs -> ".join(' ',@rhscopy)."\t(Rule $ruleno)\n";
132             }
133              
134             #Prepare Actions
135 0         0 for (keys(%{$$states[$stateno]{ACTIONS}})) {
  0         0  
136 0         0 my($term,$action)=($_,$$states[$stateno]{ACTIONS}{$_});
137              
138 0 0       0 $term eq chr(0)
139             and $term = '$end';
140              
141             not defined($action)
142 0 0       0 and do {
143 0         0 push(@errors,$term);
144 0         0 next;
145             };
146              
147             $action > 0
148 0 0       0 and do {
149 0         0 push(@shifts,[ $term, $action ]);
150 0         0 next;
151             };
152              
153 0         0 $action = -$action;
154              
155             $term
156 0 0       0 or do {
157 0         0 $default= [ '$default', $action ];
158 0         0 next;
159             };
160              
161 0         0 push(@reduces,[ $term, $action ]);
162             }
163              
164             #Dump shifts
165             @shifts
166 0 0       0 and do {
167 0         0 $text.="\n";
168 0         0 for (sort { $$a[0] cmp $$b[0] } @shifts) {
  0         0  
169 0         0 my($term,$shift)=@$_;
170              
171 0         0 $text.="\t$term\tshift, and go to state $shift\n";
172             }
173             };
174              
175             #Dump errors
176             @errors
177 0 0       0 and do {
178 0         0 $text.="\n";
179 0         0 for my $term (sort { $a cmp $b } @errors) {
  0         0  
180 0         0 $text.="\t$term\terror (nonassociative)\n";
181             }
182             };
183              
184             #Prepare reduces
185             exists($$self{CONFLICTS}{FORCED}{DETAIL}{$stateno})
186 0 0       0 and push(@reduces,@{$$self{CONFLICTS}{FORCED}{DETAIL}{$stateno}{LIST}});
  0         0  
187              
188 0 0       0 @reduces=sort { $$a[0] cmp $$b[0] or $$a[1] <=> $$b[1] } @reduces;
  0         0  
189              
190 0 0       0 defined($default)
191             and push(@reduces,$default);
192              
193             #Dump reduces
194             @reduces
195 0 0       0 and do {
196 0         0 $text.="\n";
197 0         0 for (@reduces) {
198 0         0 my($term,$ruleno)=@$_;
199 0         0 my($discard);
200              
201             $ruleno < 0
202 0 0       0 and do {
203 0         0 ++$discard;
204 0         0 $ruleno = -$ruleno;
205             };
206              
207 0 0       0 $text.= "\t$term\t".($discard ? "[" : "");
208 0 0       0 if($ruleno) {
209 0         0 $text.= "reduce using rule $ruleno ".
210             "($$grammar{RULES}[$ruleno][0])";
211             }
212             else {
213 0         0 $text.='accept';
214             }
215 0 0       0 $text.=($discard ? "]" : "")."\n";
216             }
217             };
218              
219             #Dump gotos
220             exists($$states[$stateno]{GOTOS})
221 0 0       0 and do {
222 0         0 $text.= "\n";
223 0         0 for (keys(%{$$states[$stateno]{GOTOS}})) {
  0         0  
224 0         0 $text.= "\t$_\tgo to state $$states[$stateno]{GOTOS}{$_}\n";
225             }
226             };
227              
228 0         0 $text.="\n";
229             }
230 0         0 $text;
231             }
232              
233             ######################################
234             # Method to get summary about parser #
235             ######################################
236             sub Summary {
237 0     0 0 0 my($self)=shift;
238 0         0 my($text);
239              
240 0         0 $text=$self->SUPER::Summary();
241             $text.="Number of states : ".
242 0         0 scalar(@{$$self{STATES}})."\n";
  0         0  
243 0         0 $text;
244             }
245              
246             #######################################
247             # Method To Get Infos about conflicts #
248             #######################################
249             sub Conflicts {
250 0     0 0 0 my($self)=shift;
251 0         0 my($states)=$$self{STATES};
252 0         0 my($conflicts)=$$self{CONFLICTS};
253 0         0 my($text);
254              
255 0         0 for my $stateno ( sort { $a <=> $b } keys(%{$$conflicts{SOLVED}})) {
  0         0  
  0         0  
256              
257 0         0 for (@{$$conflicts{SOLVED}{$stateno}}) {
  0         0  
258 0         0 my($ruleno,$token,$how)=@$_;
259              
260 0 0       0 $token eq chr(0)
261             and $token = '$end';
262              
263 0         0 $text.="Conflict in state $stateno between rule ".
264             "$ruleno and token $token resolved as $how.\n";
265             }
266             };
267              
268 0         0 for my $stateno ( sort { $a <=> $b } keys(%{$$conflicts{FORCED}{DETAIL}})) {
  0         0  
  0         0  
269 0         0 my($nbsr,$nbrr)=@{$$conflicts{FORCED}{DETAIL}{$stateno}{TOTAL}};
  0         0  
270              
271 0         0 $text.="State $stateno contains ";
272              
273 0 0       0 $nbsr
    0          
274             and $text.="$nbsr shift/reduce conflict".
275             ($nbsr > 1 ? "s" : "");
276              
277             $nbrr
278 0 0       0 and do {
279 0 0       0 $nbsr
280             and $text.=" and ";
281              
282 0 0       0 $text.="$nbrr reduce/reduce conflict".
283             ($nbrr > 1 ? "s" : "");
284             };
285 0         0 $text.="\n";
286             };
287              
288 0         0 $text;
289             }
290              
291             #################################
292             # Method to dump parsing tables #
293             #################################
294             sub DfaTable {
295 9     9 0 19 my($self)=shift;
296 9         18 my($states)=$$self{STATES};
297 9         16 my($stateno);
298             my($text);
299              
300 9         16 $text="[\n\t{";
301              
302             $text.=join("\n\t},\n\t{",
303             map {
304 9         23 my($state)=$_;
  107         150  
305 107         116 my($text);
306              
307 107         161 $text="#State ".$stateno++."\n\t\t";
308              
309             ( not exists($$state{ACTIONS}{''})
310 63         162 or keys(%{$$state{ACTIONS}}) > 1)
311 107 100 100     234 and do {
312              
313 57         86 $text.="ACTIONS => {\n\t\t\t";
314              
315             $text.=join(",\n\t\t\t",
316             map {
317 126         217 my($term,$action)=($_,$$state{ACTIONS}{$_});
318 126         127 my($text);
319              
320 126 100       208 if(substr($term,0,1) eq "'") {
321 96         168 $term=~s/([\@\$\"])/\\$1/g;
322 96         296 $term=~s/^'|'$/"/g;
323             }
324             else {
325 30 100       58 $term= $term eq chr(0)
326             ? "''"
327             : "'$term'";
328             }
329              
330 126 100       202 if(defined($action)) {
331 125         152 $action=int($action);
332             }
333             else {
334 1         2 $action='undef';
335             }
336              
337 126         270 "$term => $action";
338            
339 57         71 } grep { $_ } keys(%{$$state{ACTIONS}}));
  139         205  
  57         109  
340              
341 57         97 $text.="\n\t\t}";
342             };
343              
344             exists($$state{ACTIONS}{''})
345 107 100       200 and do {
346 63 100       98 keys(%{$$state{ACTIONS}}) > 1
  63         112  
347             and $text.=",\n\t\t";
348              
349 63         131 $text.="DEFAULT => $$state{ACTIONS}{''}";
350             };
351              
352             exists($$state{GOTOS})
353 107 100       170 and do {
354 29         49 $text.=",\n\t\tGOTOS => {\n\t\t\t";
355             $text.=join(",\n\t\t\t",
356             map {
357 43         73 my($nterm,$stateno)=($_,$$state{GOTOS}{$_});
358 43         49 my($text);
359              
360 43         126 "'$nterm' => $stateno";
361            
362 29         40 } keys(%{$$state{GOTOS}}));
  29         60  
363 29         46 $text.="\n\t\t}";
364             };
365              
366 107         214 $text;
367              
368             }@$states);
369              
370 9         24 $text.="\n\t}\n]";
371              
372 9         32 $text;
373              
374             }
375              
376              
377             ####################################
378             # Method to build Dfa from Grammar #
379             ####################################
380             sub _Compile {
381 10     10   20 my($self)=shift;
382 10         18 my($grammar,$states);
383              
384 10         21 $grammar=$self->{GRAMMAR};
385              
386 10         31 $states = _LR0($grammar);
387              
388 10         36 $self->{CONFLICTS} = _LALR($grammar,$states);
389              
390 10         23 $self->{STATES}=$states;
391             }
392              
393             #########################
394             # LR0 States Generation #
395             #########################
396             #
397             ###########################
398             # General digraph routine #
399             ###########################
400             sub _Digraph {
401 30     30   48 my($rel,$F)=@_;
402 30         50 my(%N,@S);
403 30         43 my($infinity)=(~(1<<31));
404 30         36 my($Traverse);
405              
406             $Traverse = sub {
407 10659     10659   13716 my($x,$d)=@_;
408 10659         10525 my($y);
409              
410 10659         12973 push(@S,$x);
411 10659         15217 $N{$x}=$d;
412              
413             exists($$rel{$x})
414 10659 100       17173 and do {
415 9183         9679 for $y (keys(%{$$rel{$x}})) {
  9183         25060  
416 42992 100       69637 exists($N{$y})
417             or &$Traverse($y,$d+1);
418              
419             $N{$y} < $N{$x}
420 42992 100       61949 and $N{$x} = $N{$y};
421              
422 42992         65390 $$F{$x}|=$$F{$y};
423             }
424             };
425              
426             $N{$x} == $d
427 10659 100       17596 and do {
428 10322         10421 for(;;) {
429 10659         11487 $y=pop(@S);
430 10659         11555 $N{$y}=$infinity;
431 10659 100       18002 $y eq $x
432             and last;
433 337         422 $$F{$y}=$$F{$x};
434             }
435             };
436 30         151 };
437              
438 30         2800 for (keys(%$rel)) {
439 9183 100       14919 exists($N{$_})
440             or &$Traverse($_,1);
441             }
442             }
443             #######################
444             # Generate LR0 states #
445             #######################
446             =for nobody
447             Formula used for closures:
448              
449             CLOSE(A) = DCLOSE(A) u U (CLOSE(B) | A close B)
450              
451             where:
452              
453             DCLOSE(A) = { [ A -> alpha ] in P }
454              
455             A close B iff [ A -> B gamma ] in P
456              
457             =cut
458             sub _SetClosures {
459 10     10   20 my($grammar)=@_;
460 10         15 my($rel,$closures);
461              
462 10         17 for my $symbol (keys(%{$$grammar{NTERM}})) {
  10         70  
463 270         305 $closures->{$symbol}=pack('b'.@{$$grammar{RULES}});
  270         598  
464              
465 270         347 for my $ruleno (@{$$grammar{NTERM}{$symbol}}) {
  270         402  
466 888         1153 my($rhs)=$$grammar{RULES}[$ruleno][1];
467              
468 888         1429 vec($closures->{$symbol},$ruleno,1)=1;
469              
470             @$rhs > 0
471             and exists($$grammar{NTERM}{$$rhs[0]})
472 888 100 100     2809 and ++$rel->{$symbol}{$$rhs[0]};
473             }
474             }
475 10         45 _Digraph($rel,$closures);
476              
477 10         23 $closures
478             }
479              
480             sub _Closures {
481 1718     1718   2685 my($grammar,$core,$closures)=@_;
482 1718         2193 my($ruleset)=pack('b'.@{$$grammar{RULES}});
  1718         6111  
483              
484 1718         3449 for (@$core) {
485 3895         7826 my($ruleno,$pos)=@$_;
486 3895         6871 my($rhs)=$$grammar{RULES}[$ruleno][1];
487              
488             $pos < @$rhs
489             and exists($closures->{$$rhs[$pos]})
490 3895 100 100     14596 and $ruleset|=$closures->{$$rhs[$pos]};
491             }
492 56069         92270 [ @$core, map { [ $_, 0 ] }
493 1330007         1526757 grep { vec($ruleset,$_,1) }
494 1718         2835 0..$#{$$grammar{RULES}} ];
  1718         19498  
495             }
496              
497             sub _Transitions {
498 1718     1718   3180 my($grammar,$cores,$closures,$states,$stateno)=@_;
499 1718         3675 my($core)=$$states[$stateno]{'CORE'};
500 1718         2080 my(%transitions);
501              
502 1718         2009 for (@{_Closures($grammar,$core,$closures)}) {
  1718         3121  
503 59964         85598 my($ruleno,$pos)=@$_;
504 59964         87931 my($rhs)=$$grammar{RULES}[$ruleno][1];
505              
506             $pos == @$rhs
507 59964 100       87229 and do {
508 1187         1458 push(@{$$states[$stateno]{ACTIONS}{''}},$ruleno);
  1187         4508  
509 1187         2449 next;
510             };
511 58777         61157 push(@{$transitions{$$rhs[$pos]}},[ $ruleno, $pos+1 ]);
  58777         144102  
512             }
513              
514 1718         13278 for (keys(%transitions)) {
515 23159         33990 my($symbol,$core)=($_,$transitions{$_});
516 58777         123596 my($corekey)=join(',',map { join('.',@$_) }
517 23159 50       38114 sort { $$a[0] <=> $$b[0]
  51997         80339  
518             or $$a[1] <=> $$b[1] }
519             @$core);
520 23159         33513 my($tostateno);
521              
522             exists($cores->{$corekey})
523 23159 100       44165 or do {
524 1708         4022 push(@$states,{ 'CORE' => $core });
525 1708         4032 $cores->{$corekey}=$#$states;
526             };
527              
528 23159         28558 $tostateno=$cores->{$corekey};
529 23159         24493 push(@{$$states[$tostateno]{FROM}},$stateno);
  23159         43916  
530              
531             exists($$grammar{TERM}{$_})
532 23159 100       40093 and do {
533 11824         21693 $$states[$stateno]{ACTIONS}{$_} = [ $tostateno ];
534 11824         28881 next;
535             };
536 11335         28068 $$states[$stateno]{GOTOS}{$_} = $tostateno;
537             }
538             }
539              
540             sub _LR0 {
541 10     10   21 my($grammar)=@_;
542 10         20 my($states) = [];
543 10         17 my($stateno);
544             my($closures); #$closures={ nterm => ruleset,... }
545 10         20 my($cores)={}; # { "itemlist" => stateno, ... }
546             # where "itemlist" has the form:
547             # "ruleno.pos,ruleno.pos" ordered by ruleno,pos
548              
549 10         29 $closures = _SetClosures($grammar);
550 10         37 push(@$states,{ 'CORE' => [ [ 0, 0 ] ] });
551 10         30 for($stateno=0;$stateno<@$states;++$stateno) {
552 1718         3831 _Transitions($grammar,$cores,$closures,$states,$stateno);
553             }
554              
555 10         502 $states
556             }
557              
558             #########################################################
559             # Add Lookahead tokens where needed to make LALR states #
560             #########################################################
561             =for nobody
562             Compute First sets for non-terminal using the following formula:
563              
564             FIRST(A) = { a in T u { epsilon } | A l a }
565             u
566             U { FIRST(B) | B in V and A l B }
567              
568             where:
569              
570             A l x iff [ A -> X1 X2 .. Xn x alpha ] in P and Xi =>* epsilon, 1 <= i <= n
571             =cut
572             sub _SetFirst {
573 10     10   20 my($grammar,$termlst,$terminx)=@_;
574 10         24 my($rel,$first)=( {}, {} );
575              
576 10         13 for my $symbol (keys(%{$$grammar{NTERM}})) {
  10         73  
577 270         579 $first->{$symbol}=pack('b'.@$termlst);
578              
579             RULE:
580 270         292 for my $ruleno (@{$$grammar{NTERM}{$symbol}}) {
  270         443  
581 888         1267 my($rhs)=$$grammar{RULES}[$ruleno][1];
582              
583 888         1109 for (@$rhs) {
584             exists($terminx->{$_})
585 868 100       1294 and do {
586 299         477 vec($first->{$symbol},$terminx->{$_},1)=1;
587 299         513 next RULE;
588             };
589 569         755 ++$rel->{$symbol}{$_};
590 569 100       1003 exists($$grammar{NULLABLE}{$_})
591             or next RULE;
592             }
593 47         103 vec($first->{$symbol},0,1)=1;
594             }
595             }
596 10         50 _Digraph($rel,$first);
597              
598 10         32 $first
599             }
600              
601             sub _Preds {
602 8974     8974   13002 my($states,$stateno,$len)=@_;
603 8974         10359 my($queue, $preds);
604              
605 8974 100       22793 $len
606             or return [ $stateno ];
607              
608 1004         2081 $queue=[ [ $stateno, $len ] ];
609 1004         1883 while(@$queue) {
610 3862         4763 my($pred) = shift(@$queue);
611 3862         5344 my($stateno, $len) = @$pred;
612              
613             $len == 1
614 3862 100       5687 and do {
615 3169         3708 push(@$preds,@{$states->[$stateno]{FROM}});
  3169         8742  
616 3169         5916 next;
617             };
618              
619 2858         5627 push(@$queue, map { [ $_, $len - 1 ] }
620 693         965 @{$states->[$stateno]{FROM}});
  693         1822  
621             }
622              
623             # Pass @$preds through a hash to ensure unicity
624 1004         1217 [ keys( %{ +{ map { ($_,1) } @$preds } } ) ];
  1004         1563  
  34302         69183  
625             }
626              
627             sub _FirstSfx {
628 1235     1235   2147 my($grammar,$firstset,$termlst,$terminx,$ruleno,$pos,$key)=@_;
629 1235         2402 my($first)=pack('b'.@$termlst);
630 1235         2339 my($rhs)=$$grammar{RULES}[$ruleno][1];
631              
632 1235         2288 for (;$pos < @$rhs;++$pos) {
633             exists($terminx->{$$rhs[$pos]})
634 817 100       1818 and do {
635 395         936 vec($first,$terminx->{$$rhs[$pos]},1)=1;
636 395         1329 return($first);
637             };
638 422         796 $first|=$firstset->{$$rhs[$pos]};
639              
640 422 100       869 vec($first,0,1)
641             and vec($first,0,1)=0;
642              
643 422 100       1335 exists($$grammar{NULLABLE}{$$rhs[$pos]})
644             or return($first);
645              
646             }
647 597         1272 vec($first,0,1)=1;
648 597         1732 $first;
649             }
650              
651             =for noboby
652             Compute Follow sets using following formula:
653              
654             FOLLOW(p,A) = READ(p,A)
655             u
656             U { FOLLOW(q,B) | (p,A) include (q,B)
657              
658             where:
659            
660             READ(p,A) = U { FIRST(beta) | [ A -> alpha A . beta ] in KERNEL(GOTO(p,A))
661             } - { epsilon }
662              
663             (p,a) include (q,B) iff [ B -> alpha A . beta ] in KERNEL(GOTO(p,A),
664             epsilon in FIRST(beta) and
665             q in PRED(p,alpha)
666             =cut
667             sub _ComputeFollows {
668 10     10   19 my($grammar,$states,$termlst)=@_;
669 10         19 my($firstset,$terminx);
670 10         26 my($inconsistent, $rel, $follows, $sfx)= ( {}, {}, {}, {} );
671              
672 10         29 %$terminx= map { ($termlst->[$_],$_) } 0..$#$termlst;
  174         251  
673              
674 10         39 $firstset=_SetFirst($grammar,$termlst,$terminx);
675              
676 10         32 for my $stateno (0..$#$states) {
677 1718         2771 my($state)=$$states[$stateno];
678              
679             exists($$state{ACTIONS}{''})
680             and ( @{$$state{ACTIONS}{''}} > 1
681             or keys(%{$$state{ACTIONS}}) > 1 )
682 1718 100 100     4155 and do {
      100        
683 397         1006 ++$inconsistent->{$stateno};
684              
685 397         508 for my $ruleno (@{$$state{ACTIONS}{''}}) {
  397         872  
686 436         524 my($lhs,$rhs)=@{$$grammar{RULES}[$ruleno]}[0,1];
  436         1196  
687              
688 436         599 for my $predno (@{_Preds($states,$stateno,scalar(@$rhs))}) {
  436         829  
689 11975         22302 ++$rel->{"$stateno.$ruleno"}{"$predno.$lhs"};
690             }
691             }
692             };
693              
694             exists($$state{GOTOS})
695 1718 100       3450 or next;
696              
697 656         761 for my $symbol (keys(%{$$state{GOTOS}})) {
  656         3340  
698 11335         19475 my($tostate)=$$states[$$state{GOTOS}{$symbol}];
699 11335         16921 my($goto)="$stateno.$symbol";
700              
701 11335         32367 $follows->{$goto}=pack('b'.@$termlst);
702              
703 11335         13539 for my $item (@{$$tostate{'CORE'}}) {
  11335         19563  
704 39723         61396 my($ruleno,$pos)=@$item;
705 39723         50517 my($key)="$ruleno.$pos";
706              
707             exists($sfx->{$key})
708 39723 100       65622 or $sfx->{$key} = _FirstSfx($grammar,$firstset,
709             $termlst,$terminx,
710             $ruleno,$pos,$key);
711              
712 39723         56361 $follows->{$goto}|=$sfx->{$key};
713              
714             vec($follows->{$goto},0,1)
715 39723 100       70857 and do {
716 8538         14735 my($lhs)=$$grammar{RULES}[$ruleno][0];
717              
718 8538         16385 vec($follows->{$goto},0,1)=0;
719              
720 8538         12080 for my $predno (@{_Preds($states,$stateno,$pos-1)}) {
  8538         14489  
721 30297         71639 ++$rel->{$goto}{"$predno.$lhs"};
722             }
723             };
724             }
725             }
726             }
727 10         32 _Digraph($rel,$follows);
728              
729 10         1286 ($follows,$inconsistent)
730             }
731              
732             sub _ComputeLA {
733 10     10   22 my($grammar,$states)=@_;
734 10         15 my($termlst)= [ '',keys(%{$$grammar{TERM}}) ];
  10         144  
735              
736 10         43 my($follows,$inconsistent) = _ComputeFollows($grammar,$states,$termlst);
737              
738 10         129 for my $stateno ( keys(%$inconsistent ) ) {
739 397         807 my($state)=$$states[$stateno];
740 397         440 my($conflict);
741              
742             #NB the sort is VERY important for conflicts resolution order
743 397         437 for my $ruleno (sort { $a <=> $b }
  41         87  
744 397         1166 @{$$state{ACTIONS}{''}}) {
745 436         1142 for my $term ( map { $termlst->[$_] } grep {
  7755         9293  
746 46056         58436 vec($follows->{"$stateno.$ruleno"},$_,1) }
747             0..$#$termlst) {
748 7755 100       11257 exists($$state{ACTIONS}{$term})
749             and ++$conflict;
750 7755         7339 push(@{$$state{ACTIONS}{$term}},-$ruleno);
  7755         16070  
751             }
752             }
753 397         676 delete($$state{ACTIONS}{''});
754             $conflict
755 397 100       822 or delete($inconsistent->{$stateno});
756             }
757              
758             $inconsistent
759 10         162 }
760              
761             #############################
762             # Solve remaining conflicts #
763             #############################
764              
765             sub _SolveConflicts {
766 10     10   23 my($grammar,$states,$inconsistent)=@_;
767 10         13 my(%rulesprec,$RulePrec);
768 10         56 my($conflicts)={ SOLVED => {},
769             FORCED => { TOTAL => [ 0, 0 ],
770             DETAIL => {}
771             }
772             };
773              
774             $RulePrec = sub {
775 845     845   950 my($ruleno)=@_;
776 845         876 my($rhs,$rprec)=@{$$grammar{RULES}[$ruleno]}[1,2];
  845         1329  
777 845         921 my($lastterm);
778              
779 845 100       1305 defined($rprec)
780             and return($rprec);
781              
782             exists($rulesprec{$ruleno})
783 612 100       1143 and return($rulesprec{$ruleno});
784              
785 67         113 $lastterm=(grep { exists($$grammar{TERM}{$_}) } @$rhs)[-1];
  137         298  
786              
787             defined($lastterm)
788             and ref($$grammar{TERM}{$lastterm})
789 67 100 66     201 and do {
790 48         124 $rulesprec{$ruleno}=$$grammar{TERM}{$lastterm}[1];
791 48         93 return($rulesprec{$ruleno});
792             };
793              
794 19         27 undef;
795 10         52 };
796              
797 10         65 for my $stateno (keys(%$inconsistent)) {
798 155         342 my($state)=$$states[$stateno];
799 155         276 my($actions)=$$state{ACTIONS};
800 155         206 my($nbsr,$nbrr);
801              
802 155         974 for my $term ( keys(%$actions) ) {
803 5702         6621 my($act)=$$actions{$term};
804              
805 5702 100       8409 @$act > 1
806             or next;
807              
808             $$act[0] > 0
809             and ref($$grammar{TERM}{$term})
810 895 100 100     2427 and do {
811 842         887 my($assoc,$tprec)=@{$$grammar{TERM}{$term}};
  842         1302  
812 842         972 my($k,$error);
813              
814 842         1210 for ($k=1;$k<@$act;++$k) {
815 845         1029 my($ruleno)=-$$act[$k];
816 845         1110 my($rprec)=&$RulePrec($ruleno);
817              
818 845 100       1224 defined($rprec)
819             or next;
820              
821             ( $tprec > $rprec
822             or ( $tprec == $rprec and $assoc eq 'RIGHT'))
823 826 100 100     1528 and do {
      100        
824 542         548 push(@{$$conflicts{SOLVED}{$stateno}},
  542         1321  
825             [ $ruleno, $term, 'shift' ]);
826 542         711 splice(@$act,$k--,1);
827 542         947 next;
828             };
829             ( $tprec < $rprec
830             or $assoc eq 'LEFT')
831 284 100 100     505 and do {
832 283         304 push(@{$$conflicts{SOLVED}{$stateno}},
  283         648  
833             [ $ruleno, $term, 'reduce' ]);
834             $$act[0] > 0
835 283 50       463 and do {
836 283         357 splice(@$act,0,1);
837 283         295 --$k;
838             };
839 283         492 next;
840             };
841 1         2 push(@{$$conflicts{SOLVED}{$stateno}},
  1         4  
842             [ $ruleno, $term, 'error' ]);
843 1         3 splice(@$act,$k--,1);
844             $$act[0] > 0
845 1 50       5 and do {
846 1         2 splice(@$act,0,1);
847 1         2 ++$error;
848 1         3 --$k;
849             };
850             }
851 842 100       1266 $error
852             and unshift(@$act,undef);
853             };
854              
855             @$act > 1
856 895 100       1421 and do {
857 72         90 $nbrr += @$act - 2;
858 72 100       107 ($$act[0] > 0 ? $nbsr : $nbrr) += 1;
859 72         197 push(@{$$conflicts{FORCED}{DETAIL}{$stateno}{LIST}},
860 72         80 map { [ $term, $_ ] } splice(@$act,1));
  72         184  
861             };
862             }
863              
864             $nbsr
865 155 100       419 and do {
866 27         40 $$conflicts{FORCED}{TOTAL}[0]+=$nbsr;
867 27         56 $$conflicts{FORCED}{DETAIL}{$stateno}{TOTAL}[0]+=$nbsr;
868             };
869              
870             $nbrr
871 155 100       282 and do {
872 11         17 $$conflicts{FORCED}{TOTAL}[1]+=$nbrr;
873 11         25 $$conflicts{FORCED}{DETAIL}{$stateno}{TOTAL}[1]+=$nbrr;
874             };
875              
876             }
877              
878             $conflicts
879 10         122 }
880              
881             ###############################
882             # Make default reduce actions #
883             ###############################
884             sub _SetDefaults {
885 10     10   21 my($states)=@_;
886              
887 10         22 for my $state (@$states) {
888 1718         2627 my($actions)=$$state{ACTIONS};
889 1718         1904 my(%reduces,$default,$nodefault);
890              
891             exists($$actions{''})
892 1718 100       2747 and do {
893 751         1098 $$actions{''}[0] = -$$actions{''}[0];
894 751         807 ++$nodefault;
895             };
896              
897             #shift error token => no default
898             exists($$actions{error})
899 1718 100 100     2829 and $$actions{error}[0] > 0
900             and ++$nodefault;
901              
902 1718         5812 for my $term (keys(%$actions)) {
903              
904 19432         28314 $$actions{$term}=$$actions{$term}[0];
905              
906             ( not defined($$actions{$term})
907 19432 100 100     49542 or $$actions{$term} > 0
      100        
908             or $nodefault)
909             and next;
910              
911 7059         6921 push(@{$reduces{$$actions{$term}}},$term);
  7059         12322  
912             }
913              
914 1718 100       3755 keys(%reduces) > 0
915             or next;
916              
917 416         697 $default=( map { $$_[0] }
918 40 50       148 sort { $$b[1] <=> $$a[1] or $$b[0] <=> $$a[0] }
919 377         622 map { [ $_, scalar(@{$reduces{$_}}) ] }
  416         447  
  416         975  
920             keys(%reduces))[0];
921              
922 377         498 delete(@$actions{ @{$reduces{$default}} });
  377         1159  
923 377         1245 $$state{ACTIONS}{''}=$default;
924             }
925             }
926              
927             sub _LALR {
928 10     10   21 my($grammar,$states) = @_;
929 10         18 my($conflicts,$inconsistent);
930              
931 10         30 $inconsistent = _ComputeLA($grammar,$states);
932              
933 10         32 $conflicts = _SolveConflicts($grammar,$states,$inconsistent);
934 10         33 _SetDefaults($states);
935              
936 10         74 $conflicts
937             }
938              
939              
940             1;