File Coverage

blib/lib/Language/P/Toy/Opcodes/Regex.pm
Criterion Covered Total %
statement 149 161 92.5
branch 40 40 100.0
condition 17 20 85.0
subroutine 21 22 95.4
pod 0 14 0.0
total 227 257 88.3


line stmt bran cond sub pod time code
1             package Language::P::Toy::Opcodes::Regex;
2              
3 30     30   218 use strict;
  30         68  
  30         1042  
4 30     30   192 use warnings;
  30         61  
  30         856  
5 30     30   153 use Exporter 'import';
  30         57  
  30         96140  
6              
7             our @EXPORT_OK = qw(o_rx_start_match o_rx_accept o_rx_exact o_rx_start_group
8             o_rx_quantifier o_rx_capture_start o_rx_capture_end o_rx_try
9             o_rx_start_special o_rx_end_special
10             o_rx_match);
11             our %EXPORT_TAGS =
12             ( opcodes => \@EXPORT_OK,
13             );
14              
15             sub v(@) {
16 1327     1327 0 1572 return;
17 0         0 print @_;
18             }
19              
20             sub vv(@) {
21 222     222 0 3854 return;
22 0         0 print @_;
23             }
24              
25             sub d(@) {
26 31     31 0 3155 require Data::Dumper;
27              
28 31         15070 return Data::Dumper::Dumper( @_ );
29             }
30              
31             sub _save_groups {
32 129     129   203 my( $cxt, $op, $clear ) = @_;
33 129         141 my @saved;
34 129         618 my $groups = { start => $op->{subgroups_start},
35             data => \@saved,
36             last_cap => $cxt->{last_cap},
37             last_open=> $cxt->{last_open},
38             };
39              
40 129         502 vv "Saving $op->{subgroups_start}-$op->{subgroups_end}\n";
41 129         416 for( my $i = $op->{subgroups_start}; $i < $op->{subgroups_end}; ++$i ) {
42 93 100 100     268 my $g = $clear ? [-1, -1] : $cxt->{capt}[$i] || [-1, -1];
43 93         1594 push @saved, [ $g->[0], $g->[1] ];
44             }
45              
46 129         277 return $groups;
47             }
48              
49             sub _restore_groups {
50 31     31   51 my( $cxt, $groups ) = @_;
51 31         73 vv d $groups, $cxt->{capt};
52              
53 31         339 my $index = $groups->{start};
54 31         50 foreach my $g ( @{$groups->{data}} ) {
  31         86  
55 19         61 $cxt->{capt}[$index] = [ @$g ];
56 19         56 ++$index;
57             }
58              
59 31         123 vv "Restored $groups->{start}-$index\n";
60 31         62 $cxt->{last_cap} = $groups->{last_cap};
61 31         77 $cxt->{last_open} = $groups->{last_open};
62             }
63              
64             sub _start_capture {
65 150     150   205 my( $cxt, $group ) = @_;
66              
67 150         511 v "SCapt: $group: $cxt->{pos}\n";
68              
69 150         475 for( my $i = $cxt->{last_open} + 1; $i < $group; ++$i ) {
70 5         27 $cxt->{capt}[$i][0] = $cxt->{capt}[$i][1] = -1;
71             }
72              
73 150         364 $cxt->{capt}[$group][0] = $cxt->{pos};
74 150         268 $cxt->{capt}[$group][1] = -1;
75              
76 150 100       468 $cxt->{last_open} = $group if $cxt->{last_open} < $group;
77             }
78              
79             sub _end_capture {
80 75     75   101 my( $cxt, $group ) = @_;
81              
82 75         172 $cxt->{capt}[$group][1] = $cxt->{pos};
83              
84 75         273 v "ECapt: $group (last: $cxt->{last_cap}): $cxt->{pos}\n";
85 75         244 for( my $i = $cxt->{last_open} + 1; $i < $group; ++$i ) {
86 0         0 $cxt->{capt}[$i][0] = $cxt->{capt}[$i][1] = -1;
87             }
88              
89 75         138 $cxt->{last_cap} = $group;
90             }
91              
92             sub _backtrack {
93 280     280   351 my( $runtime, $cxt ) = @_;
94 280         365 my $st = $cxt->{st};
95              
96 280 100       557 if( @$st ) {
97 170         261 v "Pop state\n";
98 170         286 my $bt = pop @$st;
99 170         450 ( my $pc, $cxt->{pos} ) = ( $bt->{r}, $bt->{s} );
100 170 100       584 if( $pc >= 0 ) {
101 117 100       261 if( $bt->{btg} ) {
102 31         92 vv "Restoring $bt->{btg}\n";
103 31         59 $cxt->{btg} = $bt->{btg};
104             }
105 117 100       284 _restore_groups( $cxt, $bt->{g} ) if $bt->{g};
106 117         317 v "Bt pc: $pc pos: $cxt->{pos}\n";
107 117         534 return $pc;
108             }
109             }
110              
111 163         355 v "Pop\n";
112 163         207 my $stack = $cxt->{stack};
113 163 100       312 if( @$stack ) {
114 57         93 $st = $cxt->{st} = pop @$stack;
115 57         99 $cxt->{btg} = $cxt->{btg}->{btg};
116              
117 57         170 return _backtrack( $runtime, $cxt );
118             } else {
119 106         310 my $rpc = $runtime->call_return;
120 106         130 push @{$runtime->{_stack}}, { matched => 0 };
  106         313  
121              
122 106         586 return $rpc + 1;
123             }
124             }
125              
126             sub o_rx_match {
127 0     0 0 0 my( $op, $runtime, $pc ) = @_;
128 0         0 my $pattern = pop @{$runtime->{_stack}};
  0         0  
129 0         0 my $scalar = pop @{$runtime->{_stack}};
  0         0  
130              
131 0         0 my $match = $pattern->match( $runtime, $scalar->as_string );
132              
133 0         0 push @{$runtime->{_stack}}, Language::P::Toy::Value::StringNumber->new
  0         0  
134             ( { integer => $match->{matched} } );
135              
136 0         0 return $pc + 1;
137             }
138              
139             sub o_rx_start_match {
140 145     145 0 212 my( $op, $runtime, $pc ) = @_;
141 145         305 my $string = $runtime->{_stack}[$runtime->{_frame} - 3];
142 145         256 my $start = $runtime->{_stack}[$runtime->{_frame} - 4];
143 145         161 my @stack;
144 145         1166 my $cxt = { string => $string,
145             pos => $start,
146             length => length( $string ),
147             stack => \@stack,
148             st => \@stack,
149             btg => undef,
150             capt => [],
151             last_cap => -1,
152             last_open=> -1,
153             };
154 145         219 push @{$runtime->{_stack}}, $cxt;
  145         278  
155              
156 145         510 v "String '$string', start $start\n";
157              
158 145         465 return $pc + 1;
159             }
160              
161             sub o_rx_accept {
162 39     39 0 77 my( $op, $runtime, $pc ) = @_;
163 39         82 my $cxt = $runtime->{_stack}->[-1];
164 39         397 my $rpc = $runtime->call_return;
165              
166             # null-out unclosed groups
167 39         183 for( my $i = $cxt->{last_open} + 1; $i < $op->{groups}; ++$i ) {
168 3         14 $cxt->{capt}[$i][0] = $cxt->{capt}[$i][1] = -1;
169             }
170              
171 39         54 push @{$runtime->{_stack}}, { matched => 1,
  39         231  
172             match_end => $cxt->{pos},
173             captures => $cxt->{capt} };
174              
175 39         256 return $rpc + 1;
176             }
177              
178             sub o_rx_exact {
179 331     331 0 492 my( $op, $runtime, $pc ) = @_;
180 331         571 my $cxt = $runtime->{_stack}->[-1];
181              
182 331         976 v "Exact '$op->{string}' at $cxt->{pos}\n";
183 331 100       1109 if( substr( $cxt->{string}, $cxt->{pos}, $op->{length} )
184             ne $op->{string} ) {
185 217         432 return _backtrack( $runtime, $cxt );
186             }
187 114         186 $cxt->{pos} += $op->{length};
188              
189 114         298 return $pc + 1;
190             }
191              
192             sub o_rx_start_group {
193 91     91 0 155 my( $op, $runtime, $pc ) = @_;
194 91         151 my $cxt = $runtime->{_stack}->[-1];
195 91         129 my $st = $cxt->{st};
196              
197             # push a new group context
198 91         394 $cxt->{btg} = { c => -1,
199             st => [],
200             btg => $cxt->{btg},
201             lm => -1,
202             };
203             # add a new backtrack group
204 91         157 my $nst = $cxt->{btg}->{st};
205 91         111 push @{$cxt->{stack}}, $st;
  91         183  
206 91         130 $cxt->{st} = $nst;
207              
208 91         252 return $op->{to};
209             }
210              
211             sub o_rx_quantifier {
212 168     168 0 245 my( $op, $runtime, $pc ) = @_;
213 168         274 my $cxt = $runtime->{_stack}->[-1];
214 168         285 my $c = $cxt->{btg}->{c} += 1;
215              
216 168 100       8876 v "Quantifier at $pc (pos: $cxt->{pos}, rep: $c) btg: $cxt->{btg} old: ${$cxt->{btg}->{btg} ? \$cxt->{btg}->{btg} : \''}\n";
  168         668  
217 168 100 100     655 _end_capture( $cxt, $op->{group} ) if $c > 0 && defined $op->{group};
218              
219 168 100       377 if( $c == $op->{max} ) {
220 5         11 v "Reached max limit\n";
221 5         11 $cxt->{btg} = $cxt->{btg}->{btg};
222 5         19 return $pc + 1;
223             }
224              
225             # try to continue match if matched twice at the same position
226             # (i.e. zero-length match)
227 163 100       386 if( $cxt->{pos} == $cxt->{btg}->{lm} ) {
228 3         9 v "Zero-length match ($c)\n";
229 3         7 $cxt->{btg} = $cxt->{btg}->{btg};
230 3         7 return $pc + 1;
231             }
232              
233 160 100 66     897 my $groups = defined $op->{subgroups_start} && ( $c == 0 || $c >= $op->{min} ) ?
234             _save_groups( $cxt, $op, $c == 0 ) : undef;
235              
236 160 100 100     767 if( $c == 0 && $op->{min} > 0 ) {
    100          
237             # force failure of the group on backtrack
238 65         69 push @{$cxt->{st}}, { s => $cxt->{pos},
  65         291  
239             r => -2,
240             g => $groups,
241             };
242             } elsif( $c >= $op->{min} ) {
243 79         136 push @{$cxt->{st}}, { s => $cxt->{pos},
  79         442  
244             r => $pc + 1,
245             g => $groups,
246             btg => $cxt->{btg}->{btg},
247             };
248             }
249              
250 160         353 $cxt->{btg}->{lm} = $cxt->{pos};
251              
252             # if nongreedy, match at least min
253 160 100 100     512 if( !$op->{greedy} && $c >= $op->{min} ) {
254 7         16 $cxt->{btg} = $cxt->{btg}->{btg};
255 7         32 return $pc + 1;
256             }
257              
258 153 100       455 _start_capture( $cxt, $op->{group} ) if defined $op->{group};
259              
260 153         511 return $op->{to};
261             }
262              
263             sub o_rx_try {
264 84     84 0 142 my( $op, $runtime, $pc ) = @_;
265 84         122 my $cxt = $runtime->{_stack}->[-1];
266              
267 84 100       236 my $groups = defined $op->{subgroups_start} ?
268             _save_groups( $cxt, $op ) : undef;
269              
270 84         81 push @{$cxt->{st}}, { s => $cxt->{pos},
  84         368  
271             r => $op->{to},
272             g => $groups,
273             btg => $cxt->{btg},
274             };
275              
276 84         218 return $pc + 1;
277             }
278              
279             sub o_rx_capture_start {
280 122     122 0 173 my( $op, $runtime, $pc ) = @_;
281 122         236 my $cxt = $runtime->{_stack}->[-1];
282              
283 122         268 _start_capture( $cxt, $op->{group} );
284              
285 122         303 return $pc + 1;
286             }
287              
288             sub o_rx_capture_end {
289 55     55 0 84 my( $op, $runtime, $pc ) = @_;
290 55         98 my $cxt = $runtime->{_stack}->[-1];
291              
292 55         126 _end_capture( $cxt, $op->{group} );
293              
294 55         138 return $pc + 1;
295             }
296              
297             sub o_rx_start_special {
298 7     7 0 10 my( $op, $runtime, $pc ) = @_;
299 7         13 my $cxt = $runtime->{_stack}->[-1];
300              
301 7 100       24 if( $cxt->{pos} != 0 ) {
302 5         11 return _backtrack( $runtime, $cxt );
303             }
304              
305 2         8 return $pc + 1;
306             }
307              
308             sub o_rx_end_special {
309 5     5 0 10 my( $op, $runtime, $pc ) = @_;
310 5         19 my $cxt = $runtime->{_stack}->[-1];
311              
312 5 100 66     43 if( $cxt->{pos} != $cxt->{length}
      66        
313             && ( $cxt->{pos} != $cxt->{length} - 1
314             || substr( $cxt->{string}, -1, 1 ) ne "\n" ) ) {
315 1         6 return _backtrack( $runtime, $cxt );
316             }
317              
318 4         19 return $pc + 1;
319             }
320              
321             1;