File Coverage

blib/lib/Data/Match.pm
Criterion Covered Total %
statement 459 618 74.2
branch 146 266 54.8
condition 59 100 59.0
subroutine 94 122 77.0
pod 8 13 61.5
total 766 1119 68.4


line stmt bran cond sub pod time code
1             package Data::Match;
2              
3             #########################################################################
4              
5             =head1 NAME
6              
7             Data::Match - Complex data structure pattern matching
8              
9             =head1 SYNOPSIS
10              
11             use Data::Match qw(:all);
12             my ($match, $results) = match($structure, $pattern);
13              
14             use Data::Match;
15             my $obj = new Data::Match;
16             my ($match, $results) = $obj->execute($structure, $pattern);
17              
18             =head1 DESCRIPTION
19              
20             Data::Match provides extensible complex Perl data structure searching and matching.
21              
22             =head1 EXPORT
23              
24             None are exported by default. C<:func> exports C and C, C<:pat> exports all the pattern element generators below, C<:all> exports C<:func> and C<:pat>.
25              
26             =head1 PATTERNS
27              
28             A data pattern is a complex data structure that possibly matches another complex data structure. For example:
29              
30             matches([ 1, 2 ], [ 1, 2 ]); # TRUE
31              
32             matches([ 1, 2, 3 ], [ 1, ANY, 3 ]); # TRUE
33              
34             matches([ 1, 2, 3 ], [ 1, ANY, 2 ]); # FALSE: 3 != 2
35              
36             C matches anything, including an undefined value.
37              
38             my $results = matches([ 1, 2, 1 ], [ BIND('x'), ANY, BIND('x') ]); # TRUE
39              
40             C matches anything and remembers each match and its position with every C in C<$result->{'BIND'}{$name}>. If C is not the same as the first value bound to C it does not match. For example:
41              
42             my $results = matches([ 1, 2, 3 ], [ BIND('x'), 2, BIND('x') ]); # FALSE: 3 != 1
43              
44             C is similar to BIND but does not compare first bound values.
45              
46             C matches all remaining elements of an array or hash.
47              
48             matches([ 1, 2, 3 ], [ 1, REST() ]); # TRUE
49             matches({ 'a'=>1, 'b'=>1 }, { 'b'=>1, REST() => REST() }); # TRUE
50              
51             C searches at all depths for matching sub-patterns.
52              
53             matches([ 1, [ 1, 2 ], 3], FIND(COLLECT('x', [ 1, REST() ])); # is true.
54              
55             See the test script C in the package distribution for more pattern examples.
56              
57             =head1 MATCH COLLECTIONS
58              
59             When a C or C matches a datum, an entry is collected in C<$result-E{BIND}> and C<$result-E{COLLECT}>, respectively. (This might change in the future)
60              
61             Each entry for the binding name is a hash containing C<'v'>, C<'p'> and C<'ps'> lists.
62              
63             =over 4
64              
65             =item C<'v'>
66              
67             is a list of the value at each match.
68              
69             =item C<'p'>
70              
71             is a list of match paths describing where the corresponding match was found based on the root of the search at each match. See C. C<'p'> is not collected if C<$matchobj-C{'no_collect_path'}>.
72              
73             =item C<'ps'>
74              
75             is a list of code strings (C) that describes where the match was for each match. C<'ps'> is collected only if C<$matchobj-C{'collect_path_str'}>.
76              
77             =over
78              
79             =head1 SUB-PATTERNS
80              
81             All patterns can have sub-patterns. Most patterns match the AND-ed results of their sub-patterns and their own behavior, first trying the sub-patterns before attempting to match the intrinsic behavior. However, C and C match any sub-patterns;
82              
83             For example:
84              
85             match([ ['a', 1 ], ['b', 2], ['a', 3] ], EACH(COLLECT('x', ['a', ANY() ]))) # TRUE
86              
87             The above pattern means:
88              
89             =over 2
90            
91             For EACH element in the root structure (an array):
92              
93             =over 2
94              
95             COLLECT each element, into collection named C<'x'>, that is,
96              
97             =over 2
98              
99             An ARRAY of length 2 that starts with C<'a'>.
100            
101             =back
102              
103             =back
104              
105             =back
106              
107             On the other hand.
108              
109             match( [ ['a', 1 ], ['b', 2], ['a', 3] ], ALL(COLLECT('x', [ 'a', ANY() ])) )
110             # IS FALSE
111              
112             Because the second root element (an array) does not start with C<'a'>. But,
113              
114             match( [ ['a', 1 ], ['a', 2], ['a', 3] ], ALL(COLLECT('x', [ 'a', ANY() ])) )
115             # IS TRUE
116              
117             The pattern below flattens the nested array into atoms:
118              
119             match(
120             [ 1, 'x',
121             [ 2, 'x',
122             [ 3, 'x'],
123             [ 4,
124             [ 5,
125             [ 'x' ]
126             ],
127             6
128             ]
129             ]
130             ],
131             FIND(COLLECT('x', EXPR(q{! ref}))),
132             { 'no_collect_path' => 1 }
133             )->{'COLLECT'}{'x'}{'v'};
134              
135             C causes C and C to not collect any paths.
136              
137              
138             =head1 MATCH SLICES
139              
140             Match slices are objects that contain slices of matched portions of a data structure. This is useful for inflicting change into substructures matched by patterns like C.
141              
142             For example:
143              
144             do {
145             my $a = [ 1, 2, 3, 4 ];
146             my $p = [ 1, ANY, REST(BIND('s')) ];
147             my $r = matches($a, $p);
148             ok($r); # TRUE
149             ok(Compare($r->{'BIND'}{'s'}{'v'}[0], [ 3, 4 ])); # TRUE
150             $r->{'BIND'}{'s'}{'v'}[0][0] = 'x'; # Change match slice
151             matches($a, [ 1, 2, 'x', 4 ]); # TRUE
152             }
153              
154             Hash match slices are generated for each key-value pair for a hash matched by C and C. Each of these match slices can be matched as a hash with a single key-value pair.
155              
156             Match slices are useful for search and replace missions.
157              
158             =head1 VISITATION ADAPTERS
159              
160             By default Data::Match is blind to Perl object interfaces. To instruct Data::Match to not traverse object implementation containers and honor object interfaces you must provide a visitation adapter. A visitation adapter tells Data::Match how to traverse through an object interface and how to keep track of how it got through.
161              
162             For example:
163              
164             package Foo;
165             sub new
166             {
167             my ($cls, %opts) = @_;
168             bless \%opts, $cls;
169             }
170             sub x { shift->{x}; }
171             sub parent { shift->{parent}; }
172             sub children { shift->{children}; }
173             sub add_child {
174             my $self = shift;
175             for my $c ( @_ ) {
176             $c->{parent} = $self;
177             }
178             push(@{$self->{children}}, @_);
179             }
180              
181              
182             my $foos = [ map(new Foo('x' => $_), 1 .. 10) ];
183             for my $f ( @$foos ) { $f->add_child($foos->[rand($#$foo)); }
184              
185             my $pat = FIND(COLLECT('Foo', ISA('Foo', { 'parent' => $foos->[0], REST() => REST() })));
186             $match->match($foos, $pat);
187              
188             The problem with the above example is: C will not honor the interface of class Foo by default and will eventually find a Foo where C<$_Eparent eq $foos-E[0]> through all the parent and child links in the objects' implementation container. To force Data::Match to honor an interface (or a subset of an interface) during C traversal we create a 'find' adapter sub that will do the right thing.
189              
190             my $opts = {
191             'find' => {
192             'Foo' => sub {
193             my ($self, $visitor, $match) = @_;
194              
195             # Always do 'x'.
196             $visitor->($self->x, 'METHOD', 'x');
197              
198             # Optional children traversal.
199             if ( $match->{'Foo_find_children'} ) {
200             $visitor->($self->children, 'METHOD', 'children');
201             }
202              
203             # Optional parent traversal.
204             if ( $match->{'Foo_find_parent'} ) {
205             $visitor->($self->parent, 'METHOD', 'parent');
206             }
207             }
208             }
209             }
210             my $match = new Data::Match($opts, 'Foo_find_children' => 1);
211             $match = $match->execute($foos, $pat);
212              
213             See C for more examples of visitation adapters.
214              
215             =head1 DESIGN
216              
217             Data::Match employs a mostly-functional external interface since this module was inspired by a Lisp tutorial ("The Little Lisper", maybe) I read too many years ago; besides, pattern matching is largely recursively functional. The optional control hashes and traverse adapter interfaces are better represented by an object interface so I implemented a functional veneer over the core object interface.
218              
219             Internally, objects are used to represent the pattern primitives because most of the pattern primitives have common behavior. There are a few design patterns that are particularly applicable in Data::Match: Visitor and Adapter. Adapter is used to provide the extensibility for the traversal of blessed structures such that Data::Match can honor the external interfaces of a class and not blindly violate encapsulation. Visitor is the basis for some of the C pattern implementation. The C classes that provide the match slices are probably a Veneer on the array and hash types through the tie meta-behaviors.
220              
221             =head1 CAVEATS
222              
223             =over 4
224              
225             =item *
226              
227             Does not have regexp-like operators like '?', '*', '+'.
228              
229             =item *
230              
231             Should probably have more interfaces with Data::DRef and Data::Walker.
232              
233             =item *
234              
235             The visitor adapters do not use C to search for the adapter; it uses C. This will be fixed in a future release.
236              
237             =item *
238              
239             Since hash keys do not retain blessedness (what was Larry thinking?) it is difficult to have patterns match keys without resorting to some bizarre regexp instead of using C.
240              
241             =item *
242              
243             C and C do not work through C<'METHOD'> path boundaries. This will be fixed in a future release.
244              
245             =item *
246              
247             C and C need scoping operators for deeply collected patterns.
248              
249             =back
250              
251             =head1 STATUS
252              
253             If you find this to be useful please contact the author. This is alpha software; all APIs, semantics and behaviors are subject to change.
254              
255             =head1 INTERFACE
256              
257             This section describes the external interface of this module.
258              
259             =cut
260             #'oh emacs, when will perl-mode recognize =pod?
261              
262             #########################################################################
263              
264              
265 4     4   28379 use strict;
  4         15  
  4         155  
266 4     4   23 use warnings;
  4         4  
  4         443  
267              
268             our $VERSION = '0.06';
269             our $REVISION = do { my @r = (q$Revision: 1.12 $ =~ /\d+/g); sprintf "%d." . "%02d" x $#r, @r };
270              
271             our $PACKAGE = __PACKAGE__;
272              
273 4     4   23 use Exporter;
  4         9  
  4         798  
274             our @ISA = qw(Exporter);
275             our @EXPORT = qw();
276              
277             our @export_func = qw(match matches match_path_str match_path_get match_path_ref);
278             our @autoload_pat =
279             qw(
280             ANY
281             AND
282             OR
283             NOT
284             BIND
285             COLLECT
286             REGEX
287             ISA REF
288             DEPTH
289             REST
290             RANG STAR PLUS QUES
291             EACH
292             ALL
293             FIND
294             LENGTH
295             EXPR
296             );
297             our @export_pat = @autoload_pat;
298             our @EXPORT_OK = (@export_func, @export_pat);
299             our %EXPORT_TAGS = (
300             'all' => \@EXPORT_OK,
301             'func' => \@export_func,
302             'pat' => \@export_pat,
303             );
304              
305 4     4   3935 use String::Escape qw(printable);
  4         28020  
  4         416  
306 4     4   2441 use Sort::Topological qw(:all);
  4         10  
  4         679  
307              
308 4     4   4513 use Data::Dumper;
  4         61006  
  4         290  
309 4     4   3372 use Data::Compare;
  4         60818  
  4         46  
310 4     4   18426 use Carp qw(confess);
  4         7  
  4         526  
311              
312             our $debug = 0;
313              
314              
315             #########################################################################
316             # Automagically create creator functions for common patterns.
317             #
318              
319              
320             our %autoload_pat = map(($_, 1), @autoload_pat);
321              
322             sub AUTOLOAD
323             {
324 4     4   22 no strict "refs";
  4         6  
  4         127  
325 4     4   19 use vars qw($AUTOLOAD);
  4         8  
  4         5532  
326              
327 21     21   1684 my ($pkg, $pat) = $AUTOLOAD =~ /^(.*)::(\w+)$/;
328              
329 21         59 my ($self) = @_;
330              
331 21 50       97 if ( $autoload_pat{$pat} eq 1 ) {
332 21         52 my $pat_cls = "${pkg}::Pattern::${pat}";
333             # $DB::single = 1;
334 21     27   2017 my $code = eval "sub { new $pat_cls(\@_); }";
  27         6758  
  30         1741  
  21         105  
  23         102  
  11         51  
  21         1939  
  33         6718  
  5         25  
  12         1353  
  14         66  
  25         9172  
  3         20  
  1         15  
  4         668  
  31         2830  
335 21 50       70 die "$@: PAT=$pat" if $@;
336 21         50 *{$AUTOLOAD} = $autoload_pat{$pat} = $code;
  21         97  
337             #warn "AUTOLOADED $pat_cls";
338             #print "AUTOLOAD $AUTOLOAD: ", Data::Dumper->new([ \@_ ], [ qw(@_) ])->Indent(0)->Purity(1)->Terse(0)->Dump(), "\n\n";
339 21         909 $code->(@_);
340             } else {
341 0         0 warn "no autoload_pat{$pat}";
342 0         0 $self->SUPER::AUTOLOAD(@_);
343 0         0 die "no such method: $AUTOLOAD";
344             }
345             }
346              
347              
348             sub DESTROY
349 0     0   0 {
350             # NOTHING.
351             }
352              
353              
354             *OR = \&ANY; # See ANY::match => match_or.
355              
356              
357             #########################################################################
358             # Instance initialization.
359             #
360              
361             sub new
362             {
363 118     118 0 186 my ($self, @opts) = @_;
364 118 100       355 my %opts = @opts & 1 ? ( %{$opts[0]}, @opts[1..$#opts]) : @opts;
  2         11  
365 118         407 (bless \%opts, $self)->defaults->initialize;
366             }
367              
368              
369             sub defaults
370             {
371 118     118 0 286 shift;
372             }
373              
374              
375             sub initialize
376             {
377 118     118 0 376 shift;
378             }
379              
380              
381             #=head2 _self_or_instance
382             #
383             #Returns self if called as an instance method or a new instance if called as a class method.
384             #
385             #=cut
386             sub _self_or_instance
387             {
388 0     0   0 my $self = shift;
389             # $DB::single = 1;
390 0 0       0 ref($self) ? $self : __PACKAGE__->new(@_);
391             }
392              
393              
394              
395             #########################################################################
396             # Low-level container match traversals.
397             #
398              
399             sub _match_ARRAY_REST($$$$$)
400             {
401 60     60   83 my ($self, $x, $p, $x_i, $p_i) = @_;
402              
403 60         66 my $match = 1;
404              
405             ARRAY:
406             {
407             # Each element must match.
408 60         53 while ( $$p_i < @$p ) {
  60         135  
409             # [ 'x', 'y', REST ] matches [ 'x', 'y', 'z', '1', '2', '3' ]
410             # Where SUBPAT in REST(SUBPAT) is bound to [ 'z', '1', '2', '3' ].
411 116 100 66     700 if ( ! $self->{'disable_patterns'} &&
412             UNIVERSAL::isa($p->[$$p_i], 'Data::Match::Pattern::REST') ) {
413              
414             # Match REST's subpatterns against the REST slice.
415 21   66     143 $match &&= $p->[$$p_i]->_match_REST_ARRAY($x, $p, $self, $x_i, $p_i);
416             } else {
417             # Match each element of $x against each element $p.
418 95         183 $self->_match_path_push('ARRAY', $$x_i);
419            
420 95   100     352 $match = $$x_i < @$x && $self->_match($x->[$$x_i], $p->[$$p_i]);
421            
422 95         200 $self->_match_path_pop;
423             }
424              
425 116 100       266 last ARRAY unless $match;
426              
427 94         99 ++ $$x_i;
428 94         206 ++ $$p_i;
429             }
430            
431             # Make sure lengths are same.
432 38   66     227 $match &&= $$p_i == @$p && $$x_i == @$x;
      66        
433             }
434              
435 60         162 $match;
436             }
437              
438              
439             #=head2 _match_ARRAY
440             #
441             #Internal recursive match routine. Assumes $matchobj is initialized.
442             #
443             #=cut
444             sub _match_ARRAY($$$)
445             {
446 42     42   60 my ($self, $x, $p) = @_;
447              
448 42         55 my $x_i = 0;
449 42         45 my $p_i = 0;
450            
451 42         114 $self->_match_ARRAY_REST($x, $p, \$x_i, \$p_i);
452             }
453              
454              
455              
456             #=head2 _match_HASH
457             #
458             #Internal recursive match routine. Assumes $matchobj is initialized.
459             #
460             #=cut
461             sub _match_HASH($$$)
462             {
463 27     27   37 my ($self, $x, $p) = @_;
464              
465             # $DB::single = 1;
466              
467 27         27 my $match = 1;
468              
469 27         27 HASH:
470             {
471 27         27 my $rest_pat;
472 27         26 my $any_key = 0;
473            
474 27         28 my %matched_keys;
475              
476 27         60 for my $k ( keys %$p ) {
477             # ANY in a pattern key matches any other elements.
478 29 100 66     372 if ( ! $self->{'disable_patterns'} &&
    100 33        
      66        
      100        
479             (
480             ($k =~ /^Data::Match::Pattern::ANY=/) # unless grep(ref $_, keys %hash)
481             || UNIVERSAL::isa($k, 'Data::Match::Pattern::ANY') # if grep($ref $_, keys %hash)
482             )) {
483 11 50       23 if ( ! $any_key ++ ) {
484 11         11 my $matches = 0;
485            
486 11         25 for my $xk ( keys %$x ) {
487 11         21 $self->_match_path_push('HASH', $xk);
488              
489 11         17 ++ $matched_keys{$xk};
490 11 100       112 ++ $matches if $self->_match($x->{$xk}, $p->{$k});
491            
492 11         26 $self->_match_path_pop;
493             }
494            
495             # Must have at least one match.
496             # { ANY => 'x' } does not match { }.
497 11   66     44 $match &&= $matches;
498             }
499             }
500            
501             # Rest in a pattern causes the rest to match.
502             elsif ( ! $self->{'disable_patterns'} &&
503             ! $rest_pat &&
504             UNIVERSAL::isa($p->{$k}, 'Data::Match::Pattern::REST')
505             ) {
506 3         6 $rest_pat = $p->{$k};
507             }
508              
509             else {
510             # Match the $x value for $k with the pattern value for $k.
511 15         31 $self->_match_path_push('HASH', $k);
512            
513             # If the key does not exist in pattern, no match.
514 15         27 ++ $matched_keys{$k};
515 15   100     77 $match &&= exists $x->{$k} && $self->_match($x->{$k}, $p->{$k});
      66        
516            
517 15         33 $self->_match_path_pop;
518             }
519              
520 29 100       92 last HASH unless $match;
521             }
522            
523             # Handle REST pattern's subpatterns.
524 12 100       37 if ( $rest_pat ) {
525 3   33     22 $match &&= $rest_pat->_match_REST_HASH($x, $p, $self,
526             # What keys in $x have not been matched against?
527             [ grep(! exists $matched_keys{$_}, keys %$x) ]
528             );
529             } else {
530             # Make sure they are the same length.
531 9 100 66     76 $match &&= (scalar values %$p) == (scalar values %$x) unless $any_key;
532             }
533             }
534              
535 27         62 $match;
536             }
537              
538              
539             #=head2 _match_SCALAR
540             #
541             #Internal recursive match routine. Assumes $matchobj is initialized.
542             #
543             #=cut
544             sub _match_SCALAR($$$)
545             {
546 0     0   0 my ($self, $x, $p) = @_;
547              
548 0         0 $self->_match_path_push('SCALAR', undef);
549              
550 0         0 my $match = $self->_match($$x, $$p);
551              
552 0         0 $self->_match_path_pop;
553              
554 0         0 $match;
555             };
556              
557              
558              
559             #=head2 _match_path_push
560             #
561             #Internal recursive match routine. Assumes $self is initialized.
562             #
563             #=cut
564             sub _match_path_push($$$)
565             {
566 673     673   758 my $self = shift;
567 673         966 ++ $self->{'depth'};
568 673         687 push(@{$self->{'path'}}, @_);
  673         2038  
569             }
570              
571              
572             #=head2 _match_path_pop
573             #
574             #Internal recursive match routine. Assumes $self is initialized.
575             #
576             #=cut
577             sub _match_path_pop
578             {
579 673     673   786 my $self = shift;
580             # $DB::single = 1;
581 673 50       1481 confess "too many _match_path_pop" unless $self->{'depth'} > 0;
582 673 50       629 confess "corrupted path" unless (@{$self->{'path'}} & 1) == 0;
  673         1681  
583 673         753 splice(@{$self->{'path'}}, -2);
  673         1226  
584 673         1648 -- $self->{'depth'};
585             }
586              
587              
588             #=head2 _match
589             #
590             #Internal recursive match routine. Assumes $self is initialized.
591             #
592             #=cut
593             sub _match
594             {
595 1603     1603   2268 my ($self, $x, $p) = @_;
596              
597 1603         1831 my $match = 0;
598              
599             # $DB::single = 1;
600              
601 4         2621 RESULT:
602             {
603 4     4   28 no warnings;
  4         7  
  1603         1611  
604              
605             # Is it simply the same?
606 1603 100 66     10459 if ( $x eq $p ) {
    100          
    100          
607 80         98 $match = 1;
608             }
609              
610             # Is pattern a pattern?
611             elsif ( ! $self->{'disable_patterns'} && UNIVERSAL::isa($p, 'Data::Match::Pattern') ) {
612             # Delegate match to pattern object.
613 1338         2719 $match = $p->match($x, $self);
614             }
615              
616             # Handle deep structures.
617             elsif ( ref($x) ) {
618             # Acquire visitation lock.
619 83 100 100     915 if ( $self->{'visiting'}{$x} ++ ) {
    50 66        
    100 33        
    100          
    50          
620 2         10 $match = Compare($x, $p);
621             }
622             # Class-specific visit adaptor?
623             elsif ( my $visit = $self->{'match'}{ref($x)} ) {
624             # $match = 1;
625             my $visitor = sub {
626 0   0 0   0 $match &&= $self->_match($_[0], $p); #should this be ||= or &&=?
627 0         0 };
628 0         0 $match = $visit->($x, $visitor);
629             }
630             # Array pattern template?
631             elsif ( UNIVERSAL::isa($x, 'ARRAY') && UNIVERSAL::isa($p, 'ARRAY') ) {
632 42         98 $match = $self->_match_ARRAY($x, $p);
633             }
634             # Hash pattern template?
635             elsif ( UNIVERSAL::isa($x, 'HASH') && UNIVERSAL::isa($p, 'HASH') ) {
636 27         134 $match = $self->_match_HASH($x, $p);
637             }
638             # Scalar ref pattern template?
639             elsif ( UNIVERSAL::isa($x, 'SCALAR') && UNIVERSAL::isa($p, 'SCALAR') ) {
640 0         0 $match = $self->_match_SCALAR($x, $p);
641             }
642             else {
643             # Extensible comparators?
644 12 50 50     43 if ( my $comparator = $self->{'compare'}{ref($x) || '*'} ) {
645             # Try a comparator.
646 0         0 $match = $comparator->($x, $p, $self);
647             } else {
648             # Default to eq.
649 12         29 $match = $x eq $p;
650             }
651             }
652             } else {
653             # Scalar eq.
654 102         141 $match = $x eq $p;
655             }
656              
657             # Release visitation lock.
658 1603         5319 -- $self->{'visiting'}{$x};
659             };
660              
661             #$DB::single = 1;
662              
663 1603         6693 $match;
664             }
665              
666              
667             =head2 %match_opts
668              
669             Default options for C.
670              
671             =cut
672             our %match_opts
673             = (
674             #'collect_path_DRef' => 1,
675             #'collect_path_str' => 0,
676             #'no_collect_path' => 1.
677             );
678              
679              
680             #=head2 _match_pre
681             #
682             #Initialize the match object before pattern traversal.
683             #
684             #=cut
685             sub _match_pre
686             {
687 118     118   166 my ($self, $x, $p, $opts) = @_;
688              
689             # Install opts.
690 118         240 @{$self}{keys %match_opts} = values %match_opts;
  118         332  
691 118 50       282 @{$self}{keys %$opts} = values %$opts if ( $opts );
  0         0  
692              
693             # Initialize state.
694 118   50     591 $self->{'depth'} ||= 0;
695 118   50     484 $self->{'visiting'} ||= { };
696 118   50     459 $self->{'path'} ||= [ ];
697 118   66     467 $self->{'root'} ||= $x;
698 118   66     445 $self->{'pattern'} ||= $p;
699 118   50     443 $self->{'_COLLECT'} ||= 'COLLECT';
700 118   50     422 $self->{'_BIND'} ||= 'BIND';
701            
702 118         194 $self;
703             }
704              
705              
706             #=head2 _match_post
707             #
708             #Initialize the match object before pattern traversal.
709             #
710             #=cut
711             sub _match_post
712             {
713 118     118   213 my ($self, $x, $p) = @_;
714              
715 118 50       558 delete $self->{'visiting'} unless $self->{'keep_visiting'};
716              
717             # Post conditions.
718             {
719 4     4   23 no warnings;
  4         6  
  4         8417  
  118         147  
720              
721 118 50       290 confess "Expected results->{depth} == 0, found $self->{depth}" unless $self->{'depth'} == 0;
722 118 50       121 confess "Expected results->{path} eq [ ]" unless ! @{$self->{'path'}};
  118         480  
723 118 50       364 confess "Expected results->{root} eq root" unless $self->{'root'} eq $x;
724 118 50       411 confess "Expected results->{pattern} eq pattern" unless $self->{'pattern'} eq $p;
725             }
726              
727 118         210 $self;
728             }
729              
730              
731             =head2 execute
732              
733             Matches a structure against a pattern. In a list context, returns both the match success and results; in a scalar context returns the results hash if match succeeded or undef.
734              
735             use Data::Match;
736             my $obj = new Data::Match();
737             my $matched = $obj->execute($thing, $pattern);
738              
739             =cut
740             sub execute
741             {
742 118     118 1 186 my ($self, $x, $p) = @_;
743              
744 118         266 $self->_match_pre($x, $p);
745 118         255 my $matches = $self->_match($x, $p);
746 118         376 $self->_match_post($x, $p);
747              
748             # Return results.
749 118 50       220 if ( wantarray ) {
750 118         1171 return ($matches, $self);
751             } else {
752 0 0       0 return $matches ? $self : undef;
753             }
754             }
755              
756              
757             =head2 match
758              
759             use Data::Match qw(match);
760             match($thing, $pattern, @opts)
761              
762             is equivalent to:
763              
764             use Data::Match;
765             Data::Match->new(@opts)->execute($thing, $pattern);
766              
767             =cut
768             sub match
769             {
770 118     118 1 3843 my ($x, $p, @opts) = @_;
771              
772 118         342 __PACKAGE__->new(@opts)->execute($x, $p);
773             }
774              
775              
776             =head2 matches
777              
778             Same as C in scalar context.
779              
780             =cut
781             sub matches
782             {
783 11     11 1 6621 my ($x, $p, @opts) = @_;
784              
785 11         41 my ($match, $results) = match($x, $p, @opts);
786              
787 11 50       89 $match ? $results : undef;
788             }
789              
790              
791              
792             #=head2 _match_state_save
793             #
794             #
795             #=cut
796             sub _match_state_save
797             {
798 18     18   20 my ($self) = @_;
799            
800 18         24 my $state = { };
801              
802 18         37 for my $x ( $self->{'_COLLECT'}, $self->{'_BIND'} ) {
803 36         46 my $c = $self->{$x};
804 36 50       84 next unless $c;
805 0         0 my $s = $state->{$x} = { };
806 0         0 for my $k ( keys %$c ) {
807 0 0       0 @{$s->{$k}{'v'}} = $c->{$k}{'v'} ? @{$c->{$k}{'v'}} : () ;
  0         0  
  0         0  
808 0 0       0 @{$s->{$x}{$k}{'p'}} = $c->{$k}{'p'} ? @{$c->{$k}{'p'}} : () ;
  0         0  
  0         0  
809 0 0       0 @{$s->{$x}{$k}{'ps'}} = $c->{$k}{'ps'} ? @{$c->{$k}{'ps'}} : () ;
  0         0  
  0         0  
810 0 0       0 @{$s->{$x}{$k}{'pdr'}} = $c->{$k}{'pdr'} ? @{$c->{$k}{'pdr'}} : () ;
  0         0  
  0         0  
811             }
812             }
813              
814 18         38 $state;
815             }
816              
817              
818             #=head2 _match_state_restore
819             #
820             #
821             #=cut
822             sub _match_state_restore
823             {
824 11     11   15 my ($self, $state) = @_;
825              
826 11         22 for my $x ( $self->{'_COLLECT'}, $self->{'_BIND'} ) {
827 22         26 my $c = $self->{$x};
828 22 50       56 next unless $c;
829 0         0 my $s = $state->{$x};
830 0         0 for my $k ( keys %$c ) {
831 0 0       0 if ( ! $s->{$k} ) {
832 0         0 undef $c->{$k};
833 0         0 next;
834             }
835 0 0       0 @{$c->{$k}{'v'}} = $s->{$k}{'v'} ? @{$s->{$k}{'v'}} : () ;
  0         0  
  0         0  
836 0 0       0 @{$c->{$x}{$k}{'p'}} = $s->{$k}{'p'} ? @{$s->{$k}{'p'}} : () ;
  0         0  
  0         0  
837 0 0       0 @{$c->{$x}{$k}{'ps'}} = $s->{$k}{'ps'} ? @{$s->{$k}{'ps'}} : () ;
  0         0  
  0         0  
838 0 0       0 @{$c->{$x}{$k}{'pdr'}} = $s->{$k}{'pdr'} ? @{$s->{$k}{'pdr'}} : () ;
  0         0  
  0         0  
839             }
840             }
841 11         19 $self;
842             }
843              
844             ##################################################
845             # Path support
846             #
847              
848              
849             # String::Escape::printable does not handle '$' and '@' interpolations
850             # in a qq{} context correctly.
851             sub qinterp
852             {
853            
854 16     16 0 100 my $x = shift;
855 16         21 $x =~ s/([\$\@])/\\$1/sgo;
856 16         66 $x;
857             }
858              
859              
860             # qprintable is conditional about putting '"' around strings
861             # printable is not conditional, so wrap it and throw in a join.
862             sub qqquote
863             {
864 16     16 0 50 join(',', map('"' . qinterp(printable($_)) . '"', @_));
865             }
866              
867              
868              
869             =head2 match_path_str
870              
871             Returns a perl expression that will generate code to point to the element of the path.
872              
873             $matchobj->match_path_str($path, $str);
874              
875             C<$str> defaults to C<'$_'>.
876              
877             =cut
878             sub match_path_str
879             {
880 150     150 1 1775 my ($matchobj, $path, $str) = @_;
881              
882 150 100       317 $str = '$_' unless defined $str;
883              
884             # $DB::single = ! ref $path;
885 150         344 my @path = @$path;
886              
887 150         320 while ( @path ) {
888 267         375 my $ref = shift @path;
889 267         336 my $ind = shift @path;
890              
891 267 100       623 if ( $ref eq 'ARRAY' ) {
    100          
    50          
    50          
892 186 100       297 if ( ref($ind) eq 'ARRAY' ) {
893             # Create a temporary array slice.
894 2         11 $str = "(Data::Match::Slice::Array->new($str,$ind->[0],$ind->[1]))";
895             } else {
896 184         603 $str .= "->[$ind]";
897             }
898             }
899             elsif ( $ref eq 'HASH' ) {
900 16 50       34 if ( ref($ind) eq 'ARRAY' ) {
901             # Create a temporary hash slice.
902 16         40 my $elems = qqquote(sort @$ind);
903 16         71 $str = "(Data::Match::Slice::Hash->new($str,[$elems]))";
904             } else {
905 0         0 $ind = qqquote($ind);
906 0         0 $str .= "->{$ind}";
907             }
908             }
909             elsif ( $ref eq 'SCALAR' ) {
910             # Maybe there is a better -> syntax?
911 0         0 $str = "(\${$str})";
912             }
913             elsif ( $ref eq 'METHOD' ) {
914 65 50       119 if ( ref($ind) eq 'ARRAY' ) {
915 0         0 my @args = @$ind;
916 0         0 my $method = shift @args;
917            
918 0         0 $str = $str . "->$method(" . qqquote(@args) . ')';
919             } else {
920 65         217 $str = $str . "->$ind()";
921             }
922             }
923             else {
924 0         0 $str = undef;
925             }
926             }
927              
928 150         351 $str;
929             }
930              
931              
932              
933             =head2 match_path_DRef_path
934              
935             Returns a string suitable for Data::DRef.
936              
937             $matchobj->match_path_DRef_path($path, $str, $sep);
938              
939             C<$str> is used as a prefix for the Data::DRef path.
940             C<$str> defaults to C<''>;
941             C<$sep> defaults to C<$Data::DRef::Separator> or C<'.'>;
942              
943             =cut
944             sub match_path_DRef_path
945             {
946 0     0 1 0 my ($matchobj, $path, $str, $sep) = @_;
947              
948 0 0       0 $str = '' unless defined $str;
949 0 0 0     0 $sep = ($Data::DRef::Separator || '.') unless defined $sep;
950              
951 0         0 my @path = @$path;
952              
953 0         0 while ( @path ) {
954 0         0 my $ref = shift @path;
955 0         0 my $ind = shift @path;
956              
957 0 0       0 if ( $ref eq 'ARRAY' ) {
    0          
    0          
    0          
958 0 0       0 if ( ref($ind) eq 'ARRAY' ) {
959             # Not supported by DRef.
960 0         0 $str .= $sep . '[' . $ind->[0] . '..' . ($ind->[1] - 1) . ']';
961             } else {
962 0         0 $str .= $sep . $ind;
963             }
964             }
965             elsif ( $ref eq 'HASH' ) {
966 0 0       0 if ( ref($ind) eq 'ARRAY' ) {
967             # Not supported by DRef.
968 0         0 $str .= $sep . '{' . join(',', @$ind->[0]) . '}';
969             } else {
970 0         0 $str .= $sep . $ind;
971             }
972             }
973             elsif ( $ref eq 'SCALAR' ) {
974             # Not supported by DRef.
975 0         0 $str .= $sep . '$'; #'emacs
976             }
977             elsif ( $ref eq 'METHOD' ) {
978             # Not supported by DRef.
979 0         0 confess "Ugh $ref";
980             }
981             else {
982             # Not supported by DRef.
983 0         0 confess "Ugh $ref";
984             }
985             }
986              
987 0         0 $str =~ s/^$sep//;
988              
989 0         0 $str;
990             }
991              
992              
993             =head2 match_path_get
994              
995             Returns the value pointing to the location for the match path in the root.
996              
997             $matchobj->match_path_get($path, $root);
998              
999             C<$root> defaults to C<$matchobj-C{'root'}>;
1000              
1001             Example:
1002              
1003             my $results = matches($thing, FIND(BIND('x', [ 'x', REST ])));
1004             my $x = $results->match_path_get($thing, $results->{'BIND'}{'x'}{'p'}[0]);
1005              
1006             The above example returns the first array that begins with C<'x'>.
1007              
1008             =cut
1009             sub match_path_get
1010             {
1011 55     55 1 190 my ($results, $path, $root) = @_;
1012              
1013 55         99 my $ps = $results->match_path_str($path, '$_[0]');
1014              
1015             # warn "ps = $ps" if ( 1 || $ps =~ /,/ );
1016              
1017 55         4375 my $pfunc = eval "sub { $ps; }";
1018 55 50       154 die "$@: $ps" if $@;
1019              
1020 55 50       172 $root = $results->{'root'} if ! defined $root;
1021              
1022 55         1312 $pfunc->($root);
1023             }
1024              
1025              
1026              
1027             =head2 match_path_set
1028              
1029             Returns the value pointing to the location for the match path in the root.
1030              
1031             $matchobj->match_path_set($path, $value, $root);
1032              
1033             C<$root> defaults to C<$matchobj-C{'root'}>;
1034              
1035             Example:
1036              
1037             my $results = matches($thing, FIND(BIND('x', [ 'x', REST ])));
1038             $results->match_path_set($thing, $results->{'BIND'}{'x'}{'p'}[0], 'y');
1039              
1040             The above example replaces the first array found that starts with 'x' with 'y';
1041              
1042             =cut
1043             sub match_path_set
1044             {
1045 0     0 1 0 my ($results, $path, $value, $root) = @_;
1046              
1047 0         0 my $ps = $results->match_path_str($path, '$_[0]');
1048              
1049             # warn "ps = $ps" if ( 1 || $ps =~ /,/ );
1050              
1051 0         0 my $pfunc = eval "sub { $ps = \$_[1]; }";
1052 0 0       0 die "$@: $ps" if $@;
1053              
1054 0 0       0 $root = $results->{'root'} if ! defined $root;
1055              
1056 0         0 $pfunc->($root, $value);
1057             }
1058              
1059              
1060             =head2 match_path_ref
1061              
1062             Returns a scalar ref pointing to the location for the match path in the root.
1063              
1064             $matchobj->match_path_ref($path, $root);
1065              
1066             C<$root> defaults to C<$matchobj-C{'root'}>;
1067              
1068             Example:
1069              
1070             my $results = matches($thing, FIND(BIND('x', [ 'x', REST ])));
1071             my $ref = $results->match_path_ref($thing, $results->{'BIND'}{'x'}{'p'}[0]);
1072             $$ref = 'y';
1073              
1074             The above example replaces the first array that starts with 'x' with 'y';
1075              
1076             =cut
1077             sub match_path_ref
1078             {
1079 0     0 1 0 my ($results, $path, $root) = @_;
1080              
1081 0         0 my $ps = $results->match_path_str($path, '$_[0]');
1082              
1083             # warn "ps = $ps" if ( 1 || $ps =~ /,/ );
1084              
1085 0         0 my $pfunc = eval "sub { \\{$ps}; }";
1086 0 0       0 die "$@: $ps" if $@;
1087              
1088 0 0       0 $root = $results->{'root'} if ! defined $root;
1089              
1090 0         0 $pfunc->($root);
1091             }
1092              
1093              
1094             ##################################################
1095              
1096              
1097             package Data::Match::Pattern;
1098              
1099 4     4   31 use Carp qw(confess);
  4         8  
  4         3637  
1100              
1101              
1102             sub new
1103             {
1104 261     261   568 my ($cls, @args) = @_;
1105             # $DB::single = 1;
1106 261         1132 (bless \@args, $cls)->initialize->_is_valid;
1107             }
1108              
1109              
1110 221     221   836 sub initialize { shift; }
1111              
1112              
1113             sub _is_valid
1114             {
1115 261     261   312 my $self = shift;
1116              
1117 261 50       822 confess("INVALID " . ref($self) . ": expected at least " . $self->subpattern_offset . " elements")
1118             unless @$self >= $self->subpattern_offset;
1119              
1120 261         6865 $self;
1121             }
1122              
1123              
1124 719     719   1765 sub subpattern_offset { 0; }
1125              
1126             sub match_and
1127             {
1128 1435     1435   2121 my ($self, $x, $matchobj) = @_;
1129              
1130 1435         2383 for my $i ( $self->subpattern_offset .. $#$self ) {
1131 1335 100       2895 return 0 unless $matchobj->_match($x, $self->[$i]);
1132             }
1133              
1134 358         1100 1;
1135             }
1136              
1137              
1138             sub match_or
1139             {
1140 24     24   31 my ($self, $x, $matchobj) = @_;
1141              
1142 24         42 for my $i ( $self->subpattern_offset .. $#$self ) {
1143 44 100       94 return 1 if $matchobj->_match($x, $self->[$i]);
1144             }
1145              
1146 15         37 0;
1147             }
1148              
1149              
1150             *match = \&match_and;
1151              
1152              
1153             ##################################################
1154              
1155              
1156             package Data::Match::Pattern::AND;
1157              
1158             our @ISA = qw(Data::Match::Pattern);
1159              
1160              
1161             ##################################################
1162              
1163              
1164             package Data::Match::Pattern::NOT;
1165              
1166             our @ISA = qw(Data::Match::Pattern);
1167              
1168             sub match
1169             {
1170 7     7   12 my ($self, $x, $matchobj) = @_;
1171              
1172             # $DB::single = 1;
1173 7 100       25 ! ((scalar @$self) ? $self->match_and($x, $matchobj) : $x);
1174             }
1175              
1176              
1177             ##################################################
1178              
1179              
1180             package Data::Match::Pattern::ANY;
1181              
1182             our @ISA = qw(Data::Match::Pattern);
1183              
1184             sub match
1185             {
1186 42     42   59 my ($self, $x, $matchobj) = @_;
1187              
1188             #$DB::single = 1;
1189             # ANY always matches.
1190              
1191 42 100       169 if ( @{$self} ) {
  42         84  
1192             # Do subpatterns.
1193 24         50 $self->match_or($x, $matchobj);
1194             } else {
1195 18         33 1;
1196             }
1197             }
1198              
1199              
1200             ##################################################
1201              
1202              
1203             package Data::Match::Pattern::COLLECT;
1204              
1205             #use Data::Match qw(match_path_str);
1206              
1207             our @ISA = qw(Data::Match::Pattern);
1208              
1209 612     612   1580 sub subpattern_offset { 1; };
1210              
1211 0     0   0 sub binding { $_[0]->[0]; };
1212              
1213             sub _collect
1214             {
1215 119     119   195 my ($self, $x, $matchobj, $binding) = @_;
1216              
1217 119         133 push(@{$binding->{'v'}}, $x );
  119         311  
1218              
1219 119         165 my $path = [ @{$matchobj->{'path'}} ];
  119         390  
1220              
1221 119 100       302 push(@{$binding->{'p'}}, $path)
  56         107  
1222             unless $matchobj->{'no_collect_path'};
1223              
1224 119 100       246 push(@{$binding->{'ps'}}, $matchobj->match_path_str($path))
  40         154  
1225             if ( $matchobj->{'collect_path_str'} );
1226              
1227 119 50       420 push(@{$binding->{'pdr'}}, $matchobj->match_path_DRef_path($path))
  0         0  
1228             if ( $matchobj->{'collect_path_DRef'} );
1229             }
1230              
1231              
1232             sub match
1233             {
1234 561     561   827 my ($self, $x, $matchobj) = @_;
1235              
1236             # warn "MATCH($self->[0])";
1237              
1238             # $DB::single = 1;
1239            
1240             # Do subpatterns.
1241 561 100       1018 return 0 unless $self->match_and($x, $matchobj);
1242              
1243 98   100     479 my $binding = $matchobj->{$matchobj->{'_COLLECT'}}{$self->[0]} ||= { };
1244              
1245 98         214 $self->_collect($x, $matchobj, $binding);
1246              
1247             #$DB::single = 1;
1248 98         171 1;
1249             }
1250              
1251              
1252             ##################################################
1253              
1254              
1255             package Data::Match::Pattern::BIND;
1256              
1257 4     4   29 use Data::Compare;
  4         9  
  4         38  
1258              
1259             our @ISA = qw(Data::Match::Pattern::COLLECT);
1260              
1261 51     51   120 sub subpattern_offset { 1; };
1262              
1263 0     0   0 sub binding { $_[0]->[0]; };
1264              
1265             sub match
1266             {
1267 25     25   35 my ($self, $x, $matchobj) = @_;
1268              
1269             # warn "MATCH($self->[0])";
1270              
1271             # $DB::single = 1;
1272              
1273             # Do subpatterns.
1274 25 100       59 return 0 unless $self->match_and($x, $matchobj);
1275              
1276 24         70 my $binding = $matchobj->{$matchobj->{'_BIND'}}{$self->[0]};
1277              
1278 24 100       53 if ( $binding ) {
1279             #$DB::single = 1;
1280 7 100       27 if ( Compare($binding->{'v'}[0], $x) ) {
1281 4         267 $self->_collect($x, $matchobj, $binding);
1282             } else {
1283 3         151 return 0;
1284             }
1285             } else {
1286 17         80 $self->_collect($x, $matchobj, $matchobj->{$matchobj->{'_BIND'}}{$self->[0]} = {});
1287             }
1288              
1289 21         37 1;
1290             }
1291              
1292              
1293             ##################################################
1294              
1295              
1296             package Data::Match::Pattern::REGEX;
1297              
1298             our @ISA = qw(Data::Match::Pattern);
1299              
1300 2     2   11 sub subpattern_offset { 1; };
1301              
1302             sub match
1303             {
1304 3     3   7 my ($self, $x, $matchobj) = @_;
1305              
1306             # $DB::single = 1;
1307            
1308             # Note: do not check that it is not a ref incase the object can be coerced into a string.
1309 3 100       62 ($x =~ /$self->[0]/sx) && $self->match_and($x, $matchobj);
1310             }
1311              
1312              
1313             ##################################################
1314              
1315              
1316             package Data::Match::Pattern::ISA;
1317              
1318             our @ISA = qw(Data::Match::Pattern);
1319              
1320 191     191   434 sub subpattern_offset { 1; };
1321              
1322             sub match
1323             {
1324 399     399   578 my ($self, $x, $matchobj) = @_;
1325              
1326 399 100       2081 UNIVERSAL::isa($x, $self->[0]) and $self->match_and($x, $matchobj);
1327             }
1328              
1329              
1330             ##################################################
1331              
1332              
1333             package Data::Match::Pattern::REF;
1334              
1335             our @ISA = qw(Data::Match::Pattern);
1336              
1337 0     0   0 sub subpattern_offset { 0; };
1338              
1339             sub match
1340             {
1341 0     0   0 my ($self, $x, $matchobj) = @_;
1342              
1343 0         0 $x = ref($x);
1344 0 0       0 $x && $self->match_and($x, $matchobj);
1345             }
1346              
1347              
1348             ##################################################
1349              
1350              
1351             package Data::Match::Pattern::DEPTH;
1352              
1353             our @ISA = qw(Data::Match::Pattern);
1354              
1355 16     16   37 sub subpattern_offset { 0; };
1356              
1357             sub match
1358             {
1359 15     15   27 my ($self, $x, $matchobj) = @_;
1360              
1361 15         20 $x = $matchobj->{'depth'};
1362              
1363 15         37 $self->match_and($x, $matchobj);
1364             }
1365              
1366              
1367             ##################################################
1368              
1369              
1370             package Data::Match::Pattern::LENGTH;
1371              
1372             our @ISA = qw(Data::Match::Pattern);
1373              
1374 15     15   38 sub subpattern_offset { 0; };
1375              
1376             sub match
1377             {
1378 12     12   17 my ($self, $x, $matchobj) = @_;
1379              
1380 4     4   9506 no warnings;
  4         9  
  4         632  
1381              
1382 12 100       22 if ( ref($x) ) {
1383 10 100       32 if ( UNIVERSAL::isa($x, 'ARRAY') ) {
    50          
    0          
1384 8         19 $x = @$x;
1385             }
1386             elsif ( UNIVERSAL::isa($x, 'HASH') ) {
1387 2         8 $x = %$x;
1388             }
1389             elsif ( UNIVERSAL::isa($x, 'SCALAR') ) {
1390 0 0       0 $x = $x ? 1 : 0;
1391             }
1392             else {
1393 0         0 $x = undef;
1394             }
1395             } else {
1396 2         3 $x = length $x;
1397             }
1398              
1399 12 100       42 @$self ? $self->match_and($x, $matchobj) : $x;
1400             }
1401              
1402              
1403             ##################################################
1404              
1405              
1406             package Data::Match::Pattern::EXPR;
1407              
1408 4     4   22 use Carp qw(confess);
  4         8  
  4         2337  
1409              
1410             our @ISA = qw(Data::Match::Pattern);
1411              
1412 103     103   294 sub subpattern_offset { 2; };
1413              
1414              
1415             sub initialize
1416             {
1417 29     29   46 my $self = shift;
1418              
1419             # $DB::single = 1;
1420              
1421             # Make room for EXPR sub.
1422 29         84 splice(@$self, 1, 0, 'UGH');
1423              
1424 29 100       118 if ( UNIVERSAL::isa($self->[0], 'CODE') ) {
1425 20         43 $self->[1] = $self->[0];
1426             } else {
1427 9         16 my $expr = $self->[0];
1428 9         641 $self->[1] = eval "sub { local \$_ = \$_[0]; $expr; }";
1429 9 50       29 confess "$@: $expr" if $@;
1430             }
1431              
1432 29         99 $self;
1433             }
1434              
1435              
1436             sub match
1437             {
1438 224     224   284 my ($self, $x, $matchobj) = @_;
1439              
1440             # $DB::single = 1;
1441              
1442 224 100       1871 $self->[1]->($x, $matchobj, $self) && $self->match_and($x, $matchobj);
1443             }
1444              
1445              
1446             ##################################################
1447              
1448              
1449             package Data::Match::Pattern::REST;
1450              
1451             our @ISA = qw(Data::Match::Pattern);
1452              
1453              
1454             sub match
1455             {
1456             # Should only match in an array or hash context.
1457 2     2   5 0;
1458             }
1459              
1460              
1461             sub _match_REST_ARRAY($$$$$$)
1462             {
1463 13     13   23 my ($self, $x, $p, $matchobj, $x_i, $p_i) = @_;
1464              
1465 13         14 my $match;
1466              
1467 13         39 $matchobj->_match_path_push('ARRAY', [$$x_i, scalar @$x]);
1468            
1469             # Create an new array slice to match the rest of the array.
1470             # The Slice::Array object will forward changes to
1471             # the real array.
1472 13         53 my $slice = Data::Match::Slice::Array->new($x, $$x_i, scalar @$x);
1473              
1474 13   66     66 $match = ref($x) && $self->match_and($slice, $matchobj);
1475              
1476 13         28 $matchobj->_match_path_pop;
1477              
1478             # Slurp up remaining $x and $p.
1479 13         19 $$x_i = $#$x;
1480 13         19 $$p_i = $#$p;
1481              
1482 13         65 $match;
1483             }
1484              
1485              
1486             sub _match_REST_HASH
1487             {
1488 3     3   4 my ($self, $x, $p, $matchobj, $rest_keys) = @_;
1489              
1490 3         8 $matchobj->_match_path_push('HASH', $rest_keys);
1491              
1492             # Create a temporary hash slice containing
1493             # the values from $x for all the unmatched keys.
1494 3         10 my $slice = Data::Match::Slice::Hash->new($x, $rest_keys);
1495              
1496             #$DB::single = 1;
1497 3         10 my $match = $self->match_and($slice, $matchobj);
1498            
1499 3         8 $matchobj->_match_path_pop;
1500              
1501 3         12 $match;
1502             }
1503              
1504              
1505             ##################################################
1506              
1507              
1508             package Data::Match::Pattern::RANG;
1509              
1510             our @ISA = qw(Data::Match::Pattern::REST);
1511              
1512              
1513 4     4   29 use Carp qw(confess);
  4         10  
  4         14557  
1514              
1515              
1516 20     20   34 sub subpattern_offset { 2; };
1517              
1518              
1519             sub initialize
1520             {
1521 11     11   13 my $self = shift;
1522              
1523 11 50       47 $self->[0] = 0 unless defined $self->[0];
1524              
1525 11         37 $self;
1526             }
1527              
1528              
1529             sub _match_REST_ARRAY
1530             {
1531 8     8   15 my ($self, $x, $p, $matchobj, $x_i, $p_i) = @_;
1532              
1533             # $DB::single = 1;
1534              
1535 8         10 my $count = 0;
1536              
1537 8         6 my ($match_sub, $match_rest);
1538 0         0 my $rest_saved_state;
1539              
1540 0         0 my $matched_rest;
1541              
1542             # Loop for until entire array is eaten,
1543             TRY:
1544 8         6 while ( 1 ) {
1545             # Save the match state for rollback after failure.
1546 9         20 my $saved_state = $matchobj->_match_state_save;
1547              
1548             # Try to match the subpattern.
1549             {
1550 9         13 my $sub_x_i = $$x_i;
  9         11  
1551 9         17 my $sub_p_i = $self->subpattern_offset;
1552 9         20 $match_sub = $matchobj->_match_ARRAY_REST($x, $self, \$sub_x_i, \$sub_p_i);
1553            
1554 9 100       20 if ( $match_sub ) {
1555 2         4 $$x_i = $sub_x_i;
1556             } else {
1557             # Restore match state if failed.
1558 7         18 $matchobj->_match_state_restore($saved_state);
1559             }
1560             }
1561              
1562             # Try to match rest of pattern.
1563 9         17 $saved_state = $matchobj->_match_state_save;
1564             {
1565 9         11 my $next_x_i = $$x_i;
  9         12  
1566 9         12 my $next_p_i = $$p_i + 1;
1567 9         20 $match_rest = $matchobj->_match_ARRAY_REST($x, $p, \$next_x_i, \$next_p_i);
1568             }
1569              
1570 9 100       16 if ( $match_rest ) {
1571 5         6 $matched_rest = $match_rest;
1572             } else {
1573             # Restore match state if failed.
1574 4         9 $matchobj->_match_state_restore($saved_state);
1575             }
1576              
1577             # Did it work?
1578 9 100 66     41 if ( $match_sub && $match_rest ) {
1579             # Increment the subpattern match count.
1580 2         3 ++ $count;
1581 2 100 66     12 last TRY if ( defined $self->[1] && $count >= $self->[1] );
1582             } else {
1583 7         14 last TRY;
1584             }
1585             }
1586              
1587             # If matched the correct number of things.
1588 8 100       18 if ( $self->[0] <= $count ) {
1589 6         10 $$p_i = $#$p;
1590 6         7 $$x_i = $#$x;
1591             } else {
1592 2         3 $matched_rest = 0;
1593             }
1594              
1595 8         28 $matched_rest;
1596             }
1597              
1598              
1599             sub match
1600             {
1601 0     0   0 my ($self, $x, $matchobj) = @_;
1602              
1603 0         0 confess "RE pattern must be used in ARRAY context";
1604             }
1605              
1606              
1607             ##################################################
1608              
1609             package Data::Match::Pattern::QUES;
1610              
1611             our @ISA = qw(Data::Match::Pattern::RANG);
1612              
1613             sub new
1614             {
1615 6     6   14 my ($self, @opts) = @_;
1616 6         24 $self->SUPER::new(0, 1, @opts);
1617             }
1618              
1619              
1620             ##################################################
1621              
1622             package Data::Match::Pattern::STAR;
1623              
1624             our @ISA = qw(Data::Match::Pattern::RANG);
1625              
1626             sub new
1627             {
1628 0     0   0 my ($self, @opts) = @_;
1629 0         0 $self->SUPER::new(0, undef, @opts);
1630             }
1631              
1632              
1633             ##################################################
1634              
1635             package Data::Match::Pattern::PLUS;
1636              
1637             our @ISA = qw(Data::Match::Pattern::RANG);
1638              
1639             sub new
1640             {
1641 5     5   12 my ($self, @opts) = @_;
1642 5         59 $self->SUPER::new(1, undef, @opts);
1643             }
1644              
1645              
1646             ##################################################
1647              
1648              
1649             package Data::Match::Pattern::EACH;
1650              
1651             our @ISA = qw(Data::Match::Pattern);
1652              
1653              
1654             sub _match_each_ARRAY
1655             {
1656 12     12   21 my ($self, $x, $matchobj, $matches) = @_;
1657              
1658 12         11 my $i = -1;
1659 12         33 for my $e ( @$x ) {
1660 41         69 $matchobj->_match_path_push('ARRAY', ++ $i);
1661              
1662 41 100       96 ++ $$matches if $self->match_and($e, $matchobj);
1663              
1664 41         83 $matchobj->_match_path_pop;
1665             }
1666             }
1667              
1668              
1669             sub _match_each_HASH
1670             {
1671 5     5   9 my ($self, $x, $matchobj, $matches) = @_;
1672              
1673 5         16 for my $k ( keys %$x ) {
1674 16         36 my @k = ( $k );
1675              
1676             # We compensate the path for hash slice.
1677 16         37 $matchobj->_match_path_push('HASH', \@k);
1678            
1679             # Create a temporary hash slice.
1680             # because we are matching EACH element of the hash.
1681 16         20 my $slice;
1682 16         16 if ( 1 ) {
1683 16         42 $slice = Data::Match::Slice::Hash->new($x, \@k);
1684             } else {
1685             $slice = { $k => $x->{$k} };
1686             }
1687              
1688 16 100       40 ++ $$matches if $self->match_and($slice, $matchobj);
1689            
1690 16         32 $matchobj->_match_path_pop;
1691             }
1692             }
1693              
1694              
1695             sub _match_each_SCALAR
1696             {
1697 0     0   0 my ($self, $x, $matchobj, $matches) = @_;
1698              
1699 0         0 $matchobj->_match_path_push('SCALAR', undef);
1700            
1701 0 0       0 ++ $$matches if $self->match_and($$x, $matchobj);
1702            
1703 0         0 $matchobj->_match_path_pop;
1704             }
1705              
1706              
1707             sub _match_each
1708             {
1709 17     17   24 my ($self, $x, $matchobj, $matches) = @_;
1710              
1711             # Traverse.
1712 17 50       43 if ( ref($x) ) {
1713 17 50       119 if ( my $eacher = $matchobj->{'each'}{ref($x)} ) {
    100          
    50          
    0          
1714 0 0   0   0 my $visitor = sub { ++ $$matches if ( $self->_match_and($_[0], $matchobj) ); };
  0         0  
1715 0         0 $eacher->($x, $visitor);
1716             }
1717             elsif ( UNIVERSAL::isa($x, 'ARRAY') ) {
1718 12         34 $self->_match_each_ARRAY($x, $matchobj, $matches);
1719             }
1720             elsif ( UNIVERSAL::isa($x, 'HASH') ) {
1721 5         17 $self->_match_each_HASH($x, $matchobj, $matches);
1722             }
1723             elsif ( UNIVERSAL::isa($x, 'SCALAR') ) {
1724 0         0 $self->_match_each_SCALAR($x, $matchobj, $matches);
1725             }
1726             else {
1727             # Try to match it explicitly.
1728 0 0       0 ++ $$matches if $self->match_and($x, $matchobj);
1729             }
1730             }
1731             }
1732              
1733              
1734             sub match
1735             {
1736 14     14   23 my ($self, $x, $matchobj) = @_;
1737              
1738 14         17 my $matches = 0;
1739              
1740 14         38 $self->_match_each($x, $matchobj, \$matches);
1741              
1742 14         35 $matches;
1743             }
1744              
1745              
1746             ##################################################
1747              
1748              
1749             package Data::Match::Pattern::ALL;
1750              
1751             our @ISA = qw(Data::Match::Pattern::EACH);
1752              
1753              
1754             sub match
1755             {
1756 3     3   5 my ($self, $x, $matchobj) = @_;
1757              
1758 3         5 my $matches = 0;
1759              
1760 3         3 my $expected = $self;
1761              
1762 3 50       10 if ( UNIVERSAL::isa($x, 'ARRAY') ) {
    0          
1763 3         6 $expected = scalar @$x;
1764             }
1765             elsif ( UNIVERSAL::isa($x, 'HASH') ) {
1766 0         0 $expected = scalar %$x;
1767             } else {
1768 0         0 $expected = -1;
1769             }
1770              
1771 3         11 $self->_match_each($x, $matchobj, \$matches);
1772              
1773 3         6 $matches == $expected;
1774             }
1775              
1776              
1777              
1778             ##################################################
1779              
1780              
1781             package Data::Match::Pattern::FIND;
1782              
1783             our @ISA = qw(Data::Match::Pattern);
1784              
1785              
1786             sub _match_find_ARRAY
1787             {
1788 104     104   198 my ($self, $x, $matchobj, $matches, $visited) = @_;
1789              
1790 104         119 my $i = -1;
1791 104         205 for my $e ( @$x ) {
1792 251         502 $matchobj->_match_path_push('ARRAY', ++ $i);
1793 251         569 $self->_match_find($e, $matchobj, $matches, $visited);
1794 251         1452 $matchobj->_match_path_pop;
1795             }
1796             }
1797              
1798              
1799             sub _match_find_HASH
1800             {
1801 3     3   7 my ($self, $x, $matchobj, $matches, $visited) = @_;
1802              
1803 3         10 for my $k ( keys %$x ) {
1804 4         12 $matchobj->_match_path_push('HASH', [ $k ]);
1805             # This needs a new Slice class.
1806 4         10 $self->_match_find($k, $matchobj, $matches); # HUH?
1807 4         9 $matchobj->_match_path_pop;
1808            
1809 4         8 $matchobj->_match_path_push('HASH', $k);
1810 4         12 $self->_match_find($x->{$k}, $matchobj, $matches, $visited);
1811 4         11 $matchobj->_match_path_pop;
1812             }
1813             }
1814              
1815              
1816             sub _match_find
1817             {
1818 510     510   784 my ($self, $x, $matchobj, $matches, $visited) = @_;
1819              
1820             # Does this match directly?
1821 510 100       923 ++ $$matches if ( $self->match_and($x, $matchobj) );
1822              
1823             # Traverse.
1824 510 100       1478 if ( ref($x) ) {
1825              
1826 272 100       837 return if ( $visited->{$x} ++ );
1827              
1828             # $DB::single = 1;
1829              
1830 267 100 66     1404 if ( my $visit = ($matchobj->{'find'}{ref($x)} || $matchobj->{'visit'}{ref($x)}) ) {
    100          
    50          
    0          
1831             my $visitor = sub {
1832 220     220   1670 my $thing = shift;
1833 220 50       701 $matchobj->_match_path_push(@_) if @_;
1834 220         535 $self->_match_find($thing, $matchobj, $matches, $visited);
1835 220 50       769 $matchobj->_match_path_pop if @_;
1836 160         696 };
1837 160         430 $visit->($x, $visitor, $matchobj);
1838             }
1839             elsif ( UNIVERSAL::isa($x, 'ARRAY') ) {
1840 104         272 $self->_match_find_ARRAY($x, $matchobj, $matches, $visited);
1841             }
1842             elsif ( UNIVERSAL::isa($x, 'HASH') ) {
1843 3         7 $self->_match_find_HASH($x, $matchobj, $matches, $visited);
1844             }
1845             elsif ( UNIVERSAL::isa($x, 'SCALAR') ) {
1846 0         0 $matchobj->_match_path_push('SCALAR', undef);
1847 0         0 $self->_match_find($$x, $matchobj, $matches, $visited);
1848 0         0 $matchobj->_match_path_pop;
1849             }
1850             else {
1851 0         0 warn "Huh?";
1852             }
1853             }
1854             }
1855              
1856              
1857             sub match
1858             {
1859 31     31   51 my ($self, $x, $matchobj) = @_;
1860              
1861 31         41 my $matches = 0;
1862              
1863 31         92 $self->_match_find($x, $matchobj, \$matches, { });
1864              
1865 31         185 $matches;
1866             }
1867              
1868              
1869             #################################################
1870              
1871              
1872             package Data::Match::Slice::Array;
1873              
1874             our $debug = 0;
1875              
1876             sub new
1877             {
1878 14     14   20 my $cls = shift;
1879 14         17 my @x;
1880 14         64 tie @x, $cls, @_;
1881 14         36 \@x;
1882             }
1883              
1884              
1885 0     0   0 sub _SLICE_SRC { $_[0][0]; }
1886 0     0   0 sub _SLICE_BEG { $_[0][1]; }
1887 0     0   0 sub _SLICE_END { $_[0][2]; }
1888              
1889              
1890             sub TIEARRAY
1891             {
1892 15     15   49 my ($cls, $src, $from, $to) = @_;
1893 15         26 $DB::single = $debug;
1894 15 50       48 die "$src must be ARRAY" unless UNIVERSAL::isa($src, 'ARRAY');
1895 15 50       42 $from = 0 unless defined $from;
1896 15 50       36 $to = @$src unless defined $to;
1897 15 50       37 die "slice must be $from <= $to" unless $from <= $to;
1898 15         67 bless [ $src, $from, $to ], $cls;
1899             }
1900              
1901             sub FETCH
1902             {
1903 9     9   48 my $i = $_[1];
1904 9         14 $DB::single = $debug;
1905 9 50       22 $i = FETCHSIZE($_[0]) - $i if $i < 0;
1906 9 100 66     30 0 <= $i && $i < FETCHSIZE($_[0])
1907             ? $_[0][0]->[$_[0][1] + $i]
1908             : undef;
1909             }
1910             sub STORE
1911             {
1912 1     1   2 $DB::single = $debug;
1913 1 50       4 STORESIZE($_[0], $_[1] + 1) if ( $_[1] >= $_[0][1] );
1914 1         5 $_[0][0]->[$_[0][1] + $_[1]] = $_[2];
1915             }
1916             sub FETCHSIZE
1917             {
1918 24     24   2214 $DB::single = $debug;
1919 24         143 $_[0][2] - $_[0][1];
1920             }
1921             sub STORESIZE
1922             {
1923 0     0   0 $DB::single = $debug;
1924 0 0       0 if ( $_[1] > FETCHSIZE($_[0]) ) {
1925 0         0 PUSH($_[0], (undef) x (FETCHSIZE($_[0]) - $_[1]));
1926             } else {
1927 0         0 SPLICE($_[0], 0, $_[1]);
1928             }
1929 0         0 $_[0][2] = $_[0][1] + $_[1];
1930             }
1931             sub POP
1932             {
1933 0     0   0 $DB::single = $debug;
1934 0 0       0 $_[0][2] > $_[0][1] ? splice(@{$_[0][0]}, -- $_[0][2], 1) : undef;
  0         0  
1935             }
1936             sub PUSH
1937             {
1938 1     1   2 my $s = shift;
1939 1         3 my $o = $s->[2];
1940 1         3 $s->[2] += scalar(@_);
1941 1         2 splice(@{$s->[0]}, $s->[2], $o, @_);
  1         121  
1942             }
1943             sub SHIFT
1944             {
1945 0     0   0 $DB::single = $debug;
1946 0         0 $_[0][1] < $_[0][2]
1947 0 0       0 ? splice(@{$_[0][0]}, $_[0][1] ++, 1)
1948             : undef;
1949             }
1950             sub UNSHIFT
1951             {
1952 0     0   0 $DB::single = $debug;
1953 0         0 my $s = shift;
1954 0         0 $_[0][2] += scalar @_;
1955 0         0 splice(@{$s->[0]}, $_[0][1], 0, @_);
  0         0  
1956             }
1957             sub SPLICE
1958             {
1959 0     0   0 $DB::single = $debug;
1960 0         0 my $s = shift;
1961 0         0 my $o = shift;
1962 0         0 my $l = shift;
1963 0         0 $_[0][2] += @_ - $l;
1964 0         0 splice(@{$_[0][0]}, $_[0][1] + $o, $l, @_);
  0         0  
1965             }
1966             sub DELETE
1967             {
1968 0     0   0 $DB::single = $debug;
1969 0 0 0     0 0 <= $_[1] && $_[1] < FETCHSIZE($_[0]) && delete $_[0][0][$_[0][1] + $_[1]];
1970             }
1971             sub EXTEND
1972             {
1973 0     0   0 $DB::single = $debug;
1974 0         0 $_[0][0];
1975             }
1976             sub EXISTS
1977             {
1978 0     0   0 $DB::single = $debug;
1979 0 0 0     0 0 <= $_[1] && $_[1] < FETCHSIZE($_[0]) && defined $_[0][0][$_[0][1] + $_[1]];
1980             }
1981              
1982              
1983             #########################################################################
1984              
1985              
1986             package Data::Match::Slice::Hash;
1987              
1988             our $debug = 0;
1989              
1990             sub new
1991             {
1992 27     27   35 my $cls = shift;
1993 27         28 my %x;
1994 27         101 tie %x, $cls, @_;
1995 27         119 \%x;
1996             }
1997              
1998              
1999             sub TIEHASH
2000             {
2001 28     28   77 my ($cls, $src, $keys) = @_;
2002 28         35 $DB::single = $debug;
2003 28 50       84 die "src $src must be a HASH" unless UNIVERSAL::isa($src, 'HASH');
2004 28 50       69 die "keys $keys must be an ARRAY" unless UNIVERSAL::isa($keys, 'ARRAY');
2005 28         194 bless [ $src, { map(($_, 1), @$keys) } ], $cls;
2006             }
2007              
2008              
2009             sub FETCH
2010             {
2011 50     50   313 $DB::single = $debug;
2012 50 100       193 $_[0][1]->{$_[1]} ? $_[0][0]->{$_[1]} : undef;
2013             }
2014             sub STORE
2015             {
2016 2     2   172 $DB::single = $debug;
2017 2         5 $_[0][1]->{$_[1]} = 1;
2018 2         13 $_[0][0]->{$_[1]} = $_[2];
2019             }
2020             sub DELETE
2021             {
2022 0     0   0 $DB::single = $debug;
2023 0 0       0 if ( exists $_[0][1]->{$_[1]} ) {
2024 0         0 delete $_[0][1]->{$_[1]};
2025 0         0 delete $_[0][0]->{$_[1]};
2026             }
2027             }
2028             sub CLEAR
2029             {
2030 0     0   0 $DB::single = $debug;
2031 0         0 for my $k ( keys %{$_[0][1]} ) {
  0         0  
2032 0         0 delete $_[0][0]->{$k}
2033             };
2034 0         0 %{$_[0][1]} = ();
  0         0  
2035             }
2036             sub EXISTS
2037             {
2038 7     7   11 $DB::single = $debug;
2039 7         61 exists $_[0][1]->{$_[1]};
2040             }
2041             sub FIRSTKEY
2042             {
2043 30     30   454 $DB::single = $debug;
2044 30         30 each %{$_[0][1]};
  30         133  
2045             }
2046             sub NEXTKEY
2047             {
2048 31     31   42 $DB::single = $debug;
2049 31         32 each %{$_[0][1]};
  31         109  
2050             }
2051              
2052              
2053              
2054             #########################################################################
2055              
2056             =head1 VERSION
2057              
2058             Version 0.05, $Revision: 1.12 $.
2059              
2060             =head1 AUTHOR
2061              
2062             Kurt A. Stephens
2063              
2064             =head1 COPYRIGHT
2065              
2066             Copyright (c) 2001, 2002 Kurt A. Stephens and ION, INC.
2067              
2068             =head1 SEE ALSO
2069              
2070             L, L, L, L, L, L.
2071              
2072             =cut
2073              
2074             ##################################################
2075              
2076             1;
2077              
2078             ### Keep these comments at end of file: kstephens@cpan.org 2001/12/28 ###
2079             ### Local Variables: ###
2080             ### mode:perl ###
2081             ### perl-indent-level:2 ###
2082             ### perl-continued-statement-offset:0 ###
2083             ### perl-brace-offset:0 ###
2084             ### perl-label-offset:0 ###
2085             ### End: ###