File Coverage

blib/lib/Parse/Eyapp/Lalr.pm
Criterion Covered Total %
statement 349 554 63.0
branch 104 196 53.0
condition 35 45 77.7
subroutine 24 31 77.4
pod 0 9 0.0
total 512 835 61.3


line stmt bran cond sub pod time code
1             package Parse::Eyapp::Lalr;
2             @ISA=qw( Parse::Eyapp::Grammar );
3              
4             require 5.004;
5              
6 61     61   68752 use Parse::Eyapp::Grammar;
  61         268  
  61         3422  
7 61     61   470 use Data::Dumper;
  61         177  
  61         5444  
8              
9             # Parse::Eyapp::Compile Object Structure:
10             # --------------------------------------
11             # {
12             # GRAMMAR => Parse::Eyapp::Grammar,
13             # STATES => [ { CORE => [ items... ],
14             # ACTIONS => { term => action }
15             # GOTOS => { nterm => stateno }
16             # }... ]
17             # CONFLICTS=>{ SOLVED => { stateno => [ ruleno, token, solved ] },
18             # FORCED => { TOTAL => [ nbsr, nbrr ],
19             # DETAIL => { stateno => { TOTAL => [ nbsr, nbrr ] }
20             # LIST => [ ruleno, token ]
21             # }
22             # }
23             # }
24             # }
25             #
26             # 'items' are of form: [ ruleno, dotpos ]
27             # 'term' in ACTIONS is '' means default action
28             # 'action' may be:
29             # undef: explicit error (nonassociativity)
30             # 0 : accept
31             # >0 : shift and go to state 'action'
32             # <0 : reduce using rule -'action'
33             # 'solved' may have values of:
34             # 'shift' if solved as Shift
35             # 'reduce' if solved as Reduce
36             # 'error' if solved by discarding both Shift and Reduce (nonassoc)
37             #
38             # SOLVED is a set of states containing Solved conflicts
39             # FORCED are forced conflict resolutions
40             #
41             # nbsr and nbrr are number of shift/reduce and reduce/reduce conflicts
42             #
43             # TOTAL is the total number of SR/RR conflicts for the parser
44             #
45             # DETAIL is the detail of conflicts for each state
46             # TOTAL is the total number of SR/RR conflicts for a state
47             # LIST is the list of discarded reductions (for display purpose only)
48              
49              
50 61     61   460 use strict;
  61         135  
  61         4895  
51              
52 61     61   376 use Carp;
  61         127  
  61         549537  
53              
54             ###############
55             # Constructor #
56             ###############
57             sub new {
58 54     54 0 171 my($class)=shift;
59              
60 54 50       258 ref($class)
61             and $class=ref($class);
62              
63 54         1059 my($self)=$class->SUPER::new(@_);
64 54         999 $self->_Compile();
65 54         1011 $self->_DynamicConflicts(); # call it only if dynamic conflict handlers
66              
67 54 50       456 if ($self->Option('prefix')) {
68             # weak accept for nested parsing !!!!!!!!!!!!
69             # substitute End Of Input by DEFAULT for each state
70 0         0 for (@{$self->{STATES}}) {
  0         0  
71 0 0       0 if (exists($_->{ACTIONS}{"\c@"})) {
72             # what if DEFAULT action already exists ?
73             # Shall I have to use an option in eyapp????
74 0         0 $_->{ACTIONS}{''} = $_->{ACTIONS}{"\c@"};
75 0         0 delete($_->{ACTIONS}{"\c@"});
76             }
77             }
78             }
79              
80 54         634 bless($self,$class);
81             }
82             ###########
83             # Methods #
84             ###########
85              
86             ###########################
87             # Method To View Warnings #
88             ###########################
89             sub Warnings {
90 4     4 0 73 my($self)=shift;
91 4         14 my($text) = '';
92              
93             # $nbsr = number of shift-reduce conflicts
94             # $nbrr = number of reduce-reduce conflicts
95 4         8 my($nbsr,$nbrr)=@{$$self{CONFLICTS}{FORCED}{TOTAL}};
  4         29  
96              
97 4         55 $text=$self->SUPER::Warnings();
98              
99 4         20 my $expected = $$self{GRAMMAR}{EXPECT};
100 4 50       25 my ($sre, $rre) = ref($expected) ? @$expected : ($expected, 0);
101              
102 4 100 66     31 $nbsr != $sre and $nbsr > 0 and do {
103 1 50       8 $text.="$nbsr shift/reduce conflict".($nbsr > 1 ? "s " : " ");
104             }; # end of $nbsr != $sre There were shift-reduce conflicts
105              
106 4 50 33     23 $nbrr != $rre and $nbrr > 0 and do {
107 0 0       0 $nbsr != $sre and $text.="and ";
108 0 0       0 $text.="$nbrr reduce/reduce conflict".($nbrr > 1 ? "s" : "");
109             };
110              
111 4         18 $text;
112             }
113             #############################
114             # Method To View DFA States #
115             #############################
116             sub ShowDfa {
117 0     0 0 0 my($self)=shift;
118 0         0 my($text) = '';
119 0         0 my($grammar,$states)=($$self{GRAMMAR}, $$self{STATES});
120              
121 0         0 for my $stateno (0..$#$states) {
122 0         0 my(@shifts,@reduces,@errors,$default);
123              
124 0         0 $text.="State $stateno:\n\n";
125              
126             #Dump Kernel Items
127 0 0       0 for (sort { $$a[0] <=> $$b[0]
  0         0  
  0         0  
128             or $$a[1] <=> $$b[1] } @{$$states[$stateno]{'CORE'}}) {
129 0         0 my($ruleno,$pos)=@$_;
130 0         0 my($lhs,$rhs)=@{$$grammar{RULES}[$ruleno]}[0,1];
  0         0  
131 0         0 my(@rhscopy)=@$rhs;
132            
133 0 0       0 $ruleno
134             or $rhscopy[-1] = '$end';
135              
136 0         0 splice(@rhscopy,$pos,0,'.');
137 0         0 $text.= "\t$lhs -> ".join(' ',@rhscopy)."\t(Rule $ruleno)\n";
138             }
139              
140             #Prepare Actions
141 0         0 for (keys(%{$$states[$stateno]{ACTIONS}})) {
  0         0  
142 0         0 my($term,$action)=($_,$$states[$stateno]{ACTIONS}{$_});
143              
144 0 0       0 $term eq chr(0)
145             and $term = '$end';
146              
147             not defined($action)
148 0 0       0 and do {
149 0         0 push(@errors,$term);
150 0         0 next;
151             };
152              
153             $action > 0
154 0 0       0 and do {
155 0         0 push(@shifts,[ $term, $action ]);
156 0         0 next;
157             };
158              
159 0         0 $action = -$action;
160              
161             $term
162 0 0       0 or do {
163 0         0 $default= [ '$default', $action ];
164 0         0 next;
165             };
166              
167 0         0 push(@reduces,[ $term, $action ]);
168             }
169              
170             #Dump shifts
171             @shifts
172 0 0       0 and do {
173 0         0 $text.="\n";
174 0         0 for (sort { $$a[0] cmp $$b[0] } @shifts) {
  0         0  
175 0         0 my($term,$shift)=@$_;
176              
177 0         0 $text.="\t$term\tshift, and go to state $shift\n";
178             }
179             };
180              
181             #Dump errors
182             @errors
183 0 0       0 and do {
184 0         0 $text.="\n";
185 0         0 for my $term (sort { $a cmp $b } @errors) {
  0         0  
186 0         0 $text.="\t$term\terror (nonassociative)\n";
187             }
188             };
189              
190             #Prepare reduces
191 0         0 exists($$self{CONFLICTS}{FORCED}{DETAIL}{$stateno})
192 0 0       0 and push(@reduces,@{$$self{CONFLICTS}{FORCED}{DETAIL}{$stateno}{LIST}});
193              
194 0 0       0 @reduces=sort { $$a[0] cmp $$b[0] or $$a[1] <=> $$b[1] } @reduces;
  0         0  
195              
196 0 0       0 defined($default)
197             and push(@reduces,$default);
198              
199             #Dump reduces
200             @reduces
201 0 0       0 and do {
202 0         0 $text.="\n";
203 0         0 for (@reduces) {
204 0         0 my($term,$ruleno)=@$_;
205 0         0 my($discard);
206              
207             $ruleno < 0
208 0 0       0 and do {
209 0         0 ++$discard;
210 0         0 $ruleno = -$ruleno;
211             };
212              
213 0 0       0 $term eq chr(0)
214             and $term = '$end';
215              
216 0 0       0 $text.= "\t$term\t".($discard ? "[" : "");
217 0 0       0 if($ruleno) {
218 0         0 $text.= "reduce using rule $ruleno ".
219             "($$grammar{RULES}[$ruleno][0])";
220             }
221             else {
222 0         0 $text.='accept';
223             }
224 0 0       0 $text.=($discard ? "]" : "")."\n";
225             }
226             };
227              
228             #Dump gotos
229             exists($$states[$stateno]{GOTOS})
230 0 0       0 and do {
231 0         0 $text.= "\n";
232 0         0 for (keys(%{$$states[$stateno]{GOTOS}})) {
  0         0  
233 0         0 $text.= "\t$_\tgo to state $$states[$stateno]{GOTOS}{$_}\n";
234             }
235             };
236              
237 0         0 $text.="\n";
238             }
239 0         0 $text;
240             }
241              
242             ####################################################################
243             # Usage : $parser->outputtables($path, $base)
244             # Purpose : Gives support to eyapp option -v
245             # Parameters : The parser object plus the $path and $base names for the .output
246             # file
247              
248             sub outputtables {
249 0     0 0 0 my ($parser, $path, $base) = @_;
250              
251 0 0       0 my($output)=$base?"$path$base.output":"STDOUT";
252 0         0 my($tmp);
253              
254 0 0       0 open(my $OUT,">$output")
255             or die "Cannot create $base.output for writing.\n";
256              
257 0 0       0 $tmp=$parser->Warnings()
258             and print $OUT "Warnings:\n---------\n$tmp\n";
259 0 0       0 $tmp=$parser->Conflicts()
260             and print $OUT "Conflicts:\n----------\n$tmp\n";
261 0         0 print $OUT "Rules:\n------\n";
262 0         0 print $OUT $parser->ShowRules()."\n";
263 0         0 print $OUT "States:\n-------\n";
264 0         0 print $OUT $parser->ShowDfa()."\n";
265 0         0 print $OUT "Summary:\n--------\n";
266 0         0 print $OUT $parser->Summary();
267              
268 0         0 close($OUT);
269             }
270              
271             sub outputDot {
272 0     0 0 0 my ($parser, $path, $base, $labelWithCore) = @_;
273              
274 0 0       0 my ($output)=$base?"$path$base.dot":"STDOUT";
275              
276 0 0       0 open(my $OUT,">$output")
277             or die "Cannot create $base.dot for writing.\n";
278              
279 0         0 my $graph = '';
280              
281 0         0 my $dfa = $parser->ShowDfa();
282              
283             #warn "$dfa\n";
284              
285 0         0 my $grammar = $parser->ShowRules()."\n";
286              
287             #warn "$grammar\n";
288              
289             # make an array from the grammar
290              
291 0         0 my %grammar = $grammar =~ m{(\d+):\s+(.*)}gx;
292              
293             # escape double quotes inside %grammar
294 0         0 $graph .= qq{ "g0" [label="0: $grammar{0}", shape = doubleoctagon, fontcolor=blue, color=blue ]\n};
295 0         0 for (1 .. (keys %grammar)-1) {
296 0         0 $grammar{$_} =~ s/\\/\\\\/g; # escape escapes
297 0         0 $grammar{$_} =~ s/"/\\"/g; # escape double quotes
298              
299             #warn "$_ => $grammar{$_}\n";
300              
301 0         0 $graph .= qq{ "g$_" [label="$_: $grammar{$_}", shape = box, fontcolor=blue, color=blue ]\n};
302             }
303              
304 0         0 for (0 .. (keys %grammar)-2) {
305 0         0 my $n = $_+1;
306 0         0 $graph .= qq{ g$_ ->g$n [style=dotted];\n};
307             }
308              
309 0         0 my $conflicts = $parser->Conflicts();
310              
311             #warn $conflicts;
312            
313             # State 13 contains 5 shift/reduce conflicts
314             # State 23 contains 5 shift/reduce conflicts
315 0         0 my @conflictstates = $conflicts =~ m{State\s+(\d+)\s+contains\s+\d+\s+(?:shift|reduce)/reduce\s+conflicts?\s*}gx;
316              
317             #warn "(@conflictstates)\n";
318              
319 0         0 $graph .= qq{$_ [shape = diamond, fontcolor=red, color=red]\n} for @conflictstates;
320              
321 0         0 my %states = ($dfa =~ m{State\s*(\d+)\s*:\n\s*
322             (
323             (?:
324             .*->.* | # a production line
325             .*go\s+to.* | # a shift or a goto line
326             .*reduce.* | # a reduce line
327             .*accept.* | # an accept line
328             \s+ | # white lines
329             )+
330             )
331             }gx);
332              
333 0         0 for (sort { $a <=> $b } keys %states) {
  0         0  
334 0         0 my $desc = $states{$_};
335 0         0 my @LRitems = $desc =~ m{(\S.*->.*[^\s.])\s+\(Rule\s+\d+\)}g; # remove productions
336              
337             # label states with core LR-0 items
338 0 0       0 if ($labelWithCore) { # this is optional
339 0         0 local $" = "\\n";
340 0         0 $graph .= qq{$_ [ label = "$_\\n@LRitems"}; #shape = plaintext,
341 0         0 my $s = $_;
342 0 0       0 $graph .= qq{, shape = plaintext} unless (grep { $_ eq $s} @conflictstates);
  0         0  
343 0         0 $graph .= "]\n";
344             }
345              
346             #warn "LRitems in $_:\n@LRitems\n";
347              
348 0         0 $desc =~ s/\n\s*\n/\n/g; # remove white lines
349              
350             # build digraph
351             # ID shift, and go to state 4
352 0         0 while ($desc =~ m{\t(.*)\s+shift,\s+and\s+go\s+to\s+state\s+(\d+)}gx) {
353 0         0 my ($label, $state) = ($1, $2);
354 0         0 $label =~ s/\\(?!")/\\\\/g;
355 0         0 $graph .= qq{$_ -> $state [label = "$label"]\n};
356             }
357              
358             # decl go to state 1
359 0         0 while ($desc =~ m{\t(\S+)\s+go\s+to\s+state\s+(\d+)}gx) {
360 0         0 $graph .= qq{$_ -> $2 [label = "$1", arrowhead = odot, color = "red", fontcolor = "red"]\n};
361             }
362              
363             # $default reduce using rule 1 (prog)
364             # ID reduce using rule 15 (decORexp_explorer)
365 0         0 while ($desc =~ m{\t(\S+)\s+reduce\s+using\s+rule\s+(\d+)}gx) {
366 0         0 $graph .= qq{$_ -> "g$2" [label = "$1", arrowhead=dot, color = "blue", fontcolor = "blue"]\n};
367             }
368              
369             # shift-reduce conflicts
370             # ';' [reduce using rule 4 (ds)]
371 0         0 while ($desc =~ m{\t(\S+)\s+\[\s*reduce\s+using\s+rule\s+(\d+)}gx) {
372 0         0 $graph .=
373             qq{$_ -> "g$2" [label = "$1", arrowhead=dot, style=dotted, color = "red", fontcolor = "red"]\n};
374             }
375              
376             # $default accept
377 0 0       0 if ($desc =~ m{\t\$default\s+accept\s*}gx) {
378 0         0 $graph .= qq{$_ [shape = doublecircle]\n};
379 0         0 $graph .= qq{$_ -> "g0" [arrowhead = dot, color = blue]\n};
380             }
381              
382             #warn "$_ => $desc\n";
383            
384             }
385 0         0 print $OUT <<"EOGRAPH";
386             digraph G {
387             #concentrate = true
388              
389             $graph
390             }
391             EOGRAPH
392 0         0 close $OUT;
393             }
394              
395             sub qtables {
396 0     0 0 0 my ($parser) = @_;
397              
398 0         0 my($tmp);
399              
400 0         0 my $warnings = $parser->Warnings();
401 0         0 my $conflicts = $parser->Conflicts();
402 0         0 my $rules = $parser->ShowRules();
403 0         0 my $states = $parser->ShowDfa();
404 0         0 my $summary = $parser->Summary();
405              
406 0         0 my $tables =<<"ENDOFLALAR"
407             Warnings:
408             ---------
409             $warnings
410             Conflicts:
411             ----------
412             $conflicts
413             Rules:
414             ------
415             $rules
416             States:
417             ------
418             $states
419             $states
420             Summary:
421             --------
422             $summary
423             ENDOFLALAR
424             }
425              
426             ######################################
427             # Method to get summary about parser #
428             ######################################
429             sub Summary {
430 0     0 0 0 my($self)=shift;
431 0         0 my($text) = '';
432              
433 0         0 $text=$self->SUPER::Summary();
434 0         0 $text.="Number of states : ".
435 0         0 scalar(@{$$self{STATES}})."\n";
436 0         0 $text;
437             }
438              
439             #######################################
440             # Method To Get Infos about conflicts #
441             #######################################
442             sub Conflicts {
443 0     0 0 0 my($self)=shift;
444 0         0 my($states)=$$self{STATES};
445 0         0 my($conflicts)=$$self{CONFLICTS};
446 0         0 my($text) = '';
447              
448 0         0 for my $stateno ( sort { $a <=> $b } keys(%{$$conflicts{SOLVED}})) {
  0         0  
  0         0  
449              
450 0         0 for (@{$$conflicts{SOLVED}{$stateno}}) {
  0         0  
451 0         0 my($ruleno,$token,$how)=@$_;
452              
453 0 0       0 $token eq chr(0)
454             and $token = '$end';
455              
456 0         0 $text.="Conflict in state $stateno between rule ".
457             "$ruleno and token $token resolved as $how.\n";
458             }
459             };
460              
461 0         0 for my $stateno ( sort { $a <=> $b } keys(%{$$conflicts{FORCED}{DETAIL}})) {
  0         0  
  0         0  
462 0         0 my($nbsr,$nbrr)=@{$$conflicts{FORCED}{DETAIL}{$stateno}{TOTAL}};
  0         0  
463              
464 0         0 $text.="State $stateno contains ";
465              
466 0 0       0 $nbsr
    0          
467             and $text.="$nbsr shift/reduce conflict".
468             ($nbsr > 1 ? "s" : "");
469              
470             $nbrr
471 0 0       0 and do {
472 0 0       0 $nbsr
473             and $text.=" and ";
474              
475 0 0       0 $text.="$nbrr reduce/reduce conflict".
476             ($nbrr > 1 ? "s" : "");
477             };
478 0         0 $text.="\n";
479             };
480              
481 0         0 $text;
482             }
483              
484             #################################
485             # Method to dump parsing tables #
486             #################################
487             sub DfaTable {
488 54     54 0 150 my($self)=shift;
489 54         159 my($states)=$$self{STATES};
490 54         114 my($stateno);
491             my($text);
492              
493 54         135 $text="[\n\t{";
494              
495 1289         1823 $text.=join("\n\t},\n\t{",
496             map {
497 54         177 my($state)=$_;
498 1289         1310 my($text);
499              
500 1289         2534 $text="#State ".$stateno++."\n\t\t";
501              
502 694         2795 ( not exists($$state{ACTIONS}{''})
503             or keys(%{$$state{ACTIONS}}) > 1)
504 1289 100 100     4139 and do {
505              
506 877         1092 $text.="ACTIONS => {\n\t\t\t";
507              
508 3101         18101 $text.=join(",\n\t\t\t",
509             map {
510 3383         6091 my($term,$action)=($_,$$state{ACTIONS}{$_});
511 3101         3034 my($text);
512              
513 3101 100       5928 if(substr($term,0,1) eq "'") {
514 2247         3431 $term=~s/([\@\$\"])/\\$1/g;
515 2247         8544 $term=~s/^'|'$/"/g;
516             }
517             else {
518 854 100       2094 $term= $term eq chr(0)
519             ? "''"
520             : "'$term'";
521             }
522              
523 3101 50       5337 if(defined($action)) {
524 3101         3392 $action=int($action);
525             }
526             else {
527 0         0 $action='undef';
528             }
529              
530 3101         8207 "$term => $action";
531            
532 877         1269 } grep { $_ } keys(%{$$state{ACTIONS}}));
  877         2415  
533              
534 877         1965 $text.="\n\t\t}";
535             };
536              
537             exists($$state{ACTIONS}{''})
538 1289 100       4157 and do {
539 694 100       758 keys(%{$$state{ACTIONS}}) > 1
  694         1913  
540             and $text.=",\n\t\t";
541              
542 694         1591 $text.="DEFAULT => $$state{ACTIONS}{''}";
543             };
544              
545             exists($$state{GOTOS})
546 1289 100       3643 and do {
547 481         599 $text.=",\n\t\tGOTOS => {\n\t\t\t";
548 909         1610 $text.=join(",\n\t\t\t",
549             map {
550 481         1301 my($nterm,$stateno)=($_,$$state{GOTOS}{$_});
551 909         960 my($text);
552              
553 909         3736 "'$nterm' => $stateno";
554            
555 481         940 } keys(%{$$state{GOTOS}}));
556 481         885 $text.="\n\t\t}";
557             };
558              
559 1289         3715 $text;
560              
561             }@$states);
562              
563 54         468 $text.="\n\t}\n]";
564              
565 54         496 $text;
566              
567             }
568              
569             sub _DynamicConflicts {
570 54     54   157 my $self = shift;
571 54         225 my $ch = $self->{GRAMMAR}{CONFLICTHANDLERS};
572              
573 54 50       306 return unless %$ch;
574              
575 0         0 my $co = $self->{CONFLICTS}{FORCED}{DETAIL};
576              
577 0         0 my %C; # keys:
578             # conflictive grammar productions.
579             # Values:
580             # tokens for which there is a conflict with this production
581 0         0 for my $state (keys %$co) {
582 0         0 my @conList = @{$co->{$state}{LIST}};
  0         0  
583              
584 0         0 for my $c (@conList) {
585 0         0 my ($token, $production) = @$c;
586              
587             # the action chosen is in: $self->{STATES}[$state]{ACTIONS}{$token}
588 0         0 push @{$C{($production)}{$state}}, $token;
  0         0  
589             }
590             }
591              
592 0         0 for my $c (keys %$ch) { # for each conflict handler
593 0         0 my $d = $ch->{$c}{production}; # hash ref of productions managed by this handler
594 0         0 for my $p (keys %$d) { # for each production
595             # # if $p reduce or shift?
596             # # find the conflictive states where $p appears
597             # # if $p is reduce and appears in state $s as -$p it is a state of conflict (the other is in the action table)
598              
599 0 0       0 if ($C{$p}) {
600 0         0 push @{$ch->{$c}{states}}, $C{$p}
  0         0  
601             }
602             else {
603             # check that it is a shift with this production.
604             }
605             }
606             }
607             }
608              
609             ####################################
610             # Method to build Dfa from Grammar #
611             ####################################
612             sub _Compile {
613 54     54   161 my($self)=shift;
614 54         107 my($grammar,$states);
615              
616 54         144 $grammar=$self->{GRAMMAR};
617              
618 54         303 $states = _LR0($grammar);
619              
620 54         331 $self->{CONFLICTS} = _LALR($grammar,$states);
621              
622 54         238 $self->{STATES}=$states;
623             }
624              
625             #########################
626             # LR0 States Generation #
627             #########################
628             #
629             ###########################
630             # General digraph routine #
631             ###########################
632             sub _Digraph {
633 162     162   397 my($rel,$F)=@_;
634 162         287 my(%N,@S);
635 162         313 my($infinity)=(~(1<<31));
636 162         239 my($Traverse);
637              
638             $Traverse = sub {
639 1747     1747   2941 my($x,$d)=@_;
640 1747         2242 my($y);
641              
642 1747         2281 push(@S,$x);
643 1747         2918 $N{$x}=$d;
644              
645             exists($$rel{$x})
646 1747 100       5382 and do {
647 1487         1655 for $y (keys(%{$$rel{$x}})) {
  1487         19886  
648 7959 100       20004 exists($N{$y})
649             or &$Traverse($y,$d+1);
650              
651 7959 100       16937 $N{$y} < $N{$x}
652             and $N{$x} = $N{$y};
653              
654 7959         19257 $$F{$x}|=$$F{$y};
655             }
656             };
657              
658             $N{$x} == $d
659 1747 100       5047 and do {
660 1538         2239 for(;;) {
661 1747         2302 $y=pop(@S);
662 1747         2428 $N{$y}=$infinity;
663 1747 100       5764 $y eq $x
664             and last;
665 209         350 $$F{$y}=$$F{$x};
666             }
667             };
668 162         1678 };
669              
670 162         989 for (keys(%$rel)) {
671 1487 100       4366 exists($N{$_})
672             or &$Traverse($_,1);
673             }
674             }
675             #######################
676             # Generate LR0 states #
677             #######################
678             # Formula used for closures:
679             #
680             # CLOSE(A) = DCLOSE(A) u U (CLOSE(B) | A close B)
681             #
682             # where:
683             #
684             # DCLOSE(A) = { [ A -> alpha ] in P }
685             #
686             # A close B iff [ A -> B gamma ] in P
687              
688             sub _SetClosures {
689 54     54   184 my($grammar)=@_;
690 54         126 my($rel,$closures);
691              
692 54         109 for my $symbol (keys(%{$$grammar{NTERM}})) {
  54         299  
693 277         393 $closures->{$symbol}=pack('b'.@{$$grammar{RULES}});
  277         1167  
694              
695 277         1075 for my $ruleno (@{$$grammar{NTERM}{$symbol}}) {
  277         770  
696 691         1356 my($rhs)=$$grammar{RULES}[$ruleno][1];
697              
698 691         2131 vec($closures->{$symbol},$ruleno,1)=1;
699              
700 691 100 100     5061 @$rhs > 0
701             and exists($$grammar{NTERM}{$$rhs[0]})
702             and ++$rel->{$symbol}{$$rhs[0]};
703             }
704             }
705 54         310 _Digraph($rel,$closures);
706              
707 54         176 $closures
708             }
709              
710             sub _Closures {
711 1289     1289   2879 my($grammar,$core,$closures)=@_;
712 1289         1448 my($ruleset)=pack('b'.@{$$grammar{RULES}});
  1289         4414  
713              
714 1289         2882 for (@$core) {
715 3265         18854 my($ruleno,$pos)=@$_;
716 3265         6609 my($rhs)=$$grammar{RULES}[$ruleno][1];
717              
718 3265 100 100     17500 $pos < @$rhs
719             and exists($closures->{$$rhs[$pos]})
720             and $ruleset|=$closures->{$$rhs[$pos]};
721             }
722 5201         9632 [ @$core, map { [ $_, 0 ] }
  34895         48073  
723 1289         4194 grep { vec($ruleset,$_,1) }
724 1289         3378 0..$#{$$grammar{RULES}} ];
725             }
726              
727             sub _Transitions {
728 1289     1289   1935 my($grammar,$cores,$closures,$states,$stateno)=@_;
729 1289         2317 my($core)=$$states[$stateno]{'CORE'};
730 1289         1522 my(%transitions);
731              
732 1289         1340 for (@{_Closures($grammar,$core,$closures)}) {
  1289         2827  
733 8466         13362 my($ruleno,$pos)=@$_;
734 8466         14782 my($rhs)=$$grammar{RULES}[$ruleno][1];
735              
736             $pos == @$rhs
737 8466 100       15991 and do {
738 717         1056 push(@{$$states[$stateno]{ACTIONS}{''}},$ruleno);
  717         4726  
739 717         2023 next;
740             };
741 7749         7633 push(@{$transitions{$$rhs[$pos]}},[ $ruleno, $pos+1 ]);
  7749         31452  
742             }
743              
744 1289         6787 for (keys(%transitions)) {
745 4777         7766 my($symbol,$core)=($_,$transitions{$_});
746 7749 50       22251 my($corekey)=join(',',map { join('.',@$_) }
  5988         12557  
747 4777         9766 sort { $$a[0] <=> $$b[0]
748             or $$a[1] <=> $$b[1] }
749             @$core);
750 4777         12282 my($tostateno);
751              
752             exists($cores->{$corekey})
753 4777 100       11760 or do {
754 1235         3541 push(@$states,{ 'CORE' => $core });
755 1235         37140 $cores->{$corekey}=$#$states;
756             };
757              
758 4777         6897 $tostateno=$cores->{$corekey};
759 4777         6093 push(@{$$states[$tostateno]{FROM}},$stateno);
  4777         10811  
760              
761             exists($$grammar{TERM}{$_})
762 4777 100       12741 and do {
763 3868         11516 $$states[$stateno]{ACTIONS}{$_} = [ $tostateno ];
764 3868         12580 next;
765             };
766 909         4149 $$states[$stateno]{GOTOS}{$_} = $tostateno;
767             }
768             }
769              
770             sub _LR0 {
771 54     54   139 my($grammar)=@_;
772 54         171 my($states) = [];
773 54         146 my($stateno);
774             my($closures); #$closures={ nterm => ruleset,... }
775 54         137 my($cores)={}; # { "itemlist" => stateno, ... }
776             # where "itemlist" has the form:
777             # "ruleno.pos,ruleno.pos" ordered by ruleno,pos
778              
779 54         273 $closures = _SetClosures($grammar);
780 54         349 push(@$states,{ 'CORE' => [ [ 0, 0 ] ] });
781 54         441 for($stateno=0;$stateno<@$states;++$stateno) {
782 1289         2705 _Transitions($grammar,$cores,$closures,$states,$stateno);
783             }
784              
785 54         676 $states
786             }
787              
788             #########################################################
789             # Add Lookahead tokens where needed to make LALR states #
790             #########################################################
791             # Compute First sets for non-terminal using the following formula:
792             #
793             # FIRST(A) = { a in T u { epsilon } | A l a }
794             # u
795             # U { FIRST(B) | B in V and A l B }
796             #
797             # where:
798             #
799             # A l x iff [ A -> X1 X2 .. Xn x alpha ] in P and Xi =>* epsilon, 1 <= i <= n
800              
801             sub _SetFirst {
802 54     54   139 my($grammar,$termlst,$terminx)=@_;
803 54         168 my($rel,$first)=( {}, {} );
804              
805 54         127 for my $symbol (keys(%{$$grammar{NTERM}})) {
  54         265  
806 277         899 $first->{$symbol}=pack('b'.@$termlst);
807              
808 277         853 RULE:
809 277         363 for my $ruleno (@{$$grammar{NTERM}{$symbol}}) {
810 691         1282 my($rhs)=$$grammar{RULES}[$ruleno][1];
811              
812 691         1244 for (@$rhs) {
813             exists($terminx->{$_})
814 680 100       1706 and do {
815 269         761 vec($first->{$symbol},$terminx->{$_},1)=1;
816 269         793 next RULE;
817             };
818 411         2393 ++$rel->{$symbol}{$_};
819 411 100       1645 exists($$grammar{NULLABLE}{$_})
820             or next RULE;
821             }
822 28         199 vec($first->{$symbol},0,1)=1;
823             }
824             }
825 54         477 _Digraph($rel,$first);
826              
827 54         166 $first
828             }
829              
830             sub _Preds {
831 1073     1073   1548 my($states,$stateno,$len)=@_;
832 1073         1088 my($queue, $preds);
833              
834 1073 100       3088 $len
835             or return [ $stateno ];
836              
837 696         1797 $queue=[ [ $stateno, $len ] ];
838 696         1657 while(@$queue) {
839 4898         6732 my($pred) = shift(@$queue);
840 4898         6465 my($stateno, $len) = @$pred;
841              
842             $len == 1
843 4898 100       9172 and do {
844 4136         4918 push(@$preds,@{$states->[$stateno]{FROM}});
  4136         18265  
845 4136         16111 next;
846             };
847              
848 4202         10336 push(@$queue, map { [ $_, $len - 1 ] }
  762         3006  
849 762         995 @{$states->[$stateno]{FROM}});
850             }
851              
852             # Pass @$preds through a hash to ensure unicity
853 696         765 [ keys( %{ +{ map { ($_,1) } @$preds } } ) ];
  696         1001  
  7052         22907  
854             }
855              
856             sub _FirstSfx {
857 825     825   1776 my($grammar,$firstset,$termlst,$terminx,$ruleno,$pos,$key)=@_;
858 825         1739 my($first)=pack('b'.@$termlst);
859 825         2722 my($rhs)=$$grammar{RULES}[$ruleno][1];
860              
861 825         2288 for (;$pos < @$rhs;++$pos) {
862             exists($terminx->{$$rhs[$pos]})
863 414 100       1397 and do {
864 344         895 vec($first,$terminx->{$$rhs[$pos]},1)=1;
865 344         1413 return($first);
866             };
867 70         136 $first|=$firstset->{$$rhs[$pos]};
868              
869 70 100       190 vec($first,0,1)
870             and vec($first,0,1)=0;
871              
872 70 100       409 exists($$grammar{NULLABLE}{$$rhs[$pos]})
873             or return($first);
874              
875             }
876 417         973 vec($first,0,1)=1;
877 417         1602 $first;
878             }
879              
880             # Compute Follow sets using following formula:
881             #
882             # FOLLOW(p,A) = READ(p,A)
883             # u
884             # U { FOLLOW(q,B) | (p,A) include (q,B)
885             #
886             # where:
887             #
888             # READ(p,A) = U { FIRST(beta) | [ A -> alpha A . beta ] in KERNEL(GOTO(p,A))
889             # } - { epsilon }
890             #
891             # (p,a) include (q,B) iff [ B -> alpha A . beta ] in KERNEL(GOTO(p,A),
892             # epsilon in FIRST(beta) and
893             # q in PRED(p,alpha)
894              
895             # >> x $firstset
896             # 0 HASH(0x1f7af60)
897             # '$start' => "\cG"
898             # 'a' => "\cB"
899             # 'b' => "\cH"
900             # 's' => "\cC"
901             # >> x $firstset->{'a'} # firstset es una string compactada de 0 y 1 que es trratada como un conjunto
902             # 0 "\cB"
903             # >> x unpack ("b*", $firstset->{'a'})
904             # 0 01000000
905             # >> x unpack ("b*", $firstset->{'b'})
906             # 0 00010000
907             # >> x unpack ("b*", $firstset->{'s'})
908             # 0 11000000
909              
910             sub _ComputeFollows {
911 54     54   178 my($grammar,$states,$termlst)=@_;
912 54         109 my($firstset,$terminx);
913 54         437 my($inconsistent, $rel, $follows, $sfx)= ( {}, {}, {}, {} );
914              
915 54         211 %$terminx= map { ($termlst->[$_],$_) } 0..$#$termlst;
  624         1791  
916              
917 54         695 $firstset=_SetFirst($grammar,$termlst,$terminx);
918              
919 54         258 for my $stateno (0..$#$states) {
920 1289         2197 my($state)=$$states[$stateno];
921              
922             exists($$state{ACTIONS}{''})
923             and ( @{$$state{ACTIONS}{''}} > 1
924             or keys(%{$$state{ACTIONS}}) > 1 )
925 1289 100 100     3961 and do {
      66        
926 393         958 ++$inconsistent->{$stateno};
927              
928 393         461 for my $ruleno (@{$$state{ACTIONS}{''}}) {
  393         929  
929 400         494 my($lhs,$rhs)=@{$$grammar{RULES}[$ruleno]}[0,1];
  400         2494  
930              
931 400         496 for my $predno (@{_Preds($states,$stateno,scalar(@$rhs))}) {
  400         907  
932 3693         13317 ++$rel->{"$stateno.$ruleno"}{"$predno.$lhs"};
933             }
934             }
935             };
936              
937 1289 100       3665 exists($$state{GOTOS})
938             or next;
939              
940 481         636 for my $symbol (keys(%{$$state{GOTOS}})) {
  481         1644  
941 909         1864 my($tostate)=$$states[$$state{GOTOS}{$symbol}];
942 909         1637 my($goto)="$stateno.$symbol";
943              
944 909         2869 $follows->{$goto}=pack('b'.@$termlst);
945              
946 909         1052 for my $item (@{$$tostate{'CORE'}}) {
  909         1901  
947 3497         5938 my($ruleno,$pos)=@$item;
948 3497         5559 my($key)="$ruleno.$pos";
949              
950 3497 100       9615 exists($sfx->{$key})
951             or $sfx->{$key} = _FirstSfx($grammar,$firstset,
952             $termlst,$terminx,
953             $ruleno,$pos,$key);
954              
955 3497         6357 $follows->{$goto}|=$sfx->{$key};
956              
957             vec($follows->{$goto},0,1)
958 3497 100       11742 and do {
959 673         1531 my($lhs)=$$grammar{RULES}[$ruleno][0];
960              
961 673         1590 vec($follows->{$goto},0,1)=0;
962              
963 673         1024 for my $predno (@{_Preds($states,$stateno,$pos-1)}) {
  673         1752  
964 3736         11513 ++$rel->{$goto}{"$predno.$lhs"};
965             }
966             };
967             }
968             }
969             }
970 54         250 _Digraph($rel,$follows);
971              
972 54         707 ($follows,$inconsistent)
973             }
974              
975             sub _ComputeLA {
976 54     54   130 my($grammar,$states)=@_;
977 54         371 my($termlst)= [ '',keys(%{$$grammar{TERM}}) ];
  54         460  
978              
979 54         339 my($follows,$inconsistent) = _ComputeFollows($grammar,$states,$termlst);
980              
981 54         293 for my $stateno ( keys(%$inconsistent ) ) {
982 393         866 my($state)=$$states[$stateno];
983 393         607 my($conflict);
984              
985             #NB the sort is VERY important for conflicts resolution order
986 393         478 for my $ruleno (sort { $a <=> $b }
  7         19  
  393         1238  
987             @{$$state{ACTIONS}{''}}) {
988 400         1146 for my $term ( map { $termlst->[$_] } grep {
  2715         3988  
  7450         11404  
989             vec($follows->{"$stateno.$ruleno"},$_,1) }
990             0..$#$termlst) {
991 2715 100       14615 exists($$state{ACTIONS}{$term})
992             and ++$conflict;
993 2715         3261 push(@{$$state{ACTIONS}{$term}},-$ruleno);
  2715         8938  
994             }
995             }
996 393         1078 delete($$state{ACTIONS}{''});
997 393 100       1239 $conflict
998             or delete($inconsistent->{$stateno});
999             }
1000              
1001             $inconsistent
1002 54         312 }
1003              
1004             #############################
1005             # Solve remaining conflicts #
1006             #############################
1007              
1008             sub _SolveConflicts {
1009 54     54   3948 my($grammar,$states,$inconsistent)=@_;
1010 54         111 my(%rulesprec,$RulePrec);
1011 54         452 my($conflicts)={ SOLVED => {},
1012             FORCED => { TOTAL => [ 0, 0 ],
1013             DETAIL => {}
1014             }
1015             };
1016              
1017             $RulePrec = sub {
1018 1377     1377   1547 my($ruleno)=@_;
1019 1377         1506 my($rhs,$rprec)=@{$$grammar{RULES}[$ruleno]}[1,2];
  1377         2854  
1020 1377         1719 my($lastterm);
1021              
1022 1377 100       2822 defined($rprec)
1023             and return($rprec);
1024              
1025 1244 100       3887 exists($rulesprec{$ruleno})
1026             and return($rulesprec{$ruleno});
1027              
1028 210         423 $lastterm=(grep { exists($$grammar{TERM}{$_}) } @$rhs)[-1];
  632         1806  
1029              
1030             defined($lastterm)
1031             and ref($$grammar{TERM}{$lastterm})
1032 210 100 66     1636 and do {
1033 209         627 $rulesprec{$ruleno}=$$grammar{TERM}{$lastterm}[1];
1034 209         549 return($rulesprec{$ruleno});
1035             };
1036              
1037 1         6 undef;
1038 54         513 };
1039              
1040 54         238 for my $stateno (keys(%$inconsistent)) {
1041 263         583 my($state)=$$states[$stateno];
1042 263         442 my($actions)=$$state{ACTIONS};
1043 263         306 my($nbsr,$nbrr);
1044              
1045 263         881 for my $term ( keys(%$actions) ) {
1046 2252         12549 my($act)=$$actions{$term};
1047              
1048 2252 100       4823 @$act > 1
1049             or next;
1050              
1051             $$act[0] > 0
1052             and ref($$grammar{TERM}{$term})
1053 1393 100 66     8110 and do {
1054 1377         1548 my($assoc,$tprec)=@{$$grammar{TERM}{$term}};
  1377         3144  
1055 1377         1662 my($k,$error);
1056              
1057 1377         3331 for ($k=1;$k<@$act;++$k) {
1058 1377         2434 my($ruleno)=-$$act[$k];
1059 1377         2356 my($rprec)=&$RulePrec($ruleno);
1060              
1061 1377 100       9172 defined($rprec)
1062             or next;
1063              
1064             ( $tprec > $rprec
1065             or ( $tprec == $rprec and $assoc eq 'RIGHT'))
1066 1376 100 100     5649 and do {
      66        
1067 602         667 push(@{$$conflicts{SOLVED}{$stateno}},
  602         2932  
1068             [ $ruleno, $term, 'shift' ]);
1069 602         1024 splice(@$act,$k--,1);
1070 602         1947 next;
1071             };
1072             ( $tprec < $rprec
1073             or $assoc eq 'LEFT')
1074 774 50 66     2443 and do {
1075 774         1224 push(@{$$conflicts{SOLVED}{$stateno}},
  774         14946  
1076             [ $ruleno, $term, 'reduce' ]);
1077             $$act[0] > 0
1078 774 50       1960 and do {
1079 774         1072 splice(@$act,0,1);
1080 774         1602 --$k;
1081             };
1082 774         2666 next;
1083             };
1084 0         0 push(@{$$conflicts{SOLVED}{$stateno}},
  0         0  
1085             [ $ruleno, $term, 'error' ]);
1086 0         0 splice(@$act,$k--,1);
1087             $$act[0] > 0
1088 0 0       0 and do {
1089 0         0 splice(@$act,0,1);
1090 0         0 ++$error;
1091 0         0 --$k;
1092             };
1093             }
1094 1377 50       3221 $error
1095             and unshift(@$act,undef);
1096             };
1097              
1098             @$act > 1
1099 1393 100       3587 and do {
1100 17         33 $nbrr += @$act - 2;
1101 17 50       42 ($$act[0] > 0 ? $nbsr : $nbrr) += 1;
1102 17         89 push(@{$$conflicts{FORCED}{DETAIL}{$stateno}{LIST}},
  17         74  
1103 17         20 map { [ $term, $_ ] } splice(@$act,1));
1104             };
1105             }
1106              
1107             $nbsr
1108 263 100       827 and do {
1109 17         34 $$conflicts{FORCED}{TOTAL}[0]+=$nbsr;
1110 17         49 $$conflicts{FORCED}{DETAIL}{$stateno}{TOTAL}[0]+=$nbsr;
1111             };
1112              
1113             $nbrr
1114 263 50       907 and do {
1115 0         0 $$conflicts{FORCED}{TOTAL}[1]+=$nbrr;
1116 0         0 $$conflicts{FORCED}{DETAIL}{$stateno}{TOTAL}[1]+=$nbrr;
1117             };
1118              
1119             }
1120              
1121             $conflicts
1122 54         706 }
1123              
1124             ###############################
1125             # Make default reduce actions #
1126             ###############################
1127             sub _SetDefaults {
1128 54     54   156 my($states)=@_;
1129              
1130 54         182 for my $state (@$states) {
1131 1289         3062 my($actions)=$$state{ACTIONS};
1132              
1133             # %reduces: - rule number => array of tokens to reduce
1134             # $nodefault is true if no default can be derived
1135 1289         1386 my(%reduces,$default,$nodefault);
1136              
1137             #If action with ''=> no default
1138             exists($$actions{''})
1139 1289 100       3212 and do {
1140 317         688 $$actions{''}[0] = -$$actions{''}[0];
1141 317         453 ++$nodefault;
1142             };
1143              
1144             #shift error token => no default
1145 1289 100 66     3370 exists($$actions{error})
1146             and $$actions{error}[0] > 0
1147             and ++$nodefault;
1148              
1149 1289         3736 for my $term (keys(%$actions)) {
1150              
1151 5507         11041 $$actions{$term}=$$actions{$term}[0];
1152              
1153 5507 100 66     34313 (not defined($$actions{$term}) or $$actions{$term} > 0 or $nodefault)
      100        
1154             and next;
1155              
1156 2096         2460 push(@{$reduces{$$actions{$term}}},$term);
  2096         6924  
1157             }
1158              
1159 1289 100       5357 keys(%reduces) > 0 or next;
1160              
1161             # Find the production rule with the largest reduce set, i.e.
1162             # the largest number of tokens
1163              
1164             # OLD CODE:
1165             # $default=(
1166             # # take the largest ...
1167             # map { $$_[0] }
1168             # # sort them by cardinal (in reverse)
1169             # sort { $$b[1] <=> $$a[1] or $$b[0] <=> $$a[0] }
1170             # # list of [ - rule number, number of tokens for that rule ]
1171             # map { [ $_, scalar(@{$reduces{$_}}) ] }
1172             # keys(%reduces) # list of - rule numbers
1173             # )[0];
1174              
1175 377         483 my $max = 0;
1176 377         929 for (keys(%reduces)) {
1177 384         435 my $t = @{$reduces{$_}};
  384         692  
1178 384 100       10135 ($max, $default) = ($t, $_) if $t > $max;
1179             }
1180              
1181 377         543 delete(@$actions{ @{$reduces{$default}} });
  377         1207  
1182 377         1586 $$state{ACTIONS}{''}=$default;
1183             }
1184             }
1185              
1186             sub _dereference {
1187 0     0   0 my($states)=@_;
1188              
1189 0         0 for my $state (@$states) {
1190 0         0 my($actions)=$$state{ACTIONS};
1191              
1192             exists($$actions{''})
1193 0 0       0 and do {
1194 0         0 $$actions{''}[0] = -$$actions{''}[0];
1195             };
1196              
1197 0         0 for my $term (keys(%$actions)) {
1198 0         0 $$actions{$term}=$$actions{$term}[0];
1199             }
1200              
1201             }
1202             }
1203              
1204             sub _LALR {
1205 54     54   137 my($grammar,$states) = @_;
1206 54         117 my($conflicts,$inconsistent);
1207              
1208 54         276 $inconsistent = _ComputeLA($grammar,$states);
1209              
1210 54         320 $conflicts = _SolveConflicts($grammar,$states,$inconsistent);
1211              
1212 54 50       347 if ($grammar->{NOCOMPACT}) {
1213 0         0 _dereference($states);
1214             }
1215             else {
1216 54         281 _SetDefaults($states);
1217             }
1218              
1219 54         472 $conflicts
1220             }
1221              
1222              
1223             1;
1224