File Coverage

blib/lib/Regexp/Flow.pm
Criterion Covered Total %
statement 64 69 92.7
branch 24 34 70.5
condition 2 5 40.0
subroutine 10 11 90.9
pod 2 2 100.0
total 102 121 84.3


line stmt bran cond sub pod time code
1             package Regexp::Flow;
2 1     1   23845 use strict;
  1         2  
  1         34  
3 1     1   5 use warnings;
  1         1  
  1         26  
4 1     1   5 use Exporter 'import';
  1         16  
  1         48  
5             our @EXPORT_OK = qw(re_matches re_substitutions);
6 1     1   660 use Regexp::Flow::Result;
  1         4  
  1         43  
7 1     1   733 use Regexp::Flow::Results;
  1         3  
  1         908  
8              
9             =head1 NAME
10              
11             Regexp::Flow - flow control for using regular expression
12              
13             =cut
14              
15             our $VERSION = '0.002';
16              
17             =head1 SYNOPSIS
18              
19             use Regexp::Flow qw(re_matches re_substitutions);
20              
21             ...
22              
23             my $m_results =
24             re_matches ( $string, $re, $code, $flags );
25              
26             my $s_results =
27             re_substitutions ( $string, $re, $code, $flags );
28              
29             ...
30              
31             foreach (@$m_results) {
32             print $_->match; # assuming you used the /p flag
33             }
34              
35             re_matches ( ... ) or warn 'no matches';
36             # which is easier than using while, which doesn't allow or/else
37              
38             =head1 FUNCTIONS
39              
40             =cut
41              
42             =head3 re_matches
43              
44             my $results = re_matches ( $string, $re, $code, $flags );
45             my $results = re_matches ( $string, $re, $flags );
46             say $_->prematch for re_matches('1.23', qr/\D/p,''); #?
47              
48             Finds all instances of C<$re> within C and runs C<$code> each
49             time a match is found. A M object will be
50             created and passed as the first argument to C<$code>.
51              
52             If C<$flags> is not present, C will be assumed. If not, you must
53             include it yourself.
54              
55             If the third argument is a string, it will be used as the flags.
56             Otherwise, it will be executed as a coderef on the
57             M object, i.e. C<< $code->($rfr) >>
58              
59             Within C<$code>, you can call C on C<$rfr> to stop executing
60             C<$code> any more.
61              
62             Note: Remember you can use any of C on the regexp and do
63             not need to put these in C<$flags>.
64              
65             So, for instance, to print C<$1> the first time it contains a word
66             character you could do:
67              
68             my $code = sub {
69             my $rr = shift;
70             if ($rr->c(1) =~ /\w/) {
71             print $rr->c(1);
72             $rr->last;
73             }
74             }
75              
76             my $string = q{'', 'a', 'b'});
77              
78             re_matches ($string, qr/'([^']+)',?/, $code);
79              
80             The return value of C<$code> is discarded (this may change).
81              
82             In scalar context, the return value is a L
83             object (which evaluates to the number of times a match was found, and
84             allows access to each of the results contained within).
85              
86             In void context, this value is not returned.
87              
88             In list context, should it return each result?
89              
90             =cut
91              
92             sub re_matches {
93 5     5 1 1926 my $string = shift;
94 5         9 my $re = shift;
95 5         9 my $code = shift;
96 5         9 my $flags = 'g';
97 5 100       20 if (!ref $code) {
    100          
98 2 100       11 $flags = $code if defined $code;
99 2     1   197 $code = sub {};
  1         1  
100             }
101             elsif (@_) {
102 1   33     4 $flags = shift // $flags;
103             }
104              
105 5         8 my $results;
106 5 100       10 if (defined wantarray) {
107 3         50 $results = Regexp::Flow::Results->new;
108             }
109             my $action = sub {
110 9     9   19 my $rfr = shift;
111 9         71 $rfr->string($string);
112 9         22 $rfr->re($re);
113 9 100       27 if (defined $results) {
114 4         4 push @{$results->contents}, $rfr;
  4         9  
115             }
116 9         28 my $returnvalue = $code->($rfr);
117 9         252 $returnvalue;
118 5         37 };
119 5 50       23 die unless $flags =~ /^[a-z]+$/;
120 5 100       17 if ($flags =~ m/g/) {
121 4         495 eval qq`
122             while (\$string =~ m/\$re/$flags) {
123             my \$rfr = Regexp::Flow::Result->new;
124             \$action->(\$rfr);
125             last if 'last' eq \$rfr->continue_action;
126             }
127             `; #~ we use the string eval to put flags in there.
128             }
129             else {
130 1         67 eval qq`
131             \$action->(
132             Regexp::Flow::Result->new
133             ) if \$string =~ m/\$re/$flags;
134             `;
135             }
136 5 50       26 if ($@) {
137 0         0 die ($@);
138             }
139 5         32 return $results;
140             }
141              
142             =head3 re_substitutions
143              
144             my $results = re_substitutions ( $string, $re, $code, $flags );
145             my $results = re_substitutions ( $string, $re, $code );
146             my $results = re_substitutions ( $string, $re, $string );
147             my $results = re_substitutions ( $string, $re );
148              
149             Finds all instances of C<$re> within C<$string> and runs C<$code> each
150             time a match is found. A L object will be
151             created and passed as the first argument to C<$code>.
152             The return value of C<$code> is used as the replacement for the
153             matched string. If a string is passed as the third argument, it
154             (C<$string>) will be the replcement. Therefore B pass flags
155             as the third argument.
156              
157             Just like C, this makes changes to the source string, unless
158             the C flag is present, in which case the source string will be
159             untouched and the return value will be the modified string.
160              
161             If flags are not provided, C is assumed.
162              
163             =cut
164              
165             sub re_substitutions {
166 1     1 1 1060 my ($string, $re, $code, $flags) = @_; #~ we need to leave them in @_ to do in-place substitution
167 1 50       5 if (!ref $code) {
168 0     0   0 $code = sub {$code};
  0         0  
169             }
170 1   50     3 $flags //= 'g';
171 1 50       5 my $rflag = ($flags =~ /r/ ? 1 :0 );
172 1         1 my $results;
173 1 50       4 if (defined wantarray) {
174 1         27 $results = Regexp::Flow::Results->new;
175             }
176 1         5 my $last = 0;
177             my $action = sub {
178 3     3   3 my $rfr = shift;
179 3 50       14 if (defined $results) {
180 3         3 push @{$results->contents}, $rfr;
  3         7  
181             }
182 3         7 my $returnvalue = $code->($rfr);
183 3 100       22 $last = 1 if 'last' eq $rfr->continue_action;
184 3         65 $returnvalue;
185 1         5 };
186 1 50       5 die ('Unexpected flags [a-z] only permitted in '.$flags)
187             unless $flags =~ /^[a-z]+$/;
188             #~ In the following code, We will be using s~~~e
189 1         104 eval qq`
190             \$string =~ s~\$re~
191             my \$rfr = Regexp::Flow::Result->new;
192             \$rfr->string(\$string);
193             \$rfr->re(\$re);
194             if (!\$last) {
195             \$action->(\$rfr);
196             }
197             else {
198             \$rfr->match;
199             }
200             ~e$flags
201             `; #~ we use the string eval to put flags in there.
202 1 50       6 if ($@) {
203 0         0 warn ($@);
204             }
205 1 50       3 if ($rflag) {
206 0         0 return $string;
207             }
208             #~ implicit else
209 1 50       5 $_[0] = $string if $results;
210 1         6 return $results;
211             }
212              
213             =head1 SEE ALSO
214              
215             Regexp::Result - base class for information about a regexp match
216              
217             Regexp::Flow::Result - the class available within coderefs above
218              
219             Regexp::Flow::Results - the list of results returned by functions above
220              
221             =head1 BUGS
222              
223             Please report any bugs or feature requests to the github issues tracker
224             at L. I will be notified,
225             and then you'll automatically be notified of progress on your bug as I
226             make changes.
227              
228             =head1 AUTHORS
229              
230             Daniel Perrett
231              
232             =head1 LICENSE AND COPYRIGHT
233              
234             Copyright 2012-2013 Daniel Perrett.
235              
236             This program is free software; you can redistribute it and/or modify it
237             under the terms of either: the GNU General Public License as published
238             by the Free Software Foundation; or the Artistic License.
239              
240             See L for more information.
241              
242             =cut
243              
244             1;
245              
246              
247