File Coverage

blib/lib/Regexp/Parser.pm
Criterion Covered Total %
statement 201 225 89.3
branch 59 78 75.6
condition 14 20 70.0
subroutine 45 53 84.9
pod 2 32 6.2
total 321 408 78.6


line stmt bran cond sub pod time code
1             package Regexp::Parser;
2              
3 18     18   1903925 use strict;
  18         38  
  18         686  
4 18     18   175 use warnings;
  18         32  
  18         1380  
5              
6             our $VERSION = '0.26';
7              
8 18     18   352 use 5.006;
  18         61  
9 18     18   117 use Carp qw( carp croak );
  18         80  
  18         1306  
10 18     18   105 use base 'Exporter';
  18         34  
  18         2999  
11 18     18   102 use strict;
  18         29  
  18         440  
12 18     18   66 use warnings;
  18         44  
  18         788  
13 18     18   9448 use charnames ();
  18         181030  
  18         3449  
14              
15             our %loaded;
16             our @EXPORT = qw( Rx RxPOS RxCUR RxLEN Rf SIZE_ONLY LATEST );
17              
18 18413     18413 0 47308 sub Rx :lvalue { $_[0]{regex} }
19 3213     3213 0 3248 sub RxPOS :lvalue { pos ${&Rx} }
  3213         3945  
20 34     34 0 41 sub RxCUR { substr ${&Rx}, &RxPOS }
  34         42  
21 4361     4361 0 7683 sub RxLEN { $_[0]{len} }
22              
23 5857     5857 0 10990 sub Rf :lvalue { $_[0]{flags}[-1] }
24              
25 4941     4941 0 12717 sub SIZE_ONLY { ! $_[0]{tree} }
26 0     0 0 0 sub LATEST :lvalue { $_[0]{tree}[-1] }
27              
28 18     18   8926 use Regexp::Parser::Diagnostics;
  18         50  
  18         577  
29 18     18   10232 use Regexp::Parser::Objects;
  18         70  
  18         804  
30 18     18   214865 use Regexp::Parser::Handlers;
  18         97  
  18         29718  
31              
32              
33             # this handles 'use base "Regexp::Parser"'
34             # which wouldn't call 'import'
35             {
36             my ($level, $prev, $pkg);
37             while (my ($curr) = caller $level++) {
38             $pkg = $curr, last if $prev and $prev eq "base" and $curr ne "base";
39             $prev = $curr;
40             }
41             Regexp::Parser->export_to_level($level, $pkg, @EXPORT) if $pkg;
42             }
43              
44              
45             sub new {
46 258     258 1 3138133 my ($class, $rx) = @_;
47 258         764 my $self = bless {}, $class;
48 258         1273 $self->init;
49 258 100       730 $self->regex($rx) if defined $rx;
50 258         784 return $self;
51             }
52              
53              
54             sub regex {
55 442     442 1 69339 my ($self, $rx, $flags) = @_;
56 442         620 my $init_flags = 0;
57 442 100       865 if (defined $flags) {
58 7         22 for my $ch (split //, $flags) {
59 9         18 my $method = "FLAG_$ch";
60 9 50       39 if ($self->can($method)) {
61 9         28 my $v = $self->$method;
62             # /xx: if x is already on, set the xx bit (Perl 5.26+)
63 9 100 100     35 if ($ch eq 'x' && ($init_flags & $v)) {
64 1         2 $init_flags |= 0x200;
65             }
66 9         22 $init_flags |= $v;
67             }
68             }
69             }
70 442         6877 %$self = (
71             regex => \"$rx",
72             len => length $rx,
73             tree => undef,
74             stack => [],
75             maxpar => 0,
76             nparen => 0,
77             captures => [],
78             named_captures => {},
79             flags => [$init_flags],
80             next => ['atom'],
81             quotemeta => 0,
82             );
83              
84             # do the initial scan (populates maxpar)
85             # because tree is undef, nothing gets built
86 442         1080 &RxPOS = 0;
87 442         827 eval { $self->parse };
  442         960  
88 442 100       925 $self->{errmsg} = $@, return if $@;
89              
90             # reset things, define tree as []
91 417         565 &RxPOS = 0;
92 417         875 $self->{tree} = [];
93 417         935 $self->{flags} = [$init_flags];
94 417         843 $self->{next} = ['atom'];
95 417         624 $self->{quotemeta} = 0;
96              
97 417         1216 return 1;
98             }
99              
100              
101             sub parse {
102 853     853 0 1337 my ($self, $rx) = @_;
103 853 50 0     1509 $self->regex($rx) or return if defined $rx;
104 853 50       1342 croak "no regex defined" unless &RxLEN;
105 853         1765 1 while $self->next;
106 824         1262 return 1;
107             }
108              
109              
110             sub root {
111 55     55 0 177 my ($self) = @_;
112 55 100       149 $self->parse if $self->{stack};
113 55         272 return $self->{tree};
114             }
115              
116              
117             sub nparen {
118 2     2 0 5 my ($self) = @_;
119 2 50       6 $self->parse if $self->{stack};
120 2         8 return $self->{nparen};
121             }
122              
123              
124             sub captures {
125 1     1 0 4 my ($self, $n) = @_;
126 1 50       5 $self->parse if $self->{stack};
127 1 50       2 return $self->{captures}[--$n] if $n;
128 1         4 return $self->{captures};
129             }
130              
131              
132             sub named_captures {
133 1     1 0 3 my ($self, $name) = @_;
134 1 50       3 $self->parse if $self->{stack};
135 1 50       3 return $self->{named_captures}{$name} if $name;
136 1         3 return $self->{named_captures};
137             }
138              
139              
140             sub nchar {
141 6     6 0 11 my $self = shift;
142 6 50       39 return map chr(/^\^(\S)/ ? (64 ^ ord $1) : charnames::vianame($_)), @_;
143             }
144              
145              
146             sub error_is {
147 0     0 0 0 my ($self, $enum) = @_;
148 0   0     0 return $self->{errnum} && $self->{errnum} == $enum;
149             }
150              
151              
152             sub errmsg {
153 0     0 0 0 my ($self) = @_;
154 0         0 return $self->{errmsg};
155             }
156              
157              
158             sub errnum {
159 0     0 0 0 my ($self) = @_;
160 0         0 return $self->{errnum};
161             }
162              
163              
164             sub error {
165 29     29 0 78 my ($self, $enum, $err, @args) = @_;
166 29         48 $err .= "; marked by <-- HERE in m/%s <-- HERE %s/";
167 29         62 push @args, substr(${&Rx}, 0, &RxPOS), &RxCUR;
  29         40  
168 29         77 $self->{errnum} = $enum;
169 29         104 $self->{errmsg} = sprintf $err, @args;
170 29         4517 croak $self->{errmsg};
171             }
172              
173              
174             sub warn {
175 0     0 0 0 my ($self, $enum, $err, @args) = @_;
176 0         0 $err .= "; marked by <-- HERE in m/%s <-- HERE %s/";
177 0         0 push @args, substr(${&Rx}, 0, &RxPOS), &RxCUR;
  0         0  
178 0 0       0 carp sprintf $err, @args if &SIZE_ONLY;
179             }
180              
181              
182             sub awarn {
183 0     0 0 0 my ($self, $enum, $err, @args) = @_;
184 0         0 local $self->{tree};
185 0         0 $self->warn($enum, $err, @args);
186             }
187              
188              
189             sub next {
190 2666     2666 0 3418 my ($self) = @_;
191 2666 50       3323 croak "no regex defined" unless &RxLEN;
192              
193 2666         2934 while (my $try = pop @{ $self->{next} }) {
  6458         11756  
194 5633 100       10576 if (defined(my $r = $self->$try)) {
195 1815         4849 $r->insert($self->{tree});
196 1813         5949 return $r;
197             }
198             }
199              
200 825 100       898 $self->error(RPe_RPAREN) if ${&Rx} =~ m{ \G \) }xgc;
  825         1076  
201 824 50       1132 $self->error(0, "PANIC! %d %s", &RxPOS, &RxCUR) if &RxPOS != &RxLEN;
202              
203 824 100       1199 if (! &SIZE_ONLY) {
204 407         475 $self->{tree} = pop @{ $self->{stack} } while @{ $self->{stack} };
  416         920  
  9         18  
205 407         766 delete $self->{stack};
206             }
207              
208 824         1502 return;
209             }
210              
211              
212             sub walker {
213 18     18 0 2988 my ($self, $depth) = @_;
214 18 50       54 croak "no regex defined" unless &RxLEN;
215 18 100       104 $self->parse if $self->{stack};
216              
217 18 100       52 $depth = -1 unless defined $depth;
218 18         44 my $d = $depth;
219 18         28 my @stack = @{ $self->{tree} };
  18         60  
220 18         27 my $next;
221              
222             return sub {
223 136 100 66 136   90162 return $depth if @_ and $_[0] eq -depth;
224 132 50       306 carp "unexpected argument ($_[0]) to iterator" if @_;
225              
226             {
227 132         183 $next = shift @stack;
  242         692  
228 242 100       838 $d += $next->(), redo if ref($next) eq "CODE";
229             }
230              
231 132 100       290 @stack = @{ $self->{tree} }, return unless $next;
  14         84  
232 118         596 $next->walk(\@stack, $d);
233 118 100       587 return wantarray ? ($next, $depth-$d) : $next;
234             }
235 18         204 }
236              
237              
238             sub visual {
239 335     335 0 8188 my ($self) = @_;
240 335 50       1153 $self->parse if $self->{stack};
241 331         467 my $vis = join "", map($_->visual, @{ $self->{tree} });
  331         1143  
242 331         1054 return $vis;
243             }
244              
245              
246             sub qr {
247 31     31 0 630 my ($self) = @_;
248 31 100       128 $self->parse if $self->{stack};
249 31         78 my $rx = $self->{tree};
250 18     18   167 no warnings 'regexp';
  18         34  
  18         1433  
251 18     18   129 use re 'eval';
  18         48  
  18         9749  
252              
253 31 100 100     114 if (@$rx == 1 and $rx->[0]->family eq 'group') {
254 6         8 my $vis = join "", map $_->qr, @{ $rx->[0]->{data} };
  6         25  
255 6         17 my $flags = $rx->[0]->on;
256 6         20 $flags =~ s/^\^//; # strip caret prefix for qr// modifier
257 6         401 return eval('qr/$vis/' . $flags);
258             }
259              
260 25         105 $rx = join "", map($_->qr, @$rx);
261 25         646 return qr/$rx/;
262             }
263              
264              
265             sub nextchar {
266 4940     4940 0 5704 my ($self) = @_;
267              
268             {
269 4940 100       4813 if (${&Rx} =~ m{ \G \(\?\# [^)]* }xgc) {
  4956         4692  
  4956         5799  
270 4 50       5 ${&Rx} =~ m{ \G \) }xgc and redo;
  4         5  
271 0         0 $self->error(RPe_NOTERM);
272             }
273 4952 100 100     6468 &Rf & $self->FLAG_x and ${&Rx} =~ m{ \G (?: \s+ | \# .* )+ }xgc and redo;
  98         109  
274             }
275             }
276              
277              
278             sub object {
279 3654 100   3654 0 42540 return if &SIZE_ONLY;
280 1803         2244 my $self = shift;
281 1803         2963 $self->force_object(@_);
282             }
283              
284              
285             sub force_object {
286 2015 50   2015 0 2968 Carp::croak("class name passed where object required") unless ref $_[0];
287 2015         3072 my $type = splice @_, 1, 1;
288 2015         2448 my $ref = ref $_[0];
289 2015         2590 my $class = "${ref}::$type";
290              
291 2015 100 100     3618 if ($ref ne __PACKAGE__ and !$loaded{$class}++) {
292 18     18   138 no strict 'refs';
  18         50  
  18         4099  
293 11         35 my $orig_base = $Regexp::Parser::{$type . '::'};
294 11         14 my $user_base = ${"${ref}::"}{'__object__::'};
  11         31  
295              
296 11 50       15 push @{ "${class}::ISA" }, $ref . "::__object__" if $user_base;
  11         120  
297 11 100       39 push @{ "${class}::ISA" }, __PACKAGE__ . "::$type" if $orig_base;
  10         87  
298             }
299              
300 2015         6353 return $class->new(@_);
301             }
302              
303              
304             sub add_flag {
305 3096     3096 0 5076 my ($self, $seq, $func) = @_;
306 18     18   153 no strict 'refs';
  18         74  
  18         744  
307 18     18   114 no warnings 'redefine';
  18         49  
  18         2230  
308 3096         3668 *{ ref($self) . "::FLAG_$seq" } = $func;
  3096         13530  
309             }
310              
311              
312             sub del_flag {
313 0     0 0 0 my ($self, @flags) = @_;
314 18     18   103 no strict 'refs';
  18         37  
  18         2228  
315 0         0 my $stash = \%{ ref($self) . "::" };
  0         0  
316 0         0 undef $stash->{"FLAG_$_"} for @flags;
317             }
318              
319              
320             sub add_handler {
321 24772     24772 0 32315 my ($self, $seq, $func) = @_;
322 18     18   101 no strict 'refs';
  18         49  
  18         850  
323 18     18   92 no warnings 'redefine';
  18         31  
  18         1981  
324 24772         24191 *{ ref($self) . "::$seq" } = $func;
  24772         112903  
325             }
326              
327              
328             sub del_handler {
329 0     0 0   my ($self, @handles) = @_;
330 18     18   97 no strict 'refs';
  18         32  
  18         2065  
331 0           my $stash = \%{ ref($self) . "::" };
  0            
332 0           undef $stash->{$_} for @handles;
333             }
334              
335              
336             1;
337              
338             __END__