File Coverage

blib/lib/Regexp/Optimizer.pm
Criterion Covered Total %
statement 69 84 82.1
branch 25 46 54.3
condition 4 6 66.6
subroutine 11 12 91.6
pod 3 3 100.0
total 112 151 74.1


line stmt bran cond sub pod time code
1             #
2             # $Id: Optimizer.pm,v 0.15 2004/12/05 16:07:34 dankogai Exp dankogai $
3             #
4             package Regexp::Optimizer;
5 4     4   61284 use 5.006; # qr/(??{}/ needed
  4         12  
  4         205  
6 4     4   21 use strict;
  4         6  
  4         159  
7 4     4   28 use warnings;
  4         6  
  4         151  
8 4     4   22 use base qw/Regexp::List/;
  4         6  
  4         2018  
9 4     4   350213 use charnames qw();
  4         110912  
  4         2491  
10             our $VERSION = '0.16_001';
11              
12             #our @EXPORT = qw();
13             #our %EXPORT_TAGS = ( 'all' => [ qw() ] );
14             #our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
15             #our $DEBUG = 0;
16              
17             # see perldoc perlop
18              
19             # perldoc perlop on perl 5.8.4 or later
20             #
21             # Pragmata are now correctly propagated into (?{...}) constructions in
22             # regexps. Code such as
23             #
24             # my $x = qr{ ... (??{ $x }) ... };
25             #
26             # will now (correctly) fail under use strict. (As the inner $x is
27             # and has always referred to $::x)
28              
29             our $RE_PAREN; # predeclear
30             $RE_PAREN =
31             qr{
32             \(
33             (?:
34             (?> [^()]+ )
35             |
36             (??{ $RE_PAREN })
37             )*
38             \)
39             }xo;
40             our $RE_EXPR; # predeclear
41             $RE_EXPR =
42             qr{
43             \{
44             (?:
45             (?> [^{}]+ )
46             |
47             (??{ $RE_EXPR })
48             )*
49             \}
50             }xo;
51             our $RE_PIPE = qr/(?!\\)\|/o;
52             our $RE_CHAR =
53             qr{(?:
54             # single character...
55             (?!\\)[^\\\[(|)\]] | # raw character except '[(|)]'
56             $Regexp::List::RE_XCHAR | # extended characters
57             )}xo;
58             our $RE_CCLASS =
59             qr{(?:
60             (?!\\)\[ $RE_CHAR+? \] |
61             $Regexp::List::RE_XCHAR | # extended characters
62             (?!\\)[^(|)] | # raw character except '[(|)]'
63             # Note pseudo-characters are not included
64             )}xo;
65             our $RE_QUANT =
66             qr{(?:
67             (?!\\)
68             (?:
69             \? |
70             \+ |
71             \* |
72             \{[\d,]+\}
73             )\??
74             )}xo;
75             our $RE_TOKEN =
76             qr{(?:
77             (?:
78             \\[ULQ] (?:$RE_CHAR+)(?:\\E|$) | # [ul]c or quotemeta
79             $Regexp::List::RE_PCHAR | # pseudo-characters
80             $RE_CCLASS |
81             $RE_CHAR
82             )
83             $RE_QUANT?
84             )}xo;
85             our $RE_START = $Regexp::List::RE_START;
86              
87             our %PARAM = (meta => 1,
88             quotemeta => 0,
89             lookahead => 0,
90             optim_cc => 1,
91             modifiers => '',
92             _char => $RE_CHAR,
93             _token => $RE_TOKEN,
94             _cclass => $RE_CCLASS,
95             );
96              
97             sub new{
98 4 50   4 1 410 my $class = ref $_[0] ? ref shift : shift;
99 4         36 my $self = $class->SUPER::new;
100 4         26 $self->set(%PARAM, @_);
101 4         8 $self;
102             }
103              
104             sub list2re{
105 25     25 1 55 shift->SUPER::list2re(map {_strip($_)} @_);
  480         486  
106             }
107              
108             sub optimize{
109 27     27 1 4930 my $self = shift;
110 27         37 my $str = shift;
111 27 50       71 $self->{unexpand} and $str = $self->unexpand($str);
112             # safetey feature against qq/(?:foo)(?:bar)/
113 27 50 66     485 !ref $str and $str =~ /^$RE_START/ and $str = qr/$str/;
114 27         66 my $re = $self->_optimize($str);
115 27         1606 qr/$re/;
116             }
117              
118             sub _strip{
119 508     508   418 my ($str, $force) = @_;
120 508 100 66     1732 $force or ref $str eq 'Regexp' or return $str;
121 28 100       232 $str =~ s/^($RE_START)//o or return $str;
122 2         7 my $regopt = $1; $str =~ s/\)$//o;
  2         10  
123 2         7 $regopt =~ s/^\(\??//o;
124 2 50       7 $regopt =~ /^[-:]/ and $regopt = undef;
125 2         9 ($str, $regopt);
126             }
127              
128             my %my_l2r_opts =
129             (
130             as_string => 1,
131             debug => 0,
132             _token => qr/$RE_PAREN$RE_QUANT?|$RE_PIPE|$RE_TOKEN/,
133             );
134              
135             sub _optimize{
136 4     4   38 no warnings 'uninitialized';
  4         7  
  4         2603  
137 28     28   34 my $self = shift;
138 28 50       64 $self->{debug} and $self->{_indent}++;
139 28 50       112 $self->{debug} and
140             print STDERR '>'x $self->{_indent}, " ", $_[0], "\n";
141 28         72 my ($result, $regopt) = _strip(shift, 1);
142 28         97 $result =~ s/\\([()])/"\\x" . sprintf("%X", ord($1))/ego;
  0         0  
143 28         78 $result =~ s/\\N{(.+?)}/"\\x" . sprintf("{%X}", charnames::vianame($1))/ego;
  6         206  
144             # $result =~ s/(\s)/"\\x" . sprintf("%X", ord($1))/ego;
145 28 100       9741 $result !~ /$RE_PIPE/ and goto RESULT;
146 26         151 my $l = $self->clone->set(%my_l2r_opts);
147             # optimize
148 26 100       131 unless ($result =~ /$RE_PAREN/){
149 25         490 my @words = split /$RE_PIPE/ => $result;
150 25         117 $result = $l->list2re(@words);
151 25         270 goto RESULT;
152             }
153 1         2 my (@term, $sp);
154 1         5 while ($result){
155 3 100       653 if ($result =~ s/^($RE_PAREN)($RE_QUANT?)//){
    50          
    50          
156 1         7 my ($term, $quant) = ($1, $2);
157 1         10 $term = $self->_optimize($term);
158 1 50       6 $l->{optim_cc} = $quant ? 0 : 1;
159 1 50       23 if ($quant){
160 0 0       0 if ($term =~ /^$self->{_cclass}$/){
161 0         0 $term .= $quant;
162             }else{
163 0         0 $term = $self->{po} . $term . $self->{pc} . $quant;
164             }
165             }
166 1         11 $term[$sp] .= $term;
167             }elsif($result =~ s/^$RE_PIPE//){
168 0         0 $sp += 2;
169 0         0 push @term, '|';
170             }elsif($result =~ s/^($RE_TOKEN+)//){
171             # warn $1;
172 2         23 $term[$sp] .= $1;
173             }else{
174 0         0 die "something is wrong !";
175             }
176             }
177             # warn scalar @term , ";", join(";" => @term);
178             # sleep 1;
179 1         2 my @stack;
180 1         5 while (my $term = shift @term){
181 1 50       4 if ($term eq '|'){
182 0         0 push @stack, $l->list2re(pop @stack, shift @term);
183             }else{
184 1         5 push @stack, $term;
185             }
186             }
187 1         5 $result = join('' => @stack);
188 28 100       52 RESULT:
189             $result = $regopt ? qq/(?$regopt$result)/ : $result;
190             # warn qq($result, $regopt);
191 28 50       72 $self->{debug} and
192             print STDERR '<'x $self->{_indent}, " ", $result, "\n";
193 28 50       56 $self->{debug} and $self->{_indent}--;
194 28         233 $result;
195             }
196              
197             sub _pair2re{
198 0     0     my $self = shift;
199 0 0         $_[0] eq $_[1] and return $_[0];
200 0 0         my ($first, $second) =
201             length $_[0] <= length $_[1] ? @_ : ($_[1], $_[0]);
202 0           my $l = length($first);
203 0           $l -= 1
204             while $self->_head($first, $l) ne $self->_head($second, $l);
205 0 0         $l > 0 or return join("", @_);
206 0           return $self->_head($first, $l) .
207             $self->{po} .
208             $self->_tail($first, $l) . '|' . $self->_tail($second, $l) .
209             $self->{pc};
210             }
211              
212             1;
213             __END__