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   18 use Carp;
  3         5  
  3         8512  
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 20  
28 10 50       24 # Main rule
29             yapp: head body tail ;
30              
31             #Common rules:
32              
33             symbol: LITERAL {
34             exists($$syms{$_[1][0]})
35 523 100   523   1199 or do {
36 66         142 $$syms{$_[1][0]} = $_[1][1];
37 66         100 $$term{$_[1][0]} = undef;
38             };
39 523         873 $_[1]
40             }
41             | ident #default action
42             ;
43              
44             ident: IDENT {
45             exists($$syms{$_[1][0]})
46 1738 100   1738   3739 or do {
47 290         679 $$syms{$_[1][0]} = $_[1][1];
48 290         465 $$term{$_[1][0]} = undef;
49             };
50 1738         2785 $_[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         51  
71 63         96 my($symbol,$lineno)=@$_;
72              
73             exists($$token{$symbol})
74 63 50       111 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         87 $$token{$symbol}=$lineno;
82 63         107 $$term{$symbol} = [ ];
83             }
84             undef
85 27         66 }
86             | ASSOC typedecl symlist '\n'
87             {
88 37     37   49 for (@{$_[3]}) {
  37         72  
89 74         123 my($symbol,$lineno)=@$_;
90              
91             defined($$term{$symbol}[0])
92 74 50       168 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         113 $$token{$symbol}=$lineno;
100 74         187 $$term{$symbol} = [ $_[1][0], $prec ];
101             }
102 37         53 ++$prec;
103             undef
104 37         66 }
105 1     1   2 | START ident '\n' { $start=$_[2][0]; undef }
  1         2  
106 10     10   20 | HEADCODE '\n' { push(@$head,$_[1]); undef }
  10         17  
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   101 symlist: symlist symbol { push(@{$_[1]},$_[2]); $_[1] }
  73         143  
  73         128  
134 64     64   135 | 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   35 $start
145             or $start=$$rules[1][0];
146              
147 10 50       32 ref($$nterm{$start})
148             or _SyntaxError(2,"Start symbol $start not found ".
149             "in rules section",$_[2][1]);
150              
151 10         36 $$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   552 rules: IDENT ':' rhss ';' { _AddRules($_[1],$_[3]); undef }
  257         436  
161 0     0   0 | error ';' { $_[0]->YYErrok }
162             ;
163              
164 618     618   722 rhss: rhss '|' rule { push(@{$_[1]},$_[3]); $_[1] }
  618         1015  
  618         977  
165 257     257   472 | rule { [ $_[1] ] }
166             ;
167              
168 117     117   137 rule: rhs prec epscode { push(@{$_[1]}, $_[2], $_[3]); $_[1] }
  117         241  
  117         188  
169             | rhs {
170 758     758   943 my($code)=undef;
171              
172             defined($_[1])
173             and $_[1][-1][0] eq 'CODE'
174 758 100 100     3044 and $code = ${pop(@{$_[1]})}[1];
  32         54  
  32         69  
175              
176 758         945 push(@{$_[1]}, undef, $code);
  758         1481  
177              
178 758         1350 $_[1]
179             }
180             ;
181              
182             rhs: #empty #default action (will return undef)
183             | rhselts #default action
184             ;
185              
186 1207     1207   1415 rhselts: rhselts rhselt { push(@{$_[1]},$_[2]); $_[1] }
  1207         2055  
  1207         1942  
187 834     834   1490 | rhselt { [ $_[1] ] }
188             ;
189              
190 2006     2006   4006 rhselt: symbol { [ 'SYMB', $_[1] ] }
191 35     35   87 | code { [ 'CODE', $_[1] ] }
192             ;
193              
194             prec: PREC symbol
195             {
196             defined($$term{$_[2][0]})
197 117 50   117   271 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         169 ++$$precterm{$_[2][0]};
204 117         224 $$term{$_[2][0]}[1];
205             }
206             ;
207              
208 114     114   184 epscode: { undef }
209 3     3   9 | code { $_[1] }
210             ;
211              
212 38     38   70 code: CODE { $_[1] }
213             ;
214              
215             # Tail section:
216              
217             tail: /*empty*/
218 10     10   27 | TAILCODE { $tail=$_[1] }
219 10         1263 ;
220              
221             %%
222 10         83 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   7392 pos($$input) >= length($$input)
234             and return('',[ undef, -1 ]);
235              
236             #In TAIL section
237             $lexlevel > 1
238 4038 100       6655 and do {
239 10         20 my($pos)=pos($$input);
240              
241 10         23 $lineno[0]=$lineno[1];
242 10         17 $lineno[1]=-1;
243 10         28 pos($$input)=length($$input);
244 10         53 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       16162 and do {
    100          
260 3583         6733 my($blanks)=$1;
261              
262             #Maybe At EOF
263 3583 50       6314 pos($$input) >= length($$input)
264             and return('',[ undef, -1 ]);
265              
266 3583         5355 $lineno[1]+= $blanks=~tr/\n//;
267             };
268              
269 4028         5186 $lineno[0]=$lineno[1];
270              
271 4028 100       13439 $$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       5971 and do {
276             $1 eq "'error'"
277 523 50       1204 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         1654 return('LITERAL',[ $1, $lineno[0] ]);
283             };
284              
285             $$input=~/\G(%%)/gc
286 1510 100       3054 and do {
287 20         27 ++$lexlevel;
288 20         75 return($1, [ $1, $lineno[0] ]);
289             };
290              
291             $$input=~/\G\{/gc
292 1490 100       2876 and do {
293 38         62 my($level,$from,$code);
294              
295 38         56 $from=pos($$input);
296              
297 38         46 $level=1;
298 38         115 while($$input=~/([{}])/gc) {
299 54 50       161 substr($$input,pos($$input)-1,1) eq '\\' #Quoted
300             and next;
301 54 100       203 $level += ($1 eq '{' ? 1 : -1)
    100          
302             or last;
303             }
304             $level
305 38 50       73 and _SyntaxError(2,"Unmatched { opened line $lineno[0]",-1);
306 38         83 $code = substr($$input,$from,pos($$input)-$from-1);
307 38         64 $lineno[1]+= $code=~tr/\n//;
308 38         134 return('CODE',[ $code, $lineno[0] ]);
309             };
310              
311 1452 100       2257 if($lexlevel == 0) {# In head section
312 203 100       560 $$input=~/\G%(left|right|nonassoc)/gc
313             and return('ASSOC',[ uc($1), $lineno[0] ]);
314 166 100       326 $$input=~/\G%(start)/gc
315             and return('START',[ undef, $lineno[0] ]);
316 165 50       318 $$input=~/\G%(expect)/gc
317             and return('EXPECT',[ undef, $lineno[0] ]);
318             $$input=~/\G%\{/gc
319 165 100       350 and do {
320 10         15 my($code);
321              
322 10 50       45 $$input=~/\G(.*?)%}/sgc
323             or _SyntaxError(2,"Unmatched %{ opened line $lineno[0]",-1);
324              
325 10         27 $code=$1;
326 10         20 $lineno[1]+= $code=~tr/\n//;
327 10         40 return('HEADCODE',[ $code, $lineno[0] ]);
328             };
329 155 100       361 $$input=~/\G%(token)/gc
330             and return('TOKEN',[ undef, $lineno[0] ]);
331 128 50       245 $$input=~/\G%(type)/gc
332             and return('TYPE',[ undef, $lineno[0] ]);
333 128 50       247 $$input=~/\G%(union)/gc
334             and return('UNION',[ undef, $lineno[0] ]);
335 128 50       301 $$input=~/\G([0-9]+)/gc
336             and return('NUMBER',[ $1, $lineno[0] ]);
337              
338             }
339             else {# In rule section
340 1249 100       2718 $$input=~/\G%(prec)/gc
341             and return('PREC',[ undef, $lineno[0] ]);
342             }
343              
344             #Always return something
345 1260 50       2957 $$input=~/\G(.)/sg
346             or die "Parse::Yapp::Grammar::Parse: Match (.) failed: report as a BUG";
347              
348 1260 100       2648 $1 eq "\n"
349             and ++$lineno[1];
350              
351 1260         3981 ( $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   286 my($lhs,$lineno)=@{$_[0]};
  257         426  
378 257         365 my($rhss)=$_[1];
379              
380             ref($$nterm{$lhs})
381 257 50       577 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       536 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       477 or do {
397 257         384 $$syms{$lhs}=$lineno; #Say it's declared here
398 257         397 delete($$term{$lhs}); #No more a terminal
399             };
400 257         438 $$nterm{$lhs}=[]; #It's a non-terminal now
401              
402 257         327 my($epsrules)=0; #To issue a warning if more than one epsilon rule
403              
404 257         404 for my $rhs (@$rhss) {
405 875         1739 my($tmprule)=[ $lhs, [ ], splice(@$rhs,-2) ]; #Init rule
406              
407             @$rhs
408 875 100       1587 or do {
409 41         81 ++$$nullable{$lhs};
410 41         47 ++$epsrules;
411             };
412              
413 875         1415 for (0..$#$rhs) {
414 2009         2056 my($what,$value)=@{$$rhs[$_]};
  2009         2718  
415              
416             $what eq 'CODE'
417 2009 100       3389 and do {
418 3         8 my($name)='@'.++$labelno."-$_";
419 3         12 push(@$rules,[ $name, [], undef, $value ]);
420 3         4 push(@{$$tmprule[1]},$name);
  3         5  
421 3         4 next;
422             };
423 2006         2001 push(@{$$tmprule[1]},$$value[0]);
  2006         3523  
424             }
425 875         1155 push(@$rules,$tmprule);
426 875         873 push(@{$$nterm{$lhs}},$#$rules);
  875         1471  
427             }
428              
429 257 50       546 $epsrules > 1
430             and _SyntaxError(0,"More than one empty rule for symbol $lhs",$lineno);
431             }
432              
433             sub Parse {
434 10     10 0 22 my($self)=shift;
435              
436 10 50       24 @_ > 0
437             or croak("No input grammar\n");
438              
439 10         20 my($parsed)={};
440              
441 10         15 $input=\$_[0];
442              
443 10         16 $lexlevel=0;
444 10         27 @lineno=(1,1);
445 10         17 $nberr=0;
446 10         17 $prec=0;
447 10         13 $labelno=0;
448              
449 10         15 $head=();
450 10         14 $tail="";
451              
452 10         13 $syms={};
453 10         12 $token={};
454 10         14 $term={};
455 10         14 $nterm={};
456 10         16 $rules=[ undef ]; #reserve slot 0 for start rule
457 10         15 $precterm={};
458              
459 10         14 $start="";
460 10         15 $nullable={};
461 10         12 $expect=0;
462              
463 10         29 pos($$input)=0;
464              
465              
466 10         48 $self->YYParse(yylex => \&_Lexer, yyerror => \&_Error);
467              
468 10 50       28 $nberr
469             and _SyntaxError(2,"Errors detected: No output",-1);
470              
471 10         81 @$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         20 undef($input);
477 10         16 undef($lexlevel);
478 10         18 undef(@lineno);
479 10         16 undef($nberr);
480 10         12 undef($prec);
481 10         17 undef($labelno);
482              
483 10         12 undef($head);
484 10         11 undef($tail);
485              
486 10         15 undef($syms);
487 10         32 undef($token);
488 10         13 undef($term);
489 10         17 undef($nterm);
490 10         13 undef($rules);
491 10         14 undef($precterm);
492              
493 10         11 undef($start);
494 10         13 undef($nullable);
495 10         12 undef($expect);
496              
497 10         33 $parsed
498             }
499