File Coverage

blib/lib/PPI/Token/_QuoteEngine/Full.pm
Criterion Covered Total %
statement 171 171 100.0
branch 74 88 84.0
condition n/a
subroutine 12 12 100.0
pod 0 1 0.0
total 257 272 94.4


line stmt bran cond sub pod time code
1             package PPI::Token::_QuoteEngine::Full;
2              
3             # Full quote engine
4              
5 65     65   504 use strict;
  65         133  
  65         1841  
6 65     65   309 use Clone ();
  65         147  
  65         806  
7 65     65   284 use Carp ();
  65         144  
  65         913  
8 65     65   308 use PPI::Token::_QuoteEngine ();
  65         120  
  65         142781  
9              
10             our $VERSION = '1.277';
11              
12             our @ISA = 'PPI::Token::_QuoteEngine';
13              
14             # Prototypes for the different braced sections
15             my %SECTIONS = (
16             '(' => { type => '()', _close => ')' },
17             '<' => { type => '<>', _close => '>' },
18             '[' => { type => '[]', _close => ']' },
19             '{' => { type => '{}', _close => '}' },
20             );
21              
22             # For each quote type, the extra fields that should be set.
23             # This should give us faster initialization.
24             my %QUOTES = (
25             'q' => { operator => 'q', braced => undef, separator => undef, _sections => 1 },
26             'qq' => { operator => 'qq', braced => undef, separator => undef, _sections => 1 },
27             'qx' => { operator => 'qx', braced => undef, separator => undef, _sections => 1 },
28             'qw' => { operator => 'qw', braced => undef, separator => undef, _sections => 1 },
29             'qr' => { operator => 'qr', braced => undef, separator => undef, _sections => 1, modifiers => 1 },
30             'm' => { operator => 'm', braced => undef, separator => undef, _sections => 1, modifiers => 1 },
31             's' => { operator => 's', braced => undef, separator => undef, _sections => 2, modifiers => 1 },
32             'tr' => { operator => 'tr', braced => undef, separator => undef, _sections => 2, modifiers => 1 },
33              
34             # Y is the little-used variant of tr
35             'y' => { operator => 'y', braced => undef, separator => undef, _sections => 2, modifiers => 1 },
36              
37             '/' => { operator => undef, braced => 0, separator => '/', _sections => 1, modifiers => 1 },
38              
39             # Angle brackets quotes mean "readline(*FILEHANDLE)"
40             '<' => { operator => undef, braced => 1, separator => undef, _sections => 1, },
41              
42             # The final ( and kind of depreciated ) "first match only" one is not
43             # used yet, since I'm not sure on the context differences between
44             # this and the trinary operator, but it's here for completeness.
45             '?' => { operator => undef, braced => 0, separator => '?', _sections => 1, modifiers => 1 },
46              
47             # parse prototypes as a literal quote
48             '(' => { operator => undef, braced => 1, separator => undef, _sections => 1, },
49             );
50              
51              
52             sub new {
53 3841     3841 0 6681 my $class = shift;
54 3841 50       8464 my $init = defined $_[0]
55             ? shift
56             : Carp::croak("::Full->new called without init string");
57              
58             # Create the token
59             ### This manual SUPER'ing ONLY works because none of
60             ### Token::Quote, Token::QuoteLike and Token::Regexp
61             ### implement a new function of their own.
62 3841 50       11184 my $self = PPI::Token::new( $class, $init ) or return undef;
63              
64             # Do we have a prototype for the initializer? If so, add the extra fields
65 3841 50       13330 my $options = $QUOTES{$init} or return $self->_error(
66             "Unknown quote type '$init'"
67             );
68 3841         16336 foreach ( keys %$options ) {
69 17264         33084 $self->{$_} = $options->{$_};
70             }
71              
72             # Set up the modifiers hash if needed
73 3841 100       11309 $self->{modifiers} = {} if $self->{modifiers};
74              
75             # Handle the special < base
76 3841 100       10209 $self->{sections}[0] = Clone::clone $SECTIONS{'<'} if $init eq '<';
77 3841 100       11205 $self->{sections}[0] = Clone::clone $SECTIONS{'('} if $init eq '(';
78              
79 3841         12129 $self;
80             }
81              
82             sub _fill {
83 3841     3841   6452 my $class = shift;
84 3841         5866 my $t = shift;
85             my $self = $t->{token}
86 3841 50       10601 or Carp::croak("::Full->_fill called without current token");
87              
88             # Load in the operator stuff if needed
89 3841 100       9397 if ( $self->{operator} ) {
90             # In an operator based quote-like, handle the gap between the
91             # operator and the opening separator.
92 2688 100       11130 if ( substr( $t->{line}, $t->{line_cursor}, 1 ) =~ /\s/ ) {
93             # Go past the gap
94 549         1991 my $gap = $self->_scan_quote_like_operator_gap( $t );
95 549 50       1287 return undef unless defined $gap;
96 549 100       1264 if ( ref $gap ) {
97             # End of file
98 185         383 $self->{content} .= $$gap;
99 185         495 return 0;
100             }
101 364         714 $self->{content} .= $gap;
102             }
103              
104             # The character we are now on is the separator. Capture,
105             # and advance into the first section.
106 2503         6049 my $sep = substr( $t->{line}, $t->{line_cursor}++, 1 );
107 2503         4762 $self->{content} .= $sep;
108              
109             # Determine if these are normal or braced type sections
110 2503 100       6070 if ( my $section = $SECTIONS{$sep} ) {
111 1100         2201 $self->{braced} = 1;
112 1100         12709 $self->{sections}->[0] = Clone::clone($section);
113             } else {
114 1403         2280 $self->{braced} = 0;
115 1403         3023 $self->{separator} = $sep;
116             }
117             }
118              
119             # Parse different based on whether we are normal or braced
120             my $rv = $self->{braced}
121 3656 100       13617 ? $self->_fill_braced($t)
122             : $self->_fill_normal($t);
123 3656 100       9074 return $rv if !$rv;
124              
125             # Return now unless it has modifiers ( i.e. s/foo//eieio )
126 2803 100       7668 return 1 unless $self->{modifiers};
127              
128             # Check for modifiers
129 1215         1728 my $char;
130 1215         1931 my $len = 0;
131 1215         7787 while ( ($char = substr( $t->{line}, $t->{line_cursor} + 1, 1 )) =~ /[^\W\d_]/ ) {
132 474         959 $len++;
133 474         914 $self->{content} .= $char;
134 474         1629 $self->{modifiers}->{lc $char} = 1;
135 474         2742 $t->{line_cursor}++;
136             }
137             }
138              
139             # Handle the content parsing path for normally separated
140             sub _fill_normal {
141 2077     2077   3357 my $self = shift;
142 2077         3048 my $t = shift;
143              
144             # Get the content up to the next separator
145 2077         6160 my $string = $self->_scan_for_unescaped_character( $t, $self->{separator} );
146 2077 50       6316 return undef unless defined $string;
147 2077 100       5375 if ( ref $string ) {
148             # End of file
149 577 100       1500 if ( length($$string) > 1 ) {
150             # Complete the properties for the first section
151 435         899 my $str = $$string;
152 435         943 chop $str;
153             $self->{sections}->[0] = {
154 435         2722 position => length($self->{content}),
155             size => length($$string) - 1,
156             type => "$self->{separator}$self->{separator}",
157             };
158 435         962 $self->{_sections} = 1;
159             } else {
160             # No sections at all
161 142         358 $self->{sections} = [ ];
162 142         278 $self->{_sections} = 0;
163             }
164 577         1138 $self->{content} .= $$string;
165 577         1170 return 0;
166             }
167              
168             # Complete the properties of the first section
169             $self->{sections}->[0] = {
170             position => length $self->{content},
171 1500         8965 size => length($string) - 1,
172             type => "$self->{separator}$self->{separator}",
173             };
174 1500         3651 $self->{content} .= $string;
175              
176             # We are done if there is only one section
177 1500 100       4721 return 1 if $self->{_sections} == 1;
178              
179             # There are two sections.
180              
181             # Advance into the next section
182 391         846 $t->{line_cursor}++;
183              
184             # Get the content up to the end separator
185 391         1297 $string = $self->_scan_for_unescaped_character( $t, $self->{separator} );
186 391 50       1496 return undef unless defined $string;
187 391 100       1417 if ( ref $string ) {
188             # End of file
189 88 100       362 if ( length($$string) > 1 ) {
190             # Complete the properties for the second section
191 85         194 my $str = $$string;
192 85         186 chop $str;
193             $self->{sections}->[1] = {
194 85         483 position => length($self->{content}),
195             size => length($$string) - 1,
196             type => "$self->{separator}$self->{separator}",
197             };
198             } else {
199             # No sections at all
200 3         7 $self->{_sections} = 1;
201             }
202 88         248 $self->{content} .= $$string;
203 88         214 return 0;
204             }
205              
206             # Complete the properties of the second section
207             $self->{sections}->[1] = {
208 303         1413 position => length($self->{content}),
209             size => length($string) - 1
210             };
211 303         715 $self->{content} .= $string;
212              
213 303         622 1;
214             }
215              
216             # Handle content parsing for matching brace separated
217             sub _fill_braced {
218 1579     1579   2739 my $self = shift;
219 1579         2462 my $t = shift;
220              
221             # Get the content up to the close character
222 1579         2894 my $section = $self->{sections}->[0];
223 1579         5317 my $brace_str = $self->_scan_for_brace_character( $t, $section->{_close} );
224 1579 50       4229 return undef unless defined $brace_str;
225 1579 100       3677 if ( ref $brace_str ) {
226             # End of file
227 149 100       470 if ( length($$brace_str) > 1 ) {
228             # Complete the properties for the first section
229 94         211 my $str = $$brace_str;
230 94         202 chop $str;
231             $self->{sections}->[0] = {
232             position => length($self->{content}),
233             size => length($$brace_str) - 1,
234             type => $section->{type},
235 94         542 };
236 94         224 $self->{_sections} = 1;
237             } else {
238             # No sections at all
239 55         170 $self->{sections} = [ ];
240 55         122 $self->{_sections} = 0;
241             }
242 149         321 $self->{content} .= $$brace_str;
243 149         465 return 0;
244             }
245              
246             # Complete the properties of the first section
247 1430         3339 $section->{position} = length $self->{content};
248 1430         3188 $section->{size} = length($brace_str) - 1;
249 1430         2701 $self->{content} .= $brace_str;
250 1430         2899 delete $section->{_close};
251              
252             # We are done if there is only one section
253 1430 100       4502 return 1 if $self->{_sections} == 1;
254              
255             # There are two sections.
256              
257             # Is there a gap between the sections.
258 165         453 my $char = substr( $t->{line}, ++$t->{line_cursor}, 1 );
259 165 100       581 if ( $char =~ /\s/ ) {
260             # Go past the gap
261 113         311 my $gap_str = $self->_scan_quote_like_operator_gap( $t );
262 113 50       262 return undef unless defined $gap_str;
263 113 100       238 if ( ref $gap_str ) {
264             # End of file
265 2         6 $self->{content} .= $$gap_str;
266 2         11 return 0;
267             }
268 111         219 $self->{content} .= $gap_str;
269 111         270 $char = substr( $t->{line}, $t->{line_cursor}, 1 );
270             }
271              
272 163         344 $section = $SECTIONS{$char};
273              
274 163 100       508 if ( $section ) {
    100          
275             # It's a brace
276              
277             # Initialize the second section
278 117         210 $self->{content} .= $char;
279 117         413 $section = { %$section };
280              
281             # Advance into the second section
282 117         233 $t->{line_cursor}++;
283              
284             # Get the content up to the close character
285 117         365 $brace_str = $self->_scan_for_brace_character( $t, $section->{_close} );
286 117 50       329 return undef unless defined $brace_str;
287 117 100       277 if ( ref $brace_str ) {
288             # End of file
289 6 100       21 if ( length($$brace_str) > 1 ) {
290             # Complete the properties for the second section
291 4         224 my $str = $$brace_str;
292 4         11 chop $str;
293             $self->{sections}->[1] = {
294             position => length($self->{content}),
295             size => length($$brace_str) - 1,
296             type => $section->{type},
297 4         22 };
298 4         149 $self->{_sections} = 2;
299             } else {
300             # No sections at all
301 2         12 $self->{_sections} = 1;
302             }
303 6         17 $self->{content} .= $$brace_str;
304 6         22 return 0;
305             } else {
306             # Complete the properties for the second section
307             $self->{sections}->[1] = {
308             position => length($self->{content}),
309             size => length($brace_str) - 1,
310             type => $section->{type},
311 111         478 };
312 111         228 $self->{content} .= $brace_str;
313             }
314             } elsif ( $char =~ m/ \A [^\w\s] \z /smx ) {
315             # It is some other delimiter (weird, but possible)
316              
317             # Add the delimiter to the content.
318 33         107 $self->{content} .= $char;
319              
320             # Advance into the next section
321 33         121 $t->{line_cursor}++;
322              
323             # Get the content up to the end separator
324 33         138 my $string = $self->_scan_for_unescaped_character( $t, $char );
325 33 50       225 return undef unless defined $string;
326 33 100       133 if ( ref $string ) {
327             # End of file
328 18 100       60 if ( length($$string) > 1 ) {
329             # Complete the properties for the second section
330 10         43 my $str = $$string;
331 10         29 chop $str;
332             $self->{sections}->[1] = {
333 10         71 position => length($self->{content}),
334             size => length($$string) - 1,
335             type => "$char$char",
336             };
337             } else {
338             # Only the one section
339 8         17 $self->{_sections} = 1;
340             }
341 18         54 $self->{content} .= $$string;
342 18         49 return 0;
343             }
344              
345             # Complete the properties of the second section
346             $self->{sections}->[1] = {
347 15         109 position => length($self->{content}),
348             size => length($string) - 1,
349             type => "$char$char",
350             };
351 15         62 $self->{content} .= $string;
352              
353             } else {
354              
355             # Error, it has to be a delimiter of some sort.
356             # Although this will result in a REALLY illegal regexp,
357             # we allow it anyway.
358              
359             # Create a null second section
360             $self->{sections}->[1] = {
361 13         73 position => length($self->{content}),
362             size => 0,
363             type => '',
364             };
365              
366             # Attach an error to the token and move on
367 13         51 $self->{_error} = "No second section of regexp, or does not start with a balanced character";
368              
369             # Roll back the cursor one char and return signalling end of regexp
370 13         29 $t->{line_cursor}--;
371 13         34 return 0;
372             }
373              
374 126         327 1;
375             }
376              
377              
378              
379              
380              
381             #####################################################################
382             # Additional methods to find out about the quote
383              
384             # In a scalar context, get the number of sections
385             # In an array context, get the section information
386             sub _sections {
387 78 100   78   4041 wantarray ? @{$_[0]->{sections}} : scalar @{$_[0]->{sections}}
  68         262  
  10         74  
388             }
389              
390             # Get a section's content
391             sub _section_content {
392 504     504   964 my $self = shift;
393 504         750 my $i = shift;
394 504 50       1392 $self->{sections} or return;
395 504 100       1241 my $section = $self->{sections}->[$i] or return;
396 500         1436 return substr( $self->content, $section->{position}, $section->{size} );
397             }
398              
399             # Get the modifiers if any.
400             # In list context, return the modifier hash.
401             # In scalar context, clone the hash and return a reference to it.
402             # If there are no modifiers, simply return.
403             sub _modifiers {
404 7     7   23 my $self = shift;
405 7 50       23 $self->{modifiers} or return;
406 7 100       23 wantarray and return %{ $self->{modifiers} };
  5         42  
407 2         6 return +{ %{ $self->{modifiers} } };
  2         15  
408             }
409              
410             # Get the delimiters, or at least give it a good try to get them.
411             sub _delimiters {
412 492     492   724 my $self = shift;
413 492 50       1014 $self->{sections} or return;
414 492         730 my @delims;
415 492         650 foreach my $sect ( @{ $self->{sections} } ) {
  492         1079  
416 494 100       1094 if ( exists $sect->{type} ) {
417 493         1230 push @delims, $sect->{type};
418             } else {
419 1         5 my $content = $self->content;
420             push @delims,
421             substr( $content, $sect->{position} - 1, 1 ) .
422 1         7 substr( $content, $sect->{position} + $sect->{size}, 1 );
423             }
424             }
425 492         1545 return @delims;
426             }
427              
428             1;
429              
430             =pod
431              
432             =head1 SUPPORT
433              
434             See the L in the main module.
435              
436             =head1 AUTHOR
437              
438             Adam Kennedy Eadamk@cpan.orgE
439              
440             =head1 COPYRIGHT
441              
442             Copyright 2001 - 2011 Adam Kennedy.
443              
444             This program is free software; you can redistribute
445             it and/or modify it under the same terms as Perl itself.
446              
447             The full text of the license can be found in the
448             LICENSE file included with this module.
449              
450             =cut