File Coverage

blib/lib/Regexp/Parser/Objects.pm
Criterion Covered Total %
statement 319 567 56.2
branch 66 182 36.2
condition 30 69 43.4
subroutine 90 151 59.6
pod n/a
total 505 969 52.1


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