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   1197 use Parse::Yapp::Grammar;
  3         9  
  3         95  
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         6  
  3         50  
61              
62 3     3   14 use Carp;
  3         5  
  3         10409  
63              
64             ###############
65             # Constructor #
66             ###############
67             sub new {
68 10     10 0 1858 my($class)=shift;
69              
70 10 50       32 ref($class)
71             and $class=ref($class);
72              
73 10         60 my($self)=$class->SUPER::new(@_);
74 10         65 $self->_Compile();
75 10         44 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 18 my($self)=shift;
296 9         17 my($states)=$$self{STATES};
297 9         16 my($stateno);
298             my($text);
299              
300 9         17 $text="[\n\t{";
301              
302             $text.=join("\n\t},\n\t{",
303             map {
304 9         16 my($state)=$_;
  107         147  
305 107         144 my($text);
306              
307 107         175 $text="#State ".$stateno++."\n\t\t";
308              
309             ( not exists($$state{ACTIONS}{''})
310 63         219 or keys(%{$$state{ACTIONS}}) > 1)
311 107 100 100     250 and do {
312              
313 57         85 $text.="ACTIONS => {\n\t\t\t";
314              
315             $text.=join(",\n\t\t\t",
316             map {
317 126         227 my($term,$action)=($_,$$state{ACTIONS}{$_});
318 126         152 my($text);
319              
320 126 100       245 if(substr($term,0,1) eq "'") {
321 96         155 $term=~s/([\@\$\"])/\\$1/g;
322 96         271 $term=~s/^'|'$/"/g;
323             }
324             else {
325 30 100       65 $term= $term eq chr(0)
326             ? "''"
327             : "'$term'";
328             }
329              
330 126 100       227 if(defined($action)) {
331 125         168 $action=int($action);
332             }
333             else {
334 1         3 $action='undef';
335             }
336              
337 126         282 "$term => $action";
338            
339 57         75 } grep { $_ } keys(%{$$state{ACTIONS}}));
  139         248  
  57         115  
340              
341 57         102 $text.="\n\t\t}";
342             };
343              
344             exists($$state{ACTIONS}{''})
345 107 100       225 and do {
346 63 100       75 keys(%{$$state{ACTIONS}}) > 1
  63         143  
347             and $text.=",\n\t\t";
348              
349 63         118 $text.="DEFAULT => $$state{ACTIONS}{''}";
350             };
351              
352             exists($$state{GOTOS})
353 107 100       213 and do {
354 29         47 $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         56 my($text);
359              
360 43         104 "'$nterm' => $stateno";
361            
362 29         46 } keys(%{$$state{GOTOS}}));
  29         59  
363 29         48 $text.="\n\t\t}";
364             };
365              
366 107         202 $text;
367              
368             }@$states);
369              
370 9         33 $text.="\n\t}\n]";
371              
372 9         27 $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         20 my($grammar,$states);
383              
384 10         24 $grammar=$self->{GRAMMAR};
385              
386 10         33 $states = _LR0($grammar);
387              
388 10         36 $self->{CONFLICTS} = _LALR($grammar,$states);
389              
390 10         25 $self->{STATES}=$states;
391             }
392              
393             #########################
394             # LR0 States Generation #
395             #########################
396             #
397             ###########################
398             # General digraph routine #
399             ###########################
400             sub _Digraph {
401 30     30   56 my($rel,$F)=@_;
402 30         50 my(%N,@S);
403 30         48 my($infinity)=(~(1<<31));
404 30         52 my($Traverse);
405              
406             $Traverse = sub {
407 10659     10659   16523 my($x,$d)=@_;
408 10659         13595 my($y);
409              
410 10659         14578 push(@S,$x);
411 10659         18232 $N{$x}=$d;
412              
413             exists($$rel{$x})
414 10659 100       22908 and do {
415 9183         11934 for $y (keys(%{$$rel{$x}})) {
  9183         30201  
416 42992 100       91941 exists($N{$y})
417             or &$Traverse($y,$d+1);
418              
419             $N{$y} < $N{$x}
420 42992 100       82489 and $N{$x} = $N{$y};
421              
422 42992         79575 $$F{$x}|=$$F{$y};
423             }
424             };
425              
426             $N{$x} == $d
427 10659 100       22869 and do {
428 10322         13171 for(;;) {
429 10659         14525 $y=pop(@S);
430 10659         14975 $N{$y}=$infinity;
431 10659 100       23137 $y eq $x
432             and last;
433 337         484 $$F{$y}=$$F{$x};
434             }
435             };
436 30         128 };
437              
438 30         3316 for (keys(%$rel)) {
439 9183 100       20707 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   21 my($grammar)=@_;
460 10         19 my($rel,$closures);
461              
462 10         18 for my $symbol (keys(%{$$grammar{NTERM}})) {
  10         68  
463 270         381 $closures->{$symbol}=pack('b'.@{$$grammar{RULES}});
  270         644  
464              
465 270         408 for my $ruleno (@{$$grammar{NTERM}{$symbol}}) {
  270         465  
466 888         1311 my($rhs)=$$grammar{RULES}[$ruleno][1];
467              
468 888         1622 vec($closures->{$symbol},$ruleno,1)=1;
469              
470             @$rhs > 0
471             and exists($$grammar{NTERM}{$$rhs[0]})
472 888 100 100     3842 and ++$rel->{$symbol}{$$rhs[0]};
473             }
474             }
475 10         44 _Digraph($rel,$closures);
476              
477 10         27 $closures
478             }
479              
480             sub _Closures {
481 1718     1718   2868 my($grammar,$core,$closures)=@_;
482 1718         2574 my($ruleset)=pack('b'.@{$$grammar{RULES}});
  1718         5475  
483              
484 1718         3673 for (@$core) {
485 3895         8716 my($ruleno,$pos)=@$_;
486 3895         6903 my($rhs)=$$grammar{RULES}[$ruleno][1];
487              
488             $pos < @$rhs
489             and exists($closures->{$$rhs[$pos]})
490 3895 100 100     17548 and $ruleset|=$closures->{$$rhs[$pos]};
491             }
492 56069         101941 [ @$core, map { [ $_, 0 ] }
493 1330007         1749211 grep { vec($ruleset,$_,1) }
494 1718         2945 0..$#{$$grammar{RULES}} ];
  1718         18370  
495             }
496              
497             sub _Transitions {
498 1718     1718   3148 my($grammar,$cores,$closures,$states,$stateno)=@_;
499 1718         3787 my($core)=$$states[$stateno]{'CORE'};
500 1718         2440 my(%transitions);
501              
502 1718         2638 for (@{_Closures($grammar,$core,$closures)}) {
  1718         3150  
503 59964         96646 my($ruleno,$pos)=@$_;
504 59964         93574 my($rhs)=$$grammar{RULES}[$ruleno][1];
505              
506             $pos == @$rhs
507 59964 100       113417 and do {
508 1187         1705 push(@{$$states[$stateno]{ACTIONS}{''}},$ruleno);
  1187         4278  
509 1187         2509 next;
510             };
511 58777         75151 push(@{$transitions{$$rhs[$pos]}},[ $ruleno, $pos+1 ]);
  58777         145947  
512             }
513              
514 1718         12581 for (keys(%transitions)) {
515 23159         38149 my($symbol,$core)=($_,$transitions{$_});
516 58777         126264 my($corekey)=join(',',map { join('.',@$_) }
517 23159 50       40792 sort { $$a[0] <=> $$b[0]
  51997         103737  
518             or $$a[1] <=> $$b[1] }
519             @$core);
520 23159         36416 my($tostateno);
521              
522             exists($cores->{$corekey})
523 23159 100       53023 or do {
524 1708         3933 push(@$states,{ 'CORE' => $core });
525 1708         4104 $cores->{$corekey}=$#$states;
526             };
527              
528 23159         34308 $tostateno=$cores->{$corekey};
529 23159         30124 push(@{$$states[$tostateno]{FROM}},$stateno);
  23159         44481  
530              
531             exists($$grammar{TERM}{$_})
532 23159 100       48419 and do {
533 11824         23507 $$states[$stateno]{ACTIONS}{$_} = [ $tostateno ];
534 11824         28575 next;
535             };
536 11335         29618 $$states[$stateno]{GOTOS}{$_} = $tostateno;
537             }
538             }
539              
540             sub _LR0 {
541 10     10   20 my($grammar)=@_;
542 10         22 my($states) = [];
543 10         20 my($stateno);
544             my($closures); #$closures={ nterm => ruleset,... }
545 10         19 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         42 push(@$states,{ 'CORE' => [ [ 0, 0 ] ] });
551 10         37 for($stateno=0;$stateno<@$states;++$stateno) {
552 1718         4075 _Transitions($grammar,$cores,$closures,$states,$stateno);
553             }
554              
555 10         691 $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   24 my($grammar,$termlst,$terminx)=@_;
574 10         22 my($rel,$first)=( {}, {} );
575              
576 10         16 for my $symbol (keys(%{$$grammar{NTERM}})) {
  10         76  
577 270         734 $first->{$symbol}=pack('b'.@$termlst);
578              
579             RULE:
580 270         371 for my $ruleno (@{$$grammar{NTERM}{$symbol}}) {
  270         535  
581 888         1601 my($rhs)=$$grammar{RULES}[$ruleno][1];
582              
583 888         1422 for (@$rhs) {
584             exists($terminx->{$_})
585 868 100       1778 and do {
586 299         582 vec($first->{$symbol},$terminx->{$_},1)=1;
587 299         616 next RULE;
588             };
589 569         970 ++$rel->{$symbol}{$_};
590 569 100       1334 exists($$grammar{NULLABLE}{$_})
591             or next RULE;
592             }
593 47         400 vec($first->{$symbol},0,1)=1;
594             }
595             }
596 10         60 _Digraph($rel,$first);
597              
598 10         35 $first
599             }
600              
601             sub _Preds {
602 8974     8974   14364 my($states,$stateno,$len)=@_;
603 8974         12152 my($queue, $preds);
604              
605 8974 100       26156 $len
606             or return [ $stateno ];
607              
608 1004         2349 $queue=[ [ $stateno, $len ] ];
609 1004         2429 while(@$queue) {
610 3862         5800 my($pred) = shift(@$queue);
611 3862         6059 my($stateno, $len) = @$pred;
612              
613             $len == 1
614 3862 100       7604 and do {
615 3169         4244 push(@$preds,@{$states->[$stateno]{FROM}});
  3169         9709  
616 3169         7234 next;
617             };
618              
619 2858         6173 push(@$queue, map { [ $_, $len - 1 ] }
620 693         1023 @{$states->[$stateno]{FROM}});
  693         1699  
621             }
622              
623             # Pass @$preds through a hash to ensure unicity
624 1004         1456 [ keys( %{ +{ map { ($_,1) } @$preds } } ) ];
  1004         1744  
  34302         73648  
625             }
626              
627             sub _FirstSfx {
628 1235     1235   2301 my($grammar,$firstset,$termlst,$terminx,$ruleno,$pos,$key)=@_;
629 1235         2703 my($first)=pack('b'.@$termlst);
630 1235         2821 my($rhs)=$$grammar{RULES}[$ruleno][1];
631              
632 1235         3070 for (;$pos < @$rhs;++$pos) {
633             exists($terminx->{$$rhs[$pos]})
634 817 100       2293 and do {
635 395         911 vec($first,$terminx->{$$rhs[$pos]},1)=1;
636 395         1325 return($first);
637             };
638 422         894 $first|=$firstset->{$$rhs[$pos]};
639              
640 422 100       1084 vec($first,0,1)
641             and vec($first,0,1)=0;
642              
643 422 100       1639 exists($$grammar{NULLABLE}{$$rhs[$pos]})
644             or return($first);
645              
646             }
647 597         1399 vec($first,0,1)=1;
648 597         1923 $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         19 my($firstset,$terminx);
670 10         27 my($inconsistent, $rel, $follows, $sfx)= ( {}, {}, {}, {} );
671              
672 10         36 %$terminx= map { ($termlst->[$_],$_) } 0..$#$termlst;
  174         294  
673              
674 10         42 $firstset=_SetFirst($grammar,$termlst,$terminx);
675              
676 10         32 for my $stateno (0..$#$states) {
677 1718         3021 my($state)=$$states[$stateno];
678              
679             exists($$state{ACTIONS}{''})
680             and ( @{$$state{ACTIONS}{''}} > 1
681             or keys(%{$$state{ACTIONS}}) > 1 )
682 1718 100 100     5298 and do {
      100        
683 397         1074 ++$inconsistent->{$stateno};
684              
685 397         589 for my $ruleno (@{$$state{ACTIONS}{''}}) {
  397         879  
686 436         692 my($lhs,$rhs)=@{$$grammar{RULES}[$ruleno]}[0,1];
  436         1347  
687              
688 436         810 for my $predno (@{_Preds($states,$stateno,scalar(@$rhs))}) {
  436         949  
689 11975         24874 ++$rel->{"$stateno.$ruleno"}{"$predno.$lhs"};
690             }
691             }
692             };
693              
694             exists($$state{GOTOS})
695 1718 100       4213 or next;
696              
697 656         1000 for my $symbol (keys(%{$$state{GOTOS}})) {
  656         3620  
698 11335         21133 my($tostate)=$$states[$$state{GOTOS}{$symbol}];
699 11335         19056 my($goto)="$stateno.$symbol";
700              
701 11335         33627 $follows->{$goto}=pack('b'.@$termlst);
702              
703 11335         15947 for my $item (@{$$tostate{'CORE'}}) {
  11335         21723  
704 39723         68808 my($ruleno,$pos)=@$item;
705 39723         58492 my($key)="$ruleno.$pos";
706              
707             exists($sfx->{$key})
708 39723 100       82748 or $sfx->{$key} = _FirstSfx($grammar,$firstset,
709             $termlst,$terminx,
710             $ruleno,$pos,$key);
711              
712 39723         64487 $follows->{$goto}|=$sfx->{$key};
713              
714             vec($follows->{$goto},0,1)
715 39723 100       86419 and do {
716 8538         15527 my($lhs)=$$grammar{RULES}[$ruleno][0];
717              
718 8538         17907 vec($follows->{$goto},0,1)=0;
719              
720 8538         14051 for my $predno (@{_Preds($states,$stateno,$pos-1)}) {
  8538         15834  
721 30297         77522 ++$rel->{$goto}{"$predno.$lhs"};
722             }
723             };
724             }
725             }
726             }
727 10         37 _Digraph($rel,$follows);
728              
729 10         1610 ($follows,$inconsistent)
730             }
731              
732             sub _ComputeLA {
733 10     10   21 my($grammar,$states)=@_;
734 10         18 my($termlst)= [ '',keys(%{$$grammar{TERM}}) ];
  10         155  
735              
736 10         42 my($follows,$inconsistent) = _ComputeFollows($grammar,$states,$termlst);
737              
738 10         150 for my $stateno ( keys(%$inconsistent ) ) {
739 397         947 my($state)=$$states[$stateno];
740 397         523 my($conflict);
741              
742             #NB the sort is VERY important for conflicts resolution order
743 397         536 for my $ruleno (sort { $a <=> $b }
  41         107  
744 397         1548 @{$$state{ACTIONS}{''}}) {
745 436         1265 for my $term ( map { $termlst->[$_] } grep {
  7755         11269  
746 46056         69315 vec($follows->{"$stateno.$ruleno"},$_,1) }
747             0..$#$termlst) {
748 7755 100       14686 exists($$state{ACTIONS}{$term})
749             and ++$conflict;
750 7755         9754 push(@{$$state{ACTIONS}{$term}},-$ruleno);
  7755         19271  
751             }
752             }
753 397         816 delete($$state{ACTIONS}{''});
754             $conflict
755 397 100       1082 or delete($inconsistent->{$stateno});
756             }
757              
758             $inconsistent
759 10         196 }
760              
761             #############################
762             # Solve remaining conflicts #
763             #############################
764              
765             sub _SolveConflicts {
766 10     10   22 my($grammar,$states,$inconsistent)=@_;
767 10         21 my(%rulesprec,$RulePrec);
768 10         54 my($conflicts)={ SOLVED => {},
769             FORCED => { TOTAL => [ 0, 0 ],
770             DETAIL => {}
771             }
772             };
773              
774             $RulePrec = sub {
775 845     845   1250 my($ruleno)=@_;
776 845         1070 my($rhs,$rprec)=@{$$grammar{RULES}[$ruleno]}[1,2];
  845         1593  
777 845         1134 my($lastterm);
778              
779 845 100       1717 defined($rprec)
780             and return($rprec);
781              
782             exists($rulesprec{$ruleno})
783 612 100       1483 and return($rulesprec{$ruleno});
784              
785 67         134 $lastterm=(grep { exists($$grammar{TERM}{$_}) } @$rhs)[-1];
  137         329  
786              
787             defined($lastterm)
788             and ref($$grammar{TERM}{$lastterm})
789 67 100 66     263 and do {
790 48         118 $rulesprec{$ruleno}=$$grammar{TERM}{$lastterm}[1];
791 48         99 return($rulesprec{$ruleno});
792             };
793              
794 19         35 undef;
795 10         58 };
796              
797 10         67 for my $stateno (keys(%$inconsistent)) {
798 155         372 my($state)=$$states[$stateno];
799 155         348 my($actions)=$$state{ACTIONS};
800 155         224 my($nbsr,$nbrr);
801              
802 155         1125 for my $term ( keys(%$actions) ) {
803 5702         8180 my($act)=$$actions{$term};
804              
805 5702 100       11125 @$act > 1
806             or next;
807              
808             $$act[0] > 0
809             and ref($$grammar{TERM}{$term})
810 895 100 100     3508 and do {
811 842         1120 my($assoc,$tprec)=@{$$grammar{TERM}{$term}};
  842         1483  
812 842         1188 my($k,$error);
813              
814 842         1746 for ($k=1;$k<@$act;++$k) {
815 845         1244 my($ruleno)=-$$act[$k];
816 845         1396 my($rprec)=&$RulePrec($ruleno);
817              
818 845 100       1660 defined($rprec)
819             or next;
820              
821             ( $tprec > $rprec
822             or ( $tprec == $rprec and $assoc eq 'RIGHT'))
823 826 100 100     2187 and do {
      100        
824 542         691 push(@{$$conflicts{SOLVED}{$stateno}},
  542         1644  
825             [ $ruleno, $term, 'shift' ]);
826 542         875 splice(@$act,$k--,1);
827 542         1195 next;
828             };
829             ( $tprec < $rprec
830             or $assoc eq 'LEFT')
831 284 100 100     713 and do {
832 283         377 push(@{$$conflicts{SOLVED}{$stateno}},
  283         819  
833             [ $ruleno, $term, 'reduce' ]);
834             $$act[0] > 0
835 283 50       899 and do {
836 283         408 splice(@$act,0,1);
837 283         365 --$k;
838             };
839 283         649 next;
840             };
841 1         2 push(@{$$conflicts{SOLVED}{$stateno}},
  1         4  
842             [ $ruleno, $term, 'error' ]);
843 1         4 splice(@$act,$k--,1);
844             $$act[0] > 0
845 1 50       3 and do {
846 1         2 splice(@$act,0,1);
847 1         2 ++$error;
848 1         3 --$k;
849             };
850             }
851 842 100       1649 $error
852             and unshift(@$act,undef);
853             };
854              
855             @$act > 1
856 895 100       1856 and do {
857 72         113 $nbrr += @$act - 2;
858 72 100       126 ($$act[0] > 0 ? $nbsr : $nbrr) += 1;
859 72         222 push(@{$$conflicts{FORCED}{DETAIL}{$stateno}{LIST}},
860 72         102 map { [ $term, $_ ] } splice(@$act,1));
  72         222  
861             };
862             }
863              
864             $nbsr
865 155 100       495 and do {
866 27         52 $$conflicts{FORCED}{TOTAL}[0]+=$nbsr;
867 27         63 $$conflicts{FORCED}{DETAIL}{$stateno}{TOTAL}[0]+=$nbsr;
868             };
869              
870             $nbrr
871 155 100       355 and do {
872 11         21 $$conflicts{FORCED}{TOTAL}[1]+=$nbrr;
873 11         26 $$conflicts{FORCED}{DETAIL}{$stateno}{TOTAL}[1]+=$nbrr;
874             };
875              
876             }
877              
878             $conflicts
879 10         115 }
880              
881             ###############################
882             # Make default reduce actions #
883             ###############################
884             sub _SetDefaults {
885 10     10   20 my($states)=@_;
886              
887 10         25 for my $state (@$states) {
888 1718         3030 my($actions)=$$state{ACTIONS};
889 1718         2301 my(%reduces,$default,$nodefault);
890              
891             exists($$actions{''})
892 1718 100       3793 and do {
893 751         1313 $$actions{''}[0] = -$$actions{''}[0];
894 751         1035 ++$nodefault;
895             };
896              
897             #shift error token => no default
898             exists($$actions{error})
899 1718 100 100     4119 and $$actions{error}[0] > 0
900             and ++$nodefault;
901              
902 1718         6710 for my $term (keys(%$actions)) {
903              
904 19432         31184 $$actions{$term}=$$actions{$term}[0];
905              
906             ( not defined($$actions{$term})
907 19432 100 100     79688 or $$actions{$term} > 0
      100        
908             or $nodefault)
909             and next;
910              
911 7059         9255 push(@{$reduces{$$actions{$term}}},$term);
  7059         13852  
912             }
913              
914 1718 100       4559 keys(%reduces) > 0
915             or next;
916              
917 416         792 $default=( map { $$_[0] }
918 41 50       137 sort { $$b[1] <=> $$a[1] or $$b[0] <=> $$a[0] }
919 377         731 map { [ $_, scalar(@{$reduces{$_}}) ] }
  416         546  
  416         1072  
920             keys(%reduces))[0];
921              
922 377         599 delete(@$actions{ @{$reduces{$default}} });
  377         1303  
923 377         1110 $$state{ACTIONS}{''}=$default;
924             }
925             }
926              
927             sub _LALR {
928 10     10   20 my($grammar,$states) = @_;
929 10         19 my($conflicts,$inconsistent);
930              
931 10         32 $inconsistent = _ComputeLA($grammar,$states);
932              
933 10         35 $conflicts = _SolveConflicts($grammar,$states,$inconsistent);
934 10         42 _SetDefaults($states);
935              
936 10         79 $conflicts
937             }
938              
939              
940             1;