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   1081 use Parse::Yapp::Grammar;
  3         8  
  3         97  
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   16 use strict;
  3         7  
  3         107  
61              
62 3     3   17 use Carp;
  3         5  
  3         10615  
63              
64             ###############
65             # Constructor #
66             ###############
67             sub new {
68 10     10 0 1639 my($class)=shift;
69              
70 10 50       36 ref($class)
71             and $class=ref($class);
72              
73 10         56 my($self)=$class->SUPER::new(@_);
74 10         72 $self->_Compile();
75 10         40 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         20 my($states)=$$self{STATES};
297 9         67 my($stateno);
298             my($text);
299              
300 9         18 $text="[\n\t{";
301              
302             $text.=join("\n\t},\n\t{",
303             map {
304 9         17 my($state)=$_;
  107         153  
305 107         139 my($text);
306              
307 107         179 $text="#State ".$stateno++."\n\t\t";
308              
309             ( not exists($$state{ACTIONS}{''})
310 63         198 or keys(%{$$state{ACTIONS}}) > 1)
311 107 100 100     261 and do {
312              
313 57         92 $text.="ACTIONS => {\n\t\t\t";
314              
315             $text.=join(",\n\t\t\t",
316             map {
317 126         232 my($term,$action)=($_,$$state{ACTIONS}{$_});
318 126         156 my($text);
319              
320 126 100       243 if(substr($term,0,1) eq "'") {
321 96         163 $term=~s/([\@\$\"])/\\$1/g;
322 96         278 $term=~s/^'|'$/"/g;
323             }
324             else {
325 30 100       67 $term= $term eq chr(0)
326             ? "''"
327             : "'$term'";
328             }
329              
330 126 100       229 if(defined($action)) {
331 125         162 $action=int($action);
332             }
333             else {
334 1         2 $action='undef';
335             }
336              
337 126         318 "$term => $action";
338            
339 57         84 } grep { $_ } keys(%{$$state{ACTIONS}}));
  139         218  
  57         109  
340              
341 57         100 $text.="\n\t\t}";
342             };
343              
344             exists($$state{ACTIONS}{''})
345 107 100       230 and do {
346 63 100       93 keys(%{$$state{ACTIONS}}) > 1
  63         137  
347             and $text.=",\n\t\t";
348              
349 63         126 $text.="DEFAULT => $$state{ACTIONS}{''}";
350             };
351              
352             exists($$state{GOTOS})
353 107 100       208 and do {
354 29         48 $text.=",\n\t\tGOTOS => {\n\t\t\t";
355             $text.=join(",\n\t\t\t",
356             map {
357 43         80 my($nterm,$stateno)=($_,$$state{GOTOS}{$_});
358 43         66 my($text);
359              
360 43         116 "'$nterm' => $stateno";
361            
362 29         46 } keys(%{$$state{GOTOS}}));
  29         56  
363 29         49 $text.="\n\t\t}";
364             };
365              
366 107         211 $text;
367              
368             }@$states);
369              
370 9         22 $text.="\n\t}\n]";
371              
372 9         30 $text;
373              
374             }
375              
376              
377             ####################################
378             # Method to build Dfa from Grammar #
379             ####################################
380             sub _Compile {
381 10     10   24 my($self)=shift;
382 10         19 my($grammar,$states);
383              
384 10         22 $grammar=$self->{GRAMMAR};
385              
386 10         31 $states = _LR0($grammar);
387              
388 10         31 $self->{CONFLICTS} = _LALR($grammar,$states);
389              
390 10         26 $self->{STATES}=$states;
391             }
392              
393             #########################
394             # LR0 States Generation #
395             #########################
396             #
397             ###########################
398             # General digraph routine #
399             ###########################
400             sub _Digraph {
401 30     30   60 my($rel,$F)=@_;
402 30         52 my(%N,@S);
403 30         47 my($infinity)=(~(1<<31));
404 30         49 my($Traverse);
405              
406             $Traverse = sub {
407 10659     10659   16529 my($x,$d)=@_;
408 10659         13368 my($y);
409              
410 10659         15680 push(@S,$x);
411 10659         18495 $N{$x}=$d;
412              
413             exists($$rel{$x})
414 10659 100       23251 and do {
415 9183         11863 for $y (keys(%{$$rel{$x}})) {
  9183         30167  
416 42992 100       91803 exists($N{$y})
417             or &$Traverse($y,$d+1);
418              
419             $N{$y} < $N{$x}
420 42992 100       84835 and $N{$x} = $N{$y};
421              
422 42992         80093 $$F{$x}|=$$F{$y};
423             }
424             };
425              
426             $N{$x} == $d
427 10659 100       23075 and do {
428 10322         13467 for(;;) {
429 10659         14639 $y=pop(@S);
430 10659         14729 $N{$y}=$infinity;
431 10659 100       23510 $y eq $x
432             and last;
433 337         500 $$F{$y}=$$F{$x};
434             }
435             };
436 30         135 };
437              
438 30         3164 for (keys(%$rel)) {
439 9183 100       21652 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   23 my($grammar)=@_;
460 10         21 my($rel,$closures);
461              
462 10         16 for my $symbol (keys(%{$$grammar{NTERM}})) {
  10         85  
463 270         423 $closures->{$symbol}=pack('b'.@{$$grammar{RULES}});
  270         743  
464              
465 270         407 for my $ruleno (@{$$grammar{NTERM}{$symbol}}) {
  270         567  
466 888         1507 my($rhs)=$$grammar{RULES}[$ruleno][1];
467              
468 888         1876 vec($closures->{$symbol},$ruleno,1)=1;
469              
470             @$rhs > 0
471             and exists($$grammar{NTERM}{$$rhs[0]})
472 888 100 100     4574 and ++$rel->{$symbol}{$$rhs[0]};
473             }
474             }
475 10         49 _Digraph($rel,$closures);
476              
477 10         63 $closures
478             }
479              
480             sub _Closures {
481 1718     1718   2955 my($grammar,$core,$closures)=@_;
482 1718         2610 my($ruleset)=pack('b'.@{$$grammar{RULES}});
  1718         6887  
483              
484 1718         4014 for (@$core) {
485 3895         9180 my($ruleno,$pos)=@$_;
486 3895         7534 my($rhs)=$$grammar{RULES}[$ruleno][1];
487              
488             $pos < @$rhs
489             and exists($closures->{$$rhs[$pos]})
490 3895 100 100     18996 and $ruleset|=$closures->{$$rhs[$pos]};
491             }
492 56069         110571 [ @$core, map { [ $_, 0 ] }
493 1330007         1816200 grep { vec($ruleset,$_,1) }
494 1718         3172 0..$#{$$grammar{RULES}} ];
  1718         20453  
495             }
496              
497             sub _Transitions {
498 1718     1718   3429 my($grammar,$cores,$closures,$states,$stateno)=@_;
499 1718         4193 my($core)=$$states[$stateno]{'CORE'};
500 1718         2772 my(%transitions);
501              
502 1718         2420 for (@{_Closures($grammar,$core,$closures)}) {
  1718         3645  
503 59964         102512 my($ruleno,$pos)=@$_;
504 59964         104130 my($rhs)=$$grammar{RULES}[$ruleno][1];
505              
506             $pos == @$rhs
507 59964 100       117583 and do {
508 1187         1737 push(@{$$states[$stateno]{ACTIONS}{''}},$ruleno);
  1187         5016  
509 1187         2928 next;
510             };
511 58777         76907 push(@{$transitions{$$rhs[$pos]}},[ $ruleno, $pos+1 ]);
  58777         166150  
512             }
513              
514 1718         14011 for (keys(%transitions)) {
515 23159         41454 my($symbol,$core)=($_,$transitions{$_});
516 58777         137149 my($corekey)=join(',',map { join('.',@$_) }
517 23159 50       45566 sort { $$a[0] <=> $$b[0]
  51997         109558  
518             or $$a[1] <=> $$b[1] }
519             @$core);
520 23159         39039 my($tostateno);
521              
522             exists($cores->{$corekey})
523 23159 100       57435 or do {
524 1708         5073 push(@$states,{ 'CORE' => $core });
525 1708         4930 $cores->{$corekey}=$#$states;
526             };
527              
528 23159         36937 $tostateno=$cores->{$corekey};
529 23159         31004 push(@{$$states[$tostateno]{FROM}},$stateno);
  23159         54495  
530              
531             exists($$grammar{TERM}{$_})
532 23159 100       52499 and do {
533 11824         26416 $$states[$stateno]{ACTIONS}{$_} = [ $tostateno ];
534 11824         31045 next;
535             };
536 11335         33490 $$states[$stateno]{GOTOS}{$_} = $tostateno;
537             }
538             }
539              
540             sub _LR0 {
541 10     10   21 my($grammar)=@_;
542 10         23 my($states) = [];
543 10         23 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         28 $closures = _SetClosures($grammar);
550 10         43 push(@$states,{ 'CORE' => [ [ 0, 0 ] ] });
551 10         38 for($stateno=0;$stateno<@$states;++$stateno) {
552 1718         4892 _Transitions($grammar,$cores,$closures,$states,$stateno);
553             }
554              
555 10         642 $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   25 my($grammar,$termlst,$terminx)=@_;
574 10         23 my($rel,$first)=( {}, {} );
575              
576 10         18 for my $symbol (keys(%{$$grammar{NTERM}})) {
  10         93  
577 270         734 $first->{$symbol}=pack('b'.@$termlst);
578              
579             RULE:
580 270         365 for my $ruleno (@{$$grammar{NTERM}{$symbol}}) {
  270         538  
581 888         1637 my($rhs)=$$grammar{RULES}[$ruleno][1];
582              
583 888         1345 for (@$rhs) {
584             exists($terminx->{$_})
585 868 100       1863 and do {
586 299         627 vec($first->{$symbol},$terminx->{$_},1)=1;
587 299         617 next RULE;
588             };
589 569         1108 ++$rel->{$symbol}{$_};
590 569 100       1339 exists($$grammar{NULLABLE}{$_})
591             or next RULE;
592             }
593 47         203 vec($first->{$symbol},0,1)=1;
594             }
595             }
596 10         65 _Digraph($rel,$first);
597              
598 10         32 $first
599             }
600              
601             sub _Preds {
602 8974     8974   16074 my($states,$stateno,$len)=@_;
603 8974         12768 my($queue, $preds);
604              
605 8974 100       29577 $len
606             or return [ $stateno ];
607              
608 1004         2777 $queue=[ [ $stateno, $len ] ];
609 1004         2669 while(@$queue) {
610 3862         6046 my($pred) = shift(@$queue);
611 3862         6379 my($stateno, $len) = @$pred;
612              
613             $len == 1
614 3862 100       7874 and do {
615 3169         4402 push(@$preds,@{$states->[$stateno]{FROM}});
  3169         11233  
616 3169         7574 next;
617             };
618              
619 2858         6785 push(@$queue, map { [ $_, $len - 1 ] }
620 693         1052 @{$states->[$stateno]{FROM}});
  693         1958  
621             }
622              
623             # Pass @$preds through a hash to ensure unicity
624 1004         1714 [ keys( %{ +{ map { ($_,1) } @$preds } } ) ];
  1004         2002  
  34302         82565  
625             }
626              
627             sub _FirstSfx {
628 1235     1235   2779 my($grammar,$firstset,$termlst,$terminx,$ruleno,$pos,$key)=@_;
629 1235         3019 my($first)=pack('b'.@$termlst);
630 1235         3005 my($rhs)=$$grammar{RULES}[$ruleno][1];
631              
632 1235         3314 for (;$pos < @$rhs;++$pos) {
633             exists($terminx->{$$rhs[$pos]})
634 817 100       2594 and do {
635 395         1121 vec($first,$terminx->{$$rhs[$pos]},1)=1;
636 395         1606 return($first);
637             };
638 422         996 $first|=$firstset->{$$rhs[$pos]};
639              
640 422 100       1198 vec($first,0,1)
641             and vec($first,0,1)=0;
642              
643 422 100       1870 exists($$grammar{NULLABLE}{$$rhs[$pos]})
644             or return($first);
645              
646             }
647 597         1512 vec($first,0,1)=1;
648 597         1977 $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   21 my($grammar,$states,$termlst)=@_;
669 10         16 my($firstset,$terminx);
670 10         27 my($inconsistent, $rel, $follows, $sfx)= ( {}, {}, {}, {} );
671              
672 10         27 %$terminx= map { ($termlst->[$_],$_) } 0..$#$termlst;
  174         308  
673              
674 10         43 $firstset=_SetFirst($grammar,$termlst,$terminx);
675              
676 10         33 for my $stateno (0..$#$states) {
677 1718         3537 my($state)=$$states[$stateno];
678              
679             exists($$state{ACTIONS}{''})
680             and ( @{$$state{ACTIONS}{''}} > 1
681             or keys(%{$$state{ACTIONS}}) > 1 )
682 1718 100 100     5603 and do {
      100        
683 397         1292 ++$inconsistent->{$stateno};
684              
685 397         711 for my $ruleno (@{$$state{ACTIONS}{''}}) {
  397         954  
686 436         766 my($lhs,$rhs)=@{$$grammar{RULES}[$ruleno]}[0,1];
  436         1538  
687              
688 436         745 for my $predno (@{_Preds($states,$stateno,scalar(@$rhs))}) {
  436         1127  
689 11975         26467 ++$rel->{"$stateno.$ruleno"}{"$predno.$lhs"};
690             }
691             }
692             };
693              
694             exists($$state{GOTOS})
695 1718 100       4502 or next;
696              
697 656         1056 for my $symbol (keys(%{$$state{GOTOS}})) {
  656         4036  
698 11335         23839 my($tostate)=$$states[$$state{GOTOS}{$symbol}];
699 11335         20249 my($goto)="$stateno.$symbol";
700              
701 11335         38644 $follows->{$goto}=pack('b'.@$termlst);
702              
703 11335         16450 for my $item (@{$$tostate{'CORE'}}) {
  11335         25641  
704 39723         77126 my($ruleno,$pos)=@$item;
705 39723         63396 my($key)="$ruleno.$pos";
706              
707             exists($sfx->{$key})
708 39723 100       91453 or $sfx->{$key} = _FirstSfx($grammar,$firstset,
709             $termlst,$terminx,
710             $ruleno,$pos,$key);
711              
712 39723         68082 $follows->{$goto}|=$sfx->{$key};
713              
714             vec($follows->{$goto},0,1)
715 39723 100       91300 and do {
716 8538         17992 my($lhs)=$$grammar{RULES}[$ruleno][0];
717              
718 8538         19915 vec($follows->{$goto},0,1)=0;
719              
720 8538         15064 for my $predno (@{_Preds($states,$stateno,$pos-1)}) {
  8538         17360  
721 30297         87217 ++$rel->{$goto}{"$predno.$lhs"};
722             }
723             };
724             }
725             }
726             }
727 10         36 _Digraph($rel,$follows);
728              
729 10         1500 ($follows,$inconsistent)
730             }
731              
732             sub _ComputeLA {
733 10     10   25 my($grammar,$states)=@_;
734 10         17 my($termlst)= [ '',keys(%{$$grammar{TERM}}) ];
  10         160  
735              
736 10         40 my($follows,$inconsistent) = _ComputeFollows($grammar,$states,$termlst);
737              
738 10         161 for my $stateno ( keys(%$inconsistent ) ) {
739 397         1062 my($state)=$$states[$stateno];
740 397         569 my($conflict);
741              
742             #NB the sort is VERY important for conflicts resolution order
743 397         578 for my $ruleno (sort { $a <=> $b }
  41         121  
744 397         1546 @{$$state{ACTIONS}{''}}) {
745 436         1410 for my $term ( map { $termlst->[$_] } grep {
  7755         12192  
746 46056         72131 vec($follows->{"$stateno.$ruleno"},$_,1) }
747             0..$#$termlst) {
748 7755 100       15670 exists($$state{ACTIONS}{$term})
749             and ++$conflict;
750 7755         10118 push(@{$$state{ACTIONS}{$term}},-$ruleno);
  7755         20470  
751             }
752             }
753 397         894 delete($$state{ACTIONS}{''});
754             $conflict
755 397 100       1212 or delete($inconsistent->{$stateno});
756             }
757              
758             $inconsistent
759 10         174 }
760              
761             #############################
762             # Solve remaining conflicts #
763             #############################
764              
765             sub _SolveConflicts {
766 10     10   21 my($grammar,$states,$inconsistent)=@_;
767 10         16 my(%rulesprec,$RulePrec);
768 10         50 my($conflicts)={ SOLVED => {},
769             FORCED => { TOTAL => [ 0, 0 ],
770             DETAIL => {}
771             }
772             };
773              
774             $RulePrec = sub {
775 845     845   1290 my($ruleno)=@_;
776 845         1221 my($rhs,$rprec)=@{$$grammar{RULES}[$ruleno]}[1,2];
  845         1790  
777 845         1218 my($lastterm);
778              
779 845 100       1754 defined($rprec)
780             and return($rprec);
781              
782             exists($rulesprec{$ruleno})
783 612 100       1645 and return($rulesprec{$ruleno});
784              
785 67         147 $lastterm=(grep { exists($$grammar{TERM}{$_}) } @$rhs)[-1];
  137         364  
786              
787             defined($lastterm)
788             and ref($$grammar{TERM}{$lastterm})
789 67 100 66     292 and do {
790 48         134 $rulesprec{$ruleno}=$$grammar{TERM}{$lastterm}[1];
791 48         112 return($rulesprec{$ruleno});
792             };
793              
794 19         37 undef;
795 10         46 };
796              
797 10         65 for my $stateno (keys(%$inconsistent)) {
798 155         406 my($state)=$$states[$stateno];
799 155         372 my($actions)=$$state{ACTIONS};
800 155         219 my($nbsr,$nbrr);
801              
802 155         1264 for my $term ( keys(%$actions) ) {
803 5702         8886 my($act)=$$actions{$term};
804              
805 5702 100       11983 @$act > 1
806             or next;
807              
808             $$act[0] > 0
809             and ref($$grammar{TERM}{$term})
810 895 100 100     3793 and do {
811 842         1227 my($assoc,$tprec)=@{$$grammar{TERM}{$term}};
  842         1589  
812 842         1368 my($k,$error);
813              
814 842         1896 for ($k=1;$k<@$act;++$k) {
815 845         1280 my($ruleno)=-$$act[$k];
816 845         1476 my($rprec)=&$RulePrec($ruleno);
817              
818 845 100       1889 defined($rprec)
819             or next;
820              
821             ( $tprec > $rprec
822             or ( $tprec == $rprec and $assoc eq 'RIGHT'))
823 826 100 100     2514 and do {
      100        
824 542         735 push(@{$$conflicts{SOLVED}{$stateno}},
  542         1785  
825             [ $ruleno, $term, 'shift' ]);
826 542         921 splice(@$act,$k--,1);
827 542         1326 next;
828             };
829             ( $tprec < $rprec
830             or $assoc eq 'LEFT')
831 284 100 100     809 and do {
832 283         385 push(@{$$conflicts{SOLVED}{$stateno}},
  283         928  
833             [ $ruleno, $term, 'reduce' ]);
834             $$act[0] > 0
835 283 50       658 and do {
836 283         449 splice(@$act,0,1);
837 283         393 --$k;
838             };
839 283         629 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       4 and do {
846 1         2 splice(@$act,0,1);
847 1         2 ++$error;
848 1         3 --$k;
849             };
850             }
851 842 100       1799 $error
852             and unshift(@$act,undef);
853             };
854              
855             @$act > 1
856 895 100       2045 and do {
857 72         113 $nbrr += @$act - 2;
858 72 100       155 ($$act[0] > 0 ? $nbsr : $nbrr) += 1;
859 72         249 push(@{$$conflicts{FORCED}{DETAIL}{$stateno}{LIST}},
860 72         95 map { [ $term, $_ ] } splice(@$act,1));
  72         220  
861             };
862             }
863              
864             $nbsr
865 155 100       580 and do {
866 27         58 $$conflicts{FORCED}{TOTAL}[0]+=$nbsr;
867 27         76 $$conflicts{FORCED}{DETAIL}{$stateno}{TOTAL}[0]+=$nbsr;
868             };
869              
870             $nbrr
871 155 100       431 and do {
872 11         28 $$conflicts{FORCED}{TOTAL}[1]+=$nbrr;
873 11         33 $$conflicts{FORCED}{DETAIL}{$stateno}{TOTAL}[1]+=$nbrr;
874             };
875              
876             }
877              
878             $conflicts
879 10         133 }
880              
881             ###############################
882             # Make default reduce actions #
883             ###############################
884             sub _SetDefaults {
885 10     10   19 my($states)=@_;
886              
887 10         25 for my $state (@$states) {
888 1718         3738 my($actions)=$$state{ACTIONS};
889 1718         2533 my(%reduces,$default,$nodefault);
890              
891             exists($$actions{''})
892 1718 100       4115 and do {
893 751         1475 $$actions{''}[0] = -$$actions{''}[0];
894 751         1081 ++$nodefault;
895             };
896              
897             #shift error token => no default
898             exists($$actions{error})
899 1718 100 100     4655 and $$actions{error}[0] > 0
900             and ++$nodefault;
901              
902 1718         8013 for my $term (keys(%$actions)) {
903              
904 19432         35335 $$actions{$term}=$$actions{$term}[0];
905              
906             ( not defined($$actions{$term})
907 19432 100 100     86032 or $$actions{$term} > 0
      100        
908             or $nodefault)
909             and next;
910              
911 7059         9998 push(@{$reduces{$$actions{$term}}},$term);
  7059         16057  
912             }
913              
914 1718 100       5443 keys(%reduces) > 0
915             or next;
916              
917 416         853 $default=( map { $$_[0] }
918 40 50       208 sort { $$b[1] <=> $$a[1] or $$b[0] <=> $$a[0] }
919 377         881 map { [ $_, scalar(@{$reduces{$_}}) ] }
  416         594  
  416         1404  
920             keys(%reduces))[0];
921              
922 377         662 delete(@$actions{ @{$reduces{$default}} });
  377         1540  
923 377         1375 $$state{ACTIONS}{''}=$default;
924             }
925             }
926              
927             sub _LALR {
928 10     10   23 my($grammar,$states) = @_;
929 10         15 my($conflicts,$inconsistent);
930              
931 10         30 $inconsistent = _ComputeLA($grammar,$states);
932              
933 10         33 $conflicts = _SolveConflicts($grammar,$states,$inconsistent);
934 10         35 _SetDefaults($states);
935              
936 10         80 $conflicts
937             }
938              
939              
940             1;