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   6 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   2891 class MarpaX::Languages::M4::Impl::Regexp {
  1     1   28  
  1         6  
  1         3  
  1         60  
  1         5  
  1         2  
  1         9  
  1         287  
  1         3  
  1         6  
  1         58  
  1         2  
  1         43  
  1         6  
  1         2  
  1         82  
  1         33  
  1         6  
  1         2  
  1         6  
  1         4453  
  1         2  
  1         7  
  1         368  
  1         2  
  1         8  
  1         134  
  1         3  
  1         7  
  1         71  
  1         2  
  1         6  
  1         191  
  1         2  
  1         7  
  1         830  
  1         3  
  1         6  
  1         2221  
  1         3  
  1         5  
  1         2  
  1         25  
  1         6  
  1         1  
  1         41  
  1         4  
  1         2  
  1         119  
  1         6254  
8              
9 1         12 our $VERSION = '0.020'; # VERSION
10              
11 1         2 our $AUTHORITY = 'cpan:JDDPAUSE'; # AUTHORITY
12              
13 1     1   399 use MarpaX::Languages::M4::Role::Regexp;
  1         3  
  1         9  
14 1     1   417 use MarpaX::Languages::M4::Type::Regexp -all;
  1         3  
  1         13  
15 1     1   1366 use MooX::HandlesVia;
  1         2  
  1         8  
16 1     1   132 use Types::Common::Numeric -all;
  1         2  
  1         7  
17              
18 1         4 has _regexp_type => (
19             is => 'rwp',
20             isa => M4RegexpType
21             );
22              
23 1         2287 has _regexp => (
24             is => 'rwp',
25             isa => RegexpRef
26             );
27              
28 1         1458 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         2954 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   10409 method regexp_compile (ConsumerOf['MarpaX::Languages::M4::Role::Impl'] $impl, M4RegexpType $regexpType, Str $regexpString --> Bool) {
  1 50   190   3  
  1 50       162  
  1 50       6  
  1 50       2  
  1 50       160  
  1 50       6  
  1 50       1  
  1 50       92  
  1 50       7  
  1 50       2  
  1 50       108  
  1         2872  
  190         7196  
  190         781  
  190         764  
  190         911  
  190         428  
  190         348  
  190         1179  
  190         1649  
  190         1390  
  190         8475  
  190         783  
  190         513  
  190         1375  
  190         822  
  190         651  
  190         515  
  190         777  
  190         421  
49              
50 190         524 my $regexp;
51              
52 190         649 my $hasPreviousRegcomp = exists( $^H{regcomp} );
53 190 50       778 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   6 no warnings;
  1         2  
  1         102  
61 190 100   190   9148 if ( $regexpType eq 'perl' ) {
62             #
63             # Just make sure this really is perl
64             #
65 154         1169 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         1899 $regexp = qr/$regexpString(?#)/sm;
71             }
72 0         0 else {
73 1     1   448 use re::engine::GNU 0.024;
  1         1362  
  1         4  
74 36         3368 $regexp = qr/$regexpString/sm;
75 1     1   55 no re::engine::GNU;
  1         3  
  1         4  
76             }
77              
78             }
79             catch {
80 1     1   43 $impl->logger_error( '%s: %s',
81             $impl->impl_quote($regexpString), $_ );
82 190         2370 };
83              
84             $hasPreviousRegcomp
85             ? $^H{regcomp}
86             = $previousRegcomp
87 190 50       4473 : delete( $^H{regcomp} );
88              
89 190 100       779 if ( defined($regexp) ) {
90 189         4890 $self->_set__regexp($regexp);
91 189         9842 $self->_set__regexp_type($regexpType);
92 189         6269 return true;
93             }
94             else {
95 1         7 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   5229 method regexp_exec (ConsumerOf['MarpaX::Languages::M4::Role::Impl'] $impl, Str $string, PositiveOrZeroInt $pos? --> Int) {
  1 50   15217   4  
  1 50       165  
  1 50       6  
  1 50       2  
  1 50       168  
  1 50       5  
  1 50       2  
  1 50       87  
  1 50       5  
  1 100       2  
  1 50       124  
  1 100       2242  
  15217         136545  
  15217         38148  
  15217         36677  
  15217         37196  
  15217         36824  
  15217         22307  
  15217         20593  
  15217         60828  
  15217         63215  
  15217         54053  
  15217         418206  
  15217         41366  
  15217         26775  
  15217         47319  
  15217         38841  
  15217         33377  
  15190         22247  
  15190         35268  
  15217         23872  
106              
107 15217         179406 pos($string) = $pos; # undef is ok
108 15217         33379 my $rc = -1;
109              
110             #
111             # Just make sure this really is perl
112             #
113 15217         35109 my $hasPreviousRegcomp = exists( $^H{regcomp} );
114 15217 50       40455 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   7 no warnings;
  1         2  
  1         165  
127 15217     15217   634437 my $regexp = $self->_regexp;
128 15217 100       46793 if ( $self->_regexp_type eq 'perl' ) {
129             #
130             # Just make sure this really is perl
131             #
132 15073         75797 delete( $^H{regcomp} );
133             #
134             # Execute perl engine
135             #
136 15073 100       268065 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         296988 $self->{regexp_lpos} = [ @- ];
152 14742         282075 $self->{regexp_rpos} = [ @+ ];
153 14742         279423 $rc = $-[0];
154             }
155             }
156 0         0 else {
157 1     1   6 use re::engine::GNU 0.024;
  1         22  
  1         5  
158             #
159             # Execute re::engine::GNU engine
160             #
161 144 100       6387 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         650 $self->{regexp_lpos} = [ @- ];
175 108         491 $self->{regexp_rpos} = [ @+ ];
176 108         406 $rc = $-[0];
177             }
178 1     1   89 no re::engine::GNU;
  1         2  
  1         4  
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         121183 };
187              
188             $hasPreviousRegcomp
189             ? $^H{regcomp}
190             = $previousRegcomp
191 15217 50       290856 : delete( $^H{regcomp} );
192              
193 15217         309821 return $rc;
194             }
195              
196             #
197             # A perl version of GNU M4's internal
198             # substitute routine
199             #
200 1 50 33 1   4837 method regexp_substitute (ConsumerOf['MarpaX::Languages::M4::Role::Impl'] $impl, Str $victim, Str $repl --> Str) {
  1 50   45   2  
  1 50       164  
  1 50       6  
  1 50       2  
  1 50       191  
  1 50       7  
  1 50       2  
  1 50       91  
  1 50       5  
  1 50       2  
  1 50       555  
  1         1985  
  45         519  
  45         169  
  45         184  
  45         166  
  45         86  
  45         82  
  45         278  
  45         268  
  45         175  
  45         1245  
  45         159  
  45         80  
  45         187  
  45         153  
  45         185  
  45         79  
  45         168  
  45         85  
201 45         91 my $rc = '';
202 45         67 my $replPos = 0;
203 45         157 my $maxReplPos = length($repl) - 1;
204 45         909 my $maxIndice = $self->regexp_lpos_count - 1;
205 45         2210 my %warned = ();
206              
207 45         164 while ( $replPos <= $maxReplPos ) {
208 88         333 my $backslashPos = index( $repl, '\\', $replPos );
209 88 100       228 if ( $backslashPos < 0 ) {
210 29         73 $rc .= substr( $repl, $replPos );
211 29         56 last;
212             }
213 59         169 $rc .= substr( $repl, $replPos, $backslashPos - $replPos );
214 59         107 $replPos = $backslashPos;
215 59         142 my $ch = substr( $repl, ++$replPos, 1 );
216 59 100 66     459 if ( $replPos > $maxReplPos ) {
    100          
    100          
217 3         53 $impl->logger_warn( 'trailing %s ignored in replacement',
218             '\\' );
219 3         11 $warned{undef} = 1;
220 3         8 last;
221             }
222             elsif ( $ch eq '0' || $ch eq '&' ) {
223 14 50       41 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         297 $rc .= substr(
230             $victim,
231             $self->regexp_lpos_get(0),
232             $self->regexp_rpos_get(0) - $self->regexp_lpos_get(0)
233             );
234 14         1929 ++$replPos;
235             }
236             elsif ( $ch =~ /[1-9]/ ) {
237 33 100       116 if ( $maxIndice < $ch ) {
238 8 50       35 if ( !$warned{$ch} ) {
239 8         177 $impl->logger_warn( 'sub-expression %d not present',
240             $ch );
241 8         31 $warned{$ch} = 1;
242             }
243             }
244             else {
245 25         454 my $rpos = $self->regexp_rpos_get($ch);
246 25 100       1201 if ( $rpos > 0 ) {
247 23         404 $rc .= substr( $victim, $self->regexp_lpos_get($ch),
248             $self->regexp_rpos_get($ch)
249             - $self->regexp_lpos_get($ch) );
250             }
251             }
252 33         3406 ++$replPos;
253             }
254             else {
255 9         21 $rc .= $ch;
256 9         27 ++$replPos;
257             }
258             }
259              
260 45         739 return $rc;
261             }
262              
263 1         1886 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.020
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