File Coverage

blib/lib/Regexp/Optimizer.pm
Criterion Covered Total %
statement 48 48 100.0
branch 13 14 92.8
condition n/a
subroutine 9 9 100.0
pod 3 3 100.0
total 73 74 98.6


line stmt bran cond sub pod time code
1             package Regexp::Optimizer;
2              
3 2     2   29148 use 5.008001;
  2         7  
  2         100  
4 2     2   12 use strict;
  2         3  
  2         77  
5 2     2   10 use warnings FATAL => 'all';
  2         8  
  2         85  
6 2     2   2740 use Regexp::Assemble;
  2         55202  
  2         619  
7             our $VERSION = sprintf "%d.%02d", q$Revision: 0.23 $ =~ /(\d+)/g;
8              
9             my $re_nested;
10             $re_nested = qr{
11             \( # open paren
12             ((?: # start capture
13             (?>[^()]+) | # Non-parens w/o backtracking or ...
14             (??{ $re_nested }) # Group with matching parens
15             )*) # end capture
16             \) # close paren
17             }msx;
18              
19             my $re_optimize = qr{(?<=[^\\])\|}ms;
20              
21             sub new {
22 1     1 1 764 my $class = shift;
23 1         7 bless {@_}, $class;
24             }
25              
26             sub _assemble {
27 24     24   37 my $str = shift;
28 24 50       143 return $str if $str !~ $re_optimize;
29 24 100       75 if ( $str !~ m/[(]/ms ) {
30 7         32 my $ra = Regexp::Assemble->new();
31 7         507 $ra->add( split m{[|]}, $str );
32 7         2128 return $ra->as_string;
33             }
34 17         109 $str =~ s{$re_nested}{
35 2     2   24 no warnings 'uninitialized';
  2         4  
  2         6624  
36 17         44 my $sub = $1;
37 17 100       67 if ($sub =~ m/\A\?(?:[\?\{\(PR]|[\+\-]?[0-9])/ms) {
38 2         8 "($sub)"; # (?{CODE}) and like ruled out
39             }else{
40 15 100       71 my $mod = ($sub =~ s/\A\?//) ? '?' : '';
41 15 100       41 if ($mod) {
42 14         52 $sub =~ s{\A(
43             [\w\^\-]*: | # modifier
44             [<]?[=!] | # assertions
45             [<]\w+[>] | # named capture
46             [']\w+['] | # ditto
47             [|] # branch reset
48             )
49             }{}msx;
50 14         34 $mod .= $1;
51             }
52 15         44 '(' . $mod . _assemble($sub) . ')'
53             }
54             }msxge;
55 17         4337 $str;
56             }
57              
58             sub as_string {
59 10     10 1 2383 my ( $self, $str ) = @_;
60 10 100       86 return $str if $str !~ $re_optimize;
61 9         49 my ($mod) = ($str =~ m/\A\(\?(.*?):/);
62 9 100       120 if ( $mod =~ /x/ ) {
63 1         11 $str =~ s{^\s+}{}mg;
64 1         12 $str =~ s{(?<=[^\\])\s*?#.*?$}{}mg;
65 1         8 $str =~ s{\s+[|]\s+}{|}mg;
66 1         12 $str =~ s{(?:\r\n?|\n)}{}msg;
67 1         3 $str =~ s{[ ]+}{ }msgx;
68             # warn $str;
69             }
70             # escape all occurance of '\(' and '\)'
71 9         25 $str =~ s/\\([\(\)])/sprintf "\\x%02x" , ord $1/ge;
  2         58  
72 9         24 _assemble($str);
73             }
74              
75             sub optimize {
76 1     1 1 3 my $self = shift;
77 1         5 my $re = $self->as_string(shift);
78 1         8 qr{$re};
79             }
80              
81             1; # End of Regexp::Optimizer
82              
83             __END__