File Coverage

blib/lib/Pugs/Emitter/Rule/Perl6/Ratchet.pm
Criterion Covered Total %
statement 17 248 6.8
branch 0 152 0.0
condition 0 23 0.0
subroutine 6 37 16.2
pod 0 31 0.0
total 23 491 4.6


line stmt bran cond sub pod time code
1             package Pugs::Emitter::Rule::Perl6::Ratchet;
2              
3             # p6-rule perl5 emitter for ":ratchet" (non-backtracking)
4             # see: RuleInline.pl, RuleInline-more.pl for a program prototype
5              
6 1     1   921 use strict;
  1         3  
  1         42  
7 1     1   7 use warnings;
  1         3  
  1         32  
8 1     1   6 use Data::Dumper;
  1         4  
  1         844  
9             $Data::Dumper::Indent = 1;
10              
11             our $direction = "+"; # XXX make lexical
12             our $sigspace = 0;
13             our $capture_count;
14             our $capture_to_array;
15              
16             our $count = 1000 + int(rand(1000));
17 0     0 0   sub id { 'I' . ($count++) }
18              
19             # Calling convention:
20             # $grammar.$rule({ str => '...', pos => $pos, other_arg => $x })
21              
22             =for global vars
23              
24             class Grammar::Base {
25             my $.PRIOR;
26             }
27              
28             Grammar::Base.PRIOR := $rule;
29              
30             =cut
31              
32             sub call_subrule {
33 0     0 0   my ( $subrule, $tab, @param ) = @_;
34 0 0         $subrule = "\$grammar." . $subrule
35             unless $subrule =~ / :: | \. | -> /x;
36             return
37 0           "$tab $subrule( { str => \$str, pos => \$m.to, " .
38             join(", ",@param) .
39             " }, undef )";
40             }
41              
42             sub quote_constant {
43 0     0 0   my $const;
44 0 0         if ( $_[0] eq "\\" ) {
    0          
45 0           $const = "chr(".ord("\\").")";
46             }
47             elsif ( $_[0] eq "'" ) {
48 0           $const = "chr(".ord("'").")"
49             }
50             else {
51 0           $const = "'$_[0]'"
52             }
53 0           return $const;
54             }
55              
56             sub call_constant {
57 0 0   0 0   return " 1 # null constant\n"
58             unless length($_[0]);
59 0           my $const = quote_constant( $_[0] );
60 0           my $len = length( eval $const );
61             #print "Const: [$_[0]] $const $len \n";
62             return
63 0           "$_[1] ( ( substr( \$str, \$m.to, $len ) eq $const )
64             $_[1] ? ( (\$m.to := ( \$m.to $direction $len ) or 1 )
65             $_[1] : 0
66             $_[1] )";
67             }
68              
69             sub call_perl5 {
70 0     0 0   my $const = $_[0];
71             #print "CONST: $const - $direction \n";
72             return
73 0           "$_[1] ( ( substr( \$str, \$m.to ) =~ m:P5/^($const)/ )
74             $_[1] ? ( \$m.to := \$m.to $direction length( \$1 ) or 1 )
75             $_[1] : 0
76             $_[1] )";
77             }
78              
79             sub emit {
80 0     0 0   my ($grammar, $ast, $param) = @_;
81             # runtime parameters: $grammar, $string, $state, $arg_list
82             # rule parameters: see Runtime::Rule.pm
83 0           local $sigspace = $param->{sigspace}; # XXX - $sigspace should be lexical
84 0           local $capture_count = -1;
85 0           local $capture_to_array = 0;
86             #print "rule: ", Dumper( $ast );
87             return
88 0           'do { my $rule; $rule = method ($grammar: {:$str, :$pos, :$continue, :$KEY}) {' .
89             "
90             my \$m;
91             for ( defined \$pos && ! \$continue
92             ? \$pos
93             : ( ( \$pos || 0 ) .. length( \$str ) )
94             ) -> \$pos1 {
95             my \%pad;
96             my \%named;
97             \%named{KEY} := \$KEY
98             if defined \$KEY;
99             \$m := Pugs::Runtime::Match( {
100             str => \$str, from => (0+\$pos1), to => \$pos1,
101             bool => 1, match => [], named => \%named, capture => undef,
102             } );
103             {
104             my \$prior := Grammar::Base.PRIOR;
105             temp Grammar::Base.PRIOR := \$prior;
106             \$m.bool := 0 unless
107             " .
108             #" do { TAILCALL: ;\n" .
109             emit_rule( $ast, ' ' ) . ";
110             }
111             if ( \$m.bool ) {
112             my \$prior := Grammar::Base.PRIOR;
113             Grammar::Base.PRIOR := sub {
114             temp Grammar::Base.PRIOR := \$prior;
115             \$rule.(\@_);
116             };
117             last;
118             }
119             } # /for
120             # Grammar::Base.MATCH := \$m; # this must be set in the caller side
121             return \$m;
122             } }
123             ";
124             }
125              
126             sub emit_rule {
127 0     0 0   my $n = $_[0];
128 0           my $tab = $_[1] . ' ';
129 0 0         die "unknown node: ", Dumper( $n )
130             unless ref( $n ) eq 'HASH';
131             #print "NODE ", Dumper($n);
132 0           my ($k) = keys %$n;
133 0           my $v = $$n{$k};
134             # XXX - use real references
135 1     1   7 no strict 'refs';
  1         2  
  1         3966  
136             #print "NODE ", Dumper($k), ", ", Dumper($v);
137 0           my $code = &$k( $v, $tab );
138 0           return $code;
139             }
140              
141             #rule nodes
142              
143             sub non_capturing_group {
144 0     0 0   return emit_rule( $_[0], $_[1] );
145             }
146             sub quant {
147 0     0 0   my $term = $_[0]->{'term'};
148 0   0       my $quantifier = $_[0]->{quant} || '';
149 0   0       my $greedy = $_[0]->{greedy} || ''; # + ?
150 0 0         die "greediness control not implemented: $greedy"
151             if $greedy;
152             #print "QUANT: ",Dumper($_[0]);
153             # TODO: fix grammar to not emit empty quantifier
154 0 0         my $tab = ( $quantifier eq '' ) ? $_[1] : $_[1] . " ";
155 0           my $ws = metasyntax( '?ws', $tab );
156 0 0 0       my $ws3 = ( $sigspace && $_[0]->{ws3} ne '' ) ? " &&\n$ws" : '';
157              
158 0           my $rul;
159             {
160             #print "Term: ", Dumper($term), "\n";
161 0           my $cap = $capture_to_array;
  0            
162 0   0       local $capture_to_array = $cap || ( $quantifier ne '' );
163 0           $rul = emit_rule( $term, $tab );
164             }
165              
166 0 0 0       $rul = "$ws &&\n$rul" if $sigspace && $_[0]->{ws1} ne '';
167 0 0 0       $rul = "$rul &&\n$ws" if $sigspace && $_[0]->{ws2} ne '';
168             #print $rul;
169 0 0         return $rul
170             if $quantifier eq '';
171             # * + ?
172             # TODO: *? +? ??
173             # TODO: *+ ++ ?+
174             # TODO: quantifier + capture creates Array
175             return
176 0 0         "$_[1] (\n$rul\n" .
177             "$_[1] || ( \$m.bool = 1 )\n" .
178             "$_[1] ) $ws3"
179             if $quantifier eq '?';
180             return
181 0 0         "$_[1] do { while (\n$rul) {}; \$m.bool := 1 }$ws3"
182             if $quantifier eq '*';
183             return
184 0 0         "$_[1] (\n$rul\n" .
185             "$_[1] && do { while (\n$rul) {}; \$m.bool := 1 }\n" .
186             "$_[1] ) $ws3"
187             if $quantifier eq '+';
188 0           die "quantifier not implemented: $quantifier";
189             }
190             sub alt {
191 0     0 0   my @s;
192             # print 'Alt: ';
193 0           my $count = $capture_count;
194 0           my $max = -1;
195 0           my $id = id();
196 0           for ( @{$_[0]} ) {
  0            
197 0           $capture_count = $count;
198 0           my $tmp = emit_rule( $_, $_[1].' ' );
199             # print ' ',$capture_count;
200 0 0         $max = $capture_count
201             if $capture_count > $max;
202 0 0         push @s, $tmp if $tmp;
203             }
204 0           $capture_count = $max;
205             # print " max = $capture_count\n";
206             return
207 0           "$_[1] (
208             $_[1] ( \%pad{$id} := \$m.to or 1 )
209             $_[1] && (
210             " . join( "
211             $_[1] )
212             $_[1] || (
213             $_[1] ( ( \$m.bool := 1 ) && ( \$m.to := \%pad{$id} ) or 1 )
214             $_[1] && ",
215             @s
216             ) . "
217             $_[1] )
218             $_[1] )";
219             }
220 0     0 0   sub alt1 { &alt }
221             sub conjunctive {
222 0     0 0   my @s;
223             # print 'conjunctive: ';
224 0           my $count = $capture_count;
225 0           my $max = -1;
226 0           my $id = id();
227 0           for ( @{$_[0]} ) {
  0            
228 0           $capture_count = $count;
229 0           my $tmp = emit_rule( $_, $_[1].' ' );
230             # print ' ',$capture_count;
231 0 0         $max = $capture_count
232             if $capture_count > $max;
233 0 0         push @s, $tmp if $tmp;
234             }
235 0           $capture_count = $max;
236             # print " max = $capture_count\n";
237             return
238 0           "$_[1] (
239             $_[1] ( \%pad{$id} := \$m.to or 1 )
240             $_[1] && (
241             " . join( "
242             $_[1] )
243             $_[1] && (
244             $_[1] ( ( \$m.bool := 1 ) && ( \$m.to := \%pad{$id} ) or 1 )
245             $_[1] && ",
246             @s
247             ) . "
248             $_[1] )
249             $_[1] )";
250             }
251             sub concat {
252 0     0 0   my @s;
253              
254             =for optimizing
255             # optimize for the common case of "words"
256             # Note: this optimization has almost no practical effect
257             my $is_constant = 0;
258             for ( @{$_[0]} ) {
259             if ( ! $sigspace && exists $_->{quant} ) {
260             my $was_constant = $is_constant;
261             $is_constant =
262             $_->{quant}->{quant} eq ''
263             && exists $_->{quant}->{term}->{constant};
264             #print "concat: ", Dumper( $_ );
265             if ( $is_constant && $was_constant && $direction ne '-' ) {
266             $s[-1]->{quant}->{term}->{constant} .=
267             $_->{quant}->{term}->{constant};
268             #print "constant: ",$s[-1]->{quant}->{term}->{constant},"\n";
269             next;
270             }
271             }
272             push @s, $_;
273             }
274              
275             for ( @s ) {
276             $_ = emit_rule( $_, $_[1] );
277             }
278             =cut
279              
280 0           for ( @{$_[0]} ) {
  0            
281 0           my $tmp = emit_rule( $_, $_[1] );
282 0 0         push @s, $tmp if $tmp;
283             }
284 0 0         @s = reverse @s if $direction eq '-';
285 0           return "$_[1] (\n" . join( "\n$_[1] &&\n", @s ) . "\n$_[1] )";
286             }
287             sub code {
288 0     0 0   return "$_[1] $_[0]\n";
289             }
290             sub dot {
291 0     0 0   "$_[1] ( substr( \$s, \$m.to$direction$direction, 1 ) ne '' )"
292             }
293              
294             sub variable {
295 0     0 0   my $name = "$_[0]";
296 0           my $value = undef;
297             # XXX - eval $name doesn't look up in user lexical pad
298             # XXX - what &xxx interpolate to?
299            
300 0 0         if ( $name =~ /^\$/ ) {
301             # $^a, $^b
302 0 0         if ( $name =~ /^ \$ \^ ([^\s]*) /x ) {
303 0           my $index = ord($1)-ord('a');
304             #print "Variable #$index\n";
305             #return "$_[1] constant( \$_[7][$index] )\n";
306            
307 0           my $code =
308             " ... sub {
309             #print \"Runtime Variable args[\", join(\",\",\@_) ,\"] \$_[7][$index]\\n\";
310             return constant( \$_[7][$index] ).(\@_);
311             }";
312 0           $code =~ s/^/$_[1]/mg;
313 0           return "$code\n";
314             }
315             else {
316 0           $value = eval $name;
317             }
318             }
319            
320 0 0         $value = join('', eval $name) if $name =~ /^\@/;
321 0 0         if ( $name =~ /^%/ ) {
322 0           my $id = '$' . id();
323 0           my $preprocess_hash = 'Pugs::Runtime::Regex::preprocess_hash';
324 0           my $code = "
325             do {
326             state $id;
327             state ${id}_sizes;
328             unless ( $id ) {
329             my \$hash := $name;
330             my \%sizes := \%\$hash.keys.map:{ .length => 1 };
331             ${id}_sizes := [ \%sizes.keys.sort:{ \$^b <=> \$^a } ];
332             " . #print \"sizes: \@${id}_sizes\\n\";
333             "$id = \$hash;
334             }
335             " . #print 'keys: ',Dumper( $id );
336             "my \$match := 0;
337             my \$key;
338             for \@". $id ."_sizes {
339             \$key := ( \$m.to <= length( \$s )
340             ? substr( \$s, \$m.to, \$_ )
341             : '' );
342             " . #print \"try ".$name." \$_ = \$key; \$s\\\n\";
343             "if ( %". $id .".exists( \$key ) ) {
344             " . #\$named{KEY} = \$key;
345             #Grammar::Base.MATCH := \$m;
346             #print \"m: \", Dumper( Grammar::Base.MATCH )
347             # if ( \$key eq 'until' );
348             #print \"* ".$name."\{'\$key\'} at \$m.to \\\n\";
349             "\$match = $preprocess_hash( $id, \$key ).({ str => \$str, grammar => \$grammar, pos => ( \$m.to + \$_ ), KEY => \$key });
350             " . #print \"match: \", Dumper( \$match.data );
351             "last if \$match;
352             }
353             }
354             if ( \$match ) {
355             \$m.to = \$match.to;
356             " . #print \"match: \$key at \$m.to = \", Dumper( \$match.data );
357             "\$match.bool = 1;
358             };
359             \$match;
360             }";
361             #print $code;
362 0           return $code;
363             }
364 0 0         die "interpolation of $name not implemented"
365             unless defined $value;
366              
367 0           return call_constant( $value, $_[1] );
368             }
369             sub special_char {
370 0     0 0   my $char = substr($_[0],1);
371 0 0         return call_perl5( '(?:\n\r?|\r\n?)', $_[1] )
372             if $char eq 'n';
373 0 0         return call_perl5( '(?!\n\r?|\r\n?).', $_[1] )
374             if $char eq 'N';
375 0           for ( qw( r n t e f w d s ) ) {
376 0 0         return call_perl5( "\\$_", $_[1] ) if $char eq $_;
377 0 0         return call_perl5( "[^\\$_]", $_[1] ) if $char eq uc($_);
378             }
379 0 0         $char = '\\\\' if $char eq '\\';
380 0           return call_constant( $char, $_[1] );
381             }
382             sub match_variable {
383 0     0 0   my $name = $_[0];
384 0           my $num = substr($name,1);
385             #print "var name: ", $num, "\n";
386 0           my $code =
387             " ... sub {
388             my \$m = Pugs::Runtime::Match( \$_[2] );
389             return constant( \"\$m.[$num]\" ).(\@_);
390             }";
391 0           $code =~ s/^/$_[1]/mg;
392 0           return "$code\n";
393             }
394             sub closure {
395 0     0 0   my $code = $_[0];
396            
397 0 0         if ( ref( $code ) ) {
398 0 0         if ( defined $Pugs::Compiler::Perl6::VERSION ) {
399             #print " perl6 compiler is loaded \n";
400 0           my $perl5 = Pugs::Emitter::Perl6::Perl5::emit( 'grammar', $code, 'self' );
401             return
402 0 0         "do {
403             temp Grammar::Base.MATCH := \$m;
404             temp Grammar::Base.SUCCEED := 1;
405             \$m.capture := sub $perl5.();
406             \$m.bool := Grammar::Base.SUCCEED;
407             Grammar::Base.MATCH := \$m if \$m.bool;
408             return \$m if \$m.bool;
409             }" if $perl5 =~ /return/;
410             return
411 0           "do {
412             Grammar::Base.MATCH := \$m;
413             temp Grammar::Base.SUCCEED := 1;
414             sub $perl5.();
415             Grammar::Base.SUCCEED;
416             }";
417             }
418             }
419              
420             #print " perl6 compiler is NOT loaded \n";
421             #print "Code: $code\n";
422            
423             return
424 0 0         "$_[1] do {\n" .
425             "$_[1] local Grammar::Base.SUCCEED := 1;\n" .
426             "$_[1] Grammar::Base.MATCH := \$m;\n" .
427             "$_[1] sub $code.( \$m );\n" .
428             "$_[1] Grammar::Base.SUCCEED;\n" .
429             "$_[1] }"
430             unless $code =~ /return/;
431            
432             return
433 0           "$_[1] do { \n" .
434             "$_[1] local Grammar::Base.SUCCEED := 1;\n" .
435             "$_[1] Grammar::Base.MATCH := \$m;\n" .
436             "$_[1] \$m.capture := \\( sub $code.( \$m ) ); \n" .
437             "$_[1] \$m.bool := Grammar::Base.SUCCEED;\n" .
438             "$_[1] Grammar::Base.MATCH := \$m if \$m.bool; \n" .
439             "$_[1] return \$m if \$m.bool; \n" .
440             "$_[1] }";
441              
442             }
443             sub capturing_group {
444 0     0 0   my $program = $_[0];
445              
446 0           $capture_count++;
447             {
448 0           local $capture_count = -1;
  0            
449 0           local $capture_to_array = 0;
450 0 0         $program = emit_rule( $program, $_[1].' ' )
451             if ref( $program );
452             }
453              
454 0 0         return "$_[1] do{
455             $_[1] my \$hash := do {
456             $_[1] my \$bool := 1;
457             $_[1] my \$from := \$m.to;
458             $_[1] my \@match;
459             $_[1] my \%named;
460             $_[1] \$bool := 0 unless
461             " . $program . ";
462             $_[1] { str => \$s, from => \$from, match => \@match, named => \%named, bool => \$bool, to => (0+\$m.to), capture => undef }
463             $_[1] };
464             $_[1] my \$bool = \$hash.{'bool'};" .
465             ( $capture_to_array
466             ? "
467             $_[1] if ( \$bool ) {
468             $_[1] push \@( \$match[ $capture_count ] ), Pugs::Runtime::Match( \$hash );
469             $_[1] }"
470             : "
471             $_[1] \$match[ $capture_count ] = Pugs::Runtime::Match( \$hash );"
472             ) . "
473             $_[1] \$bool;
474             $_[1] }";
475             }
476              
477             sub capture_as_result {
478 0     0 0   my $program = $_[0];
479              
480 0           $capture_count++;
481             {
482 0           local $capture_count = -1;
  0            
483 0           local $capture_to_array = 0;
484 0 0         $program = emit_rule( $program, $_[1].' ' )
485             if ref( $program );
486             }
487 0           return "$_[1] do{
488             $_[1] my \$hash := do {
489             $_[1] my \$bool := 1;
490             $_[1] my \$from := \$m.to;
491             $_[1] my \@match;
492             $_[1] my \%named;
493             $_[1] \$bool := 0 unless
494             " . $program . ";
495             $_[1] { str => \$s, from => \$from, match => \@match, named => \%named, bool => \$bool, to => (0+\$m.to), capture => undef }
496             $_[1] };
497             $_[1] my \$bool := \$hash.{'bool'};
498             $_[1] \$m.capture := ~Pugs::Runtime::Match( \$hash );
499             $_[1] \$bool;
500             $_[1] }";
501             }
502             sub named_capture {
503 0     0 0   my $name = $_[0]{ident};
504 0 0         $name = $name->{match_variable} if ref($name) eq 'HASH';
505 0           $name =~ s/^[\$\@\%]//; # TODO - change semantics as needed
506 0           my $program = $_[0]{rule};
507             #print "name [$name]\n";
508            
509 0 0         if ( exists $program->{metasyntax} ) {
    0          
510             #print "aliased subrule\n";
511             # $/ = $/
512            
513 0           my $cmd = $program->{metasyntax};
514 0 0         die "invalid aliased subrule"
515             unless $cmd =~ /^[_[:alnum:]]/;
516            
517             #
518 0           my ( $subrule, $param_list ) = split( /[\(\)]/, $cmd );
519 0 0         $param_list = '' unless defined $param_list;
520 0           my @param = split( ',', $param_list );
521 0 0         return "$_[1] do {
522             my \$prior := \$::_V6_PRIOR_;
523             my \$match := \n" .
524             call_subrule( $subrule, $_[1]." ", @param ) . ";
525             \$::_V6_PRIOR_ := \$prior;
526             if ( \$match ) {" .
527             ( $capture_to_array
528             ? " push \@(\$named{'$name'}), \$match;"
529             : " \$named{'$name'} := \$match;"
530             ) . "
531             \$m.to := \$match.to;
532             1
533             }
534             else { 0 }
535             }";
536             }
537             elsif ( exists $program->{capturing_group} ) {
538             #print "aliased capturing_group\n";
539             # $/ = $/[0]
540             {
541 0           local $capture_count = -1;
  0            
542 0           local $capture_to_array = 0;
543 0 0         $program = emit_rule( $program, $_[1].' ' )
544             if ref( $program );
545             }
546 0 0         return "$_[1] do{
547             my \$match := Pugs::Runtime::Match( do {
548             my \$bool := 1;
549             my \$from := \$m.to;
550             my \@match;
551             my \%named;
552             \$bool := 0 unless " .
553             $program . ";
554             { str => \$s, from => \$from, match => \@match, named => \%named, bool => \$bool, to => (0+\$m.to), capture => undef }
555             } );
556             if ( \$match ) {" .
557             ( $capture_to_array
558             ? " push \@(\$named{'$name'}), \$match;"
559             : " \$named{'$name'} := \$match;"
560             ) . "
561             \$m.to := \$match.to;
562             1
563             }
564             else { 0 }
565             }";
566             }
567             else {
568             #print "aliased non_capturing_group\n";
569             # $/ = "$/"
570             #print Dumper( $_[0] );
571 0           $program = emit_rule( $program, $_[1].' ' );
572 0 0         return "$_[1] do{
573             my \$from := \$m.to;
574             my \$bool := $program;
575             my \$match := Pugs::Runtime::Match(
576             { str => \$s, from => \$from, match => [], named => {}, bool => 1, to => (0+\$m.to), capture => undef }
577             );" .
578             ( $capture_to_array
579             ? " push \@(\$named{'$name'}), \$match;"
580             : " \$named{'$name'} = \$match;"
581             ) . "
582             \$bool
583             }";
584             }
585             }
586             sub negate {
587 0     0 0   my $program = $_[0];
588             #print "Negate: ", Dumper($_[0]);
589 0 0         $program = emit_rule( $program, $_[1].' ' )
590             if ref( $program );
591 0           return "$_[1] do{
592             $_[1] my \$pos1 := \$m.to;
593             $_[1] do {
594             $_[1] my \$pos := \$pos1;
595             $_[1] my \$from := \$pos;
596             $_[1] my \@match;
597             $_[1] my \%named;
598             $_[1] \$bool := " . $program . " ? 0 : 1;
599             $_[1] \$bool;
600             $_[1] };
601             $_[1] }";
602             }
603             sub before {
604 0     0 0   my $program = $_[0]{rule};
605 0 0         $program = emit_rule( $program, $_[1].' ' )
606             if ref( $program );
607 0           return "$_[1] do{
608             $_[1] my \$pos1 := \$m.to;
609             $_[1] do {
610             $_[1] my \$pos := \$pos1;
611             $_[1] my \$from := \$pos;
612             $_[1] my \@match;
613             $_[1] my \%named;
614             $_[1] \$bool := 0 unless
615             " . $program . ";
616             $_[1] \$bool;
617             $_[1] };
618             $_[1] }";
619             }
620             sub not_before {
621 0     0 0   my $program = $_[0]{rule};
622 0 0         $program = emit_rule( $program, $_[1].' ' )
623             if ref( $program );
624 0           return "$_[1] do{
625             $_[1] my \$pos1 := \$m.to;
626             $_[1] do {
627             $_[1] my \$pos := \$pos1;
628             $_[1] my \$from := \$pos;
629             $_[1] my \@match;
630             $_[1] my \%named;
631             $_[1] my \$bool := 1;
632             $_[1] \$bool := 0 unless
633             " . $program . ";
634             $_[1] ! \$bool;
635             $_[1] };
636             $_[1] }";
637             }
638             sub after {
639 0     0 0   local $direction = "-";
640 0           my $program = $_[0]{rule};
641 0 0         $program = emit_rule( $program, $_[1].' ' )
642             if ref( $program );
643 0           return "$_[1] do{
644             $_[1] my \$pos1 := \$m.to;
645             $_[1] do {
646             $_[1] my \$pos := \$pos1 - 1;
647             $_[1] my \$from := \$pos;
648             $_[1] my \@match;
649             $_[1] my \%named;
650             $_[1] \$bool := 0 unless
651             " . $program . ";
652             $_[1] \$bool;
653             $_[1] };
654             $_[1] }";
655             }
656             sub not_after {
657 0     0 0   warn ' not implemented';
658 0           return;
659             }
660             sub colon {
661 0     0 0   my $str = $_[0];
662 0 0         return "$_[1] 1 # : no-op\n"
663             if $str eq ':';
664 0 0         return "$_[1] ( \$m.to >= length( \$s ) ) \n"
665             if $str eq '$';
666 0 0         return "$_[1] ( \$m.to == 0 ) \n"
667             if $str eq '^';
668            
669 0 0         return "$_[1] ( \$m.to >= length( \$s ) || substr( \$s, \$m.to ) =~ /^(?:\n\r?|\r\n?)/m ) \n"
670             if $str eq '$$';
671 0 0         return "$_[1] ( \$m.to == 0 || substr( \$s, 0, \$m.to ) =~ /(?:\n\r?|\r\n?)\$/m ) \n"
672             if $str eq '^^';
673              
674 0 0         return metasyntax( '?_wb_left', $_[1] )
675             if $str eq '<<';
676 0 0         return metasyntax( '?_wb_right', $_[1] )
677             if $str eq '>>';
678            
679 0           die "'$str' not implemented";
680             }
681             sub modifier {
682 0     0 0   my $str = $_[0];
683 0           die "modifier '$str' not implemented";
684             }
685             sub constant {
686 0     0 0   call_constant( @_ );
687             }
688              
689 1     1   9 use vars qw( %char_class );
  1         3  
  1         100  
690             BEGIN {
691 1     1   4 %char_class = map { $_ => 1 } qw(
  14         1271  
692             alpha alnum ascii blank
693             cntrl digit graph lower
694             print punct space upper
695             word xdigit
696             );
697             }
698              
699             sub metasyntax {
700             #
701 0     0 0   my $cmd = $_[0];
702 0           my $prefix = substr( $cmd, 0, 1 );
703 0 0         if ( $prefix eq '@' ) {
704             # XXX - wrap @array items - see end of Pugs::Grammar::Rule
705             # TODO - param list
706 0           my $name = substr( $cmd, 1 );
707             return
708 0 0         "$_[1] do {
709             my \$match;
710             for my \$subrule ( $cmd ) {
711             \$match := \$subrule.match( \$str, \$grammar, { pos => ( \$m.to ), args => {} }, undef );
712             last if \$match;
713             }
714             if ( \$match ) {" .
715             ( $capture_to_array
716             ? " push \@(\$named{'$name'}), \$match;"
717             : " \$named{'$name'} := \$match;"
718             ) . "
719             \$m.to := \$match.to;
720             1
721             }
722             else { 0 }
723             }";
724             }
725              
726 0 0         if ( $prefix eq '%' ) {
727             # XXX - runtime or compile-time interpolation?
728 0           my $name = substr( $cmd, 1 );
729             # print "<$cmd>\n";
730             # return variable( $cmd );
731 0 0         return "$_[1] do{
732             my \$match := " . variable( $cmd, $_[1] ) . ";
733             if ( \$match ) {" .
734             ( $capture_to_array
735             ? " push \@{\$named{'$name'}}, \$match;"
736             : " \$named{'$name'} := \$match;"
737             ) . "
738             \$m.to := \$match.to;
739             1
740             }
741             else { 0 }
742             }";
743             }
744              
745 0 0         if ( $prefix eq '$' ) {
746 0 0         if ( $cmd =~ /::/ ) {
747             # call method in fully qualified $package::var
748             # ...->match( $rule, $str, $grammar, $flags, $state )
749             # TODO - send $pos to subrule
750             return
751 0           "$_[1] do {\n" .
752             "$_[1] push \@match,\n" .
753             "$_[1] $cmd.match( \$str, \$grammar, {pod => \$m.to}, undef );\n" .
754             "$_[1] \$m.to := \$match[-1].to;\n" .
755             "$_[1] !\$match[-1] != 1;\n" .
756             "$_[1] }"
757             }
758             # call method in lexical $var
759             return
760 0           "$_[1] do {\n" .
761             "$_[1] my \$r := $cmd;\n" .
762             "$_[1] push \@match,\n" .
763             "$_[1] \$r.match( \$str, \$grammar, {pos => \$m.to}, undef );\n" .
764             "$_[1] \$m.to := \$match[-1].to;\n" .
765             "$_[1] !\$match[-1] != 1;\n" .
766             "$_[1] }"
767             }
768 0 0         if ( $prefix eq q(') ) { # single quoted literal '
769 0           $cmd = substr( $cmd, 1, -1 );
770 0           return call_constant( $cmd, $_[1] );
771             }
772 0 0         if ( $prefix eq q(") ) { # interpolated literal "
773 0           $cmd = substr( $cmd, 1, -1 );
774 0           warn "<\"...\"> not implemented";
775 0           return;
776             }
777 0 0         if ( $prefix =~ /[-+[]/ ) { # character class
778 0           $cmd =~ s/\.\./-/g;
779 0 0         if ( $prefix eq '-' ) {
    0          
780 0           $cmd = '[^' . substr($cmd, 2);
781             }
782             elsif ( $prefix eq '+' ) {
783 0           $cmd = substr($cmd, 2);
784             }
785 0           $cmd =~ s/\s+|\n//g;
786             # XXX <[^a]> means [\^a] instead of [^a] in perl5re
787 0           return call_perl5($cmd, $_[1]);
788             }
789 0 0 0       if (
790             $prefix eq '.'
791             || $prefix eq '?' # XXX FIXME
792             )
793             { # non_capturing_subrule / code assertion
794 0           $cmd = substr( $cmd, 1 );
795 0 0         if ( $cmd =~ /^{/ ) {
796 0           warn "code assertion not implemented";
797 0           return;
798             }
799 0 0         if ( exists $char_class{$cmd} ) {
800             # XXX - inlined char classes are not inheritable, but this should be ok
801 0           return call_perl5( "[[:$cmd:]]", $_[1] );
802             }
803 0           my @param; # TODO
804 0           my $subrule = $cmd;
805             return
806 0           "$_[1] do {
807             $_[1] my \$prior := Grammar::Base.PRIOR;
808             $_[1] my \$match := \n" .
809             call_subrule( $subrule, $_[1]." ", @param ) . ";
810             $_[1] Grammar::Base.PRIOR := \$prior;
811             $_[1] my \$bool := (!\$match != 1);
812             $_[1] \$m.to := \$match.to if \$bool;
813             $_[1] \$match;
814             $_[1] }";
815             }
816 0 0         if ( $prefix =~ /[_[:alnum:]]/ ) {
817 0 0         if ( $cmd eq 'cut' ) {
818 0           warn "<$cmd> not implemented";
819 0           return;
820             }
821 0 0         if ( $cmd eq 'commit' ) {
822 0           warn "<$cmd> not implemented";
823 0           return;
824             }
825 0 0         if ( $cmd eq 'null' ) {
826 0           return "$_[1] 1 # null\n"
827             }
828             #
829 0           my ( $subrule, $param_list ) = split( /[\(\)]/, $cmd );
830 0   0       $param_list ||= '';
831              
832 0 0         if ( $subrule eq 'at' ) {
833 0   0       $param_list ||= 0; # XXX compile-time only
834 0           return "$_[1] ( \$m.to == $param_list )\n"
835             }
836              
837 0           return named_capture(
838             {
839             ident => $subrule,
840             rule => { metasyntax => $cmd },
841             },
842             $_[1],
843             );
844             }
845             #if ( $prefix eq '.' ) {
846             # my ( $method, $param_list ) = split( /[\(\)]/, $cmd );
847             # $method =~ s/^\.//;
848             # $param_list ||= '';
849             # return " ( \$str.$method( $param_list ) ? 1 : 0 ) ";
850             #}
851 0           die "<$cmd> not implemented";
852             }
853              
854             1;