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 69     69   359 use strict;
  69         98  
  69         1832  
6 69     69   259 use Clone ();
  69         102  
  69         806  
7 69     69   235 use Carp ();
  69         113  
  69         730  
8 69     69   210 use PPI::Token::_QuoteEngine ();
  69         103  
  69         127493  
9              
10             our $VERSION = '1.291';
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 3852     3852 0 5703 my $class = shift;
54 3852 50       8182 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 3852 50       8888 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 3852 50       9945 my $options = $QUOTES{$init} or return $self->_error(
66             "Unknown quote type '$init'"
67             );
68 3852         13275 foreach ( keys %$options ) {
69 17315         29415 $self->{$_} = $options->{$_};
70             }
71              
72             # Set up the modifiers hash if needed
73 3852 100       9526 $self->{modifiers} = {} if $self->{modifiers};
74              
75             # Handle the special < base
76 3852 100       8028 $self->{sections}[0] = Clone::clone $SECTIONS{'<'} if $init eq '<';
77 3852 100       9906 $self->{sections}[0] = Clone::clone $SECTIONS{'('} if $init eq '(';
78              
79 3852         9367 $self;
80             }
81              
82             sub _fill {
83 3852     3852   4653 my $class = shift;
84 3852         4480 my $t = shift;
85             my $self = $t->{token}
86 3852 50       9093 or Carp::croak("::Full->_fill called without current token");
87              
88             # Load in the operator stuff if needed
89 3852 100       8099 if ( $self->{operator} ) {
90             # In an operator based quote-like, handle the gap between the
91             # operator and the opening separator.
92 2696 100       9409 if ( substr( $t->{line}, $t->{line_cursor}, 1 ) =~ /\s/ ) {
93             # Go past the gap
94 536         1627 my $gap = $self->_scan_quote_like_operator_gap( $t );
95 536 50       969 return undef unless defined $gap;
96 536 100       2409 if ( ref $gap ) {
97             # End of file
98 181         348 $self->{content} .= $$gap;
99 181         418 return 0;
100             }
101 355         750 $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         4895 my $sep = substr( $t->{line}, $t->{line_cursor}++, 1 );
107 2515         3951 $self->{content} .= $sep;
108              
109             # Determine if these are normal or braced type sections
110 2515 100       5215 if ( my $section = $SECTIONS{$sep} ) {
111 1114         1786 $self->{braced} = 1;
112 1114         11205 $self->{sections}->[0] = Clone::clone($section);
113             } else {
114 1401         2007 $self->{braced} = 0;
115 1401         2437 $self->{separator} = $sep;
116             }
117             }
118              
119             # Parse different based on whether we are normal or braced
120             my $rv = $self->{braced}
121 3671 100       11777 ? $self->_fill_braced($t)
122             : $self->_fill_normal($t);
123 3671 100       7272 return $rv if !$rv;
124              
125             # Return now unless it has modifiers ( i.e. s/foo//eieio )
126 2807 100       6615 return 1 unless $self->{modifiers};
127              
128             # Check for modifiers
129 1209         1390 my $char;
130 1209         1628 my $len = 0;
131 1209         6221 while ( ($char = substr( $t->{line}, $t->{line_cursor} + 1, 1 )) =~ /[^\W\d_]/ ) {
132 469         634 $len++;
133 469         677 $self->{content} .= $char;
134 469         1213 $self->{modifiers}->{lc $char} = 1;
135 469         1978 $t->{line_cursor}++;
136             }
137             }
138              
139             # Handle the content parsing path for normally separated
140             sub _fill_normal {
141 2086     2086   2663 my $self = shift;
142 2086         2382 my $t = shift;
143              
144             # Get the content up to the next separator
145 2086         5449 my $string = $self->_scan_for_unescaped_character( $t, $self->{separator} );
146 2086 50       4240 return undef unless defined $string;
147 2086 100       3752 if ( ref $string ) {
148             # End of file
149 579 100       1110 if ( length($$string) > 1 ) {
150             # Complete the properties for the first section
151 433         822 my $str = $$string;
152 433         716 chop $str;
153             $self->{sections}->[0] = {
154 433         2482 position => length($self->{content}),
155             size => length($$string) - 1,
156             type => "$self->{separator}$self->{separator}",
157             };
158 433         849 $self->{_sections} = 1;
159             } else {
160             # No sections at all
161 146         283 $self->{sections} = [ ];
162 146         215 $self->{_sections} = 0;
163             }
164 579         1002 $self->{content} .= $$string;
165 579         921 return 0;
166             }
167              
168             # Complete the properties of the first section
169             $self->{sections}->[0] = {
170             position => length $self->{content},
171 1507         8681 size => length($string) - 1,
172             type => "$self->{separator}$self->{separator}",
173             };
174 1507         2900 $self->{content} .= $string;
175              
176             # We are done if there is only one section
177 1507 100       3793 return 1 if $self->{_sections} == 1;
178              
179             # There are two sections.
180              
181             # Advance into the next section
182 385         678 $t->{line_cursor}++;
183              
184             # Get the content up to the end separator
185 385         1018 $string = $self->_scan_for_unescaped_character( $t, $self->{separator} );
186 385 50       962 return undef unless defined $string;
187 385 100       749 if ( ref $string ) {
188             # End of file
189 84 100       235 if ( length($$string) > 1 ) {
190             # Complete the properties for the second section
191 83         142 my $str = $$string;
192 83         175 chop $str;
193             $self->{sections}->[1] = {
194 83         372 position => length($self->{content}),
195             size => length($$string) - 1,
196             type => "$self->{separator}$self->{separator}",
197             };
198             } else {
199             # No sections at all
200 1         2 $self->{_sections} = 1;
201             }
202 84         180 $self->{content} .= $$string;
203 84         181 return 0;
204             }
205              
206             # Complete the properties of the second section
207             $self->{sections}->[1] = {
208 301         1034 position => length($self->{content}),
209             size => length($string) - 1
210             };
211 301         566 $self->{content} .= $string;
212              
213 301         501 1;
214             }
215              
216             # Handle content parsing for matching brace separated
217             sub _fill_braced {
218 1585     1585   2131 my $self = shift;
219 1585         2163 my $t = shift;
220              
221             # Get the content up to the close character
222 1585         2390 my $section = $self->{sections}->[0];
223 1585         5135 my $brace_str = $self->_scan_for_brace_character( $t, $section->{_close} );
224 1585 50       3088 return undef unless defined $brace_str;
225 1585 100       3191 if ( ref $brace_str ) {
226             # End of file
227 153 100       330 if ( length($$brace_str) > 1 ) {
228             # Complete the properties for the first section
229 96         146 my $str = $$brace_str;
230 96         161 chop $str;
231             $self->{sections}->[0] = {
232             position => length($self->{content}),
233             size => length($$brace_str) - 1,
234             type => $section->{type},
235 96         489 };
236 96         198 $self->{_sections} = 1;
237             } else {
238             # No sections at all
239 57         132 $self->{sections} = [ ];
240 57         98 $self->{_sections} = 0;
241             }
242 153         300 $self->{content} .= $$brace_str;
243 153         404 return 0;
244             }
245              
246             # Complete the properties of the first section
247 1432         3034 $section->{position} = length $self->{content};
248 1432         2735 $section->{size} = length($brace_str) - 1;
249 1432         2287 $self->{content} .= $brace_str;
250 1432         2435 delete $section->{_close};
251              
252             # We are done if there is only one section
253 1432 100       3639 return 1 if $self->{_sections} == 1;
254              
255             # There are two sections.
256              
257             # Is there a gap between the sections.
258 170         378 my $char = substr( $t->{line}, ++$t->{line_cursor}, 1 );
259 170 100       495 if ( $char =~ /\s/ ) {
260             # Go past the gap
261 119         237 my $gap_str = $self->_scan_quote_like_operator_gap( $t );
262 119 50       261 return undef unless defined $gap_str;
263 119 100       198 if ( ref $gap_str ) {
264             # End of file
265 2         4 $self->{content} .= $$gap_str;
266 2         4 return 0;
267             }
268 117         170 $self->{content} .= $gap_str;
269 117         241 $char = substr( $t->{line}, $t->{line_cursor}, 1 );
270             }
271              
272 168         290 $section = $SECTIONS{$char};
273              
274 168 100       461 if ( $section ) {
    100          
275             # It's a brace
276              
277             # Initialize the second section
278 118         174 $self->{content} .= $char;
279 118         320 $section = { %$section };
280              
281             # Advance into the second section
282 118         183 $t->{line_cursor}++;
283              
284             # Get the content up to the close character
285 118         243 $brace_str = $self->_scan_for_brace_character( $t, $section->{_close} );
286 118 50       226 return undef unless defined $brace_str;
287 118 100       200 if ( ref $brace_str ) {
288             # End of file
289 6 100       12 if ( length($$brace_str) > 1 ) {
290             # Complete the properties for the second section
291 4         8 my $str = $$brace_str;
292 4         9 chop $str;
293             $self->{sections}->[1] = {
294             position => length($self->{content}),
295             size => length($$brace_str) - 1,
296             type => $section->{type},
297 4         17 };
298 4         7 $self->{_sections} = 2;
299             } else {
300             # No sections at all
301 2         4 $self->{_sections} = 1;
302             }
303 6         8 $self->{content} .= $$brace_str;
304 6         18 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 112         360 };
312 112         204 $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 30         51 $self->{content} .= $char;
319              
320             # Advance into the next section
321 30         47 $t->{line_cursor}++;
322              
323             # Get the content up to the end separator
324 30         95 my $string = $self->_scan_for_unescaped_character( $t, $char );
325 30 50       85 return undef unless defined $string;
326 30 100       94 if ( ref $string ) {
327             # End of file
328 20 100       54 if ( length($$string) > 1 ) {
329             # Complete the properties for the second section
330 11         25 my $str = $$string;
331 11         28 chop $str;
332             $self->{sections}->[1] = {
333 11         66 position => length($self->{content}),
334             size => length($$string) - 1,
335             type => "$char$char",
336             };
337             } else {
338             # Only the one section
339 9         16 $self->{_sections} = 1;
340             }
341 20         38 $self->{content} .= $$string;
342 20         45 return 0;
343             }
344              
345             # Complete the properties of the second section
346             $self->{sections}->[1] = {
347 10         63 position => length($self->{content}),
348             size => length($string) - 1,
349             type => "$char$char",
350             };
351 10         22 $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 20         85 position => length($self->{content}),
362             size => 0,
363             type => '',
364             };
365              
366             # Attach an error to the token and move on
367 20         53 $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 20         38 $t->{line_cursor}--;
371 20         38 return 0;
372             }
373              
374 122         329 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   2823 wantarray ? @{$_[0]->{sections}} : scalar @{$_[0]->{sections}}
  68         226  
  10         40  
388             }
389              
390             # Get a section's content
391             sub _section_content {
392 506     506   782 my $self = shift;
393 506         758 my $i = shift;
394 506 50       1317 $self->{sections} or return;
395 506 100       1233 my $section = $self->{sections}->[$i] or return;
396 502         1569 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   12 my $self = shift;
405 7 50       20 $self->{modifiers} or return;
406 7 100       16 wantarray and return %{ $self->{modifiers} };
  5         29  
407 2         4 return +{ %{ $self->{modifiers} } };
  2         11  
408             }
409              
410             # Get the delimiters, or at least give it a good try to get them.
411             sub _delimiters {
412 494     494   689 my $self = shift;
413 494 50       929 $self->{sections} or return;
414 494         630 my @delims;
415 494         668 foreach my $sect ( @{ $self->{sections} } ) {
  494         1159  
416 496 100       1025 if ( exists $sect->{type} ) {
417 495         1219 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         5 substr( $content, $sect->{position} + $sect->{size}, 1 );
423             }
424             }
425 494         1443 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