File Coverage

blib/lib/Pugs/Compiler/Regex.pm
Criterion Covered Total %
statement 77 114 67.5
branch 18 46 39.1
condition 15 30 50.0
subroutine 15 24 62.5
pod 7 8 87.5
total 132 222 59.4


line stmt bran cond sub pod time code
1             package Pugs::Compiler::Regex;
2              
3             #use Smart::Comments;
4              
5             our $VERSION = '0.28';
6             # Documentation in the __END__
7 17     17   3211 use strict;
  17         35  
  17         596  
8 17     17   99 use warnings;
  17         37  
  17         454  
9              
10 17     17   9555 use Pugs::Grammar::Rule;
  17         54  
  17         966  
11 17     17   8067 use Pugs::Compiler::RegexPerl5;
  17         45  
  17         488  
12              
13 17     17   13159 use Pugs::Emitter::Rule::Perl5;
  17         80  
  17         1910  
14 17     17   180 use Pugs::Emitter::Rule::Perl5::Ratchet;
  17         44  
  17         381  
15              
16 17     17   105 use Pugs::Runtime::Regex;
  17         51  
  17         804  
17              
18             # complete the dependency circularity
19             push @Pugs::Grammar::Rule::ISA, 'Pugs::Grammar::Base';
20              
21 17     17   110 use Carp qw( croak carp );
  17         40  
  17         1414  
22 17     17   111 use Data::Dumper;
  17         42  
  17         945  
23 17     17   21896 use Symbol 'qualify_to_ref';
  17         19975  
  17         1450  
24 17     17   137 use Digest::MD5 'md5_hex';
  17         44  
  17         26302  
25              
26             our $NoCache = $ENV{PCR_NO_CACHE}; # Depresses any caching if set to true
27              
28             my $cache;
29             eval {
30             require Cache::FileCache;
31             $cache = new Cache::FileCache( { 'namespace' => 'v6-rules' } );
32             };
33              
34 0     0 0 0 sub new { $_[0] }
35              
36             sub compile {
37 15     15 1 181 local $::_V6_MATCH_; # avoid messing with global $/
38              
39             # $class->compile( $source )
40             # $class->compile( $source, { p=>1 } )
41             # $class->compile( $source, { signature => $sig } ) -- TODO
42              
43 15         47 my ( $class, $rule_source, $param ) = @_;
44              
45 15 100 66     245 return Pugs::Compiler::RegexPerl5->compile( $rule_source, $param )
46             if exists $param->{P5} || exists $param->{Perl5};
47             #warn length($rule_source);
48              
49 13         57 my $self = { source => $rule_source };
50              
51             #print Dumper @_;
52              
53             # XXX - should use user's lexical pad instead of an explicit grammar?
54 13   50     131 $self->{grammar} = delete $param->{grammar} ||
55             'Pugs::Grammar::Base';
56 13   100     97 $self->{ratchet} = delete $param->{ratchet} ||
57             0;
58              
59 13   33     133 $self->{p} = delete $param->{pos} ||
60             delete $param->{p};
61             # default = undef;
62 13         37 delete $param->{p};
63              
64 13   100     148 $self->{sigspace} = delete $param->{sigspace} ||
65             delete $param->{s} ||
66             0;
67 13         32 delete $param->{s};
68              
69 13   50     147 $self->{continue} = delete $param->{continue} ||
70             delete $param->{c} ||
71             0;
72 13         33 delete $param->{c};
73              
74 13   50     157 $self->{ignorecase} = delete $param->{ignorecase} ||
75             delete $param->{i} ||
76             0;
77 13         29 delete $param->{i};
78              
79 13         28 my $error;
80             $error .= "Error in rule: unknown parameter '$_'"
81 13         69 for keys %$param;
82 13 50       67 croak $error if %$param;
83              
84 13         144 my $digest = md5_hex(Dumper($self));
85 13         1865 my $cached;
86              
87 13 50 66     135 if (!$NoCache && $cache && ($cached = $cache->get($digest))) {
      33        
88             ### using cached rule...
89 0         0 $self->{perl5} = $cached;
90             }
91             else {
92             ### compiling rule...
93              
94             #print 'rule source: ', $self->{source}, "\n";
95             #print "match: ", Dumper( Pugs::Grammar::Rule->rule( $self->{source} ) );
96 13         167 my $ast = Pugs::Grammar::Rule->rule(
97             $self->{source} )->();
98 0 0       0 if (!defined $ast) {
99 0         0 carp "Invalid regex syntax";
100 0         0 return undef;
101             }
102             ### rule AST: $ast
103              
104             # save the ast for debugging
105 0         0 $self->{ast} = $ast;
106              
107             #warn "ast: ",Dumper($ast),"\n";
108             #die "Error in rule: '$rule_source' at: '$ast->tail'\n" if $ast->tail;
109             #print 'rule ast: ', do{use Data::Dumper; Dumper($ast{capture})};
110              
111             #use Pugs::Emitter::Rule::Perl5::Preprocess;
112             #my $ast2 = Pugs::Emitter::Rule::Perl5::Preprocess::emit(
113             # $self->{grammar}, $ast, $self );
114              
115 0 0       0 if ( $self->{ratchet} ) {
116 0         0 $self->{perl5} = Pugs::Emitter::Rule::Perl5::Ratchet::emit(
117             $self->{grammar}, $ast, $self );
118             #print "token: ", $self->{perl5};
119             }
120             else {
121 0         0 $self->{perl5} = Pugs::Emitter::Rule::Perl5::emit(
122             $self->{grammar}, $ast, $self );
123             }
124             #print 'rule perl5: ', do{use Data::Dumper; Dumper($self->{perl5})};
125              
126 0 0       0 $cache->set($digest, $self->{perl5}, 'never') if $cache;
127             }
128              
129             #our $evals++;
130              
131 0         0 local $@;
132 0         0 $self->{code} = eval
133             # "\#line " . ($evals*1000) . "\n" .
134             $self->{perl5};
135 0 0       0 die "Error in evaluation: $@\nSource:\n$self->{perl5}\n" if $@;
136              
137             #my $code = $self->{code};
138             #my $e = $evals;
139             #my $c = $self->{perl5};
140             #my $x = 1;
141             #$c =~ s/\n/"\n".++$x.": "/seg;
142             #$self->{code} = sub { print "calling #$e <<< $rule_source >>> compiles to <<< $c >>>\n"; $code->(@_); };
143              
144 0         0 bless $self, $class;
145             }
146              
147             sub code {
148 156     156 1 284 my $rule = shift;
149             sub {
150 0     0   0 $rule->match( $_[1], $_[0], $_[2], $_[3] );
151             }
152 156         1446 }
153              
154             sub match {
155 10     10 1 49 my ( $rule, $str, $grammar, $flags, $state ) = @_;
156              
157             #print "match: ",Dumper($rule);
158             #print "match: ",Dumper(\@_);
159             #print "PCR::match: ",Dumper($_[2]);
160              
161 10 50       24 return Pugs::Runtime::Match->new( { bool => \0 } )
162             unless defined $str; # XXX - fix?
163              
164 10 100       26 if ( ref $grammar eq 'HASH' ) {
165             # backwards compatibility - grammar can now be specified in $flags
166 7         8 $state = $flags;
167 7         6 $flags = $grammar;
168 7         12 $grammar = $flags->{grammar};
169             }
170              
171 10   33     45 $grammar ||= $rule->{grammar};
172             #print "match: grammar $rule->{grammar}, $_[0], $flags\n";
173             #print "match: Variables: ", Dumper ( $flags->{args} ) if defined $flags->{args};
174             #print "match: Flags: ", Dumper ( $flags ) if defined $flags;
175              
176 10 100       34 my $p = defined $flags->{p}
    100          
177             ? $flags->{p}
178             : defined $flags->{pos}
179             ? $flags->{pos}
180             : $rule->{p};
181              
182 10 100       30 my $continue = defined $flags->{c}
    50          
183             ? $flags->{c}
184             : defined $flags->{continue}
185             ? $flags->{continue}
186             : $rule->{continue};
187              
188 10 50       33 my $ignorecase = defined $flags->{i}
    50          
189             ? $flags->{i}
190             : defined $flags->{ignorecase}
191             ? $flags->{ignorecase}
192             : $rule->{ignorecase};
193              
194             #print "flag p";
195             #print "match: grammar $rule->{grammar}, $str, %$flags\n";
196             #print $rule->{code};
197              
198             # XXX BUG! - $rule->{code} disappeared - in t/08-hash.t ???
199 10 50       24 unless ( defined $rule->{code} ) {
200 0         0 local $@;
201 0 0       0 if (!defined $rule->{perl5}) {
202 0         0 croak "Error in evaluation: \$rule->{perl5} is missing";
203             }
204 0         0 $rule->{code} = eval
205             $rule->{perl5};
206 0 0       0 croak "Error in evaluation: $@\nSource:\n$rule->{perl5}" if $@;
207             }
208              
209 10         12 my %args;
210 10 50 33     45 %args = %{$flags->{args}} if defined $flags && defined $flags->{args};
  0         0  
211 10         14 $args{p} = $p;
212 10         26 $args{continue} = $continue;
213 10         17 $args{ignorecase} = $ignorecase;
214              
215             #print "calling code with ",Dumper([ $grammar,$str, $state,\%args ] );
216 10         302 my $match = $rule->{code}(
217             $grammar,
218             $_[1], # pass $str itself instead of a copy so as to make pos work
219             $state,
220             \%args,
221             );
222             #print __PACKAGE__ . ": match result: ", $match->perl;
223 10         44 return $match;
224             }
225              
226             sub reinstall {
227 0     0 1   _install(0, scalar(caller), @_);
228             }
229              
230             sub install {
231 0     0 1   _install(1, scalar(caller), @_);
232             }
233              
234             sub _install {
235 0     0     my($check, $caller, $class, $name, @etc) = @_;
236              
237             # If we have a fully qualified name, use that, otherwise extrapolate.
238 0 0         my $rule = index($name, '::') > -1 ? $name : $caller."::$name";
239 0           my $slot = qualify_to_ref($rule);
240              
241 0 0 0       croak "Can't install regex '$name' as '$rule' which already exists"
242             if $check && *$slot{CODE};
243              
244 0           eval {
245 17     17   139 no warnings 'redefine';
  17         41  
  17         7322  
246 0           *$slot = $class->compile(@etc)->code;
247             };
248 0 0         warn $@ if $@;
249             }
250              
251 0 0   0     sub _str { defined $_[0] ? $_[0] : 'undef' }
252             sub _quot {
253 0     0     my $s = $_[0];
254 0           $s =~ s/\\/\\\\/sg;
255 0           return $s;
256             }
257              
258             sub perl5 {
259 0     0 1   my $self = shift;
260 0           return "bless {\n" .
261             " grammar " . "=> q(" . _str( $self->{grammar} ) . "),\n" .
262             " ratchet " . "=> q(" . _str( $self->{ratchet} ) . "),\n" .
263             " p " . "=> " . _str( $self->{p} ) . ",\n" .
264             " sigspace " . "=> q(" . _str( $self->{sigspace} ) . "),\n" .
265             " ignorecase ". "=> q(" . _str( $self->{ignorecase} )."),\n" .
266             " code " . "=> " . $self->{perl5} . ",\n" .
267             " perl5 " . "=> q(" . _quot( $self->{perl5} ) . "), }, " .
268             "q(" . ref($self) . ")";
269             }
270              
271 0     0 1   sub perl { perl5(@_) }
272              
273             1;
274              
275             __END__