File Coverage

blib/lib/Regexp/Parser.pm
Criterion Covered Total %
statement 200 224 89.2
branch 58 78 74.3
condition 13 20 65.0
subroutine 45 53 84.9
pod 2 32 6.2
total 318 407 78.1


line stmt bran cond sub pod time code
1             package Regexp::Parser;
2              
3 16     16   1862611 use strict;
  16         57  
  16         631  
4 16     16   174 use warnings;
  16         30  
  16         1254  
5              
6             our $VERSION = '0.25';
7              
8 16     16   391 use 5.006;
  16         58  
9 16     16   105 use Carp qw( carp croak );
  16         55  
  16         1289  
10 16     16   596 use base 'Exporter';
  16         31  
  16         2689  
11 16     16   94 use strict;
  16         29  
  16         453  
12 16     16   63 use warnings;
  16         39  
  16         709  
13 16     16   9205 use charnames ();
  16         191816  
  16         3567  
14              
15             our %loaded;
16             our @EXPORT = qw( Rx RxPOS RxCUR RxLEN Rf SIZE_ONLY LATEST );
17              
18 16507     16507 0 54379 sub Rx :lvalue { $_[0]{regex} }
19 2933     2933 0 3967 sub RxPOS :lvalue { pos ${&Rx} }
  2933         4269  
20 34     34 0 45 sub RxCUR { substr ${&Rx}, &RxPOS }
  34         53  
21 3769     3769 0 8336 sub RxLEN { $_[0]{len} }
22              
23 5391     5391 0 12509 sub Rf :lvalue { $_[0]{flags}[-1] }
24              
25 4361     4361 0 14439 sub SIZE_ONLY { ! $_[0]{tree} }
26 0     0 0 0 sub LATEST :lvalue { $_[0]{tree}[-1] }
27              
28 16     16   8549 use Regexp::Parser::Diagnostics;
  16         48  
  16         496  
29 16     16   9953 use Regexp::Parser::Objects;
  16         69  
  16         747  
30 16     16   212633 use Regexp::Parser::Handlers;
  16         76  
  16         28582  
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 231     231 1 3419545 my ($class, $rx) = @_;
47 231         599 my $self = bless {}, $class;
48 231         1151 $self->init;
49 231 100       836 $self->regex($rx) if defined $rx;
50 231         790 return $self;
51             }
52              
53              
54             sub regex {
55 381     381 1 65048 my ($self, $rx, $flags) = @_;
56 381         742 my $init_flags = 0;
57 381 100       939 if (defined $flags) {
58 7         45 for my $ch (split //, $flags) {
59 9         16 my $method = "FLAG_$ch";
60 9 50       60 if ($self->can($method)) {
61 9         24 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         4 $init_flags |= 0x200;
65             }
66 9         52 $init_flags |= $v;
67             }
68             }
69             }
70 381         6927 %$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             );
82              
83             # do the initial scan (populates maxpar)
84             # because tree is undef, nothing gets built
85 381         1207 &RxPOS = 0;
86 381         863 eval { $self->parse };
  381         1031  
87 381 100       1001 $self->{errmsg} = $@, return if $@;
88              
89             # reset things, define tree as []
90 356         608 &RxPOS = 0;
91 356         977 $self->{tree} = [];
92 356         1156 $self->{flags} = [$init_flags];
93 356         961 $self->{next} = ['atom'];
94              
95 356         1446 return 1;
96             }
97              
98              
99             sub parse {
100 731     731 0 1400 my ($self, $rx) = @_;
101 731 50 0     1591 $self->regex($rx) or return if defined $rx;
102 731 50       1372 croak "no regex defined" unless &RxLEN;
103 731         2128 1 while $self->next;
104 702         1352 return 1;
105             }
106              
107              
108             sub root {
109 41     41 0 150 my ($self) = @_;
110 41 100       134 $self->parse if $self->{stack};
111 41         134 return $self->{tree};
112             }
113              
114              
115             sub nparen {
116 2     2 0 6 my ($self) = @_;
117 2 50       6 $self->parse if $self->{stack};
118 2         8 return $self->{nparen};
119             }
120              
121              
122             sub captures {
123 1     1 0 3 my ($self, $n) = @_;
124 1 50       5 $self->parse if $self->{stack};
125 1 50       3 return $self->{captures}[--$n] if $n;
126 1         3 return $self->{captures};
127             }
128              
129              
130             sub named_captures {
131 1     1 0 4 my ($self, $name) = @_;
132 1 50       4 $self->parse if $self->{stack};
133 1 50       2 return $self->{named_captures}{$name} if $name;
134 1         2 return $self->{named_captures};
135             }
136              
137              
138             sub nchar {
139 6     6 0 8 my $self = shift;
140 6 50       37 return map chr(/^\^(\S)/ ? (64 ^ ord $1) : charnames::vianame($_)), @_;
141             }
142              
143              
144             sub error_is {
145 0     0 0 0 my ($self, $enum) = @_;
146 0   0     0 return $self->{errnum} && $self->{errnum} == $enum;
147             }
148              
149              
150             sub errmsg {
151 0     0 0 0 my ($self) = @_;
152 0         0 return $self->{errmsg};
153             }
154              
155              
156             sub errnum {
157 0     0 0 0 my ($self) = @_;
158 0         0 return $self->{errnum};
159             }
160              
161              
162             sub error {
163 29     29 0 86 my ($self, $enum, $err, @args) = @_;
164 29         148 $err .= "; marked by <-- HERE in m/%s <-- HERE %s/";
165 29         49 push @args, substr(${&Rx}, 0, &RxPOS), &RxCUR;
  29         59  
166 29         104 $self->{errnum} = $enum;
167 29         151 $self->{errmsg} = sprintf $err, @args;
168 29         5397 croak $self->{errmsg};
169             }
170              
171              
172             sub warn {
173 0     0 0 0 my ($self, $enum, $err, @args) = @_;
174 0         0 $err .= "; marked by <-- HERE in m/%s <-- HERE %s/";
175 0         0 push @args, substr(${&Rx}, 0, &RxPOS), &RxCUR;
  0         0  
176 0 0       0 carp sprintf $err, @args if &SIZE_ONLY;
177             }
178              
179              
180             sub awarn {
181 0     0 0 0 my ($self, $enum, $err, @args) = @_;
182 0         0 local $self->{tree};
183 0         0 $self->warn($enum, $err, @args);
184             }
185              
186              
187             sub next {
188 2319     2319 0 3636 my ($self) = @_;
189 2319 50       3744 croak "no regex defined" unless &RxLEN;
190              
191 2319         3256 while (my $try = pop @{ $self->{next} }) {
  5644         14628  
192 4941 100       12279 if (defined(my $r = $self->$try)) {
193 1590         5299 $r->insert($self->{tree});
194 1588         7558 return $r;
195             }
196             }
197              
198 703 100       972 $self->error(RPe_RPAREN) if ${&Rx} =~ m{ \G \) }xgc;
  703         1119  
199 702 50       1205 $self->error(0, "PANIC! %d %s", &RxPOS, &RxCUR) if &RxPOS != &RxLEN;
200              
201 702 100       1169 if (! &SIZE_ONLY) {
202 346         533 $self->{tree} = pop @{ $self->{stack} } while @{ $self->{stack} };
  355         916  
  9         28  
203 346         809 delete $self->{stack};
204             }
205              
206 702         1629 return;
207             }
208              
209              
210             sub walker {
211 17     17 0 2844 my ($self, $depth) = @_;
212 17 50       54 croak "no regex defined" unless &RxLEN;
213 17 100       86 $self->parse if $self->{stack};
214              
215 17 100       44 $depth = -1 unless defined $depth;
216 17         33 my $d = $depth;
217 17         27 my @stack = @{ $self->{tree} };
  17         60  
218 17         28 my $next;
219              
220             return sub {
221 134 100 66 134   99455 return $depth if @_ and $_[0] eq -depth;
222 130 50       291 carp "unexpected argument ($_[0]) to iterator" if @_;
223              
224             {
225 130         195 $next = shift @stack;
  240         685  
226 240 100       900 $d += $next->(), redo if ref($next) eq "CODE";
227             }
228              
229 130 100       283 @stack = @{ $self->{tree} }, return unless $next;
  13         108  
230 117         620 $next->walk(\@stack, $d);
231 117 50       570 return wantarray ? ($next, $depth-$d) : $next;
232             }
233 17         231 }
234              
235              
236             sub visual {
237 291     291 0 2201 my ($self) = @_;
238 291 50       1298 $self->parse if $self->{stack};
239 287         456 my $vis = join "", map($_->visual, @{ $self->{tree} });
  287         1277  
240 287         1409 return $vis;
241             }
242              
243              
244             sub qr {
245 29     29 0 909 my ($self) = @_;
246 29 100       91 $self->parse if $self->{stack};
247 29         61 my $rx = $self->{tree};
248 16     16   163 no warnings 'regexp';
  16         36  
  16         1320  
249 16     16   103 use re 'eval';
  16         32  
  16         9654  
250              
251 29 100 66     116 if (@$rx == 1 and $rx->[0]->family eq 'group') {
252 6         10 my $vis = join "", map $_->qr, @{ $rx->[0]->{data} };
  6         36  
253 6         19 my $flags = $rx->[0]->on;
254 6         25 $flags =~ s/^\^//; # strip caret prefix for qr// modifier
255 6         514 return eval('qr/$vis/' . $flags);
256             }
257              
258 23         96 $rx = join "", map($_->qr, @$rx);
259 23         670 return qr/$rx/;
260             }
261              
262              
263             sub nextchar {
264 4506     4506 0 7798 my ($self) = @_;
265              
266             {
267 4506 100       5849 if (${&Rx} =~ m{ \G \(\?\# [^)]* }xgc) {
  4522         5377  
  4522         7138  
268 4 50       4 ${&Rx} =~ m{ \G \) }xgc and redo;
  4         7  
269 0         0 $self->error(RPe_NOTERM);
270             }
271 4518 100 100     7439 &Rf & $self->FLAG_x and ${&Rx} =~ m{ \G (?: \s+ | \# .* )+ }xgc and redo;
  98         160  
272             }
273             }
274              
275              
276             sub object {
277 3222 100   3222 0 46974 return if &SIZE_ONLY;
278 1587         2658 my $self = shift;
279 1587         3436 $self->force_object(@_);
280             }
281              
282              
283             sub force_object {
284 1781 50   1781 0 3377 Carp::croak("class name passed where object required") unless ref $_[0];
285 1781         3410 my $type = splice @_, 1, 1;
286 1781         2806 my $ref = ref $_[0];
287 1781         2875 my $class = "${ref}::$type";
288              
289 1781 100 100     4097 if ($ref ne __PACKAGE__ and !$loaded{$class}++) {
290 16     16   139 no strict 'refs';
  16         41  
  16         3698  
291 11         49 my $orig_base = $Regexp::Parser::{$type . '::'};
292 11         15 my $user_base = ${"${ref}::"}{'__object__::'};
  11         63  
293              
294 11 50       26 push @{ "${class}::ISA" }, $ref . "::__object__" if $user_base;
  11         168  
295 11 100       27 push @{ "${class}::ISA" }, __PACKAGE__ . "::$type" if $orig_base;
  10         162  
296             }
297              
298 1781         6849 return $class->new(@_);
299             }
300              
301              
302             sub add_flag {
303 2772     2772 0 4631 my ($self, $seq, $func) = @_;
304 16     16   108 no strict 'refs';
  16         58  
  16         671  
305 16     16   96 no warnings 'redefine';
  16         59  
  16         1998  
306 2772         3540 *{ ref($self) . "::FLAG_$seq" } = $func;
  2772         12086  
307             }
308              
309              
310             sub del_flag {
311 0     0 0 0 my ($self, @flags) = @_;
312 16     16   117 no strict 'refs';
  16         40  
  16         2222  
313 0         0 my $stash = \%{ ref($self) . "::" };
  0         0  
314 0         0 undef $stash->{"FLAG_$_"} for @flags;
315             }
316              
317              
318             sub add_handler {
319 21718     21718 0 34074 my ($self, $seq, $func) = @_;
320 16     16   103 no strict 'refs';
  16         35  
  16         672  
321 16     16   80 no warnings 'redefine';
  16         30  
  16         1954  
322 21718         25972 *{ ref($self) . "::$seq" } = $func;
  21718         126217  
323             }
324              
325              
326             sub del_handler {
327 0     0 0   my ($self, @handles) = @_;
328 16     16   95 no strict 'refs';
  16         29  
  16         1961  
329 0           my $stash = \%{ ref($self) . "::" };
  0            
330 0           undef $stash->{$_} for @handles;
331             }
332              
333              
334             1;
335              
336             __END__