File Coverage

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


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