NAME

PPIx::QuoteLike - Parse Perl string literals and string-literal-like things.

SYNOPSIS

use PPIx::QuoteLike;

my $str = PPIx::QuoteLike->new( q<"fu$bar"> );
say $str->interpolates() ?
   'interpolates' :
   'does not interpolate';

DESCRIPTION

This Perl class parses Perl string literals and things that are reasonably like string literals. Its real reason for being is to find interpolated variables for Perl::Critic policies and similar code.

METHODS

This class supports the following public methods:

new

my $str = PPIx::QuoteLike->new( $source, %arg );

This static method parses the argument, and returns a new object containing the parse. The $source argument can be either a scalar or an appropriate PPI::Element object.

If the $source argument is a scalar, it is presumed to represent a quote-like literal of some sort, provided it begins like one. Otherwise this method will return nothing. The scalar representation of a here document is a multi-line string whose first line consists of the leading << and the start delimiter, and whose subsequent lines consist of the content of the here document and the end delimiter.

PPI classes that can be handled are PPI::Token::Quote, PPI::Token::QuoteLike::Backtick, PPI::Token::QuoteLike::Command, PPI::Token::QuoteLike::Readline, and PPI::Token::HereDoc. Any other object will cause new() to return nothing.

Additional optional arguments can be passed as name/value pairs. Supported arguments are:

encoding

This is the encoding of the $source. If this is specified as something other than undef, the $source will be decoded before processing.

If the $source is a PPI::Element, this encoding is used only if the document that contains the element has neither a byte order mark nor 'use utf8'.

postderef

This Boolean argument determines whether postfix dereferencing is recognized in interpolation. If unspecified, or specified as undef, it defaults to the value of $PPIx::QuoteLike::DEFAULT_POSTDEREF. This variable is not exported, and is true by default. If you change the value, the change should be properly localized:

local $PPIx::QuoteLike::DEFAULT_POSTDEREF = 0;
trace

This Boolean argument causes a trace of the parse to be written to standard out. Setting this to a true value is unsupported in the sense that the author makes no representation as to what will happen if you do it, and reserves the right to make changes to the functionality, or retract it completely, without notice.

All other arguments are unsupported and reserved to the author.

child

my $kid = $str->child( 0 );

This method returns the child element whose index is given as the argument. Children do not include the type(), or the start() or finish() delimiters. Negative indices are valid, and given the usual Perl interpretation.

children

my @kids = $str->children();

This method returns all child elements. Children do not include the type(), or the start() or finish() delimiters.

content

say $str->content();

This method returns the content of the object. If the original argument was a valid Perl string, this should be the same as the originally-parsed string.

delimiters

say $str->delimiters();

This method returns the delimiters of the object, as a string. This will be two characters unless the argument to new() was a here document, missing its end delimiter, or an invalid string. In the latter case the return might be anything.

elements

my @elem = $str->elements();

This method returns all elements of the object. This includes type(), start(), children(), and finish(), in that order.

failures

say $str->failures();

This method returns the number of parse failures found. These are instances where the parser could not figure out what was going on, and should be the same as the number of PPIx::QuoteLike::Token::Unknown objects returned by elements().

find

for ( @{[ $str->find( $criteria ) || [] } ) {
    ...
}

This method finds and returns a reference to an array of all elements that meet the given criteria. If nothing is found, a false value is returned.

The $criteria can be either the name of a PPIx::QuoteLike::Token class, or a code reference. In the latter case, the code is called for each element in elements(), with the element as the only argument. The element is included in the output if the code returns a true value.

finish

say map { $_->content() } $str->finish();

This method returns the finishing elements of the parse. It is actually an array, with the first element being a PPIx::QuoteLike::Token::Delimiter. If the parse is of a here document there will be a second element, which will be a PPIx::QuoteLike::Token::Whitespace containing the trailing new line character.

If called in list context you get the whole array. If called in scalar context you get the element whose index is given in the argument, or element zero if no argument is specified.

handles

say PPIx::QuoteLike->handles( $string ) ?
    "We can handle $string" :
    "We can not handle $string";

This convenience static method returns a true value if this package can be expected to handle the content of $string (be it scalar or object), and a false value otherwise.

interpolates

say $str->interpolates() ?
    'The string interpolates' :
    'The string does not interpolate';

This method returns a true value if the parsed string interpolates, and a false value if it does not. This does not indicate whether any interpolation actually takes place, only whether the string is double-quotish or single-quotish.

perl_version_introduced

This method returns the maximum value of perl_version_introduced returned by any of its elements. In other words, it returns the minimum version of Perl under which this quote-like object is valid. If there are no elements, 5.000 is returned, since that is the minimum value of Perl supported by this package.

perl_version_removed

This method returns the minimum defined value of perl_version_removed returned by any of the quote-like object's elements. In other words, it returns the lowest version of Perl in which this object is not valid. If there are no elements, or if no element has a defined perl_version_removed, undef is returned.

schild

my $skid = $str->schild( 0 );

This method returns the significant child elements whose index is given by the argument. Negative indices are interpreted in the usual way.

schildren

my @skids = $str->schildren();

This method returns the significant children.

source

my $source = $str->source();

This method returns the $source argument to new(), whatever it was.

start

say map { $_->content() } $str->start();

This method returns the starting elements of the parse. It is actually an array, with the first element being a PPIx::QuoteLike::Token::Delimiter. If the parse is of a here document there will be a second element, which will be a PPIx::QuoteLike::Token::Whitespace containing the trailing new line character.

If called in list context you get the whole array. If called in scalar context you get the element whose index is given in the argument, or element zero if no argument is specified.

type

my $type = $str->type();

This method returns the type object. This will be a PPIx::QuoteLike::Token::Structure if the parse was successful; otherwise it might be undef. Its contents will be everything up to the start delimiter, and will typically be 'q', 'qq', 'qx', '<<' (for here documents), or '' (for quoted strings).

The type data are actually an array. If the second element is present it will be the white space (if any) separating the actual type from the value. If called in list context you get the whole array. If called in scalar context you get the element whose index is given in the argument, or element zero if no argument is specified.

variables

say "Interpolates $_" for $str->variables();

This convenience method returns all interpolated variables. Each is returned only once, and they are returned in no particular order. If the object does not represent a string that interpolates, nothing is returned.

RESTRICTIONS

By the nature of this module, it is never going to get everything right. Many of the known problem areas involve interpolations one way or another.

Changes in Syntax

Sometimes the introduction of new syntax changes the way a string is parsed. For example, the \F (fold case) case control was introduced in Perl 5.15.8. But it did not represent a syntax error prior to that version of Perl, it was simply parsed as V. So

$ perl -le 'print "Foo\FBar"'

prints "FooFBar" under Perl 5.14.4, but "Foobar" under 5.16.0. PPIx::QuoteLike generally assumes the more modern parse in cases like this.

Static Parsing

It is well known that Perl can not be statically parsed. That is, you can not completely parse a piece of Perl code without executing that same code.

Nevertheless, this class is trying to statically parse quote-like things. I do not have any examples of where the parse of a quote-like thing would change based on what is interpolated, but neither can I rule it out. Caveat user.

Non-Standard Syntax

There are modules out there that alter the syntax of Perl. If the syntax of a regular expression is altered, this module has no way to understand that it has been altered, much less to adapt to the alteration. The following modules are known to cause problems:

Acme::PerlML, which renders Perl as XML.

Data::PostfixDeref, which causes Perl to interpret suffixed empty brackets as dereferencing the thing they suffix, and which is inconsistent with the postfix dereference syntax introduced in 5.19.5 and mainstreamed with 5.24.0.

Filter::Trigraph, which recognizes ANSI C trigraphs, allowing Perl to be written in the ISO 646 character set.

Perl6::Pugs. Enough said.

SUPPORT

Support is by the author. Please file bug reports at http://rt.cpan.org, or in electronic mail to the author.

AUTHOR

Thomas R. Wyant, III wyant at cpan dot org

COPYRIGHT AND LICENSE

Copyright (C) 2016-2018 by Thomas R. Wyant, III

This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES.

This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose.