File Coverage

YappParse.yp
Criterion Covered Total %
statement 190 230 82.6
branch 67 102 65.6
condition 3 3 100.0
subroutine 28 38 73.6
pod 0 2 0.0
total 288 375 76.8


line stmt bran cond sub pod time code
1             %{
2             # Copyright © 1998, 1999, 2000, 2001, Francois Desarmenien.
3             # Copyright © 2017 William N. Braswell, Jr.
4             # All Rights Reserved.
5             # (see COPYRIGHT in Parse::Yapp.pm pod section for use and distribution rights)
6             #
7             # Parse/Yapp/Parser.yp: Parse::Yapp::Parser.pm source file
8             #
9             # Use: yapp -m 'Parse::Yapp::Parse' -o Parse/Yapp/Parse.pm YappParse.yp
10             #
11             # to generate the Parser module.
12             #
13             %}
14              
15             %{
16             require 5.004;
17              
18 3     3   22 use Carp;
  3         6  
  3         11361  
19              
20             my($input,$lexlevel,@lineno,$nberr,$prec,$labelno);
21             my($syms,$head,$tail,$token,$term,$nterm,$rules,$precterm,$start,$nullable);
22             my($expect);
23              
24             %}
25              
26             %%
27 10     10 0 17  
28 10 50       23 # Main rule
29             yapp: head body tail ;
30              
31             #Common rules:
32              
33             symbol: LITERAL {
34             exists($$syms{$_[1][0]})
35 523 100   523   1224 or do {
36 66         159 $$syms{$_[1][0]} = $_[1][1];
37 66         117 $$term{$_[1][0]} = undef;
38             };
39 523         949 $_[1]
40             }
41             | ident #default action
42             ;
43              
44             ident: IDENT {
45             exists($$syms{$_[1][0]})
46 1738 100   1738   4270 or do {
47 290         772 $$syms{$_[1][0]} = $_[1][1];
48 290         490 $$term{$_[1][0]} = undef;
49             };
50 1738         2875 $_[1]
51             }
52             ;
53              
54              
55             # Head section:
56             head: headsec '%%'
57             ;
58              
59             headsec: #empty #default action
60             | decls #default action
61             ;
62              
63             decls: decls decl #default action
64             | decl #default action
65             ;
66              
67             decl: '\n' #default action
68             | TOKEN typedecl symlist '\n'
69             {
70 27     27   34 for (@{$_[3]}) {
  27         53  
71 63         90 my($symbol,$lineno)=@$_;
72              
73             exists($$token{$symbol})
74 63 50       119 and do {
75 0         0 _SyntaxError(0,
76             "Token $symbol redefined: ".
77             "Previously defined line $$syms{$symbol}",
78             $lineno);
79 0         0 next;
80             };
81 63         91 $$token{$symbol}=$lineno;
82 63         101 $$term{$symbol} = [ ];
83             }
84             undef
85 27         48 }
86             | ASSOC typedecl symlist '\n'
87             {
88 37     37   51 for (@{$_[3]}) {
  37         131  
89 74         115 my($symbol,$lineno)=@$_;
90              
91             defined($$term{$symbol}[0])
92 74 50       167 and do {
93 0         0 _SyntaxError(1,
94             "Precedence for symbol $symbol redefined: ".
95             "Previously defined line $$syms{$symbol}",
96             $lineno);
97 0         0 next;
98             };
99 74         120 $$token{$symbol}=$lineno;
100 74         168 $$term{$symbol} = [ $_[1][0], $prec ];
101             }
102 37         54 ++$prec;
103             undef
104 37         65 }
105 1     1   2 | START ident '\n' { $start=$_[2][0]; undef }
  1         2  
106 10     10   23 | HEADCODE '\n' { push(@$head,$_[1]); undef }
  10         21  
107 0     0   0 | UNION CODE '\n' { undef } #ignore
108             | TYPE typedecl identlist '\n'
109             {
110 0     0   0 for ( @{$_[3]} ) {
  0         0  
111 0         0 my($symbol,$lineno)=@$_;
112              
113             exists($$nterm{$symbol})
114 0 0       0 and do {
115 0         0 _SyntaxError(0,
116             "Non-terminal $symbol redefined: ".
117             "Previously defined line $$syms{$symbol}",
118             $lineno);
119 0         0 next;
120             };
121 0         0 delete($$term{$symbol}); #not a terminal
122 0         0 $$nterm{$symbol}=undef; #is a non-terminal
123             }
124             }
125 0     0   0 | EXPECT NUMBER '\n' { $expect=$_[2][0]; undef }
  0         0  
126 0     0   0 | error '\n' { $_[0]->YYErrok }
127             ;
128              
129             typedecl: #empty
130             | '<' IDENT '>'
131             ;
132              
133 73     73   125 symlist: symlist symbol { push(@{$_[1]},$_[2]); $_[1] }
  73         137  
  73         126  
134 64     64   136 | symbol { [ $_[1] ] }
135             ;
136              
137 0     0   0 identlist: identlist ident { push(@{$_[1]},$_[2]); $_[1] }
  0         0  
  0         0  
138 0     0   0 | ident { [ $_[1] ] }
139             ;
140              
141             # Rule section
142             body: rulesec '%%'
143             {
144 10 100   10   34 $start
145             or $start=$$rules[1][0];
146              
147 10 50       30 ref($$nterm{$start})
148             or _SyntaxError(2,"Start symbol $start not found ".
149             "in rules section",$_[2][1]);
150              
151 10         42 $$rules[0]=[ '$start', [ $start, chr(0) ], undef, undef ];
152             }
153 0     0   0 | '%%' { _SyntaxError(2,"No rules in input grammar",$_[1][1]); }
154             ;
155              
156             rulesec: rulesec rules #default action
157             | rules #default action
158             ;
159              
160 257     257   670 rules: IDENT ':' rhss ';' { _AddRules($_[1],$_[3]); undef }
  257         473  
161 0     0   0 | error ';' { $_[0]->YYErrok }
162             ;
163              
164 618     618   842 rhss: rhss '|' rule { push(@{$_[1]},$_[3]); $_[1] }
  618         1080  
  618         1030  
165 257     257   538 | rule { [ $_[1] ] }
166             ;
167              
168 117     117   140 rule: rhs prec epscode { push(@{$_[1]}, $_[2], $_[3]); $_[1] }
  117         262  
  117         219  
169             | rhs {
170 758     758   1042 my($code)=undef;
171              
172             defined($_[1])
173             and $_[1][-1][0] eq 'CODE'
174 758 100 100     3048 and $code = ${pop(@{$_[1]})}[1];
  32         40  
  32         72  
175              
176 758         981 push(@{$_[1]}, undef, $code);
  758         1539  
177              
178 758         1469 $_[1]
179             }
180             ;
181              
182             rhs: #empty #default action (will return undef)
183             | rhselts #default action
184             ;
185              
186 1207     1207   1542 rhselts: rhselts rhselt { push(@{$_[1]},$_[2]); $_[1] }
  1207         2276  
  1207         2055  
187 834     834   1683 | rhselt { [ $_[1] ] }
188             ;
189              
190 2006     2006   4647 rhselt: symbol { [ 'SYMB', $_[1] ] }
191 35     35   85 | code { [ 'CODE', $_[1] ] }
192             ;
193              
194             prec: PREC symbol
195             {
196             defined($$term{$_[2][0]})
197 117 50   117   295 or do {
198 0         0 _SyntaxError(1,"No precedence for symbol $_[2][0]",
199             $_[2][1]);
200 0         0 return undef;
201             };
202              
203 117         214 ++$$precterm{$_[2][0]};
204 117         233 $$term{$_[2][0]}[1];
205             }
206             ;
207              
208 114     114   203 epscode: { undef }
209 3     3   10 | code { $_[1] }
210             ;
211              
212 38     38   74 code: CODE { $_[1] }
213             ;
214              
215             # Tail section:
216              
217             tail: /*empty*/
218 10     10   32 | TAILCODE { $tail=$_[1] }
219 10         1321 ;
220              
221             %%
222 10         77 sub _Error {
223 0     0   0 my($value)=$_[0]->YYCurval;
224              
225 0 0       0 my($what)= $token ? "input: '$$value[0]'" : "end of input";
226              
227 0         0 _SyntaxError(1,"Unexpected $what",$$value[1]);
228             }
229              
230             sub _Lexer {
231            
232             #At EOF
233 4048 100   4048   8131 pos($$input) >= length($$input)
234             and return('',[ undef, -1 ]);
235              
236             #In TAIL section
237             $lexlevel > 1
238 4038 100       7064 and do {
239 10         18 my($pos)=pos($$input);
240              
241 10         15 $lineno[0]=$lineno[1];
242 10         13 $lineno[1]=-1;
243 10         29 pos($$input)=length($$input);
244 10         55 return('TAILCODE',[ substr($$input,$pos), $lineno[0] ]);
245             };
246              
247             #Skip blanks
248             $lexlevel == 0
249             ? $$input=~m{\G((?:
250             [\t\ ]+ # Any white space char but \n
251             | \#[^\n]* # Perl like comments
252             | /\*.*?\*/ # C like comments
253             )+)}xsgc
254             : $$input=~m{\G((?:
255             \s+ # any white space char
256             | \#[^\n]* # Perl like comments
257             | /\*.*?\*/ # C like comments
258             )+)}xsgc
259 4028 100       16429 and do {
    100          
260 3583         7034 my($blanks)=$1;
261              
262             #Maybe At EOF
263 3583 50       6644 pos($$input) >= length($$input)
264             and return('',[ undef, -1 ]);
265              
266 3583         5564 $lineno[1]+= $blanks=~tr/\n//;
267             };
268              
269 4028         4998 $lineno[0]=$lineno[1];
270              
271 4028 100       13455 $$input=~/\G([A-Za-z_][A-Za-z0-9_]*)/gc
272             and return('IDENT',[ $1, $lineno[0] ]);
273              
274             $$input=~/\G('(?:[^'\\]|\\\\|\\'|\\)+?')/gc
275 2033 100       5658 and do {
276             $1 eq "'error'"
277 523 50       1175 and do {
278 0         0 _SyntaxError(0,"Literal 'error' ".
279             "will be treated as error token",$lineno[0]);
280 0         0 return('IDENT',[ 'error', $lineno[0] ]);
281             };
282 523         1779 return('LITERAL',[ $1, $lineno[0] ]);
283             };
284              
285             $$input=~/\G(%%)/gc
286 1510 100       2908 and do {
287 20         26 ++$lexlevel;
288 20         89 return($1, [ $1, $lineno[0] ]);
289             };
290              
291             $$input=~/\G\{/gc
292 1490 100       3071 and do {
293 38         54 my($level,$from,$code);
294              
295 38         50 $from=pos($$input);
296              
297 38         50 $level=1;
298 38         114 while($$input=~/([{}])/gc) {
299 54 50       135 substr($$input,pos($$input)-1,1) eq '\\' #Quoted
300             and next;
301 54 100       173 $level += ($1 eq '{' ? 1 : -1)
    100          
302             or last;
303             }
304             $level
305 38 50       75 and _SyntaxError(2,"Unmatched { opened line $lineno[0]",-1);
306 38         84 $code = substr($$input,$from,pos($$input)-$from-1);
307 38         57 $lineno[1]+= $code=~tr/\n//;
308 38         135 return('CODE',[ $code, $lineno[0] ]);
309             };
310              
311 1452 100       2345 if($lexlevel == 0) {# In head section
312 203 100       584 $$input=~/\G%(left|right|nonassoc)/gc
313             and return('ASSOC',[ uc($1), $lineno[0] ]);
314 166 100       317 $$input=~/\G%(start)/gc
315             and return('START',[ undef, $lineno[0] ]);
316 165 50       286 $$input=~/\G%(expect)/gc
317             and return('EXPECT',[ undef, $lineno[0] ]);
318             $$input=~/\G%\{/gc
319 165 100       305 and do {
320 10         15 my($code);
321              
322 10 50       44 $$input=~/\G(.*?)%}/sgc
323             or _SyntaxError(2,"Unmatched %{ opened line $lineno[0]",-1);
324              
325 10         29 $code=$1;
326 10         21 $lineno[1]+= $code=~tr/\n//;
327 10         59 return('HEADCODE',[ $code, $lineno[0] ]);
328             };
329 155 100       349 $$input=~/\G%(token)/gc
330             and return('TOKEN',[ undef, $lineno[0] ]);
331 128 50       232 $$input=~/\G%(type)/gc
332             and return('TYPE',[ undef, $lineno[0] ]);
333 128 50       236 $$input=~/\G%(union)/gc
334             and return('UNION',[ undef, $lineno[0] ]);
335 128 50       306 $$input=~/\G([0-9]+)/gc
336             and return('NUMBER',[ $1, $lineno[0] ]);
337              
338             }
339             else {# In rule section
340 1249 100       2661 $$input=~/\G%(prec)/gc
341             and return('PREC',[ undef, $lineno[0] ]);
342             }
343              
344             #Always return something
345 1260 50       2736 $$input=~/\G(.)/sg
346             or die "Parse::Yapp::Grammar::Parse: Match (.) failed: report as a BUG";
347              
348 1260 100       2784 $1 eq "\n"
349             and ++$lineno[1];
350              
351 1260         4314 ( $1 ,[ $1, $lineno[0] ]);
352              
353             }
354              
355             sub _SyntaxError {
356 0     0   0 my($level,$message,$lineno)=@_;
357              
358 0 0       0 $message= "*".
359             [ 'Warning', 'Error', 'Fatal' ]->[$level].
360             "* $message, at ".
361             ($lineno < 0 ? "eof" : "line $lineno").
362             ".\n";
363              
364 0 0       0 $level > 1
365             and die $message;
366              
367 0         0 warn $message;
368              
369 0 0       0 $level > 0
370             and ++$nberr;
371              
372 0 0       0 $nberr == 20
373             and die "*Fatal* Too many errors detected.\n"
374             }
375              
376             sub _AddRules {
377 257     257   345 my($lhs,$lineno)=@{$_[0]};
  257         475  
378 257         363 my($rhss)=$_[1];
379              
380             ref($$nterm{$lhs})
381 257 50       698 and do {
382 0         0 _SyntaxError(1,"Non-terminal $lhs redefined: ".
383             "Previously declared line $$syms{$lhs}",$lineno);
384 0         0 return;
385             };
386              
387             ref($$term{$lhs})
388 257 50       572 and do {
389 0 0       0 my($where) = exists($$token{$lhs}) ? $$token{$lhs} : $$syms{$lhs};
390 0         0 _SyntaxError(1,"Non-terminal $lhs previously ".
391             "declared as token line $where",$lineno);
392 0         0 return;
393             };
394              
395             ref($$nterm{$lhs}) #declared through %type
396 257 50       488 or do {
397 257         457 $$syms{$lhs}=$lineno; #Say it's declared here
398 257         438 delete($$term{$lhs}); #No more a terminal
399             };
400 257         524 $$nterm{$lhs}=[]; #It's a non-terminal now
401              
402 257         369 my($epsrules)=0; #To issue a warning if more than one epsilon rule
403              
404 257         510 for my $rhs (@$rhss) {
405 875         2017 my($tmprule)=[ $lhs, [ ], splice(@$rhs,-2) ]; #Init rule
406              
407             @$rhs
408 875 100       1777 or do {
409 41         100 ++$$nullable{$lhs};
410 41         49 ++$epsrules;
411             };
412              
413 875         1523 for (0..$#$rhs) {
414 2009         2165 my($what,$value)=@{$$rhs[$_]};
  2009         2865  
415              
416             $what eq 'CODE'
417 2009 100       3410 and do {
418 3         20 my($name)='@'.++$labelno."-$_";
419 3         20 push(@$rules,[ $name, [], undef, $value ]);
420 3         8 push(@{$$tmprule[1]},$name);
  3         9  
421 3         9 next;
422             };
423 2006         2068 push(@{$$tmprule[1]},$$value[0]);
  2006         3794  
424             }
425 875         1362 push(@$rules,$tmprule);
426 875         941 push(@{$$nterm{$lhs}},$#$rules);
  875         1582  
427             }
428              
429 257 50       570 $epsrules > 1
430             and _SyntaxError(0,"More than one empty rule for symbol $lhs",$lineno);
431             }
432              
433             sub Parse {
434 10     10 0 19 my($self)=shift;
435              
436 10 50       26 @_ > 0
437             or croak("No input grammar\n");
438              
439 10         20 my($parsed)={};
440              
441 10         14 $input=\$_[0];
442              
443 10         16 $lexlevel=0;
444 10         23 @lineno=(1,1);
445 10         16 $nberr=0;
446 10         11 $prec=0;
447 10         14 $labelno=0;
448              
449 10         13 $head=();
450 10         14 $tail="";
451              
452 10         16 $syms={};
453 10         11 $token={};
454 10         14 $term={};
455 10         15 $nterm={};
456 10         18 $rules=[ undef ]; #reserve slot 0 for start rule
457 10         13 $precterm={};
458              
459 10         13 $start="";
460 10         15 $nullable={};
461 10         15 $expect=0;
462              
463 10         29 pos($$input)=0;
464              
465              
466 10         54 $self->YYParse(yylex => \&_Lexer, yyerror => \&_Error);
467              
468 10 50       27 $nberr
469             and _SyntaxError(2,"Errors detected: No output",-1);
470              
471 10         77 @$parsed{ 'HEAD', 'TAIL', 'RULES', 'NTERM', 'TERM',
472             'NULL', 'PREC', 'SYMS', 'START', 'EXPECT' }
473             = ( $head, $tail, $rules, $nterm, $term,
474             $nullable, $precterm, $syms, $start, $expect);
475              
476 10         24 undef($input);
477 10         14 undef($lexlevel);
478 10         20 undef(@lineno);
479 10         13 undef($nberr);
480 10         15 undef($prec);
481 10         14 undef($labelno);
482              
483 10         14 undef($head);
484 10         14 undef($tail);
485              
486 10         15 undef($syms);
487 10         45 undef($token);
488 10         16 undef($term);
489 10         14 undef($nterm);
490 10         16 undef($rules);
491 10         16 undef($precterm);
492              
493 10         15 undef($start);
494 10         12 undef($nullable);
495 10         13 undef($expect);
496              
497 10         33 $parsed
498             }
499