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   333 use strict;
  67         96  
  67         1784  
6 67     67   256 use Clone ();
  67         90  
  67         651  
7 67     67   234 use Carp ();
  67         87  
  67         779  
8 67     67   214 use PPI::Token::_QuoteEngine ();
  67         95  
  67         127796  
9              
10             our $VERSION = '1.284';
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 3752     3752 0 5600 my $class = shift;
54 3752 50       9336 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 3752 50       9480 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 3752 50       10776 my $options = $QUOTES{$init} or return $self->_error(
66             "Unknown quote type '$init'"
67             );
68 3752         13793 foreach ( keys %$options ) {
69 16838         29528 $self->{$_} = $options->{$_};
70             }
71              
72             # Set up the modifiers hash if needed
73 3752 100       10201 $self->{modifiers} = {} if $self->{modifiers};
74              
75             # Handle the special < base
76 3752 100       8284 $self->{sections}[0] = Clone::clone $SECTIONS{'<'} if $init eq '<';
77 3752 100       10632 $self->{sections}[0] = Clone::clone $SECTIONS{'('} if $init eq '(';
78              
79 3752         9587 $self;
80             }
81              
82             sub _fill {
83 3752     3752   4957 my $class = shift;
84 3752         4784 my $t = shift;
85             my $self = $t->{token}
86 3752 50       9840 or Carp::croak("::Full->_fill called without current token");
87              
88             # Load in the operator stuff if needed
89 3752 100       8150 if ( $self->{operator} ) {
90             # In an operator based quote-like, handle the gap between the
91             # operator and the opening separator.
92 2611 100       10182 if ( substr( $t->{line}, $t->{line_cursor}, 1 ) =~ /\s/ ) {
93             # Go past the gap
94 535         1589 my $gap = $self->_scan_quote_like_operator_gap( $t );
95 535 50       965 return undef unless defined $gap;
96 535 100       1118 if ( ref $gap ) {
97             # End of file
98 185         504 $self->{content} .= $$gap;
99 185         370 return 0;
100             }
101 350         671 $self->{content} .= $gap;
102             }
103              
104             # The character we are now on is the separator. Capture,
105             # and advance into the first section.
106 2426         5848 my $sep = substr( $t->{line}, $t->{line_cursor}++, 1 );
107 2426         4417 $self->{content} .= $sep;
108              
109             # Determine if these are normal or braced type sections
110 2426 100       5604 if ( my $section = $SECTIONS{$sep} ) {
111 1109         1962 $self->{braced} = 1;
112 1109         12436 $self->{sections}->[0] = Clone::clone($section);
113             } else {
114 1317         2288 $self->{braced} = 0;
115 1317         2828 $self->{separator} = $sep;
116             }
117             }
118              
119             # Parse different based on whether we are normal or braced
120             my $rv = $self->{braced}
121 3567 100       12665 ? $self->_fill_braced($t)
122             : $self->_fill_normal($t);
123 3567 100       7964 return $rv if !$rv;
124              
125             # Return now unless it has modifiers ( i.e. s/foo//eieio )
126 2710 100       7399 return 1 unless $self->{modifiers};
127              
128             # Check for modifiers
129 1141         1574 my $char;
130 1141         2094 my $len = 0;
131 1141         7013 while ( ($char = substr( $t->{line}, $t->{line_cursor} + 1, 1 )) =~ /[^\W\d_]/ ) {
132 433         582 $len++;
133 433         812 $self->{content} .= $char;
134 433         1453 $self->{modifiers}->{lc $char} = 1;
135 433         1935 $t->{line_cursor}++;
136             }
137             }
138              
139             # Handle the content parsing path for normally separated
140             sub _fill_normal {
141 1999     1999   2853 my $self = shift;
142 1999         2505 my $t = shift;
143              
144             # Get the content up to the next separator
145 1999         6375 my $string = $self->_scan_for_unescaped_character( $t, $self->{separator} );
146 1999 50       5068 return undef unless defined $string;
147 1999 100       3918 if ( ref $string ) {
148             # End of file
149 576 100       1071 if ( length($$string) > 1 ) {
150             # Complete the properties for the first section
151 432         917 my $str = $$string;
152 432         753 chop $str;
153             $self->{sections}->[0] = {
154 432         2822 position => length($self->{content}),
155             size => length($$string) - 1,
156             type => "$self->{separator}$self->{separator}",
157             };
158 432         948 $self->{_sections} = 1;
159             } else {
160             # No sections at all
161 144         216 $self->{sections} = [ ];
162 144         234 $self->{_sections} = 0;
163             }
164 576         1019 $self->{content} .= $$string;
165 576         945 return 0;
166             }
167              
168             # Complete the properties of the first section
169             $self->{sections}->[0] = {
170             position => length $self->{content},
171 1423         9299 size => length($string) - 1,
172             type => "$self->{separator}$self->{separator}",
173             };
174 1423         2854 $self->{content} .= $string;
175              
176             # We are done if there is only one section
177 1423 100       4071 return 1 if $self->{_sections} == 1;
178              
179             # There are two sections.
180              
181             # Advance into the next section
182 334         530 $t->{line_cursor}++;
183              
184             # Get the content up to the end separator
185 334         1138 $string = $self->_scan_for_unescaped_character( $t, $self->{separator} );
186 334 50       1075 return undef unless defined $string;
187 334 100       761 if ( ref $string ) {
188             # End of file
189 76 100       232 if ( length($$string) > 1 ) {
190             # Complete the properties for the second section
191 75         144 my $str = $$string;
192 75         162 chop $str;
193             $self->{sections}->[1] = {
194 75         376 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 76         173 $self->{content} .= $$string;
203 76         167 return 0;
204             }
205              
206             # Complete the properties of the second section
207             $self->{sections}->[1] = {
208 258         816 position => length($self->{content}),
209             size => length($string) - 1
210             };
211 258         539 $self->{content} .= $string;
212              
213 258         525 1;
214             }
215              
216             # Handle content parsing for matching brace separated
217             sub _fill_braced {
218 1568     1568   2515 my $self = shift;
219 1568         2095 my $t = shift;
220              
221             # Get the content up to the close character
222 1568         2934 my $section = $self->{sections}->[0];
223 1568         5348 my $brace_str = $self->_scan_for_brace_character( $t, $section->{_close} );
224 1568 50       3627 return undef unless defined $brace_str;
225 1568 100       3537 if ( ref $brace_str ) {
226             # End of file
227 158 100       398 if ( length($$brace_str) > 1 ) {
228             # Complete the properties for the first section
229 103         189 my $str = $$brace_str;
230 103         220 chop $str;
231             $self->{sections}->[0] = {
232             position => length($self->{content}),
233             size => length($$brace_str) - 1,
234             type => $section->{type},
235 103         596 };
236 103         200 $self->{_sections} = 1;
237             } else {
238             # No sections at all
239 55         125 $self->{sections} = [ ];
240 55         89 $self->{_sections} = 0;
241             }
242 158         349 $self->{content} .= $$brace_str;
243 158         420 return 0;
244             }
245              
246             # Complete the properties of the first section
247 1410         3919 $section->{position} = length $self->{content};
248 1410         2959 $section->{size} = length($brace_str) - 1;
249 1410         2517 $self->{content} .= $brace_str;
250 1410         2840 delete $section->{_close};
251              
252             # We are done if there is only one section
253 1410 100       4110 return 1 if $self->{_sections} == 1;
254              
255             # There are two sections.
256              
257             # Is there a gap between the sections.
258 168         416 my $char = substr( $t->{line}, ++$t->{line_cursor}, 1 );
259 168 100       553 if ( $char =~ /\s/ ) {
260             # Go past the gap
261 118         311 my $gap_str = $self->_scan_quote_like_operator_gap( $t );
262 118 50       245 return undef unless defined $gap_str;
263 118 100       218 if ( ref $gap_str ) {
264             # End of file
265 2         4 $self->{content} .= $$gap_str;
266 2         3 return 0;
267             }
268 116         208 $self->{content} .= $gap_str;
269 116         243 $char = substr( $t->{line}, $t->{line_cursor}, 1 );
270             }
271              
272 166         283 $section = $SECTIONS{$char};
273              
274 166 100       462 if ( $section ) {
    100          
275             # It's a brace
276              
277             # Initialize the second section
278 121         204 $self->{content} .= $char;
279 121         439 $section = { %$section };
280              
281             # Advance into the second section
282 121         221 $t->{line_cursor}++;
283              
284             # Get the content up to the close character
285 121         265 $brace_str = $self->_scan_for_brace_character( $t, $section->{_close} );
286 121 50       262 return undef unless defined $brace_str;
287 121 100       245 if ( ref $brace_str ) {
288             # End of file
289 9 100       24 if ( length($$brace_str) > 1 ) {
290             # Complete the properties for the second section
291 7         13 my $str = $$brace_str;
292 7         15 chop $str;
293             $self->{sections}->[1] = {
294             position => length($self->{content}),
295             size => length($$brace_str) - 1,
296             type => $section->{type},
297 7         31 };
298 7         14 $self->{_sections} = 2;
299             } else {
300             # No sections at all
301 2         5 $self->{_sections} = 1;
302             }
303 9         17 $self->{content} .= $$brace_str;
304 9         25 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         406 };
312 112         259 $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 22         44 $self->{content} .= $char;
319              
320             # Advance into the next section
321 22         34 $t->{line_cursor}++;
322              
323             # Get the content up to the end separator
324 22         85 my $string = $self->_scan_for_unescaped_character( $t, $char );
325 22 50       64 return undef unless defined $string;
326 22 100       70 if ( ref $string ) {
327             # End of file
328 13 100       37 if ( length($$string) > 1 ) {
329             # Complete the properties for the second section
330 5         23 my $str = $$string;
331 5         12 chop $str;
332             $self->{sections}->[1] = {
333 5         27 position => length($self->{content}),
334             size => length($$string) - 1,
335             type => "$char$char",
336             };
337             } else {
338             # Only the one section
339 8         14 $self->{_sections} = 1;
340             }
341 13         22 $self->{content} .= $$string;
342 13         30 return 0;
343             }
344              
345             # Complete the properties of the second section
346             $self->{sections}->[1] = {
347 9         47 position => length($self->{content}),
348             size => length($string) - 1,
349             type => "$char$char",
350             };
351 9         23 $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 23         97 position => length($self->{content}),
362             size => 0,
363             type => '',
364             };
365              
366             # Attach an error to the token and move on
367 23         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 23         85 $t->{line_cursor}--;
371 23         46 return 0;
372             }
373              
374 121         341 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   2857 wantarray ? @{$_[0]->{sections}} : scalar @{$_[0]->{sections}}
  68         273  
  10         41  
388             }
389              
390             # Get a section's content
391             sub _section_content {
392 531     531   805 my $self = shift;
393 531         606 my $i = shift;
394 531 50       1459 $self->{sections} or return;
395 531 100       1112 my $section = $self->{sections}->[$i] or return;
396 527         1443 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   15 my $self = shift;
405 7 50       23 $self->{modifiers} or return;
406 7 100       17 wantarray and return %{ $self->{modifiers} };
  5         35  
407 2         5 return +{ %{ $self->{modifiers} } };
  2         27  
408             }
409              
410             # Get the delimiters, or at least give it a good try to get them.
411             sub _delimiters {
412 519     519   574 my $self = shift;
413 519 50       936 $self->{sections} or return;
414 519         571 my @delims;
415 519         585 foreach my $sect ( @{ $self->{sections} } ) {
  519         1026  
416 521 100       1016 if ( exists $sect->{type} ) {
417 520         1076 push @delims, $sect->{type};
418             } else {
419 1         3 my $content = $self->content;
420             push @delims,
421             substr( $content, $sect->{position} - 1, 1 ) .
422 1         6 substr( $content, $sect->{position} + $sect->{size}, 1 );
423             }
424             }
425 519         1322 return @delims;
426             }
427              
428             1;
429              
430             =pod
431              
432             =head1 SUPPORT
433              
434             See the L<support section|PPI/SUPPORT> in the main module.
435              
436             =head1 AUTHOR
437              
438             Adam Kennedy E<lt>adamk@cpan.orgE<gt>
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