File Coverage

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


line stmt bran cond sub pod time code
1             package Regexp::Parser;
2              
3 8     8   74147 use strict;
  8         18  
  8         207  
4 8     8   40 use warnings;
  8         14  
  8         285  
5              
6             our $VERSION = '0.21_02';
7              
8 8     8   125 use 5.006;
  8         28  
9 8     8   37 use Carp qw( carp croak );
  8         15  
  8         374  
10 8     8   41 use base 'Exporter';
  8         12  
  8         690  
11 8     8   51 use strict;
  8         13  
  8         156  
12 8     8   41 use warnings;
  8         11  
  8         204  
13 8     8   2370 use charnames ();
  8         180246  
  8         1197  
14              
15             our %loaded;
16             our @EXPORT = qw( Rx RxPOS RxCUR RxLEN Rf SIZE_ONLY LATEST );
17              
18 1533     1533 0 4337 sub Rx :lvalue { $_[0]{regex} }
19 269     269 0 308 sub RxPOS :lvalue { pos ${&Rx} }
  269         331  
20 14     14 0 19 sub RxCUR { substr ${&Rx}, &RxPOS }
  14         17  
21 263     263 0 501 sub RxLEN { $_[0]{len} }
22              
23 579     579 0 1117 sub Rf :lvalue { $_[0]{flags}[-1] }
24              
25 434     434 0 1286 sub SIZE_ONLY { ! $_[0]{tree} }
26 0     0 0 0 sub LATEST :lvalue { $_[0]{tree}[-1] }
27              
28 8     8   2078 use Regexp::Parser::Diagnostics;
  8         16  
  8         175  
29 8     8   2385 use Regexp::Parser::Objects;
  8         21  
  8         273  
30 8     8   46117 use Regexp::Parser::Handlers;
  8         21  
  8         7866  
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 9     9 1 1028 my ($class, $rx) = @_;
47 9         28 my $self = bless {}, $class;
48 9         44 $self->init;
49 9 50       35 $self->regex($rx) if defined $rx;
50 9         27 return $self;
51             }
52              
53              
54             sub regex {
55 28     28 1 1077 my ($self, $rx) = @_;
56 28         323 %$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 28         70 &RxPOS = 0;
71 28         57 eval { $self->parse };
  28         69  
72 28 100       417 $self->{errmsg} = $@, return if $@;
73              
74             # reset things, define tree as []
75 17         31 &RxPOS = 0;
76 17         41 $self->{tree} = [];
77 17         41 $self->{flags} = [0];
78 17         36 $self->{next} = ['atom'];
79              
80 17         57 return 1;
81             }
82              
83              
84             sub parse {
85 42     42 0 72 my ($self, $rx) = @_;
86 42 50 0     92 $self->regex($rx) or return if defined $rx;
87 42 50       70 croak "no regex defined" unless &RxLEN;
88 42         93 1 while $self->next;
89 31         54 return 1;
90             }
91              
92              
93             sub root {
94 2     2 0 6 my ($self) = @_;
95 2 100       8 $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 3 my ($self, $n) = @_;
109 1 50       11 $self->parse if $self->{stack};
110 1 50       2 return $self->{captures}[--$n] if $n;
111 1         3 return $self->{captures};
112             }
113              
114              
115             sub nchar {
116 2     2 0 7 my $self = shift;
117 2 50       8 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 24 my ($self, $enum, $err, @args) = @_;
141 11         16 $err .= "; marked by <-- HERE in m/%s <-- HERE %s/";
142 11         16 push @args, substr(${&Rx}, 0, &RxPOS), &RxCUR;
  11         15  
143 11         23 $self->{errnum} = $enum;
144 11         43 $self->{errmsg} = sprintf $err, @args;
145 11         1115 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 179     179 0 271 my ($self) = @_;
166 179 50       264 croak "no regex defined" unless &RxLEN;
167              
168 179         228 while (my $try = pop @{ $self->{next} }) {
  565         1137  
169 534 100       1085 if (defined(my $r = $self->$try)) {
170 137         432 $r->insert($self->{tree});
171 137         466 return $r;
172             }
173             }
174              
175 31 50       59 $self->error(RPe_RPAREN) if ${&Rx} =~ m{ \G \) }xgc;
  31         47  
176 31 50       59 $self->error(0, "PANIC! %d %s", &RxPOS, &RxCUR) if &RxPOS != &RxLEN;
177              
178 31 100       56 if (! &SIZE_ONLY) {
179 14         22 $self->{tree} = pop @{ $self->{stack} } while @{ $self->{stack} };
  14         38  
  0         0  
180 14         29 delete $self->{stack};
181             }
182              
183 31         87 return;
184             }
185              
186              
187             sub walker {
188 11     11 0 141 my ($self, $depth) = @_;
189 11 50       18 croak "no regex defined" unless &RxLEN;
190 11 100       36 $self->parse if $self->{stack};
191              
192 11 100       26 $depth = -1 unless defined $depth;
193 11         14 my $d = $depth;
194 11         15 my @stack = @{ $self->{tree} };
  11         25  
195 11         21 my $next;
196              
197             return sub {
198 123 100 66 123   5592 return $depth if @_ and $_[0] eq -depth;
199 119 50       193 carp "unexpected argument ($_[0]) to iterator" if @_;
200              
201             {
202 119         133 $next = shift @stack;
  229         415  
203 229 100       487 $d += $next->(), redo if ref($next) eq "CODE";
204             }
205              
206 119 100       197 @stack = @{ $self->{tree} }, return unless $next;
  11         40  
207 108         313 $next->walk(\@stack, $d);
208 108 50       314 return wantarray ? ($next, $depth-$d) : $next;
209             }
210 11         56 }
211              
212              
213             sub visual {
214 4     4 0 9 my ($self) = @_;
215 4 50       18 $self->parse if $self->{stack};
216 4         5 my $vis = join "", map($_->visual, @{ $self->{tree} });
  4         20  
217 4         17 return $vis;
218             }
219              
220              
221             sub qr {
222 17     17 0 447 my ($self) = @_;
223 17 50       39 $self->parse if $self->{stack};
224 17         27 my $rx = $self->{tree};
225 8     8   64 no warnings 'regexp';
  8         15  
  8         389  
226 8     8   38 use re 'eval';
  8         15  
  8         2468  
227              
228 17 50 33     46 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         55 $rx = join "", map($_->qr, @$rx);
234 17         252 return qr/$rx/;
235             }
236              
237              
238             sub nextchar {
239 455     455 0 585 my ($self) = @_;
240              
241             {
242 455 50       501 if (${&Rx} =~ m{ \G \(\?\# [^)]* }xgc) {
  455         483  
  455         573  
243 0 0       0 ${&Rx} =~ m{ \G \) }xgc and redo;
  0         0  
244 0         0 $self->error(RPe_NOTERM);
245             }
246 455 50 33     665 &Rf & $self->FLAG_x and ${&Rx} =~ m{ \G (?: \s+ | \# .* )+ }xgc and redo;
  0         0  
247             }
248             }
249              
250              
251             sub object {
252 381 100   381 0 9069 return if &SIZE_ONLY;
253 178         302 my $self = shift;
254 178         331 $self->force_object(@_);
255             }
256              
257              
258             sub force_object {
259 185 50   185 0 367 Carp::croak("class name passed where object required") unless ref $_[0];
260 185         285 my $type = splice @_, 1, 1;
261 185         272 my $ref = ref $_[0];
262 185         321 my $class = "${ref}::$type";
263              
264 185 100 100     398 if ($ref ne __PACKAGE__ and !$loaded{$class}++) {
265 8     8   52 no strict 'refs';
  8         14  
  8         1014  
266 11         31 my $orig_base = $Regexp::Parser::{$type . '::'};
267 11         13 my $user_base = ${"${ref}::"}{'__object__::'};
  11         31  
268              
269 11 50       19 push @{ "${class}::ISA" }, $ref . "::__object__" if $user_base;
  11         97  
270 11 100       23 push @{ "${class}::ISA" }, __PACKAGE__ . "::$type" if $orig_base;
  10         96  
271             }
272              
273 185         610 return $class->new(@_);
274             }
275              
276              
277             sub add_flag {
278 63     63 0 105 my ($self, $seq, $func) = @_;
279 8     8   45 no strict 'refs';
  8         13  
  8         181  
280 8     8   36 no warnings 'redefine';
  8         12  
  8         526  
281 63         76 *{ ref($self) . "::FLAG_$seq" } = $func;
  63         239  
282             }
283              
284              
285             sub del_flag {
286 0     0 0 0 my ($self, @flags) = @_;
287 8     8   42 no strict 'refs';
  8         14  
  8         575  
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 697     697 0 1090 my ($self, $seq, $func) = @_;
295 8     8   44 no strict 'refs';
  8         119  
  8         259  
296 8     8   41 no warnings 'redefine';
  8         13  
  8         478  
297 697         750 *{ ref($self) . "::$seq" } = $func;
  697         2430  
298             }
299              
300              
301             sub del_handler {
302 0     0 0   my ($self, @handles) = @_;
303 8     8   38 no strict 'refs';
  8         12  
  8         528  
304 0           my $stash = \%{ ref($self) . "::" };
  0            
305 0           undef $stash->{$_} for @handles;
306             }
307              
308              
309             1;
310              
311             __END__