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   7 use Moops;
  1         3  
  1         7  
2              
3             # PODNAME: MarpaX::Languages::M4::Impl::Regexp
4              
5             # ABSTRACT: M4 Regexp generic implementation
6              
7 1     1   3159 class MarpaX::Languages::M4::Impl::Regexp {
  1     1   28  
  1         7  
  1         2  
  1         68  
  1         5  
  1         2  
  1         9  
  1         350  
  1         2  
  1         8  
  1         61  
  1         2  
  1         46  
  1         5  
  1         2  
  1         84  
  1         32  
  1         5  
  1         3  
  1         7  
  1         4778  
  1         2  
  1         11  
  1         436  
  1         3  
  1         10  
  1         146  
  1         2  
  1         10  
  1         81  
  1         2  
  1         6  
  1         219  
  1         2  
  1         10  
  1         983  
  1         3  
  1         7  
  1         2129  
  1         4  
  1         5  
  1         2  
  1         27  
  1         5  
  1         3  
  1         52  
  1         11  
  1         3  
  1         135  
  1         6743  
8              
9 1         13 our $VERSION = '0.019'; # VERSION
10              
11 1         2 our $AUTHORITY = 'cpan:JDDPAUSE'; # AUTHORITY
12              
13 1     1   447 use MarpaX::Languages::M4::Role::Regexp;
  1         5  
  1         12  
14 1     1   490 use MarpaX::Languages::M4::Type::Regexp -all;
  1         4  
  1         13  
15 1     1   1442 use MooX::HandlesVia;
  1         2  
  1         10  
16 1     1   147 use Types::Common::Numeric -all;
  1         2  
  1         9  
17              
18 1         4 has _regexp_type => (
19             is => 'rwp',
20             isa => M4RegexpType
21             );
22              
23 1         2349 has _regexp => (
24             is => 'rwp',
25             isa => RegexpRef
26             );
27              
28 1         1465 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         2991 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   11897 method regexp_compile (ConsumerOf['MarpaX::Languages::M4::Role::Impl'] $impl, M4RegexpType $regexpType, Str $regexpString --> Bool) {
  1 50   190   2  
  1 50       176  
  1 50       6  
  1 50       2  
  1 50       167  
  1 50       10  
  1 50       3  
  1 50       121  
  1 50       6  
  1 50       2  
  1 50       128  
  1         3019  
  190         6416  
  190         682  
  190         674  
  190         823  
  190         411  
  190         341  
  190         1050  
  190         1170  
  190         1015  
  190         7277  
  190         655  
  190         341  
  190         1046  
  190         822  
  190         691  
  190         363  
  190         746  
  190         427  
49              
50 190         389 my $regexp;
51              
52 190         666 my $hasPreviousRegcomp = exists( $^H{regcomp} );
53 190 50       486 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         109  
61 190 100   190   9011 if ( $regexpType eq 'perl' ) {
62             #
63             # Just make sure this really is perl
64             #
65 154         1021 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         1751 $regexp = qr/$regexpString(?#)/sm;
71             }
72 0         0 else {
73 1     1   482 use re::engine::GNU 0.024;
  1         1511  
  1         5  
74 36         2866 $regexp = qr/$regexpString/sm;
75 1     1   54 no re::engine::GNU;
  1         3  
  1         3  
76             }
77              
78             }
79             catch {
80 1     1   40 $impl->logger_error( '%s: %s',
81             $impl->impl_quote($regexpString), $_ );
82 190         2094 };
83              
84             $hasPreviousRegcomp
85             ? $^H{regcomp}
86             = $previousRegcomp
87 190 50       4119 : delete( $^H{regcomp} );
88              
89 190 100       706 if ( defined($regexp) ) {
90 189         4443 $self->_set__regexp($regexp);
91 189         9807 $self->_set__regexp_type($regexpType);
92 189         7014 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   5215 method regexp_exec (ConsumerOf['MarpaX::Languages::M4::Role::Impl'] $impl, Str $string, PositiveOrZeroInt $pos? --> Int) {
  1 50   15217   3  
  1 50       140  
  1 50       7  
  1 50       2  
  1 50       150  
  1 50       7  
  1 50       3  
  1 50       84  
  1 50       6  
  1 100       2  
  1 50       126  
  1 100       2721  
  15217         136876  
  15217         33139  
  15217         37984  
  15217         34144  
  15217         33947  
  15217         23192  
  15217         19482  
  15217         58583  
  15217         63552  
  15217         47774  
  15217         395422  
  15217         32676  
  15217         23722  
  15217         38141  
  15217         34951  
  15217         30213  
  15190         20392  
  15190         34940  
  15217         21926  
106              
107 15217         178308 pos($string) = $pos; # undef is ok
108 15217         32250 my $rc = -1;
109              
110             #
111             # Just make sure this really is perl
112             #
113 15217         33411 my $hasPreviousRegcomp = exists( $^H{regcomp} );
114 15217 50       32962 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         152  
127 15217     15217   628534 my $regexp = $self->_regexp;
128 15217 100       42165 if ( $self->_regexp_type eq 'perl' ) {
129             #
130             # Just make sure this really is perl
131             #
132 15073         68005 delete( $^H{regcomp} );
133             #
134             # Execute perl engine
135             #
136 15073 100       262515 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         295542 $self->{regexp_lpos} = [ @- ];
152 14742         279530 $self->{regexp_rpos} = [ @+ ];
153 14742         280100 $rc = $-[0];
154             }
155             }
156 0         0 else {
157 1     1   6 use re::engine::GNU 0.024;
  1         17  
  1         5  
158             #
159             # Execute re::engine::GNU engine
160             #
161 144 100       6507 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         712 $self->{regexp_lpos} = [ @- ];
175 108         516 $self->{regexp_rpos} = [ @+ ];
176 108         444 $rc = $-[0];
177             }
178 1     1   85 no re::engine::GNU;
  1         2  
  1         5  
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         111032 };
187              
188             $hasPreviousRegcomp
189             ? $^H{regcomp}
190             = $previousRegcomp
191 15217 50       287941 : delete( $^H{regcomp} );
192              
193 15217         311913 return $rc;
194             }
195              
196             #
197             # A perl version of GNU M4's internal
198             # substitute routine
199             #
200 1 50 33 1   4841 method regexp_substitute (ConsumerOf['MarpaX::Languages::M4::Role::Impl'] $impl, Str $victim, Str $repl --> Str) {
  1 50   45   8  
  1 50       133  
  1 50       6  
  1 50       2  
  1 50       157  
  1 50       6  
  1 50       2  
  1 50       93  
  1 50       6  
  1 50       2  
  1 50       658  
  1         2074  
  45         475  
  45         186  
  45         149  
  45         141  
  45         72  
  45         77  
  45         216  
  45         218  
  45         144  
  45         1073  
  45         112  
  45         90  
  45         143  
  45         144  
  45         130  
  45         69  
  45         128  
  45         78  
201 45         92 my $rc = '';
202 45         85 my $replPos = 0;
203 45         123 my $maxReplPos = length($repl) - 1;
204 45         892 my $maxIndice = $self->regexp_lpos_count - 1;
205 45         2140 my %warned = ();
206              
207 45         149 while ( $replPos <= $maxReplPos ) {
208 88         317 my $backslashPos = index( $repl, '\\', $replPos );
209 88 100       209 if ( $backslashPos < 0 ) {
210 29         64 $rc .= substr( $repl, $replPos );
211 29         54 last;
212             }
213 59         149 $rc .= substr( $repl, $replPos, $backslashPos - $replPos );
214 59         105 $replPos = $backslashPos;
215 59         135 my $ch = substr( $repl, ++$replPos, 1 );
216 59 100 66     386 if ( $replPos > $maxReplPos ) {
    100          
    100          
217 3         48 $impl->logger_warn( 'trailing %s ignored in replacement',
218             '\\' );
219 3         10 $warned{undef} = 1;
220 3         9 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         235 $rc .= substr(
230             $victim,
231             $self->regexp_lpos_get(0),
232             $self->regexp_rpos_get(0) - $self->regexp_lpos_get(0)
233             );
234 14         2064 ++$replPos;
235             }
236             elsif ( $ch =~ /[1-9]/ ) {
237 33 100       98 if ( $maxIndice < $ch ) {
238 8 50       30 if ( !$warned{$ch} ) {
239 8         194 $impl->logger_warn( 'sub-expression %d not present',
240             $ch );
241 8         27 $warned{$ch} = 1;
242             }
243             }
244             else {
245 25         466 my $rpos = $self->regexp_rpos_get($ch);
246 25 100       1192 if ( $rpos > 0 ) {
247 23         399 $rc .= substr( $victim, $self->regexp_lpos_get($ch),
248             $self->regexp_rpos_get($ch)
249             - $self->regexp_lpos_get($ch) );
250             }
251             }
252 33         3383 ++$replPos;
253             }
254             else {
255 9         19 $rc .= $ch;
256 9         21 ++$replPos;
257             }
258             }
259              
260 45         755 return $rc;
261             }
262              
263 1         2091 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.019
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