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   208272 use 5.006;
  7         58  
4              
5 7     7   31 use strict;
  7         12  
  7         130  
6 7     7   26 use warnings;
  7         22  
  7         172  
7              
8 7     7   38 use Carp;
  7         13  
  7         397  
9 7     7   3348 use Encode ();
  7         63121  
  7         146  
10 7     7   40 use List::Util ();
  7         11  
  7         191  
11 7         894 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   1391 };
  7         12  
22 7     7   2028 use PPIx::QuoteLike::Token::Control;
  7         13  
  7         183  
23 7     7   2243 use PPIx::QuoteLike::Token::Delimiter;
  7         16  
  7         203  
24 7     7   2082 use PPIx::QuoteLike::Token::Interpolation;
  7         16  
  7         169  
25 7     7   2043 use PPIx::QuoteLike::Token::String;
  7         15  
  7         164  
26 7     7   36 use PPIx::QuoteLike::Token::Structure;
  7         13  
  7         138  
27 7     7   1956 use PPIx::QuoteLike::Token::Unknown;
  7         14  
  7         183  
28 7     7   1921 use PPIx::QuoteLike::Token::Whitespace;
  7         16  
  7         213  
29 7         402 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   38 };
  7         11  
40 7     7   34 use Scalar::Util ();
  7         10  
  7         183  
41              
42             our $VERSION = '0.022';
43              
44 7     7   28 use constant CLASS_CONTROL => 'PPIx::QuoteLike::Token::Control';
  7         12  
  7         323  
45 7     7   34 use constant CLASS_DELIMITER => 'PPIx::QuoteLike::Token::Delimiter';
  7         13  
  7         293  
46 7     7   34 use constant CLASS_INTERPOLATION => 'PPIx::QuoteLike::Token::Interpolation';
  7         12  
  7         284  
47 7     7   36 use constant CLASS_STRING => 'PPIx::QuoteLike::Token::String';
  7         12  
  7         275  
48 7     7   33 use constant CLASS_STRUCTURE => 'PPIx::QuoteLike::Token::Structure';
  7         13  
  7         303  
49 7     7   34 use constant CLASS_UNKNOWN => 'PPIx::QuoteLike::Token::Unknown';
  7         12  
  7         260  
50 7     7   33 use constant CLASS_WHITESPACE => 'PPIx::QuoteLike::Token::Whitespace';
  7         12  
  7         343  
51              
52 7     7   33 use constant CODE_REF => ref sub {};
  7         13  
  7         284  
53              
54 7         251 use constant ILLEGAL_FIRST =>
55 7     7   43 'Tokenizer found illegal first characters';
  7         18  
56 7         239 use constant MISMATCHED_DELIM =>
57 7     7   32 'Tokenizer found mismatched delimiters';
  7         11  
58 7         23838 use constant NO_INDENTATION =>
59 7     7   29 'No indentation string found';
  7         19  
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 112     112 1 28771 my ( $class, $source, %arg ) = @_;
68              
69 112         172 my @children;
70              
71 112 100       236 if ( $arg{location} ) {
72             ARRAY_REF eq ref $arg{location}
73 2 50       9 or croak q;
74 2         8 foreach my $inx ( 0 .. 3 ) {
75 8 50       25 $arg{location}[$inx] =~ m/ [^0-9] /smx
76             and croak "Argument 'location' element $inx must be an unsigned integer";
77             }
78             }
79              
80 112 50       210 if ( ! defined $arg{index_locations} ) {
81             $arg{index_locations} = !! $arg{location} ||
82 112   100     364 __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 112         435 source => $source,
92             };
93              
94 112   33     335 bless $self, ref $class || $class;
95              
96 112 100       224 defined( my $string = $self->_stringify_source( $source ) )
97             or return;
98              
99 60         168 my ( $type, $gap, $gap2, $content, $end_delim, $indented, $start_delim );
100              
101             $arg{trace}
102 60 50       117 and warn "Initial match of $string\n";
103              
104             # q<>, qq<>, qx<>
105 60 100       1077 if ( $string =~ m/ \A \s* ( q [qx]? ) ( \s* ) ( . ) /smxgc ) {
    100          
    50          
106 9         32 ( $type, $gap, $start_delim ) = ( $1, $2, $3 );
107 9 50 66     39 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       18 and warn "Initial match '$type$start_delim'\n";
113 9   100     52 $self->{interpolates} = 'qq' eq $type ||
114             'qx' eq $type && q<'> ne $start_delim;
115 9   50     29 $content = substr $string, ( pos $string || 0 );
116 9         46 $end_delim = __matching_delimiter( $start_delim );
117 9 50       21 if ( $end_delim eq substr $content, -1 ) {
118 9         17 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         42 ( $type, $gap, $indented, $gap2, $start_delim ) = (
130             $1, $2, $3, $4, $5 );
131             $arg{trace}
132 7 50       33 and warn "Initial match '$type$start_delim$gap$indented'\n";
133 7         26 $self->{interpolates} = $start_delim !~ m/ \A [\\'] /smx;
134 7   50     111 $content = substr $string, ( pos $string || 0 );
135 7         24 $end_delim = _unquote( $start_delim );
136             # NOTE that the indentation is specifically space or tab
137             # only.
138 7 50       164 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       23 if ( $indented ) {
143             # Version per perldelta.pod for that release.
144 4         14 $self->{perl_version_introduced} = '5.025007';
145 4         11 $self->{indentation} = "$1";
146 4         49 $self->{_indentation_re} = qr/
147             ^ \Q$self->{indentation}\E /smx;
148             }
149             } else {
150 0         0 $end_delim = '';
151             }
152             $self->{start} = [
153 7         26 $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         32 [ CLASS_DELIMITER, $end_delim ],
160             [ CLASS_WHITESPACE, "\n" ],
161             ];
162              
163             # ``, '', "", <>
164             } elsif ( $string =~ m/ \A \s* ( [`'"<] ) /smxgc ) {
165 44         160 ( $type, $gap, $start_delim ) = ( '', '', $1 );
166             $arg{trace}
167 44 50       108 and warn "Initial match '$type$start_delim'\n";
168 44         119 $self->{interpolates} = q<'> ne $start_delim;
169 44   50     140 $content = substr $string, ( pos $string || 0 );
170 44         123 $end_delim = __matching_delimiter( $start_delim );
171 44 50       110 if ( $end_delim eq substr $content, -1 ) {
172 44         86 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 60 100       183 $self->{interpolates} = $self->{interpolates} ? 1 : 0;
186              
187 60   100     228 defined or $_ = '' for $indented, $gap2;
188             $self->{type} = [
189 60 100       139 $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 60   100     213 $self->_make_token( CLASS_DELIMITER, $start_delim ),
202             ];
203              
204             $arg{trace}
205 60 50       117 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 60 100       112 if ( $self->{interpolates} ) {
210 47         104 push @children, [ '' => '' ]; # Prime the pump
211 47         61 while ( 1 ) {
212              
213 144 100       670 if ( $content =~ m/ \G ( \\ [ULulQEF] ) /smxgc ) {
    100          
    100          
    100          
214 4         15 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 49         160 push @children, $self->_interpolation( "$1", $content );
238             } elsif ( $content =~ m/ \G ( \\ . | [^\\\$\@]+ ) /smxgc ) {
239 43         167 push @children, $self->_remove_here_doc_indentation(
240             "$1",
241             sibling => \@children,
242             );
243             } else {
244 47         79 last;
245             }
246             }
247              
248 47         110 @children = _merge_strings( @children );
249 47         67 shift @children; # remove the priming
250              
251             # Make the tokens, at long last.
252 47         97 foreach ( @children ) {
253 94         105 $_ = $self->_make_token( @{ $_ } );
  94         175  
254             }
255              
256             } else {
257              
258             length $content
259 13 100       51 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 60 100       179 CLASS_WHITESPACE, $self->{indentation} );
270              
271 60 100       144 if ( $self->{finish} ) {
272             # If we already have something here it is data, not objects.
273 7         13 foreach ( @{ $self->{finish} } ) {
  7         42  
274 14         18 $_ = $self->_make_token( @{ $_ } );
  14         42  
275             }
276             } else {
277             $self->{finish} = [
278 53         125 $self->_make_token( CLASS_DELIMITER, $end_delim ),
279             ];
280             }
281              
282 60 100       167 ref $_[1]
283             and pos( $_[1] ) = pos $string;
284              
285 60         165 return $self->_link_elems();
286             }
287             }
288              
289             sub child {
290 46     46 1 8480 my ( $self, $number ) = @_;
291 46         111 return $self->{children}[$number];
292             }
293              
294             sub children {
295 114     114 1 196 my ( $self ) = @_;
296 114         129 return @{ $self->{children} };
  114         281  
297             }
298              
299             sub content {
300 27     27 1 51 my ( $self ) = @_;
301 27         48 return join '', map { $_->content() } grep { $_ } $self->elements();
  142         255  
  142         174  
302             }
303              
304             sub delimiters {
305 27     27 1 50 my ( $self ) = @_;
306 54         192 return join '', grep { defined }
307 27         49 map { $self->_get_value_scalar( $_ ) }
  54         86  
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   72 my ( $self, $method ) = @_;
363 54 50       114 defined( my $val = $self->$method() )
364             or return;
365 54 50       159 return ref $val ? $val->content() : $val;
366             }
367              
368             sub elements {
369 84     84 1 2408 my ( $self ) = @_;
370 84         100 return @{ $self->{elements} ||= [
371 84   100     335 map { $self->$_() } qw{ type start children finish }
  192         419  
372             ] };
373             }
374              
375             sub encoding {
376 34     34 1 61 my ( $self ) = @_;
377 34         115 return $self->{encoding};
378             }
379              
380             sub failures {
381 36     36 1 7994 my ( $self ) = @_;
382 36         140 return $self->{failures};
383             }
384              
385             sub find {
386 1     1 1 730 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   7 sub { $_[0]->isa( $target ) };
  5 50       24  
391 1         3 my @found;
392 1         4 foreach my $elem ( $self, $self->elements() ) {
393 5 100       7 $check->( $elem )
394             and push @found, $elem;
395             }
396              
397             @found
398 1 50       3 or return 0;
399              
400 1         26 return \@found;
401             }
402              
403             sub finish {
404 109     109 1 160 my ( $self, $inx ) = @_;
405             $self->{finish}
406 109 50       222 or return;
407             wantarray
408 109 100       182 and return @{ $self->{finish} };
  55         221  
409 54   50     183 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         29 return $self->{indentation};
420             }
421              
422             sub interpolates {
423 80     80 1 123 my ( $self ) = @_;
424 80         279 return $self->{interpolates};
425             }
426              
427             sub location {
428 6     6 1 12 my ( $self ) = @_;
429 6         10 return $self->type()->location();
430             }
431              
432             sub _make_token {
433 318     318   553 my ( $self, $class, $content, %arg ) = @_;
434 318         1019 my $token = $class->__new( content => $content, %arg );
435             CLASS_UNKNOWN eq $class
436 318 100       613 and $self->{failures}++;
437             $self->{index_locations}
438 318 100       604 and $self->_update_location( $token );
439 318         849 return $token;
440             }
441              
442             sub _update_location {
443 92     92   122 my ( $self, $token ) = @_;
444             $token->{location} # Idempotent
445 92 50       179 and return;
446 92   66     189 my $loc = $self->{_location} ||= do {
447             my %loc = (
448             line_content => '',
449             location => $self->{location},
450 17         47 );
451 17 100       43 if ( __instance( $self->{source}, 'PPI::Element' ) ) {
452 15   33     132 $loc{location} ||= $self->{source}->location();
453 15 50       2950 if ( my $doc = $self->{source}->document() ) {
454 15         265 $loc{tab_width} = $doc->tab_width();
455             }
456             }
457 17   100     114 $loc{tab_width} ||= 1;
458 17         48 \%loc;
459             };
460             $loc->{location}
461 92 50       136 or return;
462 92         101 $token->{location} = [ @{ $loc->{location} } ];
  92         185  
463              
464 92 50       281 if ( defined( my $content = $token->content() ) ) {
465              
466 92         102 my $lines;
467 92         163 pos( $content ) = 0;
468 92         227 $lines++ while $content =~ m/ \n /smxgc;
469 92 100       160 if ( pos $content ) {
470 4         6 $loc->{location}[LOCATION_LINE] += $lines;
471 4         5 $loc->{location}[LOCATION_LOGICAL_LINE] += $lines;
472             $loc->{location}[LOCATION_CHARACTER] =
473 4         5 $loc->{location}[LOCATION_COLUMN] = 1;
474             }
475              
476 92 100       168 if ( my $chars = length( $content ) - pos( $content ) ) {
477 77         91 $loc->{location}[LOCATION_CHARACTER] += $chars;
478 77 100 100     174 if ( $loc->{tab_width} > 1 && $content =~ m/ \t /smx ) {
479 4         8 my $pos = $loc->{location}[LOCATION_COLUMN];
480 4         4 my $tab_width = $loc->{tab_width};
481             # Stolen shamelessly from PPI::Document::_visual_length
482 4         5 my ( $vis_inc );
483 4         13 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         6 $vis_inc = length $part;
488             }
489 10         12 $pos += $vis_inc;
490             }
491 4         7 $loc->{location}[LOCATION_COLUMN] = $pos;
492             } else {
493 73         103 $loc->{location}[LOCATION_COLUMN] += $chars;
494             }
495             }
496              
497             }
498              
499 92         137 return;
500             }
501              
502             sub parent {
503 1     1 1 3 return;
504             }
505              
506             sub perl_version_introduced {
507 18     18 1 73 my ( $self ) = @_;
508 133         309 return List::Util::max( grep { defined $_ } MINIMUM_PERL,
509             $self->{perl_version_introduced},
510 18         58 map { $_->perl_version_introduced() } $self->elements() );
  97         221  
511             }
512              
513             sub perl_version_removed {
514 9     9 1 18 my ( $self ) = @_;
515 9         12 my $max;
516 9         19 foreach my $elem ( $self->elements() ) {
517 58 50       128 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         24 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 17 my ( $self ) = @_;
544 10         30 return $self->{source};
545             }
546              
547             sub start {
548 109     109 1 187 my ( $self, $inx ) = @_;
549             $self->{start}
550 109 50       219 or return;
551             wantarray
552 109 100       200 and return @{ $self->{start} };
  55         98  
553 54   50     196 return $self->{start}[ $inx || 0 ];
554             }
555              
556             sub top {
557 2     2 1 5 my ( $self ) = @_;
558 2         5 return $self;
559             }
560              
561             sub type {
562 88     88 1 148 my ( $self, $inx ) = @_;
563             $self->{type}
564 88 50       210 or return;
565             wantarray
566 88 100       170 and return @{ $self->{type} };
  55         146  
567 33   50     125 return $self->{type}[ $inx || 0 ];
568             }
569              
570             sub variables {
571 38     38 1 64 my ( $self ) = @_;
572              
573 38 100       69 $self->interpolates()
574             or return;
575              
576 32         44 my %var;
577 32         68 foreach my $kid ( $self->children() ) {
578 65         197 foreach my $sym ( $kid->variables() ) {
579 34         79 $var{$sym} = 1;
580             }
581             }
582 32         201 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 21     21   80 my ( $self, $data, $encoding ) = @_;
598 21   33     80 $encoding ||= $self->{encoding};
599 21 50 33     134 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 15     15   26 my ( $elem ) = @_;
635              
636 15 50       108 my $doc = $elem->top()
637             or return;
638              
639 15 100 66     225 $cached_doc
640             and $doc == $cached_doc
641             and return $cached_encoding;
642              
643 9 50       46 my $bom = $doc->first_element()
644             or return;
645              
646 9         60 Scalar::Util::weaken( $cached_doc = $doc );
647              
648 9 50       40 if ( $bom->isa( 'PPI::Token::BOM' ) ) {
649             return ( $cached_encoding = $known_bom{
650 0         0 uc unpack 'H*', $bom->content() } );
651             }
652              
653 9         14 $cached_encoding = undef;
654              
655 9         13 foreach my $use (
656 9 100       21 @{ $doc->find( 'PPI::Statement::Include' ) || [] }
657             ) {
658 2 50       1385 'use' eq $use->type()
659             or next;
660 2 50       44 defined( my $module = $use->module() )
661             or next;
662 2 50       41 'utf8' eq $module
663             or next;
664 0         0 $cached_encoding = 'utf-8';
665 0         0 last;
666             }
667              
668 9         1556 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 49     49   86 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 49 100       146 if ( $_[2] =~ m/ \G (?= \{ ) /smxgc ) {
703             # variable name enclosed in {}
704 12         40 my $delim_re = __match_enclosed( qw< { > );
705 12 50       664 if ( $_[2] =~ m/ \G $delim_re /smxgc ) {
706 12         37 my $rest = $1;
707 12 100       94 $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       22 _has_postderef( "$1" )
712             and push @arg, postderef => 1;
713 5         308 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       136 if ( $_[2] =~ m< \G ( @{[ VARIABLE_RE ]} ) >smxgco
  5         654  
722             ) {
723             # variable name not enclosed in {}
724 34         95 my $interp = "$sigil$1";
725 34         123 while ( $_[2] =~ m/ \G ( (?: -> )? ) (?= ( [[{] ) ) /smxgc ) { # }]
726 2         3 my $lead_in = $1;
727 2         6 my $delim_re = __match_enclosed( $2 );
728 2 50       107 if ( $_[2] =~ m/ \G ( $delim_re ) /smxgc ) {
729 2         11 $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         49 my @arg;
740              
741 34 100       78 if ( defined( my $deref = _match_postderef( $_[2] ) ) ) {
742 6         21 $interp .= $deref;
743 6         14 push @arg, postderef => 1;
744             }
745              
746 34         173 return [ CLASS_INTERPOLATION, $interp, @arg ];
747             }
748              
749 3         5 my $code;
750 3 50 33     14 $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 60     60   101 my ( $self, @arg ) = @_;
762              
763 60         71 push @{ $self->{children} }, @arg;
  60         113  
764              
765 60         96 foreach my $key ( qw{ type start children finish } ) {
766 240         243 my $prev;
767 240         249 foreach my $elem ( @{ $self->{$key} } ) {
  240         366  
768 318         770 Scalar::Util::weaken( $elem->{parent} = $self );
769 318 100       471 if ( $prev ) {
770 81         161 Scalar::Util::weaken( $elem->{previous_sibling} = $prev );
771 81         158 Scalar::Util::weaken( $prev->{next_sibling} = $elem );
772             }
773 318         434 $prev = $elem;
774             }
775             }
776              
777 60         496 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   51 my $pos = pos $_[0];
790             # Only scalars and arrays interpolate
791 34 100       122 $_[0] =~ m/ \G ( -> ) ( \$ \# | [\$\@] ) /smxgc
792             or return;
793 6         17 my $match = "$1$2";
794 6         12 my $sigil = $2;
795 6 100       83 $_[0] =~ m/ \G ( [*] ) /smxgc
796             and return "$match$1";
797              
798 1 50 33     16 if (
799             $allow_subscr{$sigil} &&
800             $_[0] =~ m/ \G (?= ( [[{] ) ) /smxgc # }]
801             ) {
802 1         5 my $re = __match_enclosed( "$1" );
803 1 50       55 $_[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         13  
  7         5671  
814             my %is_postderef = map { $_ => 1 } qw{ $ $# @ % & * $* $#* @* %* &* ** };
815             sub _has_postderef {
816 5     5   13 my ( $string ) = @_;
817 5         36 my $doc = PPI::Document->new( \$string );
818 5 100       6040 foreach my $elem ( @{ $doc->find( 'PPI::Token::Symbol' ) || [] } ) {
  5         24  
819 3 50       985 my $next = $elem->snext_sibling()
820             or next;
821 3 50       113 $next->isa( 'PPI::Token::Operator' )
822             or next;
823 3 50       8 $next->content() eq '->'
824             or next;
825 3 50       23 $next = $next->snext_sibling()
826             or next;
827 3 50       70 $next->isa( 'PPI::Token::Cast' )
828             or next;
829 3         10 my $content = $next->content();
830 3 50       19 $is_postderef{$content}
831             or next;
832 3 50       27 $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         621 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 57     57   117 my @arg = @_;
857 57         73 my @rslt;
858 57         101 foreach my $elem ( @arg ) {
859 161 100 100     535 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 156         253 push @rslt, $elem;
865             }
866             }
867 57         130 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   131 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       214 or return [ CLASS_STRING, $string ];
882              
883 5         7 my $ignore_first;
884 5 100       14 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     12 $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         9 my @rslt;
900              
901 5         64 foreach ( split qr/ (?<= \n ) /smx, $string ) {
902 7 100       17 if ( $ignore_first ) {
903 1         6 push @rslt, [ CLASS_STRING, "$_" ];
904 1         2 $ignore_first = 0;
905             } else {
906 6 100       68 if ( "\n" eq $_ ) {
    50          
907 1         3 push @rslt,
908             [ CLASS_STRING, "$_" ],
909             ;
910             } elsif ( s/ ( $self->{_indentation_re} ) //smx ) {
911 5         28 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         25 return @rslt;
924             }
925              
926             sub _stringify_source {
927 112     112   185 my ( $self, $string, %opt ) = @_;
928              
929 112 100       247 if ( Scalar::Util::blessed( $string ) ) {
930              
931 67 50       141 $string->isa( 'PPI::Element' )
932             or return;
933              
934 67         86 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 226 100       516 $string->isa( $class )
941             or next;
942             $opt{test}
943 14 50       28 and return 1;
944              
945 14         44 my $encoding = _get_ppi_encoding( $string );
946 14         65 return $self->__decode( $string->content(), $encoding );
947             }
948              
949 53 100       108 if ( $string->isa( 'PPI::Token::HereDoc' ) ) {
950             $opt{test}
951 1 50       3 and return 1;
952              
953 1         3 my $encoding = _get_ppi_encoding( $string );
954             my $heredoc = join '',
955 1         8 map { $self->__decode( $_, $encoding) }
  5         13  
956             $string->heredoc();
957 1         4 my $terminator = $self->__decode( $string->terminator(),
958             $encoding );
959 1         5 $terminator =~ s/ (?<= \n ) \z /\n/smx;
960 1         4 return $self->__decode( $string->content(), $encoding ) .
961             "\n" . $heredoc . $terminator;
962             }
963              
964 52         292 return;
965              
966             }
967              
968 45 50       94 ref $string
969             and return;
970              
971             $string =~ m/ \A \s* (?: q [qx]? | << | [`'"<] ) /smx
972 45 50       345 and return $opt{test} ? 1 : $string;
    50          
973              
974 0         0 return;
975             }
976              
977             sub _unquote {
978 7     7   17 my ( $string ) = @_;
979 7 100       30 $string =~ s/ \A ['"] //smx
980             and chop $string;
981 7         76 $string =~ s/ \\ (?= . ) //smxg;
982 7         26 return $string;
983             }
984              
985             1;
986              
987             __END__