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 67     67   458 use strict;
  67         139  
  67         2528  
6 67     67   405 use Clone ();
  67         134  
  67         1246  
7 67     67   321 use Carp ();
  67         137  
  67         1383  
8 67     67   417 use PPI::Token::_QuoteEngine ();
  67         151  
  67         196575  
9              
10             our $VERSION = '1.28401'; # TRIAL
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 3845     3845 0 7909 my $class = shift;
54 3845 50       12392 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 3845 50       15471 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 3845 50       15635 my $options = $QUOTES{$init} or return $self->_error(
66             "Unknown quote type '$init'"
67             );
68 3845         24026 foreach ( keys %$options ) {
69 17295         45836 $self->{$_} = $options->{$_};
70             }
71              
72             # Set up the modifiers hash if needed
73 3845 100       15329 $self->{modifiers} = {} if $self->{modifiers};
74              
75             # Handle the special < base
76 3845 100       20590 $self->{sections}[0] = Clone::clone $SECTIONS{'<'} if $init eq '<';
77 3845 100       16620 $self->{sections}[0] = Clone::clone $SECTIONS{'('} if $init eq '(';
78              
79 3845         15765 $self;
80             }
81              
82             sub _fill {
83 3845     3845   7056 my $class = shift;
84 3845         7807 my $t = shift;
85             my $self = $t->{token}
86 3845 50       13705 or Carp::croak("::Full->_fill called without current token");
87              
88             # Load in the operator stuff if needed
89 3845 100       11129 if ( $self->{operator} ) {
90             # In an operator based quote-like, handle the gap between the
91             # operator and the opening separator.
92 2708 100       18234 if ( substr( $t->{line}, $t->{line_cursor}, 1 ) =~ /\s/ ) {
93             # Go past the gap
94 543         2622 my $gap = $self->_scan_quote_like_operator_gap( $t );
95 543 50       1626 return undef unless defined $gap;
96 543 100       3455 if ( ref $gap ) {
97             # End of file
98 193         610 $self->{content} .= $$gap;
99 193         618 return 0;
100             }
101 350         1074 $self->{content} .= $gap;
102             }
103              
104             # The character we are now on is the separator. Capture,
105             # and advance into the first section.
106 2515         8428 my $sep = substr( $t->{line}, $t->{line_cursor}++, 1 );
107 2515         6324 $self->{content} .= $sep;
108              
109             # Determine if these are normal or braced type sections
110 2515 100       8560 if ( my $section = $SECTIONS{$sep} ) {
111 1125         2542 $self->{braced} = 1;
112 1125         17636 $self->{sections}->[0] = Clone::clone($section);
113             } else {
114 1390         4580 $self->{braced} = 0;
115 1390         4112 $self->{separator} = $sep;
116             }
117             }
118              
119             # Parse different based on whether we are normal or braced
120             my $rv = $self->{braced}
121 3652 100       19862 ? $self->_fill_braced($t)
122             : $self->_fill_normal($t);
123 3652 100       15354 return $rv if !$rv;
124              
125             # Return now unless it has modifiers ( i.e. s/foo//eieio )
126 2788 100       10547 return 1 unless $self->{modifiers};
127              
128             # Check for modifiers
129 1207         2106 my $char;
130 1207         2429 my $len = 0;
131 1207         11948 while ( ($char = substr( $t->{line}, $t->{line_cursor} + 1, 1 )) =~ /[^\W\d_]/ ) {
132 469         909 $len++;
133 469         958 $self->{content} .= $char;
134 469         1873 $self->{modifiers}->{lc $char} = 1;
135 469         3046 $t->{line_cursor}++;
136             }
137             }
138              
139             # Handle the content parsing path for normally separated
140             sub _fill_normal {
141 2072     2072   3976 my $self = shift;
142 2072         3701 my $t = shift;
143              
144             # Get the content up to the next separator
145 2072         13746 my $string = $self->_scan_for_unescaped_character( $t, $self->{separator} );
146 2072 50       6439 return undef unless defined $string;
147 2072 100       6204 if ( ref $string ) {
148             # End of file
149 586 100       1886 if ( length($$string) > 1 ) {
150             # Complete the properties for the first section
151 442         1813 my $str = $$string;
152 442         1427 chop $str;
153             $self->{sections}->[0] = {
154 442         4641 position => length($self->{content}),
155             size => length($$string) - 1,
156             type => "$self->{separator}$self->{separator}",
157             };
158 442         1190 $self->{_sections} = 1;
159             } else {
160             # No sections at all
161 144         551 $self->{sections} = [ ];
162 144         356 $self->{_sections} = 0;
163             }
164 586         1703 $self->{content} .= $$string;
165 586         1684 return 0;
166             }
167              
168             # Complete the properties of the first section
169             $self->{sections}->[0] = {
170             position => length $self->{content},
171 1486         14282 size => length($string) - 1,
172             type => "$self->{separator}$self->{separator}",
173             };
174 1486         4120 $self->{content} .= $string;
175              
176             # We are done if there is only one section
177 1486 100       6321 return 1 if $self->{_sections} == 1;
178              
179             # There are two sections.
180              
181             # Advance into the next section
182 394         950 $t->{line_cursor}++;
183              
184             # Get the content up to the end separator
185 394         1730 $string = $self->_scan_for_unescaped_character( $t, $self->{separator} );
186 394 50       1715 return undef unless defined $string;
187 394 100       1316 if ( ref $string ) {
188             # End of file
189 75 100       298 if ( length($$string) > 1 ) {
190             # Complete the properties for the second section
191 72         185 my $str = $$string;
192 72         216 chop $str;
193             $self->{sections}->[1] = {
194 72         543 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         10 $self->{_sections} = 1;
201             }
202 75         238 $self->{content} .= $$string;
203 75         244 return 0;
204             }
205              
206             # Complete the properties of the second section
207             $self->{sections}->[1] = {
208 319         1725 position => length($self->{content}),
209             size => length($string) - 1
210             };
211 319         1045 $self->{content} .= $string;
212              
213 319         922 1;
214             }
215              
216             # Handle content parsing for matching brace separated
217             sub _fill_braced {
218 1580     1580   3309 my $self = shift;
219 1580         2998 my $t = shift;
220              
221             # Get the content up to the close character
222 1580         4129 my $section = $self->{sections}->[0];
223 1580         8478 my $brace_str = $self->_scan_for_brace_character( $t, $section->{_close} );
224 1580 50       4756 return undef unless defined $brace_str;
225 1580 100       5434 if ( ref $brace_str ) {
226             # End of file
227 164 100       517 if ( length($$brace_str) > 1 ) {
228             # Complete the properties for the first section
229 109         304 my $str = $$brace_str;
230 109         314 chop $str;
231             $self->{sections}->[0] = {
232             position => length($self->{content}),
233             size => length($$brace_str) - 1,
234             type => $section->{type},
235 109         970 };
236 109         309 $self->{_sections} = 1;
237             } else {
238             # No sections at all
239 55         199 $self->{sections} = [ ];
240 55         131 $self->{_sections} = 0;
241             }
242 164         505 $self->{content} .= $$brace_str;
243 164         638 return 0;
244             }
245              
246             # Complete the properties of the first section
247 1416         10769 $section->{position} = length $self->{content};
248 1416         6563 $section->{size} = length($brace_str) - 1;
249 1416         3359 $self->{content} .= $brace_str;
250 1416         3579 delete $section->{_close};
251              
252             # We are done if there is only one section
253 1416 100       10278 return 1 if $self->{_sections} == 1;
254              
255             # There are two sections.
256              
257             # Is there a gap between the sections.
258 155         531 my $char = substr( $t->{line}, ++$t->{line_cursor}, 1 );
259 155 100       770 if ( $char =~ /\s/ ) {
260             # Go past the gap
261 115         376 my $gap_str = $self->_scan_quote_like_operator_gap( $t );
262 115 50       346 return undef unless defined $gap_str;
263 115 100       349 if ( ref $gap_str ) {
264             # End of file
265 2         4 $self->{content} .= $$gap_str;
266 2         4 return 0;
267             }
268 113         406 $self->{content} .= $gap_str;
269 113         340 $char = substr( $t->{line}, $t->{line_cursor}, 1 );
270             }
271              
272 153         429 $section = $SECTIONS{$char};
273              
274 153 100       554 if ( $section ) {
    100          
275             # It's a brace
276              
277             # Initialize the second section
278 119         265 $self->{content} .= $char;
279 119         538 $section = { %$section };
280              
281             # Advance into the second section
282 119         276 $t->{line_cursor}++;
283              
284             # Get the content up to the close character
285 119         444 $brace_str = $self->_scan_for_brace_character( $t, $section->{_close} );
286 119 50       409 return undef unless defined $brace_str;
287 119 100       358 if ( ref $brace_str ) {
288             # End of file
289 6 100       23 if ( length($$brace_str) > 1 ) {
290             # Complete the properties for the second section
291 4         10 my $str = $$brace_str;
292 4         10 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         7 $self->{_sections} = 2;
299             } else {
300             # No sections at all
301 2         6 $self->{_sections} = 1;
302             }
303 6         13 $self->{content} .= $$brace_str;
304 6         24 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 113         639 };
312 113         304 $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 16         49 $self->{content} .= $char;
319              
320             # Advance into the next section
321 16         38 $t->{line_cursor}++;
322              
323             # Get the content up to the end separator
324 16         83 my $string = $self->_scan_for_unescaped_character( $t, $char );
325 16 50       68 return undef unless defined $string;
326 16 100       88 if ( ref $string ) {
327             # End of file
328 13 100       54 if ( length($$string) > 1 ) {
329             # Complete the properties for the second section
330 5         18 my $str = $$string;
331 5         33 chop $str;
332             $self->{sections}->[1] = {
333 5         42 position => length($self->{content}),
334             size => length($$string) - 1,
335             type => "$char$char",
336             };
337             } else {
338             # Only the one section
339 8         25 $self->{_sections} = 1;
340             }
341 13         45 $self->{content} .= $$string;
342 13         43 return 0;
343             }
344              
345             # Complete the properties of the second section
346             $self->{sections}->[1] = {
347 3         20 position => length($self->{content}),
348             size => length($string) - 1,
349             type => "$char$char",
350             };
351 3         10 $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 18         99 position => length($self->{content}),
362             size => 0,
363             type => '',
364             };
365              
366             # Attach an error to the token and move on
367 18         59 $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 18         41 $t->{line_cursor}--;
371 18         47 return 0;
372             }
373              
374 116         432 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   4786 wantarray ? @{$_[0]->{sections}} : scalar @{$_[0]->{sections}}
  68         348  
  10         87  
388             }
389              
390             # Get a section's content
391             sub _section_content {
392 506     506   1134 my $self = shift;
393 506         906 my $i = shift;
394 506 50       2386 $self->{sections} or return;
395 506 100       1769 my $section = $self->{sections}->[$i] or return;
396 502         2240 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   19 my $self = shift;
405 7 50       34 $self->{modifiers} or return;
406 7 100       28 wantarray and return %{ $self->{modifiers} };
  5         47  
407 2         6 return +{ %{ $self->{modifiers} } };
  2         19  
408             }
409              
410             # Get the delimiters, or at least give it a good try to get them.
411             sub _delimiters {
412 494     494   1016 my $self = shift;
413 494 50       1713 $self->{sections} or return;
414 494         958 my @delims;
415 494         849 foreach my $sect ( @{ $self->{sections} } ) {
  494         1778  
416 496 100       1375 if ( exists $sect->{type} ) {
417 495         1793 push @delims, $sect->{type};
418             } else {
419 1         4 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 494         2090 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