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 68     68   353 use strict;
  68         96  
  68         1900  
6 68     68   270 use Clone ();
  68         89  
  68         700  
7 68     68   188 use Carp ();
  68         81  
  68         685  
8 68     68   205 use PPI::Token::_QuoteEngine ();
  68         99  
  68         128927  
9              
10             our $VERSION = '1.287';
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 3834     3834 0 6055 my $class = shift;
54 3834 50       8067 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 3834 50       10276 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 3834 50       10995 my $options = $QUOTES{$init} or return $self->_error(
66             "Unknown quote type '$init'"
67             );
68 3834         14090 foreach ( keys %$options ) {
69 17239         29467 $self->{$_} = $options->{$_};
70             }
71              
72             # Set up the modifiers hash if needed
73 3834 100       10951 $self->{modifiers} = {} if $self->{modifiers};
74              
75             # Handle the special < base
76 3834 100       8581 $self->{sections}[0] = Clone::clone $SECTIONS{'<'} if $init eq '<';
77 3834 100       9739 $self->{sections}[0] = Clone::clone $SECTIONS{'('} if $init eq '(';
78              
79 3834         9485 $self;
80             }
81              
82             sub _fill {
83 3834     3834   4995 my $class = shift;
84 3834         4221 my $t = shift;
85             my $self = $t->{token}
86 3834 50       9154 or Carp::croak("::Full->_fill called without current token");
87              
88             # Load in the operator stuff if needed
89 3834 100       7426 if ( $self->{operator} ) {
90             # In an operator based quote-like, handle the gap between the
91             # operator and the opening separator.
92 2660 100       10041 if ( substr( $t->{line}, $t->{line_cursor}, 1 ) =~ /\s/ ) {
93             # Go past the gap
94 535         1585 my $gap = $self->_scan_quote_like_operator_gap( $t );
95 535 50       981 return undef unless defined $gap;
96 535 100       2335 if ( ref $gap ) {
97             # End of file
98 189         333 $self->{content} .= $$gap;
99 189         389 return 0;
100             }
101 346         616 $self->{content} .= $gap;
102             }
103              
104             # The character we are now on is the separator. Capture,
105             # and advance into the first section.
106 2471         5490 my $sep = substr( $t->{line}, $t->{line_cursor}++, 1 );
107 2471         4535 $self->{content} .= $sep;
108              
109             # Determine if these are normal or braced type sections
110 2471 100       5421 if ( my $section = $SECTIONS{$sep} ) {
111 1101         1943 $self->{braced} = 1;
112 1101         11815 $self->{sections}->[0] = Clone::clone($section);
113             } else {
114 1370         2264 $self->{braced} = 0;
115 1370         2657 $self->{separator} = $sep;
116             }
117             }
118              
119             # Parse different based on whether we are normal or braced
120             my $rv = $self->{braced}
121 3645 100       12620 ? $self->_fill_braced($t)
122             : $self->_fill_normal($t);
123 3645 100       7550 return $rv if !$rv;
124              
125             # Return now unless it has modifiers ( i.e. s/foo//eieio )
126 2793 100       7007 return 1 unless $self->{modifiers};
127              
128             # Check for modifiers
129 1214         1526 my $char;
130 1214         1987 my $len = 0;
131 1214         6990 while ( ($char = substr( $t->{line}, $t->{line_cursor} + 1, 1 )) =~ /[^\W\d_]/ ) {
132 470         665 $len++;
133 470         695 $self->{content} .= $char;
134 470         1311 $self->{modifiers}->{lc $char} = 1;
135 470         2102 $t->{line_cursor}++;
136             }
137             }
138              
139             # Handle the content parsing path for normally separated
140             sub _fill_normal {
141 2079     2079   2474 my $self = shift;
142 2079         2384 my $t = shift;
143              
144             # Get the content up to the next separator
145 2079         6247 my $string = $self->_scan_for_unescaped_character( $t, $self->{separator} );
146 2079 50       4556 return undef unless defined $string;
147 2079 100       3836 if ( ref $string ) {
148             # End of file
149 585 100       1117 if ( length($$string) > 1 ) {
150             # Complete the properties for the first section
151 443         867 my $str = $$string;
152 443         768 chop $str;
153             $self->{sections}->[0] = {
154 443         2700 position => length($self->{content}),
155             size => length($$string) - 1,
156             type => "$self->{separator}$self->{separator}",
157             };
158 443         850 $self->{_sections} = 1;
159             } else {
160             # No sections at all
161 142         301 $self->{sections} = [ ];
162 142         198 $self->{_sections} = 0;
163             }
164 585         1027 $self->{content} .= $$string;
165 585         1125 return 0;
166             }
167              
168             # Complete the properties of the first section
169             $self->{sections}->[0] = {
170             position => length $self->{content},
171 1494         9226 size => length($string) - 1,
172             type => "$self->{separator}$self->{separator}",
173             };
174 1494         2759 $self->{content} .= $string;
175              
176             # We are done if there is only one section
177 1494 100       3992 return 1 if $self->{_sections} == 1;
178              
179             # There are two sections.
180              
181             # Advance into the next section
182 380         619 $t->{line_cursor}++;
183              
184             # Get the content up to the end separator
185 380         1045 $string = $self->_scan_for_unescaped_character( $t, $self->{separator} );
186 380 50       797 return undef unless defined $string;
187 380 100       935 if ( ref $string ) {
188             # End of file
189 76 100       256 if ( length($$string) > 1 ) {
190             # Complete the properties for the second section
191 74         148 my $str = $$string;
192 74         161 chop $str;
193             $self->{sections}->[1] = {
194 74         342 position => length($self->{content}),
195             size => length($$string) - 1,
196             type => "$self->{separator}$self->{separator}",
197             };
198             } else {
199             # No sections at all
200 2         5 $self->{_sections} = 1;
201             }
202 76         178 $self->{content} .= $$string;
203 76         189 return 0;
204             }
205              
206             # Complete the properties of the second section
207             $self->{sections}->[1] = {
208 304         1157 position => length($self->{content}),
209             size => length($string) - 1
210             };
211 304         1239 $self->{content} .= $string;
212              
213 304         531 1;
214             }
215              
216             # Handle content parsing for matching brace separated
217             sub _fill_braced {
218 1566     1566   2229 my $self = shift;
219 1566         1909 my $t = shift;
220              
221             # Get the content up to the close character
222 1566         2598 my $section = $self->{sections}->[0];
223 1566         5092 my $brace_str = $self->_scan_for_brace_character( $t, $section->{_close} );
224 1566 50       3375 return undef unless defined $brace_str;
225 1566 100       2908 if ( ref $brace_str ) {
226             # End of file
227 150 100       452 if ( length($$brace_str) > 1 ) {
228             # Complete the properties for the first section
229 95         202 my $str = $$brace_str;
230 95         235 chop $str;
231             $self->{sections}->[0] = {
232             position => length($self->{content}),
233             size => length($$brace_str) - 1,
234             type => $section->{type},
235 95         514 };
236 95         199 $self->{_sections} = 1;
237             } else {
238             # No sections at all
239 55         113 $self->{sections} = [ ];
240 55         101 $self->{_sections} = 0;
241             }
242 150         281 $self->{content} .= $$brace_str;
243 150         423 return 0;
244             }
245              
246             # Complete the properties of the first section
247 1416         3232 $section->{position} = length $self->{content};
248 1416         2820 $section->{size} = length($brace_str) - 1;
249 1416         2411 $self->{content} .= $brace_str;
250 1416         2420 delete $section->{_close};
251              
252             # We are done if there is only one section
253 1416 100       3658 return 1 if $self->{_sections} == 1;
254              
255             # There are two sections.
256              
257             # Is there a gap between the sections.
258 166         339 my $char = substr( $t->{line}, ++$t->{line_cursor}, 1 );
259 166 100       516 if ( $char =~ /\s/ ) {
260             # Go past the gap
261 116         224 my $gap_str = $self->_scan_quote_like_operator_gap( $t );
262 116 50       194 return undef unless defined $gap_str;
263 116 100       210 if ( ref $gap_str ) {
264             # End of file
265 2         3 $self->{content} .= $$gap_str;
266 2         4 return 0;
267             }
268 114         202 $self->{content} .= $gap_str;
269 114         250 $char = substr( $t->{line}, $t->{line_cursor}, 1 );
270             }
271              
272 164         368 $section = $SECTIONS{$char};
273              
274 164 100       431 if ( $section ) {
    100          
275             # It's a brace
276              
277             # Initialize the second section
278 119         190 $self->{content} .= $char;
279 119         358 $section = { %$section };
280              
281             # Advance into the second section
282 119         211 $t->{line_cursor}++;
283              
284             # Get the content up to the close character
285 119         300 $brace_str = $self->_scan_for_brace_character( $t, $section->{_close} );
286 119 50       218 return undef unless defined $brace_str;
287 119 100       253 if ( ref $brace_str ) {
288             # End of file
289 6 100       14 if ( length($$brace_str) > 1 ) {
290             # Complete the properties for the second section
291 4         7 my $str = $$brace_str;
292 4         8 chop $str;
293             $self->{sections}->[1] = {
294             position => length($self->{content}),
295             size => length($$brace_str) - 1,
296             type => $section->{type},
297 4         16 };
298 4         5 $self->{_sections} = 2;
299             } else {
300             # No sections at all
301 2         4 $self->{_sections} = 1;
302             }
303 6         9 $self->{content} .= $$brace_str;
304 6         16 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         456 };
312 113         207 $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 23         41 $self->{content} .= $char;
319              
320             # Advance into the next section
321 23         29 $t->{line_cursor}++;
322              
323             # Get the content up to the end separator
324 23         90 my $string = $self->_scan_for_unescaped_character( $t, $char );
325 23 50       68 return undef unless defined $string;
326 23 100       52 if ( ref $string ) {
327             # End of file
328 11 100       43 if ( length($$string) > 1 ) {
329             # Complete the properties for the second section
330 3         9 my $str = $$string;
331 3         7 chop $str;
332             $self->{sections}->[1] = {
333 3         17 position => length($self->{content}),
334             size => length($$string) - 1,
335             type => "$char$char",
336             };
337             } else {
338             # Only the one section
339 8         15 $self->{_sections} = 1;
340             }
341 11         18 $self->{content} .= $$string;
342 11         24 return 0;
343             }
344              
345             # Complete the properties of the second section
346             $self->{sections}->[1] = {
347 12         55 position => length($self->{content}),
348             size => length($string) - 1,
349             type => "$char$char",
350             };
351 12         26 $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 22         107 position => length($self->{content}),
362             size => 0,
363             type => '',
364             };
365              
366             # Attach an error to the token and move on
367 22         50 $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 22         36 $t->{line_cursor}--;
371 22         45 return 0;
372             }
373              
374 125         291 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   4132 wantarray ? @{$_[0]->{sections}} : scalar @{$_[0]->{sections}}
  68         223  
  10         43  
388             }
389              
390             # Get a section's content
391             sub _section_content {
392 506     506   983 my $self = shift;
393 506         813 my $i = shift;
394 506 50       1674 $self->{sections} or return;
395 506 100       1197 my $section = $self->{sections}->[$i] or return;
396 502         1665 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   13 my $self = shift;
405 7 50       23 $self->{modifiers} or return;
406 7 100       16 wantarray and return %{ $self->{modifiers} };
  5         30  
407 2         3 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   740 my $self = shift;
413 494 50       1333 $self->{sections} or return;
414 494         674 my @delims;
415 494         872 foreach my $sect ( @{ $self->{sections} } ) {
  494         1261  
416 496 100       1138 if ( exists $sect->{type} ) {
417 495         1219 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         4 substr( $content, $sect->{position} + $sect->{size}, 1 );
423             }
424             }
425 494         1834 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