File Coverage

blib/lib/Blatte/Parser.pm
Criterion Covered Total %
statement 341 366 93.1
branch 130 206 63.1
condition 3 9 33.3
subroutine 25 27 92.5
pod 4 22 18.1
total 503 630 79.8


line stmt bran cond sub pod time code
1             package Blatte::Parser;
2              
3 12     12   83 use strict;
  12         134  
  12         628  
4              
5 12     12   63 use Blatte;
  12         20  
  12         649  
6 12     12   65 use Blatte::Syntax;
  12         20  
  12         243  
7 12     12   61 use Blatte::Ws;
  12         19  
  12         270  
8              
9 12     12   56 use vars qw($identifier_regex);
  12         27  
  12         80109  
10              
11             $identifier_regex = qr/[A-Za-z][A-Za-z0-9_]*(?![A-Za-z0-9_])/;
12              
13             sub new {
14 12     12 0 31 my $type = shift;
15 12         222 bless { special_forms => [\&define_expr,
16             \&set_expr,
17             \&lambda_expr,
18             \&any_let_expr,
19             \&if_expr,
20             \&cond_expr,
21             \&while_expr,
22             \&and_expr,
23             \&or_expr] }, $type;
24             }
25              
26             sub add_special_form {
27 0     0 0 0 my $self = shift;
28 0         0 push(@{$self->{special_forms}}, @_);
  0         0  
29             }
30              
31             sub parse {
32 43     43 1 86 my($self, $input_arg) = @_;
33 43         201 my $expr = $self->expr($input_arg);
34 43 50       163 return undef unless defined($expr);
35 43         147 sprintf("&Blatte::wrapws('%s',\n %s)",
36             &Blatte::wsof($expr), $expr->transform(16));
37             }
38              
39             sub expr {
40 252     252 1 353 my($self, $input_arg) = @_;
41              
42 252         547 my $input = $input_arg;
43 252 100       843 if (ref($input)) {
44 209         319 $input = $$input;
45             }
46              
47 252         637 my $ws = &consume_whitespace(\$input);
48              
49 252 50       641 return undef if ($input eq '');
50              
51 252         264 my $syntax;
52              
53 252 100       1833 if (substr($input, 0, 1) eq '{') {
    100          
    100          
    100          
54 57         107 $input = substr($input, 1);
55              
56 57         191 $syntax = $self->special_form(\$input);
57 57 100       235 $syntax = new Blatte::Syntax::List($self->list_subexprs(\$input))
58             unless defined $syntax;
59              
60 57         124 &consume_whitespace(\$input);
61 57 50       153 return undef if ($input eq '');
62 57 50       155 return undef if (substr($input, 0, 1) ne '}');
63 57         99 $input = substr($input, 1);
64             } elsif ($input =~ /^\\\"([^\\]+|\\[^\"])*\\\"/g) {
65 1         47 my $str = substr($input, 0, pos($input));
66 1         4 $input = substr($input, pos($input));
67 1         48 $str = substr($str, 2, length($str) - 4);
68 1         2 $str =~ s/\\(.)/$1/g;
69 1         5 $syntax = new Blatte::Syntax::Literal($str);
70             } elsif ($input =~ /^\\($identifier_regex)/go) {
71 42         101 my $name = $1;
72 42         100 $input = substr($input, pos($input));
73 42         210 $syntax = new Blatte::Syntax::VarRef($name);
74             } elsif ($input =~ /^([^\\{}\s]+|\\[\\{}])+/g) {
75 94         179 my $atom = substr($input, 0, pos($input));
76 94         181 $input = substr($input, pos($input));
77 94         148 $atom =~ s/\\(.)/$1/g;
78 94         453 $syntax = new Blatte::Syntax::Literal($atom);
79             } else {
80 58         136 return undef;
81             }
82              
83 194 100       485 if (ref($input_arg)) {
84 151         419 $$input_arg = $input;
85             }
86              
87 194         835 return &Blatte::wrapws($ws, $syntax);
88             }
89              
90             sub list_subexprs {
91 35     35 0 63 my($self, $input_arg) = @_;
92              
93 35         70 my $input = $input_arg;
94 35 50       87 if (ref($input)) {
95 35         79 $input = $$input_arg;
96             }
97              
98 35         47 my @subexprs;
99 35         48 while (1) {
100 135         264 my $ws = &consume_whitespace(\$input);
101 135 100       1249 if ($input =~ /^\\($identifier_regex)=/go) {
102 2         4 my $name = $1;
103 2         3 $input = substr($input, pos($input));
104              
105 2         5 my $expr = $self->expr(\$input);
106 2 50       6 return undef unless defined($expr);
107              
108 2         6 push(@subexprs,
109             new Blatte::Syntax::Assignment($name, &Blatte::unwrapws($expr)));
110             } else {
111 133         336 $input = ($ws . $input);
112 133         377 my $expr = $self->expr(\$input);
113 133 100       336 last unless defined($expr);
114 98         205 push(@subexprs, $expr);
115             }
116             }
117              
118 35 50       109 if (ref($input_arg)) {
119 35         60 $$input_arg = $input;
120             }
121              
122 35         252 return @subexprs;
123             }
124              
125             sub special_form {
126 57     57 0 92 my($self, $input_arg) = @_;
127              
128 57         245 my $input = $input_arg;
129 57 50       149 if (ref($input)) {
130 57         183 $input = $$input_arg;
131             }
132              
133 57         74 my $syntax;
134 57         71 foreach my $formfn (@{$self->{special_forms}}) {
  57         571  
135 399         1409 $syntax = &$formfn($self, \$input);
136 399 100       1027 last if defined($syntax);
137             }
138              
139 57 100       185 return undef unless defined($syntax);
140              
141 22 50       63 if (ref($input_arg)) {
142 22         44 $$input_arg = $input;
143             }
144              
145 22         52 return $syntax;
146             }
147              
148             sub define_expr {
149 57     57 0 85 my($self, $input_arg) = @_;
150              
151 57         85 my $input = $input_arg;
152 57 50       142 if (ref($input)) {
153 57         89 $input = $$input;
154             }
155              
156 57         163 my $syntax = $self->define_var_expr(\$input);
157 57 100       243 $syntax = $self->define_fn_expr(\$input) unless defined($syntax);
158              
159 57 100       210 return undef unless defined($syntax);
160              
161 7 50       22 if (ref($input_arg)) {
162 7         14 $$input_arg = $input;
163             }
164              
165 7         12 return $syntax;
166             }
167              
168             sub define_var_expr {
169 57     57 0 88 my($self, $input_arg) = @_;
170              
171 57         75 my $input = $input_arg;
172 57 50       137 if (ref($input)) {
173 57         155 $input = $$input_arg;
174             }
175              
176 57         151 &consume_whitespace(\$input);
177 57 100       227 return undef unless ($input =~ /^\\define(?![A-Za-z0-9_])/);
178 7         19 $input = substr($input, 7);
179              
180 7         32 &consume_whitespace(\$input);
181 7 100       199 return undef unless ($input =~ /^\\($identifier_regex)/go);
182 3         9 my $name = $1;
183 3         8 $input = substr($input, pos($input));
184              
185 3         18 my $expr = $self->expr(\$input);
186 3 50       53 return undef unless defined($expr);
187              
188 3         20 my $syntax = new Blatte::Syntax::DefineVar($name, $expr);
189              
190 3 50       9 if (ref($input_arg)) {
191 3         7 $$input_arg = $input;
192             }
193              
194 3         8 return $syntax;
195             }
196              
197             sub define_fn_expr {
198 54     54 0 88 my($self, $input_arg) = @_;
199              
200 54         6644 my $input = $input_arg;
201 54 50       211 if (ref($input)) {
202 54         101 $input = $$input_arg;
203             }
204              
205 54         118 &consume_whitespace(\$input);
206 54 100       219 return undef unless ($input =~ /^\\define(?![A-Za-z0-9_])/);
207 4         10 $input = substr($input, 7);
208              
209 4         20 &consume_whitespace(\$input);
210 4 50 33     29 if (($input eq '') || (substr($input, 0, 1) ne '{')) {
211 0         0 return undef;
212             }
213 4         9 $input = substr($input, 1);
214              
215 4         10 &consume_whitespace(\$input);
216 4 50       76 return undef unless ($input =~ /^\\($identifier_regex)/go);
217 4         13 my $name = $1;
218 4         175 $input = substr($input, pos($input));
219              
220 4         16 my @params = $self->params(\$input);
221              
222 4         11 &consume_whitespace(\$input);
223 4 50 33     30 if (($input eq '') || (substr($input, 0, 1) ne '}')) {
224 0         0 return undef;
225             }
226 4         8 $input = substr($input, 1);
227              
228 4         18 my @exprs = $self->exprs(\$input);
229              
230 4         30 my $syntax = new Blatte::Syntax::DefineFn($name, \@params, \@exprs);
231              
232 4 50       14 if (ref($input_arg)) {
233 4         9 $$input_arg = $input;
234             }
235              
236 4         13 return $syntax;
237             }
238              
239             sub set_expr {
240 50     50 0 90 my($self, $input_arg) = @_;
241              
242 50         84 my $input = $input_arg;
243 50 50       168 if (ref($input)) {
244 50         86 $input = $$input_arg;
245             }
246              
247 50         107 &consume_whitespace(\$input);
248 50 100       174 return undef unless ($input =~ /^\\set!(?![A-Za-z0-9_])/);
249 1         3 $input = substr($input, 5);
250              
251 1         2 &consume_whitespace(\$input);
252 1 50       35 return undef unless ($input =~ /^\\($identifier_regex)/go);
253 1         3 my $name = $1;
254 1         2 $input = substr($input, pos($input));
255              
256 1         3 my $expr = $self->expr(\$input);
257 1 50       4 return undef unless defined($expr);
258              
259 1         6 my $syntax = new Blatte::Syntax::SetVar($name, $expr);
260              
261 1 50       3 if (ref($input_arg)) {
262 1         2 $$input_arg = $input;
263             }
264              
265 1         2 return $syntax;
266             }
267              
268             sub lambda_expr {
269 49     49 0 71 my($self, $input_arg) = @_;
270              
271 49         68 my $input = $input_arg;
272 49 50       130 if (ref($input)) {
273 49         1441 $input = $$input_arg;
274             }
275              
276 49         104 &consume_whitespace(\$input);
277 49 100       178 return undef unless ($input =~ /^\\lambda(?![A-Za-z0-9_])/);
278              
279 1         3 $input = substr($input, 7);
280              
281 1         3 &consume_whitespace(\$input);
282 1 50 33     10 if (($input eq '') || (substr($input, 0, 1) ne '{')) {
283 0         0 return undef;
284             }
285 1         11 $input = substr($input, 1);
286              
287 1         16 my @params = $self->params(\$input);
288              
289 1         3 &consume_whitespace(\$input);
290 1 50       4 return undef if ($input eq '');
291 1 50       3 return undef if (substr($input, 0, 1) ne '}');
292 1         2 $input = substr($input, 1);
293              
294 1         2 my @exprs;
295 1         5 while (defined(my $expr = $self->expr(\$input))) {
296 1         3 push(@exprs, $expr);
297             }
298              
299 1         8 my $syntax = new Blatte::Syntax::Lambda(\@params, \@exprs);
300              
301 1 50       4 if (ref($input_arg)) {
302 1         2 $$input_arg = $input;
303             }
304              
305 1         2 return $syntax;
306             }
307              
308             sub params {
309 5     5 0 9 my($self, $input_arg) = @_;
310              
311 5         843 my $input = $input_arg;
312 5 50       18 if (ref($input)) {
313 5         11 $input = $$input_arg;
314             }
315              
316 5         7 my @params;
317 5         6 while (1) {
318 13         24 &consume_whitespace(\$input);
319 13 100       213 if ($input =~ /^\\($identifier_regex)/g) {
    100          
    100          
320 5         8 my $name = $1;
321 5         12 $input = substr($input, pos($input));
322 5         25 push(@params, new Blatte::Syntax::Param::Positional($name));
323             } elsif ($input =~ /^\\=($identifier_regex)/g) {
324 2         4 my $name = $1;
325 2         5 $input = substr($input, pos($input));
326 2         11 push(@params, new Blatte::Syntax::Param::Named($name));
327             } elsif ($input =~ /^\\&($identifier_regex)/g) {
328 1         3 my $name = $1;
329 1         2 $input = substr($input, pos($input));
330 1         8 push(@params, new Blatte::Syntax::Param::Rest($name));
331             } else {
332 5         10 last;
333             }
334             }
335              
336 5 50       18 if (ref($input_arg)) {
337 5         10 $$input_arg = $input;
338             }
339              
340 5         19 return @params;
341             }
342              
343             sub any_let_expr {
344 48     48 0 73 my($self, $input_arg) = @_;
345              
346 48         389 my $input = $input_arg;
347 48 50       129 if (ref($input)) {
348 48         81 $input = $$input_arg;
349             }
350              
351 48         110 &consume_whitespace(\$input);
352 48 100       249 return undef unless ($input =~ /^\\(letrec|let\*?)(?![A-Za-z0-9_])/g);
353 6         17 my $keyword = $1;
354 6         18 $input = substr($input, pos($input));
355              
356 6         13 &consume_whitespace(\$input);
357 6 50       18 return undef if ($input eq '');
358 6 50       19 return undef if (substr($input, 0, 1) ne '{');
359 6         14 $input = substr($input, 1);
360              
361 6         20 my @clauses = $self->let_clauses(\$input);
362 6 50       17 return undef unless @clauses;
363              
364 6         24 &consume_whitespace(\$input);
365 6 50       19 return undef if ($input eq '');
366 6 50       20 return undef if (substr($input, 0, 1) ne '}');
367 6         11 $input = substr($input, 1);
368              
369 6         38 my @exprs = $self->exprs(\$input);
370              
371 6         14 my $syntax;
372 6 100       21 if ($keyword eq 'let') {
    50          
373 4         24 $syntax = new Blatte::Syntax::Let(\@clauses, \@exprs);
374             } elsif ($keyword eq 'let*') {
375 2         18 $syntax = new Blatte::Syntax::LetStar(\@clauses, \@exprs);
376             } else { # letrec
377 0         0 $syntax = new Blatte::Syntax::Letrec(\@clauses, \@exprs);
378             }
379              
380 6 50       20 if (ref($input_arg)) {
381 6         18 $$input_arg = $input;
382             }
383              
384 6         20 return $syntax;
385             }
386              
387             sub let_clauses {
388 6     6 0 10 my($self, $input_arg) = @_;
389              
390 6         12 my $input = $input_arg;
391 6 50       15 if (ref($input)) {
392 6         13 $input = $$input_arg;
393             }
394              
395 6         9 my @clauses;
396 6         7 while (1) {
397 15         31 &consume_whitespace(\$input);
398 15 50       37 last if ($input eq '');
399 15 100       40 last if (substr($input, 0, 1) ne '{');
400 9         17 $input = substr($input, 1);
401              
402 9         18 &consume_whitespace(\$input);
403 9 50       127 return undef unless ($input =~ /^\\($identifier_regex)/go);
404 9         19 my $name = $1;
405 9         20 $input = substr($input, pos($input));
406              
407 9         39 my $expr = $self->expr(\$input);
408 9 50       24 return undef unless defined($expr);
409              
410 9         28 &consume_whitespace(\$input);
411 9 50       26 return undef if ($input eq '');
412 9 50       24 return undef if (substr($input, 0, 1) ne '}');
413              
414 9         16 $input = substr($input, 1);
415              
416 9         91 push(@clauses, new Blatte::Syntax::LetClause($name, $expr));
417             }
418              
419 6 50       16 if (ref($input_arg)) {
420 6         12 $$input_arg = $input;
421             }
422              
423 6         24 return @clauses;
424             }
425              
426             sub if_expr {
427 42     42 0 65 my($self, $input_arg) = @_;
428              
429 42         61 my $input = $input_arg;
430 42 50       170 if (ref($input)) {
431 42         73 $input = $$input_arg;
432             }
433              
434 42         90 &consume_whitespace(\$input);
435 42 100       148 return undef unless ($input =~ /^\\if(?![A-Za-z0-9_])/);
436 2         4 $input = substr($input, 3);
437              
438 2         6 my $test = $self->expr(\$input);
439 2 50       7 return undef unless defined($test);
440              
441 2         6 my $then = $self->expr(\$input);
442 2 50       7 return undef unless defined($then);
443              
444 2         7 my @else = $self->exprs(\$input);
445              
446 2         13 my $syntax = new Blatte::Syntax::If($test, $then, @else);
447              
448 2 50       6 if (ref($input_arg)) {
449 2         4 $$input_arg = $input;
450             }
451              
452 2         5 return $syntax;
453             }
454              
455             sub cond_expr {
456 40     40 0 65 my($self, $input_arg) = @_;
457              
458 40         130 my $input = $input_arg;
459 40 50       102 if (ref($input)) {
460 40         69 $input = $$input_arg;
461             }
462              
463 40         85 &consume_whitespace(\$input);
464 40 100       135 return undef unless ($input =~ /^\\cond(?![A-Za-z0-9_])/);
465 2         6 $input = substr($input, 5);
466              
467 2         2 my @clauses;
468 2         5 while (1) {
469 9         47 &consume_whitespace(\$input);
470 9 50       24 last if ($input eq '');
471 9 100       21 last if (substr($input, 0, 1) ne '{');
472 7         12 $input = substr($input, 1);
473              
474 7         31 my $test = $self->expr(\$input);
475 7 50       17 return undef unless defined($test);
476              
477 7         20 my @actions = $self->exprs(\$input);
478              
479 7         16 &consume_whitespace(\$input);
480 7 50       15 return undef if ($input eq '');
481 7 50       17 return undef if (substr($input, 0, 1) ne '}');
482 7         12 $input = substr($input, 1);
483              
484 7         30 push(@clauses, new Blatte::Syntax::CondClause($test, @actions));
485             }
486              
487 2         12 my $syntax = new Blatte::Syntax::Cond(@clauses);
488              
489 2 50       13 if (ref($input_arg)) {
490 2         4 $$input_arg = $input;
491             }
492              
493 2         4 return $syntax;
494             }
495              
496             sub while_expr {
497 38     38 0 59 my($self, $input_arg) = @_;
498              
499 38         52 my $input = $input_arg;
500 38 50       93 if (ref($input)) {
501 38         161 $input = $$input_arg;
502             }
503              
504 38         86 &consume_whitespace(\$input);
505 38 50       149 return undef unless ($input =~ /^\\while(?![A-Za-z0-9_])/);
506 0         0 $input = substr($input, 6);
507              
508 0         0 my $test = $self->expr(\$input);
509 0 0       0 return undef unless defined($test);
510              
511 0         0 my @body = $self->exprs(\$input);
512              
513 0         0 my $syntax = new Blatte::Syntax::While($test, @body);
514              
515 0 0       0 if (ref($input_arg)) {
516 0         0 $$input_arg = $input;
517             }
518              
519 0         0 return $syntax;
520             }
521              
522             sub and_expr {
523 38     38 0 129 my($self, $input_arg) = @_;
524              
525 38         49 my $input = $input_arg;
526 38 50       106 if (ref($input)) {
527 38         65 $input = $$input_arg;
528             }
529              
530 38         95 &consume_whitespace(\$input);
531 38 100       138 return undef unless ($input =~ /^\\and(?![A-Za-z0-9_])/);
532 1         2 $input = substr($input, 4);
533              
534 1         6 my @exprs = $self->exprs(\$input);
535 1 50       4 return undef unless @exprs;
536              
537 1         8 my $syntax = new Blatte::Syntax::And(@exprs);
538              
539 1 50       4 if (ref($input_arg)) {
540 1         2 $$input_arg = $input;
541             }
542              
543 1         2 return $syntax;
544             }
545              
546             sub or_expr {
547 37     37 0 62 my($self, $input_arg) = @_;
548              
549 37         51 my $input = $input_arg;
550 37 50       96 if (ref($input)) {
551 37         73 $input = $$input_arg;
552             }
553              
554 37         204 &consume_whitespace(\$input);
555 37 100       162 return undef unless ($input =~ /^\\or(?![A-Za-z0-9_])/);
556 2         9 $input = substr($input, 4);
557              
558 2         6 my @exprs = $self->exprs(\$input);
559 2 50       12 return undef unless @exprs;
560              
561 2         11 my $syntax = new Blatte::Syntax::Or(@exprs);
562              
563 2 50       6 if (ref($input_arg)) {
564 2         3 $$input_arg = $input;
565             }
566              
567 2         4 return $syntax;
568             }
569              
570             sub exprs {
571 22     22 0 34 my($self, $input_arg) = @_;
572              
573 22         33 my $input = $input_arg;
574 22 50       59 if (ref($input)) {
575 22         41 $input = $$input_arg;
576             }
577              
578 22         23 my @exprs;
579 22         31 while (1) {
580 48         267 my $expr = $self->expr(\$input);
581 48 100       123 last unless defined($expr);
582 26         46 push(@exprs, $expr);
583             }
584              
585 22 50       63 if (ref($input_arg)) {
586 22         40 $$input_arg = $input;
587             }
588              
589 22         157 return @exprs;
590             }
591              
592             sub consume_whitespace {
593 993     993 1 1483 my $ref = shift;
594 993         1299 my $str = $$ref;
595 993         1165 my $ws = '';
596              
597 993         1223 while (1) {
598 1209 100       4797 if ($str =~ /^\s+/g) {
    50          
    100          
599 215         382 $ws .= substr($str, 0, pos($str));
600 215         448 $str = substr($str, pos($str));
601             } elsif ($str =~ /^\\;.*/g) {
602 0         0 $str = substr($str, pos($str));
603             } elsif ($str =~ /^\\\//) {
604 1         1 $ws = '';
605 1         2 $str = substr($str, 2);
606             } else {
607 993         1515 $$ref = $str;
608 993         2228 return $ws;
609             }
610             }
611             }
612              
613             sub eof {
614 0     0 1   my($self, $input_arg) = @_;
615              
616 0           my $input = $input_arg;
617 0 0         if (ref($input)) {
618 0           $input = $$input_arg;
619             }
620              
621 0           &consume_whitespace(\$input);
622 0 0         return undef unless ($input eq '');
623              
624 0 0         if (ref($input_arg)) {
625 0           $$input_arg = $input;
626             }
627              
628 0           return 1;
629             }
630              
631             1;
632              
633             __END__