File Coverage

blib/lib/PPIx/QuoteLike.pm
Criterion Covered Total %
statement 390 429 90.9
branch 152 222 68.4
condition 43 67 64.1
subroutine 63 68 92.6
pod 24 24 100.0
total 672 810 82.9


line stmt bran cond sub pod time code
1             package PPIx::QuoteLike;
2              
3 7     7   208990 use 5.006;
  7         41  
4              
5 7     7   29 use strict;
  7         14  
  7         129  
6 7     7   26 use warnings;
  7         13  
  7         187  
7              
8 7     7   42 use Carp;
  7         11  
  7         388  
9 7     7   3498 use Encode ();
  7         64689  
  7         153  
10 7     7   42 use List::Util ();
  7         12  
  7         160  
11 7         908 use PPIx::QuoteLike::Constant qw{
12             ARRAY_REF
13             LOCATION_LINE
14             LOCATION_CHARACTER
15             LOCATION_COLUMN
16             LOCATION_LOGICAL_LINE
17             LOCATION_LOGICAL_FILE
18             MINIMUM_PERL
19             VARIABLE_RE
20             @CARP_NOT
21 7     7   1380 };
  7         14  
22 7     7   2096 use PPIx::QuoteLike::Token::Control;
  7         15  
  7         184  
23 7     7   2104 use PPIx::QuoteLike::Token::Delimiter;
  7         14  
  7         233  
24 7     7   2006 use PPIx::QuoteLike::Token::Interpolation;
  7         17  
  7         174  
25 7     7   2062 use PPIx::QuoteLike::Token::String;
  7         14  
  7         160  
26 7     7   36 use PPIx::QuoteLike::Token::Structure;
  7         12  
  7         138  
27 7     7   2030 use PPIx::QuoteLike::Token::Unknown;
  7         15  
  7         155  
28 7     7   1918 use PPIx::QuoteLike::Token::Whitespace;
  7         13  
  7         226  
29 7         417 use PPIx::QuoteLike::Utils qw{
30             column_number
31             line_number
32             logical_filename
33             logical_line_number
34             statement
35             visual_column_number
36             __instance
37             __match_enclosed
38             __matching_delimiter
39 7     7   41 };
  7         16  
40 7     7   33 use Scalar::Util ();
  7         17  
  7         191  
41              
42             our $VERSION = '0.023';
43              
44 7     7   31 use constant CLASS_CONTROL => 'PPIx::QuoteLike::Token::Control';
  7         10  
  7         337  
45 7     7   32 use constant CLASS_DELIMITER => 'PPIx::QuoteLike::Token::Delimiter';
  7         18  
  7         293  
46 7     7   32 use constant CLASS_INTERPOLATION => 'PPIx::QuoteLike::Token::Interpolation';
  7         11  
  7         274  
47 7     7   33 use constant CLASS_STRING => 'PPIx::QuoteLike::Token::String';
  7         13  
  7         249  
48 7     7   33 use constant CLASS_STRUCTURE => 'PPIx::QuoteLike::Token::Structure';
  7         10  
  7         258  
49 7     7   34 use constant CLASS_UNKNOWN => 'PPIx::QuoteLike::Token::Unknown';
  7         12  
  7         290  
50 7     7   35 use constant CLASS_WHITESPACE => 'PPIx::QuoteLike::Token::Whitespace';
  7         10  
  7         323  
51              
52 7     7   32 use constant CODE_REF => ref sub {};
  7         19  
  7         303  
53              
54 7         263 use constant ILLEGAL_FIRST =>
55 7     7   33 'Tokenizer found illegal first characters';
  7         36  
56 7         249 use constant MISMATCHED_DELIM =>
57 7     7   37 'Tokenizer found mismatched delimiters';
  7         9  
58 7         24973 use constant NO_INDENTATION =>
59 7     7   39 'No indentation string found';
  7         11  
60              
61             {
62             my $match_sq = __match_enclosed( qw< ' > );
63             my $match_dq = __match_enclosed( qw< " > );
64             my $match_bt = __match_enclosed( qw< ` > );
65              
66             sub new { ## no critic (RequireArgUnpacking)
67 114     114 1 26115 my ( $class, $source, %arg ) = @_;
68              
69 114         151 my @children;
70              
71 114 100       252 if ( $arg{location} ) {
72             ARRAY_REF eq ref $arg{location}
73 2 50       16 or croak q;
74 2         13 foreach my $inx ( 0 .. 3 ) {
75 8 50       27 $arg{location}[$inx] =~ m/ [^0-9] /smx
76             and croak "Argument 'location' element $inx must be an unsigned integer";
77             }
78             }
79              
80 114 50       210 if ( ! defined $arg{index_locations} ) {
81             $arg{index_locations} = !! $arg{location} ||
82 114   100     346 __instance( $source, 'PPI::Element' );
83             }
84              
85             my $self = {
86             index_locations => $arg{index_locations},
87             children => \@children,
88             encoding => $arg{encoding},
89             failures => 0,
90             location => $arg{location},
91 114         409 source => $source,
92             };
93              
94 114   33     325 bless $self, ref $class || $class;
95              
96 114 100       220 defined( my $string = $self->_stringify_source( $source ) )
97             or return;
98              
99 62         111 my ( $type, $gap, $gap2, $content, $end_delim, $indented, $start_delim );
100              
101             $arg{trace}
102 62 50       165 and warn "Initial match of $string\n";
103              
104             # q<>, qq<>, qx<>
105 62 100       964 if ( $string =~ m/ \A \s* ( q [qx]? ) ( \s* ) ( . ) /smxgc ) {
    100          
    50          
106 9         33 ( $type, $gap, $start_delim ) = ( $1, $2, $3 );
107 9 50 66     42 not $gap
108             and $start_delim =~ m< \A \w \z >smx
109             and return $self->_link_elems( $self->_make_token(
110             CLASS_UNKNOWN, $string, error => ILLEGAL_FIRST ) );
111             $arg{trace}
112 9 50       25 and warn "Initial match '$type$start_delim'\n";
113 9   100     41 $self->{interpolates} = 'qq' eq $type ||
114             'qx' eq $type && q<'> ne $start_delim;
115 9   50     24 $content = substr $string, ( pos $string || 0 );
116 9         23 $end_delim = __matching_delimiter( $start_delim );
117 9 50       23 if ( $end_delim eq substr $content, -1 ) {
118 9         18 chop $content;
119             } else {
120 0         0 $end_delim = '';
121             }
122              
123             # here doc
124             # Note that the regexp used here is slightly wrong in that white
125             # space between the '<<' and the termination string is not
126             # allowed if the termination string is not quoted in some way.
127             } elsif ( $string =~ m/ \A \s* ( << ) ( \s* ) ( ~? ) ( \s* )
128             ( [\\]? \w+ | $match_sq | $match_dq | $match_bt ) \n /smxgc ) {
129 7         39 ( $type, $gap, $indented, $gap2, $start_delim ) = (
130             $1, $2, $3, $4, $5 );
131             $arg{trace}
132 7 50       24 and warn "Initial match '$type$start_delim$gap$indented'\n";
133 7         112 $self->{interpolates} = $start_delim !~ m/ \A [\\'] /smx;
134 7   50     36 $content = substr $string, ( pos $string || 0 );
135 7         20 $end_delim = _unquote( $start_delim );
136             # NOTE that the indentation is specifically space or tab
137             # only.
138 7 50       137 if ( $content =~ s/ ^ ( [ \t]* ) \Q$end_delim\E \n? \z //smx ) {
139             # NOTE PPI::Token::HereDoc does not preserve the
140             # indentation of an indented here document, so the
141             # indentation will appear to be '' if we came from PPI.
142 7 100       19 if ( $indented ) {
143             # Version per perldelta.pod for that release.
144 4         13 $self->{perl_version_introduced} = '5.025007';
145 4         13 $self->{indentation} = "$1";
146 4         42 $self->{_indentation_re} = qr/
147             ^ \Q$self->{indentation}\E /smx;
148             }
149             } else {
150 0         0 $end_delim = '';
151             }
152             $self->{start} = [
153 7         23 $self->_make_token( CLASS_DELIMITER, $start_delim ),
154             $self->_make_token( CLASS_WHITESPACE, "\n" ),
155             ];
156              
157             # Don't instantiate yet -- we'll do them at the end.
158             $self->{finish} = [
159 7         28 [ CLASS_DELIMITER, $end_delim ],
160             [ CLASS_WHITESPACE, "\n" ],
161             ];
162              
163             # ``, '', "", <>
164             } elsif ( $string =~ m/ \A \s* ( [`'"<] ) /smxgc ) {
165 46         150 ( $type, $gap, $start_delim ) = ( '', '', $1 );
166             $arg{trace}
167 46 50       99 and warn "Initial match '$type$start_delim'\n";
168 46         122 $self->{interpolates} = q<'> ne $start_delim;
169 46   50     124 $content = substr $string, ( pos $string || 0 );
170 46         125 $end_delim = __matching_delimiter( $start_delim );
171 46 50       106 if ( $end_delim eq substr $content, -1 ) {
172 46         83 chop $content;
173             } else {
174 0         0 $end_delim = '';
175             }
176              
177             # Something we do not recognize
178             } else {
179             $arg{trace}
180 0 0       0 and warn "No initial match\n";
181 0         0 return $self->_link_elems( $self->_make_token(
182             CLASS_UNKNOWN, $string, error => ILLEGAL_FIRST ) );
183             }
184              
185 62 100       131 $self->{interpolates} = $self->{interpolates} ? 1 : 0;
186              
187 62   100     218 defined or $_ = '' for $indented, $gap2;
188             $self->{type} = [
189 62 100       128 $self->_make_token( CLASS_STRUCTURE, $type ),
    100          
    50          
190             length $gap ?
191             $self->_make_token( CLASS_WHITESPACE, $gap ) :
192             (),
193             length $indented ?
194             $self->_make_token( CLASS_STRUCTURE, $indented ) :
195             (),
196             length $gap2 ?
197             $self->_make_token( CLASS_WHITESPACE, $gap2 ) :
198             (),
199             ];
200             $self->{start} ||= [
201 62   100     241 $self->_make_token( CLASS_DELIMITER, $start_delim ),
202             ];
203              
204             $arg{trace}
205 62 50       125 and warn "Without delimiters: '$content'\n";
206              
207             # We accumulate data and manufacure tokens at the end to reduce
208             # the overhead involved in merging strings.
209 62 100       126 if ( $self->{interpolates} ) {
210 49         112 push @children, [ '' => '' ]; # Prime the pump
211 49         68 while ( 1 ) {
212              
213 148 100       597 if ( $content =~ m/ \G ( \\ [ULulQEF] ) /smxgc ) {
    100          
    100          
    100          
214 4         22 push @children, [ CLASS_CONTROL, "$1" ];
215             } elsif (
216             $content =~ m/ \G ( \\ N [{] ( [^}]+ ) [}] ) /smxgc
217             ) {
218             # Handle \N{...} separately because it can not
219             # contain an interpolation even inside of an
220             # otherwise-interpolating string. That is to say,
221             # "\N{$foo}" is simply invalid, and does not even
222             # try to interpolate $foo. {
223             # TODO use $re = __match_enclosed( '{' ); # }
224 1         4 my ( $seq, $name ) = ( $1, $2 );
225             # TODO The Regexp is certainly too permissive. For
226             # the moment all I am doing is disallowing
227             # interpolation.
228 1 50       7 push @children, $name =~ m/ [\$\@] /smx ?
229             [ CLASS_UNKNOWN, $seq,
230             error => "Unknown charname '$name'" ] :
231             [ CLASS_STRING, $seq ];
232             # NOTE in the following that I do not read perldata as
233             # saying there can be space between the sigil and the
234             # variable name, but Perl itself seems to accept it as
235             # of 5.30.1.
236             } elsif ( $content =~ m/ \G ( [\$\@] \#? \$* ) /smxgc ) {
237 51         171 push @children, $self->_interpolation( "$1", $content );
238             } elsif ( $content =~ m/ \G ( \\ . | [^\\\$\@]+ ) /smxgc ) {
239 43         145 push @children, $self->_remove_here_doc_indentation(
240             "$1",
241             sibling => \@children,
242             );
243             } else {
244 49         68 last;
245             }
246             }
247              
248 49         104 @children = _merge_strings( @children );
249 49         71 shift @children; # remove the priming
250              
251             # Make the tokens, at long last.
252 49         95 foreach ( @children ) {
253 96         107 $_ = $self->_make_token( @{ $_ } );
  96         178  
254             }
255              
256             } else {
257              
258             length $content
259 13 100       92 and push @children, map { $self->_make_token( @{ $_ } ) }
  15         24  
  15         31  
260             _merge_strings(
261             $self->_remove_here_doc_indentation( $content )
262             );
263              
264             }
265              
266             # Add the indentation before the end marker, if needed
267             $self->{indentation}
268             and push @children, $self->_make_token(
269 62 100       138 CLASS_WHITESPACE, $self->{indentation} );
270              
271 62 100       133 if ( $self->{finish} ) {
272             # If we already have something here it is data, not objects.
273 7         12 foreach ( @{ $self->{finish} } ) {
  7         18  
274 14         25 $_ = $self->_make_token( @{ $_ } );
  14         24  
275             }
276             } else {
277             $self->{finish} = [
278 55         93 $self->_make_token( CLASS_DELIMITER, $end_delim ),
279             ];
280             }
281              
282 62 100       492 ref $_[1]
283             and pos( $_[1] ) = pos $string;
284              
285 62         163 return $self->_link_elems();
286             }
287             }
288              
289             sub child {
290 46     46 1 8433 my ( $self, $number ) = @_;
291 46         125 return $self->{children}[$number];
292             }
293              
294             sub children {
295 116     116 1 175 my ( $self ) = @_;
296 116         134 return @{ $self->{children} };
  116         298  
297             }
298              
299             sub content {
300 27     27 1 51 my ( $self ) = @_;
301 27         57 return join '', map { $_->content() } grep { $_ } $self->elements();
  142         272  
  142         179  
302             }
303              
304             sub delimiters {
305 27     27 1 53 my ( $self ) = @_;
306 54         209 return join '', grep { defined }
307 27         50 map { $self->_get_value_scalar( $_ ) }
  54         90  
308             qw{ start finish };
309             }
310              
311             # $self->_deprecation_notice( $type, $name );
312             #
313             # This method centralizes deprecation. Type is 'attribute' or
314             # 'method'. Deprecation is driven of the %deprecate hash. Values
315             # are:
316             # false - no warning
317             # 1 - warn on first use
318             # 2 - warn on each use
319             # 3 - die on each use.
320             #
321             # $self->_deprecation_in_progress( $type, $name )
322             #
323             # This method returns true if the deprecation is in progress. In
324             # fact it returns the deprecation level.
325              
326             =begin comment
327              
328             # Abandoned in place, against future need.
329              
330             {
331              
332             my %deprecate = (
333             attribute => {
334             postderef => 3,
335             },
336             );
337              
338             sub _deprecation_notice {
339             my ( undef, $type, $name, $repl ) = @_; # Invocant unused
340             $deprecate{$type} or return;
341             $deprecate{$type}{$name} or return;
342             my $msg = sprintf 'The %s %s is %s', $name, $type,
343             $deprecate{$type}{$name} > 2 ? 'removed' : 'deprecated';
344             defined $repl
345             and $msg .= "; use $repl instead";
346             $deprecate{$type}{$name} >= 3
347             and croak $msg;
348             warnings::enabled( 'deprecated' )
349             and carp $msg;
350             $deprecate{$type}{$name} == 1
351             and $deprecate{$type}{$name} = 0;
352             return;
353             }
354              
355             }
356              
357             =end comment
358              
359             =cut
360              
361             sub _get_value_scalar {
362 54     54   82 my ( $self, $method ) = @_;
363 54 50       102 defined( my $val = $self->$method() )
364             or return;
365 54 50       150 return ref $val ? $val->content() : $val;
366             }
367              
368             sub elements {
369 84     84 1 2385 my ( $self ) = @_;
370 84         95 return @{ $self->{elements} ||= [
371 84   100     369 map { $self->$_() } qw{ type start children finish }
  192         358  
372             ] };
373             }
374              
375             sub encoding {
376 34     34 1 64 my ( $self ) = @_;
377 34         113 return $self->{encoding};
378             }
379              
380             sub failures {
381 36     36 1 8354 my ( $self ) = @_;
382 36         142 return $self->{failures};
383             }
384              
385             sub find {
386 1     1 1 584 my ( $self, $target ) = @_;
387              
388             my $check = CODE_REF eq ref $target ? $target :
389             ref $target ? croak 'find() target may not be ' . ref $target :
390 1 50   5   8 sub { $_[0]->isa( $target ) };
  5 50       22  
391 1         2 my @found;
392 1         3 foreach my $elem ( $self, $self->elements() ) {
393 5 100       8 $check->( $elem )
394             and push @found, $elem;
395             }
396              
397             @found
398 1 50       4 or return 0;
399              
400 1         4 return \@found;
401             }
402              
403             sub finish {
404 109     109 1 161 my ( $self, $inx ) = @_;
405             $self->{finish}
406 109 50       203 or return;
407             wantarray
408 109 100       182 and return @{ $self->{finish} };
  55         221  
409 54   50     178 return $self->{finish}[ $inx || 0 ];
410             }
411              
412             sub handles {
413 0     0 1 0 my ( $self, $string ) = @_;
414 0         0 return $self->_stringify_source( $string, test => 1 );
415             }
416              
417             sub indentation {
418 10     10 1 19 my ( $self ) = @_;
419 10         31 return $self->{indentation};
420             }
421              
422             sub interpolates {
423 84     84 1 127 my ( $self ) = @_;
424 84         283 return $self->{interpolates};
425             }
426              
427             sub location {
428 6     6 1 8 my ( $self ) = @_;
429 6         12 return $self->type()->location();
430             }
431              
432             sub _make_token {
433 326     326   561 my ( $self, $class, $content, %arg ) = @_;
434 326         1003 my $token = $class->__new( content => $content, %arg );
435             CLASS_UNKNOWN eq $class
436 326 100       574 and $self->{failures}++;
437             $self->{index_locations}
438 326 100       621 and $self->_update_location( $token );
439 326         903 return $token;
440             }
441              
442             sub _update_location {
443 100     100   131 my ( $self, $token ) = @_;
444             $token->{location} # Idempotent
445 100 50       194 and return;
446 100   66     214 my $loc = $self->{_location} ||= do {
447             my %loc = (
448             line_content => '',
449             location => $self->{location},
450 19         65 );
451 19 100       56 if ( __instance( $self->{source}, 'PPI::Element' ) ) {
452 17   33     117 $loc{location} ||= $self->{source}->location();
453 17 50       3124 if ( my $doc = $self->{source}->document() ) {
454 17         288 $loc{tab_width} = $doc->tab_width();
455             }
456             }
457 19   100     107 $loc{tab_width} ||= 1;
458 19         50 \%loc;
459             };
460             $loc->{location}
461 100 50       153 or return;
462 100         106 $token->{location} = [ @{ $loc->{location} } ];
  100         210  
463              
464 100 50       245 if ( defined( my $content = $token->content() ) ) {
465              
466 100         111 my $lines;
467 100         184 pos( $content ) = 0;
468 100         246 $lines++ while $content =~ m/ \n /smxgc;
469 100 100       154 if ( pos $content ) {
470 4         5 $loc->{location}[LOCATION_LINE] += $lines;
471 4         5 $loc->{location}[LOCATION_LOGICAL_LINE] += $lines;
472             $loc->{location}[LOCATION_CHARACTER] =
473 4         14 $loc->{location}[LOCATION_COLUMN] = 1;
474             }
475              
476 100 100       193 if ( my $chars = length( $content ) - pos( $content ) ) {
477 83         121 $loc->{location}[LOCATION_CHARACTER] += $chars;
478 83 100 100     177 if ( $loc->{tab_width} > 1 && $content =~ m/ \t /smx ) {
479 4         6 my $pos = $loc->{location}[LOCATION_COLUMN];
480 4         6 my $tab_width = $loc->{tab_width};
481             # Stolen shamelessly from PPI::Document::_visual_length
482 4         3 my ( $vis_inc );
483 4         14 foreach my $part ( split /(\t)/, $content ) {
484 10 100       14 if ($part eq "\t") {
485 5         7 $vis_inc = $tab_width - ($pos-1) % $tab_width;
486             } else {
487 5         5 $vis_inc = length $part;
488             }
489 10         14 $pos += $vis_inc;
490             }
491 4         7 $loc->{location}[LOCATION_COLUMN] = $pos;
492             } else {
493 79         110 $loc->{location}[LOCATION_COLUMN] += $chars;
494             }
495             }
496              
497             }
498              
499 100         154 return;
500             }
501              
502             sub parent {
503 1     1 1 3 return;
504             }
505              
506             sub perl_version_introduced {
507 18     18 1 57 my ( $self ) = @_;
508 133         247 return List::Util::max( grep { defined $_ } MINIMUM_PERL,
509             $self->{perl_version_introduced},
510 18         43 map { $_->perl_version_introduced() } $self->elements() );
  97         195  
511             }
512              
513             sub perl_version_removed {
514 9     9 1 13 my ( $self ) = @_;
515 9         12 my $max;
516 9         24 foreach my $elem ( $self->elements() ) {
517 58 50       106 if ( defined ( my $ver = $elem->perl_version_removed() ) ) {
518 0 0       0 if ( defined $max ) {
519 0 0       0 $ver < $max and $max = $ver;
520             } else {
521 0         0 $max = $ver;
522             }
523             }
524             }
525 9         23 return $max;
526             }
527              
528             sub schild {
529 0     0 1 0 my ( $self, $inx ) = @_;
530 0   0     0 $inx ||= 0;
531 0         0 my @kids = $self->schildren();
532 0         0 return $kids[$inx];
533             }
534              
535             sub schildren {
536 0     0 1 0 my ( $self ) = @_;
537             return (
538 0         0 grep { $_->significant() } $self->children()
  0         0  
539             );
540             }
541              
542             sub source {
543 10     10 1 21 my ( $self ) = @_;
544 10         22 return $self->{source};
545             }
546              
547             sub start {
548 109     109 1 174 my ( $self, $inx ) = @_;
549             $self->{start}
550 109 50       223 or return;
551             wantarray
552 109 100       182 and return @{ $self->{start} };
  55         93  
553 54   50     213 return $self->{start}[ $inx || 0 ];
554             }
555              
556             sub top {
557 2     2 1 4 my ( $self ) = @_;
558 2         5 return $self;
559             }
560              
561             sub type {
562 88     88 1 147 my ( $self, $inx ) = @_;
563             $self->{type}
564 88 50       199 or return;
565             wantarray
566 88 100       173 and return @{ $self->{type} };
  55         126  
567 33   50     133 return $self->{type}[ $inx || 0 ];
568             }
569              
570             sub variables {
571 40     40 1 69 my ( $self ) = @_;
572              
573 40 100       72 $self->interpolates()
574             or return;
575              
576 34         53 my %var;
577 34         60 foreach my $kid ( $self->children() ) {
578 67         190 foreach my $sym ( $kid->variables() ) {
579 35         82 $var{$sym} = 1;
580             }
581             }
582 34         302 return ( keys %var );
583             }
584              
585             sub _chop {
586 0     0   0 my ( $middle ) = @_;
587 0         0 my $left = substr $middle, 0, 1, '';
588 0         0 my $right = substr $middle, -1, 1, '';
589 0         0 return ( $left, $middle, $right );
590             }
591              
592             # decode data using the object's {encoding}
593             # It is anticipated that if I make PPIx::Regexp depend on this package,
594             # that this will be called there.
595              
596             sub __decode {
597 23     23   91 my ( $self, $data, $encoding ) = @_;
598 23   33     157 $encoding ||= $self->{encoding};
599 23 50 33     156 defined $encoding
600             and _encode_available()
601             or return $data;
602 0         0 return Encode::decode( $encoding, $data );
603             }
604              
605             {
606              
607             my $encode_available;
608              
609             sub _encode_available {
610 0 0   0   0 defined $encode_available and return $encode_available;
611 0 0       0 return ( $encode_available = eval {
612 0         0 require Encode;
613 0         0 1;
614             } ? 1 : 0
615             );
616             }
617              
618             }
619              
620             {
621             my ( $cached_doc, $cached_encoding );
622              
623             # These are the byte order marks documented as being recognized by
624             # PPI. Only utf-8 is documented as supported.
625             my %known_bom = (
626             'EFBBBF' => 'utf-8',
627             '0000FEFF' => 'utf-32be',
628             'FFFE0000' => 'utf-32le',
629             'FEFF' => 'utf-16be',
630             'FFFE' => 'utf-16le',
631             );
632              
633             sub _get_ppi_encoding {
634 17     17   26 my ( $elem ) = @_;
635              
636 17 50       83 my $doc = $elem->top()
637             or return;
638              
639 17 100 66     248 $cached_doc
640             and $doc == $cached_doc
641             and return $cached_encoding;
642              
643 11 50       43 my $bom = $doc->first_element()
644             or return;
645              
646 11         68 Scalar::Util::weaken( $cached_doc = $doc );
647              
648 11 50       42 if ( $bom->isa( 'PPI::Token::BOM' ) ) {
649             return ( $cached_encoding = $known_bom{
650 0         0 uc unpack 'H*', $bom->content() } );
651             }
652              
653 11         15 $cached_encoding = undef;
654              
655 11         15 foreach my $use (
656 11 100       28 @{ $doc->find( 'PPI::Statement::Include' ) || [] }
657             ) {
658 2 50       1393 'use' eq $use->type()
659             or next;
660 2 50       43 defined( my $module = $use->module() )
661             or next;
662 2 50       40 'utf8' eq $module
663             or next;
664 0         0 $cached_encoding = 'utf-8';
665 0         0 last;
666             }
667              
668 11         1900 return $cached_encoding;
669              
670             }
671              
672             }
673              
674             # This subroutine was created in an attempt to simplify control flow.
675             # Argument 2 (from 0) is not unpacked because the caller needs to see
676             # the side effects of matches made on it.
677              
678             {
679              
680             my %special = (
681             '$$' => sub { # Process ID.
682             my ( undef, $sigil ) = @_;
683             return [ CLASS_INTERPOLATION, $sigil ];
684             },
685             '$' => sub { # Called if we find (e.g.) '$@'
686             my ( undef, $sigil ) = @_;
687             $_[2] =~ m/ \G ( [\@] ) /smxgc
688             or return;
689             return [ CLASS_INTERPOLATION, "$sigil$1" ];
690             },
691             '@' => sub { # Called if we find '@@'.
692             my ( undef, $sigil ) = @_;
693             return [ CLASS_STRING, $sigil ];
694             },
695             );
696              
697             sub _interpolation { ## no critic (RequireArgUnpacking)
698 51     51   87 my ( $self, $sigil ) = @_;
699             # Argument $_[2] is $content, but we can't unpack it because we
700             # need the caller to see any changes to pos().
701              
702 51 100       143 if ( $_[2] =~ m/ \G (?= \{ ) /smxgc ) {
703             # variable name enclosed in {}
704 14         43 my $delim_re = __match_enclosed( qw< { > );
705 14 50       760 if ( $_[2] =~ m/ \G $delim_re /smxgc ) {
706 14         36 my $rest = $1;
707 14 100       80 $rest =~ m/ \A \{ \s* \[ ( .* ) \] \s* \} \z /smx
708             or return [ CLASS_INTERPOLATION, "$sigil$rest" ];
709             # At this point we have @{[ ... ]}.
710 5         11 my @arg;
711 5 100       16 _has_postderef( "$1" )
712             and push @arg, postderef => 1;
713 5         264 return [ CLASS_INTERPOLATION, "$sigil$rest", @arg ];
714             }
715 0 0       0 $_[2] =~ m/ \G ( .* ) /smxgc
716             and return [ CLASS_UNKNOWN, "$sigil$1",
717             error => MISMATCHED_DELIM ];
718 0         0 confess 'Failed to match /./';
719             }
720              
721 37 100       107 if ( $_[2] =~ m< \G ( @{[ VARIABLE_RE ]} ) >smxgco
  5         637  
722             ) {
723             # variable name not enclosed in {}
724 34         93 my $interp = "$sigil$1";
725 34         112 while ( $_[2] =~ m/ \G ( (?: -> )? ) (?= ( [[{] ) ) /smxgc ) { # }]
726 2         3 my $lead_in = $1;
727 2         9 my $delim_re = __match_enclosed( $2 );
728 2 50       109 if ( $_[2] =~ m/ \G ( $delim_re ) /smxgc ) {
729 2         12 $interp .= "$lead_in$1";
730             } else {
731 0         0 $_[2] =~ m/ ( .* ) /smxgc;
732             return (
733 0         0 [ CLASS_INTERPOLATION, $interp ],
734             [ CLASS_UNKNOWN, "$1", error => MISMATCHED_DELIM ],
735             );
736             }
737             }
738              
739 34         47 my @arg;
740              
741 34 100       87 if ( defined( my $deref = _match_postderef( $_[2] ) ) ) {
742 6         12 $interp .= $deref;
743 6         21 push @arg, postderef => 1;
744             }
745              
746 34         124 return [ CLASS_INTERPOLATION, $interp, @arg ];
747             }
748              
749 3         6 my $code;
750 3 50 33     15 $code = $special{$sigil}
751             and my $elem = $code->( $self, $sigil, $_[2] )
752             or return [ CLASS_UNKNOWN, $sigil,
753             error => 'Sigil without interpolation' ];
754              
755 3         6 return $elem;
756             }
757              
758             }
759              
760             sub _link_elems {
761 62     62   104 my ( $self, @arg ) = @_;
762              
763 62         76 push @{ $self->{children} }, @arg;
  62         117  
764              
765 62         105 foreach my $key ( qw{ type start children finish } ) {
766 248         261 my $prev;
767 248         248 foreach my $elem ( @{ $self->{$key} } ) {
  248         350  
768 326         773 Scalar::Util::weaken( $elem->{parent} = $self );
769 326 100       453 if ( $prev ) {
770 81         199 Scalar::Util::weaken( $elem->{previous_sibling} = $prev );
771 81         146 Scalar::Util::weaken( $prev->{next_sibling} = $elem );
772             }
773 326         445 $prev = $elem;
774             }
775             }
776              
777 62         538 return $self;
778             }
779              
780             {
781             my %allow_subscr = map { $_ => 1 } qw{ % @ };
782              
783             # Match a postfix deref at the current position in the argument. If
784             # a match occurs it is returned, and the current position is
785             # updated. If not, nothing is returned, and the current position in
786             # the argument remains unchanged.
787             # This would all be much easier if I could count on Perl 5.10
788             sub _match_postderef { ## no critic (RequireArgUnpacking)
789 34     34   50 my $pos = pos $_[0];
790             # Only scalars and arrays interpolate
791 34 100       106 $_[0] =~ m/ \G ( -> ) ( \$ \# | [\$\@] ) /smxgc
792             or return;
793 6         15 my $match = "$1$2";
794 6         12 my $sigil = $2;
795 6 100       27 $_[0] =~ m/ \G ( [*] ) /smxgc
796             and return "$match$1";
797              
798 1 50 33     8 if (
799             $allow_subscr{$sigil} &&
800             $_[0] =~ m/ \G (?= ( [[{] ) ) /smxgc # }]
801             ) {
802 1         6 my $re = __match_enclosed( "$1" );
803 1 50       54 $_[0] =~ m/ \G $re /smxgc
804             and return "$match$1";
805             }
806              
807 0         0 pos $_[0] = $pos;
808 0         0 return;
809             }
810             }
811              
812             {
813 7     7   61 no warnings qw{ qw }; ## no critic (ProhibitNoWarnings)
  7         16  
  7         5703  
814             my %is_postderef = map { $_ => 1 } qw{ $ $# @ % & * $* $#* @* %* &* ** };
815             sub _has_postderef {
816 5     5   13 my ( $string ) = @_;
817 5         22 my $doc = PPI::Document->new( \$string );
818 5 100       5137 foreach my $elem ( @{ $doc->find( 'PPI::Token::Symbol' ) || [] } ) {
  5         30  
819 3 50       949 my $next = $elem->snext_sibling()
820             or next;
821 3 50       80 $next->isa( 'PPI::Token::Operator' )
822             or next;
823 3 50       9 $next->content() eq '->'
824             or next;
825 3 50       17 $next = $next->snext_sibling()
826             or next;
827 3 50       57 $next->isa( 'PPI::Token::Cast' )
828             or next;
829 3         7 my $content = $next->content();
830 3 50       14 $is_postderef{$content}
831             or next;
832 3 50       20 $content =~ m/ \* \z /smx
833             and return 1;
834 0 0       0 $next = $next->snext_sibling()
835             or next;
836 0 0       0 $next->isa( 'PPI::Structure::Subscript' )
837             and return 1;
838             }
839 2         628 return 0;
840             }
841             }
842              
843             # For various reasons we may get consecutive literals -- typically
844             # strings. We want to merge these. The arguments are array refs, with
845             # the class name of the token in [0] and the content in [1]. I know of
846             # no way we can generate consecutive white space tokens, but if I did I
847             # would want them merged.
848             #
849             # NOTE that merger loses all attributes of the second token, so we MUST
850             # NOT merge CLASS_UNKNOWN tokens, or any class that might have
851             # attributes other than content.
852             {
853             my %can_merge = map { $_ => 1 } CLASS_STRING, CLASS_WHITESPACE;
854              
855             sub _merge_strings {
856 59     59   108 my @arg = @_;
857 59         70 my @rslt;
858 59         103 foreach my $elem ( @arg ) {
859 165 100 100     530 if ( @rslt && $can_merge{$elem->[0]}
      100        
860             && $elem->[0] eq $rslt[-1][0]
861             ) {
862 5         13 $rslt[-1][1] .= $elem->[1];
863             } else {
864 160         261 push @rslt, $elem;
865             }
866             }
867 59         128 return @rslt;
868             }
869             }
870              
871             # If we're processing an indented here document, strings must be split
872             # on new lines and un-indented. We return array refs rather than
873             # objects because we may be called before we're ready to build the
874             # objects.
875             sub _remove_here_doc_indentation {
876 53     53   120 my ( $self, $string, %arg ) = @_;
877              
878             # NOTE that we rely on the fact that both undef (not indented) and
879             # '' (indented by zero characters) evaluate false.
880             $self->{indentation}
881 53 100       189 or return [ CLASS_STRING, $string ];
882              
883 5         8 my $ignore_first;
884 5 100       11 if ( $arg{sibling} ) {
885             # Because the calling code primes the pump, @sibling will never
886             # be empty, even when processing the first token. So:
887             # * The pump-priming specifies class '', so if that is what we
888             # see we must process the first line; otherwise
889             # * If the previous token is a string ending in "\n", we must
890             # process the first line.
891             $ignore_first = '' ne $arg{sibling}[-1][0] && (
892             CLASS_STRING ne $arg{sibling}[-1][0] ||
893 2   66     9 $arg{sibling}[-1][1] !~ m/ \n \z /smx );
894             } else {
895             # Without @sibling, we unconditionally process the first line.
896 3         6 $ignore_first = 0;
897             }
898              
899 5         8 my @rslt;
900              
901 5         41 foreach ( split qr/ (?<= \n ) /smx, $string ) {
902 7 100       17 if ( $ignore_first ) {
903 1         7 push @rslt, [ CLASS_STRING, "$_" ];
904 1         2 $ignore_first = 0;
905             } else {
906 6 100       89 if ( "\n" eq $_ ) {
    50          
907 1         4 push @rslt,
908             [ CLASS_STRING, "$_" ],
909             ;
910             } elsif ( s/ ( $self->{_indentation_re} ) //smx ) {
911 5         29 push @rslt,
912             [ CLASS_WHITESPACE, "$1" ],
913             [ CLASS_STRING, "$_" ],
914             ;
915             } else {
916 0         0 push @rslt,
917             [ CLASS_UNKNOWN, "$_", error => NO_INDENTATION ],
918             ;
919             }
920             }
921             }
922              
923 5         27 return @rslt;
924             }
925              
926             sub _stringify_source {
927 114     114   178 my ( $self, $string, %opt ) = @_;
928              
929 114 100       251 if ( Scalar::Util::blessed( $string ) ) {
930              
931 69 50       145 $string->isa( 'PPI::Element' )
932             or return;
933              
934 69         116 foreach my $class ( qw{
935             PPI::Token::Quote
936             PPI::Token::QuoteLike::Backtick
937             PPI::Token::QuoteLike::Command
938             PPI::Token::QuoteLike::Readline
939             } ) {
940 228 100       482 $string->isa( $class )
941             or next;
942             $opt{test}
943 16 50       71 and return 1;
944              
945 16         49 my $encoding = _get_ppi_encoding( $string );
946 16         77 return $self->__decode( $string->content(), $encoding );
947             }
948              
949 53 100       138 if ( $string->isa( 'PPI::Token::HereDoc' ) ) {
950             $opt{test}
951 1 50       2 and return 1;
952              
953 1         2 my $encoding = _get_ppi_encoding( $string );
954             my $heredoc = join '',
955 1         8 map { $self->__decode( $_, $encoding) }
  5         21  
956             $string->heredoc();
957 1         4 my $terminator = $self->__decode( $string->terminator(),
958             $encoding );
959 1         4 $terminator =~ s/ (?<= \n ) \z /\n/smx;
960 1         4 return $self->__decode( $string->content(), $encoding ) .
961             "\n" . $heredoc . $terminator;
962             }
963              
964 52         284 return;
965              
966             }
967              
968 45 50       83 ref $string
969             and return;
970              
971             $string =~ m/ \A \s* (?: q [qx]? | << | [`'"<] ) /smx
972 45 50       313 and return $opt{test} ? 1 : $string;
    50          
973              
974 0         0 return;
975             }
976              
977             sub _unquote {
978 7     7   15 my ( $string ) = @_;
979 7 100       91 $string =~ s/ \A ['"] //smx
980             and chop $string;
981 7         30 $string =~ s/ \\ (?= . ) //smxg;
982 7         17 return $string;
983             }
984              
985             1;
986              
987             __END__