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   338 use strict;
  69         112  
  69         1843  
6 69     69   230 use Clone ();
  69         84  
  69         685  
7 69     69   227 use Carp ();
  69         102  
  69         793  
8 69     69   201 use PPI::Token::_QuoteEngine ();
  69         105  
  69         129347  
9              
10             our $VERSION = '1.290';
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 3778     3778 0 5538 my $class = shift;
54 3778 50       7513 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 3778 50       8460 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 3778 50       10297 my $options = $QUOTES{$init} or return $self->_error(
66             "Unknown quote type '$init'"
67             );
68 3778         13332 foreach ( keys %$options ) {
69 16971         28236 $self->{$_} = $options->{$_};
70             }
71              
72             # Set up the modifiers hash if needed
73 3778 100       10116 $self->{modifiers} = {} if $self->{modifiers};
74              
75             # Handle the special < base
76 3778 100       7946 $self->{sections}[0] = Clone::clone $SECTIONS{'<'} if $init eq '<';
77 3778 100       10001 $self->{sections}[0] = Clone::clone $SECTIONS{'('} if $init eq '(';
78              
79 3778         9390 $self;
80             }
81              
82             sub _fill {
83 3778     3778   4630 my $class = shift;
84 3778         4290 my $t = shift;
85             my $self = $t->{token}
86 3778 50       8964 or Carp::croak("::Full->_fill called without current token");
87              
88             # Load in the operator stuff if needed
89 3778 100       7895 if ( $self->{operator} ) {
90             # In an operator based quote-like, handle the gap between the
91             # operator and the opening separator.
92 2654 100       9396 if ( substr( $t->{line}, $t->{line_cursor}, 1 ) =~ /\s/ ) {
93             # Go past the gap
94 564         1691 my $gap = $self->_scan_quote_like_operator_gap( $t );
95 564 50       1176 return undef unless defined $gap;
96 564 100       1193 if ( ref $gap ) {
97             # End of file
98 191         361 $self->{content} .= $$gap;
99 191         421 return 0;
100             }
101 373         1962 $self->{content} .= $gap;
102             }
103              
104             # The character we are now on is the separator. Capture,
105             # and advance into the first section.
106 2463         5151 my $sep = substr( $t->{line}, $t->{line_cursor}++, 1 );
107 2463         10912 $self->{content} .= $sep;
108              
109             # Determine if these are normal or braced type sections
110 2463 100       5164 if ( my $section = $SECTIONS{$sep} ) {
111 1123         1974 $self->{braced} = 1;
112 1123         11916 $self->{sections}->[0] = Clone::clone($section);
113             } else {
114 1340         1995 $self->{braced} = 0;
115 1340         2511 $self->{separator} = $sep;
116             }
117             }
118              
119             # Parse different based on whether we are normal or braced
120             my $rv = $self->{braced}
121 3587 100       12529 ? $self->_fill_braced($t)
122             : $self->_fill_normal($t);
123 3587 100       7037 return $rv if !$rv;
124              
125             # Return now unless it has modifiers ( i.e. s/foo//eieio )
126 2758 100       6756 return 1 unless $self->{modifiers};
127              
128             # Check for modifiers
129 1189         1582 my $char;
130 1189         1480 my $len = 0;
131 1189         6917 while ( ($char = substr( $t->{line}, $t->{line_cursor} + 1, 1 )) =~ /[^\W\d_]/ ) {
132 458         533 $len++;
133 458         687 $self->{content} .= $char;
134 458         1279 $self->{modifiers}->{lc $char} = 1;
135 458         1967 $t->{line_cursor}++;
136             }
137             }
138              
139             # Handle the content parsing path for normally separated
140             sub _fill_normal {
141 2000     2000   2818 my $self = shift;
142 2000         2355 my $t = shift;
143              
144             # Get the content up to the next separator
145 2000         5409 my $string = $self->_scan_for_unescaped_character( $t, $self->{separator} );
146 2000 50       4148 return undef unless defined $string;
147 2000 100       3858 if ( ref $string ) {
148             # End of file
149 544 100       1056 if ( length($$string) > 1 ) {
150             # Complete the properties for the first section
151 399         752 my $str = $$string;
152 399         755 chop $str;
153             $self->{sections}->[0] = {
154 399         2490 position => length($self->{content}),
155             size => length($$string) - 1,
156             type => "$self->{separator}$self->{separator}",
157             };
158 399         753 $self->{_sections} = 1;
159             } else {
160             # No sections at all
161 145         302 $self->{sections} = [ ];
162 145         195 $self->{_sections} = 0;
163             }
164 544         946 $self->{content} .= $$string;
165 544         875 return 0;
166             }
167              
168             # Complete the properties of the first section
169             $self->{sections}->[0] = {
170             position => length $self->{content},
171 1456         9511 size => length($string) - 1,
172             type => "$self->{separator}$self->{separator}",
173             };
174 1456         2704 $self->{content} .= $string;
175              
176             # We are done if there is only one section
177 1456 100       3893 return 1 if $self->{_sections} == 1;
178              
179             # There are two sections.
180              
181             # Advance into the next section
182 395         636 $t->{line_cursor}++;
183              
184             # Get the content up to the end separator
185 395         1130 $string = $self->_scan_for_unescaped_character( $t, $self->{separator} );
186 395 50       863 return undef unless defined $string;
187 395 100       864 if ( ref $string ) {
188             # End of file
189 79 100       265 if ( length($$string) > 1 ) {
190             # Complete the properties for the second section
191 75         142 my $str = $$string;
192 75         171 chop $str;
193             $self->{sections}->[1] = {
194 75         425 position => length($self->{content}),
195             size => length($$string) - 1,
196             type => "$self->{separator}$self->{separator}",
197             };
198             } else {
199             # No sections at all
200 4         7 $self->{_sections} = 1;
201             }
202 79         214 $self->{content} .= $$string;
203 79         140 return 0;
204             }
205              
206             # Complete the properties of the second section
207             $self->{sections}->[1] = {
208 316         1084 position => length($self->{content}),
209             size => length($string) - 1
210             };
211 316         556 $self->{content} .= $string;
212              
213 316         525 1;
214             }
215              
216             # Handle content parsing for matching brace separated
217             sub _fill_braced {
218 1587     1587   2024 my $self = shift;
219 1587         1990 my $t = shift;
220              
221             # Get the content up to the close character
222 1587         2687 my $section = $self->{sections}->[0];
223 1587         4802 my $brace_str = $self->_scan_for_brace_character( $t, $section->{_close} );
224 1587 50       3097 return undef unless defined $brace_str;
225 1587 100       3115 if ( ref $brace_str ) {
226             # End of file
227 165 100       360 if ( length($$brace_str) > 1 ) {
228             # Complete the properties for the first section
229 110         199 my $str = $$brace_str;
230 110         237 chop $str;
231             $self->{sections}->[0] = {
232             position => length($self->{content}),
233             size => length($$brace_str) - 1,
234             type => $section->{type},
235 110         609 };
236 110         232 $self->{_sections} = 1;
237             } else {
238             # No sections at all
239 55         104 $self->{sections} = [ ];
240 55         91 $self->{_sections} = 0;
241             }
242 165         302 $self->{content} .= $$brace_str;
243 165         537 return 0;
244             }
245              
246             # Complete the properties of the first section
247 1422         3288 $section->{position} = length $self->{content};
248 1422         2867 $section->{size} = length($brace_str) - 1;
249 1422         2331 $self->{content} .= $brace_str;
250 1422         2301 delete $section->{_close};
251              
252             # We are done if there is only one section
253 1422 100       3709 return 1 if $self->{_sections} == 1;
254              
255             # There are two sections.
256              
257             # Is there a gap between the sections.
258 161         442 my $char = substr( $t->{line}, ++$t->{line_cursor}, 1 );
259 161 100       564 if ( $char =~ /\s/ ) {
260             # Go past the gap
261 118         265 my $gap_str = $self->_scan_quote_like_operator_gap( $t );
262 118 50       261 return undef unless defined $gap_str;
263 118 100       249 if ( ref $gap_str ) {
264             # End of file
265 3         7 $self->{content} .= $$gap_str;
266 3         7 return 0;
267             }
268 115         211 $self->{content} .= $gap_str;
269 115         305 $char = substr( $t->{line}, $t->{line_cursor}, 1 );
270             }
271              
272 158         307 $section = $SECTIONS{$char};
273              
274 158 100       415 if ( $section ) {
    100          
275             # It's a brace
276              
277             # Initialize the second section
278 120         217 $self->{content} .= $char;
279 120         392 $section = { %$section };
280              
281             # Advance into the second section
282 120         245 $t->{line_cursor}++;
283              
284             # Get the content up to the close character
285 120         300 $brace_str = $self->_scan_for_brace_character( $t, $section->{_close} );
286 120 50       270 return undef unless defined $brace_str;
287 120 100       229 if ( ref $brace_str ) {
288             # End of file
289 7 100       17 if ( length($$brace_str) > 1 ) {
290             # Complete the properties for the second section
291 5         10 my $str = $$brace_str;
292 5         10 chop $str;
293             $self->{sections}->[1] = {
294             position => length($self->{content}),
295             size => length($$brace_str) - 1,
296             type => $section->{type},
297 5         19 };
298 5         9 $self->{_sections} = 2;
299             } else {
300             # No sections at all
301 2         4 $self->{_sections} = 1;
302             }
303 7         12 $self->{content} .= $$brace_str;
304 7         21 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         531 };
312 113         241 $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 21         38 $self->{content} .= $char;
319              
320             # Advance into the next section
321 21         39 $t->{line_cursor}++;
322              
323             # Get the content up to the end separator
324 21         60 my $string = $self->_scan_for_unescaped_character( $t, $char );
325 21 50       49 return undef unless defined $string;
326 21 100       45 if ( ref $string ) {
327             # End of file
328 14 100       40 if ( length($$string) > 1 ) {
329             # Complete the properties for the second section
330 6         16 my $str = $$string;
331 6         13 chop $str;
332             $self->{sections}->[1] = {
333 6         35 position => length($self->{content}),
334             size => length($$string) - 1,
335             type => "$char$char",
336             };
337             } else {
338             # Only the one section
339 8         13 $self->{_sections} = 1;
340             }
341 14         29 $self->{content} .= $$string;
342 14         35 return 0;
343             }
344              
345             # Complete the properties of the second section
346             $self->{sections}->[1] = {
347 7         34 position => length($self->{content}),
348             size => length($string) - 1,
349             type => "$char$char",
350             };
351 7         17 $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 17         64 position => length($self->{content}),
362             size => 0,
363             type => '',
364             };
365              
366             # Attach an error to the token and move on
367 17         42 $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 17         20 $t->{line_cursor}--;
371 17         32 return 0;
372             }
373              
374 120         301 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   4926 wantarray ? @{$_[0]->{sections}} : scalar @{$_[0]->{sections}}
  68         221  
  10         77  
388             }
389              
390             # Get a section's content
391             sub _section_content {
392 506     506   804 my $self = shift;
393 506         826 my $i = shift;
394 506 50       1550 $self->{sections} or return;
395 506 100       1193 my $section = $self->{sections}->[$i] or return;
396 502         1369 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       21 $self->{modifiers} or return;
406 7 100       20 wantarray and return %{ $self->{modifiers} };
  5         30  
407 2         4 return +{ %{ $self->{modifiers} } };
  2         14  
408             }
409              
410             # Get the delimiters, or at least give it a good try to get them.
411             sub _delimiters {
412 494     494   608 my $self = shift;
413 494 50       1175 $self->{sections} or return;
414 494         658 my @delims;
415 494         603 foreach my $sect ( @{ $self->{sections} } ) {
  494         1067  
416 496 100       1012 if ( exists $sect->{type} ) {
417 495         1121 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         8 substr( $content, $sect->{position} + $sect->{size}, 1 );
423             }
424             }
425 494         1444 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