File Coverage

blib/lib/Pugs/Runtime/Regex.pm
Criterion Covered Total %
statement 124 294 42.1
branch 39 94 41.4
condition 22 56 39.2
subroutine 33 76 43.4
pod 0 29 0.0
total 218 549 39.7


line stmt bran cond sub pod time code
1             package Pugs::Runtime::Regex;
2              
3             # documentation after __END__
4              
5 22     22   132 use strict;
  22         48  
  22         849  
6 22     22   142 use warnings;
  22         49  
  22         1114  
7 22     22   113 no warnings qw(recursion);
  22         47  
  22         990  
8              
9             #use Smart::Comments; #for debugging, look also at Filtered-Comments.pm
10 22     22   121 use Data::Dumper;
  22         46  
  22         1275  
11 22     22   122 use Pugs::Runtime::Match;
  22         55  
  22         696  
12 22     22   147 use Carp qw(croak);
  22         38  
  22         26623  
13              
14             # note: alternation is first match (not longest).
15             # note: the list in @$nodes can be modified at runtime
16             sub alternation {
17 18     18 0 31 my $nodes = shift;
18             return sub {
19 106 100   106   291 my @state = $_[1] ? @{$_[1]} : ( 0, undef );
  13         29  
20 106         315 while ( $state[0] <= $#$nodes ) {
21             #print "alternation $state[0] ",Dumper($nodes->[ $state[0] ]);
22 137         449 $nodes->[ $state[0] ]->( $_[0], $state[1], @_[2..7] );
23 137 50       488 last unless defined $_[3]; # test case ???
24 137         397 $state[1] = $_[3]->state;
25 137 100       3240 $state[0]++ unless $state[1];
26 137 100 66     3414 if ( $_[3] || $_[3]->data->{abort} ) {
27 100 100       419 $_[3]->data->{state} = $state[0] > $#$nodes
28             ? undef
29             : \@state;
30 100         245 return;
31             }
32             }
33 6         154 $_[3] = failed()->(@_);
34             }
35 18         132 }
36              
37             sub concat {
38 59     59 0 80 my $nodes = shift;
39 59 100       208 $nodes = [ $nodes, @_ ] unless ref($nodes) eq 'ARRAY'; # backwards compat
40 59 100       120 return null() if ! @$nodes;
41 58 100       154 return $nodes->[0] if @$nodes == 1;
42 49 100       106 if ( @$nodes > 2 ) {
43 4         21 return concat(
44             concat( [ $nodes->[0], $nodes->[1] ] ),
45             @$nodes[ 2 .. $#$nodes ],
46             );
47             }
48             return sub {
49 101 100   101   553 my @state = $_[1] ? @{$_[1]} : ( undef, undef );
  11         24  
50             #print "enter state ",Dumper(\@state);
51 101         140 my $m2;
52 101         131 my $redo_count = 0;
53             # XXX - workaround for t/regex/from_perl6_rules/capture.t test #38:
54             # regex single { o | k | e };
55             # # ...
56             # ok(!( "bokeper" ~~ m/() ($0)/ ), 'Failed positional backref');
57              
58 101   66     106 do {
      100        
      66        
59              
60 114 100       227 my %param1 = defined $_[7] ? %{$_[7]} : ();
  76         253  
61             #print "concat 1: @{[ %param1 ]} \n";
62              
63 114         424 $nodes->[0]->( $_[0], $state[0], @_[2..7] );
64 114 100 66     403 return if ! $_[3]
65             || $_[3]->data->{abort};
66              
67 90         286 my $is_empty = ( $_[3]->from == $_[3]->to );
68             # && ( $param1{was_empty} )
69             # ; # fix a problem with '^'
70 90 50 33     340 if ( $is_empty && $param1{was_empty} ) {
71             # # perl5 perlre says "the following match after a zero-length match
72             # is prohibited to have a length of zero"
73 0 0       0 return unless $_[3]->from == 0;
74             }
75              
76 90 50       163 my $param = { ( defined $_[7] ? %{$_[7]} : () ),
  90         324  
77             p => $_[3]->to,
78             was_empty => $is_empty,
79             };
80             # TODO - retry the second submatch only, until it fails
81 90         311 my $next_state = $_[3]->state;
82             #print "next_state ",Dumper($next_state);
83             #print "concat 2: "," \n";
84 90         332 $nodes->[1]->( $_[0], $state[1], $_[2], $m2,
85             $_[4], $_[3]->to, $_[6], $param );
86            
87             #return if $is_empty && $m2->from == $m2->to;
88 90         317 $state[1] = $m2->state;
89 90 100       575 $state[0] = $next_state unless $state[1];
90             #print "concat 3: "," \n";
91             #print "return state ",Dumper(\@state);
92              
93             } while ! $m2
94             && ! $m2->data->{abort}
95             && defined $state[0]
96             && $redo_count++ < 512
97             ;
98              
99             # push capture data
100             # print "Concat positional: ", Dumper( $_[3]->data->{match}, $m2->data->{match} );
101 77         139 for ( 0 .. $#{ $m2 } ) {
  77         205  
102 0 0       0 if ( ref $m2->[$_] eq 'ARRAY' ) {
    0          
103             # TODO - fully static count
104             # push @{ $_[3]->data->{match}[$_] }, @{ $m2->[$_] };
105 0         0 $_[3]->data->{match}[$_] = [
106             ( ref( $_[3]->data->{match}[$_] ) eq 'ARRAY'
107 0         0 ? @{ $_[3]->data->{match}[$_] }
108             : defined( $_[3]->data->{match}[$_] )
109             ? $_[3]->data->{match}[$_]
110             : ()
111             ),
112 0 0       0 @{ $m2->[$_] },
    0          
113             ];
114             }
115             elsif ( defined $m2->[$_] ) {
116 0         0 $_[3]->data->{match}[$_] = $m2->[$_];
117             }
118             }
119             #print "Concat named: ", Dumper( $_[3]->data->{named}, $m2->data->{named} );
120 77         123 for ( keys %{$m2} ) {
  77         194  
121 0 0       0 if ( ref $m2->{$_} eq 'ARRAY' ) {
    0          
122             # TODO - fully static count
123             #push @{ $_[3]->data->{named}{$_} }, @{ $m2->{$_} };
124 0         0 $_[3]->data->{named}{$_} = [
125             ( ref( $_[3]->data->{named}{$_} ) eq 'ARRAY'
126 0         0 ? @{ $_[3]->data->{named}{$_} }
127             : defined( $_[3]->data->{named}{$_} )
128             ? $_[3]->data->{named}{$_}
129             : ()
130             ),
131 0 0       0 @{ $m2->{$_} },
    0          
132             ];
133             }
134             elsif ( defined $m2->{$_} ) {
135 0         0 $_[3]->data->{named}{$_} = $m2->{$_};
136             }
137             }
138             # /push capture data
139              
140 77         231 %{$_[3]->data} = (
  77         217  
141 77 100 33     108 %{$_[3]->data},
      100        
142             bool => \($m2->bool),
143             to => \($m2->to),
144             capture => $m2->data->{capture} || $_[3]->data->{capture},
145             abort => $m2->data->{abort},
146             state => ( defined $state[0] || defined $state[1]
147             ? \@state
148             : undef ),
149             );
150             }
151 45         428 }
152              
153             sub try_method {
154 0     0 0 0 my $method = shift;
155 0         0 my $param_list = shift; # XXX
156 22     22   158 no warnings qw( uninitialized );
  22         51  
  22         2389  
157             # XXX method call must be inlined, due to inheritance problems
158 0         0 my $sub = 'sub {
159             my $bool = $_[0]->'.$method.'( '.$param_list.' ) ? 1 : 0;
160             $_[3] = Pugs::Runtime::Match->new({
161             bool => \$bool,
162             str => \$_[0],
163             from => \(0 + $_[5]),
164             to => \(0 + $_[5]),
165             named => {},
166             match => [],
167             });
168             }';
169             #print "sub: $sub\n";
170 0         0 return eval $sub;
171             }
172              
173              
174             sub ignorecase {
175 0     0 0 0 my $sub = shift;
176 22     22   125 no warnings qw( uninitialized );
  22         52  
  22         3411  
177             return sub {
178 0 0   0   0 my %param = ( ( defined $_[7] ? %{$_[7]} : () ), ignorecase => 1 );
  0         0  
179 0         0 $sub->( @_[0..6], \%param );
180             }
181 0         0 }
182              
183             sub constant {
184 32     32 0 111 my $const = shift;
185 32         44 my $lconst = length( $const );
186 22     22   160 no warnings qw( uninitialized );
  22         52  
  22         3989  
187             return sub {
188 157 50   157   560 my $bool = $_[7]{ignorecase}
189             ? lc( $const ) eq lc( substr( $_[0], $_[5], $lconst ) )
190             : $const eq substr( $_[0], $_[5], $lconst );
191 157         1290 $_[3] = Pugs::Runtime::Match->new({
192             bool => \$bool,
193             str => \$_[0],
194             from => \(0 + $_[5]),
195             to => \($_[5] + $lconst),
196             named => {},
197             match => [],
198             });
199             }
200 32         224 }
201              
202             sub perl5 {
203 0     0 0 0 my $rx;
204 22     22   136 no warnings qw( uninitialized );
  22         45  
  22         7762  
205             {
206 0         0 local $@;
  0         0  
207 0         0 $rx = eval " use charnames ':full'; qr(^($_[0]))s ";
208             #print "regex perl5<< $_[0] >>\n";
209 0 0       0 print "Error in perl5 regex: << $_[0] >> \n$@\n"
210             if $@;
211             #die "Error in perl5 regex: $_[0]"
212             # if $@;
213             }
214             return sub {
215             #use charnames ':full';
216 0     0   0 my $bool;
217             eval {
218 0 0       0 $bool = $_[7]{ignorecase}
219             ? substr( $_[0], $_[5] ) =~ m/(?i)$rx/
220             : substr( $_[0], $_[5] ) =~ m/$rx/;
221 0         0 $_[3] = Pugs::Runtime::Match->new({
222             bool => \$bool,
223             str => \$_[0],
224             from => \(0 + $_[5]),
225             to => \($_[5] + length $1),
226             named => {},
227             match => [],
228             });
229 0         0 1;
230             }
231 0 0       0 or do {
232 0         0 die "$@ in perl5 regex: /$rx/";
233             };
234 0         0 $_[3];
235 0         0 };
236             }
237              
238             sub null {
239 22     22   9057 no warnings qw( uninitialized );
  22         52  
  22         2897  
240             return sub {
241 13     13   150 $_[3] = Pugs::Runtime::Match->new({
242             bool => \1,
243             str => \$_[0],
244             from => \(0 + $_[5]),
245             to => \(0 + $_[5]),
246             named => {},
247             match => [],
248             });
249             }
250 9     9 0 49 };
251              
252             sub failed {
253 22     22   128 no warnings qw( uninitialized );
  22         52  
  22         2807  
254             return sub {
255 7     7   58 $_[3] = Pugs::Runtime::Match->new({
256             bool => \0,
257             str => \$_[0],
258             from => \(0 + $_[5]),
259             to => \(0 + $_[5]),
260             named => {},
261             match => [],
262             state => undef,
263             });
264             }
265 7     7 0 34 };
266              
267             sub failed_abort {
268 22     22   127 no warnings qw( uninitialized );
  22         43  
  22         21730  
269             return sub {
270 0     0   0 $_[3] = Pugs::Runtime::Match->new({
271             bool => \0,
272             str => \$_[0],
273             from => \(0 + $_[5]),
274             to => \(0 + $_[5]),
275             named => {},
276             match => [],
277             abort => 1,
278             });
279             }
280 0     0 0 0 };
281              
282             sub named {
283             # return a named capture
284 0     0 0 0 my $label = shift;
285 0         0 my $capture_to_array = shift;
286 0         0 my $node = shift;
287             sub {
288 0     0   0 my $match;
289 0         0 $node->( @_[0,1,2], $match, @_[4,5,6,7] );
290 0         0 my %matches;
291 0 0       0 $matches{ $label } = $capture_to_array ? [ $match ] : $match;
292 0         0 $_[3] = Pugs::Runtime::Match->new({
293             bool => \( $match->bool ),
294             str => \$_[0],
295             from => \( $match->from ),
296             to => \( $match->to ),
297             named => \%matches,
298             match => [],
299             capture => $match->data->{capture},
300             state => $match->state,
301             });
302             }
303 0         0 }
304 0     0 0 0 sub capture { named(@_) } # backwards compat
305              
306             sub positional {
307             # return a positional capture
308 0     0 0 0 my $num = shift;
309 0         0 my $capture_to_array = shift;
310 0         0 my $node = shift;
311             sub {
312 0     0   0 my $match;
313 0         0 $node->( @_[0,1,2], $match, @_[4,5,6,7] );
314 0         0 my @matches;
315 0 0       0 $matches[ $num ] = $capture_to_array ? [ $match ] : $match;
316 0         0 $_[3] = Pugs::Runtime::Match->new({
317             bool => \( $match->bool ),
318             str => \$_[0],
319             from => \( $match->from ),
320             to => \( $match->to ),
321             named => {},
322             match => \@matches,
323             capture => $match->data->{capture},
324             state => $match->state,
325             });
326             }
327 0         0 }
328              
329             sub capture_as_result {
330             # return a capture as the result object
331 0     0 0 0 my $node = shift;
332             sub {
333 0     0   0 my $match;
334 0         0 $node->( @_[0,1,2], $match, @_[4,5,6,7] );
335             $_[3] = Pugs::Runtime::Match->new({
336             bool => \( $match->bool ),
337             str => \$_[0],
338             from => \( $match->from ),
339             to => \( $match->to ),
340             named => {},
341             match => [],
342             capture => (
343             sub {
344             # print "Match: ", Dumper( $match );
345 0         0 '' . $match
346             }
347 0         0 ),
348             state => $match->state,
349             });
350             }
351 0         0 }
352              
353             sub ___abort {
354 0     0   0 my $op = shift;
355             return sub {
356 0     0   0 print "ABORTING\n";
357 0         0 $op->( @_ );
358 0         0 print "ABORT: [0] ",Dumper(@_); #$_[3]->perl;
359 0         0 $_[3]->data->{abort} = 1;
360 0         0 print "ABORT: ",$_[3]->perl;
361 0         0 };
362             };
363              
364             sub ___fail {
365 0     0   0 my $op = shift;
366             return abort(
367             sub {
368 0     0   0 print "FAILING\n";
369 0         0 $op->( @_ );
370 0         0 $_[3]->data->{bool} = \0;
371 0         0 print "FAIL: ",Dumper( $_[3] );
372             }
373 0         0 );
374             };
375              
376             sub before {
377 0     0 0 0 my $op = shift;
378             return sub {
379 0     0   0 my $match;
380 0         0 $op->( @_[0,1,2], $match, @_[4,5,6,7] );
381 0         0 $_[3] = Pugs::Runtime::Match->new({
382             bool => \( $match->bool ),
383             str => \$_[0],
384             from => \( $match->from ),
385             to => \( $match->from ),
386             named => {},
387             match => [],
388             state => $match->state,
389             });
390 0         0 };
391             }
392              
393             sub at_start {
394 22     22   169 no warnings qw( uninitialized );
  22         49  
  22         3257  
395             return sub {
396 0     0   0 $_[3] = Pugs::Runtime::Match->new({
397             bool => \( $_[5] == 0 ),
398             str => \$_[0],
399             from => \(0 + $_[5]),
400             to => \(0 + $_[5]),
401             named => {},
402             match => [],
403             abort => 0,
404             });
405             }
406 0     0 0 0 };
407              
408             sub at_line_start {
409 22     22   274 no warnings qw( uninitialized );
  22         49  
  22         3973  
410             return sub {
411 0   0 0   0 my $bool = $_[5] == 0
412             || substr( $_[0], 0, $_[5] ) =~ /\n$/s;
413 0         0 $_[3] = Pugs::Runtime::Match->new({
414             bool => \$bool,
415             str => \$_[0],
416             from => \(0 + $_[5]),
417             to => \(0 + $_[5]),
418             named => {},
419             match => [],
420             abort => 0,
421             });
422             }
423 0     0 0 0 };
424              
425             sub at_line_end {
426 22     22   124 no warnings qw( uninitialized );
  22         61  
  22         4000  
427             return sub {
428 0   0 0   0 my $bool = $_[5] >= length( $_[0] )
429             || substr( $_[0], $_[5] ) =~ /^\n/s;
430 0         0 $_[3] = Pugs::Runtime::Match->new({
431             bool => \$bool,
432             str => \$_[0],
433             from => \(0 + $_[5]),
434             to => \(0 + $_[5]),
435             named => {},
436             match => [],
437             abort => 0,
438             });
439             }
440 0     0 0 0 };
441              
442             sub at_end_of_string {
443 22     22   137 no warnings qw( uninitialized );
  22         52  
  22         51342  
444             return sub {
445 0     0   0 $_[3] = Pugs::Runtime::Match->new({
446             bool => \( $_[5] == length( $_[0] ) ),
447             str => \$_[0],
448             from => \(0 + $_[5]),
449             to => \(0 + $_[5]),
450             named => {},
451             match => [],
452             abort => 0,
453             });
454             }
455 0     0 0 0 };
456              
457             # experimental!
458             sub negate {
459 0     0 0 0 my $op = shift;
460             return sub {
461             #my $str = $_[0];
462 0     0   0 my $match = $op->( @_ );
463 0         0 my $bool = ! $match;
464              
465 0         0 $_[3] = Pugs::Runtime::Match->new({
466             bool => \( $bool ),
467             str => \$_[0],
468             from => \(0 + $_[5]),
469             to => \(0 + $_[5]),
470             named => {},
471             match => [],
472             abort => 0,
473             });
474              
475 0         0 };
476             };
477              
478             # ------- higher-order ruleops
479              
480             sub optional {
481 8     8 0 12 my $node = shift;
482 8         21 alternation( [ $node, null() ] );
483             }
484              
485             sub null_or_optional {
486 0     0 0 0 my $node = shift;
487 0         0 alternation( [ null(), $node ] );
488             }
489              
490             sub greedy_star {
491 2   50 2 0 22 greedy_plus( $_[0], $_[1] || 0, $_[2] )
492             }
493              
494             sub non_greedy_star {
495 0   0 0 0 0 non_greedy_plus( $_[0], $_[1] || 0, $_[2] )
496             }
497              
498             # XXX - needs optimization for faster backtracking, less stack usage
499             # TODO - run-time ranges (iterator)
500             sub greedy_plus {
501 5     5 0 9 my $node = shift;
502 5 100       11 my $min_count = defined( $_[0] ) ? $_[0] : 1;
503 5         12 my $max_count = $_[1];
504 5 50 33     18 if ( defined $max_count
505             && $max_count < 1e99
506             ) {
507 0         0 return concat( [
508             ( $node ) x $min_count,
509             ( optional( $node ) ) x ($max_count - $min_count)
510             ] );
511             }
512             # $max_count == infinity
513 5         10 my $alt;
514             $alt = concat( [
515             $node,
516 5     24   28 optional( sub{ goto $alt } ),
  24         225  
517             ] );
518 5 100       21 return optional( $alt ) if $min_count < 1;
519 3         15 return concat( [ ( $node ) x ($min_count - 1), $alt ] );
520             }
521              
522             # XXX - needs optimization for faster backtracking, less stack usage
523             # TODO - run-time ranges (iterator)
524             sub non_greedy_plus {
525 6     6 0 10 my $node = shift;
526 6 100       19 my $min_count = defined( $_[0] ) ? $_[0] : 1;
527 6   100     24 my $max_count = $_[1] || 1e99;
528             return sub {
529 21   100 21   133 my $state = $_[1]
530             || { node => concat( [ ( $node ) x $min_count ] ),
531             count => $min_count };
532 21 100       66 return failed()->(@_)
533             if $state->{count} > $max_count;
534 20         166 $state->{node}->( $_[0], undef, @_[2..7] );
535 20         134 $_[3]->data->{state} =
536             { node => concat( [ $node, $state->{node} ] ),
537             count => $state->{count} + 1 };
538             }
539 6         36 }
540              
541             sub range {
542 0     0 0   my $node = shift;
543 0           my $min_count = shift;
544 0           my $max_count = shift;
545 0           my $greedy = not shift;
546             return sub {
547              
548 0     0     my $continuation = $_[1]; #XXX how do optional continuations work?
549              
550             # Forward declarations
551              
552 0           my $try_getting_more;
553              
554             my $default_behavior;
555 0           my $fallback_behavior;
556              
557             # Loop variables
558              
559 0           my $count = 0;
560 0           my $previous_pos = -1;
561              
562             # Loop 1 - getting to min_count
563              
564 0           my $continue_towards_min;
565             my $get_minimum = sub {
566 0 0         if ( $count < $min_count ) {
567 0           $count++;
568 0           goto &$continue_towards_min;
569             } else {
570 0           goto &$try_getting_more;
571             }
572 0           };
573 0           $continue_towards_min = concat( [ $node, $get_minimum ] );
574              
575             # Loop 2 - beyond the minimum
576              
577             $try_getting_more = sub {
578              
579 0           my $current_pos = $_[5];
580              
581             # (1) Stop when max_count is reached, or if pos does not move.
582              
583 0 0 0       if ( !( $count < $max_count ) ||
584             !( $previous_pos < $current_pos ) )
585             {
586 0           goto &$continuation;
587             }
588 0           $count++;
589 0           $previous_pos = $current_pos;
590              
591             # (2) Attempt the default behavior.
592              
593             # XXX - This section needs to be filled in.
594             # try $default_behavior
595             # if successful, return.
596             # if abort, do whatever is needed.
597             # if fail, we need to backtrack:
598             # undo any side-effects from trying the $default_behavior,
599             # so we can do the $fallback_behavior.
600              
601             # (3) Since the default behavior failed, do the fall-back beharvior.
602              
603 0           goto &$fallback_behavior;
604              
605 0           };
606 0           my $get_one_and_maybe_more = concat( [ $node, $try_getting_more ] );
607              
608             # Final preparations.
609              
610 0 0         if ( $greedy ) {
611 0           $default_behavior = $get_one_and_maybe_more;
612 0           $fallback_behavior = $continuation;
613             } else { # non-greedy
614 0           $default_behavior = $continuation;
615 0           $fallback_behavior = $get_one_and_maybe_more;
616             }
617              
618             # Start.
619              
620 0           goto &$get_minimum;
621 0           };
622             }
623              
624              
625             sub preprocess_hash {
626             # TODO - move to Pugs::Runtime::Regex
627 0     0 0   my ( $h, $key ) = @_;
628             # returns AST depending on $h
629             #print "preprocess_hash: ", Dumper( \@_ );
630 0 0         if ( ref( $h->{$key} ) eq 'CODE') {
631             return sub {
632 0     0     my ( $str, $grammar, $args ) = @_;
633             #print "data: ", Dumper( \@_ );
634 0           my $ret = $h->{$key}->( @_ );
635             #print "ret: ", Dumper( $ret );
636            
637 0 0         return $ret
638             if ref( $ret ) eq 'Pugs::Runtime::Match';
639            
640 0   0       Pugs::Runtime::Match->new( {
      0        
641             bool => \1,
642             str => \$str,
643             from => \( 0 + ( $args->{p} || 0 ) ),
644             to => \( 0 + ( $args->{p} || 0 ) ),
645             named => {},
646             match => [],
647             } ) }
648 0           }
649 0 0         if ( ref( $h->{$key} ) =~ /Pugs::Compiler::/ ) {
650 0     0     return sub { $h->{$key}->match( @_ ) };
  0            
651             }
652             # fail is number != 1
653 0 0         if ( $h->{$key} =~ /^(\d+)$/ ) {
654             return sub {
655 0     0     my ( $str, $grammar, $args ) = @_;
656 0   0       Pugs::Runtime::Match->new( {
      0        
657             bool => \0,
658             str => \$str,
659             from => \( 0 + ( $args->{p} || 0 ) ),
660             to => \( 0 + ( $args->{p} || 0 ) ),
661             named => {},
662             match => [],
663 0 0         } ) } unless $1 == 1;
664             return sub {
665 0     0     my ( $str, $grammar, $args ) = @_;
666 0   0       Pugs::Runtime::Match->new( {
      0        
667             bool => \1,
668             str => \$str,
669             from => \( 0 + ( $args->{p} || 0 ) ),
670             to => \( 0 + ( $args->{p} || 0 ) ),
671             named => {},
672             match => [],
673 0           } ) };
674             }
675             # subrule
676             #print "compile: ",$h->{$key}, "\n";
677              
678             # XXX - compile to Token or to Regex ? (v6.pm needs Token)
679 0           my $r = Pugs::Compiler::Token->compile( $h->{$key} );
680 0           $h->{$key} = $r;
681 0     0     return sub { $r->match( @_ ) };
  0            
682             # return sub { warn "uncompiled subrule: $h->{$key} - not implemented " };
683             }
684              
685             # see commit #9783 for an alternate implementation
686             sub hash {
687 0     0 0   my %hash = %{shift()};
  0            
688             #print "HASH: @{[ %hash ]}\n";
689 0           my @keys = sort {length $b <=> length $a } keys %hash;
  0            
690             #print "hash keys [ @keys ]\n";
691 0           for ( @keys ) {
692 0           my $h = preprocess_hash( \%hash, $_ );
693 0           my $key = $_;
694             $_ =
695             concat( [
696             constant( $key ),
697             sub {
698             # print "hash param: ",Dumper(\@_);
699             # TODO - add $ to $_[7]
700 0     0     $_[3] = $h->( $_[0], $_[4], $_[7], $_[1] );
701             # print "result: ",Dumper($_[3]);
702             }
703 0           ] );
704             }
705 0           return alternation( \@keys );
706             }
707              
708             # not a 'rule node'
709             # gets a variable from the user's pad
710             # this is used by the <$var> rule
711             sub get_variable {
712 0     0 0   my $name = shift;
713            
714 0           local $@;
715 0           my($idx, $pad) = 0;
716 0           while(eval { require PadWalker; $pad = PadWalker::peek_my($idx) }) {
  0            
  0            
717 0 0         $idx++, next
718             unless exists $pad->{$name};
719              
720             #print "NAME $name $pad->{$name}\n";
721 0 0         return ${ $pad->{$name} } if $name =~ /^\$/;
  0            
722 0           return $pad->{$name}; # arrayref/hashref
723             }
724 0           croak "Couldn't find '$name' in surrounding lexical scope.";
725             }
726              
727              
728             1;
729              
730             __END__