File Coverage

blib/lib/Pugs/Emitter/Rule/Perl5/Ratchet.pm
Criterion Covered Total %
statement 15 300 5.0
branch 0 198 0.0
condition 0 47 0.0
subroutine 5 37 13.5
pod 0 32 0.0
total 20 614 3.2


line stmt bran cond sub pod time code
1             package Pugs::Emitter::Rule::Perl5::Ratchet;
2              
3             # p6-rule perl5 emitter for ":ratchet" (non-backtracking)
4             # see: RuleInline.pl, RuleInline-more.pl for a program prototype
5              
6             #use Smart::Comments '####';
7 18     18   112 use strict;
  18         41  
  18         727  
8 18     18   100 use warnings;
  18         37  
  18         522  
9 18     18   13240 use Pugs::Emitter::Rule::Perl5::CharClass;
  18         256  
  18         761  
10 18     18   154 use Data::Dumper;
  18         43  
  18         18226  
11             $Data::Dumper::Indent = 1;
12              
13             our $direction = "+"; # XXX make lexical
14             our $sigspace = 0;
15             our $capture_count;
16             our $capture_to_array;
17             our $RegexPos;
18              
19             our $count;
20             sub id {
21 0 0   0 0   if (!defined $count) {
22 0 0         if (defined $::PCR_SEED) {
23             #warn "SET SEED!!!";
24 0           srand($::PCR_SEED);
25             }
26 0           $count = 1000 + int(rand(1000));
27             }
28 0           'I' . ($count++)
29             }
30              
31             sub call_subrule {
32 0     0 0   my ( $subrule, $tab, $positionals, @param ) = @_;
33 0 0         $subrule = "\$grammar->" . $subrule
34             unless $subrule =~ / :: | \. | -> /x;
35 0           $subrule =~ s/\./->/; # XXX - source filter
36              
37 0 0 0       $positionals = shift @param if $positionals eq '' && @param == 1;
38              
39             return
40 0           "$tab $subrule( \$s, { "
41             . "p => \$pos, "
42             . "positionals => [ $positionals ], "
43             . "args => {" .
44             join(", ",@param) .
45             "}, "
46             . "}, undef )";
47             }
48              
49             sub quote_constant {
50 0     0 0   my $const;
51 0 0         if ( $_[0] eq "\\" ) {
    0          
52 0           $const = "chr(".ord("\\").")";
53             }
54             elsif ( $_[0] eq "'" ) {
55 0           $const = "chr(".ord("'").")"
56             }
57             else {
58 0           $const = "'$_[0]'"
59             }
60 0           return $const;
61             }
62              
63             sub call_constant {
64 0 0   0 0   return " 1 # null constant\n"
65             unless length($_[0]);
66 0           my $const = quote_constant( $_[0] );
67 0           my $len = length( eval $const );
68             #print "Const: [$_[0]] $const $len \n";
69             return
70 0           "
71             $_[1] ##
72             $_[1] ## pos: @$RegexPos
73             $_[1] ( ( substr( \$s, \$pos, $len ) eq $const )
74             $_[1] ? ( \$pos $direction= $len or 1 )
75             $_[1] : 0
76             $_[1] )
77             $_[1] ## \n";
78             }
79              
80             sub call_perl5 {
81 0     0 0   my $const = $_[0];
82 0 0         $_[1] = ' ' unless defined $_[1];
83             #print "CONST: $const - $direction \n";
84             return
85 0           "$_[1] ##
86             $_[1] ( ( substr( \$s, \$pos ) =~ m/^($const)/ )
87             $_[1] ? ( \$pos $direction= length( \$1 ) or 1 )
88             $_[1] : 0
89             $_[1] )
90             $_[1] ## \n";
91             }
92              
93             sub emit {
94 0     0 0   my ($grammar, $ast, $param) = @_;
95             # runtime parameters: $grammar, $string, $state, $arg_list
96             # rule parameters: see Runtime::Rule.pm
97 0 0         local $sigspace = $param->{sigspace} ? 1 : 0; # XXX - $sigspace should be lexical
98             ### ratchet emit sigspace: $sigspace
99 0           local $capture_count = -1;
100 0           local $capture_to_array = 0;
101             #print "rule: ", Dumper( $ast );
102             return
103 0           "##
104             ## sigspace: $sigspace
105             ## ratchet: 1
106             do { my \$rule; \$rule = sub {
107             my \$grammar = \$_[0];
108             my \$s = \$_[1];
109             \$_[3] = \$_[2] unless defined \$_[3]; # backwards compat
110             no warnings 'substr', 'uninitialized', 'syntax';
111             my \%pad;\n" .
112             #" my \$pos;\n" .
113             #" print \"match arg_list = \$_[1]\n\";\n" .
114             #" print 'match ', Dumper(\\\@_);\n" .
115             #" print \"match arg_list = \@{[\%{\$_[1]} ]}\n\" if defined \$_[1];\n" .
116             #" warn \"match pos = \", pos(\$_[1]), \"\\n\";\n" .
117             " my \$m;
118             my \$bool;
119             my \@pos;
120             # XXX :pos(X) takes the precedence over :continue ?
121             if (defined \$_[3]{p}) {
122             push \@pos, \$_[3]{p} || 0;
123             } elsif (\$_[3]{continue}) {
124             push \@pos, (pos(\$_[1]) || 0) .. length(\$s);
125             } else {
126             push \@pos, 0..length(\$s);
127             }
128             for my \$pos ( \@pos ) {
129             my \%index;
130             my \@match;
131             my \%named;
132             \$bool = 1;
133             \$named{KEY} = \$_[3]{KEY} if exists \$_[3]{KEY};
134             \$m = Pugs::Runtime::Match->new( {
135             str => \\\$s, from => \\(0+\$pos), to => \\(\$pos),
136             bool => \\\$bool, match => \\\@match, named => \\\%named, capture => undef,
137             } );
138             {
139             my \$prior = \$::_V6_PRIOR_;
140             local \$::_V6_PRIOR_ = \$prior;
141             \$bool = 0 unless
142             " .
143             #" do { TAILCALL: ;\n" .
144             emit_rule( $ast, ' ' ) . ";
145             }
146             if ( \$bool ) {
147             my \$prior = \$::_V6_PRIOR_;
148             \$::_V6_PRIOR_ = sub {
149             local \$main::_V6_PRIOR_ = \$prior;
150             \$rule->(\@_);
151             };
152             #warn \"pos2 = \", \$pos, \"\\n\";
153             pos(\$_[1]) = \$pos if \$_[3]{continue};
154             last;
155             }
156             } # /for
157             \$::_V6_MATCH_ = \$m;
158             return \$m;
159             } }
160             ## \n";
161             }
162              
163             sub emit_rule {
164 0     0 0   my $n = $_[0];
165 0           my $tab = $_[1] . ' ';
166 0 0         die "unknown node: ", Dumper( $n )
167             unless ref( $n ) eq 'HASH';
168             #print "NODE ", Dumper($n);
169 0           my @keys = grep { substr($_, 0, 1) ne '_' } keys %$n;
  0            
170             ### Node keys: @keys
171 0           my ($k) = @keys;
172 0           my $v = $n->{$k};
173 0           local $RegexPos = $n->{_pos};
174             ### $RegexPos
175 0 0         if (!defined $RegexPos) {
176             # warn "WARNING: No _pos slot found for AST node '$k'.\n";
177             # warn Dumper($n);
178 0           $RegexPos = [];
179             }
180             # XXX - use real references
181 18     18   136 no strict 'refs';
  18         39  
  18         151066  
182             #print "NODE ", Dumper($k), ", ", Dumper($v);
183 0           my $code = $k->( $v, $tab );
184 0           return $code;
185             }
186              
187             #rule nodes
188              
189             sub non_capturing_group {
190 0     0 0   return emit_rule( $_[0], $_[1] );
191             }
192             sub quant {
193 0     0 0   my $term = $_[0]->{'term'};
194 0   0       my $quantifier = $_[0]->{quant} || '';
195 0   0       my $greedy = $_[0]->{greedy} || ''; # + ?
196 0 0         die "greediness control not implemented: $greedy"
197             if $greedy;
198             #print "QUANT: ",Dumper($_[0]);
199 0           my $id = id();
200 0 0         my $tab = ( $quantifier eq '' ) ? $_[1] : $_[1] . " ";
201 0           my $ws = metasyntax( { metasyntax => 'ws', modifier => '.' }, $tab );
202 0 0 0       my $ws3 = ( $sigspace && $_[0]->{ws3} ne '' ) ? " &&\n$ws" : '';
203              
204 0           my $rul;
205             {
206             #print "Term: ", Dumper($term), "\n";
207 0           my $cap = $capture_to_array;
  0            
208 0   0       local $capture_to_array = $cap || ( $quantifier ne '' );
209 0           $rul = emit_rule( $term, $tab );
210              
211             # rollback on fail
212 0           $rul = "$_[1] ( "
213             . " ( \$pad{$id} = \$pos or 1 ) &&\n"
214             . $rul
215             . " ||"
216             . " ( ( \$pos = \$pad{$id} ) && 0 )"
217             . " )";
218             }
219              
220 0 0 0       $rul = "$ws &&\n$rul" if $sigspace && $_[0]->{ws1} ne '';
221 0 0 0       $rul = "$rul &&\n$ws" if $sigspace && $_[0]->{ws2} ne '';
222             #print $rul;
223 0 0         return "
224             $_[1] ##
225             $_[1] ## pos: @$RegexPos
226             " . $rul . "
227             $_[1] ## \n"
228             if $quantifier eq '';
229             # * + ?
230             # TODO: *? +? ??
231             # TODO: *+ ++ ?+
232             # TODO: quantifier + capture creates Array
233             #warn Dumper( $quantifier );
234 0 0         if ( ref( $quantifier ) eq 'HASH' )
235             {
236 0           my $code = $quantifier->{closure};
237 0 0         if ( ref( $code ) ) {
238 0 0         if ( defined $Pugs::Compiler::Perl6::VERSION ) {
239             #print " perl6 compiler is loaded \n";
240 0           $code = Pugs::Emitter::Perl6::Perl5::emit( 'grammar', $code, 'self' );
241             }
242             };
243 0           my @count = eval $code;
244             #warn "code: $code = [ @count ]";
245              
246 0 0 0       die "quantifier not implemented: " . Dumper( $quantifier )
247             if @count ne 1
248             || $count[0] == 0;
249              
250             return
251 0           "$_[1] ## \n" .
252             "$_[1] ## pos: @$RegexPos\n" .
253             "$_[1] (\n" .
254             join( ' && ', ($rul) x $count[0] ) .
255             "\n" .
256             "$_[1] )$ws3\n" .
257             "$_[1] ## \n";
258             }
259             return
260 0 0         "$_[1] ## \n" .
261             "$_[1] ## pos: @$RegexPos\n" .
262             "$_[1] (\n$rul\n" .
263             "$_[1] || ( \$bool = 1 )\n" .
264             "$_[1] )$ws3\n" .
265             "$_[1] ## \n"
266             if $quantifier eq '?';
267             return
268 0 0         "$_[1] ## \n" .
269             "$_[1] ## pos: @$RegexPos\n" .
270             "$_[1] do { while (\n$rul) {}; \$bool = 1 }$ws3\n" .
271             "$_[1] ## \n"
272             if $quantifier eq '*';
273             return
274 0 0         "$_[1] ## \n" .
275             "$_[1] ## pos: @$RegexPos\n" .
276             "$_[1] (\n$rul\n" .
277             "$_[1] && do { while (\n$rul) {}; \$bool = 1 }\n" .
278             "$_[1] )$ws3\n" .
279             "$_[1] ## \n"
280             if $quantifier eq '+';
281 0           die "quantifier not implemented: $quantifier";
282             }
283              
284             sub alt {
285 0     0 0   my @s;
286             # print 'Alt: ';
287 0           my $count = $capture_count;
288 0           my $max = -1;
289 0           my $id = id();
290 0           for ( @{$_[0]} ) {
  0            
291 0           $capture_count = $count;
292 0           my $tmp = emit_rule( $_, $_[1].' ' );
293             # print ' ',$capture_count;
294 0 0         $max = $capture_count
295             if $capture_count > $max;
296 0 0         push @s, $tmp if $tmp;
297             }
298 0           $capture_count = $max;
299             # print " max = $capture_count\n";
300             return
301 0           "$_[1] ##
302             $_[1] ## pos: @$RegexPos
303             $_[1] (
304             $_[1] ( \$pad{$id} = \$pos or 1 )
305             $_[1] && (
306             " . join( "
307             $_[1] )
308             $_[1] || (
309             $_[1] ( ( \$bool = 1 ) && ( \$pos = \$pad{$id} ) or 1 )
310             $_[1] && ",
311             @s
312             ) . "
313             $_[1] )
314             $_[1] )
315             $_[1] ## \n";
316             }
317 0     0 0   sub alt1 { &alt }
318             sub conjunctive {
319 0     0 0   my @s;
320             # print 'conjunctive: ';
321 0           my $count = $capture_count;
322 0           my $max = -1;
323 0           my $id = id();
324 0           for ( @{$_[0]} ) {
  0            
325 0           $capture_count = $count;
326 0           my $tmp = emit_rule( $_, $_[1].' ' );
327             # print ' ',$capture_count;
328 0 0         $max = $capture_count
329             if $capture_count > $max;
330 0 0         push @s, $tmp if $tmp;
331             }
332 0           $capture_count = $max;
333             # print " max = $capture_count\n";
334             return
335 0           "$_[1] ##
336             $_[1] ## pos: @$RegexPos
337             $_[1] (
338             $_[1] ( \$pad{$id} = \$pos or 1 )
339             $_[1] && (
340             " . join( "
341             $_[1] )
342             $_[1] && (
343             $_[1] ( ( \$bool = 1 ) && ( \$pos = \$pad{$id} ) or 1 )
344             $_[1] && ",
345             @s
346             ) . "
347             $_[1] )
348             $_[1] )
349             $_[1] ## \n";
350             }
351 0     0 0   sub conjunctive1 { &conjunctive }
352             sub concat {
353 0     0 0   my @s;
354              
355             =for optimizing
356             # optimize for the common case of "words"
357             # Note: this optimization has almost no practical effect
358             my $is_constant = 0;
359             for ( @{$_[0]} ) {
360             if ( ! $sigspace && exists $_->{quant} ) {
361             my $was_constant = $is_constant;
362             $is_constant =
363             $_->{quant}->{quant} eq ''
364             && exists $_->{quant}->{term}->{constant};
365             #print "concat: ", Dumper( $_ );
366             if ( $is_constant && $was_constant && $direction ne '-' ) {
367             $s[-1]->{quant}->{term}->{constant} .=
368             $_->{quant}->{term}->{constant};
369             #print "constant: ",$s[-1]->{quant}->{term}->{constant},"\n";
370             next;
371             }
372             }
373             push @s, $_;
374             }
375              
376             for ( @s ) {
377             $_ = emit_rule( $_, $_[1] );
378             }
379             =cut
380              
381             # Try to remove non-greedy quantifiers, by inserting a lookahead;
382             # cheat: / .*? b /
383             # into: / [ . ]* b /
384             # TODO - make it work for '+' quantifier too
385 0           for my $i ( 0 .. @{$_[0]} - 1 ) {
  0            
386 0 0 0       if ( exists $_[0][$i]{quant}
      0        
387             && $_[0][$i]{quant}{quant} eq '*'
388             && $_[0][$i]{quant}{greedy} eq '?'
389             ) {
390 0           my $tmp = { quant => {
391 0           %{ $_[0][$i]{quant} },
392             greedy => '', quant => ''
393             },
394             _pos => $_[0][$i]{_pos}
395             };
396 0           $_[0][$i] = {
397             _pos => $_[0][$i]{_pos},
398             quant => {
399             greedy => '',
400             quant => $_[0][$i]{quant}{quant},
401             ws1 => '',
402             ws2 => '',
403             ws3 => '',
404             term => {
405             _pos => $_[0][$i]{_pos},
406             concat => [
407             {
408             _pos => $_[0][$i]{_pos},
409             before => {
410             rule => {
411             _pos => $_[0][$i]{_pos},
412             concat => [
413 0           @{ $_[0] }[$i+1 .. $#{ $_[0] } ]
  0            
414             ],
415             },
416             modifier => '!',
417             }
418             },
419             $tmp,
420             ],
421             },
422             },
423             };
424             #warn "Quant: ",Dumper($_[0]);
425             }
426             }
427              
428 0           for ( @{$_[0]} ) {
  0            
429 0           my $tmp = emit_rule( $_, $_[1] );
430 0 0         push @s, $tmp if $tmp;
431             }
432 0 0         @s = reverse @s if $direction eq '-';
433             return
434 0           "$_[1] ##
435             $_[1] ## pos: @$RegexPos
436             $_[1] (\n" . join( "\n$_[1] &&\n", @s ) . "
437             $_[1] )
438             $_[1] ## \n";
439             }
440              
441             sub code {
442 0     0 0   return "$_[1] $_[0]\n";
443             }
444              
445             sub dot {
446 0     0 0   "
447             $_[1] ##
448             $_[1] ## pos: @$RegexPos
449             $_[1] ( substr( \$s, \$pos$direction$direction, 1 ) ne '' )
450             $_[1] ## \n"
451             }
452              
453             sub variable {
454 0     0 0   my $name = "$_[0]";
455 0           my $value = undef;
456             # XXX - eval $name doesn't look up in user lexical pad
457             # XXX - what &xxx interpolate to?
458              
459             #print "VAR: $name \n";
460             # expand embedded $scalar
461 0 0         if ( $name =~ /^\$/ ) {
462             # $^a, $^b
463 0 0         if ( $name =~ /^ \$ \^ ([^\s]*) /x ) {
464 0           my $index = ord($1)-ord('a');
465             #print "Variable #$index\n";
466             #return "$_[1] constant( \$_[7][$index] )\n";
467              
468 0           my $code =
469             " ... sub {
470             #print \"Runtime Variable args[\", join(\",\",\@_) ,\"] \$_[7][$index]\\n\";
471             return constant( \$_[7][$index] )->(\@_);
472             }";
473 0           $code =~ s/^/$_[1]/mg;
474 0           return "$code\n";
475             }
476              
477 0           $value = eval $name;
478             }
479              
480             # expand embedded @arrays
481 0 0         if ( $name =~ /^\@/ ) {
482 0           my $code = q!
483             join(
484             '|',
485             ! . $name . q!
486             )
487             !;
488             return
489 0           "$_[1] ##
490             $_[1] ## pos: @$RegexPos
491             $_[1] ( eval( '( substr( \$s, \$pos ) =~ m/^(' . $code . ')/ )
492             $_[1] ? ( \$pos $direction= length( \$1 ) or 1 )
493             $_[1] : 0
494             $_[1] ') )
495             $_[1] ## \n";
496             }
497              
498             # expand embedded %hash
499 0 0         if ( $name =~ /^%/ ) {
500 0           my $id = '$' . id();
501 0           my $preprocess_hash = 'Pugs::Runtime::Regex::preprocess_hash';
502 0           my $code =
503             "
504             ##
505             ## pos: @$RegexPos
506             do {
507             our $id;
508             our ${id}_sizes;
509             unless ( $id ) {
510             my \$hash = \\$name;
511             my \%sizes = map { length(\$_) => 1 } keys \%\$hash;
512             ${id}_sizes = [ sort { \$b <=> \$a } keys \%sizes ];
513             " . #print \"sizes: \@${id}_sizes\\n\";
514             "$id = \$hash;
515             }
516             " . #print 'keys: ',Dumper( $id );
517             "my \$match = 0;
518             my \$key;
519             for ( \@". $id ."_sizes ) {
520             \$key = ( \$pos <= length( \$s )
521             ? substr( \$s, \$pos, \$_ )
522             : '' );
523             " . #print \"try ".$name." \$_ = \$key; \$s\\\n\";
524             "if ( exists ". $id ."->{\$key} ) {
525             #\$named{KEY} = \$key;
526             #\$::_V6_MATCH_ = \$m;
527             #print \"m: \", Dumper( \$::_V6_MATCH_->data )
528             # if ( \$key eq 'until' );
529             " . #print \"* ".$name."\{'\$key\'} at \$pos \\\n\";
530             "\$match = $preprocess_hash( $id, \$key )->( \$s, \$grammar, { p => ( \$pos + \$_ ), positionals => [ ], args => { KEY => \$key } }, undef );
531             " . #print \"match: \", Dumper( \$match->data );
532             "last if \$match;
533             }
534             }
535             if ( \$match ) {
536             \$pos = \$match->to;
537             #print \"match: \$key at \$pos = \", Dumper( \$match->data );
538             \$bool = 1;
539             }; # else { \$bool = 0 }
540             \$match;
541             }
542             ##
543             ";
544             #print $code;
545 0           return $code;
546             }
547 0 0         die "interpolation of $name not implemented"
548             unless defined $value;
549              
550 0           return call_constant( $value, $_[1] );
551             }
552             sub special_char {
553 0     0 0   my ($char, $data) = $_[0] =~ /^.(.)(.*)/;
554              
555 0 0         return call_perl5( '\\N{$data}', $_[1] )
556             if $char eq 'c';
557 0 0         return call_perl5( '(?!\\N{$data}).', $_[1] )
558             if $char eq 'C';
559              
560 0 0         return call_perl5( '\\x{'.$data.'}', $_[1] )
561             if $char eq 'x';
562 0 0         return call_perl5( '(?!\\x{'.$data.'}).', $_[1] )
563             if $char eq 'X';
564              
565 0 0         return special_char( sprintf("\\x%X", oct($data) ) )
566             if $char eq 'o';
567 0 0         return special_char( sprintf("\\X%X", oct($data) ) )
568             if $char eq 'O';
569              
570 0 0         return call_perl5( '(?:\n\r?|\r\n?)', $_[1] )
571             if $char eq 'n';
572 0 0         return call_perl5( '(?!\n\r?|\r\n?).', $_[1] )
573             if $char eq 'N';
574              
575             # XXX - Infinite loop in pugs stdrules.t
576             #return metasyntax( '?_horizontal_ws', $_[1] )
577 0 0         return call_perl5( '[\x20\x09]' )
578             if $char eq 'h';
579 0 0         return call_perl5( '[^\x20\x09]' )
580             if $char eq 'H';
581             #return metasyntax( '?_vertical_ws', $_[1] )
582 0 0         return call_perl5( '[\x0A\x0D]' )
583             if $char eq 'v';
584 0 0         return call_perl5( '[^\x0A\x0D]' )
585             if $char eq 'V';
586              
587 0           for ( qw( r n t e f w d s ) ) {
588 0 0         return call_perl5( "\\$_", $_[1] ) if $char eq $_;
589 0 0         return call_perl5( "[^\\$_]", $_[1] ) if $char eq uc($_);
590             }
591 0 0         $char = '\\\\' if $char eq '\\';
592             ### special char: $char
593 0           return call_constant( $char, $_[1] );
594             }
595              
596             sub match_variable {
597 0     0 0   my $name = $_[0];
598 0           my $num = substr($name,1);
599             #print "var name: ", $num, "\n";
600              
601             return
602 0           "
603             $_[1] ##
604             $_[1] ## pos: @$RegexPos
605             $_[1] ( eval( '( substr( \$s, \$pos ) =~ m/^(' . \$m->{$num} . ')/ )
606             $_[1] ? ( \$pos $direction= length( \$1 ) or 1 )
607             $_[1] : 0
608             $_[1] ') )
609             $_[1] ##
610             ";
611             }
612              
613             sub closure {
614             #print "closure: ",Dumper($_[0]);
615 0     0 0   my $code = $_[0]{closure};
616 0           my $modifier = $_[0]{modifier}; # 'plain', '', '?', '!'
617              
618 0 0         die "invalid closure modifier: . "
619             if $modifier eq '.';
620              
621             #die "closure modifier not implemented '$modifier'"
622             # unless $modifier eq 'plain';
623              
624 0 0 0       if ( ref( $code )
625             && defined $Pugs::Compiler::Perl6::VERSION
626             ) {
627             #print " perl6 compiler is loaded \n";
628 0           $code = Pugs::Emitter::Perl6::Perl5::emit( 'grammar', $code, 'self' );
629 0           $code = '{ my $_V6_SELF = shift; ' . $code . '}'; # make it a "method"
630             }
631             else {
632             #print " perl6 compiler is NOT loaded \n";
633             # XXX XXX XXX - source-filter - temporary hacks to translate p6 to p5
634             # $()
635 0           $code =~ s/ ([^']) \$ \$ (\d+) /$1\${ \$_[0]->[$2] }/sgx;
636 0           $code =~ s/ ([^']) \$ (\d+) /$1\$_[0]->[$2]/sgx;
637 0           $code =~ s/ ([^']) \$ \( \) < (.*?) > /$1\$_[0]->{$2}/sgx;
638             # $
639 0           $code =~ s/ ([^']) \$ \$ < (.*?) > /$1\${ \$_[0]->{qw($2)} }/sgx;
640 0           $code =~ s/ ([^']) \$ < (.*?) > /$1\$_[0]->{qw($2)}/sgx;
641             # $()
642 0           $code =~ s/ ([^']) \$ \( \) /$1\$_[0]->()/sgx;
643             # $/
644 0           $code =~ s/ ([^']) \$ \/ ([\{\[]) /$1\$_[0]->$2/sgx;
645 0           $code =~ s/ ([^']) \$ \/ /$1\$_[0]/sgx;
646             #$code =~ s/ use \s+ v6 \s* ; / # use v6\n/sgx;
647             }
648             #print "Code: $code\n";
649             # "plain" {...return ...}
650             return
651 0 0         "$_[1] ## \n"
652             . "$_[1] ## pos: @$RegexPos\n"
653             . "$_[1] do {\n"
654             . "$_[1] local \$::_V6_SUCCEED = 1;\n"
655             . "$_[1] \$::_V6_MATCH_ = \$m;\n"
656             . "$_[1] \$m->data->{capture} = \\( sub $code->( \$m ) ); \n"
657             . "$_[1] \$bool = \$::_V6_SUCCEED;\n"
658             . "$_[1] \$::_V6_MATCH_ = \$m if \$bool; \n"
659             . "$_[1] return \$m if \$bool; \n"
660             . "$_[1] }\n"
661             . "$_[1] ## \n"
662             if $code =~ /return/;
663              
664             # "plain" {...} without return
665             return
666 0 0         "$_[1] ## \n"
667             . "$_[1] ## pos: @$RegexPos\n"
668             . "$_[1] do { \n"
669             . "$_[1] local \$::_V6_SUCCEED = 1;\n"
670             . "$_[1] \$::_V6_MATCH_ = \$m;\n"
671             . "$_[1] sub $code->( \$m );\n"
672             . "$_[1] 1;\n"
673             . "$_[1] }\n"
674             . "$_[1] ## \n"
675             if $modifier eq 'plain';
676             # "?"
677             return
678 0 0         "$_[1] ## \n" .
679             "$_[1] ## pos: @$RegexPos\n" .
680             "$_[1] do { \n" .
681             "$_[1] local \$::_V6_SUCCEED = 1;\n" .
682             "$_[1] \$::_V6_MATCH_ = \$m;\n" .
683             "$_[1] \$bool = ( sub $code->( \$m ) ) ? 1 : 0; \n" .
684             "$_[1] }" .
685             "$_[1] ## \n"
686             if $modifier eq '?';
687             # "!"
688             return
689 0 0         "$_[1] ## \n" .
690             "$_[1] ## pos: @$RegexPos\n" .
691             "$_[1] do { \n" .
692             "$_[1] local \$::_V6_SUCCEED = 1;\n" .
693             "$_[1] \$::_V6_MATCH_ = \$m;\n" .
694             "$_[1] \$bool = ( sub $code->( \$m ) ) ? 0 : 1; \n" .
695             "$_[1] }" .
696             "$_[1] ## \n"
697             if $modifier eq '!';
698              
699             }
700             sub capturing_group {
701 0     0 0   my $program = $_[0];
702              
703 0           $capture_count++;
704             {
705 0           local $capture_count = -1;
  0            
706 0           local $capture_to_array = 0;
707 0 0         $program = emit_rule( $program, $_[1].' ' )
708             if ref( $program );
709             }
710              
711 0 0         return "
712             $_[1] ##
713             $_[1] do{
714             $_[1] my \$hash = do {
715             $_[1] my \$bool = 1;
716             $_[1] my \$from = \$pos;
717             $_[1] my \@match;
718             $_[1] my \%named;
719             $_[1] \$bool = 0 unless
720             " . $program . ";
721             $_[1] { str => \\\$s, from => \\\$from, match => \\\@match, named => \\\%named, bool => \\\$bool, to => \\(0+\$pos), capture => undef }
722             $_[1] };
723             $_[1] my \$bool = \${\$hash->{'bool'}};" .
724             ( $capture_to_array
725             ? "
726             $_[1] if ( \$bool ) {
727             $_[1] push \@{ \$match[ $capture_count ] }, Pugs::Runtime::Match->new( \$hash );
728             $_[1] }"
729             : "
730             $_[1] \$match[ $capture_count ] = Pugs::Runtime::Match->new( \$hash );"
731             ) . "
732             $_[1] \$bool;
733             $_[1] }
734             $_[1] ## \n";
735             }
736              
737             sub capture_as_result {
738 0     0 0   my $program = $_[0];
739              
740 0           $capture_count++;
741             {
742 0           local $capture_count = -1;
  0            
743 0           local $capture_to_array = 0;
744 0 0         $program = emit_rule( $program, $_[1].' ' )
745             if ref( $program );
746             }
747 0           return "$_[1] ##
748             $_[1] ## pos: @$RegexPos
749             $_[1] do{
750             $_[1] my \$hash = do {
751             $_[1] my \$bool = 1;
752             $_[1] my \$from = \$pos;
753             $_[1] my \@match;
754             $_[1] my \%named;
755             $_[1] \$bool = 0 unless
756             " . $program . ";
757             $_[1] { str => \\\$s, from => \\\$from, match => \\\@match, named => \\\%named, bool => \\\$bool, to => \\(0+\$pos), capture => undef }
758             $_[1] };
759             $_[1] my \$bool = \${\$hash->{'bool'}};
760             $_[1] \$m->data->{capture} = \\( \"\" . Pugs::Runtime::Match->new( \$hash ) );
761             $_[1] \$bool;
762             $_[1] }
763             $_[1] ## \n";
764             }
765             sub named_capture {
766 0     0 0   my $name = $_[0]{ident};
767             ### $name
768 0 0         if (ref($name) eq 'HASH') {
769 0   0       $name = $name->{match_variable} || $name->{variable};
770             }
771 0           $name =~ s/^[\$\@\%]//; # TODO - change semantics as needed
772 0           my $program = $_[0]{rule};
773             #warn "name [$name]\n";
774              
775 0 0         if ( exists $program->{metasyntax} ) {
    0          
776             #print "aliased subrule\n";
777             # $/ = $/
778              
779 0           my $cmd = $program->{metasyntax}{metasyntax};
780 0 0         die "invalid aliased subrule"
781             unless $cmd =~ /^[_[:alnum:]]/;
782              
783             #
784 0           my ( $subrule, $param_list ) = split( /[\(\)]/, $cmd );
785 0 0         $param_list = '' unless defined $param_list;
786 0           my @param = split( ',', $param_list );
787 0 0         return "$_[1] ##
788             $_[1] ## pos: @$RegexPos
789             $_[1] do {
790             my \$prior = \$::_V6_PRIOR_;
791             my \$match =\n" .
792             call_subrule( $subrule, $_[1]." ", "", @param ) . ";
793             \$::_V6_PRIOR_ = \$prior;
794             if ( \$match ) {" .
795             ( $capture_to_array
796             ? " push \@{\$named{'$name'}}, \$match;"
797             : " \$named{'$name'} = \$match;"
798             ) . "
799             \$pos = \$match->to;
800             1
801             }
802             else { 0 }
803             }
804             $_[1] ## \n";
805             }
806             elsif ( exists $program->{capturing_group} ) {
807             #print "aliased capturing_group\n";
808             # $/ = $/[0]
809             {
810 0           local $capture_count = -1;
  0            
811 0           local $capture_to_array = 0;
812 0 0         $program = emit_rule( $program, $_[1].' ' )
813             if ref( $program );
814             }
815 0 0         return "$_[1] ##
816             $_[1] ## pos: @$RegexPos
817             $_[1] do{
818             my \$match = Pugs::Runtime::Match->new( do {
819             my \$bool = 1;
820             my \$from = \$pos;
821             my \@match;
822             my \%named;
823             \$bool = 0 unless " .
824             $program . ";
825             { str => \\\$s, from => \\\$from, match => \\\@match, named => \\\%named, bool => \\\$bool, to => \\(0+\$pos), capture => undef }
826             } );
827             if ( \$match ) {" .
828             ( $capture_to_array
829             ? " push \@{\$named{'$name'}}, \$match;"
830             : " \$named{'$name'} = \$match;"
831             ) . "
832             \$pos = \$match->to;
833             1
834             }
835             else { 0 }
836             }
837             $_[1] ## \n";
838             }
839             else {
840             #print "aliased non_capturing_group\n";
841             # $/ = "$/"
842             #print Dumper( $_[0] );
843 0           $program = emit_rule( $program, $_[1].' ' );
844 0 0         return "$_[1] ##
845             $_[1] ## pos: @$RegexPos
846             $_[1] do{
847             my \$from = \$pos;
848             my \$bool = $program;
849             my \$match = Pugs::Runtime::Match->new(
850             { str => \\\$s, from => \\\$from, match => [], named => {}, bool => \\1, to => \\(0+\$pos), capture => undef }
851             );" .
852             ( $capture_to_array
853             ? " push \@{\$named{'$name'}}, \$match;"
854             : " \$named{'$name'} = \$match;"
855             ) . "
856             \$bool
857             }
858             $_[1] ## \n";
859             }
860             }
861             sub negate {
862 0     0 0   my $program = $_[0];
863             #print "Negate: ", Dumper($_[0]);
864 0 0         $program = emit_rule( $program, $_[1].' ' )
865             if ref( $program );
866 0           return "$_[1] ##
867             $_[1] ## pos: @$RegexPos
868             $_[1] do{
869             $_[1] my \$pos1 = \$pos;
870             $_[1] do {
871             $_[1] my \$pos = \$pos1;
872             $_[1] my \$from = \$pos;
873             $_[1] my \@match;
874             $_[1] my \%named;
875             $_[1] \$bool = " . $program . " ? 0 : 1;
876             $_[1] \$bool;
877             $_[1] };
878             $_[1] }
879             $_[1] ## \n";
880             }
881              
882             sub before {
883 0   0 0 0   my $mod = delete $_[0]{modifier} || '';
884             #### before atom: $_[0]
885 0 0         return negate( { before => $_[0], _pos => $_[0]{rule}{_pos}, }, $_[1] ) if $mod eq '!';
886 0           my $program = $_[0]{rule};
887 0 0         $program = emit_rule( $program, $_[1].' ' )
888             if ref( $program );
889 0           return "
890             $_[1] ##
891             $_[1] ## pos: @$RegexPos
892             $_[1] do{
893             $_[1] my \$pos1 = \$pos;
894             $_[1] do {
895             $_[1] my \$pos = \$pos1;
896             $_[1] my \$from = \$pos;
897             $_[1] my \@match;
898             $_[1] my \%named;
899             $_[1] \$bool = 0 unless
900             " . $program . ";
901             $_[1] \$bool;
902             $_[1] };
903             $_[1] }
904             $_[1] ## \n";
905             }
906              
907             sub after {
908 0     0 0   my $mod = delete $_[0]{modifier};
909 0 0         return negate( { after => $_[0] }, $_[1] ) if $mod eq '!';
910 0           local $direction = "-";
911 0           my $program = $_[0]{rule};
912 0 0         $program = emit_rule( $program, $_[1].' ' )
913             if ref( $program );
914 0           return "$_[1] ##
915             $_[1] ## pos: @$RegexPos
916             $_[1] do{
917             $_[1] my \$pos1 = \$pos;
918             $_[1] do {
919             $_[1] my \$pos = \$pos1 - 1;
920             $_[1] my \$from = \$pos;
921             $_[1] my \@match;
922             $_[1] my \%named;
923             $_[1] \$bool = 0 unless
924             " . $program . ";
925             $_[1] \$bool;
926             $_[1] };
927             $_[1] }
928             $_[1] ## \n";
929             }
930              
931             sub colon {
932 0     0 0   my $str = $_[0];
933 0 0         return "$_[1] 1 # : no-op\n"
934             if $str eq ':';
935 0 0         return "$_[1] ( \$pos >= length( \$s ) )\n"
936             if $str eq '$';
937 0 0         return "$_[1] ( \$pos == 0 )\n"
938             if $str eq '^';
939              
940 0 0         return "$_[1] ( \$pos >= length( \$s ) || substr( \$s, \$pos ) =~ ".'/^(?:\n\r?|\r\n?)/m'." )\n"
941             if $str eq '$$';
942 0 0         return "$_[1] ( \$pos == 0 || substr( \$s, 0, \$pos ) =~ ".'/(?:\n\r?|\r\n?)$/m'." )\n"
943             if $str eq '^^';
944              
945 0 0         return metasyntax( { metasyntax => '_wb_left', modifier => '?' }, $_[1] )
946             if $str eq '<<';
947 0 0         return metasyntax( { metasyntax => '_wb_right', modifier => '?' }, $_[1] )
948             if $str eq '>>';
949              
950 0           die "'$str' not implemented";
951             }
952             sub modifier {
953 0     0 0   my $str = $_[0];
954 0           die "modifier '$str' not implemented";
955             }
956             sub constant {
957 0     0 0   call_constant( @_ );
958             }
959              
960             sub char_class {
961 0     0 0   my $cmd = Pugs::Emitter::Rule::Perl5::CharClass::emit( $_[0] );
962 0           return call_perl5($cmd, $_[1]);
963             }
964              
965             sub call {
966             #die "not implemented: ", Dumper(\@_);
967 0     0 0   my $param = $_[0]{params};
968 0           my $name = $_[0]{method};
969             # capturing subrule
970             #
971 0           my ($param_list) = $param =~ /\{(.*)\}/;
972 0 0         $param_list = '' unless defined $param_list;
973 0           my @param = split( ',', $param_list );
974             #print "param: ", Dumper(\@param);
975              
976             # TODO
977              
978 0 0         if ( $name eq 'at' ) {
979 0   0       $param_list ||= 0; # XXX compile-time only
980 0           return "$_[1] ( \$pos == $param_list )\n"
981             }
982              
983 0           return named_capture(
984             {
985             ident => $name,
986             rule => { metasyntax => { metasyntax => $name }, _pos => $_[0]{_pos}, },
987             },
988             $_[1],
989             );
990             }
991              
992             sub metasyntax {
993             #
994             #print Dumper(\@_);
995 0     0 0   my $cmd = $_[0]{metasyntax};
996 0   0       my $modifier = delete $_[0]{modifier} || ''; # . ? !
997 0 0         return negate( { metasyntax => $_[0], _pos => $_[0]{_pos} }, $_[1] ) if $modifier eq '!';
998              
999 0           my $prefix = substr( $cmd, 0, 1 );
1000 0 0         if ( $prefix eq '@' ) {
1001             # XXX - wrap @array items - see end of Pugs::Grammar::Rule
1002             # TODO - param list
1003 0           my $name = substr( $cmd, 1 );
1004             return
1005 0 0         "$_[1] ##
1006             $_[1] ## pos: @$RegexPos
1007             $_[1] do {
1008             my \$match;
1009             for my \$subrule ( $cmd ) {
1010             \$match = \$subrule->match( \$s, \$grammar, { p => ( \$pos ), positionals => [ ], args => {} }, undef );
1011             last if \$match;
1012             }
1013             if ( \$match ) {" .
1014             ( $capture_to_array
1015             ? " push \@{\$named{'$name'}}, \$match;"
1016             : " \$named{'$name'} = \$match;"
1017             ) . "
1018             \$pos = \$match->to;
1019             1
1020             }
1021             else { 0 }
1022             }
1023             $_[1] ## \n";
1024             }
1025              
1026 0 0         if ( $prefix eq '%' ) {
1027             # XXX - runtime or compile-time interpolation?
1028 0           my $name = substr( $cmd, 1 );
1029             # print "<$cmd>\n";
1030             # return variable( $cmd );
1031 0 0         return "$_[1]##
1032             $_[1] ## pos: @$RegexPos
1033             $_[1] do{
1034             my \$match = " . variable( $cmd, $_[1] ) . ";
1035             if ( \$match ) {" .
1036             ( $capture_to_array
1037             ? " push \@{\$named{'$name'}}, \$match;"
1038             : " \$named{'$name'} = \$match;"
1039             ) . "
1040             \$pos = \$match->to;
1041             1
1042             }
1043             else { 0 }
1044             }\n$_[1]## \n";
1045             }
1046              
1047 0 0         if ( $prefix eq '$' ) {
1048 0 0         if ( $cmd =~ /::/ ) {
1049             # call method in fully qualified $package::var
1050             # ...->match( $rule, $str, $grammar, $flags, $state )
1051             # TODO - send $pos to subrule
1052             return
1053 0           "$_[1] ## \n" .
1054             "$_[1] ## pos: @$RegexPos\n" .
1055             "$_[1] do {\n" .
1056             "$_[1] push \@match,\n" .
1057             "$_[1] $cmd->match( \$s, \$grammar, {p => \$pos}, undef );\n" .
1058             "$_[1] \$pos = \$match[-1]->to;\n" .
1059             "$_[1] !\$match[-1] != 1;\n" .
1060             "$_[1] }\n" .
1061             "$_[1] ## \n";
1062             }
1063             # call method in lexical $var
1064             # TODO - send $pos to subrule
1065             return
1066 0           "$_[1] ## \n" .
1067             "$_[1] ## pos: @$RegexPos\n" .
1068             "$_[1] do {\n" .
1069             "$_[1] my \$r = Pugs::Runtime::Regex::get_variable( '$cmd' );\n" .
1070             "$_[1] push \@match,\n" .
1071             "$_[1] \$r->match( \$s, \$grammar, {p => \$pos}, undef );\n" .
1072             "$_[1] \$pos = \$match[-1]->to;\n" .
1073             "$_[1] !\$match[-1] != 1;\n" .
1074             "$_[1] }\n" .
1075             "$_[1] ## \n";
1076             }
1077 0 0         if ( $prefix eq q(') ) { # single quoted literal '
1078 0           $cmd = substr( $cmd, 1, -1 );
1079 0           return call_constant( $cmd, $_[1] );
1080             }
1081 0 0         if ( $prefix eq q(") ) { # interpolated literal "
1082 0           $cmd = substr( $cmd, 1, -1 );
1083 0           warn "<\"...\"> not implemented";
1084 0           return;
1085             }
1086 0 0 0       if (
1087             $modifier eq '.'
1088             || $modifier eq '?' # XXX FIXME
1089             )
1090             { # non_capturing_subrule / code assertion
1091             #$cmd = substr( $cmd, 1 );
1092 0 0         if ( $cmd =~ /^{/ ) {
1093 0           warn "code assertion not implemented";
1094 0           return;
1095             }
1096 0           my @param; # TODO
1097 0           my $subrule = $cmd;
1098             return
1099 0           "$_[1] ##
1100             $_[1] ## pos: @$RegexPos
1101             $_[1] do {
1102             $_[1] my \$prior = \$::_V6_PRIOR_;
1103             $_[1] my \$match =\n" .
1104             call_subrule( $subrule, $_[1]." ", "", @param ) . ";
1105             $_[1] \$::_V6_PRIOR_ = \$prior;
1106             $_[1] my \$bool = (!\$match != 1);
1107             $_[1] \$pos = \$match->to if \$bool;
1108             $_[1] \$match;
1109             $_[1] }
1110             $_[1] ## \n";
1111             }
1112 0 0         if ( $prefix =~ /[_[:alnum:]]/ ) {
1113 0 0         if ( $cmd eq 'cut' ) {
1114 0           warn "<$cmd> not implemented";
1115 0           return;
1116             }
1117 0 0         if ( $cmd eq 'commit' ) {
1118 0           warn "<$cmd> not implemented";
1119 0           return;
1120             }
1121 0 0         if ( $cmd eq 'null' ) {
1122 0           return "$_[1] 1 # null\n"
1123             }
1124             #
1125 0           my ( $subrule, $param_list ) = split( /[\(\)]/, $cmd );
1126 0   0       $param_list ||= '';
1127              
1128 0 0         if ( $subrule eq 'at' ) {
1129 0   0       $param_list ||= 0; # XXX compile-time only
1130 0           return "$_[1] ( \$pos == $param_list )\n"
1131             }
1132              
1133 0           return named_capture(
1134             {
1135             ident => $subrule,
1136             rule => { metasyntax => { metasyntax => $cmd }, _pos => $_[0]->{_pos} },
1137             },
1138             $_[1],
1139             );
1140             }
1141             #### $prefix
1142             #### $modifier
1143             #if ( $prefix eq '.' ) {
1144             # my ( $method, $param_list ) = split( /[\(\)]/, $cmd );
1145             # $method =~ s/^\.//;
1146             # $param_list ||= '';
1147             # return " ( \$s->$method( $param_list ) ? 1 : 0 ) ";
1148             #}
1149 0           die "<$cmd> not implemented";
1150             }
1151              
1152             1;