File Coverage

blib/lib/Parse/Eyapp/Grammar.pm
Criterion Covered Total %
statement 240 352 68.1
branch 72 132 54.5
condition 14 31 45.1
subroutine 26 34 76.4
pod 0 24 0.0
total 352 573 61.4


line stmt bran cond sub pod time code
1             #
2             # Module Parse::Eyapp::Grammar
3             #
4             # (c) Copyright 1998-2001 Francois Desarmenien, all rights reserved.
5             # (c) Copyright 2006-2008 Casiano Rodriguez-Leon, all rights reserved.
6             #
7             package Parse::Eyapp::Grammar;
8             @ISA=qw( Parse::Eyapp::Options );
9              
10             require 5.004;
11              
12 61     61   1325 use Carp;
  61         1095  
  61         6922  
13 61     61   1030 use strict;
  61         103  
  61         6351  
14 61     61   40147 use Parse::Eyapp::Options;
  61         275  
  61         1985  
15 61     61   74571 use Parse::Eyapp::Parse;
  61         238  
  61         3610  
16 61     61   506 use Scalar::Util qw{reftype};
  61         254  
  61         4956  
17 61     61   369 use Data::Dumper;
  61         124  
  61         397212  
18              
19             ###############
20             # Constructor #
21             ###############
22             sub new {
23 54     54 0 200 my($class)=shift;
24 54         159 my($values);
25              
26 54         754 my($self)=$class->SUPER::new(@_);
27              
28 54         602 my($parser)=new Parse::Eyapp::Parse;
29              
30 54 50       1407 defined($self->Option('input'))
31             or croak "No input grammar";
32              
33 54         277 $values = $parser->Parse($self->Option('input'), # 1 input
34             $self->Option('firstline'), # 2 Line where the grammar source starts
35             $self->Option('inputfile'), # 3 The file or program containing the grammar
36             $self->Option('tree'), # 4 %tree activated
37             $self->Option('nocompact'), # 5 %nocompact
38             $self->Option('lexerisdefined'), # 6 lexer is defined
39             $self->Option('prefix'), # 7 accept prefix
40             $self->Option('start'), # 8 specify start symbol
41             #$self->Option('prefixname'), # yyprefix
42             #$self->Option('buildingtree') # If building AST
43             );
44              
45 54         229 undef($parser);
46              
47 54         34457 $$self{GRAMMAR}=_ReduceGrammar($values);
48              
49 54 50       262 ref($class)
50             and $class=ref($class);
51              
52 54         271 bless($self, $class);
53              
54 54         842 my $ns = $self->{GRAMMAR}{NAMINGSCHEME} ;
55 54 50 33     390 if ($ns && reftype($ns) eq 'ARRAY') {
56 0         0 $ns = eval "sub { $ns->[0]; }; ";
57 0 0       0 warn "Error in \%namingscheme directive $@" if $@;
58 0         0 $ns = $ns->($self);
59             }
60 54   50     583 $ns ||= \&give_default_name;
61 54         136 $self->{GRAMMAR}{NAMINGSCHEME} = $ns; # added to allow programmable production naming schemes (%name)
62              
63 54         900 $self;
64             }
65              
66             ###########
67             # Methods #
68             ###########
69             ##########################
70             # Method To View Grammar #
71             ##########################
72             sub ShowRules {
73 0     0 0 0 my($self)=shift;
74 0         0 my($rules)=$$self{GRAMMAR}{RULES};
75 0         0 my($ruleno)=-1;
76 0         0 my($text);
77              
78 0         0 for (@$rules) {
79 0         0 my($lhs,$rhs)=@$_;
80              
81 0         0 $text.=++$ruleno.":\t".$lhs." -> ";
82 0 0       0 if(@$rhs) {
83 0 0       0 $text.=join(' ',map { $_ eq chr(0) ? '$end' : $_ } @$rhs);
  0         0  
84             }
85             else {
86 0         0 $text.="/* empty */";
87             }
88 0         0 $text.="\n";
89             }
90 0         0 $text;
91             }
92              
93             sub give_default_name {
94 376     376 0 622 my ($self, $index, $lhs) = @_;
95              
96 376         965 my $name = "$lhs"."_$index";
97 376         812 return $name;
98             }
99              
100             sub give_lhs_name {
101 0     0 0 0 my ($self, $index, $lhs, $rhs) = @_;
102              
103 0         0 my $name = $lhs;
104 0         0 return $name;
105             }
106              
107             sub give_token_name {
108 0     0 0 0 my ($self, $index, $lhs, $rhs) = @_;
109              
110 0         0 my @rhs = @$rhs;
111 0         0 $rhs = '';
112              
113 0 0       0 unless (@rhs) { # Empty RHS
114 0         0 return $lhs.'_is_empty';
115             }
116              
117 0   0     0 my $names = $self->{GRAMMAR}{TOKENNAMES} || {};
118 0         0 for (@rhs) {
119 0 0       0 if ($self->is_token($_)) {
120 0         0 s/^'(.*)'$/$1/;
121 0   0     0 my $name = $names->{$_} || '';
122 0 0       0 unless ($name) {
123 0 0       0 $name = $_ if /^\w+$/;
124             }
125 0 0       0 $rhs .= "_$name" if $name;
126             }
127             }
128              
129 0 0       0 unless ($rhs) { # no 'word' tokens in the RHS
130 0         0 for (@rhs) {
131 0 0       0 $rhs .= "_$_" if /^\w+$/;
132             }
133             }
134              
135             # check if another production with such name exists?
136 0         0 my $name = $lhs.'_is'.$rhs;
137 0         0 return $name;
138             }
139              
140             sub camelize
141             {
142 0     0 0 0 my $s = shift;
143              
144 0         0 my @a = split(/(?<=[A-Za-z])_(?=[A-Za-z])|\b/, $s);
145 0         0 my $a = shift @a;
146 0         0 @a = map { ucfirst $_ } @a;
  0         0  
147 0         0 join('', ($a, @a));
148             }
149              
150             sub give_rhs_name {
151 0     0 0 0 my ($self, $index, $lhs, $rhs) = @_;
152              
153 0         0 my @rhs = @$rhs;
154 0         0 $rhs = '';
155              
156 0 0       0 unless (@rhs) { # Empty RHS
157 0         0 return camelize($lhs).'_is_empty';
158             }
159              
160 0   0     0 my $names = $self->{GRAMMAR}{TOKENNAMES} || {};
161 0         0 for (@rhs) {
162 0 0       0 if ($self->is_token($_)) {
163             # remove apostrophes
164 0         0 s/^'(.*)'$/$1/;
165              
166             # explicit name given ?
167 0   0     0 my $name = $names->{$_} || '';
168              
169             # no name was given, use symbol if is an ID
170 0 0       0 unless ($name) {
171 0 0       0 $name = $_ if /^\w+$/;
172             }
173 0 0       0 $rhs .= "_$name" if $name;
174             }
175             else { # syntactic variable
176 0 0       0 next if exists $self->{GRAMMAR}{CONFLICTHANDLERS}{$_};
177 0 0       0 $rhs .= '_'.camelize($_) if /^\w*$/;
178             }
179             }
180              
181             # check if another production with such name exists?
182 0         0 my $name = camelize($lhs).'_is'.$rhs;
183 0         0 return $name;
184             }
185              
186             sub classname {
187 1382     1382 0 2979 my ($self, $name, $index, $lhs, $rhs) = @_;
188              
189 1382         2094 $name = $name->[0];
190              
191 1382 100       3281 unless (defined($name)) {
192 556 100       3466 if ($lhs =~ /\$start/) {
    100          
    100          
    100          
    100          
    50          
193 108         234 $name = "_SUPERSTART"
194             }
195             elsif ($lhs =~ /\@(\d+)-(\d+)/) {
196 16         29 $name = "_CODE"
197             }
198             elsif ($lhs =~ /PAREN-(\d+)/) {
199 16         31 $name = "_PAREN"
200             }
201             elsif ($lhs =~ /STAR-(\d+)/) {
202 16         25 $name = "_STAR_LIST"
203             }
204             elsif ($lhs =~ /PLUS-(\d+)/) {
205 24         47 $name = "_PLUS_LIST"
206             }
207             elsif ($lhs =~ /OPTIONAL-(\d+)/) {
208 0         0 $name = "_OPTIONAL"
209             }
210             }
211              
212 1382         2380 my $naming_scheme = $self->{GRAMMAR}{NAMINGSCHEME};
213 1382 100       3644 if (!$name) {
    50          
214 376         836 $name = $naming_scheme->($self, $index, $lhs, $rhs);
215             }
216             elsif ($name =~ /^:/) { # it is a label only
217 0         0 $name = $naming_scheme->($self, $index, $lhs, $rhs).$name;
218             }
219              
220 1382         2944 return $name;
221             }
222              
223             # Added by Casiano
224             #####################################
225             # Method To Return the Grammar Rules#
226             #####################################
227             sub Rules { # TODO: find proper names
228 54     54 0 153 my($self)=shift;
229 54         191 my($rules)=$$self{GRAMMAR}{RULES};
230 54         1344 my($text) = "[#[productionNameAndLabel => lhs, [ rhs], bypass]]\n";
231 54         151 my $packages = q{'TERMINAL', '_OPTIONAL', '_STAR_LIST', '_PLUS_LIST', };
232              
233 54         127 my $index = 0;
234 54         124 my $label = "{\n"; # To huild a reverse map label => production number
235 54         208 for (@$rules) {
236 691         1415 my($lhs,$rhs,$prec,$name)=@$_;
237              
238 691         1063 my $bypass = $name->[2];
239 691 100       2399 $bypass = $self->Bypass unless defined($bypass);
240              
241 691 50 66     3106 $label .= " '$1' => $index,\n" if defined($name->[0]) and $name->[0] =~ /(:.*)/;
242              
243             # find an acceptable perl identifier as name
244 691         1508 $name = $self->classname($name, $index, $lhs, $rhs);
245 691         1776 $label .= " '$name' => $index,\n";
246              
247 691         3017 $packages .= "\n".(" "x9)."'$name', ";
248              
249 691         1495 $text.= " [ '$name' => '$lhs', [ ";
250 691 100       1120 $text.=join(', ',map { $_ eq chr(0) ? "'\$end'" : $_ =~ m{^'} ? $_ : "'$_'" } @$rhs);
  1483 100       5931  
251 691         1392 $text.=" ], $bypass ],\n";
252 691         1412 $index++;
253             }
254 54         153 $text .= ']';
255 54         151 $label .= '}';
256 54         336 return ($text, $packages, $label);
257             }
258              
259             # Added by Casiano
260             #####################################
261             # Method To Return the Grammar Terms#
262             #####################################
263             sub Terms {
264 54     54 0 142 my($self)=shift;
265 54         168 my(@terms)= sort(keys(%{$$self{GRAMMAR}{TERM}}));
  54         721  
266 54         236 my %semantic = %{$self->{GRAMMAR}{SEMANTIC}};
  54         1013  
267              
268 54         230 my $text = "{ ";
269 570 100       2244 $text .= join(",\n\t",
270             # Warning! bug. Before: map { $_ eq chr(0) ? "'\$end' => 0" : "$_ => $semantic{$_}"} @terms);
271 54         167 map { $_ eq chr(0) ? "'' => { ISSEMANTIC => 0 }" : "$_ => { ISSEMANTIC => $semantic{$_} }"} @terms);
272 54         532 $text .= ",\n\terror => { ISSEMANTIC => 0 },\n}";
273             }
274              
275             sub conflictHandlers {
276 54     54 0 145 my $self = shift;
277              
278 54         451 my $t = Dumper $self->{GRAMMAR}{CONFLICTHANDLERS};
279 54         6786 $t =~ s/^\$VAR\d*\s*=\s*//;
280 54         1959 $t =~s/;$//;
281 54         159 $t =~s/\\'//g; # quotes inside quotes
282 54         252 $t;
283             }
284              
285              
286             # produces the text mapping states to conflicthandlers
287             sub stateConflict {
288 54     54 0 214 my $self = shift;
289              
290 54         170 my $c = $self->{GRAMMAR}{CONFLICTHANDLERS};
291 54         146 my %stateConflict;
292              
293 54         162 my %t = ();
294 54         317 for my $cn (keys %$c) {
295 0         0 my $ce = $c->{$cn};
296 0         0 my $codeh = $ce->{codeh};
297 0         0 $codeh = "sub { $codeh }";
298 0 0       0 my @s = defined($ce->{states}) ? @{$ce->{states}} : ();
  0         0  
299 0         0 for my $s (@s) {
300 0         0 my ($sn) = keys %$s;
301             #my ($tokens) = values %$s;
302             #$tokens = join ',', @$tokens;
303 0 0       0 $t{$sn} = '' unless defined($t{$sn});
304 0         0 $t{$sn} .= << "NEWSTATECONFLICTENTRY";
305             {
306             name => '$cn',
307             codeh => $codeh,
308             },
309             NEWSTATECONFLICTENTRY
310             } #for states
311             } #for conflict names
312            
313 54         224 my $t = '{ ';
314 54         207 for my $s (keys %t) {
315 0         0 $t .= "$s => [ $t{$s} ],";
316             }
317 54         310 $t .= ' }';
318             }
319              
320             #####################################
321             # Method To Return the Bypass Option#
322             #####################################
323             sub Bypass {
324 388     388 0 578 my($self)=shift;
325            
326 388         996 return $$self{GRAMMAR}{BYPASS}
327             }
328              
329             #####################################
330             # Method To Return the Prefix Option#
331             #####################################
332             sub Prefix {
333 54     54 0 185 my($self)=shift;
334            
335 54         264 return $$self{GRAMMAR}{PREFIX}
336             }
337              
338              
339             sub Buildingtree {
340 54     54 0 151 my($self)=shift;
341            
342 54         239 return $$self{GRAMMAR}{BUILDINGTREE}
343             }
344              
345             sub Prompt {
346 54     54 0 155 my $self = shift;
347              
348 54 50       335 return "our \$PROMPT = $$self{GRAMMAR}{INCREMENTAL};\n" if defined($$self{GRAMMAR}{INCREMENTAL});
349 54         215 return '';
350             }
351              
352             sub is_token {
353 0     0 0 0 my($self)=shift;
354              
355 0         0 exists($self->{GRAMMAR}{TERM}{$_[0]})
356             }
357              
358             #####################################
359             # Method To Return the ACCESSORS
360             #####################################
361             sub Accessors {
362 54     54 0 153 my($self)=shift;
363            
364 54         275 return $$self{GRAMMAR}{ACCESSORS}
365             }
366              
367             ###########################
368             # Method To View Warnings #
369             ###########################
370             sub Warnings {
371 4     4 0 13 my($self)=shift;
372              
373 4 50       28 return '' if $self->Option('start');
374              
375 4         14 my($text) = '';
376 4         17 my($grammar)=$$self{GRAMMAR};
377              
378             exists($$grammar{UUTERM})
379 4 50       21 and do {
380 0         0 $text="Unused terminals:\n\n";
381 0         0 for (@{$$grammar{UUTERM}}) {
  0         0  
382 0         0 $text.="\t$$_[0], declared line $$_[1]\n";
383             }
384 0         0 $text.="\n";
385             };
386             exists($$grammar{UUNTERM})
387 4 50       23 and do {
388 0         0 $text.="Useless non-terminals:\n\n";
389 0         0 for (@{$$grammar{UUNTERM}}) {
  0         0  
390 0         0 $text.="\t$$_[0], declared line $$_[1]\n";
391             }
392 0         0 $text.="\n";
393             };
394             exists($$grammar{UURULES})
395 4 50       22 and do {
396 0         0 $text.="Useless rules:\n\n";
397 0         0 for (@{$$grammar{UURULES}}) {
  0         0  
398 0         0 $text.="\t$$_[0] -> ".join(' ',@{$$_[1]})."\n";
  0         0  
399             }
400 0         0 $text.="\n";
401             };
402 4         46 $text;
403             }
404              
405             ######################################
406             # Method to get summary about parser #
407             ######################################
408             sub Summary {
409 0     0 0 0 my($self)=shift;
410 0         0 my($text);
411              
412 0         0 $text ="Number of rules : ".
413 0         0 scalar(@{$$self{GRAMMAR}{RULES}})."\n";
414 0         0 $text.="Number of terminals : ".
415 0         0 scalar(keys(%{$$self{GRAMMAR}{TERM}}))."\n";
416 0         0 $text.="Number of non-terminals : ".
417 0         0 scalar(keys(%{$$self{GRAMMAR}{NTERM}}))."\n";
418 0         0 $text;
419             }
420              
421             ###############################
422             # Method to Ouput rules table #
423             ###############################
424             sub RulesTable {
425 54     54 0 158 my($self)=shift;
426 54         291 my($inputfile)=$self->Option('inputfile');
427 54         373 my($linenums)=$self->Option('linenumbers');
428 54         209 my($rules)=$$self{GRAMMAR}{RULES};
429 54         135 my $ruleno = 0;
430 54         114 my($text);
431              
432 54 50       266 defined($inputfile)
433             or $inputfile = 'unknown';
434              
435 54         131 $text="[\n\t";
436              
437 691         1789 $text.=join(",\n\t",
438             map {
439 54         167 my($lhs,$rhs,$rname,$code)=@$_[0,1,3,4];
440 691         1013 my($len)=scalar(@$rhs);
441              
442 691         728 my($text);
443              
444 691         2030 $rname = $self->classname($rname, $ruleno, $lhs, $rhs);
445              
446 691         895 $ruleno++;
447 691         1816 $text.="[#Rule $rname\n\t\t '$lhs', $len,";
448 691 100       1341 if($code) {
449 635 100       3630 $text.= "\nsub {".
450             ( $linenums
451             ? qq(\n#line $$code[1] "$inputfile"\n)
452             : " ").
453             "$$code[0]}";
454             }
455             else {
456 56         145 $text.=' undef';
457             }
458 691         1393 $text.="\n$Parse::Eyapp::Output::pattern\n\t]";
459              
460 691         2329 $text;
461             } @$rules);
462              
463 54         238 $text.="\n]";
464              
465 54         287 $text;
466             }
467              
468             ################################
469             # Methods to get HEAD and TAIL #
470             ################################
471             sub Head {
472 54     54 0 399 my($self)=shift;
473 54         271 my($inputfile)=$self->Option('inputfile');
474 54         332 my($linenums)=$self->Option('linenumbers');
475 54         126 my($text);
476              
477 54 100       631 $$self{GRAMMAR}{HEAD}[0]
478             or return '';
479              
480 34 50       162 defined($inputfile)
481             or $inputfile = 'unkown';
482              
483 34         95 for (@{$$self{GRAMMAR}{HEAD}}) {
  34         208  
484 34 50       494 $linenums
485             and $text.=qq(#line $$_[1] "$inputfile"\n);
486 34         155 $text.=$$_[0];
487             }
488             $text
489 34         165 }
490              
491             sub Tail {
492 54     54 0 166 my($self)=shift;
493 54         346 my($inputfile)=$self->Option('inputfile');
494 54         284 my($linenums)=$self->Option('linenumbers');
495 54         146 my($text);
496              
497 54 50 33     1247 ((reftype $$self{GRAMMAR}{TAIL} eq 'ARRAY') and
498             $$self{GRAMMAR}{TAIL}[0])
499             or return '';
500              
501 54 50       244 defined($inputfile)
502             or $inputfile = 'unkown';
503              
504 54 100       430 $linenums
505             and $text=qq(#line $$self{GRAMMAR}{TAIL}[1] "$inputfile"\n);
506 54         324 $text.=$$self{GRAMMAR}{TAIL}[0];
507              
508 54         231 $text
509             }
510              
511              
512             #################
513             # Private Stuff #
514             #################
515              
516             sub _UsefulRules {
517 54     54   136 my($rules,$nterm) = @_;
518 54         114 my($ufrules,$ufnterm);
519 0         0 my($done);
520              
521 54         413 $ufrules=pack('b'.@$rules);
522 54         131 $ufnterm={};
523              
524 54         258 vec($ufrules,0,1)=1; #start rules IS always useful
525              
526             RULE:
527 54         272 for (1..$#$rules) { # Ignore start rule
528 637         841 for my $sym (@{$$rules[$_][1]}) {
  637         1311  
529 803 100       2463 exists($$nterm{$sym})
530             and next RULE;
531             }
532 155         440 vec($ufrules,$_,1)=1;
533 155         675 ++$$ufnterm{$$rules[$_][0]};
534             }
535              
536 54         147 do {
537 114         200 $done=1;
538              
539 1757         2919 RULE:
540 114         354 for (grep { vec($ufrules,$_,1) == 0 } 1..$#$rules) {
541 632         838 for my $sym (@{$$rules[$_][1]}) {
  632         1296  
542 1432 100 100     6577 exists($$nterm{$sym})
543             and not exists($$ufnterm{$sym})
544             and next RULE;
545             }
546 482         1080 vec($ufrules,$_,1)=1;
547             exists($$ufnterm{$$rules[$_][0]})
548 482 100       1997 or do {
549 109         179 $done=0;
550 109         356 ++$$ufnterm{$$rules[$_][0]};
551             };
552             }
553              
554             }until($done);
555              
556 54         250 ($ufrules,$ufnterm)
557              
558             }#_UsefulRules
559              
560             sub _Reachable {
561 54     54   161 my($rules,$nterm,$term,$ufrules,$ufnterm)=@_;
562 54         109 my($reachable);
563 54         164 my(@fifo)=( 0 );
564              
565 54         199 $reachable={ '$start' => 1 }; #$start is always reachable
566              
567 54         239 while(@fifo) {
568 683         957 my($ruleno)=shift(@fifo);
569              
570 683         785 for my $sym (@{$$rules[$ruleno][1]}) {
  683         1365  
571              
572             exists($$term{$sym})
573 1483 100       3825 and do {
574 604         1082 ++$$reachable{$sym};
575 604         1183 next;
576             };
577              
578 879 100 100     4812 ( not exists($$ufnterm{$sym})
579             or exists($$reachable{$sym}) )
580             and next;
581              
582 223         379 ++$$reachable{$sym};
583 223         300 push(@fifo, grep { vec($ufrules,$_,1) } @{$$nterm{$sym}});
  629         1308  
  223         477  
584             }
585             }
586              
587             $reachable
588              
589 54         193 }#_Reachable
590              
591             sub _SetNullable {
592 54     54   836 my($rules,$term,$nullable) = @_;
593 54         178 my(@nrules);
594             my($done);
595              
596             RULE:
597 54         178 for (@$rules) {
598 691         1119 my($lhs,$rhs)=@$_;
599              
600 691 100       1590 exists($$nullable{$lhs})
601             and next;
602              
603 659         1023 for (@$rhs) {
604 976 100       2719 exists($$term{$_})
605             and next RULE;
606             }
607 128         505 push(@nrules,[$lhs,$rhs]);
608             }
609              
610 54         192 do {
611 59         127 $done=1;
612              
613             RULE:
614 59         165 for (@nrules) {
615 184         377 my($lhs,$rhs)=@$_;
616              
617 184 100       527 exists($$nullable{$lhs})
618             and next;
619              
620 172         339 for (@$rhs) {
621 165 100       1663 exists($$nullable{$_})
622             or next RULE;
623             }
624 12         238 $done=0;
625 12         57 ++$$nullable{$lhs};
626             }
627              
628             }until($done);
629             }
630              
631             sub _ReduceGrammar {
632 54     54   177 my($values)=@_;
633 54         132 my($ufrules,$ufnterm,$reachable);
634              
635 54         1355 my($grammar)= bless {
636             HEAD => $values->{HEAD},
637             TAIL => $values->{TAIL},
638             EXPECT => $values->{EXPECT},
639             # Casiano modifications
640             SEMANTIC => $values->{SEMANTIC}, # added to simplify AST
641             BYPASS => $values->{BYPASS}, # added to simplify AST
642             BUILDINGTREE => $values->{BUILDINGTREE}, # influences the semantic of lists * + ?
643             ACCESSORS => $values->{ACCESSORS}, # getter-setter for %tree and %metatree
644             PREFIX => $values->{PREFIX}, # yyprefix
645             NAMINGSCHEME => $values->{NAMINGSCHEME}, # added to allow programmable production naming schemes (%name)
646             NOCOMPACT => $values->{NOCOMPACT}, # Do not compact action tables. No DEFAULT field for "STATES"
647             CONFLICTHANDLERS => $values->{CONFLICTHANDLERS}, # list of conflict handlers
648             TERMDEF => $values->{TERMDEF}, # token => associated regular expression (for lexical analyzer)
649             WHITES => $values->{WHITES}, # string with the code to skip whites (for lexical analyzer)
650             LEXERISDEFINED => $values->{LEXERISDEFINED}, # true if %lexer was used
651             INCREMENTAL => $values->{INCREMENTAL}, # true if '%incremental lexer' was used
652             MODULINO => $values->{MODULINO}, # hash perlpath => path, prompt => question
653             STRICT => $values->{STRICT}, # true if %stric
654             DUMMY => $values->{DUMMY}, # array ref
655             TOKENNAMES => {}, # for naming schemes
656             }, __PACKAGE__;
657              
658 54         257 my($rules,$nterm,$term) = @$values {'RULES', 'NTERM', 'TERM'};
659              
660 54         402 ($ufrules,$ufnterm) = _UsefulRules($rules,$nterm);
661              
662 54 50       374 exists($$ufnterm{$values->{START}})
663             or die "*Fatal* Start symbol $values->{START} derives nothing, at eof\n";
664              
665 54         414 $reachable = _Reachable($rules,$nterm,$term,$ufrules,$ufnterm);
666              
667 54         823 $$grammar{TERM}{chr(0)}=undef;
668 54         306 for my $sym (keys %$term) {
669             ( exists($$reachable{$sym})
670             or exists($values->{PREC}{$sym}) )
671 516 50 66     1581 and do {
672 516 100       1607 $$grammar{TERM}{$sym}
673             = defined($$term{$sym}[0]) ? $$term{$sym} : undef;
674 516         809 next;
675             };
676 0         0 push(@{$$grammar{UUTERM}},[ $sym, $values->{SYMS}{$sym} ]);
  0         0  
677             }
678              
679 54         278 $$grammar{NTERM}{'$start'}=[];
680 54         230 for my $sym (keys %$nterm) {
681             exists($$reachable{$sym})
682 223 50       652 and do {
683 223 100       584 exists($values->{NULL}{$sym})
684             and ++$$grammar{NULLABLE}{$sym};
685 223         613 $$grammar{NTERM}{$sym}=[];
686 223         509 next;
687             };
688 0         0 push(@{$$grammar{UUNTERM}},[ $sym, $values->{SYMS}{$sym} ]);
  0         0  
689             }
690              
691 54         224 for my $ruleno (0..$#$rules) {
692             vec($ufrules,$ruleno,1)
693             and exists($$grammar{NTERM}{$$rules[$ruleno][0]})
694 691 50 33     3662 and do {
695 691         1035 push(@{$$grammar{RULES}},$$rules[$ruleno]);
  691         1322  
696 691         816 push(@{$$grammar{NTERM}{$$rules[$ruleno][0]}},$#{$$grammar{RULES}});
  691         1365  
  691         1242  
697 691         1785 next;
698             };
699 0         0 push(@{$$grammar{UURULES}},[ @{$$rules[$ruleno]}[0,1] ]);
  0         0  
  0         0  
700             }
701              
702 54         425 _SetNullable(@$grammar{'RULES', 'TERM', 'NULLABLE'});
703              
704 54         376 $grammar;
705             }#_ReduceGrammar
706              
707             sub tokennames {
708 0     0 0   my $self = shift;
709              
710 0           my $grammar = $self->{GRAMMAR};
711 0 0         $grammar->{TOKENNAMES} = { (%{$grammar->{TOKENNAMES}}, @_) } if (@_);
  0            
712 0           $grammar->{TOKENNAMES}
713             }
714              
715             1;
716