File Coverage

blib/lib/Regexp/Parser/Objects.pm
Criterion Covered Total %
statement 332 571 58.1
branch 70 186 37.6
condition 30 69 43.4
subroutine 92 151 60.9
pod n/a
total 524 977 53.6


line stmt bran cond sub pod time code
1 8     8   3117 use NEXT;
  8         27156  
  8         65323  
2              
3             {
4             package Regexp::Parser::__object__;
5              
6             sub class {
7 0     0   0 my $self = shift;
8 0         0 Carp::carp("class() deprecated; use family() instead");
9 0         0 $self->family(@_);
10             }
11              
12             sub flags {
13 0     0   0 my $self = shift;
14 0         0 $self->{flags};
15             }
16              
17             sub family {
18 195     195   680 my $self = shift;
19 195         631 $self->{family};
20             }
21              
22             sub type {
23 106     106   139 my $self = shift;
24 106         238 $self->{type};
25             }
26              
27             sub qr {
28 85     85   107 my $self = shift;
29 85         114 $self->visual(@_);
30             }
31              
32             sub visual {
33 62     62   87 my $self = shift;
34 62 100       214 exists $self->{vis} ? $self->{vis} : '';
35             }
36              
37             sub raw {
38 66     66   86 my $self = shift;
39 66 50       150 exists $self->{raw} ? $self->{raw} : $self->visual(@_);
40             }
41              
42             sub data {
43 20     20   23 my $self = shift;
44 20         40 return $self->{data};
45             }
46              
47             sub ender {
48 11     11   15 my $self = shift;
49 11 50       32 unless ($self->{down}) {
50 0         0 Carp::carp("ender() ignored for ", $self->family, "/", $self->type);
51 0         0 return;
52             }
53 11         37 [ 'tail' ];
54             }
55              
56             sub walk {
57 57     57   73 my $self = shift;
58 57         85 return;
59             }
60              
61             sub omit {
62 104     104   151 my $self = shift;
63 104 100       224 $self->{omit} = shift if @_;
64 104         196 $self->{omit};
65             }
66              
67             sub insert {
68 64     64   99 my ($self, $tree) = @_;
69 64         166 my $rx = $self->{rx};
70 64         106 my $merged = 0;
71 64 50       193 return if $self->omit;
72 64         108 push @$tree, $self;
73 64         159 $self->merge;
74             }
75              
76             sub merge {
77 21     21   36 my ($self) = @_;
78 21         49 return;
79             }
80             }
81              
82              
83             {
84             # \A ^ \B \b \G \Z \z $
85             package Regexp::Parser::anchor;
86             our @ISA = qw( Regexp::Parser::__object__ );
87             push @Regexp::Parser::bol::ISA, __PACKAGE__;
88             push @Regexp::Parser::bound::ISA, __PACKAGE__;
89             push @Regexp::Parser::gpos::ISA, __PACKAGE__;
90             push @Regexp::Parser::eol::ISA, __PACKAGE__;
91              
92             sub new {
93 8     8   24 my ($class, $rx, $type, $vis) = @_;
94 8 50       32 Carp::croak("anchor is an abstract class") if $class =~ /::anchor$/;
95              
96             my $self = bless {
97             rx => $rx,
98 8         43 flags => $rx->{flags}[-1],
99             family => 'anchor',
100             type => $type,
101             vis => $vis,
102             zerolen => 1,
103             }, $class;
104 8         29 return $self;
105             }
106             }
107              
108              
109             {
110             # . \C
111             package Regexp::Parser::reg_any;
112             our @ISA = qw( Regexp::Parser::__object__ );
113              
114             sub new {
115 2     2   6 my ($class, $rx, $type, $vis) = @_;
116             my $self = bless {
117             rx => $rx,
118 2         8 flags => $rx->{flags}[-1],
119             family => 'reg_any',
120             type => $type,
121             vis => $vis,
122             }, $class;
123 2         6 return $self;
124             }
125             }
126              
127              
128             {
129             # \w \W
130             package Regexp::Parser::alnum;
131             our @ISA = qw( Regexp::Parser::__object__ );
132              
133             sub new {
134 0     0   0 my ($class, $rx, $neg) = @_;
135             my $self = bless {
136             rx => $rx,
137 0         0 flags => $rx->{flags}[-1],
138             neg => $neg,
139             }, $class;
140 0         0 return $self;
141             }
142              
143             sub neg {
144 0     0   0 my $self = shift;
145 0 0       0 $self->{neg} = shift if @_;
146 0         0 $self->{neg};
147             }
148              
149 0     0   0 sub family { 'alnum' }
150              
151             sub type {
152 0     0   0 my $self = shift;
153 0 0       0 ($self->{neg} ? 'n' : '') . $self->family;
154             }
155              
156             sub visual {
157 0     0   0 my $self = shift;
158 0 0       0 $self->{neg} ? '\W' : '\w';
159             }
160             }
161              
162              
163             {
164             # \s \S
165             package Regexp::Parser::space;
166             our @ISA = qw( Regexp::Parser::__object__ );
167              
168             sub new {
169 0     0   0 my ($class, $rx, $neg) = @_;
170             my $self = bless {
171             rx => $rx,
172 0         0 flags => $rx->{flags}[-1],
173             neg => $neg,
174             }, $class;
175 0         0 return $self;
176             }
177              
178             sub neg {
179 0     0   0 my $self = shift;
180 0 0       0 $self->{neg} = shift if @_;
181 0         0 $self->{neg};
182             }
183              
184             sub type {
185 0     0   0 my $self = shift;
186 0 0       0 ($self->{neg} ? 'n' : '') . $self->family;
187             }
188              
189 0     0   0 sub family { 'space' }
190              
191             sub visual {
192 0     0   0 my $self = shift;
193 0 0       0 $self->{neg} ? '\S' : '\s';
194             }
195             }
196              
197              
198             {
199             # \d \D
200             package Regexp::Parser::digit;
201             our @ISA = qw( Regexp::Parser::__object__ );
202              
203             sub new {
204 4     4   6 my ($class, $rx, $neg) = @_;
205             my $self = bless {
206             rx => $rx,
207 4         26 flags => $rx->{flags}[-1],
208             neg => $neg,
209             }, $class;
210 4         15 return $self;
211             }
212              
213             sub neg {
214 0     0   0 my $self = shift;
215 0 0       0 $self->{neg} = shift if @_;
216 0         0 $self->{neg};
217             }
218              
219             sub type {
220 0     0   0 my $self = shift;
221 0 0       0 ($self->{neg} ? 'n' : '') . $self->family;
222             }
223              
224 16     16   44 sub family { 'digit' }
225              
226             sub visual {
227 16     16   20 my $self = shift;
228 16 100       33 $self->{neg} ? '\D' : '\d';
229             }
230             }
231              
232              
233             {
234             package Regexp::Parser::anyof;
235             our @ISA = qw( Regexp::Parser::__object__ );
236              
237             sub new {
238 2     2   8 my ($class, $rx, $neg, @data) = @_;
239             my $self = bless {
240             rx => $rx,
241 2         15 flags => $rx->{flags}[-1],
242             family => 'anyof',
243             type => 'anyof',
244             neg => $neg,
245             data => \@data,
246             down => 1,
247             }, $class;
248 2         9 return $self;
249             }
250              
251             sub qr {
252 0     0   0 my $self = shift;
253 0         0 join "", $self->raw, map($_->qr, @{ $self->{data} }), "]";
  0         0  
254             }
255              
256             sub visual {
257 2     2   5 my $self = shift;
258 2         8 join "", $self->raw, map($_->visual, @{ $self->{data} }), "]";
  2         10  
259             }
260              
261             sub raw {
262 2     2   3 my $self = shift;
263 2 50       10 join "", "[", $self->{neg} ? "^" : "";
264             }
265              
266             sub neg {
267 0     0   0 my $self = shift;
268 0 0       0 $self->{neg} = shift if @_;
269 0         0 $self->{neg};
270             }
271              
272             sub ender {
273 0     0   0 my $self = shift;
274 0         0 [ 'anyof_close' ];
275             }
276              
277             sub data {
278 0     0   0 my $self = shift;
279 0 0       0 if (@_) {
280 0         0 my $how = shift;
281 0 0       0 if ($how eq '=') { $self->{data} = \@_ }
  0 0       0  
282 0         0 elsif ($how eq '+') { push @{ $self->{data} }, @_ }
  0         0  
283             else {
284 0         0 my $t = $self->type;
285 0         0 Carp::croak("\$$t->data([+=], \@data)");
286             }
287             }
288 0         0 $self->{data};
289             }
290              
291             sub walk {
292 0     0   0 my ($self, $ws, $d) = @_;
293 0         0 unshift @$ws, $self->{rx}->object(@{ $self->ender });
  0         0  
294 0 0   0   0 unshift @$ws, sub { -1 }, @{ $self->{data} }, sub { +1 } if $d;
  0         0  
  0         0  
  0         0  
295             }
296              
297             sub insert {
298 2     2   5 my ($self, $tree) = @_;
299 2         12 my $rx = $self->{rx};
300 2         5 push @$tree, $self;
301 2         4 push @{ $rx->{stack} }, $tree;
  2         4  
302 2         14 $rx->{tree} = $self->{data};
303             }
304             }
305              
306              
307             {
308             package Regexp::Parser::anyof_char;
309             our @ISA = qw( Regexp::Parser::__object__ );
310              
311             sub new {
312 6     6   11 my ($class, $rx, $data, $vis) = @_;
313 6 50       10 $vis = $data if not defined $vis;
314             my $self = bless {
315             rx => $rx,
316 6         40 flags => $rx->{flags}[-1],
317             family => 'anyof_char',
318             type => 'anyof_char',
319             data => $data,
320             vis => $vis,
321             }, $class;
322             }
323             }
324              
325              
326             {
327             package Regexp::Parser::anyof_range;
328             our @ISA = qw( Regexp::Parser::__object__ );
329              
330             sub new {
331 0     0   0 my ($class, $rx, $lhs, $rhs) = @_;
332             my $self = bless {
333             rx => $rx,
334 0         0 flags => $rx->{flags}[-1],
335             family => 'anyof_range',
336             type => 'anyof_range',
337             data => [$lhs, $rhs],
338             }, $class;
339             }
340              
341             sub qr {
342 0     0   0 my $self = shift;
343 0         0 join "-", $self->{data}[0]->qr, $self->{data}[1]->qr;
344             }
345              
346             sub visual {
347 0     0   0 my $self = shift;
348 0         0 join "-", $self->{data}[0]->visual, $self->{data}[1]->visual;
349             }
350             }
351              
352              
353             {
354             package Regexp::Parser::anyof_class;
355             our @ISA = qw( Regexp::Parser::__object__ );
356              
357             sub new {
358 2     2   6 my ($class, $rx, $type, $neg, $how) = @_;
359             my $self = bless {
360             rx => $rx,
361 2         8 flags => $rx->{flags}[-1],
362             family => 'anyof_class',
363             }, $class;
364              
365 2 50       7 if (ref $type) {
366 0         0 $self->{data} = $type;
367             }
368             else {
369 2         8 $self->{type} = $type;
370 2         4 $self->{data} = 'POSIX';
371 2         5 $self->{neg} = $neg;
372 2         4 $self->{how} = $how;
373             }
374              
375 2         7 return $self;
376             }
377              
378             sub type {
379 0     0   0 my $self = shift;
380 0 0       0 if (ref $self->{data}) {
381 0         0 $self->{data}->type;
382             }
383             else {
384             my $how = ref $self->{how} eq 'SCALAR' ?
385 0         0 ${ $self->{how} } :
386 0 0       0 $self->{how};
387             join "", $how, ($self->{neg} ? '^' : ''),
388 0 0       0 $self->{type}, $how;
389             }
390             }
391              
392             sub neg {
393 0     0   0 my $self = shift;
394 0 0       0 if (ref $self->{data}) {
395 0 0       0 $self->{data}->neg = shift if @_;
396 0         0 $self->{data}->neg;
397             }
398             else {
399 0 0       0 $self->{neg} = shift if @_;
400 0         0 $self->{neg};
401             }
402             }
403              
404             sub visual {
405 1     1   2 my $self = shift;
406 1 50       3 if (ref $self->{data}) {
407 0         0 $self->{data}->visual;
408             }
409             else {
410             my $how = ref $self->{how} eq 'SCALAR' ?
411 1         3 ${ $self->{how} } :
412 1 50       4 $self->{how};
413             join "", "[", $how, ($self->{neg} ? '^' : ''),
414 1 50       9 $self->{type}, $how, "]";
415             }
416             }
417             }
418              
419              
420             {
421             package Regexp::Parser::anyof_close;
422             our @ISA = qw( Regexp::Parser::__object__ );
423              
424             sub new {
425 2     2   7 my ($class, $rx) = @_;
426             my $self = bless {
427             rx => $rx,
428 2         13 flags => $rx->{flags}[-1],
429             family => 'close',
430             type => 'anyof_close',
431             raw => ']',
432             omit => 1,
433             up => 1,
434             }, $class;
435 2         8 return $self;
436             }
437              
438             sub insert {
439 2     2   3 my $self = shift;
440 2         10 my $rx = $self->{rx};
441 2         3 $rx->{tree} = pop @{ $rx->{stack} };
  2         5  
442 2         4 return $self;
443             }
444             }
445              
446              
447             {
448             package Regexp::Parser::prop;
449             our @ISA = qw( Regexp::Parser::__object__ );
450              
451             sub new {
452 1     1   4 my ($class, $rx, $type, $neg) = @_;
453             my $self = bless {
454             rx => $rx,
455 1 50       15 flags => $rx->{flags}[-1],
456             family => 'prop',
457             type => $type,
458             data => '',
459             neg => ($neg ? 1 : 0),
460             }, $class;
461 1         5 return $self;
462             }
463              
464             sub type {
465 1     1   2 my $self = shift;
466 1         9 $self->{type};
467             }
468              
469             sub neg {
470 0     0   0 my $self = shift;
471 0 0       0 $self->{neg} = shift if @_;
472 0         0 $self->{neg};
473             }
474              
475             sub visual {
476 1     1   3 my $self = shift;
477 1 50       9 sprintf "\\%s{%s}", $self->{neg} ? 'P' : 'p', $self->type;
478             }
479             }
480              
481              
482             {
483             package Regexp::Parser::clump;
484             our @ISA = qw( Regexp::Parser::__object__ );
485              
486             sub new {
487 0     0   0 my ($class, $rx, $vis) = @_;
488             my $self = bless {
489             rx => $rx,
490 0         0 flags => $rx->{flags}[-1],
491             family => 'clump',
492             type => 'clump',
493             vis => $vis,
494             }, $class;
495             }
496             }
497              
498              
499             {
500             package Regexp::Parser::branch;
501             our @ISA = qw( Regexp::Parser::__object__ );
502              
503             sub new {
504 36     36   57 my ($class, $rx) = @_;
505             my $self = bless {
506             rx => $rx,
507 36         172 flags => $rx->{flags}[-1],
508             data => [[]],
509             family => 'branch',
510             type => 'branch',
511             raw => '|',
512             branch => 1,
513             }, $class;
514             }
515              
516             sub qr {
517 0     0   0 my $self = shift;
518 0         0 join $self->raw, map join("", map $_->qr, @$_), @{ $self->{data} };
  0         0  
519             }
520              
521             sub visual {
522 36     36   44 my $self = shift;
523 36         57 join $self->raw, map join("", map $_->visual, @$_), @{ $self->{data} };
  36         136  
524             }
525              
526             sub merge {
527 0     0   0 my ($self) = @_;
528 0         0 my $tree = $self->{rx}{tree};
529 0 0       0 return unless @$tree;
530              
531 0 0       0 push @$tree, $self unless $tree->[-1] == $self;
532 0 0       0 return unless @$tree > 1;
533 0         0 my $prev = $tree->[-2];
534 0 0       0 return unless $prev->type eq $self->type;
535 0         0 push @{ $prev->{data} }, @{ $self->{data} };
  0         0  
  0         0  
536 0         0 pop @$tree;
537 0         0 return 1;
538             }
539              
540             sub walk {
541 20     20   35 my ($self, $ws, $d) = @_;
542 20 50       37 if ($d) {
543 20         38 my $br = $self->{rx}->object($self->type);
544 20         45 $br->omit(1);
545 20         23 for (reverse @{ $self->data }) {
  20         35  
546 30     30   122 unshift @$ws, $br, sub { -1 }, @$_, sub { +1 };
  30         57  
  30         46  
547             }
548 20         43 shift @$ws;
549             }
550             }
551              
552             sub insert {
553 12     12   23 my ($self, $tree) = @_;
554 12         21 my $rx = $self->{rx};
555 12         16 my $st = $rx->{stack};
556              
557             # this is a branch inside an IFTHEN
558 12 100 33     28 if (@$st and @{ $st->[-1] } and $st->[-1][-1]->type eq 'ifthen') {
  12 50 66     55  
      33        
      33        
559 6         10 my $ifthen = $st->[-1][-1];
560 6         7 my $cond = shift @{ $ifthen->{data} };
  6         8  
561 6         10 $ifthen->{data} = [ [ @{ $ifthen->{data} } ], $cond ];
  6         11  
562 6         16 $rx->{tree} = $ifthen->{data};
563             }
564              
565             # if this is the 2nd or 3rd (etc) branch...
566 6         49 elsif (@$st and @{ $st->[-1] } and $st->[-1][-1]->family eq $self->family) {
567 0         0 my $br = $st->[-1][-1];
568 0         0 $br->{data}[-1] = [ @$tree ];
569 0         0 for (@{ $br->{data}[-1] }) {
  0         0  
570 0 0 0     0 last unless $br->{zerolen} &&= $_->{zerolen};
571             }
572 0         0 push @{ $br->{data} }, [];
  0         0  
573 0         0 $rx->{tree} = $br->{data}[-1];
574             }
575              
576             # if this is the first branch
577             else {
578 6         14 $self->{data}[-1] = [ @$tree ];
579 6         7 push @{ $self->{data} }, [];
  6         13  
580 6         13 @$tree = $self;
581 6         13 $tree->[-1]{zerolen} = 1;
582 6         7 for (@{ $tree->[-1]{data}[0] }) {
  6         16  
583 6 50 33     31 last unless $tree->[-1]{zerolen} &&= $_->{zerolen};
584             }
585 6         11 push @$st, $tree;
586 6         15 $rx->{tree} = $self->{data}[-1];
587             }
588             }
589             }
590              
591              
592             {
593             package Regexp::Parser::exact;
594             our @ISA = qw( Regexp::Parser::__object__ );
595              
596             sub new {
597 43     43   86 my ($class, $rx, $data, $vis) = @_;
598 43 100       98 $vis = $data if not defined $vis;
599             my $self = bless {
600             rx => $rx,
601 43         176 flags => $rx->{flags}[-1],
602             family => 'exact',
603             data => [$data],
604             vis => [$vis],
605             }, $class;
606 43         130 return $self;
607             }
608              
609             sub visual {
610 154     154   186 my $self = shift;
611 154         205 join "", @{ $self->{vis} };
  154         484  
612             }
613              
614             sub type {
615 69     69   81 my $self = shift;
616 69 100       151 $self->{flags} & $self->{rx}->FLAG_i ? "exactf" : "exact";
617             }
618              
619             sub data {
620 1     1   3 my $self = shift;
621 1         2 join "", @{ $self->{data} };
  1         5  
622             }
623              
624             sub merge {
625 43     43   65 my ($self) = @_;
626 43         70 my $tree = $self->{rx}{tree};
627 43 50       79 return unless @$tree;
628              
629 43 50       98 push @$tree, $self unless $tree->[-1] == $self;
630 43 100       100 return unless @$tree > 1;
631 29         43 my $prev = $tree->[-2];
632 29 100       78 return unless $prev->type eq $self->type;
633            
634 5         9 push @{ $prev->{data} }, @{ $self->{data} };
  5         13  
  5         12  
635 5         7 push @{ $prev->{vis} }, @{ $self->{vis} };
  5         6  
  5         9  
636 5         8 pop @$tree;
637 5         11 return 1;
638             }
639             }
640              
641              
642             {
643             package Regexp::Parser::quant;
644             our @ISA = qw( Regexp::Parser::__object__ );
645              
646             sub new {
647 17     17   45 my ($class, $rx, $min, $max, $data) = @_;
648             my $self = bless {
649             rx => $rx,
650 17         89 flags => $rx->{flags}[-1],
651             family => 'quant',
652             data => $data,
653             min => $min,
654             max => $max,
655             }, $class;
656 17         57 return $self;
657             }
658              
659             sub min {
660 104     104   116 my $self = shift;
661 104         146 $self->{min};
662             }
663              
664             sub max {
665 104     104   118 my $self = shift;
666 104         180 $self->{max};
667             }
668              
669             sub type {
670 20     20   29 my $self = shift;
671 20         40 my ($min, $max) = ($self->min, $self->max);
672 20 100 66     95 if ($min == 0 and $max eq '') { 'star' }
  5 100 66     16  
673 6         17 elsif ($min == 1 and $max eq '') { 'plus' }
674 9         17 else { 'curly' }
675             }
676              
677             sub raw {
678 84     84   98 my $self = shift;
679 84         129 my ($min, $max) = ($self->min, $self->max);
680 84 100 100     348 if ($min == 0 and $max eq '') { '*' }
  35 100 66     120  
    100 66        
    100 66        
681 27         133 elsif ($min == 1 and $max eq '') { '+' }
682 2         13 elsif ($min == 0 and $max == 1) { '?' }
683 4         16 elsif ($max ne '' and $min == $max) { "{$min}" }
684 16         57 else { "{$min,$max}" }
685             }
686              
687             sub qr {
688 51     51   61 my $self = shift;
689 51         84 join "", $self->{data}->qr, $self->raw;
690             }
691              
692             sub visual {
693 33     33   50 my $self = shift;
694 33         83 join "", $self->{data}->visual, $self->raw;
695             }
696              
697             sub data {
698 0     0   0 my $self = shift;
699 0 0       0 if (@_) {
700 0         0 my $how = shift;
701 0 0       0 if ($how eq '=') { $self->{data} = \@_ }
  0 0       0  
702 0         0 elsif ($how eq '+') { push @{ $self->{data} }, @_ }
  0         0  
703             else {
704 0         0 my $t = $self->type;
705 0         0 Carp::croak("\$$t->data([+=], \@data)");
706             }
707             }
708 0         0 $self->{data};
709             }
710              
711             sub walk {
712 15     15   21 my ($self, $ws, $d) = @_;
713 15 100   11   61 unshift @$ws, sub { -1 }, $self->{data}, sub { +1 } if $d;
  11         22  
  11         17  
714             }
715              
716             sub insert {
717 17     17   37 my ($self, $tree) = @_;
718 17         38 my $rx = $self->{rx};
719              
720             # quantifiers must follow something
721 17 50 33     82 $rx->error($rx->RPe_EQUANT)
722             if @$tree == 0 or $tree->[-1]->family eq "flags";
723              
724             # quantifiers must NOT follow quantifiers
725 17 50       50 $rx->error($rx->RPe_NESTED)
726             if $tree->[-1]->family eq "quant";
727              
728             # on /abc+/, we extract the 'c' from the 'exact' node
729 17 50 66     31 if ($tree->[-1]->family eq "exact" and @{ $tree->[-1]->{data} } > 1) {
  8         23  
730 0         0 my $d = pop @{ $tree->[-1]->{data} };
  0         0  
731 0         0 my $v = pop @{ $tree->[-1]->{vis} };
  0         0  
732 0         0 my $q = $rx->object(exact => $d, $v);
733 0         0 $q->{flags} = $tree->[-1]->{flags};
734 0         0 $self->{data} = $q;
735 0         0 push @$tree, $self;
736             }
737             else {
738             # quantifier on (?{ ... }) is pointless;
739             # bounded quantifier (but not ?) on a
740             # zero-width assertion is unexpected
741 17 50 33     39 if (
    50 0        
      33        
      33        
      33        
742             ($tree->[-1]->family eq "assertion" and $tree->[-1]->type eq "eval") or
743             ($tree->[-1]->{zerolen} and !($self->{min} == 0 and $self->{max} == 1))
744             ) {
745 0         0 $rx->awarn($rx->RPe_ZQUANT);
746             }
747              
748             # unbounded quantifier on a zero-width
749             # assertion can match a null string a lot
750             elsif ($tree->[-1]->{zerolen} and $self->{max} eq '') {
751 0         0 $rx->awarn($rx->RPe_NULNUL, $tree->[-1]->visual . $self->raw);
752             }
753              
754 17         32 $self->{data} = $tree->[-1];
755 17         35 $tree->[-1] = $self;
756             }
757             }
758             }
759              
760              
761             {
762             # ( non-capturing
763             package Regexp::Parser::group;
764             our @ISA = qw( Regexp::Parser::__object__ );
765              
766             sub new {
767 3     3   10 my ($class, $rx, $on, $off, @data) = @_;
768             my $self = bless {
769             rx => $rx,
770 3         48 flags => $rx->{flags}[-1],
771             family => 'group',
772             type => 'group',
773             data => \@data,
774             on => $on,
775             off => $off,
776             down => 1,
777             }, $class;
778             }
779              
780             sub on {
781 0     0   0 my $self = shift;
782 0         0 $self->{on};
783             }
784              
785             sub off {
786 0     0   0 my $self = shift;
787 0         0 $self->{off};
788             }
789              
790             sub raw {
791 9     9   20 my $self = shift;
792             join "", "(?", $self->{on},
793 9 50       33 (length $self->{off} ? "-" : ""), $self->{off}, ":";
794             }
795              
796             sub qr {
797 6     6   11 my $self = shift;
798 6         8 join "", $self->raw, map($_->qr, @{ $self->{data} }), ")";
  6         19  
799             }
800              
801             sub visual {
802 3     3   5 my $self = shift;
803 3         16 join "", $self->raw, map($_->visual, @{ $self->{data} }), ")";
  3         14  
804             }
805              
806             sub data {
807 0     0   0 my $self = shift;
808 0 0       0 if (@_) {
809 0         0 my $how = shift;
810 0 0       0 if ($how eq '=') { $self->{data} = \@_ }
  0 0       0  
811 0         0 elsif ($how eq '+') { push @{ $self->{data} }, @_ }
  0         0  
812             else {
813 0         0 my $t = $self->type;
814 0         0 Carp::croak("\$$t->data([+=], \@data)");
815             }
816             }
817 0         0 $self->{data};
818             }
819              
820             sub walk {
821 1     1   3 my ($self, $ws, $d) = @_;
822 1         3 unshift @$ws, $self->{rx}->object(@{ $self->ender });
  1         5  
823 1 50   1   13 unshift @$ws, sub { -1 }, @{ $self->{data} }, sub { +1 } if $d;
  1         2  
  1         6  
  1         3  
824             }
825              
826             sub insert {
827 3     3   7 my ($self, $tree) = @_;
828 3         12 my $rx = $self->{rx};
829 3         6 push @$tree, $self;
830 3         4 push @{ $rx->{stack} }, $tree;
  3         6  
831 3         7 $rx->{tree} = $self->{data};
832             }
833             }
834              
835              
836             {
837             # ( capturing
838             package Regexp::Parser::open;
839             our @ISA = qw( Regexp::Parser::__object__ );
840              
841             sub new {
842 7     7   13 my ($class, $rx, $nparen, @data) = @_;
843             my $self = bless {
844             rx => $rx,
845 7         41 flags => $rx->{flags}[-1],
846             family => 'open',
847             nparen => $nparen,
848             data => \@data,
849             raw => '(',
850             down => 1,
851             }, $class;
852 7         21 $self->{rx}{captures}[$nparen - 1] = $self;
853 7         24 return $self;
854             }
855              
856             sub type {
857 0     0   0 my $self = shift;
858 0         0 $self->family . $self->nparen;
859             }
860              
861             sub nparen {
862 12     12   235 my $self = shift;
863 12         30 $self->{nparen};
864             }
865              
866             sub qr {
867 6     6   8 my $self = shift;
868 6         10 join "", $self->raw, map($_->qr, @{ $self->{data} }), ")";
  6         12  
869             }
870              
871             sub visual {
872 10     10   13 my $self = shift;
873 10         20 join "", $self->raw, map($_->visual, @{ $self->{data} }), ")";
  10         22  
874             }
875              
876             sub ender {
877 0     0   0 my $self = shift;
878 0         0 [ close => $self->nparen ];
879             }
880              
881             sub data {
882 0     0   0 my $self = shift;
883 0 0       0 if (@_) {
884 0         0 my $how = shift;
885 0 0       0 if ($how eq '=') { $self->{data} = \@_ }
  0 0       0  
886 0         0 elsif ($how eq '+') { push @{ $self->{data} }, @_ }
  0         0  
887             else {
888 0         0 my $t = $self->type;
889 0         0 Carp::croak("\$$t->data([+=], \@data)");
890             }
891             }
892 0         0 $self->{data};
893             }
894              
895             sub walk {
896 0     0   0 my ($self, $ws, $d) = @_;
897 0         0 unshift @$ws, $self->{rx}->object(@{ $self->ender });
  0         0  
898 0 0   0   0 unshift @$ws, sub { -1 }, @{ $self->{data} }, sub { +1 } if $d;
  0         0  
  0         0  
  0         0  
899             }
900              
901             sub insert {
902 7     7   11 my ($self, $tree) = @_;
903 7         9 my $rx = $self->{rx};
904 7         12 push @$tree, $self;
905 7         10 push @{ $rx->{stack} }, $tree;
  7         10  
906 7         13 $rx->{tree} = $self->{data};
907             }
908             }
909              
910              
911             {
912             # ) closing
913             package Regexp::Parser::close;
914             our @ISA = qw( Regexp::Parser::__object__ );
915              
916             sub new {
917 20     20   40 my ($class, $rx, $nparen) = @_;
918             my $self = bless {
919             rx => $rx,
920 20         74 flags => $rx->{flags}[-1],
921             family => 'close',
922             nparen => $nparen,
923             raw => ')',
924             omit => 1,
925             up => 1,
926             }, $class;
927 20         53 return $self;
928             }
929              
930             sub type {
931 0     0   0 my $self = shift;
932 0         0 $self->family . $self->nparen;
933             }
934              
935             sub nparen {
936 0     0   0 my $self = shift;
937 0         0 $self->{nparen};
938             }
939              
940             sub insert {
941 20     20   34 my ($self, $tree) = @_;
942 20         35 my $rx = $self->{rx};
943              
944             do {
945 26 50       26 $tree = pop @{ $rx->{stack} }
  26         93  
946             or $rx->error($rx->RPe_RPAREN)
947 20         27 } until $tree->[-1]->{down};
948              
949 20         26 $rx->{tree} = $tree;
950              
951 20 100 66     57 $self->{nparen} = $tree->[-1]->nparen
952             if $self->family eq 'close' and $tree->[-1]->can('nparen');
953              
954 20 100       52 if ($tree->[-1]->{ifthen}) {
955 6         7 my $ifthen = $tree->[-1];
956 6         12 my $br = $rx->object(branch =>);
957 6         7 my $cond;
958              
959 6 50       14 if (ref $ifthen->{data}[0] eq "ARRAY") {
960 6         7 (my($true), $cond) = splice @{ $ifthen->{data} }, 0, 2;
  6         13  
961 6         13 $br->{data} = [ $true, $ifthen->{data} ];
962             }
963             else {
964 0         0 $cond = shift @{ $ifthen->{data} };
  0         0  
965 0         0 $br->{data} = [ $ifthen->{data} ];
966             }
967              
968 6         10 $ifthen->{data} = [ $cond, $br ];
969             $ifthen->{zerolen} =
970 6         7 !grep !(grep $_->{zerolen}, @$_), @{ $ifthen->{data}[1]{data} };
  6         35  
971             }
972             else {
973             $tree->[-1]->{zerolen} ||=
974 14   66     57 !grep !$_->{zerolen}, @{ $tree->[-1]->{data} };
  10         41  
975             }
976              
977 20 50       50 push @$tree, $self unless $self->omit;
978             }
979             }
980              
981              
982             {
983             # ) for non-captures
984             package Regexp::Parser::tail;
985             our @ISA = qw( Regexp::Parser::__object__ );
986              
987             sub new {
988 11     11   18 my ($class, $rx) = @_;
989             my $self = bless {
990             rx => $rx,
991 11         47 flags => $rx->{flags}[-1],
992             family => 'close',
993             type => 'tail',
994             raw => ')',
995             omit => 1,
996             up => 1,
997             }, $class;
998 11         25 return $self;
999             }
1000              
1001             sub insert {
1002 0     0   0 my ($self, $tree) = @_;
1003 0         0 my $rx = $self->{rx};
1004              
1005             do {
1006 0 0       0 $rx->{tree} = pop @{ $rx->{stack} }
  0         0  
1007             or $rx->error($rx->RPe_RPAREN)
1008 0         0 } until $tree->[-1]->{down};
1009              
1010 0 0 0     0 $self->{nparen} = $tree->[-1]->nparen
1011             if $self->family eq 'close' and $tree->[-1]->can('nparen');
1012              
1013 0 0       0 if ($tree->[-1]->{ifthen}) {
1014 0         0 my $ifthen = $tree->[-1];
1015 0         0 my $br = $rx->object(branch =>);
1016 0         0 my $cond;
1017              
1018 0 0       0 if (ref $ifthen->{data}[0] eq "ARRAY") {
1019 0         0 (my($true), $cond) = splice @{ $ifthen->{data} }, 0, 2;
  0         0  
1020 0         0 $br->{data} = [ $true, $ifthen->{data} ];
1021             }
1022             else {
1023 0         0 $cond = shift @{ $ifthen->{data} };
  0         0  
1024 0         0 $br->{data} = [ $ifthen->{data} ];
1025             }
1026              
1027 0         0 $ifthen->{data} = [ $cond, $br ];
1028             $ifthen->{zerolen} =
1029 0         0 !grep !(grep $_->{zerolen}, @$_), @{ $ifthen->{data}[1]{data} };
  0         0  
1030             }
1031             else {
1032             $tree->[-1]->{zerolen} ||=
1033 0   0     0 !grep !$_->{zerolen}, @{ $tree->[-1]->{data} };
  0         0  
1034             }
1035              
1036 0 0       0 push @$tree, $self unless $self->omit;
1037             }
1038             }
1039              
1040              
1041             {
1042             # \1 (backrefs)
1043             package Regexp::Parser::ref;
1044             our @ISA = qw( Regexp::Parser::__object__ );
1045              
1046             sub new {
1047 0     0   0 my ($class, $rx, $nparen) = @_;
1048             my $self = bless {
1049             rx => $rx,
1050 0         0 flags => $rx->{flags}[-1],
1051             family => 'ref',
1052             nparen => $nparen,
1053             }, $class;
1054 0         0 return $self;
1055             }
1056              
1057             sub type {
1058 0     0   0 my $self = shift;
1059 0 0       0 ($self->{flags} & $self->{rx}->FLAG_i ? 'reff' : 'ref') . $self->nparen;
1060             }
1061              
1062             sub nparen {
1063 0     0   0 my $self = shift;
1064 0         0 $self->{nparen};
1065             }
1066              
1067             sub visual {
1068 0     0   0 my $self = shift;
1069 0         0 "\\$self->{nparen}";
1070             }
1071             }
1072              
1073              
1074             {
1075             package Regexp::Parser::assertion;
1076             our @ISA = qw( Regexp::Parser::__object__ );
1077              
1078             push @Regexp::Parser::ifmatch::ISA, __PACKAGE__;
1079             push @Regexp::Parser::unlessm::ISA, __PACKAGE__;
1080             push @Regexp::Parser::suspend::ISA, __PACKAGE__;
1081             push @Regexp::Parser::ifthen::ISA, __PACKAGE__;
1082             push @Regexp::Parser::eval::ISA, __PACKAGE__;
1083             push @Regexp::Parser::logical::ISA, __PACKAGE__;
1084              
1085             sub qr {
1086 6     6   26 my $self = shift;
1087 6         16 join "", $self->raw, map($_->qr, @{ $self->{data} }), ")";
  6         18  
1088             }
1089              
1090             sub visual {
1091 8     8   8 my $self = shift;
1092 8         15 join "", $self->raw, map($_->visual, @{ $self->{data} }), ")";
  8         24  
1093             }
1094              
1095             sub data {
1096 0     0   0 my $self = shift;
1097 0 0       0 if (@_) {
1098 0         0 my $how = shift;
1099 0 0       0 if ($how eq '=') { $self->{data} = \@_ }
  0 0       0  
1100 0         0 elsif ($how eq '+') { push @{ $self->{data} }, @_ }
  0         0  
1101             else {
1102 0         0 my $t = $self->type;
1103 0         0 Carp::croak("\$$t->data([+=], \@data)");
1104             }
1105             }
1106 0         0 $self->{data};
1107             }
1108              
1109             sub walk {
1110 10     10   16 my ($self, $ws, $d) = @_;
1111 10         19 unshift @$ws, $self->{rx}->object(@{ $self->ender });
  10         20  
1112 10 50   10   42 unshift @$ws, sub { -1 }, @{ $self->{data} }, sub { +1 } if $d;
  10         18  
  10         44  
  10         17  
1113             }
1114              
1115             sub insert {
1116 10     10   16 my ($self, $tree) = @_;
1117 10         21 my $rx = $self->{rx};
1118 10         14 push @$tree, $self;
1119 10         11 push @{ $rx->{stack} }, $tree;
  10         15  
1120 10         18 $rx->{tree} = $self->{data};
1121             }
1122             }
1123              
1124              
1125             {
1126             # (?=) (?<=)
1127             package Regexp::Parser::ifmatch;
1128              
1129             sub new {
1130 5     5   12 my ($class, $rx, $dir, @data) = @_;
1131             my $self = bless {
1132             rx => $rx,
1133 5         45 flags => $rx->{flags}[-1],
1134             family => 'assertion',
1135             type => 'ifmatch',
1136             dir => $dir,
1137             data => \@data,
1138             down => 1,
1139             zerolen => 1,
1140             }, $class;
1141 5         28 return $self;
1142             }
1143              
1144             sub dir {
1145 0     0   0 my $self = shift;
1146 0         0 $self->{dir};
1147             }
1148              
1149             sub raw {
1150 7     7   9 my $self = shift;
1151 7 100       26 join "", "(?", ($self->{dir} < 0 ? '<' : ''), "=";
1152             }
1153             }
1154              
1155              
1156             {
1157             # (?!) (?
1158             package Regexp::Parser::unlessm;
1159              
1160             sub new {
1161 5     5   14 my ($class, $rx, $dir, @data) = @_;
1162             my $self = bless {
1163             rx => $rx,
1164 5         45 flags => $rx->{flags}[-1],
1165             family => 'assertion',
1166             type => 'unlessm',
1167             dir => $dir,
1168             data => \@data,
1169             down => 1,
1170             zerolen => 1,
1171             }, $class;
1172 5         18 return $self;
1173             }
1174              
1175             sub dir {
1176 0     0   0 my $self = shift;
1177 0         0 $self->{dir};
1178             }
1179              
1180             sub raw {
1181 7     7   9 my $self = shift;
1182 7 100       33 join "", "(?", ($self->{dir} < 0 ? '<' : ''), "!";
1183             }
1184             }
1185              
1186              
1187             {
1188             # (?>)
1189             package Regexp::Parser::suspend;
1190              
1191             sub new {
1192 0     0   0 my ($class, $rx, @data) = @_;
1193             my $self = bless {
1194             rx => $rx,
1195 0         0 flags => $rx->{flags}[-1],
1196             family => 'assertion',
1197             type => 'suspend',
1198             data => \@data,
1199             down => 1,
1200             }, $class;
1201 0         0 return $self;
1202             }
1203              
1204             sub raw {
1205 0     0   0 my $self = shift;
1206 0         0 "(?>";
1207             }
1208             }
1209              
1210             {
1211             # (?(n)t|f)
1212             package Regexp::Parser::ifthen;
1213              
1214             sub new {
1215 6     6   14 my ($class, $rx, @data) = @_;
1216             my $self = bless {
1217             rx => $rx,
1218 6         28 flags => $rx->{flags}[-1],
1219             family => 'assertion',
1220             type => 'ifthen',
1221             data => [],
1222             down => 1,
1223             ifthen => 1,
1224             }, $class;
1225 6         27 return $self;
1226             }
1227              
1228             sub raw {
1229 6     6   8 my $self = shift;
1230 6         22 "(?";
1231             }
1232              
1233             sub qr {
1234 0     0   0 my $self = shift;
1235 0         0 join "", $self->raw, $self->{data}[0]->qr, $self->{data}[1]->qr, ")";
1236             }
1237              
1238             sub visual {
1239 6     6   8 my $self = shift;
1240 6         18 join "", $self->raw, $self->{data}[0]->visual, $self->{data}[1]->visual, ")";
1241             }
1242             }
1243              
1244              
1245             {
1246             # the N in (?(N)t|f) when N is a number
1247             package Regexp::Parser::groupp;
1248             our @ISA = qw( Regexp::Parser::__object__ );
1249              
1250             sub new {
1251 1     1   3 my ($class, $rx, $nparen) = @_;
1252             my $self = bless {
1253             rx => $rx,
1254 1         6 flags => $rx->{flags}[-1],
1255             family => 'groupp',
1256             nparen => $nparen,
1257             }, $class;
1258 1         3 return $self;
1259             }
1260              
1261             sub type {
1262 3     3   6 my $self = shift;
1263 3         7 $self->family . $self->nparen;
1264             }
1265              
1266             sub nparen {
1267 3     3   6 my $self = shift;
1268 3         8 $self->{nparen};
1269             }
1270              
1271             sub visual {
1272 2     2   5 my $self = shift;
1273 2         6 "($self->{nparen})";
1274             }
1275             }
1276              
1277              
1278             {
1279             # (?{ ... })
1280             package Regexp::Parser::eval;
1281              
1282             sub new {
1283 1     1   4 my ($class, $rx, $code) = @_;
1284             my $self = bless {
1285             rx => $rx,
1286 1         6 flags => $rx->{flags}[-1],
1287             family => 'assertion',
1288             type => 'eval',
1289             data => $code,
1290             zerolen => 1,
1291             }, $class;
1292 1         6 return $self;
1293             }
1294              
1295             sub visual {
1296 2     2   3 my $self = shift;
1297 2         6 "(?{$self->{data}})";
1298             }
1299              
1300             sub qr {
1301 0     0   0 my $self = shift;
1302 0         0 $self->visual;
1303             }
1304              
1305             sub insert {
1306 1     1   2 my ($self, $tree) = @_;
1307 1         3 push @$tree, $self;
1308             }
1309              
1310             sub walk {
1311 1     1   3 my $self = shift;
1312 1         2 return;
1313             }
1314             }
1315              
1316              
1317             {
1318             # (??{ ... })
1319             package Regexp::Parser::logical;
1320              
1321             sub new {
1322 0     0   0 my ($class, $rx, $code) = @_;
1323             my $self = bless {
1324             rx => $rx,
1325 0         0 flags => $rx->{flags}[-1],
1326             family => 'assertion',
1327             type => 'logical',
1328             data => $code,
1329             zerolen => 1,
1330             }, $class;
1331 0         0 return $self;
1332             }
1333              
1334             sub visual {
1335 0     0   0 my $self = shift;
1336 0         0 "(??{$self->{data}})";
1337             }
1338              
1339             sub qr {
1340 0     0   0 my $self = shift;
1341 0         0 $self->visual;
1342             }
1343              
1344             sub insert {
1345 0     0   0 my ($self, $tree) = @_;
1346 0         0 push @$tree, $self;
1347             }
1348              
1349             sub walk {
1350 0     0   0 my $self = shift;
1351 0         0 return;
1352             }
1353             }
1354              
1355              
1356             {
1357             package Regexp::Parser::flags;
1358             our @ISA = qw( Regexp::Parser::__object__ );
1359              
1360             sub new {
1361 2     2   5 my ($class, $rx, $on, $off) = @_;
1362             my $self = bless {
1363             rx => $rx,
1364 2         9 flags => $rx->{flags}[-1],
1365             family => 'flags',
1366             type => 'flags',
1367             on => $on,
1368             off => $off,
1369             zerolen => 1,
1370             }, $class;
1371 2         8 return $self;
1372             }
1373              
1374             sub on {
1375 0     0   0 my $self = shift;
1376 0         0 $self->{on};
1377             }
1378              
1379             sub off {
1380 0     0   0 my $self = shift;
1381 0         0 $self->{off};
1382             }
1383              
1384             sub visual {
1385 2     2   3 my $self = shift;
1386             join "", "(?", $self->{on},
1387 2 100       20 (length $self->{off} ? "-" : ""), $self->{off}, ")";
1388             }
1389             }
1390              
1391              
1392             {
1393             package Regexp::Parser::minmod;
1394             our @ISA = qw( Regexp::Parser::__object__ );
1395              
1396             sub new {
1397 2     2   8 my ($class, $rx, $data) = @_;
1398             my $self = bless {
1399             rx => $rx,
1400 2         12 flags => $rx->{flags}[-1],
1401             family => 'minmod',
1402             type => 'minmod',
1403             raw => '?',
1404             data => $data,
1405             }, $class;
1406 2         6 return $self;
1407             }
1408              
1409             sub qr {
1410 11     11   13 my $self = shift;
1411 11         22 join "", $self->{data}->qr, $self->raw;
1412             }
1413              
1414             sub visual {
1415 5     5   8 my $self = shift;
1416 5         11 join "", $self->{data}->visual, $self->raw;
1417             }
1418              
1419             sub walk {
1420 4     4   8 my ($self, $ws, $d) = @_;
1421 4 100   3   20 unshift @$ws, sub { -1 }, $self->{data}, sub { +1 } if $d;
  3         6  
  3         6  
1422             }
1423              
1424             sub insert {
1425 2     2   6 my ($self, $tree) = @_;
1426 2         9 $self->{data} = $tree->[-1];
1427 2         5 $tree->[-1] = $self;
1428             }
1429             }
1430              
1431             1;
1432              
1433             __END__