File Coverage

blib/lib/MarpaX/Languages/M4/Impl/Regexp.pm
Criterion Covered Total %
statement 249 257 96.8
branch 67 110 60.9
condition 5 12 41.6
subroutine 22 23 95.6
pod n/a
total 343 402 85.3


line stmt bran cond sub pod time code
1 1     1   5 use Moops;
  1         2  
  1         7  
2              
3             # PODNAME: MarpaX::Languages::M4::Impl::Regexp
4              
5             # ABSTRACT: M4 Regexp generic implementation
6              
7 1     1   2699 class MarpaX::Languages::M4::Impl::Regexp {
  1     1   26  
  1         6  
  1         2  
  1         50  
  1         5  
  1         1  
  1         9  
  1         273  
  1         2  
  1         7  
  1         56  
  1         2  
  1         39  
  1         4  
  1         2  
  1         75  
  1         28  
  1         5  
  1         2  
  1         19  
  1         4347  
  1         3  
  1         6  
  1         342  
  1         2  
  1         7  
  1         121  
  1         3  
  1         7  
  1         65  
  1         2  
  1         6  
  1         174  
  1         2  
  1         7  
  1         749  
  1         2  
  1         6  
  1         1890  
  1         3  
  1         5  
  1         2  
  1         19  
  1         4  
  1         3  
  1         40  
  1         5  
  1         1  
  1         109  
  1         6131  
8              
9 1         11 our $VERSION = '0.018'; # VERSION
10              
11 1         2 our $AUTHORITY = 'cpan:JDDPAUSE'; # AUTHORITY
12              
13 1     1   382 use MarpaX::Languages::M4::Role::Regexp;
  1         2  
  1         8  
14 1     1   376 use MarpaX::Languages::M4::Type::Regexp -all;
  1         3  
  1         11  
15 1     1   1242 use MooX::HandlesVia;
  1         2  
  1         8  
16 1     1   121 use Types::Common::Numeric -all;
  1         2  
  1         6  
17              
18 1         4 has _regexp_type => (
19             is => 'rwp',
20             isa => M4RegexpType
21             );
22              
23 1         1953 has _regexp => (
24             is => 'rwp',
25             isa => RegexpRef
26             );
27              
28 1         1393 has regexp_lpos => (
29             is => 'rwp',
30             isa => ArrayRef,
31             handles_via => 'Array',
32             handles => {
33             'regexp_lpos_count' => 'count',
34             'regexp_lpos_get' => 'get'
35             }
36             );
37              
38 1         2868 has regexp_rpos => (
39             is => 'rwp',
40             isa => ArrayRef,
41             handles_via => 'Array',
42             handles => {
43             'regexp_rpos_count' => 'count',
44             'regexp_rpos_get' => 'get'
45             }
46             );
47              
48 1 50 33 1   10082 method regexp_compile (ConsumerOf['MarpaX::Languages::M4::Role::Impl'] $impl, M4RegexpType $regexpType, Str $regexpString --> Bool) {
  1 50   190   3  
  1 50       142  
  1 50       6  
  1 50       2  
  1 50       150  
  1 50       6  
  1 50       2  
  1 50       90  
  1 50       6  
  1 50       2  
  1 50       106  
  1         2657  
  190         5490  
  190         685  
  190         646  
  190         640  
  190         457  
  190         356  
  190         881  
  190         967  
  190         809  
  190         6197  
  190         627  
  190         349  
  190         840  
  190         624  
  190         563  
  190         287  
  190         497  
  190         429  
49              
50 190         433 my $regexp;
51              
52 190         554 my $hasPreviousRegcomp = exists( $^H{regcomp} );
53 190 50       494 my $previousRegcomp = $hasPreviousRegcomp ? $^H{regcomp} : undef;
54              
55             try {
56             #
57             # Some versions of perl warn, some others don't -;
58             # We are only interested by real failures.
59             #
60 1     1   5 no warnings;
  1         3  
  1         101  
61 190 100   190   7984 if ( $regexpType eq 'perl' ) {
62             #
63             # Just make sure this really is perl
64             #
65 154         913 delete( $^H{regcomp} );
66             #
67             # regexp can be empty and perl have a very special
68             # behaviour in this case. Avoid empty regexp.
69             #
70 154         1539 $regexp = qr/$regexpString(?#)/sm;
71             }
72 0         0 else {
73 1     1   354 use re::engine::GNU 0.024;
  1         1265  
  1         5  
74 36         2776 $regexp = qr/$regexpString/sm;
75 1     1   51 no re::engine::GNU;
  1         2  
  1         3  
76             }
77              
78             }
79             catch {
80 1     1   59 $impl->logger_error( '%s: %s',
81             $impl->impl_quote($regexpString), $_ );
82 190         1851 };
83              
84             $hasPreviousRegcomp
85             ? $^H{regcomp}
86             = $previousRegcomp
87 190 50       3413 : delete( $^H{regcomp} );
88              
89 190 100       595 if ( defined($regexp) ) {
90 189         4104 $self->_set__regexp($regexp);
91 189         8378 $self->_set__regexp_type($regexpType);
92 189         5470 return true;
93             }
94             else {
95 1         8 return false;
96             }
97             }
98              
99             #
100             # Return value is:
101             # -2 if failure (the engine croaked)
102             # -1 if match failed
103             # >=0 Position where it matches
104             #
105 1 50 33 1   4767 method regexp_exec (ConsumerOf['MarpaX::Languages::M4::Role::Impl'] $impl, Str $string, PositiveOrZeroInt $pos? --> Int) {
  1 50   15217   2  
  1 50       131  
  1 50       5  
  1 50       3  
  1 50       146  
  1 50       5  
  1 50       2  
  1 50       87  
  1 50       6  
  1 100       3  
  1 50       118  
  1 100       2083  
  15217         121922  
  15217         31448  
  15217         31245  
  15217         31727  
  15217         31060  
  15217         21093  
  15217         18661  
  15217         50995  
  15217         53920  
  15217         42877  
  15217         356794  
  15217         32616  
  15217         20084  
  15217         34388  
  15217         31890  
  15217         30848  
  15190         19609  
  15190         33280  
  15217         24357  
106              
107 15217         169373 pos($string) = $pos; # undef is ok
108 15217         31607 my $rc = -1;
109              
110             #
111             # Just make sure this really is perl
112             #
113 15217         32522 my $hasPreviousRegcomp = exists( $^H{regcomp} );
114 15217 50       30542 my $previousRegcomp = $hasPreviousRegcomp ? $^H{regcomp} : undef;
115              
116             #
117             # Note: this looks like duplicated code, and it is.
118             # But this cannot be avoided because $-/$+ are
119             # lexically scoped, and our scope depend on the engine
120             #
121             try {
122             #
123             # Some versions of perl warn, some others don't -;
124             # We are only interested by real failures.
125             #
126 1     1   6 no warnings;
  1         2  
  1         142  
127 15217     15217   580341 my $regexp = $self->_regexp;
128 15217 100       36593 if ( $self->_regexp_type eq 'perl' ) {
129             #
130             # Just make sure this really is perl
131             #
132 15073         63929 delete( $^H{regcomp} );
133             #
134             # Execute perl engine
135             #
136 15073 100       243273 if ( $string =~ m/$regexp/gc ) {
137             #
138             # From profiling point of view this is one of the deepests
139             # method, affecting everything. So we want to have no
140             # penalty whatsoever.
141             #
142             # my @lpos = ();
143             # my @rpos = ();
144             # map { ( $lpos[$_], $rpos[$_] ) = ( $-[$_], $+[$_] ) }
145             # ( 0 .. $#- );
146             #
147             # $self->_set_regexp_lpos( \@lpos );
148             # $self->_set_regexp_rpos( \@rpos );
149             # $rc = $self->regexp_lpos_get(0);
150              
151 14742         288872 $self->{regexp_lpos} = [ @- ];
152 14742         270831 $self->{regexp_rpos} = [ @+ ];
153 14742         268797 $rc = $-[0];
154             }
155             }
156 0         0 else {
157 1     1   6 use re::engine::GNU 0.024;
  1         14  
  1         5  
158             #
159             # Execute re::engine::GNU engine
160             #
161 144 100       5829 if ( $string =~ m/$regexp/gc ) {
162             #
163             # Same remark as before
164             #
165             # my @lpos = ();
166             # my @rpos = ();
167             # map { ( $lpos[$_], $rpos[$_] ) = ( $-[$_], $+[$_] ) }
168             # ( 0 .. $#- );
169             #
170             # $self->_set_regexp_lpos( \@lpos );
171             # $self->_set_regexp_rpos( \@rpos );
172             # $rc = $self->regexp_lpos_get(0);
173              
174 108         680 $self->{regexp_lpos} = [ @- ];
175 108         456 $self->{regexp_rpos} = [ @+ ];
176 108         423 $rc = $-[0];
177             }
178 1     1   78 no re::engine::GNU;
  1         2  
  1         3  
179             }
180             }
181             catch {
182 0     0   0 my $regexp = $self->_regexp;
183 0         0 $impl->logger_error( '%s =~ %s: %s', $impl->impl_quote($string),
184             "$regexp", $_ );
185 0         0 $rc = -2;
186 15217         101402 };
187              
188             $hasPreviousRegcomp
189             ? $^H{regcomp}
190             = $previousRegcomp
191 15217 50       253909 : delete( $^H{regcomp} );
192              
193 15217         283520 return $rc;
194             }
195              
196             #
197             # A perl version of GNU M4's internal
198             # substitute routine
199             #
200 1 50 33 1   4316 method regexp_substitute (ConsumerOf['MarpaX::Languages::M4::Role::Impl'] $impl, Str $victim, Str $repl --> Str) {
  1 50   45   14  
  1 50       122  
  1 50       6  
  1 50       2  
  1 50       145  
  1 50       5  
  1 50       2  
  1 50       90  
  1 50       5  
  1 50       2  
  1 50       529  
  1         1967  
  45         395  
  45         142  
  45         134  
  45         133  
  45         94  
  45         67  
  45         223  
  45         182  
  45         138  
  45         972  
  45         126  
  45         63  
  45         140  
  45         118  
  45         137  
  45         74  
  45         122  
  45         87  
201 45         77 my $rc = '';
202 45         76 my $replPos = 0;
203 45         101 my $maxReplPos = length($repl) - 1;
204 45         730 my $maxIndice = $self->regexp_lpos_count - 1;
205 45         1993 my %warned = ();
206              
207 45         117 while ( $replPos <= $maxReplPos ) {
208 88         254 my $backslashPos = index( $repl, '\\', $replPos );
209 88 100       193 if ( $backslashPos < 0 ) {
210 29         58 $rc .= substr( $repl, $replPos );
211 29         53 last;
212             }
213 59         130 $rc .= substr( $repl, $replPos, $backslashPos - $replPos );
214 59         117 $replPos = $backslashPos;
215 59         125 my $ch = substr( $repl, ++$replPos, 1 );
216 59 100 66     335 if ( $replPos > $maxReplPos ) {
    100          
    100          
217 3         49 $impl->logger_warn( 'trailing %s ignored in replacement',
218             '\\' );
219 3         9 $warned{undef} = 1;
220 3         7 last;
221             }
222             elsif ( $ch eq '0' || $ch eq '&' ) {
223 14 50       35 if ( $ch eq '0' ) {
224 0 0       0 if ( !$warned{$ch} ) {
225 0         0 $impl->logger_warn('\\0 should be replaced by \\&');
226 0         0 $warned{$ch} = 1;
227             }
228             }
229 14         230 $rc .= substr(
230             $victim,
231             $self->regexp_lpos_get(0),
232             $self->regexp_rpos_get(0) - $self->regexp_lpos_get(0)
233             );
234 14         1746 ++$replPos;
235             }
236             elsif ( $ch =~ /[1-9]/ ) {
237 33 100       81 if ( $maxIndice < $ch ) {
238 8 50       23 if ( !$warned{$ch} ) {
239 8         132 $impl->logger_warn( 'sub-expression %d not present',
240             $ch );
241 8         26 $warned{$ch} = 1;
242             }
243             }
244             else {
245 25         368 my $rpos = $self->regexp_rpos_get($ch);
246 25 100       947 if ( $rpos > 0 ) {
247 23         302 $rc .= substr( $victim, $self->regexp_lpos_get($ch),
248             $self->regexp_rpos_get($ch)
249             - $self->regexp_lpos_get($ch) );
250             }
251             }
252 33         2768 ++$replPos;
253             }
254             else {
255 9         16 $rc .= $ch;
256 9         23 ++$replPos;
257             }
258             }
259              
260 45         759 return $rc;
261             }
262              
263 1         1807 with 'MarpaX::Languages::M4::Role::Regexp';
264             }
265              
266             1;
267              
268             __END__
269              
270             =pod
271              
272             =encoding UTF-8
273              
274             =head1 NAME
275              
276             MarpaX::Languages::M4::Impl::Regexp - M4 Regexp generic implementation
277              
278             =head1 VERSION
279              
280             version 0.018
281              
282             =head1 AUTHOR
283              
284             Jean-Damien Durand <jeandamiendurand@free.fr>
285              
286             =head1 COPYRIGHT AND LICENSE
287              
288             This software is copyright (c) 2015 by Jean-Damien Durand.
289              
290             This is free software; you can redistribute it and/or modify it under
291             the same terms as the Perl 5 programming language system itself.
292              
293             =cut