File Coverage

blib/lib/Regexp/Parser.pm
Criterion Covered Total %
statement 197 221 89.1
branch 56 76 73.6
condition 10 17 58.8
subroutine 45 53 84.9
pod 2 32 6.2
total 310 399 77.6


line stmt bran cond sub pod time code
1             package Regexp::Parser;
2              
3 14     14   1379025 use strict;
  14         32  
  14         605  
4 14     14   170 use warnings;
  14         30  
  14         1117  
5              
6             our $VERSION = '0.24';
7              
8 14     14   268 use 5.006;
  14         57  
9 14     14   89 use Carp qw( carp croak );
  14         55  
  14         1190  
10 14     14   97 use base 'Exporter';
  14         42  
  14         2513  
11 14     14   134 use strict;
  14         55  
  14         458  
12 14     14   64 use warnings;
  14         23  
  14         705  
13 14     14   8544 use charnames ();
  14         155303  
  14         3077  
14              
15             our %loaded;
16             our @EXPORT = qw( Rx RxPOS RxCUR RxLEN Rf SIZE_ONLY LATEST );
17              
18 13086     13086 0 43530 sub Rx :lvalue { $_[0]{regex} }
19 2369     2369 0 2951 sub RxPOS :lvalue { pos ${&Rx} }
  2369         3473  
20 34     34 0 44 sub RxCUR { substr ${&Rx}, &RxPOS }
  34         49  
21 3025     3025 0 6532 sub RxLEN { $_[0]{len} }
22              
23 4273     4273 0 10354 sub Rf :lvalue { $_[0]{flags}[-1] }
24              
25 3529     3529 0 11869 sub SIZE_ONLY { ! $_[0]{tree} }
26 0     0 0 0 sub LATEST :lvalue { $_[0]{tree}[-1] }
27              
28 14     14   7350 use Regexp::Parser::Diagnostics;
  14         41  
  14         474  
29 14     14   8607 use Regexp::Parser::Objects;
  14         62  
  14         616  
30 14     14   169971 use Regexp::Parser::Handlers;
  14         60  
  14         25613  
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 229     229 1 2924928 my ($class, $rx) = @_;
47 229         692 my $self = bless {}, $class;
48 229         1040 $self->init;
49 229 100       724 $self->regex($rx) if defined $rx;
50 229         697 return $self;
51             }
52              
53              
54             sub regex {
55 307     307 1 21067 my ($self, $rx, $flags) = @_;
56 307         501 my $init_flags = 0;
57 307 100       683 if (defined $flags) {
58 6         15 for my $ch (split //, $flags) {
59 7         9 my $method = "FLAG_$ch";
60 7 50       21 if ($self->can($method)) {
61 7         15 $init_flags |= $self->$method;
62             }
63             }
64             }
65 307         4687 %$self = (
66             regex => \"$rx",
67             len => length $rx,
68             tree => undef,
69             stack => [],
70             maxpar => 0,
71             nparen => 0,
72             captures => [],
73             named_captures => {},
74             flags => [$init_flags],
75             next => ['atom'],
76             );
77              
78             # do the initial scan (populates maxpar)
79             # because tree is undef, nothing gets built
80 307         866 &RxPOS = 0;
81 307         662 eval { $self->parse };
  307         721  
82 307 100       737 $self->{errmsg} = $@, return if $@;
83              
84             # reset things, define tree as []
85 282         476 &RxPOS = 0;
86 282         676 $self->{tree} = [];
87 282         706 $self->{flags} = [$init_flags];
88 282         642 $self->{next} = ['atom'];
89              
90 282         1009 return 1;
91             }
92              
93              
94             sub parse {
95 583     583 0 1211 my ($self, $rx) = @_;
96 583 50 0     1300 $self->regex($rx) or return if defined $rx;
97 583 50       1204 croak "no regex defined" unless &RxLEN;
98 583         1456 1 while $self->next;
99 554         1164 return 1;
100             }
101              
102              
103             sub root {
104 10     10 0 36 my ($self) = @_;
105 10 100       40 $self->parse if $self->{stack};
106 10         33 return $self->{tree};
107             }
108              
109              
110             sub nparen {
111 2     2 0 5 my ($self) = @_;
112 2 50       8 $self->parse if $self->{stack};
113 2         7 return $self->{nparen};
114             }
115              
116              
117             sub captures {
118 1     1 0 2 my ($self, $n) = @_;
119 1 50       5 $self->parse if $self->{stack};
120 1 50       2 return $self->{captures}[--$n] if $n;
121 1         3 return $self->{captures};
122             }
123              
124              
125             sub named_captures {
126 1     1 0 3 my ($self, $name) = @_;
127 1 50       5 $self->parse if $self->{stack};
128 1 50       3 return $self->{named_captures}{$name} if $name;
129 1         3 return $self->{named_captures};
130             }
131              
132              
133             sub nchar {
134 6     6 0 13 my $self = shift;
135 6 50       42 return map chr(/^\^(\S)/ ? (64 ^ ord $1) : charnames::vianame($_)), @_;
136             }
137              
138              
139             sub error_is {
140 0     0 0 0 my ($self, $enum) = @_;
141 0   0     0 return $self->{errnum} && $self->{errnum} == $enum;
142             }
143              
144              
145             sub errmsg {
146 0     0 0 0 my ($self) = @_;
147 0         0 return $self->{errmsg};
148             }
149              
150              
151             sub errnum {
152 0     0 0 0 my ($self) = @_;
153 0         0 return $self->{errnum};
154             }
155              
156              
157             sub error {
158 29     29 0 67 my ($self, $enum, $err, @args) = @_;
159 29         50 $err .= "; marked by <-- HERE in m/%s <-- HERE %s/";
160 29         52 push @args, substr(${&Rx}, 0, &RxPOS), &RxCUR;
  29         65  
161 29         59 $self->{errnum} = $enum;
162 29         121 $self->{errmsg} = sprintf $err, @args;
163 29         5004 croak $self->{errmsg};
164             }
165              
166              
167             sub warn {
168 0     0 0 0 my ($self, $enum, $err, @args) = @_;
169 0         0 $err .= "; marked by <-- HERE in m/%s <-- HERE %s/";
170 0         0 push @args, substr(${&Rx}, 0, &RxPOS), &RxCUR;
  0         0  
171 0 0       0 carp sprintf $err, @args if &SIZE_ONLY;
172             }
173              
174              
175             sub awarn {
176 0     0 0 0 my ($self, $enum, $err, @args) = @_;
177 0         0 local $self->{tree};
178 0         0 $self->warn($enum, $err, @args);
179             }
180              
181              
182             sub next {
183 1871     1871 0 2979 my ($self) = @_;
184 1871 50       3028 croak "no regex defined" unless &RxLEN;
185              
186 1871         2580 while (my $try = pop @{ $self->{next} }) {
  4606         11715  
187 4051 100       10320 if (defined(my $r = $self->$try)) {
188 1290         4308 $r->insert($self->{tree});
189 1288         5817 return $r;
190             }
191             }
192              
193 555 100       706 $self->error(RPe_RPAREN) if ${&Rx} =~ m{ \G \) }xgc;
  555         775  
194 554 50       1001 $self->error(0, "PANIC! %d %s", &RxPOS, &RxCUR) if &RxPOS != &RxLEN;
195              
196 554 100       938 if (! &SIZE_ONLY) {
197 272         476 $self->{tree} = pop @{ $self->{stack} } while @{ $self->{stack} };
  278         639  
  6         16  
198 272         595 delete $self->{stack};
199             }
200              
201 554         1268 return;
202             }
203              
204              
205             sub walker {
206 17     17 0 2550 my ($self, $depth) = @_;
207 17 50       49 croak "no regex defined" unless &RxLEN;
208 17 100       77 $self->parse if $self->{stack};
209              
210 17 100       44 $depth = -1 unless defined $depth;
211 17         29 my $d = $depth;
212 17         25 my @stack = @{ $self->{tree} };
  17         56  
213 17         25 my $next;
214              
215             return sub {
216 134 100 66 134   97050 return $depth if @_ and $_[0] eq -depth;
217 130 50       357 carp "unexpected argument ($_[0]) to iterator" if @_;
218              
219             {
220 130         199 $next = shift @stack;
  240         728  
221 240 100       962 $d += $next->(), redo if ref($next) eq "CODE";
222             }
223              
224 130 100       344 @stack = @{ $self->{tree} }, return unless $next;
  13         87  
225 117         639 $next->walk(\@stack, $d);
226 117 50       584 return wantarray ? ($next, $depth-$d) : $next;
227             }
228 17         220 }
229              
230              
231             sub visual {
232 248     248 0 1753 my ($self) = @_;
233 248 50       922 $self->parse if $self->{stack};
234 244         447 my $vis = join "", map($_->visual, @{ $self->{tree} });
  244         1001  
235 244         900 return $vis;
236             }
237              
238              
239             sub qr {
240 29     29 0 744 my ($self) = @_;
241 29 100       119 $self->parse if $self->{stack};
242 29         109 my $rx = $self->{tree};
243 14     14   136 no warnings 'regexp';
  14         47  
  14         1129  
244 14     14   106 use re 'eval';
  14         38  
  14         8859  
245              
246 29 100 66     95 if (@$rx == 1 and $rx->[0]->family eq 'group') {
247 6         8 my $vis = join "", map $_->qr, @{ $rx->[0]->{data} };
  6         25  
248 6         12 my $flags = $rx->[0]->on;
249 6         17 $flags =~ s/^\^//; # strip caret prefix for qr// modifier
250 6         412 return eval('qr/$vis/' . $flags);
251             }
252              
253 23         148 $rx = join "", map($_->qr, @$rx);
254 23         664 return qr/$rx/;
255             }
256              
257              
258             sub nextchar {
259 3540     3540 0 5922 my ($self) = @_;
260              
261             {
262 3540 100       4214 if (${&Rx} =~ m{ \G \(\?\# [^)]* }xgc) {
  3556         4426  
  3556         5359  
263 4 50       8 ${&Rx} =~ m{ \G \) }xgc and redo;
  4         9  
264 0         0 $self->error(RPe_NOTERM);
265             }
266 3552 100 100     5946 &Rf & $self->FLAG_x and ${&Rx} =~ m{ \G (?: \s+ | \# .* )+ }xgc and redo;
  54         58  
267             }
268             }
269              
270              
271             sub object {
272 2622 100   2622 0 59531 return if &SIZE_ONLY;
273 1287         2359 my $self = shift;
274 1287         3004 $self->force_object(@_);
275             }
276              
277              
278             sub force_object {
279 1477 50   1477 0 2798 Carp::croak("class name passed where object required") unless ref $_[0];
280 1477         2983 my $type = splice @_, 1, 1;
281 1477         2263 my $ref = ref $_[0];
282 1477         2386 my $class = "${ref}::$type";
283              
284 1477 100 100     3218 if ($ref ne __PACKAGE__ and !$loaded{$class}++) {
285 14     14   120 no strict 'refs';
  14         71  
  14         3583  
286 11         46 my $orig_base = $Regexp::Parser::{$type . '::'};
287 11         14 my $user_base = ${"${ref}::"}{'__object__::'};
  11         45  
288              
289 11 50       24 push @{ "${class}::ISA" }, $ref . "::__object__" if $user_base;
  11         193  
290 11 100       28 push @{ "${class}::ISA" }, __PACKAGE__ . "::$type" if $orig_base;
  10         144  
291             }
292              
293 1477         5652 return $class->new(@_);
294             }
295              
296              
297             sub add_flag {
298 2748     2748 0 4869 my ($self, $seq, $func) = @_;
299 14     14   130 no strict 'refs';
  14         29  
  14         683  
300 14     14   81 no warnings 'redefine';
  14         27  
  14         1834  
301 2748         3333 *{ ref($self) . "::FLAG_$seq" } = $func;
  2748         12147  
302             }
303              
304              
305             sub del_flag {
306 0     0 0 0 my ($self, @flags) = @_;
307 14     14   94 no strict 'refs';
  14         34  
  14         1989  
308 0         0 my $stash = \%{ ref($self) . "::" };
  0         0  
309 0         0 undef $stash->{"FLAG_$_"} for @flags;
310             }
311              
312              
313             sub add_handler {
314 20156     20156 0 30983 my ($self, $seq, $func) = @_;
315 14     14   105 no strict 'refs';
  14         37  
  14         590  
316 14     14   67 no warnings 'redefine';
  14         34  
  14         1623  
317 20156         24674 *{ ref($self) . "::$seq" } = $func;
  20156         101119  
318             }
319              
320              
321             sub del_handler {
322 0     0 0   my ($self, @handles) = @_;
323 14     14   85 no strict 'refs';
  14         28  
  14         1831  
324 0           my $stash = \%{ ref($self) . "::" };
  0            
325 0           undef $stash->{$_} for @handles;
326             }
327              
328              
329             1;
330              
331             __END__