File Coverage

blib/lib/Regexp/Parser.pm
Criterion Covered Total %
statement 175 209 83.7
branch 44 68 64.7
condition 7 17 41.1
subroutine 43 52 82.6
pod 2 31 6.4
total 271 377 71.8


line stmt bran cond sub pod time code
1             package Regexp::Parser;
2              
3 8     8   149404 use strict;
  8         50  
  8         200  
4 8     8   34 use warnings;
  8         14  
  8         258  
5              
6             our $VERSION = '0.23';
7              
8 8     8   128 use 5.006;
  8         22  
9 8     8   38 use Carp qw( carp croak );
  8         19  
  8         393  
10 8     8   53 use base 'Exporter';
  8         11  
  8         888  
11 8     8   46 use strict;
  8         21  
  8         161  
12 8     8   35 use warnings;
  8         13  
  8         236  
13 8     8   3507 use charnames ();
  8         234277  
  8         1237  
14              
15             our %loaded;
16             our @EXPORT = qw( Rx RxPOS RxCUR RxLEN Rf SIZE_ONLY LATEST );
17              
18 1567     1567 0 4210 sub Rx :lvalue { $_[0]{regex} }
19 275     275 0 305 sub RxPOS :lvalue { pos ${&Rx} }
  275         332  
20 14     14 0 15 sub RxCUR { substr ${&Rx}, &RxPOS }
  14         20  
21 272     272 0 532 sub RxLEN { $_[0]{len} }
22              
23 583     583 0 1064 sub Rf :lvalue { $_[0]{flags}[-1] }
24              
25 442     442 0 1267 sub SIZE_ONLY { ! $_[0]{tree} }
26 0     0 0 0 sub LATEST :lvalue { $_[0]{tree}[-1] }
27              
28 8     8   2835 use Regexp::Parser::Diagnostics;
  8         18  
  8         187  
29 8     8   3413 use Regexp::Parser::Objects;
  8         23  
  8         234  
30 8     8   55246 use Regexp::Parser::Handlers;
  8         21  
  8         9023  
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 10     10 1 1518 my ($class, $rx) = @_;
47 10         33 my $self = bless {}, $class;
48 10         43 $self->init;
49 10 100       37 $self->regex($rx) if defined $rx;
50 10         29 return $self;
51             }
52              
53              
54             sub regex {
55 29     29 1 1208 my ($self, $rx) = @_;
56 29         370 %$self = (
57             regex => \"$rx",
58             len => length $rx,
59             tree => undef,
60             stack => [],
61             maxpar => 0,
62             nparen => 0,
63             captures => [],
64             flags => [0],
65             next => ['atom'],
66             );
67              
68             # do the initial scan (populates maxpar)
69             # because tree is undef, nothing gets built
70 29         69 &RxPOS = 0;
71 29         54 eval { $self->parse };
  29         76  
72 29 100       425 $self->{errmsg} = $@, return if $@;
73              
74             # reset things, define tree as []
75 18         55 &RxPOS = 0;
76 18         46 $self->{tree} = [];
77 18         45 $self->{flags} = [0];
78 18         46 $self->{next} = ['atom'];
79              
80 18         63 return 1;
81             }
82              
83              
84             sub parse {
85 44     44 0 80 my ($self, $rx) = @_;
86 44 50 0     102 $self->regex($rx) or return if defined $rx;
87 44 50       83 croak "no regex defined" unless &RxLEN;
88 44         101 1 while $self->next;
89 33         54 return 1;
90             }
91              
92              
93             sub root {
94 2     2 0 6 my ($self) = @_;
95 2 100       9 $self->parse if $self->{stack};
96 2         10 return $self->{tree};
97             }
98              
99              
100             sub nparen {
101 0     0 0 0 my ($self) = @_;
102 0 0       0 $self->parse if $self->{stack};
103 0         0 return $self->{nparen};
104             }
105              
106              
107             sub captures {
108 1     1 0 2 my ($self, $n) = @_;
109 1 50       7 $self->parse if $self->{stack};
110 1 50       14 return $self->{captures}[--$n] if $n;
111 1         5 return $self->{captures};
112             }
113              
114              
115             sub nchar {
116 2     2 0 4 my $self = shift;
117 2 50       13 return map chr(/^\^(\S)/ ? (64 ^ ord $1) : charnames::vianame($_)), @_;
118             }
119              
120              
121             sub error_is {
122 0     0 0 0 my ($self, $enum) = @_;
123 0   0     0 return $self->{errnum} && $self->{errnum} == $enum;
124             }
125              
126              
127             sub errmsg {
128 0     0 0 0 my ($self) = @_;
129 0         0 return $self->{errmsg};
130             }
131              
132              
133             sub errnum {
134 0     0 0 0 my ($self) = @_;
135 0         0 return $self->{errnum};
136             }
137              
138              
139             sub error {
140 11     11 0 23 my ($self, $enum, $err, @args) = @_;
141 11         15 $err .= "; marked by <-- HERE in m/%s <-- HERE %s/";
142 11         17 push @args, substr(${&Rx}, 0, &RxPOS), &RxCUR;
  11         14  
143 11         23 $self->{errnum} = $enum;
144 11         38 $self->{errmsg} = sprintf $err, @args;
145 11         1017 croak $self->{errmsg};
146             }
147              
148              
149             sub warn {
150 0     0 0 0 my ($self, $enum, $err, @args) = @_;
151 0         0 $err .= "; marked by <-- HERE in m/%s <-- HERE %s/";
152 0         0 push @args, substr(${&Rx}, 0, &RxPOS), &RxCUR;
  0         0  
153 0 0       0 carp sprintf $err, @args if &SIZE_ONLY;
154             }
155              
156              
157             sub awarn {
158 0     0 0 0 my ($self, $enum, $err, @args) = @_;
159 0         0 local $self->{tree};
160 0         0 $self->warn($enum, $err, @args);
161             }
162              
163              
164             sub next {
165 184     184 0 256 my ($self) = @_;
166 184 50       263 croak "no regex defined" unless &RxLEN;
167              
168 184         226 while (my $try = pop @{ $self->{next} }) {
  579         1052  
169 546 100       1087 if (defined(my $r = $self->$try)) {
170 140         479 $r->insert($self->{tree});
171 140         483 return $r;
172             }
173             }
174              
175 33 50       43 $self->error(RPe_RPAREN) if ${&Rx} =~ m{ \G \) }xgc;
  33         52  
176 33 50       56 $self->error(0, "PANIC! %d %s", &RxPOS, &RxCUR) if &RxPOS != &RxLEN;
177              
178 33 100       65 if (! &SIZE_ONLY) {
179 15         36 $self->{tree} = pop @{ $self->{stack} } while @{ $self->{stack} };
  15         44  
  0         0  
180 15         34 delete $self->{stack};
181             }
182              
183 33         74 return;
184             }
185              
186              
187             sub walker {
188 11     11 0 161 my ($self, $depth) = @_;
189 11 50       18 croak "no regex defined" unless &RxLEN;
190 11 100       34 $self->parse if $self->{stack};
191              
192 11 100       27 $depth = -1 unless defined $depth;
193 11         15 my $d = $depth;
194 11         12 my @stack = @{ $self->{tree} };
  11         26  
195 11         15 my $next;
196              
197             return sub {
198 123 100 66 123   5838 return $depth if @_ and $_[0] eq -depth;
199 119 50       179 carp "unexpected argument ($_[0]) to iterator" if @_;
200              
201             {
202 119         134 $next = shift @stack;
  229         427  
203 229 100       546 $d += $next->(), redo if ref($next) eq "CODE";
204             }
205              
206 119 100       193 @stack = @{ $self->{tree} }, return unless $next;
  11         38  
207 108         352 $next->walk(\@stack, $d);
208 108 50       393 return wantarray ? ($next, $depth-$d) : $next;
209             }
210 11         55 }
211              
212              
213             sub visual {
214 5     5 0 22 my ($self) = @_;
215 5 50       27 $self->parse if $self->{stack};
216 5         10 my $vis = join "", map($_->visual, @{ $self->{tree} });
  5         25  
217 5         23 return $vis;
218             }
219              
220              
221             sub qr {
222 17     17 0 490 my ($self) = @_;
223 17 50       37 $self->parse if $self->{stack};
224 17         29 my $rx = $self->{tree};
225 8     8   60 no warnings 'regexp';
  8         15  
  8         384  
226 8     8   45 use re 'eval';
  8         15  
  8         3044  
227              
228 17 50 33     40 if (@$rx == 1 and $rx->[0]->family eq 'group') {
229 0         0 my $vis = join "", map $_->qr, @{ $rx->[0]->{data} };
  0         0  
230 0         0 return eval('qr/$vis/' . $rx->[0]->on);
231             }
232              
233 17         60 $rx = join "", map($_->qr, @$rx);
234 17         286 return qr/$rx/;
235             }
236              
237              
238             sub nextchar {
239 459     459 0 548 my ($self) = @_;
240              
241             {
242 459 50       473 if (${&Rx} =~ m{ \G \(\?\# [^)]* }xgc) {
  459         465  
  459         532  
243 0 0       0 ${&Rx} =~ m{ \G \) }xgc and redo;
  0         0  
244 0         0 $self->error(RPe_NOTERM);
245             }
246 459 50 33     633 &Rf & $self->FLAG_x and ${&Rx} =~ m{ \G (?: \s+ | \# .* )+ }xgc and redo;
  0         0  
247             }
248             }
249              
250              
251             sub object {
252 385 100   385 0 12867 return if &SIZE_ONLY;
253 180         292 my $self = shift;
254 180         308 $self->force_object(@_);
255             }
256              
257              
258             sub force_object {
259 189 50   189 0 347 Carp::croak("class name passed where object required") unless ref $_[0];
260 189         279 my $type = splice @_, 1, 1;
261 189         222 my $ref = ref $_[0];
262 189         349 my $class = "${ref}::$type";
263              
264 189 100 100     413 if ($ref ne __PACKAGE__ and !$loaded{$class}++) {
265 8     8   54 no strict 'refs';
  8         14  
  8         1212  
266 11         72 my $orig_base = $Regexp::Parser::{$type . '::'};
267 11         15 my $user_base = ${"${ref}::"}{'__object__::'};
  11         31  
268              
269 11 50       21 push @{ "${class}::ISA" }, $ref . "::__object__" if $user_base;
  11         119  
270 11 100       25 push @{ "${class}::ISA" }, __PACKAGE__ . "::$type" if $orig_base;
  10         117  
271             }
272              
273 189         617 return $class->new(@_);
274             }
275              
276              
277             sub add_flag {
278 70     70 0 127 my ($self, $seq, $func) = @_;
279 8     8   50 no strict 'refs';
  8         15  
  8         247  
280 8     8   43 no warnings 'redefine';
  8         14  
  8         614  
281 70         80 *{ ref($self) . "::FLAG_$seq" } = $func;
  70         279  
282             }
283              
284              
285             sub del_flag {
286 0     0 0 0 my ($self, @flags) = @_;
287 8     8   49 no strict 'refs';
  8         14  
  8         680  
288 0         0 my $stash = \%{ ref($self) . "::" };
  0         0  
289 0         0 undef $stash->{"FLAG_$_"} for @flags;
290             }
291              
292              
293             sub add_handler {
294 774     774 0 1248 my ($self, $seq, $func) = @_;
295 8     8   48 no strict 'refs';
  8         39  
  8         273  
296 8     8   42 no warnings 'redefine';
  8         12  
  8         597  
297 774         791 *{ ref($self) . "::$seq" } = $func;
  774         3145  
298             }
299              
300              
301             sub del_handler {
302 0     0 0   my ($self, @handles) = @_;
303 8     8   57 no strict 'refs';
  8         14  
  8         610  
304 0           my $stash = \%{ ref($self) . "::" };
  0            
305 0           undef $stash->{$_} for @handles;
306             }
307              
308              
309             1;
310              
311             __END__