File Coverage

blib/lib/Regexp/Exhaustive.pm
Criterion Covered Total %
statement 61 61 100.0
branch 22 22 100.0
condition 5 6 83.3
subroutine 10 10 100.0
pod 1 1 100.0
total 99 100 99.0


line stmt bran cond sub pod time code
1             package Regexp::Exhaustive;
2 8     8   8161 use 5.006001;
  8         33  
  8         504  
3              
4             $VERSION = 0.04;
5              
6 8     8   47 use base 'Exporter';
  8         16  
  8         1008  
7             @EXPORT_OK = qw/ exhaustive /;
8             $EXPORT_TAGS{ALL} = \@EXPORT_OK;
9              
10 8     8   57 use strict;
  8         17  
  8         284  
11 8     8   44 use Carp;
  8         15  
  8         730  
12 8     8   52 use Scalar::Util qw/ blessed /;
  8         23  
  8         776  
13 8     8   49 use warnings ();
  8         20  
  8         176  
14              
15 8     8   37 use vars qw/ $Vars $Count @Matches /;
  8         13  
  8         5006  
16              
17             my %Translate = (
18             q[$`] => 'substr($$str_ref, 0, $-[0])',
19             q[$&] => 'substr($$str_ref, $-[0], $+[0] - $-[0])',
20             q[$'] => 'substr($$str_ref, $+[0])',
21             q[$+] => '$+',
22             q[$^N] => '$^N',
23             q[@-] => '[ @- ]',
24             q[@+] => '[ @+ ]',
25             q[%-] => '{ %- }',
26             q[%+] => '{ %+ }',
27             q[$^R] => '$^R',
28             );
29              
30             my %Aliases = qw/
31             $PREMATCH $`
32             $MATCH $&
33             $POSTMATCH $'
34             $LAST_PAREN_MATCH $+
35             $LAST_REGEXP_CODE_RESULT $^R
36             @LAST_MATCH_START @-
37             @LAST_MATCH_END @+
38             /;
39              
40             my $Unique = 0;
41             sub exhaustive {
42 43     43 1 37816 my $str_ref = \shift;
43 43         95 my $re = shift;
44 43         85 my @vars = @_;
45              
46 43 100       136 defined $$str_ref
47 1         310 or warnings::warnif(uninitialized => "Use of uninitialized value in &@{[__PACKAGE__]}::exhaustive");
48              
49 42 100 66     466 blessed($re) && $re->isa('Regexp')
50 1         237 or croak("The second argument to &@{[__PACKAGE__]}::exhaustive must be a Regexp object (qr//)");
51              
52 41         100 for (@vars) {
53             defined
54 38 100       71 or croak("Uninitialized value passed to &@{[__PACKAGE__]}::exhaustive as variable name");
  1         161  
55              
56 37 100       114 $_ = $Aliases{$_} if exists $Aliases{$_};
57             }
58              
59 40 100 100     93 if (my @bad = grep { not $Translate{$_} || /^\$[1-9]\d?\z/ } @vars) {
  37         212  
60 2         8 $_ = qq{"$_"} for @bad;
61 2 100       8 my $s = @bad >= 2 ? 's' : '';
62 2         4 croak("Bad variable name$s to &@{[__PACKAGE__]}::exhaustive: " . join ', ', @bad);
  2         366  
63             }
64              
65 38         44 local $Vars;
66 38         52 local $Count = 0;
67 38         65 local @Matches;
68 38         40 my $save_match;
69             {
70 38 100       46 if (wantarray) {
  38         79  
71 21 100       47 if (@vars) {
72 11 100       17 my $vars = join ',', map { $Translate{$_} || $_ } @vars;
  20         76  
73 11 100       50 $vars = "[ $vars ]" if @vars > 1;
74              
75 8     8   56 use re 'eval';
  8         14  
  8         711  
76 11         28 my $pattern = "(?{push \@Matches, $vars})"; # So $vars interpolates.
77 11         1586 $save_match = qr/$pattern/;
78             }
79             else {
80 8     8   43 no warnings 'closure';
  8         25  
  8         2883  
81             $save_match = qr/
82             (?{
83             $Vars ||= $#+
84             ? eval 'sub { \\@_ }->(' . join(',', map "\$$_", 1 .. $#+) .')'
85             : []
86             ;
87             push @Matches,
88             @$Vars == 1 ? $Vars->[0] :
89             @$Vars > 1 ? [ @$Vars ] :
90             substr($$str_ref, $-[0], $+[0] - $-[0])
91             ;
92             })
93 10         65 /x;
94             }
95             }
96             else {
97 17         111 $save_match = qr/(?{$Count++})/;
98             }
99             }
100              
101 38         79 $Unique++;
102 38         1559 $$str_ref =~ /
103             (?:$Unique){0} # I don't understand why $Unique is needed to
104             $re # make qr[.*] work. Do you?
105             $save_match
106             (?!)
107             /x;
108              
109 37 100       311 return wantarray ? @Matches : $Count;
110             }
111              
112             1;
113              
114             __END__