Initial Commit

This commit is contained in:
Riley Schneider
2025-12-03 16:38:10 +01:00
parent c5e26bf594
commit b732d8d4b5
17680 changed files with 5977495 additions and 2 deletions

301
database/perl/vendor/lib/PPI/Cache.pm vendored Normal file
View File

@@ -0,0 +1,301 @@
package PPI::Cache;
=pod
=head1 NAME
PPI::Cache - The PPI Document Caching Layer
=head1 SYNOPSIS
# Set the cache
use PPI::Cache path => '/var/cache/ppi-cache';
# Manually create a cache
my $Cache = PPI::Cache->new(
path => '/var/cache/perl/class-PPI',
readonly => 1,
);
=head1 DESCRIPTION
C<PPI::Cache> provides the default caching functionality for L<PPI>.
It integrates automatically with L<PPI> itself. Once enabled, any attempt
to load a document from the filesystem will be cached via cache.
Please note that creating a L<PPI::Document> from raw source or something
other object will B<not> be cached.
=head2 Using PPI::Cache
The most common way of using C<PPI::Cache> is to provide parameters to
the C<use> statement at the beginning of your program.
# Load the class but do not set a cache
use PPI::Cache;
# Use a fairly normal cache location
use PPI::Cache path => '/var/cache/ppi-cache';
Any of the arguments that can be provided to the C<new> constructor can
also be provided to C<use>.
=head1 METHODS
=cut
use strict;
use Carp ();
use File::Spec ();
use File::Path ();
use Storable 2.17 ();
use Digest::MD5 2.35 ();
use Params::Util qw{_INSTANCE _SCALAR};
use PPI::Document ();
our $VERSION = '1.270'; # VERSION
use constant VMS => !! ( $^O eq 'VMS' );
sub import {
my $class = ref $_[0] ? ref shift : shift;
return 1 unless @_;
# Create a cache from the params provided
my $cache = $class->new(@_);
# Make PPI::Document use it
unless ( PPI::Document->set_cache( $cache ) ) {
Carp::croak("Failed to set cache in PPI::Document");
}
1;
}
#####################################################################
# Constructor and Accessors
=pod
=head2 new param => $value, ...
The C<new> constructor creates a new standalone cache object.
It takes a number of parameters to control the cache.
=over
=item path
The C<path> param sets the base directory for the cache. It must already
exist, and must be writable.
=item readonly
The C<readonly> param is a true/false flag that allows the use of an
existing cache by a less-privileged user (such as the web user).
Existing documents will be retrieved from the cache, but new documents
will not be written to it.
=back
Returns a new C<PPI::Cache> object, or dies on error.
=cut
sub new {
my $class = shift;
my %params = @_;
# Path should exist and be usable
my $path = $params{path}
or Carp::croak("Cannot create PPI::Cache, no path provided");
unless ( -d $path ) {
Carp::croak("Cannot create PPI::Cache, path does not exist");
}
unless ( -r $path and -x $path ) {
Carp::croak("Cannot create PPI::Cache, no read permissions for path");
}
if ( ! $params{readonly} and ! -w $path ) {
Carp::croak("Cannot create PPI::Cache, no write permissions for path");
}
# Create the basic object
my $self = bless {
path => $path,
readonly => !! $params{readonly},
}, $class;
$self;
}
=pod
=head2 path
The C<path> accessor returns the path on the local filesystem that is the
root of the cache.
=cut
sub path { $_[0]->{path} }
=pod
=head2 readonly
The C<readonly> accessor returns true if documents should not be written
to the cache.
=cut
sub readonly { $_[0]->{readonly} }
#####################################################################
# PPI::Cache Methods
=pod
=head2 get_document $md5sum | \$source
The C<get_document> method checks to see if a Document is stored in the
cache and retrieves it if so.
=cut
sub get_document {
my $self = ref $_[0]
? shift
: Carp::croak('PPI::Cache::get_document called as static method');
my $md5hex = $self->_md5hex(shift) or return undef;
$self->_load($md5hex);
}
=pod
=head2 store_document $Document
The C<store_document> method takes a L<PPI::Document> as argument and
explicitly adds it to the cache.
Returns true if saved, or C<undef> (or dies) on error.
FIXME (make this return either one or the other, not both)
=cut
sub store_document {
my $self = shift;
my $Document = _INSTANCE(shift, 'PPI::Document') or return undef;
# Shortcut if we are readonly
return 1 if $self->readonly;
# Find the filename to save to
my $md5hex = $Document->hex_id or return undef;
# Store the file
$self->_store( $md5hex, $Document );
}
#####################################################################
# Support Methods
# Store an arbitrary PPI::Document object (using Storable) to a particular
# path within the cache filesystem.
sub _store {
my ($self, $md5hex, $object) = @_;
my ($dir, $file) = $self->_paths($md5hex);
# Save the file
File::Path::mkpath( $dir, 0, 0755 ) unless -d $dir;
if ( VMS ) {
Storable::lock_nstore( $object, $file );
} else {
Storable::nstore( $object, $file );
}
}
# Load an arbitrary object (using Storable) from a particular
# path within the cache filesystem.
sub _load {
my ($self, $md5hex) = @_;
my (undef, $file) = $self->_paths($md5hex);
# Load the file
return '' unless -f $file;
my $object = VMS
? Storable::retrieve( $file )
: Storable::lock_retrieve( $file );
# Security check
unless ( _INSTANCE($object, 'PPI::Document') ) {
Carp::croak("Security Violation: Object in '$file' is not a PPI::Document");
}
$object;
}
# Convert a md5 to a dir and file name
sub _paths {
my $self = shift;
my $md5hex = lc shift;
my $dir = File::Spec->catdir( $self->path, substr($md5hex, 0, 1), substr($md5hex, 0, 2) );
my $file = File::Spec->catfile( $dir, $md5hex . '.ppi' );
return ($dir, $file);
}
# Check a md5hex param
sub _md5hex {
my $either = shift;
my $it = _SCALAR($_[0])
? PPI::Util::md5hex(${$_[0]})
: $_[0];
return (defined $it and ! ref $it and $it =~ /^[[:xdigit:]]{32}\z/s)
? lc $it
: undef;
}
1;
=pod
=head1 TO DO
- Finish the basic functionality
- Add support for use PPI::Cache auto-setting $PPI::Document::CACHE
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2005 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

926
database/perl/vendor/lib/PPI/Document.pm vendored Normal file
View File

@@ -0,0 +1,926 @@
package PPI::Document;
=pod
=head1 NAME
PPI::Document - Object representation of a Perl document
=head1 INHERITANCE
PPI::Document
isa PPI::Node
isa PPI::Element
=head1 SYNOPSIS
use PPI;
# Load a document from a file
my $Document = PPI::Document->new('My/Module.pm');
# Strip out comments
$Document->prune('PPI::Token::Comment');
# Find all the named subroutines
my $sub_nodes = $Document->find(
sub { $_[1]->isa('PPI::Statement::Sub') and $_[1]->name }
);
my @sub_names = map { $_->name } @$sub_nodes;
# Save the file
$Document->save('My/Module.pm.stripped');
=head1 DESCRIPTION
The C<PPI::Document> class represents a single Perl "document". A
C<PPI::Document> object acts as a root L<PPI::Node>, with some
additional methods for loading and saving, and working with
the line/column locations of Elements within a file.
The exemption to its L<PPI::Node>-like behavior this is that a
C<PPI::Document> object can NEVER have a parent node, and is always
the root node in a tree.
=head2 Storable Support
C<PPI::Document> implements the necessary C<STORABLE_freeze> and
C<STORABLE_thaw> hooks to provide native support for L<Storable>,
if you have it installed.
However if you want to clone a Document, you are highly recommended
to use the C<$Document-E<gt>clone> method rather than Storable's
C<dclone> function (although C<dclone> should still work).
=head1 METHODS
Most of the things you are likely to want to do with a Document are
probably going to involve the methods from L<PPI::Node> class, of which
this is a subclass.
The methods listed here are the remaining few methods that are truly
Document-specific.
=cut
use strict;
use Carp ();
use List::Util 1.33 ();
use Params::Util 1.00 qw{_SCALAR0 _ARRAY0 _INSTANCE};
use Digest::MD5 ();
use PPI::Util ();
use PPI ();
use PPI::Node ();
use overload 'bool' => \&PPI::Util::TRUE;
use overload '""' => 'content';
our $VERSION = '1.270'; # VERSION
our ( $errstr, @ISA ) = ( "", "PPI::Node" );
use PPI::Document::Fragment ();
# Document cache
my $CACHE;
# Convenience constants related to constants
use constant LOCATION_LINE => 0;
use constant LOCATION_CHARACTER => 1;
use constant LOCATION_COLUMN => 2;
use constant LOCATION_LOGICAL_LINE => 3;
use constant LOCATION_LOGICAL_FILE => 4;
#####################################################################
# Constructor and Static Methods
=pod
=head2 new
# Simple construction
$doc = PPI::Document->new( $filename );
$doc = PPI::Document->new( \$source );
# With the readonly attribute set
$doc = PPI::Document->new( $filename,
readonly => 1,
);
The C<new> constructor takes as argument a variety of different sources of
Perl code, and creates a single cohesive Perl C<PPI::Document>
for it.
If passed a file name as a normal string, it will attempt to load the
document from the file.
If passed a reference to a C<SCALAR>, this is taken to be source code and
parsed directly to create the document.
If passed zero arguments, a "blank" document will be created that contains
no content at all.
In all cases, the document is considered to be "anonymous" and not tied back
to where it was created from. Specifically, if you create a PPI::Document from
a filename, the document will B<not> remember where it was created from.
The constructor also takes attribute flags.
At this time, the only available attribute is the C<readonly> flag.
Setting C<readonly> to true will allow various systems to provide
additional optimisations and caching. Note that because C<readonly> is an
optimisation flag, it is off by default and you will need to explicitly
enable it.
Returns a C<PPI::Document> object, or C<undef> if parsing fails.
L<PPI::Exception> objects can also be thrown if there are parsing problems.
=cut
sub new {
local $_; # An extra one, just in case
my $class = ref $_[0] ? ref shift : shift;
unless ( @_ ) {
my $self = $class->SUPER::new;
$self->{readonly} = ! 1;
$self->{tab_width} = 1;
return $self;
}
# Check constructor attributes
my $source = shift;
my %attr = @_;
# Check the data source
if ( ! defined $source ) {
$class->_error("An undefined value was passed to PPI::Document::new");
} elsif ( ! ref $source ) {
# Catch people using the old API
if ( $source =~ /(?:\012|\015)/ ) {
Carp::croak("API CHANGE: Source code should only be passed to PPI::Document->new as a SCALAR reference");
}
# Save the filename
$attr{filename} ||= $source;
# When loading from a filename, use the caching layer if it exists.
if ( $CACHE ) {
my $file_contents = PPI::Util::_slurp( $source );
# Errors returned as plain string
return $class->_error($file_contents) if !ref $file_contents;
# Retrieve the document from the cache
my $document = $CACHE->get_document($file_contents);
return $class->_setattr( $document, %attr ) if $document;
$document = PPI::Lexer->lex_source( $$file_contents );
if ( $document ) {
# Save in the cache
$CACHE->store_document( $document );
return $class->_setattr( $document, %attr );
}
} else {
my $document = PPI::Lexer->lex_file( $source );
return $class->_setattr( $document, %attr ) if $document;
}
} elsif ( _SCALAR0($source) ) {
my $document = PPI::Lexer->lex_source( $$source );
return $class->_setattr( $document, %attr ) if $document;
} elsif ( _ARRAY0($source) ) {
$source = join '', map { "$_\n" } @$source;
my $document = PPI::Lexer->lex_source( $source );
return $class->_setattr( $document, %attr ) if $document;
} else {
$class->_error("Unknown object or reference was passed to PPI::Document::new");
}
# Pull and store the error from the lexer
my $errstr;
if ( _INSTANCE($@, 'PPI::Exception') ) {
$errstr = $@->message;
} elsif ( $@ ) {
$errstr = $@;
$errstr =~ s/\sat line\s.+$//;
} elsif ( PPI::Lexer->errstr ) {
$errstr = PPI::Lexer->errstr;
} else {
$errstr = "Unknown error parsing Perl document";
}
PPI::Lexer->_clear;
$class->_error( $errstr );
}
sub load {
Carp::croak("API CHANGE: File names should now be passed to PPI::Document->new to load a file");
}
sub _setattr {
my ($class, $document, %attr) = @_;
$document->{readonly} = !! $attr{readonly};
$document->{filename} = $attr{filename};
return $document;
}
=pod
=head2 set_cache $cache
As of L<PPI> 1.100, C<PPI::Document> supports parser caching.
The default cache class L<PPI::Cache> provides a L<Storable>-based
caching or the parsed document based on the MD5 hash of the document as
a string.
The static C<set_cache> method is used to set the cache object for
C<PPI::Document> to use when loading documents. It takes as argument
a L<PPI::Cache> object (or something that C<isa> the same).
If passed C<undef>, this method will stop using the current cache, if any.
For more information on caching, see L<PPI::Cache>.
Returns true on success, or C<undef> if not passed a valid param.
=cut
sub set_cache {
my $class = ref $_[0] ? ref shift : shift;
if ( defined $_[0] ) {
# Enable the cache
my $object = _INSTANCE(shift, 'PPI::Cache') or return undef;
$CACHE = $object;
} else {
# Disable the cache
$CACHE = undef;
}
1;
}
=pod
=head2 get_cache
If a document cache is currently set, the C<get_cache> method will
return it.
Returns a L<PPI::Cache> object, or C<undef> if there is no cache
currently set for C<PPI::Document>.
=cut
sub get_cache {
$CACHE;
}
#####################################################################
# PPI::Document Instance Methods
=pod
=head2 filename
The C<filename> accessor returns the name of the file in which the document
is stored.
=cut
sub filename {
$_[0]->{filename};
}
=pod
=head2 readonly
The C<readonly> attribute indicates if the document is intended to be
read-only, and will never be modified. This is an advisory flag, that
writers of L<PPI>-related systems may or may not use to enable
optimisations and caches for your document.
Returns true if the document is read-only or false if not.
=cut
sub readonly {
$_[0]->{readonly};
}
=pod
=head2 tab_width [ $width ]
In order to handle support for C<location> correctly, C<Documents>
need to understand the concept of tabs and tab width. The C<tab_width>
method is used to get and set the size of the tab width.
At the present time, PPI only supports "naive" (width 1) tabs, but we do
plan on supporting arbitrary, default and auto-sensing tab widths later.
Returns the tab width as an integer, or C<die>s if you attempt to set the
tab width.
=cut
sub tab_width {
my $self = shift;
return $self->{tab_width} unless @_;
$self->{tab_width} = shift;
}
=pod
=head2 save
$document->save( $file )
The C<save> method serializes the C<PPI::Document> object and saves the
resulting Perl document to a file. Returns C<undef> on failure to open
or write to the file.
=cut
sub save {
my $self = shift;
local *FILE;
open( FILE, '>', $_[0] ) or return undef;
binmode FILE;
print FILE $self->serialize or return undef;
close FILE or return undef;
return 1;
}
=pod
=head2 serialize
Unlike the C<content> method, which shows only the immediate content
within an element, Document objects also have to be able to be written
out to a file again.
When doing this we need to take into account some additional factors.
Primarily, we need to handle here-docs correctly, so that are written
to the file in the expected place.
The C<serialize> method generates the actual file content for a given
Document object. The resulting string can be written straight to a file.
Returns the serialized document as a string.
=cut
sub serialize {
my $self = shift;
my @tokens = $self->tokens;
# The here-doc content buffer
my $heredoc = '';
# Start the main loop
my $output = '';
foreach my $i ( 0 .. $#tokens ) {
my $Token = $tokens[$i];
# Handle normal tokens
unless ( $Token->isa('PPI::Token::HereDoc') ) {
my $content = $Token->content;
# Handle the trivial cases
unless ( $heredoc ne '' and $content =~ /\n/ ) {
$output .= $content;
next;
}
# We have pending here-doc content that needs to be
# inserted just after the first newline in the content.
if ( $content eq "\n" ) {
# Shortcut the most common case for speed
$output .= $content . $heredoc;
} else {
# Slower and more general version
$content =~ s/\n/\n$heredoc/;
$output .= $content;
}
$heredoc = '';
next;
}
# This token is a HereDoc.
# First, add the token content as normal, which in this
# case will definitely not contain a newline.
$output .= $Token->content;
# Now add all of the here-doc content to the heredoc buffer.
foreach my $line ( $Token->heredoc ) {
$heredoc .= $line;
}
if ( $Token->{_damaged} ) {
# Special Case:
# There are a couple of warning/bug situations
# that can occur when a HereDoc content was read in
# from the end of a file that we silently allow.
#
# When writing back out to the file we have to
# auto-repair these problems if we aren't going back
# on to the end of the file.
# When calculating $last_line, ignore the final token if
# and only if it has a single newline at the end.
my $last_index = $#tokens;
if ( $tokens[$last_index]->{content} =~ /^[^\n]*\n$/ ) {
$last_index--;
}
# This is a two part test.
# First, are we on the last line of the
# content part of the file
my $last_line = List::Util::none {
$tokens[$_] and $tokens[$_]->{content} =~ /\n/
} (($i + 1) .. $last_index);
if ( ! defined $last_line ) {
# Handles the null list case
$last_line = 1;
}
# Secondly, are their any more here-docs after us,
# (with content or a terminator)
my $any_after = List::Util::any {
$tokens[$_]->isa('PPI::Token::HereDoc')
and (
scalar(@{$tokens[$_]->{_heredoc}})
or
defined $tokens[$_]->{_terminator_line}
)
} (($i + 1) .. $#tokens);
if ( ! defined $any_after ) {
# Handles the null list case
$any_after = '';
}
# We don't need to repair the last here-doc on the
# last line. But we do need to repair anything else.
unless ( $last_line and ! $any_after ) {
# Add a terminating string if it didn't have one
unless ( defined $Token->{_terminator_line} ) {
$Token->{_terminator_line} = $Token->{_terminator};
}
# Add a trailing newline to the terminating
# string if it didn't have one.
unless ( $Token->{_terminator_line} =~ /\n$/ ) {
$Token->{_terminator_line} .= "\n";
}
}
}
# Now add the termination line to the heredoc buffer
if ( defined $Token->{_terminator_line} ) {
$heredoc .= $Token->{_terminator_line};
}
}
# End of tokens
if ( $heredoc ne '' ) {
# If the file doesn't end in a newline, we need to add one
# so that the here-doc content starts on the next line.
unless ( $output =~ /\n$/ ) {
$output .= "\n";
}
# Now we add the remaining here-doc content
# to the end of the file.
$output .= $heredoc;
}
$output;
}
=pod
=head2 hex_id
The C<hex_id> method generates an unique identifier for the Perl document.
This identifier is basically just the serialized document, with
Unix-specific newlines, passed through MD5 to produce a hexadecimal string.
This identifier is used by a variety of systems (such as L<PPI::Cache>
and L<Perl::Metrics>) as a unique key against which to store or cache
information about a document (or indeed, to cache the document itself).
Returns a 32 character hexadecimal string.
=cut
sub hex_id {
PPI::Util::md5hex($_[0]->serialize);
}
=pod
=head2 index_locations
Within a document, all L<PPI::Element> objects can be considered to have a
"location", a line/column position within the document when considered as a
file. This position is primarily useful for debugging type activities.
The method for finding the position of a single Element is a bit laborious,
and very slow if you need to do it a lot. So the C<index_locations> method
will index and save the locations of every Element within the Document in
advance, making future calls to <PPI::Element::location> virtually free.
Please note that this index should always be cleared using C<flush_locations>
once you are finished with the locations. If content is added to or removed
from the file, these indexed locations will be B<wrong>.
=cut
sub index_locations {
my $self = shift;
my @tokens = $self->tokens;
# Whenever we hit a heredoc we will need to increment by
# the number of lines in its content section when we
# encounter the next token with a newline in it.
my $heredoc = 0;
# Find the first Token without a location
my ($first, $location) = ();
foreach ( 0 .. $#tokens ) {
my $Token = $tokens[$_];
next if $Token->{_location};
# Found the first Token without a location
# Calculate the new location if needed.
if ($_) {
$location =
$self->_add_location( $location, $tokens[$_ - 1], \$heredoc );
} else {
my $logical_file =
$self->can('filename') ? $self->filename : undef;
$location = [ 1, 1, 1, 1, $logical_file ];
}
$first = $_;
last;
}
# Calculate locations for the rest
if ( defined $first ) {
foreach ( $first .. $#tokens ) {
my $Token = $tokens[$_];
$Token->{_location} = $location;
$location = $self->_add_location( $location, $Token, \$heredoc );
# Add any here-doc lines to the counter
if ( $Token->isa('PPI::Token::HereDoc') ) {
$heredoc += $Token->heredoc + 1;
}
}
}
1;
}
sub _add_location {
my ($self, $start, $Token, $heredoc) = @_;
my $content = $Token->{content};
# Does the content contain any newlines
my $newlines =()= $content =~ /\n/g;
my ($logical_line, $logical_file) =
$self->_logical_line_and_file($start, $Token, $newlines);
unless ( $newlines ) {
# Handle the simple case
return [
$start->[LOCATION_LINE],
$start->[LOCATION_CHARACTER] + length($content),
$start->[LOCATION_COLUMN]
+ $self->_visual_length(
$content,
$start->[LOCATION_COLUMN]
),
$logical_line,
$logical_file,
];
}
# This is the more complex case where we hit or
# span a newline boundary.
my $physical_line = $start->[LOCATION_LINE] + $newlines;
my $location = [ $physical_line, 1, 1, $logical_line, $logical_file ];
if ( $heredoc and $$heredoc ) {
$location->[LOCATION_LINE] += $$heredoc;
$location->[LOCATION_LOGICAL_LINE] += $$heredoc;
$$heredoc = 0;
}
# Does the token have additional characters
# after their last newline.
if ( $content =~ /\n([^\n]+?)\z/ ) {
$location->[LOCATION_CHARACTER] += length($1);
$location->[LOCATION_COLUMN] +=
$self->_visual_length(
$1, $location->[LOCATION_COLUMN],
);
}
$location;
}
sub _logical_line_and_file {
my ($self, $start, $Token, $newlines) = @_;
# Regex taken from perlsyn, with the correction that there's no space
# required between the line number and the file name.
if ($start->[LOCATION_CHARACTER] == 1) {
if ( $Token->isa('PPI::Token::Comment') ) {
if (
$Token->content =~ m<
\A
\# \s*
line \s+
(\d+) \s*
(?: (\"?) ([^\"]* [^\s\"]) \2 )?
\s*
\z
>xms
) {
return $1, ($3 || $start->[LOCATION_LOGICAL_FILE]);
}
}
elsif ( $Token->isa('PPI::Token::Pod') ) {
my $content = $Token->content;
my $line;
my $file = $start->[LOCATION_LOGICAL_FILE];
my $end_of_directive;
while (
$content =~ m<
^
\# \s*?
line \s+?
(\d+) (?: (?! \n) \s)*
(?: (\"?) ([^\"]*? [^\s\"]) \2 )??
\s*?
$
>xmsg
) {
($line, $file) = ($1, ( $3 || $file ) );
$end_of_directive = pos $content;
}
if (defined $line) {
pos $content = $end_of_directive;
my $post_directive_newlines =()= $content =~ m< \G [^\n]* \n >xmsg;
return $line + $post_directive_newlines - 1, $file;
}
}
}
return
$start->[LOCATION_LOGICAL_LINE] + $newlines,
$start->[LOCATION_LOGICAL_FILE];
}
sub _visual_length {
my ($self, $content, $pos) = @_;
my $tab_width = $self->tab_width;
my ($length, $vis_inc);
return length $content if $content !~ /\t/;
# Split the content in tab and non-tab parts and calculate the
# "visual increase" of each part.
for my $part ( split(/(\t)/, $content) ) {
if ($part eq "\t") {
$vis_inc = $tab_width - ($pos-1) % $tab_width;
}
else {
$vis_inc = length $part;
}
$length += $vis_inc;
$pos += $vis_inc;
}
$length;
}
=pod
=head2 flush_locations
When no longer needed, the C<flush_locations> method clears all location data
from the tokens.
=cut
sub flush_locations {
shift->_flush_locations(@_);
}
=pod
=head2 normalized
The C<normalized> method is used to generate a "Layer 1"
L<PPI::Document::Normalized> object for the current Document.
A "normalized" Perl Document is an arbitrary structure that removes any
irrelevant parts of the document and refactors out variations in style,
to attempt to approach something that is closer to the "true meaning"
of the Document.
See L<PPI::Normal> for more information on document normalization and
the tasks for which it is useful.
Returns a L<PPI::Document::Normalized> object, or C<undef> on error.
=cut
sub normalized {
# The normalization process will utterly destroy and mangle
# anything passed to it, so we are going to only give it a
# clone of ourselves.
PPI::Normal->process( $_[0]->clone );
}
=pod
=head1 complete
The C<complete> method is used to determine if a document is cleanly
structured, all braces are closed, the final statement is
fully terminated and all heredocs are fully entered.
Returns true if the document is complete or false if not.
=cut
sub complete {
my $self = shift;
# Every structure has to be complete
$self->find_any( sub {
$_[1]->isa('PPI::Structure')
and
! $_[1]->complete
} )
and return '';
# Strip anything that isn't a statement off the end
my @child = $self->children;
while ( @child and not $child[-1]->isa('PPI::Statement') ) {
pop @child;
}
# We must have at least one statement
return '' unless @child;
# Check the completeness of the last statement
return $child[-1]->_complete;
}
#####################################################################
# PPI::Node Methods
# We are a scope boundary
### XS -> PPI/XS.xs:_PPI_Document__scope 0.903+
sub scope() { 1 }
#####################################################################
# PPI::Element Methods
sub insert_before {
return undef;
# die "Cannot insert_before a PPI::Document";
}
sub insert_after {
return undef;
# die "Cannot insert_after a PPI::Document";
}
sub replace {
return undef;
# die "Cannot replace a PPI::Document";
}
#####################################################################
# Error Handling
# Set the error message
sub _error {
$errstr = $_[1];
undef;
}
# Clear the error message.
# Returns the object as a convenience.
sub _clear {
$errstr = '';
$_[0];
}
=pod
=head2 errstr
For error that occur when loading and saving documents, you can use
C<errstr>, as either a static or object method, to access the error message.
If a Document loads or saves without error, C<errstr> will return false.
=cut
sub errstr {
$errstr;
}
#####################################################################
# Native Storable Support
sub STORABLE_freeze {
my $self = shift;
my $class = ref $self;
my %hash = %$self;
return ($class, \%hash);
}
sub STORABLE_thaw {
my ($self, undef, $class, $hash) = @_;
bless $self, $class;
foreach ( keys %$hash ) {
$self->{$_} = delete $hash->{$_};
}
$self->__link_children;
}
1;
=pod
=head1 TO DO
- May need to overload some methods to forcefully prevent Document
objects becoming children of another Node.
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 SEE ALSO
L<PPI>, L<http://ali.as/>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,136 @@
package PPI::Document::File;
=pod
=head1 NAME
PPI::Document::File - A Perl Document located in a specific file
=head1 DESCRIPTION
B<WARNING: This class is experimental, and may change without notice>
B<PPI::Document::File> provides a L<PPI::Document> subclass that represents
a Perl document stored in a specific named file.
=head1 METHODS
=cut
use strict;
use Carp ();
use Params::Util qw{_STRING _INSTANCE};
use PPI::Document ();
our $VERSION = '1.270'; # VERSION
our @ISA = 'PPI::Document';
#####################################################################
# Constructor and Accessors
=pod
=head2 new
my $file = PPI::Document::File->new( 'Module.pm' );
The C<new> constructor works the same as for the regular one, except
that the only params allowed is a file name. You cannot create an
"anonymous" PPI::Document::File object, not can you create an empty one.
Returns a new PPI::Document::File object, or C<undef> on error.
=cut
sub new {
my $class = shift;
my $filename = _STRING(shift);
unless ( defined $filename ) {
# Perl::Critic got a complaint about not handling a file
# named "0".
return $class->_error("Did not provide a file name to load");
}
# Load the Document
my $self = $class->SUPER::new( $filename, @_ ) or return undef;
# Unlike a normal inheritance situation, due to our need to stay
# compatible with caching magic, this actually returns a regular
# anonymous document. We need to rebless if
if ( _INSTANCE($self, 'PPI::Document') ) {
bless $self, 'PPI::Document::File';
} else {
die "PPI::Document::File SUPER call returned an object of the wrong type";
}
$self;
}
=pod
=head2 save
# Save to the file we were loaded from
$file->save;
# Save a copy to somewhere else
$file->save( 'Module2.pm' );
The C<save> method works similarly to the one in the parent L<PPI::Document>
class, saving a copy of the document to a file.
The difference with this subclass is that if C<save> is not passed any
filename, it will save it back to the file it was loaded from.
Note: When saving to a different file, it is considered to be saving a
B<copy> and so the value returned by the C<filename> accessor will stay
the same, and not change to the new filename.
=cut
sub save {
my $self = shift;
# Save to where?
my $filename = shift;
unless ( defined $filename ) {
$filename = $self->filename;
}
# Hand off to main save method
$self->SUPER::save( $filename, @_ );
}
1;
=pod
=head1 TO DO
- May need to overload some methods to forcefully prevent Document
objects becoming children of another Node.
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,90 @@
package PPI::Document::Fragment;
=pod
=head1 NAME
PPI::Document::Fragment - A fragment of a Perl Document
=head1 DESCRIPTION
In some situations you might want to work with a fragment of a larger
document. C<PPI::Document::Fragment> is a class intended for this purpose.
It is functionally almost identical to a normal L<PPI::Document>, except
that it is not possible to get line/column positions for the elements
within it, and it does not represent a scope.
=head1 METHODS
=cut
use strict;
use PPI::Document ();
our $VERSION = '1.270'; # VERSION
our @ISA = 'PPI::Document';
#####################################################################
# PPI::Document Methods
=pod
=head2 index_locations
Unlike when called on a PPI::Document object, you should not be attempting
to find locations of things within a PPI::Document::Fragment, and thus any
call to the C<index_locations> will print a warning and return C<undef>
instead of attempting to index the locations of the Elements.
=cut
# There's no point indexing a fragment
sub index_locations {
warn "Useless attempt to index the locations of a document fragment";
undef;
}
#####################################################################
# PPI::Element Methods
# We are not a scope boundary
### XS -> PPI/XS.xs:_PPI_Document_Fragment__scope 0.903+
sub scope() { '' }
1;
=pod
=head1 TO DO
Integrate this into the rest of PPI so it has actual practical uses. The most
obvious would be to implement arbitrary cut/copy/paste more easily.
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,315 @@
package PPI::Document::Normalized;
=pod
=head1 NAME
PPI::Document::Normalized - A normalized Perl Document
=head1 DESCRIPTION
A C<Normalized Document> object is the result of the normalization process
contained in the L<PPI::Normal> class. See the documentation for
L<PPI::Normal> for more information.
The object contains a version stamp and function list for the version
of L<PPI::Normal> used to create it, and a processed and delinked
L<PPI::Document> object.
Typically, the Document object will have been mangled by the normalization
process in a way that would make it fatal to try to actually DO anything
with it.
Put simply, B<never> use the Document object after normalization.
B<YOU HAVE BEEN WARNED!>
The object is designed the way it is to provide a bias towards false
negatives. A comparison between two ::Normalized object will only return
true if they were produced by the same version of PPI::Normal, with the
same set of normalization functions (in the same order).
You may get false negatives if you are caching objects across an upgrade.
Please note that this is done for security purposes, as there are many
cases in which low layer normalization is likely to be done as part of
a code security process, and false positives could be highly dangerous.
=head1 METHODS
=cut
# For convenience (and since this isn't really a public class), import
# the methods we will need from Scalar::Util.
use strict;
use Scalar::Util qw{refaddr reftype blessed};
use Params::Util qw{_INSTANCE _ARRAY};
use PPI::Util ();
our $VERSION = '1.270'; # VERSION
use overload 'bool' => \&PPI::Util::TRUE;
use overload '==' => 'equal';
#####################################################################
# Constructor and Accessors
=pod
=head2 new
The C<new> method is intended for use only by the L<PPI::Normal> class,
and to get ::Normalized objects, you are highly recommended to use
either that module, or the C<normalized> method of the L<PPI::Document>
object itself.
=cut
sub new {
my $class = shift;
my %args = @_;
# Check the required params
my $Document = _INSTANCE($args{Document}, 'PPI::Document') or return undef;
my $version = $args{version};
my $functions = _ARRAY($args{functions}) or return undef;
# Create the object
my $self = bless {
Document => $Document,
version => $version,
functions => $functions,
}, $class;
$self;
}
sub _Document { $_[0]->{Document} }
=pod
=head2 version
The C<version> accessor returns the L<PPI::Normal> version used to create
the object.
=cut
sub version { $_[0]->{version} }
=pod
=head2 functions
The C<functions> accessor returns a reference to an array of the
normalization functions (in order) that were called when creating
the object.
=cut
sub functions { $_[0]->{functions} }
#####################################################################
# Comparison Methods
=pod
=head2 equal $Normalized
The C<equal> method is the primary comparison method, taking another
PPI::Document::Normalized object, and checking for equivalence to it.
The C<==> operator is also overload to this method, so that you can
do something like the following:
my $first = PPI::Document->load('first.pl');
my $second = PPI::Document->load('second.pl');
if ( $first->normalized == $second->normalized ) {
print "The two documents are equivalent";
}
Returns true if the normalized documents are equivalent, false if not,
or C<undef> if there is an error.
=cut
sub equal {
my $self = shift;
my $other = _INSTANCE(shift, 'PPI::Document::Normalized') or return undef;
# Prevent multiple concurrent runs
return undef if $self->{processing};
# Check the version and function list first
my $v1 = $self->version || "undef";
my $v2 = $other->version || "undef";
return '' if $v1 ne $v2;
$self->_equal_ARRAY( $self->functions, $other->functions ) or return '';
# Do the main comparison run
$self->{seen} = {};
my $rv = $self->_equal_blessed( $self->_Document, $other->_Document );
delete $self->{seen};
$rv;
}
# Check that two objects are matched
sub _equal_blessed {
my ($self, $this, $that) = @_;
my ($bthis, $bthat) = (blessed $this, blessed $that);
$bthis and $bthat and $bthis eq $bthat or return '';
# Check the object as a reference
$self->_equal_reference( $this, $that );
}
# Check that two references match their types
sub _equal_reference {
my ($self, $this, $that) = @_;
my ($rthis, $rthat) = (refaddr $this, refaddr $that);
$rthis and $rthat or return undef;
# If we have seen this before, are the pointing
# is it the same one we saw in both sides
my $seen = $self->{seen}->{$rthis};
if ( $seen and $seen ne $rthat ) {
return '';
}
# Check the reference types
my ($tthis, $tthat) = (reftype $this, reftype $that);
$tthis and $tthat and $tthis eq $tthat or return undef;
# Check the children of the reference type
$self->{seen}->{$rthis} = $rthat;
my $method = "_equal_$tthat";
my $rv = $self->$method( $this, $that );
delete $self->{seen}->{$rthis};
$rv;
}
# Compare the children of two SCALAR references
sub _equal_SCALAR {
my ($self, $this, $that) = @_;
my ($cthis, $cthat) = ($$this, $$that);
return $self->_equal_blessed( $cthis, $cthat ) if blessed $cthis;
return $self->_equal_reference( $cthis, $cthat ) if ref $cthis;
return (defined $cthat and $cthis eq $cthat) if defined $cthis;
! defined $cthat;
}
# For completeness sake, lets just treat REF as a specialist SCALAR case
sub _equal_REF { shift->_equal_SCALAR(@_) }
# Compare the children of two ARRAY references
sub _equal_ARRAY {
my ($self, $this, $that) = @_;
# Compare the number of elements
scalar(@$this) == scalar(@$that) or return '';
# Check each element in the array.
# Descend depth-first.
foreach my $i ( 0 .. scalar(@$this) ) {
my ($cthis, $cthat) = ($this->[$i], $that->[$i]);
if ( blessed $cthis ) {
return '' unless $self->_equal_blessed( $cthis, $cthat );
} elsif ( ref $cthis ) {
return '' unless $self->_equal_reference( $cthis, $cthat );
} elsif ( defined $cthis ) {
return '' unless (defined $cthat and $cthis eq $cthat);
} else {
return '' if defined $cthat;
}
}
1;
}
# Compare the children of a HASH reference
sub _equal_HASH {
my ($self, $this, $that) = @_;
# Compare the number of keys
return '' unless scalar(keys %$this) == scalar(keys %$that);
# Compare each key, descending depth-first.
foreach my $k ( keys %$this ) {
return '' unless exists $that->{$k};
my ($cthis, $cthat) = ($this->{$k}, $that->{$k});
if ( blessed $cthis ) {
return '' unless $self->_equal_blessed( $cthis, $cthat );
} elsif ( ref $cthis ) {
return '' unless $self->_equal_reference( $cthis, $cthat );
} elsif ( defined $cthis ) {
return '' unless (defined $cthat and $cthis eq $cthat);
} else {
return '' if defined $cthat;
}
}
1;
}
# We do not support GLOB comparisons
sub _equal_GLOB {
my ($self, $this, $that) = @_;
warn('GLOB comparisons are not supported');
'';
}
# We do not support CODE comparisons
sub _equal_CODE {
my ($self, $this, $that) = @_;
refaddr $this == refaddr $that;
}
# We don't support IO comparisons
sub _equal_IO {
my ($self, $this, $that) = @_;
warn('IO comparisons are not supported');
'';
}
sub DESTROY {
# Take the screw up Document with us
if ( $_[0]->{Document} ) {
$_[0]->{Document}->DESTROY;
delete $_[0]->{Document};
}
}
1;
=pod
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2005 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

309
database/perl/vendor/lib/PPI/Dumper.pm vendored Normal file
View File

@@ -0,0 +1,309 @@
package PPI::Dumper;
=pod
=head1 NAME
PPI::Dumper - Dumping of PDOM trees
=head1 SYNOPSIS
# Load a document
my $Module = PPI::Document->new( 'MyModule.pm' );
# Create the dumper
my $Dumper = PPI::Dumper->new( $Module );
# Dump the document
$Dumper->print;
=head1 DESCRIPTION
The PDOM trees in PPI are quite complex, and getting a dump of their
structure for development and debugging purposes is important.
This module provides that functionality.
The process is relatively simple. Create a dumper object with a
particular set of options, and then call one of the dump methods to
generate the dump content itself.
=head1 METHODS
=cut
use strict;
use Params::Util qw{_INSTANCE};
our $VERSION = '1.270'; # VERSION
#####################################################################
# Constructor
=pod
=head2 new $Element, param => value, ...
The C<new> constructor creates a dumper, and takes as argument a single
L<PPI::Element> object of any type to serve as the root of the tree to
be dumped, and a number of key-E<gt>value parameters to control the output
format of the Dumper. Details of the parameters are listed below.
Returns a new C<PPI::Dumper> object, or C<undef> if the constructor
is not passed a correct L<PPI::Element> root object.
=over
=item memaddr
Should the dumper print the memory addresses of each PDOM element.
True/false value, off by default.
=item indent
Should the structures being dumped be indented. This value is numeric,
with the number representing the number of spaces to use when indenting
the dumper output. Set to '2' by default.
=item class
Should the dumper print the full class for each element.
True/false value, on by default.
=item content
Should the dumper show the content of each element. True/false value,
on by default.
=item whitespace
Should the dumper show whitespace tokens. By not showing the copious
numbers of whitespace tokens the structure of the code can often be
made much clearer. True/false value, on by default.
=item comments
Should the dumper show comment tokens. In situations where you have
a lot of comments, the code can often be made clearer by ignoring
comment tokens. True/false value, on by default.
=item locations
Should the dumper show the location of each token. The values shown are
[ line, rowchar, column ]. See L<PPI::Element/"location"> for a description of
what these values really are. True/false value, off by default.
=back
=cut
sub new {
my $class = shift;
my $Element = _INSTANCE(shift, 'PPI::Element') or return undef;
# Create the object
my $self = bless {
root => $Element,
display => {
memaddr => '', # Show the refaddr of the item
indent => 2, # Indent the structures
class => 1, # Show the object class
content => 1, # Show the object contents
whitespace => 1, # Show whitespace tokens
comments => 1, # Show comment tokens
locations => 0, # Show token locations
},
}, $class;
# Handle the options
my @options = map { lc $_ } @_; # strict hashpairs # https://github.com/adamkennedy/PPI/issues/201
my %options = @options;
foreach ( keys %{$self->{display}} ) {
if ( exists $options{$_} ) {
if ( $_ eq 'indent' ) {
$self->{display}->{indent} = $options{$_};
} else {
$self->{display}->{$_} = !! $options{$_};
}
}
}
$self->{indent_string} = join '', (' ' x $self->{display}->{indent});
$self;
}
#####################################################################
# Main Interface Methods
=pod
=head2 print
The C<print> method generates the dump and prints it to STDOUT.
Returns as for the internal print function.
=cut
sub print {
CORE::print(shift->string);
}
=pod
=head2 string
The C<string> method generates the dump and provides it as a
single string.
Returns a string or undef if there is an error while generating the dump.
=cut
sub string {
my $array_ref = shift->_dump or return undef;
join '', map { "$_\n" } @$array_ref;
}
=pod
=head2 list
The C<list> method generates the dump and provides it as a raw
list, without trailing newlines.
Returns a list or the null list if there is an error while generating
the dump.
=cut
sub list {
my $array_ref = shift->_dump or return ();
@$array_ref;
}
#####################################################################
# Generation Support Methods
sub _dump {
my $self = ref $_[0] ? shift : shift->new(shift);
my $Element = _INSTANCE($_[0], 'PPI::Element') ? shift : $self->{root};
my $indent = shift || '';
my $output = shift || [];
# Print the element if needed
my $show = 1;
if ( $Element->isa('PPI::Token::Whitespace') ) {
$show = 0 unless $self->{display}->{whitespace};
} elsif ( $Element->isa('PPI::Token::Comment') ) {
$show = 0 unless $self->{display}->{comments};
}
push @$output, $self->_element_string( $Element, $indent ) if $show;
# Recurse into our children
if ( $Element->isa('PPI::Node') ) {
my $child_indent = $indent . $self->{indent_string};
foreach my $child ( @{$Element->{children}} ) {
$self->_dump( $child, $child_indent, $output );
}
}
$output;
}
sub _element_string {
my $self = ref $_[0] ? shift : shift->new(shift);
my $Element = _INSTANCE($_[0], 'PPI::Element') ? shift : $self->{root};
my $indent = shift || '';
my $string = '';
# Add the memory location
if ( $self->{display}->{memaddr} ) {
$string .= $Element->refaddr . ' ';
}
# Add the location if such exists
if ( $self->{display}->{locations} ) {
my $loc_string;
if ( $Element->isa('PPI::Token') ) {
my $location = $Element->location;
if ($location) {
$loc_string = sprintf("[ % 4d, % 3d, % 3d ] ", @$location);
}
}
# Output location or pad with 20 spaces
$string .= $loc_string || " " x 20;
}
# Add the indent
if ( $self->{display}->{indent} ) {
$string .= $indent;
}
# Add the class name
if ( $self->{display}->{class} ) {
$string .= ref $Element;
}
if ( $Element->isa('PPI::Token') ) {
# Add the content
if ( $self->{display}->{content} ) {
my $content = $Element->content;
$content =~ s/\n/\\n/g;
$content =~ s/\t/\\t/g;
$content =~ s/\f/\\f/g;
$string .= " \t'$content'";
}
} elsif ( $Element->isa('PPI::Structure') ) {
# Add the content
if ( $self->{display}->{content} ) {
my $start = $Element->start
? $Element->start->content
: '???';
my $finish = $Element->finish
? $Element->finish->content
: '???';
$string .= " \t$start ... $finish";
}
}
$string;
}
1;
=pod
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

889
database/perl/vendor/lib/PPI/Element.pm vendored Normal file
View File

@@ -0,0 +1,889 @@
package PPI::Element;
=pod
=head1 NAME
PPI::Element - The abstract Element class, a base for all source objects
=head1 INHERITANCE
PPI::Element is the root of the PDOM tree
=head1 DESCRIPTION
The abstract C<PPI::Element> serves as a base class for all source-related
objects, from a single whitespace token to an entire document. It provides
a basic set of methods to provide a common interface and basic
implementations.
=head1 METHODS
=cut
use strict;
use Clone 0.30 ();
use Scalar::Util qw{refaddr};
use Params::Util qw{_INSTANCE _ARRAY};
use List::Util ();
use PPI::Util ();
use PPI::Node ();
use PPI::Singletons '%_PARENT';
our $VERSION = '1.270'; # VERSION
our $errstr = "";
use overload 'bool' => \&PPI::Util::TRUE;
use overload '""' => 'content';
use overload '==' => '__equals';
use overload '!=' => '__nequals';
use overload 'eq' => '__eq';
use overload 'ne' => '__ne';
#####################################################################
# General Properties
=pod
=head2 significant
Because we treat whitespace and other non-code items as Tokens (in order to
be able to "round trip" the L<PPI::Document> back to a file) the
C<significant> method allows us to distinguish between tokens that form a
part of the code, and tokens that aren't significant, such as whitespace,
POD, or the portion of a file after (and including) the C<__END__> token.
Returns true if the Element is significant, or false it not.
=cut
### XS -> PPI/XS.xs:_PPI_Element__significant 0.845+
sub significant() { 1 }
=pod
=head2 class
The C<class> method is provided as a convenience, and really does nothing
more than returning C<ref($self)>. However, some people have found that
they appreciate the laziness of C<$Foo-E<gt>class eq 'whatever'>, so I
have caved to popular demand and included it.
Returns the class of the Element as a string
=cut
sub class { ref($_[0]) }
=pod
=head2 tokens
The C<tokens> method returns a list of L<PPI::Token> objects for the
Element, essentially getting back that part of the document as if it had
not been lexed.
This also means there are no Statements and no Structures in the list,
just the Token classes.
=cut
sub tokens { $_[0] }
=pod
=head2 content
For B<any> C<PPI::Element>, the C<content> method will reconstitute the
base code for it as a single string. This method is also the method used
for overloading stringification. When an Element is used in a double-quoted
string for example, this is the method that is called.
B<WARNING:>
You should be aware that because of the way that here-docs are handled, any
here-doc content is not included in C<content>, and as such you should
B<not> eval or execute the result if it contains any L<PPI::Token::HereDoc>.
The L<PPI::Document> method C<serialize> should be used to stringify a PDOM
document into something that can be executed as expected.
Returns the basic code as a string (excluding here-doc content).
=cut
### XS -> PPI/XS.xs:_PPI_Element__content 0.900+
sub content() { '' }
#####################################################################
# Navigation Methods
=pod
=head2 parent
Elements themselves are not intended to contain other Elements, that is
left to the L<PPI::Node> abstract class, a subclass of C<PPI::Element>.
However, all Elements can be contained B<within> a parent Node.
If an Element is within a parent Node, the C<parent> method returns the
Node.
=cut
sub parent { $_PARENT{refaddr $_[0]} }
=pod
=head2 descendant_of $element
Answers whether a C<PPI::Element> is contained within another one.
C<PPI::Element>s are considered to be descendants of themselves.
=cut
sub descendant_of {
my $cursor = shift;
my $parent = shift or return undef;
while ( refaddr $cursor != refaddr $parent ) {
$cursor = $_PARENT{refaddr $cursor} or return '';
}
return 1;
}
=pod
=head2 ancestor_of $element
Answers whether a C<PPI::Element> is contains another one.
C<PPI::Element>s are considered to be ancestors of themselves.
=cut
sub ancestor_of {
my $self = shift;
my $cursor = shift or return undef;
while ( refaddr $cursor != refaddr $self ) {
$cursor = $_PARENT{refaddr $cursor} or return '';
}
return 1;
}
=pod
=head2 statement
For a C<PPI::Element> that is contained (at some depth) within a
L<PPI::Statement>, the C<statement> method will return the first parent
Statement object lexically 'above' the Element.
Returns a L<PPI::Statement> object, which may be the same Element if the
Element is itself a L<PPI::Statement> object.
Returns false if the Element is not within a Statement and is not itself
a Statement.
=cut
sub statement {
my $cursor = shift;
while ( ! _INSTANCE($cursor, 'PPI::Statement') ) {
$cursor = $_PARENT{refaddr $cursor} or return '';
}
$cursor;
}
=pod
=head2 top
For a C<PPI::Element> that is contained within a PDOM tree, the C<top> method
will return the top-level Node in the tree. Most of the time this should be
a L<PPI::Document> object, however this will not always be so. For example,
if a subroutine has been removed from its Document, to be moved to another
Document.
Returns the top-most PDOM object, which may be the same Element, if it is
not within any parent PDOM object.
=cut
sub top {
my $cursor = shift;
while ( my $parent = $_PARENT{refaddr $cursor} ) {
$cursor = $parent;
}
$cursor;
}
=pod
=head2 document
For an Element that is contained within a L<PPI::Document> object,
the C<document> method will return the top-level Document for the Element.
Returns the L<PPI::Document> for this Element, or false if the Element is not
contained within a Document.
=cut
sub document {
my $top = shift->top;
_INSTANCE($top, 'PPI::Document') and $top;
}
=pod
=head2 next_sibling
All L<PPI::Node> objects (specifically, our parent Node) contain a number of
C<PPI::Element> objects. The C<next_sibling> method returns the C<PPI::Element>
immediately after the current one, or false if there is no next sibling.
=cut
sub next_sibling {
my $self = shift;
my $parent = $_PARENT{refaddr $self} or return '';
my $key = refaddr $self;
my $elements = $parent->{children};
my $position = List::Util::first {
refaddr $elements->[$_] == $key
} 0..$#$elements;
$elements->[$position + 1] || '';
}
=pod
=head2 snext_sibling
As per the other 's' methods, the C<snext_sibling> method returns the next
B<significant> sibling of the C<PPI::Element> object.
Returns a C<PPI::Element> object, or false if there is no 'next' significant
sibling.
=cut
sub snext_sibling {
my $self = shift;
my $parent = $_PARENT{refaddr $self} or return '';
my $key = refaddr $self;
my $elements = $parent->{children};
my $position = List::Util::first {
refaddr $elements->[$_] == $key
} 0..$#$elements;
while ( defined(my $it = $elements->[++$position]) ) {
return $it if $it->significant;
}
'';
}
=pod
=head2 previous_sibling
All L<PPI::Node> objects (specifically, our parent Node) contain a number of
C<PPI::Element> objects. The C<previous_sibling> method returns the Element
immediately before the current one, or false if there is no 'previous'
C<PPI::Element> object.
=cut
sub previous_sibling {
my $self = shift;
my $parent = $_PARENT{refaddr $self} or return '';
my $key = refaddr $self;
my $elements = $parent->{children};
my $position = List::Util::first {
refaddr $elements->[$_] == $key
} 0..$#$elements;
$position and $elements->[$position - 1] or '';
}
=pod
=head2 sprevious_sibling
As per the other 's' methods, the C<sprevious_sibling> method returns
the previous B<significant> sibling of the C<PPI::Element> object.
Returns a C<PPI::Element> object, or false if there is no 'previous' significant
sibling.
=cut
sub sprevious_sibling {
my $self = shift;
my $parent = $_PARENT{refaddr $self} or return '';
my $key = refaddr $self;
my $elements = $parent->{children};
my $position = List::Util::first {
refaddr $elements->[$_] == $key
} 0..$#$elements;
while ( $position-- and defined(my $it = $elements->[$position]) ) {
return $it if $it->significant;
}
'';
}
=pod
=head2 first_token
As a support method for higher-order algorithms that deal specifically with
tokens and actual Perl content, the C<first_token> method finds the first
PPI::Token object within or equal to this one.
That is, if called on a L<PPI::Node> subclass, it will descend until it
finds a L<PPI::Token>. If called on a L<PPI::Token> object, it will return
the same object.
Returns a L<PPI::Token> object, or dies on error (which should be extremely
rare and only occur if an illegal empty L<PPI::Statement> exists below the
current Element somewhere.)
=cut
sub first_token {
my $cursor = shift;
while ( $cursor->isa('PPI::Node') ) {
$cursor = $cursor->first_element
or die "Found empty PPI::Node while getting first token";
}
$cursor;
}
=pod
=head2 last_token
As a support method for higher-order algorithms that deal specifically with
tokens and actual Perl content, the C<last_token> method finds the last
PPI::Token object within or equal to this one.
That is, if called on a L<PPI::Node> subclass, it will descend until it
finds a L<PPI::Token>. If called on a L<PPI::Token> object, it will return
the itself.
Returns a L<PPI::Token> object, or dies on error (which should be extremely
rare and only occur if an illegal empty L<PPI::Statement> exists below the
current Element somewhere.)
=cut
sub last_token {
my $cursor = shift;
while ( $cursor->isa('PPI::Node') ) {
$cursor = $cursor->last_element
or die "Found empty PPI::Node while getting first token";
}
$cursor;
}
=pod
=head2 next_token
As a support method for higher-order algorithms that deal specifically with
tokens and actual Perl content, the C<next_token> method finds the
L<PPI::Token> object that is immediately after the current Element, even if
it is not within the same parent L<PPI::Node> as the one for which the
method is being called.
Note that this is B<not> defined as a L<PPI::Token>-specific method,
because it can be useful to find the next token that is after, say, a
L<PPI::Statement>, although obviously it would be useless to want the
next token after a L<PPI::Document>.
Returns a L<PPI::Token> object, or false if there are no more tokens after
the Element.
=cut
sub next_token {
my $cursor = shift;
# Find the next element, going upwards as needed
while ( 1 ) {
my $element = $cursor->next_sibling;
if ( $element ) {
return $element if $element->isa('PPI::Token');
return $element->first_token;
}
$cursor = $cursor->parent or return '';
if ( $cursor->isa('PPI::Structure') and $cursor->finish ) {
return $cursor->finish;
}
}
}
=pod
=head2 previous_token
As a support method for higher-order algorithms that deal specifically with
tokens and actual Perl content, the C<previous_token> method finds the
L<PPI::Token> object that is immediately before the current Element, even
if it is not within the same parent L<PPI::Node> as this one.
Note that this is not defined as a L<PPI::Token>-only method, because it can
be useful to find the token is before, say, a L<PPI::Statement>, although
obviously it would be useless to want the next token before a
L<PPI::Document>.
Returns a L<PPI::Token> object, or false if there are no more tokens before
the C<Element>.
=cut
sub previous_token {
my $cursor = shift;
# Find the previous element, going upwards as needed
while ( 1 ) {
my $element = $cursor->previous_sibling;
if ( $element ) {
return $element if $element->isa('PPI::Token');
return $element->last_token;
}
$cursor = $cursor->parent or return '';
if ( $cursor->isa('PPI::Structure') and $cursor->start ) {
return $cursor->start;
}
}
}
#####################################################################
# Manipulation
=pod
=head2 clone
As per the L<Clone> module, the C<clone> method makes a perfect copy of
an Element object. In the generic case, the implementation is done using
the L<Clone> module's mechanism itself. In higher-order cases, such as for
Nodes, there is more work involved to keep the parent-child links intact.
=cut
sub clone {
Clone::clone(shift);
}
=pod
=head2 insert_before @Elements
The C<insert_before> method allows you to insert lexical perl content, in
the form of C<PPI::Element> objects, before the calling C<Element>. You
need to be very careful when modifying perl code, as it's easy to break
things.
In its initial incarnation, this method allows you to insert a single
Element, and will perform some basic checking to prevent you inserting
something that would be structurally wrong (in PDOM terms).
In future, this method may be enhanced to allow the insertion of multiple
Elements, inline-parsed code strings or L<PPI::Document::Fragment> objects.
Returns true if the Element was inserted, false if it can not be inserted,
or C<undef> if you do not provide a C<PPI::Element> object as a parameter.
=cut
sub __insert_before {
my $self = shift;
$self->parent->__insert_before_child( $self, @_ );
}
=pod
=head2 insert_after @Elements
The C<insert_after> method allows you to insert lexical perl content, in
the form of C<PPI::Element> objects, after the calling C<Element>. You need
to be very careful when modifying perl code, as it's easy to break things.
In its initial incarnation, this method allows you to insert a single
Element, and will perform some basic checking to prevent you inserting
something that would be structurally wrong (in PDOM terms).
In future, this method may be enhanced to allow the insertion of multiple
Elements, inline-parsed code strings or L<PPI::Document::Fragment> objects.
Returns true if the Element was inserted, false if it can not be inserted,
or C<undef> if you do not provide a C<PPI::Element> object as a parameter.
=cut
sub __insert_after {
my $self = shift;
$self->parent->__insert_after_child( $self, @_ );
}
=pod
=head2 remove
For a given C<PPI::Element>, the C<remove> method will remove it from its
parent B<intact>, along with all of its children.
Returns the C<Element> itself as a convenience, or C<undef> if an error
occurs while trying to remove the C<Element>.
=cut
sub remove {
my $self = shift;
my $parent = $self->parent or return $self;
$parent->remove_child( $self );
}
=pod
=head2 delete
For a given C<PPI::Element>, the C<delete> method will remove it from its
parent, immediately deleting the C<Element> and all of its children (if it
has any).
Returns true if the C<Element> was successfully deleted, or C<undef> if
an error occurs while trying to remove the C<Element>.
=cut
sub delete {
$_[0]->remove or return undef;
$_[0]->DESTROY;
1;
}
=pod
=head2 replace $Element
Although some higher level class support more exotic forms of replace,
at the basic level the C<replace> method takes a single C<Element> as
an argument and replaces the current C<Element> with it.
To prevent accidental damage to code, in this initial implementation the
replacement element B<must> be of the same class (or a subclass) as the
one being replaced.
=cut
sub replace {
my $self = ref $_[0] ? shift : return undef;
_INSTANCE(shift, ref $self) or return undef;
die "The ->replace method has not yet been implemented";
}
=pod
=head2 location
If the Element exists within a L<PPI::Document> that has
indexed the Element locations using C<PPI::Document::index_locations>, the
C<location> method will return the location of the first character of the
Element within the Document.
Returns the location as a reference to a five-element array in the form C<[
$line, $rowchar, $col, $logical_line, $logical_file_name ]>. The values are in
a human format, with the first character of the file located at C<[ 1, 1, 1, ?,
'something' ]>.
The second and third numbers are similar, except that the second is the
literal horizontal character, and the third is the visual column, taking
into account tabbing (see L<PPI::Document/"tab_width [ $width ]">).
The fourth number is the line number, taking into account any C<#line>
directives. The fifth element is the name of the file that the element was
found in, if available, taking into account any C<#line> directives.
Returns C<undef> on error, or if the L<PPI::Document> object has not been
indexed.
=cut
sub location {
my $self = shift;
$self->_ensure_location_present or return undef;
# Return a copy, not the original
return [ @{$self->{_location}} ];
}
=pod
=head2 line_number
If the Element exists within a L<PPI::Document> that has indexed the Element
locations using C<PPI::Document::index_locations>, the C<line_number> method
will return the line number of the first character of the Element within the
Document.
Returns C<undef> on error, or if the L<PPI::Document> object has not been
indexed.
=cut
sub line_number {
my $self = shift;
my $location = $self->location() or return undef;
return $location->[0];
}
=pod
=head2 column_number
If the Element exists within a L<PPI::Document> that has indexed the Element
locations using C<PPI::Document::index_locations>, the C<column_number> method
will return the column number of the first character of the Element within the
Document.
Returns C<undef> on error, or if the L<PPI::Document> object has not been
indexed.
=cut
sub column_number {
my $self = shift;
my $location = $self->location() or return undef;
return $location->[1];
}
=pod
=head2 visual_column_number
If the Element exists within a L<PPI::Document> that has indexed the Element
locations using C<PPI::Document::index_locations>, the C<visual_column_number>
method will return the visual column number of the first character of the
Element within the Document, according to the value of
L<PPI::Document/"tab_width [ $width ]">.
Returns C<undef> on error, or if the L<PPI::Document> object has not been
indexed.
=cut
sub visual_column_number {
my $self = shift;
my $location = $self->location() or return undef;
return $location->[2];
}
=pod
=head2 logical_line_number
If the Element exists within a L<PPI::Document> that has indexed the Element
locations using C<PPI::Document::index_locations>, the C<logical_line_number>
method will return the line number of the first character of the Element within
the Document, taking into account any C<#line> directives.
Returns C<undef> on error, or if the L<PPI::Document> object has not been
indexed.
=cut
sub logical_line_number {
my $self = shift;
return $self->location()->[3];
}
=pod
=head2 logical_filename
If the Element exists within a L<PPI::Document> that has indexed the Element
locations using C<PPI::Document::index_locations>, the C<logical_filename>
method will return the logical file name containing the first character of the
Element within the Document, taking into account any C<#line> directives.
Returns C<undef> on error, or if the L<PPI::Document> object has not been
indexed.
=cut
sub logical_filename {
my $self = shift;
my $location = $self->location() or return undef;
return $location->[4];
}
sub _ensure_location_present {
my $self = shift;
unless ( exists $self->{_location} ) {
# Are we inside a normal document?
my $Document = $self->document or return undef;
if ( $Document->isa('PPI::Document::Fragment') ) {
# Because they can't be serialized, document fragments
# do not support the concept of location.
return undef;
}
# Generate the locations. If they need one location, then
# the chances are they'll want more, and it's better that
# everything is already pre-generated.
$Document->index_locations or return undef;
unless ( exists $self->{_location} ) {
# erm... something went very wrong here
return undef;
}
}
return 1;
}
# Although flush_locations is only publically a Document-level method,
# we are able to implement it at an Element level, allowing us to
# selectively flush only the part of the document that occurs after the
# element for which the flush is called.
sub _flush_locations {
my $self = shift;
unless ( $self == $self->top ) {
return $self->top->_flush_locations( $self );
}
# Get the full list of all Tokens
my @Tokens = $self->tokens;
# Optionally allow starting from an arbitrary element (or rather,
# the first Token equal-to-or-within an arbitrary element)
if ( _INSTANCE($_[0], 'PPI::Element') ) {
my $start = shift->first_token;
while ( my $Token = shift @Tokens ) {
return 1 unless $Token->{_location};
next unless refaddr($Token) == refaddr($start);
# Found the start. Flush its location
delete $$Token->{_location};
last;
}
}
# Iterate over any remaining Tokens and flush their location
foreach my $Token ( @Tokens ) {
delete $Token->{_location};
}
1;
}
#####################################################################
# XML Compatibility Methods
sub _xml_name {
my $class = ref $_[0] || $_[0];
my $name = lc join( '_', split /::/, $class );
substr($name, 4);
}
sub _xml_attr {
return {};
}
sub _xml_content {
defined $_[0]->{content} ? $_[0]->{content} : '';
}
#####################################################################
# Internals
# Set the error string
sub _error {
$errstr = $_[1];
undef;
}
# Clear the error string
sub _clear {
$errstr = '';
$_[0];
}
# Being DESTROYed in this manner, rather than by an explicit
# ->delete means our reference count has probably fallen to zero.
# Therefore we don't need to remove ourselves from our parent,
# just the index ( just in case ).
### XS -> PPI/XS.xs:_PPI_Element__DESTROY 0.900+
sub DESTROY { delete $_PARENT{refaddr $_[0]} }
# Operator overloads
sub __equals { ref $_[1] and refaddr($_[0]) == refaddr($_[1]) }
sub __nequals { !__equals(@_) }
sub __eq {
my $self = _INSTANCE($_[0], 'PPI::Element') ? $_[0]->content : $_[0];
my $other = _INSTANCE($_[1], 'PPI::Element') ? $_[1]->content : $_[1];
$self eq $other;
}
sub __ne { !__eq(@_) }
1;
=pod
=head1 TO DO
It would be nice if C<location> could be used in an ad-hoc manner. That is,
if called on an Element within a Document that has not been indexed, it will
do a one-off calculation to find the location. It might be very painful if
someone started using it a lot, without remembering to index the document,
but it would be handy for things that are only likely to use it once, such
as error handlers.
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,108 @@
package PPI::Exception;
=head1 NAME
PPI::Exception - The PPI exception base class
=head1 SYNOPSIS
use PPI::Exception;
my $e = PPI::Exception->new( 'something happened' );
$e->throw;
PPI::Exception->new( message => 'something happened' )->throw;
PPI::Exception->throw( message => 'something happened' );
=head1 DESCRIPTION
All exceptions thrown from within PPI will be instances or derivations
of this class.
=cut
use strict;
use Params::Util qw{_INSTANCE};
our $VERSION = '1.270'; # VERSION
=head1 METHODS
=head2 new $message | message => $message, ...
Constructs and returns a new C<PPI::Exception> object.
A message for the exception can be passed, either as a string
or as C<< message => $message >>. The message is available via the
C<message> method.
=cut
sub new {
my $class = shift;
return bless { @_ }, $class if @_ > 1;
return bless { message => $_[0] }, $class if @_;
return bless { message => 'Unknown Exception' }, $class;
}
=head2 throw
If called on a C<PPI::Exception> object, throws the object.
If called on the class name, uses the arguments to construct a
C<PPI::Exception> and then throw it.
Each time the object is thrown, information from the Perl <caller(0)>
call is saved and made available via the C<callers> method.
This method never returns.
=cut
sub throw {
my $it = shift;
if ( _INSTANCE($it, 'PPI::Exception') ) {
if ( $it->{callers} ) {
push @{ $it->{callers} }, [ caller(0) ];
} else {
$it->{callers} ||= [];
}
} else {
my $message = $_[0] || 'Unknown Exception';
$it = $it->new(
message => $message,
callers => [
[ caller(0) ],
],
);
}
die $it;
}
=head2 message
Returns the exception message passed to the object's constructor,
or a default message.
=cut
sub message {
$_[0]->{message};
}
=head2 callers
Returns a listref, each element of which is a listref of C<caller(0)>
information. The returned listref can be empty.
=cut
sub callers {
@{ $_[0]->{callers} || [] };
}
1;

View File

@@ -0,0 +1,10 @@
package PPI::Exception::ParserRejection;
use strict;
use PPI::Exception ();
our $VERSION = '1.270'; # VERSION
our @ISA = 'PPI::Exception';
1;

391
database/perl/vendor/lib/PPI/Find.pm vendored Normal file
View File

@@ -0,0 +1,391 @@
package PPI::Find;
=pod
=head1 NAME
PPI::Find - Object version of the Element->find method
=head1 SYNOPSIS
# Create the Find object
my $Find = PPI::Find->new( \&wanted );
# Return all matching Elements as a list
my @found = $Find->in( $Document );
# Can we find any matching Elements
if ( $Find->any_matches($Document) ) {
print "Found at least one matching Element";
}
# Use the object as an iterator
$Find->start($Document) or die "Failed to execute search";
while ( my $token = $Find->match ) {
...
}
=head1 DESCRIPTION
PPI::Find is the primary PDOM searching class in the core PPI package.
=head2 History
It became quite obvious during the development of PPI that many of the
modules that would be built on top of it were going to need large numbers
of saved, storable or easily creatable search objects that could be
reused a number of times.
Although the internal ->find method provides a basic ability to search,
it is by no means thorough. PPI::Find attempts to resolve this problem.
=head2 Structure and Style
PPI::Find provides a similar API to the popular L<File::Find::Rule>
module for file searching, but without the ability to assemble queries.
The implementation of a separate PPI::Find::Rule sub-class that does
provide this ability is left as an exercise for the reader.
=head2 The &wanted function
At the core of each PPI::Find object is a "wanted" function that is
passed a number of arguments and returns a value which controls the
flow of the search.
As the search executes, each Element will be passed to the wanted function
in depth-first order.
It will be provided with two arguments. The current Element to test as $_[0],
and the top-level Element of the search as $_[1].
The &wanted function is expected to return 1 (positive) if the Element
matches the condition, 0 (false) if it does not, and undef (undefined) if
the condition does not match, and the Find search should not descend to
any of the current Element's children.
Errors should be reported from the &wanted function via die, which will be
caught by the Find object and returned as an error.
=head1 METHODS
=cut
use strict;
use Params::Util qw{_INSTANCE};
our $VERSION = '1.270'; # VERSION
#####################################################################
# Constructor
=pod
=head2 new &wanted
The C<new> constructor takes a single argument of the &wanted function,
as described above and creates a new search.
Returns a new PPI::Find object, or C<undef> if not passed a CODE reference.
=cut
sub new {
my $class = ref $_[0] ? ref shift : shift;
my $wanted = ref $_[0] eq 'CODE' ? shift : return undef;
# Create the object
my $self = bless {
wanted => $wanted,
}, $class;
$self;
}
=pod
=head2 clone
The C<clone> method creates another instance of the same Find object.
The cloning is done safely, so if your existing Find object is in the
middle of an iteration, the cloned Find object will not also be in the
iteration and can be safely used independently.
Returns a duplicate PPI::Find object.
=cut
sub clone {
my $self = ref $_[0] ? shift
: die "->clone can only be called as an object method";
my $class = ref $self;
# Create the object
my $clone = bless {
wanted => $self->{wanted},
}, $class;
$clone;
}
####################################################################
# Search Execution Methods
=pod
=head2 in $Document [, array_ref => 1 ]
The C<in> method starts and completes a full run of the search.
It takes as argument a single L<PPI::Element> object which will
serve as the top of the search process.
Returns a list of PPI::Element objects that match the condition
described by the &wanted function, or the null list on error.
You should check the ->errstr method for any errors if you are
returned the null list, which may also mean simply that no Elements
were found that matched the condition.
Because of this need to explicitly check for errors, an alternative
return value mechanism is provide. If you pass the C<< array_ref => 1 >>
parameter to the method, it will return the list of matched Elements
as a reference to an ARRAY. The method will return false if no elements
were matched, or C<undef> on error.
The ->errstr method can still be used to get the error message as normal.
=cut
sub in {
my $self = shift;
my $Element = shift;
my %params = @_;
delete $self->{errstr};
# Are we already acting as an iterator
if ( $self->{in} ) {
return $self->_error('->in called while another search is in progress', %params);
}
# Get the root element for the search
unless ( _INSTANCE($Element, 'PPI::Element') ) {
return $self->_error('->in was not passed a PPI::Element object', %params);
}
# Prepare the search
$self->{in} = $Element;
$self->{matches} = [];
# Execute the search
if ( !eval { $self->_execute; 1 } ) {
my $errstr = $@;
$errstr =~ s/\s+at\s+line\s+.+$//;
return $self->_error("Error while searching: $errstr", %params);
}
# Clean up and return
delete $self->{in};
if ( $params{array_ref} ) {
if ( @{$self->{matches}} ) {
return delete $self->{matches};
}
delete $self->{matches};
return '';
}
# Return as a list
my $matches = delete $self->{matches};
@$matches;
}
=pod
=head2 start $Element
The C<start> method lets the Find object act as an iterator. The method
is passed the parent PPI::Element object as for the C<in> method, but does
not accept any parameters.
To simplify error handling, the entire search is done at once, with the
results cached and provided as-requested.
Returns true if the search completes, and false on error.
=cut
sub start {
my $self = shift;
my $Element = shift;
delete $self->{errstr};
# Are we already acting as an iterator
if ( $self->{in} ) {
return $self->_error('->in called while another search is in progress');
}
# Get the root element for the search
unless ( _INSTANCE($Element, 'PPI::Element') ) {
return $self->_error('->in was not passed a PPI::Element object');
}
# Prepare the search
$self->{in} = $Element;
$self->{matches} = [];
# Execute the search
if ( !eval { $self->_execute; 1 } ) {
my $errstr = $@;
$errstr =~ s/\s+at\s+line\s+.+$//;
$self->_error("Error while searching: $errstr");
return undef;
}
1;
}
=pod
=head2 match
The C<match> method returns the next matching Element in the iteration.
Returns a PPI::Element object, or C<undef> if there are no remaining
Elements to be returned.
=cut
sub match {
my $self = shift;
return undef unless $self->{matches};
# Fetch and return the next match
my $match = shift @{$self->{matches}};
return $match if $match;
$self->finish;
undef;
}
=pod
=head2 finish
The C<finish> method provides a mechanism to end iteration if you wish to
stop the iteration prematurely. It resets the Find object and allows it to
be safely reused.
A Find object will be automatically finished when C<match> returns false.
This means you should only need to call C<finish> when you stop
iterating early.
You may safely call this method even when not iterating and it will return
without failure.
Always returns true
=cut
sub finish {
my $self = shift;
delete $self->{in};
delete $self->{matches};
delete $self->{errstr};
1;
}
#####################################################################
# Support Methods and Error Handling
sub _execute {
my $self = shift;
my $wanted = $self->{wanted};
my @queue = ( $self->{in} );
# Pull entries off the queue and hand them off to the wanted function
while ( my $Element = shift @queue ) {
my $rv = &$wanted( $Element, $self->{in} );
# Add to the matches if returns true
push @{$self->{matches}}, $Element if $rv;
# Continue and don't descend if it returned undef
# or if it doesn't have children
next unless defined $rv;
next unless $Element->isa('PPI::Node');
# Add the children to the head of the queue
if ( $Element->isa('PPI::Structure') ) {
unshift @queue, $Element->finish if $Element->finish;
unshift @queue, $Element->children;
unshift @queue, $Element->start if $Element->start;
} else {
unshift @queue, $Element->children;
}
}
1;
}
=pod
=head2 errstr
The C<errstr> method returns the error messages when a given PPI::Find
object fails any action.
Returns a string, or C<undef> if there is no error.
=cut
sub errstr {
shift->{errstr};
}
sub _error {
my $self = shift;
$self->{errstr} = shift;
my %params = @_;
$params{array_ref} ? undef : ();
}
1;
=pod
=head1 TO DO
- Implement the L<PPI::Find::Rule> class
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

1537
database/perl/vendor/lib/PPI/Lexer.pm vendored Normal file

File diff suppressed because it is too large Load Diff

801
database/perl/vendor/lib/PPI/Node.pm vendored Normal file
View File

@@ -0,0 +1,801 @@
package PPI::Node;
=pod
=head1 NAME
PPI::Node - Abstract PPI Node class, an Element that can contain other Elements
=head1 INHERITANCE
PPI::Node
isa PPI::Element
=head1 SYNOPSIS
# Create a typical node (a Document in this case)
my $Node = PPI::Document->new;
# Add an element to the node( in this case, a token )
my $Token = PPI::Token::Word->new('my');
$Node->add_element( $Token );
# Get the elements for the Node
my @elements = $Node->children;
# Find all the barewords within a Node
my $barewords = $Node->find( 'PPI::Token::Word' );
# Find by more complex criteria
my $my_tokens = $Node->find( sub { $_[1]->content eq 'my' } );
# Remove all the whitespace
$Node->prune( 'PPI::Token::Whitespace' );
# Remove by more complex criteria
$Node->prune( sub { $_[1]->content eq 'my' } );
=head1 DESCRIPTION
The C<PPI::Node> class provides an abstract base class for the Element
classes that are able to contain other elements L<PPI::Document>,
L<PPI::Statement>, and L<PPI::Structure>.
As well as those listed below, all of the methods that apply to
L<PPI::Element> objects also apply to C<PPI::Node> objects.
=head1 METHODS
=cut
use strict;
use Carp ();
use Scalar::Util qw{refaddr};
use List::Util ();
use Params::Util qw{_INSTANCE _CLASS _CODELIKE _NUMBER};
use PPI::Element ();
use PPI::Singletons '%_PARENT';
our $VERSION = '1.270'; # VERSION
our @ISA = "PPI::Element";
#####################################################################
# The basic constructor
sub new {
my $class = ref $_[0] || $_[0];
bless { children => [] }, $class;
}
#####################################################################
# PDOM Methods
=pod
=head2 scope
The C<scope> method returns true if the node represents a lexical scope
boundary, or false if it does not.
=cut
### XS -> PPI/XS.xs:_PPI_Node__scope 0.903+
sub scope() { '' }
=pod
=head2 add_element $Element
The C<add_element> method adds a L<PPI::Element> object to the end of a
C<PPI::Node>. Because Elements maintain links to their parent, an
Element can only be added to a single Node.
Returns true if the L<PPI::Element> was added. Returns C<undef> if the
Element was already within another Node, or the method is not passed
a L<PPI::Element> object.
=cut
sub add_element {
my $self = shift;
# Check the element
my $Element = _INSTANCE(shift, 'PPI::Element') or return undef;
$_PARENT{refaddr $Element} and return undef;
# Add the argument to the elements
push @{$self->{children}}, $Element;
Scalar::Util::weaken(
$_PARENT{refaddr $Element} = $self
);
1;
}
# In a typical run profile, add_element is the number 1 resource drain.
# This is a highly optimised unsafe version, for internal use only.
sub __add_element {
Scalar::Util::weaken(
$_PARENT{refaddr $_[1]} = $_[0]
);
push @{$_[0]->{children}}, $_[1];
}
=pod
=head2 elements
The C<elements> method accesses all child elements B<structurally> within
the C<PPI::Node> object. Note that in the base of the L<PPI::Structure>
classes, this C<DOES> include the brace tokens at either end of the
structure.
Returns a list of zero or more L<PPI::Element> objects.
Alternatively, if called in the scalar context, the C<elements> method
returns a count of the number of elements.
=cut
sub elements {
if ( wantarray ) {
return @{$_[0]->{children}};
} else {
return scalar @{$_[0]->{children}};
}
}
=pod
=head2 first_element
The C<first_element> method accesses the first element structurally within
the C<PPI::Node> object. As for the C<elements> method, this does include
the brace tokens for L<PPI::Structure> objects.
Returns a L<PPI::Element> object, or C<undef> if for some reason the
C<PPI::Node> object does not contain any elements.
=cut
# Normally the first element is also the first child
sub first_element {
$_[0]->{children}->[0];
}
=pod
=head2 last_element
The C<last_element> method accesses the last element structurally within
the C<PPI::Node> object. As for the C<elements> method, this does include
the brace tokens for L<PPI::Structure> objects.
Returns a L<PPI::Element> object, or C<undef> if for some reason the
C<PPI::Node> object does not contain any elements.
=cut
# Normally the last element is also the last child
sub last_element {
$_[0]->{children}->[-1];
}
=pod
=head2 children
The C<children> method accesses all child elements lexically within the
C<PPI::Node> object. Note that in the case of the L<PPI::Structure>
classes, this does B<NOT> include the brace tokens at either end of the
structure.
Returns a list of zero of more L<PPI::Element> objects.
Alternatively, if called in the scalar context, the C<children> method
returns a count of the number of lexical children.
=cut
# In the default case, this is the same as for the elements method
sub children {
wantarray ? @{$_[0]->{children}} : scalar @{$_[0]->{children}};
}
=pod
=head2 schildren
The C<schildren> method is really just a convenience, the significant-only
variation of the normal C<children> method.
In list context, returns a list of significant children. In scalar context,
returns the number of significant children.
=cut
sub schildren {
return grep { $_->significant } @{$_[0]->{children}} if wantarray;
my $count = 0;
foreach ( @{$_[0]->{children}} ) {
$count++ if $_->significant;
}
return $count;
}
=pod
=head2 child $index
The C<child> method accesses a child L<PPI::Element> object by its
position within the Node.
Returns a L<PPI::Element> object, or C<undef> if there is no child
element at that node.
=cut
sub child {
my ( $self, $index ) = @_;
PPI::Exception->throw( "method child() needs an index" )
if not defined _NUMBER $index;
$self->{children}->[$index];
}
=pod
=head2 schild $index
The lexical structure of the Perl language ignores 'insignificant' items,
such as whitespace and comments, while L<PPI> treats these items as valid
tokens so that it can reassemble the file at any time. Because of this,
in many situations there is a need to find an Element within a Node by
index, only counting lexically significant Elements.
The C<schild> method returns a child Element by index, ignoring
insignificant Elements. The index of a child Element is specified in the
same way as for a normal array, with the first Element at index 0, and
negative indexes used to identify a "from the end" position.
=cut
sub schild {
my $self = shift;
my $idx = 0 + shift;
my $el = $self->{children};
if ( $idx < 0 ) {
my $cursor = 0;
while ( exists $el->[--$cursor] ) {
return $el->[$cursor] if $el->[$cursor]->significant and ++$idx >= 0;
}
} else {
my $cursor = -1;
while ( exists $el->[++$cursor] ) {
return $el->[$cursor] if $el->[$cursor]->significant and --$idx < 0;
}
}
undef;
}
=pod
=head2 contains $Element
The C<contains> method is used to determine if another L<PPI::Element>
object is logically "within" a C<PPI::Node>. For the special case of the
brace tokens at either side of a L<PPI::Structure> object, they are
generally considered "within" a L<PPI::Structure> object, even if they are
not actually in the elements for the L<PPI::Structure>.
Returns true if the L<PPI::Element> is within us, false if not, or C<undef>
on error.
=cut
sub contains {
my $self = shift;
my $Element = _INSTANCE(shift, 'PPI::Element') or return undef;
# Iterate up the Element's parent chain until we either run out
# of parents, or get to ourself.
while ( $Element = $Element->parent ) {
return 1 if refaddr($self) == refaddr($Element);
}
'';
}
=pod
=head2 find $class | \&wanted
The C<find> method is used to search within a code tree for
L<PPI::Element> objects that meet a particular condition.
To specify the condition, the method can be provided with either a simple
class name (full or shortened), or a C<CODE>/function reference.
# Find all single quotes in a Document (which is a Node)
$Document->find('PPI::Quote::Single');
# The same thing with a shortened class name
$Document->find('Quote::Single');
# Anything more elaborate, we go with the sub
$Document->find( sub {
# At the top level of the file...
$_[1]->parent == $_[0]
and (
# ...find all comments and POD
$_[1]->isa('PPI::Token::Pod')
or
$_[1]->isa('PPI::Token::Comment')
)
} );
The function will be passed two arguments, the top-level C<PPI::Node>
you are searching in and the current L<PPI::Element> that the condition
is testing.
The anonymous function should return one of three values. Returning true
indicates a condition match, defined-false (C<0> or C<''>) indicates
no-match, and C<undef> indicates no-match and no-descend.
In the last case, the tree walker will skip over anything below the
C<undef>-returning element and move on to the next element at the same
level.
To halt the entire search and return C<undef> immediately, a condition
function should throw an exception (i.e. C<die>).
Note that this same wanted logic is used for all methods documented to
have a C<\&wanted> parameter, as this one does.
The C<find> method returns a reference to an array of L<PPI::Element>
objects that match the condition, false (but defined) if no Elements match
the condition, or C<undef> if you provide a bad condition, or an error
occurs during the search process.
In the case of a bad condition, a warning will be emitted as well.
=cut
sub find {
my $self = shift;
my $wanted = $self->_wanted(shift) or return undef;
# Use a queue based search, rather than a recursive one
my @found;
my @queue = @{$self->{children}};
my $ok = eval {
while ( @queue ) {
my $Element = shift @queue;
my $rv = &$wanted( $self, $Element );
push @found, $Element if $rv;
# Support "don't descend on undef return"
next unless defined $rv;
# Skip if the Element doesn't have any children
next unless $Element->isa('PPI::Node');
# Depth-first keeps the queue size down and provides a
# better logical order.
if ( $Element->isa('PPI::Structure') ) {
unshift @queue, $Element->finish if $Element->finish;
unshift @queue, @{$Element->{children}};
unshift @queue, $Element->start if $Element->start;
} else {
unshift @queue, @{$Element->{children}};
}
}
1;
};
if ( !$ok ) {
# Caught exception thrown from the wanted function
return undef;
}
@found ? \@found : '';
}
=pod
=head2 find_first $class | \&wanted
If the normal C<find> method is like a grep, then C<find_first> is
equivalent to the L<List::Util> C<first> function.
Given an element class or a wanted function, it will search depth-first
through a tree until it finds something that matches the condition,
returning the first Element that it encounters.
See the C<find> method for details on the format of the search condition.
Returns the first L<PPI::Element> object that matches the condition, false
if nothing matches the condition, or C<undef> if given an invalid condition,
or an error occurs.
=cut
sub find_first {
my $self = shift;
my $wanted = $self->_wanted(shift) or return undef;
# Use the same queue-based search as for ->find
my @queue = @{$self->{children}};
my $rv;
my $ok = eval {
# The defined() here prevents a ton of calls to PPI::Util::TRUE
while ( @queue ) {
my $Element = shift @queue;
my $element_rv = $wanted->( $self, $Element );
if ( $element_rv ) {
$rv = $Element;
last;
}
# Support "don't descend on undef return"
next if !defined $element_rv;
# Skip if the Element doesn't have any children
next if !$Element->isa('PPI::Node');
# Depth-first keeps the queue size down and provides a
# better logical order.
if ( $Element->isa('PPI::Structure') ) {
unshift @queue, $Element->finish if defined($Element->finish);
unshift @queue, @{$Element->{children}};
unshift @queue, $Element->start if defined($Element->start);
} else {
unshift @queue, @{$Element->{children}};
}
}
1;
};
if ( !$ok ) {
# Caught exception thrown from the wanted function
return undef;
}
$rv or '';
}
=pod
=head2 find_any $class | \&wanted
The C<find_any> method is a short-circuiting true/false method that behaves
like the normal C<find> method, but returns true as soon as it finds any
Elements that match the search condition.
See the C<find> method for details on the format of the search condition.
Returns true if any Elements that match the condition can be found, false if
not, or C<undef> if given an invalid condition, or an error occurs.
=cut
sub find_any {
my $self = shift;
my $rv = $self->find_first(@_);
$rv ? 1 : $rv; # false or undef
}
=pod
=head2 remove_child $Element
If passed a L<PPI::Element> object that is a direct child of the Node,
the C<remove_element> method will remove the C<Element> intact, along
with any of its children. As such, this method acts essentially as a
'cut' function.
If successful, returns the removed element. Otherwise, returns C<undef>.
=cut
sub remove_child {
my $self = shift;
my $child = _INSTANCE(shift, 'PPI::Element') or return undef;
# Find the position of the child
my $key = refaddr $child;
my $p = List::Util::first {
refaddr $self->{children}[$_] == $key
} 0..$#{$self->{children}};
return undef unless defined $p;
# Splice it out, and remove the child's parent entry
splice( @{$self->{children}}, $p, 1 );
delete $_PARENT{refaddr $child};
$child;
}
=pod
=head2 prune $class | \&wanted
The C<prune> method is used to strip L<PPI::Element> objects out of a code
tree. The argument is the same as for the C<find> method, either a class
name, or an anonymous subroutine which returns true/false. Any Element
that matches the class|wanted will be deleted from the code tree, along
with any of its children.
The C<prune> method returns the number of C<Element> objects that matched
and were removed, B<non-recursively>. This might also be zero, so avoid a
simple true/false test on the return false of the C<prune> method. It
returns C<undef> on error, which you probably B<should> test for.
=cut
sub prune {
my $self = shift;
my $wanted = $self->_wanted(shift) or return undef;
# Use a depth-first queue search
my $pruned = 0;
my @queue = $self->children;
my $ok = eval {
while ( my $element = shift @queue ) {
my $rv = &$wanted( $self, $element );
if ( $rv ) {
# Delete the child
$element->delete or return undef;
$pruned++;
next;
}
# Support the undef == "don't descend"
next unless defined $rv;
if ( _INSTANCE($element, 'PPI::Node') ) {
# Depth-first keeps the queue size down
unshift @queue, $element->children;
}
}
1;
};
if ( !$ok ) {
# Caught exception thrown from the wanted function
return undef;
}
$pruned;
}
# This method is likely to be very heavily used, so take
# it slowly and carefully.
### NOTE: Renaming this function or changing either to self will probably
### break File::Find::Rule::PPI
sub _wanted {
my $either = shift;
my $it = defined($_[0]) ? shift : do {
Carp::carp('Undefined value passed as search condition') if $^W;
return undef;
};
# Has the caller provided a wanted function directly
return $it if _CODELIKE($it);
if ( ref $it ) {
# No other ref types are supported
Carp::carp('Illegal non-CODE reference passed as search condition') if $^W;
return undef;
}
# The first argument should be an Element class, possibly in shorthand
$it = "PPI::$it" unless substr($it, 0, 5) eq 'PPI::';
unless ( _CLASS($it) and $it->isa('PPI::Element') ) {
# We got something, but it isn't an element
Carp::carp("Cannot create search condition for '$it': Not a PPI::Element") if $^W;
return undef;
}
# Create the class part of the wanted function
my $wanted_class = "\n\treturn '' unless \$_[1]->isa('$it');";
# Have we been given a second argument to check the content
my $wanted_content = '';
if ( defined $_[0] ) {
my $content = shift;
if ( ref $content eq 'Regexp' ) {
$content = "$content";
} elsif ( ref $content ) {
# No other ref types are supported
Carp::carp("Cannot create search condition for '$it': Not a PPI::Element") if $^W;
return undef;
} else {
$content = quotemeta $content;
}
# Complete the content part of the wanted function
$wanted_content .= "\n\treturn '' unless defined \$_[1]->{content};";
$wanted_content .= "\n\treturn '' unless \$_[1]->{content} =~ /$content/;";
}
# Create the complete wanted function
my $code = "sub {"
. $wanted_class
. $wanted_content
. "\n\t1;"
. "\n}";
# Compile the wanted function
$code = eval $code;
(ref $code eq 'CODE') ? $code : undef;
}
####################################################################
# PPI::Element overloaded methods
sub tokens {
map { $_->tokens } @{$_[0]->{children}};
}
### XS -> PPI/XS.xs:_PPI_Element__content 0.900+
sub content {
join '', map { $_->content } @{$_[0]->{children}};
}
# Clone as normal, but then go down and relink all the _PARENT entries
sub clone {
my $self = shift;
my $clone = $self->SUPER::clone;
$clone->__link_children;
$clone;
}
sub location {
my $self = shift;
my $first = $self->{children}->[0] or return undef;
$first->location;
}
#####################################################################
# Internal Methods
sub DESTROY {
local $_;
if ( $_[0]->{children} ) {
my @queue = $_[0];
while ( defined($_ = shift @queue) ) {
unshift @queue, @{delete $_->{children}} if $_->{children};
# Remove all internal/private weird crosslinking so that
# the cascading DESTROY calls will get called properly.
%$_ = ();
}
}
# Remove us from our parent node as normal
delete $_PARENT{refaddr $_[0]};
}
# Find the position of a child
sub __position {
my $key = refaddr $_[1];
List::Util::first { refaddr $_[0]{children}[$_] == $key } 0..$#{$_[0]{children}};
}
# Insert one or more elements before a child
sub __insert_before_child {
my $self = shift;
my $key = refaddr shift;
my $p = List::Util::first {
refaddr $self->{children}[$_] == $key
} 0..$#{$self->{children}};
foreach ( @_ ) {
Scalar::Util::weaken(
$_PARENT{refaddr $_} = $self
);
}
splice( @{$self->{children}}, $p, 0, @_ );
1;
}
# Insert one or more elements after a child
sub __insert_after_child {
my $self = shift;
my $key = refaddr shift;
my $p = List::Util::first {
refaddr $self->{children}[$_] == $key
} 0..$#{$self->{children}};
foreach ( @_ ) {
Scalar::Util::weaken(
$_PARENT{refaddr $_} = $self
);
}
splice( @{$self->{children}}, $p + 1, 0, @_ );
1;
}
# Replace a child
sub __replace_child {
my $self = shift;
my $key = refaddr shift;
my $p = List::Util::first {
refaddr $self->{children}[$_] == $key
} 0..$#{$self->{children}};
foreach ( @_ ) {
Scalar::Util::weaken(
$_PARENT{refaddr $_} = $self
);
}
splice( @{$self->{children}}, $p, 1, @_ );
1;
}
# Create PARENT links for an entire tree.
# Used when cloning or thawing.
sub __link_children {
my $self = shift;
# Relink all our children ( depth first )
my @queue = ( $self );
while ( my $Node = shift @queue ) {
# Link our immediate children
foreach my $Element ( @{$Node->{children}} ) {
Scalar::Util::weaken(
$_PARENT{refaddr($Element)} = $Node
);
unshift @queue, $Element if $Element->isa('PPI::Node');
}
# If it's a structure, relink the open/close braces
next unless $Node->isa('PPI::Structure');
Scalar::Util::weaken(
$_PARENT{refaddr($Node->start)} = $Node
) if $Node->start;
Scalar::Util::weaken(
$_PARENT{refaddr($Node->finish)} = $Node
) if $Node->finish;
}
1;
}
1;
=pod
=head1 TO DO
- Move as much as possible to L<PPI::XS>
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

257
database/perl/vendor/lib/PPI/Normal.pm vendored Normal file
View File

@@ -0,0 +1,257 @@
package PPI::Normal;
=pod
=head1 NAME
PPI::Normal - Normalize Perl Documents
=head2 DESCRIPTION
Perl Documents, as created by PPI, are typically filled with all sorts of
mess such as whitespace and comments and other things that don't effect
the actual meaning of the code.
In addition, because there is more than one way to do most things, and the
syntax of Perl itself is quite flexible, there are many ways in which the
"same" code can look quite different.
PPI::Normal attempts to resolve this by providing a variety of mechanisms
and algorithms to "normalize" Perl Documents, and determine a sort of base
form for them (although this base form will be a memory structure, and
not something that can be turned back into Perl source code).
The process itself is quite complex, and so for convenience and
extensibility it has been separated into a number of layers. At a later
point, it will be possible to write Plugin classes to insert additional
normalization steps into the various different layers.
In addition, you can choose to do the normalization only as deep as a
particular layer, depending on aggressively you want the normalization
process to be.
=head1 METHODS
=cut
use strict;
use Carp ();
use List::Util 1.33 ();
use PPI::Util '_Document';
use PPI::Document::Normalized ();
use PPI::Normal::Standard ();
use PPI::Singletons '%LAYER';
our $VERSION = '1.270'; # VERSION
# With the registration mechanism in place, load in the main set of
# normalization methods to initialize the store.
PPI::Normal::Standard->import;
#####################################################################
# Configuration
=pod
=head2 register $function => $layer, ...
The C<register> method is used by normalization method providers to
tell the normalization engines which functions need to be run, and
in which layer they apply.
Provide a set of key/value pairs, where the key is the full name of the
function (in string form), and the value is the layer (see description
of the layers above) in which it should be run.
Returns true if all functions are registered, or C<undef> on error.
=cut
sub register {
my $class = shift;
while ( @_ ) {
# Check the function
my $function = shift;
SCOPE: {
no strict 'refs';
defined $function and defined &{"$function"}
or Carp::croak("Bad function name provided to PPI::Normal");
}
# Has it already been added?
if ( List::Util::any { $_ eq $function } map @{$_}, values %LAYER ) {
return 1;
}
# Check the layer to add it to
my $layer = shift;
defined $layer and $layer =~ /^(?:1|2)$/
or Carp::croak("Bad layer provided to PPI::Normal");
# Add to the layer data store
push @{ $LAYER{$layer} }, $function;
}
1;
}
#####################################################################
# Constructor and Accessors
=pod
=head2 new
my $level_1 = PPI::Normal->new;
my $level_2 = PPI::Normal->new(2);
Creates a new normalization object, to which Document objects
can be passed to be normalized.
Of course, what you probably REALLY want is just to call
L<PPI::Document>'s C<normalize> method.
Takes an optional single parameter of the normalisation layer
to use, which at this time can be either "1" or "2".
Returns a new C<PPI::Normal> object, or C<undef> on error.
=cut
sub new {
my $class = shift;
my $layer = @_ ?
(defined $_[0] and ! ref $_[0] and $_[0] =~ /^[12]$/) ? shift : return undef
: 1;
# Create the object
my $object = bless {
layer => $layer,
}, $class;
$object;
}
=pod
=head1 layer
The C<layer> accessor returns the normalisation layer of the object.
=cut
sub layer { $_[0]->{layer} }
#####################################################################
# Main Methods
=pod
=head2 process
The C<process> method takes anything that can be converted to a
L<PPI::Document> (object, SCALAR ref, filename), loads it and
applies the normalisation process to the document.
Returns a L<PPI::Document::Normalized> object, or C<undef> on error.
=cut
sub process {
my $self = ref $_[0] ? shift : shift->new;
# PPI::Normal objects are reusable, but not re-entrant
return undef if $self->{Document};
# Get or create the document
$self->{Document} = _Document(shift) or return undef;
# Work out what functions we need to call
my @functions = map { @{ $LAYER{$_} } } ( 1 .. $self->layer );
# Execute each function
foreach my $function ( @functions ) {
no strict 'refs';
&{"$function"}( $self->{Document} );
}
# Create the normalized Document object
my $Normalized = PPI::Document::Normalized->new(
Document => $self->{Document},
version => __PACKAGE__->VERSION,
functions => \@functions,
) or return undef;
# Done, clean up
delete $self->{Document};
return $Normalized;
}
1;
=pod
=head1 NOTES
The following normalisation layers are implemented. When writing
plugins, you should register each transformation function with the
appropriate layer.
=head2 Layer 1 - Insignificant Data Removal
The basic step common to all normalization, layer 1 scans through the
Document and removes all whitespace, comments, POD, and anything else
that returns false for its C<significant> method.
It also checks each Element and removes known-useless sub-element
metadata such as the Element's physical position in the file.
=head2 Layer 2 - Significant Element Removal
After the removal of the insignificant data, Layer 2 removed larger, more
complex, and superficially "significant" elements, that can be removed
for the purposes of normalisation.
Examples from this layer include pragmas, now-useless statement
separators (since the PDOM tree is holding statement elements), and
several other minor bits and pieces.
=head2 Layer 3 - TO BE COMPLETED
This version of the forward-port of the Perl::Compare functionality
to the 0.900+ API of PPI only implements Layer 1 and 2 at this time.
=head1 TO DO
- Write the other 4-5 layers :)
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2005 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,139 @@
package PPI::Normal::Standard;
=pod
=head1 NAME
PPI::Normal::Standard - Provides standard document normalization functions
=head1 DESCRIPTION
This module provides the default normalization methods for L<PPI::Normal>.
There is no reason for you to need to load this yourself.
B<Move along, nothing to see here>.
=cut
use strict;
our $VERSION = '1.270'; # VERSION
#####################################################################
# Configuration and Registration
my @METHODS = (
remove_insignificant_elements => 1,
remove_useless_attributes => 1,
remove_useless_pragma => 2,
remove_statement_separator => 2,
remove_useless_return => 2,
);
sub import {
PPI::Normal->register(
map { /\D/ ? "PPI::Normal::Standard::$_" : $_ } @METHODS
) or die "Failed to register PPI::Normal::Standard transforms";
}
#####################################################################
# Level 1 Transforms
# Remove all insignificant elements
sub remove_insignificant_elements {
my $Document = shift;
$Document->prune( sub { ! $_[1]->significant } );
}
# Remove custom attributes that are not relevant to normalization
sub remove_useless_attributes {
my $Document = shift;
delete $Document->{tab_width};
### FIXME - Add support for more things
}
#####################################################################
# Level 2 Transforms
# Remove version dependencies and pragma
my $remove_pragma = map { $_ => 1 } qw{
strict warnings diagnostics less
};
sub remove_useless_pragma {
my $Document = shift;
$Document->prune( sub {
return '' unless $_[1]->isa('PPI::Statement::Include');
return 1 if $_[1]->version;
return 1 if $remove_pragma->{$_[1]->pragma};
'';
} );
}
# Remove all semi-colons at the end of statements
sub remove_statement_separator {
my $Document = shift;
$Document->prune( sub {
$_[1]->isa('PPI::Token::Structure') or return '';
$_[1]->content eq ';' or return '';
my $stmt = $_[1]->parent or return '';
$stmt->isa('PPI::Statement') or return '';
$_[1]->next_sibling and return '';
1;
} );
}
# In any block, the "return" in the last statement is not
# needed if there is only one and only one thing after the
# return.
sub remove_useless_return {
my $Document = shift;
$Document->prune( sub {
$_[1]->isa('PPI::Token::Word') or return '';
$_[1]->content eq 'return' or return '';
my $stmt = $_[1]->parent or return '';
$stmt->isa('PPI::Statement::Break') or return '';
$stmt->children == 2 or return '';
$stmt->next_sibling and return '';
my $block = $stmt->parent or return '';
$block->isa('PPI::Structure::Block') or return '';
1;
} );
}
1;
=pod
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2005 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,91 @@
package PPI::Singletons;
# exports some singleton variables to avoid aliasing magic
use strict;
use Exporter ();
our $VERSION = '1.270'; # VERSION
our @ISA = 'Exporter';
our @EXPORT_OK = qw{ %_PARENT %OPERATOR %MAGIC %LAYER $CURLY_SYMBOL %QUOTELIKE %KEYWORDS };
our %_PARENT; # Master Child -> Parent index
# operator index
our %OPERATOR = map { $_ => 1 } (
qw{
-> ++ -- ** ! ~ + -
=~ !~ * / % x . << >>
< > <= >= lt gt le ge
== != <=> eq ne cmp ~~
& | ^ && || // .. ...
? :
= **= += -= .= *= /= %= x= &= |= ^= <<= >>= &&= ||= //=
=> <> <<>>
and or xor not
}, ',' # Avoids "comma in qw{}" warning
);
# Magic variables taken from perlvar.
# Several things added separately to avoid warnings.
our %MAGIC = map { $_ => 1 } qw{
$1 $2 $3 $4 $5 $6 $7 $8 $9
$_ $& $` $' $+ @+ %+ $* $. $/ $|
$\\ $" $; $% $= $- @- %- $)
$~ $^ $: $? $! %! $@ $$ $< $>
$( $0 $[ $] @_ @*
$^L $^A $^E $^C $^D $^F $^H
$^I $^M $^N $^O $^P $^R $^S
$^T $^V $^W $^X %^H
$::|
}, '$}', '$,', '$#', '$#+', '$#-';
our %LAYER = ( 1 => [], 2 => [] ); # Registered function store
our $CURLY_SYMBOL = qr{\G\^[[:upper:]_]\w+\}};
our %QUOTELIKE = (
'q' => 'Quote::Literal',
'qq' => 'Quote::Interpolate',
'qx' => 'QuoteLike::Command',
'qw' => 'QuoteLike::Words',
'qr' => 'QuoteLike::Regexp',
'm' => 'Regexp::Match',
's' => 'Regexp::Substitute',
'tr' => 'Regexp::Transliterate',
'y' => 'Regexp::Transliterate',
);
# List of keywords is from regen/keywords.pl in the perl source.
our %KEYWORDS = map { $_ => 1 } qw{
abs accept alarm and atan2 bind binmode bless break caller chdir chmod
chomp chop chown chr chroot close closedir cmp connect continue cos
crypt dbmclose dbmopen default defined delete die do dump each else
elsif endgrent endhostent endnetent endprotoent endpwent endservent
eof eq eval evalbytes exec exists exit exp fc fcntl fileno flock for
foreach fork format formline ge getc getgrent getgrgid getgrnam
gethostbyaddr gethostbyname gethostent getlogin getnetbyaddr
getnetbyname getnetent getpeername getpgrp getppid getpriority
getprotobyname getprotobynumber getprotoent getpwent getpwnam
getpwuid getservbyname getservbyport getservent getsockname
getsockopt given glob gmtime goto grep gt hex if index int ioctl join
keys kill last lc lcfirst le length link listen local localtime lock
log lstat lt m map mkdir msgctl msgget msgrcv msgsnd my ne next no
not oct open opendir or ord our pack package pipe pop pos print
printf prototype push q qq qr quotemeta qw qx rand read readdir
readline readlink readpipe recv redo ref rename require reset return
reverse rewinddir rindex rmdir s say scalar seek seekdir select semctl
semget semop send setgrent sethostent setnetent setpgrp
setpriority setprotoent setpwent setservent setsockopt shift shmctl
shmget shmread shmwrite shutdown sin sleep socket socketpair sort
splice split sprintf sqrt srand stat state study sub substr symlink
syscall sysopen sysread sysseek system syswrite tell telldir tie tied
time times tr truncate uc ucfirst umask undef unless unlink unpack
unshift untie until use utime values vec wait waitpid wantarray warn
when while write x xor y
};
1;

View File

@@ -0,0 +1,344 @@
package PPI::Statement;
=pod
=head1 NAME
PPI::Statement - The base class for Perl statements
=head1 INHERITANCE
PPI::Statement
isa PPI::Node
isa PPI::Element
=head1 DESCRIPTION
PPI::Statement is the root class for all Perl statements. This includes (from
L<perlsyn>) "Declarations", "Simple Statements" and "Compound Statements".
The class PPI::Statement itself represents a "Simple Statement" as defined
in the L<perlsyn> manpage.
=head1 STATEMENT CLASSES
Please note that unless documented themselves, these classes are yet to be
frozen/finalised. Names may change slightly or be added or removed.
=head2 L<PPI::Statement::Scheduled>
This covers all "scheduled" blocks, chunks of code that are executed separately
from the main body of the code, at a particular time. This includes all
C<BEGIN>, C<CHECK>, C<UNITCHECK>, C<INIT> and C<END> blocks.
=head2 L<PPI::Statement::Package>
A package declaration, as defined in L<perlfunc|perlfunc/package>.
=head2 L<PPI::Statement::Include>
A statement that loads or unloads another module.
This includes 'use', 'no', and 'require' statements.
=head2 L<PPI::Statement::Sub>
A named subroutine declaration, or forward declaration
=head2 L<PPI::Statement::Variable>
A variable declaration statement. This could be either a straight
declaration or also be an expression.
This includes all 'my', 'state', 'local' and 'our' statements.
=head2 L<PPI::Statement::Compound>
This covers the whole family of 'compound' statements, as described in
L<perlsyn|perlsyn>.
This includes all statements starting with 'if', 'unless', 'for', 'foreach'
and 'while'. Note that this does NOT include 'do', as it is treated
differently.
All compound statements have implicit ends. That is, they do not end with
a ';' statement terminator.
=head2 L<PPI::Statement::Break>
A statement that breaks out of a structure.
This includes all of 'redo', 'goto', 'next', 'last' and 'return' statements.
=head2 L<PPI::Statement::Given>
The kind of statement introduced in Perl 5.10 that starts with 'given'. This
has an implicit end.
=head2 L<PPI::Statement::When>
The kind of statement introduced in Perl 5.10 that starts with 'when' or
'default'. This also has an implicit end.
=head2 L<PPI::Statement::Data>
A special statement which encompasses an entire C<__DATA__> block, including
the initial C<'__DATA__'> token itself and the entire contents.
=head2 L<PPI::Statement::End>
A special statement which encompasses an entire __END__ block, including
the initial '__END__' token itself and the entire contents, including any
parsed PPI::Token::POD that may occur in it.
=head2 L<PPI::Statement::Expression>
L<PPI::Statement::Expression> is a little more speculative, and is intended
to help represent the special rules relating to "expressions" such as in:
# Several examples of expression statements
# Boolean conditions
if ( expression ) { ... }
# Lists, such as for arguments
Foo->bar( expression )
=head2 L<PPI::Statement::Null>
A null statement is a special case for where we encounter two consecutive
statement terminators. ( ;; )
The second terminator is given an entire statement of its own, but one
that serves no purpose. Hence a 'null' statement.
Theoretically, assuming a correct parsing of a perl file, all null statements
are superfluous and should be able to be removed without damage to the file.
But don't do that, in case PPI has parsed something wrong.
=head2 L<PPI::Statement::UnmatchedBrace>
Because L<PPI> is intended for use when parsing incorrect or incomplete code,
the problem arises of what to do with a stray closing brace.
Rather than die, it is allocated its own "unmatched brace" statement,
which really means "unmatched closing brace". An unmatched open brace at the
end of a file would become a structure with no contents and no closing brace.
If the document loaded is intended to be correct and valid, finding a
L<PPI::Statement::UnmatchedBrace> in the PDOM is generally indicative of a
misparse.
=head2 L<PPI::Statement::Unknown>
This is used temporarily mid-parsing to hold statements for which the lexer
cannot yet determine what class it should be, usually because there are
insufficient clues, or it might be more than one thing.
You should never encounter these in a fully parsed PDOM tree.
=head1 METHODS
C<PPI::Statement> itself has very few methods. Most of the time, you will be
working with the more generic L<PPI::Element> or L<PPI::Node> methods, or one
of the methods that are subclass-specific.
=cut
use strict;
use Scalar::Util ();
use Params::Util qw{_INSTANCE};
use PPI::Node ();
use PPI::Exception ();
use PPI::Singletons '%_PARENT';
our $VERSION = '1.270'; # VERSION
our @ISA = "PPI::Node";
use PPI::Statement::Break ();
use PPI::Statement::Compound ();
use PPI::Statement::Data ();
use PPI::Statement::End ();
use PPI::Statement::Expression ();
use PPI::Statement::Include ();
use PPI::Statement::Null ();
use PPI::Statement::Package ();
use PPI::Statement::Scheduled ();
use PPI::Statement::Sub ();
use PPI::Statement::Given ();
use PPI::Statement::UnmatchedBrace ();
use PPI::Statement::Unknown ();
use PPI::Statement::Variable ();
use PPI::Statement::When ();
# "Normal" statements end at a statement terminator ;
# Some are not, and need the more rigorous _continues to see
# if we are at an implicit statement boundary.
sub __LEXER__normal() { 1 }
#####################################################################
# Constructor
sub new {
my $class = shift;
if ( ref $class ) {
PPI::Exception->throw;
}
# Create the object
my $self = bless {
children => [],
}, $class;
# If we have been passed what should be an initial token, add it
my $token = shift;
if ( _INSTANCE($token, 'PPI::Token') ) {
# Inlined $self->__add_element(shift);
Scalar::Util::weaken(
$_PARENT{Scalar::Util::refaddr $token} = $self
);
push @{$self->{children}}, $token;
}
$self;
}
=pod
=head2 label
One factor common to most statements is their ability to be labeled.
The C<label> method returns the label for a statement, if one has been
defined, but without the trailing colon. Take the following example
MYLABEL: while ( 1 .. 10 ) { last MYLABEL if $_ > 5 }
For the above statement, the C<label> method would return 'MYLABEL'.
Returns false if the statement does not have a label.
=cut
sub label {
my $first = shift->schild(1) or return '';
$first->isa('PPI::Token::Label')
? substr($first, 0, length($first) - 1)
: '';
}
=pod
=head2 specialized
Answer whether this is a plain statement or one that has more
significance.
Returns true if the statement is a subclass of this one, false
otherwise.
=cut
# Yes, this is doing precisely what it's intending to prevent
# client code from doing. However, since it's here, if the
# implementation changes, code outside PPI doesn't care.
sub specialized {
__PACKAGE__ ne ref $_[0];
}
=pod
=head2 stable
Much like the L<PPI::Document> method of the same name, the ->stable
method converts a statement to source and back again, to determine if
a modified statement is still legal, and won't be interpreted in a
different way.
Returns true if the statement is stable, false if not, or C<undef> on
error.
=cut
sub stable {
die "The ->stable method has not yet been implemented";
}
#####################################################################
# PPI::Element Methods
# Is the statement complete.
# By default for a statement, we need a semi-colon at the end.
sub _complete {
my $self = shift;
my $semi = $self->schild(-1);
return !! (
defined $semi
and
$semi->isa('PPI::Token::Structure')
and
$semi->content eq ';'
);
}
# You can insert either a statement or a non-significant token.
sub insert_before {
my $self = shift;
my $Element = _INSTANCE(shift, 'PPI::Element') or return undef;
if ( $Element->isa('PPI::Statement') ) {
return $self->__insert_before($Element);
} elsif ( $Element->isa('PPI::Token') and ! $Element->significant ) {
return $self->__insert_before($Element);
}
'';
}
# As above, you can insert a statement, or a non-significant token
sub insert_after {
my $self = shift;
my $Element = _INSTANCE(shift, 'PPI::Element') or return undef;
if ( $Element->isa('PPI::Statement') ) {
return $self->__insert_after($Element);
} elsif ( $Element->isa('PPI::Token') and ! $Element->significant ) {
return $self->__insert_after($Element);
}
'';
}
1;
=pod
=head1 TO DO
- Complete, freeze and document the remaining classes
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,75 @@
package PPI::Statement::Break;
=pod
=head1 NAME
PPI::Statement::Break - Statements which break out of normal statement flow
=head1 SYNOPSIS
last;
goto FOO;
next if condition();
return $foo;
redo;
=head1 INHERITANCE
PPI::Statement::Break
isa PPI::Statement
isa PPI::Node
isa PPI::Element
=head1 DESCRIPTION
C<PPI::Statement::Break> is intended to represent statements that break
out of the normal statement flow control. This covers the basic
types C<'redo'>, C<'goto'>, C<'next'>, C<'last'> and C<'return'>.
=head1 METHODS
C<PPI::Statement::Break> has no additional methods beyond the default ones
provided by L<PPI::Statement>, L<PPI::Node> and L<PPI::Element>.
However, it is expected to gain methods for identifying the line to break
to, or the structure to break out of.
=cut
use strict;
use PPI::Statement ();
our $VERSION = '1.270'; # VERSION
our @ISA = "PPI::Statement";
1;
=pod
=head1 TO DO
- Add the methods to identify the break target
- Add some proper unit testing
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,201 @@
package PPI::Statement::Compound;
=pod
=head1 NAME
PPI::Statement::Compound - Describes all compound statements
=head1 SYNOPSIS
# A compound if statement
if ( foo ) {
bar();
} else {
baz();
}
# A compound loop statement
foreach ( @list ) {
bar($_);
}
=head1 INHERITANCE
PPI::Statement::Compound
isa PPI::Statement
isa PPI::Node
isa PPI::Element
=head1 DESCRIPTION
C<PPI::Statement::Compound> objects are used to describe all current forms
of compound statements, as described in L<perlsyn>.
This covers blocks using C<if>, C<unless>, C<for>, C<foreach>, C<while>,
and C<continue>. Please note this does B<not> cover "simple" statements
with trailing conditions. Please note also that "do" is also not part of
a compound statement.
# This is NOT a compound statement
my $foo = 1 if $condition;
# This is also not a compound statement
do { ... } until $condition;
=head1 METHODS
C<PPI::Statement::Compound> has a number of methods in addition to the
standard L<PPI::Statement>, L<PPI::Node> and L<PPI::Element> methods.
=cut
use strict;
use PPI::Statement ();
our $VERSION = '1.270'; # VERSION
our @ISA = "PPI::Statement";
# Keyword type map
my %TYPES = (
'if' => 'if',
'unless' => 'if',
'while' => 'while',
'until' => 'while',
'for' => 'for',
'foreach' => 'foreach',
);
# Lexer clues
sub __LEXER__normal() { '' }
#####################################################################
# PPI::Statement::Compound analysis methods
=pod
=head2 type
The C<type> method returns the syntactic type of the compound statement.
There are four basic compound statement types.
The C<'if'> type includes all variations of the if and unless statements,
including any C<'elsif'> or C<'else'> parts of the compound statement.
The C<'while'> type describes the standard while and until statements, but
again does B<not> describes simple statements with a trailing while.
The C<'for'> type covers the C-style for loops, regardless of whether they
were declared using C<'for'> or C<'foreach'>.
The C<'foreach'> type covers loops that iterate over collections,
regardless of whether they were declared using C<'for'> or C<'foreach'>.
All of the compounds are a variation on one of these four.
Returns the simple string C<'if'>, C<'for'>, C<'foreach'> or C<'while'>,
or C<undef> if the type cannot be determined.
=cut
sub type {
my $self = shift;
my $p = 0; # Child position
my $Element = $self->schild($p) or return undef;
# A labelled statement
if ( $Element->isa('PPI::Token::Label') ) {
$Element = $self->schild(++$p) or return 'label';
}
# Most simple cases
my $content = $Element->content;
if ( $content =~ /^for(?:each)?\z/ ) {
$Element = $self->schild(++$p) or return $content;
if ( $Element->isa('PPI::Token') ) {
return 'foreach' if $Element->content =~ /^my|our|state\z/;
return 'foreach' if $Element->isa('PPI::Token::Symbol');
return 'foreach' if $Element->isa('PPI::Token::QuoteLike::Words');
}
if ( $Element->isa('PPI::Structure::List') ) {
return 'foreach';
}
return 'for';
}
return $TYPES{$content} if $Element->isa('PPI::Token::Word');
return 'continue' if $Element->isa('PPI::Structure::Block');
# Unknown (shouldn't exist?)
undef;
}
#####################################################################
# PPI::Node Methods
sub scope() { 1 }
#####################################################################
# PPI::Element Methods
sub _complete {
my $self = shift;
my $type = $self->type or die "Illegal compound statement type";
# Check the different types of compound statements
if ( $type eq 'if' ) {
# Unless the last significant child is a complete
# block, it must be incomplete.
my $child = $self->schild(-1) or return '';
$child->isa('PPI::Structure') or return '';
$child->braces eq '{}' or return '';
$child->_complete or return '';
# It can STILL be
} elsif ( $type eq 'while' ) {
die "CODE INCOMPLETE";
} else {
die "CODE INCOMPLETE";
}
}
1;
=pod
=head1 TO DO
- Write unit tests for this package
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,81 @@
package PPI::Statement::Data;
=pod
=head1 NAME
PPI::Statement::Data - The __DATA__ section of a file
=head1 SYNOPSIS
# Normal content
__DATA__
This: data
is: part
of: the
PPI::Statement::Data: object
=head1 INHERITANCE
PPI::Statement::Compound
isa PPI::Statement
isa PPI::Node
isa PPI::Element
=head1 DESCRIPTION
C<PPI::Statement::Data> is a utility class designed to hold content in
the __DATA__ section of a file. It provides a single statement to hold
B<all> of the data.
=head1 METHODS
C<PPI::Statement::Data> has no additional methods beyond the default ones
provided by L<PPI::Statement>, L<PPI::Node> and L<PPI::Element>.
However, it is expected to gain methods for accessing the data directly,
(as a filehandle for example) just as you would access the data in the
Perl code itself.
=cut
use strict;
use PPI::Statement ();
our $VERSION = '1.270'; # VERSION
our @ISA = "PPI::Statement";
# Data is never complete
sub _complete () { '' }
1;
=pod
=head1 TO DO
- Add the methods to read in the data
- Add some proper unit testing
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,79 @@
package PPI::Statement::End;
=pod
=head1 NAME
PPI::Statement::End - Content after the __END__ of a module
=head1 SYNOPSIS
# This is normal content
__END__
This is part of a PPI::Statement::End statement
=pod
This is not part of the ::End statement, it's POD
=cut
This is another PPI::Statement::End statement
=head1 INHERITANCE
PPI::Statement::End
isa PPI::Statement
isa PPI::Node
isa PPI::Element
=head1 DESCRIPTION
C<PPI::Statement::End> is a utility class designed to serve as a contained
for all of the content after the __END__ tag in a file.
It doesn't cover the ENTIRE of the __END__ section, and can be interspersed
with L<PPI::Token::Pod> tokens.
=head1 METHODS
C<PPI::Statement::End> has no additional methods beyond the default ones
provided by L<PPI::Statement>, L<PPI::Node> and L<PPI::Element>.
=cut
use strict;
use PPI::Statement ();
our $VERSION = '1.270'; # VERSION
our @ISA = "PPI::Statement";
# Once we have an __END__ we're done
sub _complete () { 1 }
1;
=pod
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,67 @@
package PPI::Statement::Expression;
=pod
=head1 NAME
PPI::Statement::Expression - A generic and non-specialised statement
=head1 SYNOPSIS
$foo = bar;
("Hello World!");
do_this();
=head1 INHERITANCE
PPI::Statement::Expression
isa PPI::Statement
isa PPI::Node
isa PPI::Element
=head1 DESCRIPTION
A C<PPI::Statement::Expression> is a normal statement that is evaluated,
may or may not assign, may or may not have side effects, and has no special
or redeeming features whatsoever.
It provides a default for all statements that don't fit into any other
classes.
=head1 METHODS
C<PPI::Statement::Expression> has no additional methods beyond the default ones
provided by L<PPI::Statement>, L<PPI::Node> and L<PPI::Element>.
=cut
use strict;
use PPI::Statement ();
our $VERSION = '1.270'; # VERSION
our @ISA = "PPI::Statement";
1;
=pod
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,90 @@
package PPI::Statement::Given;
=pod
=head1 NAME
PPI::Statement::Given - A given-when statement
=head1 SYNOPSIS
given ( foo ) {
say $_;
}
=head1 INHERITANCE
PPI::Statement::Given
isa PPI::Statement
isa PPI::Node
isa PPI::Element
=head1 DESCRIPTION
C<PPI::Statement::Given> objects are used to describe switch statements, as
described in L<perlsyn>.
=head1 METHODS
C<PPI::Statement::Given> has no methods beyond those provided by the
standard L<PPI::Structure>, L<PPI::Node> and L<PPI::Element> methods.
=cut
use strict;
use PPI::Statement ();
our $VERSION = '1.270'; # VERSION
our @ISA = "PPI::Statement";
# Lexer clues
sub __LEXER__normal() { '' }
sub _complete {
my $child = $_[0]->schild(-1);
return !! (
defined $child
and
$child->isa('PPI::Structure::Block')
and
$child->complete
);
}
#####################################################################
# PPI::Node Methods
sub scope() { 1 }
1;
=pod
=head1 TO DO
- Write unit tests for this package
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,265 @@
package PPI::Statement::Include;
=pod
=head1 NAME
PPI::Statement::Include - Statements that include other code
=head1 SYNOPSIS
# The following are all includes
use 5.006;
use strict;
use My::Module;
use constant FOO => 'Foo';
require Foo::Bar;
require "Foo/Bar.pm";
require $foo if 1;
no strict 'refs';
=head1 INHERITANCE
PPI::Statement::Include
isa PPI::Statement
isa PPI::Node
isa PPI::Element
=head1 DESCRIPTION
Despite its name, the C<PPI::Statement::Include> class covers a number
of different types of statement that cover all statements starting with
C<use>, C<no> and C<require>.
But basically, they cover three situations.
Firstly, a dependency on a particular version of perl (for which the
C<version> method returns true), a pragma (for which the C<pragma> method
returns true), or the loading (and unloading via no) of modules.
=head1 METHODS
C<PPI::Statement::Include> has a number of methods in addition to the standard
L<PPI::Statement>, L<PPI::Node> and L<PPI::Element> methods.
=cut
use strict;
use PPI::Statement ();
use PPI::Statement::Include::Perl6 ();
our $VERSION = '1.270'; # VERSION
our @ISA = "PPI::Statement";
=pod
=head2 type
The C<type> method returns the general type of statement (C<'use'>, C<'no'>
or C<'require'>).
Returns the type as a string, or C<undef> if the type cannot be detected.
=cut
sub type {
my $self = shift;
my $keyword = $self->schild(0) or return undef;
$keyword->isa('PPI::Token::Word') and $keyword->content;
}
=pod
=head2 module
The C<module> method returns the module name specified in any include
statement. This C<includes> pragma names, because pragma are implemented
as modules. (And lets face it, the definition of a pragma can be fuzzy
at the best of times in any case)
This covers all of these...
use strict;
use My::Module;
no strict;
require My::Module;
...but does not cover any of these...
use 5.006;
require 5.005;
require "explicit/file/name.pl";
Returns the module name as a string, or C<undef> if the include does
not specify a module name.
=cut
sub module {
my $self = shift;
my $module = $self->schild(1) or return undef;
$module->isa('PPI::Token::Word') and $module->content;
}
=pod
=head2 module_version
The C<module_version> method returns the minimum version of the module
required by the statement, if there is one.
=cut
sub module_version {
my $self = shift;
my $argument = $self->schild(3);
if ( $argument and $argument->isa('PPI::Token::Operator') ) {
return undef;
}
my $version = $self->schild(2) or return undef;
return undef unless $version->isa('PPI::Token::Number');
return $version;
}
=pod
=head2 pragma
The C<pragma> method checks for an include statement's use as a
pragma, and returns it if so.
Or at least, it claims to. In practice it's a lot harder to say exactly
what is or isn't a pragma, because the definition is fuzzy.
The C<intent> of a pragma is to modify the way in which the parser works.
This is done though the use of modules that do various types of internals
magic.
For now, PPI assumes that any "module name" that is only a set of
lowercase letters (and perhaps numbers, like C<use utf8;>). This
behaviour is expected to change, most likely to something that knows
the specific names of the various "pragmas".
Returns the name of the pragma, or false ('') if the include is not a
pragma.
=cut
sub pragma {
my $self = shift;
my $module = $self->module or return '';
$module =~ /^[a-z][a-z\d]*$/ ? $module : '';
}
=pod
=head2 version
The C<version> method checks for an include statement that introduces a
dependency on the version of C<perl> the code is compatible with.
This covers two specific statements.
use 5.006;
require 5.006;
Currently the version is returned as a string, although in future the version
may be returned as a L<version> object. If you want a numeric representation,
use C<version_literal()>. Returns false if the statement is not a version
dependency.
=cut
sub version {
my $self = shift;
my $version = $self->schild(1) or return undef;
$version->isa('PPI::Token::Number') ? $version->content : '';
}
=pod
=head2 version_literal
The C<version_literal> method has the same behavior as C<version()>, but the
version is returned as a numeric literal. Returns false if the statement is
not a version dependency.
=cut
sub version_literal {
my $self = shift;
my $version = $self->schild(1) or return undef;
$version->isa('PPI::Token::Number') ? $version->literal : '';
}
=pod
=head2 arguments
The C<arguments> method gives you the rest of the statement after the
module/pragma and module version, i.e. the stuff that will be used to
construct what gets passed to the module's C<import()> subroutine. This does
include the comma, etc. operators, but doesn't include non-significant direct
children or any final semicolon.
=cut
sub arguments {
my $self = shift;
my @args = $self->schildren;
# Remove the "use", "no" or "require"
shift @args;
# Remove the statement terminator
if (
$args[-1]->isa('PPI::Token::Structure')
and
$args[-1]->content eq ';'
) {
pop @args;
}
# Remove the module or perl version.
shift @args;
return unless @args;
if ( $args[0]->isa('PPI::Token::Number') ) {
my $after = $args[1] or return;
$after->isa('PPI::Token::Operator') or shift @args;
}
return @args;
}
1;
=pod
=head1 TO DO
- Write specific unit tests for this package
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,87 @@
package PPI::Statement::Include::Perl6;
=pod
=head1 NAME
PPI::Statement::Include::Perl6 - Inline Perl 6 file section
=head1 SYNOPSIS
use v6-alpha;
grammar My::Grammar {
...
}
=head1 INHERITANCE
PPI::Statement::Include::Perl6
isa PPI::Statement::Include
isa PPI::Statement
isa PPI::Node
isa PPI::Element
=head1 DESCRIPTION
A C<PPI::Statement::Include::Perl6> is a special include statement that
indicates the start of a section of Perl 6 code inlined into a regular
Perl 5 code file.
The primary purpose of the class is to allow L<PPI> to provide at least
basic support for "6 in 5" modules like v6.pm;
Currently, PPI only supports starting a Perl 6 block. It does not
currently support changing back to Perl 5 again. Additionally all POD
and __DATA__ blocks and __END__ blocks will be included in the Perl 6
string and will not be parsed by PPI.
=cut
use strict;
use PPI::Statement::Include ();
our $VERSION = '1.270'; # VERSION
our @ISA = "PPI::Statement::Include";
=pod
=head2 perl6
The C<perl6> method returns the block of Perl 6 code that is attached to
the "use v6...;" command.
=cut
sub perl6 {
$_[0]->{perl6};
}
1;
=pod
=head1 TO DO
- Write specific unit tests for this package
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,75 @@
package PPI::Statement::Null;
=pod
=head1 NAME
PPI::Statement::Null - A useless null statement
=head1 SYNOPSIS
my $foo = 1;
; # <-- Null statement
my $bar = 1;
=head1 INHERITANCE
PPI::Statement::Null
isa PPI::Statement
isa PPI::Node
isa PPI::Element
=head1 DESCRIPTION
C<PPI::Statement::Null> is a utility class designed to handle situations
where PPI encounters a naked statement separator.
Although strictly speaking, the semicolon is a statement B<separator>
and not a statement B<terminator>, PPI considers a semicolon to be a
statement terminator under most circumstances.
In any case, the null statement has no purpose, and can be safely deleted
with no ill effect.
=head1 METHODS
C<PPI::Statement::Null> has no additional methods beyond the default ones
provided by L<PPI::Statement>, L<PPI::Node> and L<PPI::Element>.
=cut
use strict;
use PPI::Statement ();
our $VERSION = '1.270'; # VERSION
our @ISA = "PPI::Statement";
# A null statement is not significant
sub significant() { '' }
1;
=pod
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,145 @@
package PPI::Statement::Package;
=pod
=head1 NAME
PPI::Statement::Package - A package statement
=head1 INHERITANCE
PPI::Statement::Package
isa PPI::Statement
isa PPI::Node
isa PPI::Element
=head1 DESCRIPTION
Most L<PPI::Statement> subclasses are assigned based on the value of the
first token or word found in the statement. When PPI encounters a statement
starting with 'package', it converts it to a C<PPI::Statement::Package>
object.
When working with package statements, please remember that packages only
exist within their scope, and proper support for scoping has yet to be
completed in PPI.
However, if the immediate parent of the package statement is the
top level L<PPI::Document> object, then it can be considered to define
everything found until the next top-level "file scoped" package statement.
A file may, however, contain nested temporary package, in which case you
are mostly on your own :)
=head1 METHODS
C<PPI::Statement::Package> has a number of methods in addition to the standard
L<PPI::Statement>, L<PPI::Node> and L<PPI::Element> methods.
=cut
use strict;
use PPI::Statement ();
our $VERSION = '1.270'; # VERSION
our @ISA = "PPI::Statement";
# Lexer clues
sub __LEXER__normal() { '' }
=pod
=head2 namespace
Most package declarations are simple, and just look something like
package Foo::Bar;
The C<namespace> method returns the name of the declared package, in the
above case 'Foo::Bar'. It returns this exactly as written and does not
attempt to clean up or resolve things like ::Foo to main::Foo.
If the package statement is done any different way, it returns false.
=cut
sub namespace {
my $self = shift;
my $namespace = $self->schild(1) or return '';
$namespace->isa('PPI::Token::Word')
? $namespace->content
: '';
}
=pod
=head2 version
Some package declarations may include a version:
package Foo::Bar 1.23;
package Baz v1.23;
The C<version> method returns the stringified version as seen in the
document (if any), otherwise the empty string.
=cut
sub version {
my $self = shift;
my $version = $self->schild(2) or return '';
$version->isa('PPI::Token::Structure')
? ''
: $version->content;
}
=pod
=head2 file_scoped
Regardless of whether it is named or not, the C<file_scoped> method will
test to see if the package declaration is a top level "file scoped"
statement or not, based on its location.
In general, returns true if it is a "file scoped" package declaration with
an immediate parent of the top level Document, or false if not.
Note that if the PPI DOM tree B<does not> have a PPI::Document object at
as the root element, this will return false. Likewise, it will also return
false if the root element is a L<PPI::Document::Fragment>, as a fragment of
a file does not represent a scope.
=cut
sub file_scoped {
my $self = shift;
my ($Parent, $Document) = ($self->parent, $self->top);
$Parent and $Document and $Parent == $Document
and $Document->isa('PPI::Document')
and ! $Document->isa('PPI::Document::Fragment');
}
1;
=pod
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,124 @@
package PPI::Statement::Scheduled;
=pod
=head1 NAME
PPI::Statement::Scheduled - A scheduled code block
=head1 INHERITANCE
PPI::Statement::Scheduled
isa PPI::Statement::Sub
isa PPI::Statement
isa PPI::Node
isa PPI::Element
=head1 DESCRIPTION
A scheduled code block is one that is intended to be run at a specific
time during the loading process.
There are five types of scheduled block:
BEGIN {
# Executes as soon as this block is fully defined
...
}
CHECK {
# Executes after overall compile-phase in reverse order
...
}
UNITCHECK {
# Executes after compile-phase of individual module in reverse order
...
}
INIT {
# Executes just before run-time
...
}
END {
# Executes as late as possible in reverse order
...
}
Technically these scheduled blocks are actually subroutines, and in fact
may have 'sub' in front of them.
=head1 METHODS
=cut
use strict;
use PPI::Statement::Sub ();
our $VERSION = '1.270'; # VERSION
our @ISA = "PPI::Statement::Sub";
sub __LEXER__normal() { '' }
sub _complete {
my $child = $_[0]->schild(-1);
return !! (
defined $child
and
$child->isa('PPI::Structure::Block')
and
$child->complete
);
}
=pod
=head2 type
The C<type> method returns the type of scheduled block, which should always be
one of C<'BEGIN'>, C<'CHECK'>, C<'UNITCHECK'>, C<'INIT'> or C<'END'>.
=cut
sub type {
my $self = shift;
my @children = $self->schildren or return undef;
$children[0]->content eq 'sub'
? $children[1]->content
: $children[0]->content;
}
# This is actually the same as Sub->name
sub name {
shift->type(@_);
}
1;
=pod
=head1 TO DO
- Write unit tests for this package
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,221 @@
package PPI::Statement::Sub;
=pod
=head1 NAME
PPI::Statement::Sub - Subroutine declaration
=head1 INHERITANCE
PPI::Statement::Sub
isa PPI::Statement
isa PPI::Node
isa PPI::Element
=head1 DESCRIPTION
Except for the special BEGIN, CHECK, UNITCHECK, INIT, and END subroutines
(which are part of L<PPI::Statement::Scheduled>) all subroutine declarations
are lexed as a PPI::Statement::Sub object.
Primarily, this means all of the various C<sub foo {}> statements, but also
forward declarations such as C<sub foo;> or C<sub foo($);>. It B<does not>
include anonymous subroutines, as these are merely part of a normal statement.
=head1 METHODS
C<PPI::Statement::Sub> has a number of methods in addition to the standard
L<PPI::Statement>, L<PPI::Node> and L<PPI::Element> methods.
=cut
use strict;
use List::Util ();
use Params::Util qw{_INSTANCE};
use PPI::Statement ();
our $VERSION = '1.270'; # VERSION
our @ISA = "PPI::Statement";
# Lexer clue
sub __LEXER__normal() { '' }
sub _complete {
my $child = $_[0]->schild(-1);
return !! (
defined $child
and
$child->isa('PPI::Structure::Block')
and
$child->complete
);
}
#####################################################################
# PPI::Statement::Sub Methods
=pod
=head2 name
The C<name> method returns the name of the subroutine being declared.
In some rare cases such as a naked C<sub> at the end of the file, this may return
false.
=cut
sub name {
my ($self) = @_;
# Usually the second token is the name.
my $token = $self->schild(1);
return $token->content
if defined $token and $token->isa('PPI::Token::Word');
# In the case of special subs whose 'sub' can be omitted (AUTOLOAD
# or DESTROY), the name will be the first token.
$token = $self->schild(0);
return $token->content
if defined $token and $token->isa('PPI::Token::Word');
return '';
}
=pod
=head2 prototype
If it has one, the C<prototype> method returns the subroutine's prototype.
It is returned in the same format as L<PPI::Token::Prototype/prototype>,
cleaned and removed from its brackets.
Returns the subroutine's prototype, or undef if the subroutine does not
define one. Note that when the sub has an empty prototype (C<()>) the
return is an empty string.
=cut
sub prototype {
my $self = shift;
my $Prototype = List::Util::first {
_INSTANCE($_, 'PPI::Token::Prototype')
} $self->children;
defined($Prototype) ? $Prototype->prototype : undef;
}
=pod
=head2 block
With its name and implementation shared with L<PPI::Statement::Scheduled>,
the C<block> method finds and returns the actual Structure object of the
code block for this subroutine.
Returns false if this is a forward declaration, or otherwise does not have a
code block.
=cut
sub block {
my $self = shift;
my $lastchild = $self->schild(-1) or return '';
$lastchild->isa('PPI::Structure::Block') and $lastchild;
}
=pod
=head2 forward
The C<forward> method returns true if the subroutine declaration is a
forward declaration.
That is, it returns false if the subroutine has a code block, or true
if it does not.
=cut
sub forward {
! shift->block;
}
=pod
=head2 reserved
The C<reserved> method provides a convenience method for checking to see
if this is a special reserved subroutine. It does not check against any
particular list of reserved sub names, but just returns true if the name
is all uppercase, as defined in L<perlsub>.
Note that in the case of BEGIN, CHECK, UNITCHECK, INIT and END, these will be
defined as L<PPI::Statement::Scheduled> objects, not subroutines.
Returns true if it is a special reserved subroutine, or false if not.
=cut
sub reserved {
my $self = shift;
my $name = $self->name or return '';
# perlsub is silent on whether reserveds can contain:
# - underscores;
# we allow them due to existing practice like CLONE_SKIP and __SUB__.
# - numbers; we allow them by PPI tradition.
$name eq uc $name;
}
=pod
=head2 type
The C<type> method checks and returns the declaration type of the statement,
which will be one of 'my', 'our', or 'state'.
Returns a string of the type, or C<undef> if the type is not declared.
=cut
sub type {
my $self = shift;
# Get the first significant child
my @schild = grep { $_->significant } $self->children;
# Ignore labels
shift @schild if _INSTANCE($schild[0], 'PPI::Token::Label');
# Get the type
(_INSTANCE($schild[0], 'PPI::Token::Word') and $schild[0]->content =~ /^(my|our|state)$/)
? $schild[0]->content
: undef;
}
1;
=pod
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,68 @@
package PPI::Statement::Unknown;
=pod
=head1 NAME
PPI::Statement::Unknown - An unknown or transient statement
=head1 INHERITANCE
PPI::Statement::Unknown
isa PPI::Statement
isa PPI::Node
isa PPI::Element
=head1 DESCRIPTION
The C<PPI::Statement::Unknown> class is used primarily during the lexing
process to hold elements that are known to be statement, but for which
the exact C<type> of statement is as yet unknown, and requires further
tokens in order to resolve the correct type.
They should not exist in a fully parse B<valid> document, and if any
exists they indicate either a problem in Document, or possibly (by
allowing it to get through unresolved) a bug in L<PPI::Lexer>.
=head1 METHODS
C<PPI::Statement::Unknown> has no additional methods beyond the
default ones provided by L<PPI::Statement>, L<PPI::Node> and
L<PPI::Element>.
=cut
use strict;
use PPI::Statement ();
our $VERSION = '1.270'; # VERSION
our @ISA = "PPI::Statement";
# If one of these ends up in the final document,
# we're pretty much screwed. Just call it a day.
sub _complete () { 1 }
1;
=pod
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,80 @@
package PPI::Statement::UnmatchedBrace;
=pod
=head1 NAME
PPI::Statement::UnmatchedBrace - Isolated unmatched brace
=head1 SYNOPSIS
sub foo {
1;
}
} # <--- This is an unmatched brace
=head1 INHERITANCE
PPI::Statement::UnmatchedBrace
isa PPI::Statement
isa PPI::Node
isa PPI::Element
=head1 DESCRIPTION
The C<PPI::Statement::UnmatchedBrace> class is a miscellaneous utility
class. Objects of this type should be rare, or not exist at all in normal
valid L<PPI::Document> objects.
It can be either a round ')', square ']' or curly '}' brace, this class
does not distinguish. Objects of this type are only allocated at a
structural level, not a lexical level (as they are lexically invalid
anyway).
The presence of a C<PPI::Statement::UnmatchedBrace> indicated a broken
or invalid document. Or maybe a bug in PPI, but B<far> more likely a
broken Document. :)
=head1 METHODS
C<PPI::Statement::UnmatchedBrace> has no additional methods beyond the
default ones provided by L<PPI::Statement>, L<PPI::Node> and
L<PPI::Element>.
=cut
use strict;
use PPI::Statement ();
our $VERSION = '1.270'; # VERSION
our @ISA = "PPI::Statement";
# Once we've hit a naked unmatched brace we can never truly be complete.
# So instead we always just call it a day...
sub _complete () { 1 }
1;
=pod
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,195 @@
package PPI::Statement::Variable;
=pod
=head1 NAME
PPI::Statement::Variable - Variable declaration statements
=head1 SYNOPSIS
# All of the following are variable declarations
my $foo = 1;
my ($foo, $bar) = (1, 2);
our $foo = 1;
local $foo;
local $foo = 1;
LABEL: my $foo = 1;
=head1 INHERITANCE
PPI::Statement::Variable
isa PPI::Statement::Expression
isa PPI::Statement
isa PPI::Node
isa PPI::Element
=head1 DESCRIPTION
The main intent of the C<PPI::Statement::Variable> class is to describe
simple statements that explicitly declare new local or global variables.
Note that this does not make it exclusively the only place where variables
are defined, and later on you should expect that the C<variables> method
will migrate deeper down the tree to either L<PPI::Statement> or
L<PPI::Node> to recognise this fact, but for now it stays here.
=head1 METHODS
=cut
use strict;
use Params::Util qw{_INSTANCE};
use PPI::Statement::Expression ();
our $VERSION = '1.270'; # VERSION
our @ISA = "PPI::Statement::Expression";
=pod
=head2 type
The C<type> method checks and returns the declaration type of the statement,
which will be one of 'my', 'local', 'our', or 'state'.
Returns a string of the type, or C<undef> if the type cannot be detected
(which is probably a bug).
=cut
sub type {
my $self = shift;
# Get the first significant child
my @schild = grep { $_->significant } $self->children;
# Ignore labels
shift @schild if _INSTANCE($schild[0], 'PPI::Token::Label');
# Get the type
(_INSTANCE($schild[0], 'PPI::Token::Word') and $schild[0]->content =~ /^(my|local|our|state)$/)
? $schild[0]->content
: undef;
}
=pod
=head2 variables
As for several other PDOM Element types that can declare variables, the
C<variables> method returns a list of the canonical forms of the variables
defined by the statement.
Returns a list of the canonical string forms of variables, or the null list
if it is unable to find any variables.
=cut
sub variables {
map { $_->canonical } $_[0]->symbols;
}
=pod
=head2 symbols
Returns a list of the variables defined by the statement, as
L<PPI::Token::Symbol>s.
=cut
sub symbols {
my $self = shift;
# Get the children we care about
my @schild = grep { $_->significant } $self->children;
shift @schild if _INSTANCE($schild[0], 'PPI::Token::Label');
# If the second child is a symbol, return its name
if ( _INSTANCE($schild[1], 'PPI::Token::Symbol') ) {
return $schild[1];
}
# If it's a list, return as a list
if ( _INSTANCE($schild[1], 'PPI::Structure::List') ) {
my $Expression = $schild[1]->schild(0);
$Expression and
$Expression->isa('PPI::Statement::Expression') or return ();
# my and our are simpler than local
if (
$self->type eq 'my'
or
$self->type eq 'our'
or
$self->type eq 'state'
) {
return grep {
$_->isa('PPI::Token::Symbol')
} $Expression->schildren;
}
# Local is much more icky (potentially).
# Not that we are actually going to deal with it now,
# but having this separate is likely going to be needed
# for future bug reports about local() things.
# This is a slightly better way to check.
return grep {
$self->_local_variable($_)
} grep {
$_->isa('PPI::Token::Symbol')
} $Expression->schildren;
}
# erm... this is unexpected
();
}
sub _local_variable {
my ($self, $el) = @_;
# The last symbol should be a variable
my $n = $el->snext_sibling or return 1;
my $p = $el->sprevious_sibling;
if ( ! $p or $p eq ',' ) {
# In the middle of a list
return 1 if $n eq ',';
# The first half of an assignment
return 1 if $n eq '=';
}
# Lets say no for know... additional work
# should go here.
return '';
}
1;
=pod
=head1 TO DO
- Write unit tests for this
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,100 @@
package PPI::Statement::When;
=pod
=head1 NAME
PPI::Statement::When - A when statement
=head1 SYNOPSIS
foreach ( qw/ foo bar baz / ) {
when ( m/b/ ) {
boing($_);
}
when ( m/f/ ) {
boom($_);
}
default {
tchak($_);
}
}
=head1 INHERITANCE
PPI::Statement::When
isa PPI::Statement
isa PPI::Node
isa PPI::Element
=head1 DESCRIPTION
C<PPI::Statement::When> objects are used to describe when and default
statements, as described in L<perlsyn>.
=head1 METHODS
C<PPI::Structure::When> has no methods beyond those provided by the
standard L<PPI::Structure>, L<PPI::Node> and L<PPI::Element> methods.
=cut
use strict;
use PPI::Statement ();
our $VERSION = '1.270'; # VERSION
our @ISA = "PPI::Statement";
# Lexer clues
sub __LEXER__normal() { '' }
sub _complete {
my $child = $_[0]->schild(-1);
return !! (
defined $child
and
$child->isa('PPI::Structure::Block')
and
$child->complete
);
}
#####################################################################
# PPI::Node Methods
sub scope() {
1;
}
1;
=pod
=head1 TO DO
- Write unit tests for this package
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,347 @@
package PPI::Structure;
=pod
=head1 NAME
PPI::Structure - The base class for Perl braced structures
=head1 INHERITANCE
PPI::Structure
isa PPI::Node
isa PPI::Element
=head1 DESCRIPTION
PPI::Structure is the root class for all Perl bracing structures. This
covers all forms of C< [ ... ] >, C< { ... } >, and C< ( ... ) > brace
types, and includes cases where only one half of the pair exist.
The class PPI::Structure itself is full abstract and no objects of that
type should actually exist in the tree.
=head2 Elements vs Children
A B<PPI::Structure> has an unusual existence. Unlike a L<PPI::Document>
or L<PPI::Statement>, which both simply contain other elements, a
structure B<both> contains and consists of content.
That is, the brace tokens are B<not> considered to be "children" of the
structure, but are part of it.
In practice, this will mean that while the -E<gt>elements and -E<gt>tokens
methods (and related) B<will> return a list with the brace tokens at either
end, the -E<gt>children method explicitly will B<not> return the brace.
=head1 STRUCTURE CLASSES
Excluding the transient L<PPI::Structure::Unknown> that exists briefly
inside the parser, there are eight types of structure.
=head2 L<PPI::Structure::List>
This covers all round braces used for function arguments, in C<foreach>
loops, literal lists, and braces used for precedence-ordering purposes.
=head2 L<PPI::Structure::For>
Although B<not> used for the C<foreach> loop list, this B<is> used for
the special case of the round-brace three-part semicolon-separated C<for>
loop expression (the traditional C style for loop).
=head2 L<PPI::Structure::Given>
This is for the expression being matched in switch statements.
=head2 L<PPI::Structure::When>
This is for the matching expression in "when" statements.
=head2 L<PPI::Structure::Condition>
This round-brace structure covers boolean conditional braces, such as
for C<if> and C<while> blocks.
=head2 L<PPI::Structure::Block>
This curly-brace and common structure is used for all form of code
blocks. This includes those for C<if>, C<do> and similar, as well
as C<grep>, C<map>, C<sort>, C<sub> and (labelled or anonymous)
scoping blocks.
=head2 L<PPI::Structure::Constructor>
This class covers brace structures used for the construction of
anonymous C<ARRAY> and C<HASH> references.
=head2 L<PPI::Structure::Subscript>
This class covers square-braces and curly-braces used after a
-E<gt> pointer to access the subscript of an C<ARRAY> or C<HASH>.
=head1 METHODS
C<PPI::Structure> itself has very few methods. Most of the time, you will be
working with the more generic L<PPI::Element> or L<PPI::Node> methods, or one
of the methods that are subclass-specific.
=cut
use strict;
use Scalar::Util ();
use Params::Util qw{_INSTANCE};
use PPI::Node ();
use PPI::Exception ();
use PPI::Singletons '%_PARENT';
our $VERSION = '1.270'; # VERSION
our @ISA = "PPI::Node";
use PPI::Structure::Block ();
use PPI::Structure::Condition ();
use PPI::Structure::Constructor ();
use PPI::Structure::For ();
use PPI::Structure::Given ();
use PPI::Structure::List ();
use PPI::Structure::Subscript ();
use PPI::Structure::Unknown ();
use PPI::Structure::When ();
#####################################################################
# Constructor
sub new {
my $class = shift;
my $Token = PPI::Token::__LEXER__opens($_[0]) ? shift : return undef;
# Create the object
my $self = bless {
children => [],
start => $Token,
}, $class;
# Set the start braces parent link
Scalar::Util::weaken(
$_PARENT{Scalar::Util::refaddr $Token} = $self
);
$self;
}
#####################################################################
# PPI::Structure API methods
=pod
=head2 start
For lack of better terminology (like "open" and "close") that has not
already in use for some other more important purpose, the two individual
braces for the structure are known within PPI as the "start" and "finish"
braces (at least for method purposes).
The C<start> method returns the start brace for the structure (i.e. the
opening brace).
Returns the brace as a L<PPI::Token::Structure> or C<undef> if the
structure does not have a starting brace.
Under normal parsing circumstances this should never occur, but may happen
due to manipulation of the PDOM tree.
=cut
sub start { $_[0]->{start} }
=pod
=head2 finish
The C<finish> method returns the finish brace for the structure (i.e. the
closing brace).
Returns the brace as a L<PPI::Token::Structure> or C<undef> if the
structure does not have a finishing brace. This can be quite common if
the document is not complete (for example, from an editor where the user
may be halfway through typeing a subroutine).
=cut
sub finish { $_[0]->{finish} }
=pod
=head2 braces
The C<braces> method is a utility method which returns the brace type,
regardless of whether both or just one of the braces is defined.
Returns one of the three strings C<'[]'>, C<'{}'>, or C<'()'>, or C<undef>
on error (primarily not having a start brace, as mentioned above).
=cut
sub braces {
my $self = $_[0]->{start} ? shift : return undef;
return {
'[' => '[]',
'(' => '()',
'{' => '{}',
}->{ $self->{start}->{content} };
}
=pod
=head1 complete
The C<complete> method is a convenience method that returns true if
the both braces are defined for the structure, or false if only one
brace is defined.
Unlike the top level C<complete> method which checks for completeness
in depth, the structure complete method ONLY confirms completeness
for the braces, and does not recurse downwards.
=cut
sub complete {
!! ($_[0]->{start} and $_[0]->{finish});
}
#####################################################################
# PPI::Node overloaded methods
# For us, the "elements" concept includes the brace tokens
sub elements {
my $self = shift;
if ( wantarray ) {
# Return a list in array context
return ( $self->{start} || (), @{$self->{children}}, $self->{finish} || () );
} else {
# Return the number of elements in scalar context.
# This is memory-cheaper than creating another big array
return scalar(@{$self->{children}})
+ ($self->{start} ? 1 : 0)
+ ($self->{finish} ? 1 : 0);
}
}
# For us, the first element is probably the opening brace
sub first_element {
# Technically, if we have no children and no opening brace,
# then the first element is the closing brace.
$_[0]->{start} or $_[0]->{children}->[0] or $_[0]->{finish};
}
# For us, the last element is probably the closing brace
sub last_element {
# Technically, if we have no children and no closing brace,
# then the last element is the opening brace
$_[0]->{finish} or $_[0]->{children}->[-1] or $_[0]->{start};
}
# Location is same as the start token, if any
sub location {
my $self = shift;
my $first = $self->first_element or return undef;
$first->location;
}
#####################################################################
# PPI::Element overloaded methods
# Get the full set of tokens, including start and finish
sub tokens {
my $self = shift;
my @tokens = (
$self->{start} || (),
$self->SUPER::tokens(@_),
$self->{finish} || (),
);
@tokens;
}
# Like the token method ->content, get our merged contents.
# This will recurse downwards through everything
### Reimplement this using List::Utils stuff
sub content {
my $self = shift;
my $content = $self->{start} ? $self->{start}->content : '';
foreach my $child ( @{$self->{children}} ) {
$content .= $child->content;
}
$content .= $self->{finish}->content if $self->{finish};
$content;
}
# Is the structure completed
sub _complete {
!! ( defined $_[0]->{finish} );
}
# You can insert either another structure, or a token
sub insert_before {
my $self = shift;
my $Element = _INSTANCE(shift, 'PPI::Element') or return undef;
if ( $Element->isa('PPI::Structure') ) {
return $self->__insert_before($Element);
} elsif ( $Element->isa('PPI::Token') ) {
return $self->__insert_before($Element);
}
'';
}
# As above, you can insert either another structure, or a token
sub insert_after {
my $self = shift;
my $Element = _INSTANCE(shift, 'PPI::Element') or return undef;
if ( $Element->isa('PPI::Structure') ) {
return $self->__insert_after($Element);
} elsif ( $Element->isa('PPI::Token') ) {
return $self->__insert_after($Element);
}
'';
}
1;
=pod
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,82 @@
package PPI::Structure::Block;
=pod
=head1 NAME
PPI::Structure::Block - Curly braces representing a code block
=head1 SYNOPSIS
sub foo { ... }
grep { ... } @list;
if ( condition ) {
...
}
LABEL: {
...
}
=head1 INHERITANCE
PPI::Structure::Block
isa PPI::Structure
isa PPI::Node
isa PPI::Element
=head1 DESCRIPTION
C<PPI::Structure::Block> is the class used for all curly braces that
represent code blocks. This includes subroutines, compound statements
and any other block braces.
=head1 METHODS
C<PPI::Structure::Block> has no methods beyond those provided by the
standard L<PPI::Structure>, L<PPI::Node> and L<PPI::Element> methods.
=cut
use strict;
use PPI::Structure ();
our $VERSION = '1.270'; # VERSION
our @ISA = "PPI::Structure";
#####################################################################
# PPI::Element Methods
# This is a scope boundary
sub scope() { 1 }
1;
=pod
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,67 @@
package PPI::Structure::Condition;
=pod
=head1 NAME
PPI::Structure::Condition - Round braces for boolean context conditions
=head1 SYNOPSIS
if ( condition ) {
...
}
while ( condition ) {
...
}
=head1 INHERITANCE
PPI::Structure::Condition
isa PPI::Structure
isa PPI::Node
isa PPI::Element
=head1 DESCRIPTION
C<PPI::Structure::Condition> is the class used for all round braces
that represent boolean contexts used in various conditions.
=head1 METHODS
C<PPI::Structure::Condition> has no methods beyond those provided by
the standard L<PPI::Structure>, L<PPI::Node> and L<PPI::Element> methods.
=cut
use strict;
use PPI::Structure ();
our $VERSION = '1.270'; # VERSION
our @ISA = "PPI::Structure";
1;
=pod
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,62 @@
package PPI::Structure::Constructor;
=pod
=head1 NAME
PPI::Structure::Constructor - Anonymous hash or array constructor
=head1 SYNOPSIS
my $array = [ 'foo', 'bar' ];
my $hash = { foo => 'bar' };
=head1 INHERITANCE
PPI::Structure::Constructor
isa PPI::Structure
isa PPI::Node
isa PPI::Element
=head1 DESCRIPTION
C<PPI::Structure::Constructor> is the class used for anonymous C<ARRAY>
reference of C<HASH> reference constructors.
=head1 METHODS
C<PPI::Structure::Constructor> has no methods beyond those provided by
the standard L<PPI::Structure>, L<PPI::Node> and L<PPI::Element> methods.
=cut
use strict;
use PPI::Structure ();
our $VERSION = '1.270'; # VERSION
our @ISA = "PPI::Structure";
1;
=pod
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,77 @@
package PPI::Structure::For;
=pod
=head1 NAME
PPI::Structure::For - Circular braces for a for expression
=head1 SYNOPSIS
for ( var $i = 0; $i < $max; $i++ ) {
...
}
=head1 INHERITANCE
PPI::Structure::For
isa PPI::Structure
isa PPI::Node
isa PPI::Element
=head1 DESCRIPTION
C<PPI::Structure::For> is the class used for circular braces that
contain the three part C<for> expression.
=head1 METHODS
C<PPI::Structure::For> has no methods beyond those provided by the
standard L<PPI::Structure>, L<PPI::Node> and L<PPI::Element> methods.
=cut
use strict;
use PPI::Structure ();
our $VERSION = '1.270'; # VERSION
our @ISA = "PPI::Structure";
# Highly special custom isa method that will continue to respond
# positively to ->isa('PPI::Structure::ForLoop') but warns.
my $has_warned = 0;
sub isa {
if ( $_[1] and $_[1] eq 'PPI::Structure::ForLoop' ) {
unless ( $has_warned ) {
warn("PPI::Structure::ForLoop has been deprecated");
$has_warned = 1;
}
return 1;
}
return shift->SUPER::isa(@_);
}
1;
=pod
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,63 @@
package PPI::Structure::Given;
=pod
=head1 NAME
PPI::Structure::Given - Circular braces for a switch statement
=head1 SYNOPSIS
given ( something ) {
...
}
=head1 INHERITANCE
PPI::Structure::Given
isa PPI::Structure
isa PPI::Node
isa PPI::Element
=head1 DESCRIPTION
C<PPI::Structure::Given> is the class used for circular braces that
contain the thing to be matched in a switch statement.
=head1 METHODS
C<PPI::Structure::Given> has no methods beyond those provided by the
standard L<PPI::Structure>, L<PPI::Node> and L<PPI::Element> methods.
=cut
use strict;
use PPI::Structure ();
our $VERSION = '1.270'; # VERSION
our @ISA = "PPI::Structure";
1;
=pod
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,87 @@
package PPI::Structure::List;
=pod
=head1 NAME
PPI::Structure::List - Explicit list or precedence ordering braces
=head1 SYNOPSIS
# A list used for params
function( 'param', 'param' );
# Explicit list
return ( 'foo', 'bar' );
=head1 INHERITANCE
PPI::Structure::List
isa PPI::Structure
isa PPI::Node
isa PPI::Element
=head1 DESCRIPTION
C<PPI::Structure::List> is the class used for circular braces that
represent lists, and related.
=head1 METHODS
C<PPI::Structure::List> has no methods beyond those provided by the
standard L<PPI::Structure>, L<PPI::Node> and L<PPI::Element> methods.
=cut
use strict;
use Carp ();
use PPI::Structure ();
our $VERSION = '1.270'; # VERSION
our @ISA = "PPI::Structure";
# Highly special custom isa method that will continue to respond
# positively to ->isa('PPI::Structure::ForLoop') but warns.
my $has_warned = 0;
sub isa {
if ( $_[1] and $_[1] eq 'PPI::Structure::ForLoop' ) {
if (
$_[0]->parent->isa('PPI::Statement::Compound')
and
$_[0]->parent->type =~ /^for/
) {
unless ( $has_warned ) {
local $Carp::CarpLevel = $Carp::CarpLevel + 1;
Carp::carp("PPI::Structure::ForLoop has been deprecated");
$has_warned = 1;
}
return 1;
}
}
return shift->SUPER::isa(@_);
}
1;
=pod
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,68 @@
package PPI::Structure::Subscript;
=pod
=head1 NAME
PPI::Structure::Subscript - Braces that represent an array or hash subscript
=head1 SYNOPSIS
# The end braces for all of the following are subscripts
$foo->[...]
$foo[...]
$foo{...}[...]
$foo->{...}
$foo{...}
$foo[]{...}
=head1 INHERITANCE
PPI::Structure::Subscript
isa PPI::Structure
isa PPI::Node
isa PPI::Element
=head1 DESCRIPTION
C<PPI::Structure::Subscript> is the class used for square and curly
braces that specify one element of an array or hash (or a slice/subset
of an array or hash)
=head1 METHODS
C<PPI::Structure::Subscript> has no methods beyond those provided by the
standard L<PPI::Structure>, L<PPI::Node> and L<PPI::Element> methods.
=cut
use strict;
use PPI::Structure ();
our $VERSION = '1.270'; # VERSION
our @ISA = "PPI::Structure";
1;
=pod
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,69 @@
package PPI::Structure::Unknown;
=pod
=head1 NAME
PPI::Structure::Unknown - An unknown or unresolved brace structure
=head1 INHERITANCE
PPI::Structure::Unknown
isa PPI::Structure
isa PPI::Node
isa PPI::Element
=head1 DESCRIPTION
C<PPI::Structure::Unknown> is class for braces whose type is unknown, or
temporarily unknown.
It primarily exists temporarily inside the lexer. Although some types of
braces can be determined immediately at opening, there are a number of
different brace types that can only be correctly identified after the
braces are closed.
A structure is typed as unknown during this period it is indeterminate.
A C<PPI::Structure::Unknown> object should not B<ever> make it out of the
lexer without being converted to its final type. Any time you encounter
this class in a PDOM tree it should be considered a bug and reported
accordingly.
=head1 METHODS
C<PPI::Structure::Unknown> has no methods beyond those provided by the
standard L<PPI::Structure>, L<PPI::Node> and L<PPI::Element> methods.
=cut
use strict;
use PPI::Structure ();
our $VERSION = '1.270'; # VERSION
our @ISA = "PPI::Structure";
1;
=pod
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,63 @@
package PPI::Structure::When;
=pod
=head1 NAME
PPI::Structure::When - Circular braces for a when statement
=head1 SYNOPSIS
when ( something ) {
...
}
=head1 INHERITANCE
PPI::Structure::When
isa PPI::Structure
isa PPI::Node
isa PPI::Element
=head1 DESCRIPTION
C<PPI::Structure::When> is the class used for circular braces that
contain the thing to be matched in a when statement.
=head1 METHODS
C<PPI::Structure::When> has no methods beyond those provided by the
standard L<PPI::Structure>, L<PPI::Node> and L<PPI::Element> methods.
=cut
use strict;
use PPI::Structure ();
our $VERSION = '1.270'; # VERSION
our @ISA = "PPI::Structure";
1;
=pod
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

245
database/perl/vendor/lib/PPI/Token.pm vendored Normal file
View File

@@ -0,0 +1,245 @@
package PPI::Token;
=pod
=head1 NAME
PPI::Token - A single token of Perl source code
=head1 INHERITANCE
PPI::Token
isa PPI::Element
=head1 DESCRIPTION
C<PPI::Token> is the abstract base class for all Tokens. In PPI terms, a "Token" is
a L<PPI::Element> that directly represents bytes of source code.
=head1 METHODS
=cut
use strict;
use Params::Util qw{_INSTANCE};
use PPI::Element ();
use PPI::Exception ();
our $VERSION = '1.270'; # VERSION
our @ISA = 'PPI::Element';
# We don't load the abstracts, they are loaded
# as part of the inheritance process.
# Load the token classes
use PPI::Token::BOM ();
use PPI::Token::Whitespace ();
use PPI::Token::Comment ();
use PPI::Token::Pod ();
use PPI::Token::Number ();
use PPI::Token::Number::Binary ();
use PPI::Token::Number::Octal ();
use PPI::Token::Number::Hex ();
use PPI::Token::Number::Float ();
use PPI::Token::Number::Exp ();
use PPI::Token::Number::Version ();
use PPI::Token::Word ();
use PPI::Token::DashedWord ();
use PPI::Token::Symbol ();
use PPI::Token::ArrayIndex ();
use PPI::Token::Magic ();
use PPI::Token::Quote::Single ();
use PPI::Token::Quote::Double ();
use PPI::Token::Quote::Literal ();
use PPI::Token::Quote::Interpolate ();
use PPI::Token::QuoteLike::Backtick ();
use PPI::Token::QuoteLike::Command ();
use PPI::Token::QuoteLike::Regexp ();
use PPI::Token::QuoteLike::Words ();
use PPI::Token::QuoteLike::Readline ();
use PPI::Token::Regexp::Match ();
use PPI::Token::Regexp::Substitute ();
use PPI::Token::Regexp::Transliterate ();
use PPI::Token::Operator ();
use PPI::Token::Cast ();
use PPI::Token::Structure ();
use PPI::Token::Label ();
use PPI::Token::HereDoc ();
use PPI::Token::Separator ();
use PPI::Token::Data ();
use PPI::Token::End ();
use PPI::Token::Prototype ();
use PPI::Token::Attribute ();
use PPI::Token::Unknown ();
#####################################################################
# Constructor and Related
sub new {
bless { content => (defined $_[1] ? "$_[1]" : '') }, $_[0];
}
sub set_class {
my $self = shift;
# @_ or throw Exception("No arguments to set_class");
my $class = substr( $_[0], 0, 12 ) eq 'PPI::Token::' ? shift : 'PPI::Token::' . shift;
# Find out if the current and new classes are complex
my $old_quote = (ref($self) =~ /\b(?:Quote|Regex)\b/o) ? 1 : 0;
my $new_quote = ($class =~ /\b(?:Quote|Regex)\b/o) ? 1 : 0;
# No matter what happens, we will have to rebless
bless $self, $class;
# If we are changing to or from a Quote style token, we
# can't just rebless and need to do some extra thing
# Otherwise, we have done enough
return $class if ($old_quote - $new_quote) == 0;
# Make a new token from the old content, and overwrite the current
# token's attributes with the new token's attributes.
my $token = $class->new( $self->{content} );
%$self = %$token;
# Return the class as a convenience
return $class;
}
#####################################################################
# PPI::Token Methods
=pod
=head2 set_content $string
The C<set_content> method allows you to set/change the string that the
C<PPI::Token> object represents.
Returns the string you set the Token to
=cut
sub set_content {
$_[0]->{content} = $_[1];
}
=pod
=head2 add_content $string
The C<add_content> method allows you to add additional bytes of code
to the end of the Token.
Returns the new full string after the bytes have been added.
=cut
sub add_content { $_[0]->{content} .= $_[1] }
=pod
=head2 length
The C<length> method returns the length of the string in a Token.
=cut
sub length { CORE::length($_[0]->{content}) }
#####################################################################
# Overloaded PPI::Element methods
sub content {
$_[0]->{content};
}
# You can insert either a statement, or a non-significant token.
sub insert_before {
my $self = shift;
my $Element = _INSTANCE(shift, 'PPI::Element') or return undef;
if ( $Element->isa('PPI::Structure') ) {
return $self->__insert_before($Element);
} elsif ( $Element->isa('PPI::Token') ) {
return $self->__insert_before($Element);
}
'';
}
# As above, you can insert a statement, or a non-significant token
sub insert_after {
my $self = shift;
my $Element = _INSTANCE(shift, 'PPI::Element') or return undef;
if ( $Element->isa('PPI::Structure') ) {
return $self->__insert_after($Element);
} elsif ( $Element->isa('PPI::Token') ) {
return $self->__insert_after($Element);
}
'';
}
#####################################################################
# Tokenizer Methods
sub __TOKENIZER__on_line_start() { 1 }
sub __TOKENIZER__on_line_end() { 1 }
sub __TOKENIZER__on_char() { 'Unknown' }
#####################################################################
# Lexer Methods
sub __LEXER__opens {
ref($_[0]) eq 'PPI::Token::Structure'
and
$_[0]->{content} =~ /(?:\(|\[|\{)/
}
sub __LEXER__closes {
ref($_[0]) eq 'PPI::Token::Structure'
and
$_[0]->{content} =~ /(?:\)|\]|\})/
}
1;
=pod
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,77 @@
package PPI::Token::ArrayIndex;
=pod
=head1 NAME
PPI::Token::ArrayIndex - Token getting the last index for an array
=head1 INHERITANCE
PPI::Token::ArrayIndex
isa PPI::Token
isa PPI::Element
=head1 DESCRIPTION
The C<PPI::Token::ArrayIndex> token represents an attempt to get the
last index of an array, such as C<$#array>.
=head1 METHODS
There are no additional methods beyond those provided by the parent
L<PPI::Token> and L<PPI::Element> classes.
=cut
use strict;
use PPI::Token ();
our $VERSION = '1.270'; # VERSION
our @ISA = "PPI::Token";
#####################################################################
# Tokenizer Methods
sub __TOKENIZER__on_char {
my $t = $_[1];
# Suck in till the end of the arrayindex
pos $t->{line} = $t->{line_cursor};
if ( $t->{line} =~ m/\G([\w:']+)/gc ) {
$t->{token}->{content} .= $1;
$t->{line_cursor} += length $1;
}
# End of token
$t->_finalize_token->__TOKENIZER__on_char( $t );
}
1;
=pod
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,176 @@
package PPI::Token::Attribute;
=pod
=head1 NAME
PPI::Token::Attribute - A token for a subroutine attribute
=head1 INHERITANCE
PPI::Token::Attribute
isa PPI::Token
isa PPI::Element
=head1 DESCRIPTION
In Perl, attributes are a relatively recent addition to the language.
Given the code C< sub foo : bar(something) {} >, the C<bar(something)>
part is the attribute.
A C<PPI::Token::Attribute> token represents the entire of the attribute,
as the braces and its contents are not parsed into the tree, and are
treated by Perl (and thus by us) as a single string.
=head1 METHODS
This class provides some additional methods beyond those provided by its
L<PPI::Token> and L<PPI::Element> parent classes.
=cut
use strict;
use PPI::Token ();
our $VERSION = '1.270'; # VERSION
our @ISA = "PPI::Token";
#####################################################################
# PPI::Token::Attribute Methods
=pod
=head2 identifier
The C<identifier> attribute returns the identifier part of the attribute.
That is, for the attribute C<foo(bar)>, the C<identifier> method would
return C<"foo">.
=cut
sub identifier {
my $self = shift;
$self->{content} =~ /^(.+?)\(/ ? $1 : $self->{content};
}
=pod
=head2 parameters
The C<parameters> method returns the parameter string for the attribute.
That is, for the attribute C<foo(bar)>, the C<parameters> method would
return C<"bar">.
Returns the parameters as a string (including the null string C<''> for
the case of an attribute such as C<foo()>.)
Returns C<undef> if the attribute does not have parameters.
=cut
sub parameters {
my $self = shift;
$self->{content} =~ /\((.*)\)$/ ? $1 : undef;
}
#####################################################################
# Tokenizer Methods
sub __TOKENIZER__on_char {
my $class = shift;
my $t = shift;
my $char = substr( $t->{line}, $t->{line_cursor}, 1 );
# Unless this is a '(', we are finished.
unless ( $char eq '(' ) {
# Finalise and recheck
return $t->_finalize_token->__TOKENIZER__on_char( $t );
}
# This is a bar(...) style attribute.
# We are currently on the ( so scan in until the end.
# We finish on the character AFTER our end
my $string = $class->__TOKENIZER__scan_for_end( $t );
if ( ref $string ) {
# EOF
$t->{token}->{content} .= $$string;
$t->_finalize_token;
return 0;
}
# Found the end of the attribute
$t->{token}->{content} .= $string;
$t->_finalize_token->__TOKENIZER__on_char( $t );
}
# Scan for a close braced, and take into account both escaping,
# and open close bracket pairs in the string. When complete, the
# method leaves the line cursor on the LAST character found.
sub __TOKENIZER__scan_for_end {
my $t = $_[1];
# Loop as long as we can get new lines
my $string = '';
my $depth = 0;
while ( exists $t->{line} ) {
# Get the search area
pos $t->{line} = $t->{line_cursor};
# Look for a match
unless ( $t->{line} =~ /\G((?:\\.|[^()])*?[()])/gc ) {
# Load in the next line and push to first character
$string .= substr( $t->{line}, $t->{line_cursor} );
$t->_fill_line(1) or return \$string;
$t->{line_cursor} = 0;
next;
}
# Add to the string
$string .= $1;
$t->{line_cursor} += length $1;
# Alter the depth and continue if we aren't at the end
$depth += ($1 =~ /\($/) ? 1 : -1 and next;
# Found the end
return $string;
}
# Returning the string as a reference indicates EOF
\$string;
}
1;
=pod
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,113 @@
package PPI::Token::BOM;
=pod
=head1 NAME
PPI::Token::BOM - Tokens representing Unicode byte order marks
=head1 INHERITANCE
PPI::Token::BOM
isa PPI::Token
isa PPI::Element
=head1 DESCRIPTION
This is a special token in that it can only occur at the beginning of
documents. If a BOM byte mark occurs elsewhere in a file, it should
be treated as L<PPI::Token::Whitespace>. We recognize the byte order
marks identified at this URL:
L<http://www.unicode.org/faq/utf_bom.html#BOM>
UTF-32, big-endian 00 00 FE FF
UTF-32, little-endian FF FE 00 00
UTF-16, big-endian FE FF
UTF-16, little-endian FF FE
UTF-8 EF BB BF
Note that as of this writing, PPI only has support for UTF-8
(namely, in POD and strings) and no support for UTF-16 or UTF-32. We
support the BOMs of the latter two for completeness only.
The BOM is considered non-significant, like white space.
=head1 METHODS
There are no additional methods beyond those provided by the parent
L<PPI::Token> and L<PPI::Element> classes.
=cut
use strict;
use PPI::Token ();
our $VERSION = '1.270'; # VERSION
our @ISA = "PPI::Token";
sub significant() { '' }
#####################################################################
# Parsing Methods
my %bom_types = (
"\x00\x00\xfe\xff" => 'UTF-32',
"\xff\xfe\x00\x00" => 'UTF-32',
"\xfe\xff" => 'UTF-16',
"\xff\xfe" => 'UTF-16',
"\xef\xbb\xbf" => 'UTF-8',
);
sub __TOKENIZER__on_line_start {
my $t = $_[1];
$_ = $t->{line};
if (m/^(\x00\x00\xfe\xff | # UTF-32, big-endian
\xff\xfe\x00\x00 | # UTF-32, little-endian
\xfe\xff | # UTF-16, big-endian
\xff\xfe | # UTF-16, little-endian
\xef\xbb\xbf) # UTF-8
/xs) {
my $bom = $1;
if ($bom_types{$bom} ne 'UTF-8') {
return $t->_error("$bom_types{$bom} is not supported");
}
$t->_new_token('BOM', $bom) or return undef;
$t->{line_cursor} += length $bom;
}
# Continue just as if there was no BOM
$t->{class} = 'PPI::Token::Whitespace';
return $t->{class}->__TOKENIZER__on_line_start($t);
}
1;
=pod
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module
=head1 AUTHOR
Chris Dolan E<lt>cdolan@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,87 @@
package PPI::Token::Cast;
=pod
=head1 NAME
PPI::Token::Cast - A prefix which forces a value into a different context
=head1 INHERITANCE
PPI::Token::Cast
isa PPI::Token
isa PPI::Element
=head1 DESCRIPTION
A "cast" in PPI terms is one of more characters used as a prefix which force
a value into a different class or context.
This includes referencing, dereferencing, and a few other minor cases.
For expressions such as C<@$foo> or C<@{ $foo{bar} }> the C<@> in both cases
represents a cast. In this case, an array dereference.
=head1 METHODS
There are no additional methods beyond those provided by the parent
L<PPI::Token> and L<PPI::Element> classes.
=cut
use strict;
use PPI::Token ();
our $VERSION = '1.270'; # VERSION
our @ISA = "PPI::Token";
our %POSTFIX = map { $_ => 1 } (
qw{
%* @* $*
},
'$#*' # throws warnings if it's inside a qw
);
#####################################################################
# Tokenizer Methods
# A cast is either % @ $ or $#
# and also postfix dereference are %* @* $* $#*
sub __TOKENIZER__on_char {
my $t = $_[1];
my $char = substr( $t->{line}, $t->{line_cursor}, 1 );
# Are we still an operator if we add the next character
my $content = $t->{token}->{content};
return 1 if $POSTFIX{ $content . $char };
$t->_finalize_token->__TOKENIZER__on_char( $t );
}
1;
=pod
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,146 @@
package PPI::Token::Comment;
=pod
=head1 NAME
PPI::Token::Comment - A comment in Perl source code
=head1 INHERITANCE
PPI::Token::Comment
isa PPI::Token
isa PPI::Element
=head1 SYNOPSIS
# This is a PPI::Token::Comment
print "Hello World!"; # So it this
$string =~ s/ foo # This, unfortunately, is not :(
bar
/w;
=head1 DESCRIPTION
In PPI, comments are represented by C<PPI::Token::Comment> objects.
These come in two flavours, line comment and inline comments.
A C<line comment> is a comment that stands on its own line. These comments
hold their own newline and whitespace (both leading and trailing) as part
of the one C<PPI::Token::Comment> object.
An inline comment is a comment that appears after some code, and
continues to the end of the line. This does B<not> include whitespace,
and the terminating newlines is considered a separate
L<PPI::Token::Whitespace> token.
This is largely a convenience, simplifying a lot of normal code relating
to the common things people do with comments.
Most commonly, it means when you C<prune> or C<delete> a comment, a line
comment disappears taking the entire line with it, and an inline comment
is removed from the inside of the line, allowing the newline to drop
back onto the end of the code, as you would expect.
It also means you can move comments around in blocks much more easily.
For now, this is a suitably handy way to do things. However, I do reserve
the right to change my mind on this one if it gets dangerously
anachronistic somewhere down the line.
=head1 METHODS
Only very limited methods are available, beyond those provided by our
parent L<PPI::Token> and L<PPI::Element> classes.
=cut
use strict;
use PPI::Token ();
our $VERSION = '1.270'; # VERSION
our @ISA = "PPI::Token";
### XS -> PPI/XS.xs:_PPI_Token_Comment__significant 0.900+
sub significant() { '' }
# Most stuff goes through __TOKENIZER__commit.
# This is such a rare case, do char at a time to keep the code small
sub __TOKENIZER__on_char {
my $t = $_[1];
# Make sure not to include the trailing newline
if ( substr( $t->{line}, $t->{line_cursor}, 1 ) eq "\n" ) {
return $t->_finalize_token->__TOKENIZER__on_char( $t );
}
1;
}
sub __TOKENIZER__commit {
my $t = $_[1];
# Get the rest of the line
my $rest = substr( $t->{line}, $t->{line_cursor} );
if ( chomp $rest ) { # Include the newline separately
# Add the current token, and the newline
$t->_new_token('Comment', $rest);
$t->_new_token('Whitespace', "\n");
} else {
# Add this token only
$t->_new_token('Comment', $rest);
}
# Advance the line cursor to the end
$t->{line_cursor} = $t->{line_length} - 1;
0;
}
# Comments end at the end of the line
sub __TOKENIZER__on_line_end {
$_[1]->_finalize_token if $_[1]->{token};
1;
}
=pod
=head2 line
The C<line> accessor returns true if the C<PPI::Token::Comment> is a
line comment, or false if it is an inline comment.
=cut
sub line {
# Entire line comments have a newline at the end
$_[0]->{content} =~ /\n$/ ? 1 : 0;
}
1;
=pod
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,96 @@
package PPI::Token::DashedWord;
=pod
=head1 NAME
PPI::Token::DashedWord - A dashed bareword token
=head1 INHERITANCE
PPI::Token::DashedWord
isa PPI::Token
isa PPI::Element
=head1 DESCRIPTION
The "dashed bareword" token represents literal values like C<-foo>.
NOTE: this class is currently unused. All tokens that should be
PPI::Token::DashedWords are just normal PPI::Token::Word instead.
That actually makes sense, since there really is nothing special about
this class except that dashed words cannot be subroutine names or
keywords. As such, this class may be removed from PPI in the future.
=head1 METHODS
=cut
use strict;
use PPI::Token ();
our $VERSION = '1.270'; # VERSION
our @ISA = "PPI::Token";
=pod
=head2 literal
Returns the value of the dashed word as a string. This differs from
C<content> because C<-Foo'Bar> expands to C<-Foo::Bar>.
=cut
*literal = *PPI::Token::Word::literal;
#####################################################################
# Tokenizer Methods
sub __TOKENIZER__on_char {
my $t = $_[1];
# Suck to the end of the dashed bareword
pos $t->{line} = $t->{line_cursor};
if ( $t->{line} =~ m/\G(\w+)/gc ) {
$t->{token}->{content} .= $1;
$t->{line_cursor} += length $1;
}
# Are we a file test operator?
if ( $t->{token}->{content} =~ /^\-[rwxoRWXOezsfdlpSbctugkTBMAC]$/ ) {
# File test operator
$t->{class} = $t->{token}->set_class( 'Operator' );
} else {
# No, normal dashed bareword
$t->{class} = $t->{token}->set_class( 'Word' );
}
$t->_finalize_token->__TOKENIZER__on_char( $t );
}
1;
=pod
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,102 @@
package PPI::Token::Data;
=pod
=head1 NAME
PPI::Token::Data - The actual data in the __DATA__ section of a file
=head1 INHERITANCE
PPI::Token::Data
isa PPI::Token
isa PPI::Element
=head1 DESCRIPTION
The C<PPI::Token::Data> class is used to represent the actual data inside
a file's C<__DATA__> section.
One C<PPI::Token::Data> object is used to represent the entire of the data,
primarily so that it can provide a convenient handle directly to the data.
=head1 METHODS
C<PPI::Token::Data> provides one method in addition to those provided by
our parent L<PPI::Token> and L<PPI::Element> classes.
=cut
use strict;
use IO::String 1.07 ();
use PPI::Token ();
our $VERSION = '1.270'; # VERSION
our @ISA = "PPI::Token";
#####################################################################
# Methods
=pod
=head2 handle
The C<handle> method returns a L<IO::String> handle that allows you
to do all the normal handle-y things to the contents of the __DATA__
section of the file.
Unlike in perl itself, this means you can also do things like C<print>
new data onto the end of the __DATA__ section, or modify it with
any other process that can accept an L<IO::Handle> as input or output.
Returns an L<IO::String> object.
=cut
sub handle {
my $self = shift;
IO::String->new( \$self->{content} );
}
sub __TOKENIZER__on_line_start {
my ( $self, $t ) = @_;
# Add the line
if ( defined $t->{token} ) {
$t->{token}->{content} .= $t->{line};
}
else {
defined( $t->{token} = $t->{class}->new( $t->{line} ) ) or return undef;
}
return 0;
}
1;
=pod
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,111 @@
package PPI::Token::End;
=pod
=head1 NAME
PPI::Token::End - Completely useless content after the __END__ tag
=head1 INHERITANCE
PPI::Token::End
isa PPI::Token
isa PPI::Element
=head1 DESCRIPTION
If you've read L<PPI::Token::Whitespace>, you should understand by now
the concept of documents "floating in a sea of PPI::Token::Whitespace".
Well it doesn't after the __END__ tag.
Once you __END__, it's all over. Anything after that tag isn't even fit
to be called whitespace. It just simply doesn't exist as far as perl
(the interpreter) is concerned.
That's not to say there isn't useful content. Most often people use
the __END__ tag to hide POD content, so that perl never has to see it,
and presumably providing some small speed up.
That's fine. PPI likes POD. Any POD after the __END__ tag is parsed
into valid L<PPI::Token::Pod> tags as normal. B<This> class, on the
other hand, is for "what's after __END__ when it isn't POD".
Basically, the completely worthless bits of the file :)
=head1 METHODS
This class has no method beyond what is provided by its L<PPI::Token> and
L<PPI::Element> parent classes.
=cut
use strict;
use PPI::Token ();
our $VERSION = '1.270'; # VERSION
our @ISA = "PPI::Token";
#####################################################################
# Tokenizer Methods
### XS -> PPI/XS.xs:_PPI_Token_End__significant 0.900+
sub significant() { '' }
sub __TOKENIZER__on_char() { 1 }
sub __TOKENIZER__on_line_start {
my $t = $_[1];
# Can we classify the entire line in one go
if ( $t->{line} =~ /^=(\w+)/ ) {
# A Pod tag... change to pod mode
$t->_new_token( 'Pod', $t->{line} );
unless ( $1 eq 'cut' ) {
# Normal start to pod
$t->{class} = 'PPI::Token::Pod';
}
# This is an error, but one we'll ignore
# Don't go into Pod mode, since =cut normally
# signals the end of Pod mode
} else {
if ( defined $t->{token} ) {
# Add to existing token
$t->{token}->{content} .= $t->{line};
} else {
$t->_new_token( 'End', $t->{line} );
}
}
0;
}
1;
=pod
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,323 @@
package PPI::Token::HereDoc;
=pod
=head1 NAME
PPI::Token::HereDoc - Token class for the here-doc
=head1 INHERITANCE
PPI::Token::HereDoc
isa PPI::Token
isa PPI::Element
=head1 DESCRIPTION
Here-docs are incredibly handy when writing Perl, but incredibly tricky
when parsing it, primarily because they don't follow the general flow of
input.
They jump ahead and nab lines directly off the input buffer. Whitespace
and newlines may not matter in most Perl code, but they matter in here-docs.
They are also tricky to store as an object. They look sort of like an
operator and a string, but they don't act like it. And they have a second
section that should be something like a separate token, but isn't because a
string can span from above the here-doc content to below it.
So when parsing, this is what we do.
Firstly, the PPI::Token::HereDoc object, does not represent the C<<< << >>>
operator, or the "END_FLAG", or the content, or even the terminator.
It represents all of them at once.
The token itself has only the declaration part as its "content".
# This is what the content of a HereDoc token is
<<FOO
# Or this
<<"FOO"
# Or even this
<< 'FOO'
That is, the "operator", any whitespace separator, and the quoted or bare
terminator. So when you call the C<content> method on a HereDoc token, you
get '<< "FOO"'.
As for the content and the terminator, when treated purely in "content" terms
they do not exist.
The content is made available with the C<heredoc> method, and the name of
the terminator with the C<terminator> method.
To make things work in the way you expect, PPI has to play some games
when doing line/column location calculation for tokens, and also during
the content parsing and generation processes.
Documents cannot simply by recreated by stitching together the token
contents, and involve a somewhat more expensive procedure, but the extra
expense should be relatively negligible unless you are doing huge
quantities of them.
Please note that due to the immature nature of PPI in general, we expect
C<HereDocs> to be a rich (bad) source of corner-case bugs for quite a while,
but for the most part they should more or less DWYM.
=head2 Comparison to other string types
Although technically it can be considered a quote, for the time being
C<HereDocs> are being treated as a completely separate C<Token> subclass,
and will not be found in a search for L<PPI::Token::Quote> or
L<PPI::Token::QuoteLike> objects.
This may change in the future, with it most likely to end up under
QuoteLike.
=head1 METHODS
Although it has the standard set of C<Token> methods, C<HereDoc> objects
have a relatively large number of unique methods all of their own.
=cut
use strict;
our $VERSION = '1.270'; # VERSION
our @ISA = "PPI::Token";
#####################################################################
# PPI::Token::HereDoc Methods
=pod
=head2 heredoc
The C<heredoc> method is the authoritative method for accessing the contents
of the C<HereDoc> object.
It returns the contents of the here-doc as a list of newline-terminated
strings. If called in scalar context, it returns the number of lines in
the here-doc, B<excluding> the terminator line.
=cut
sub heredoc { @{shift->{_heredoc}} }
=pod
=head2 terminator
The C<terminator> method returns the name of the terminating string for the
here-doc.
Returns the terminating string as an unescaped string (in the rare case
the terminator has an escaped quote in it).
=cut
sub terminator {
shift->{_terminator};
}
sub _is_terminator {
my ( $self, $terminator, $line, $indented ) = @_;
if ( $indented ) {
return $line =~ /^\s*\Q$terminator\E$/;
} else {
return $line eq $terminator;
}
}
sub _indent {
my ( $self, $token ) = @_;
my ($indent) = $token->{_terminator_line} =~ /^(\s*)/;
return $indent;
}
sub _is_match_indent {
my ( $self, $token, $indent ) = @_;
return (grep { /^$indent/ } @{$token->{_heredoc}}) == @{$token->{_heredoc}};
}
#####################################################################
# Tokenizer Methods
# Parse in the entire here-doc in one call
sub __TOKENIZER__on_char {
my ( $self, $t ) = @_;
# We are currently located on the first char after the <<
# Handle the most common form first for simplicity and speed reasons
### FIXME - This regex, and this method in general, do not yet allow
### for the null here-doc, which terminates at the first
### empty line.
pos $t->{line} = $t->{line_cursor};
if ( $t->{line} !~ m/\G( ~? \s* (?: "[^"]*" | '[^']*' | `[^`]*` | \\?\w+ ) )/gcx ) {
# Degenerate to a left-shift operation
$t->{token}->set_class('Operator');
return $t->_finalize_token->__TOKENIZER__on_char( $t );
}
# Add the rest of the token, work out what type it is,
# and suck in the content until the end.
my $token = $t->{token};
$token->{content} .= $1;
$t->{line_cursor} += length $1;
# Find the terminator, clean it up and determine
# the type of here-doc we are dealing with.
my $content = $token->{content};
if ( $content =~ /^\<\<(~?)(\w+)$/ ) {
# Bareword
$token->{_mode} = 'interpolate';
$token->{_indented} = 1 if $1 eq '~';
$token->{_terminator} = $2;
} elsif ( $content =~ /^\<\<(~?)\s*\'(.*)\'$/ ) {
# ''-quoted literal
$token->{_mode} = 'literal';
$token->{_indented} = 1 if $1 eq '~';
$token->{_terminator} = $2;
$token->{_terminator} =~ s/\\'/'/g;
} elsif ( $content =~ /^\<\<(~?)\s*\"(.*)\"$/ ) {
# ""-quoted literal
$token->{_mode} = 'interpolate';
$token->{_indented} = 1 if $1 eq '~';
$token->{_terminator} = $2;
$token->{_terminator} =~ s/\\"/"/g;
} elsif ( $content =~ /^\<\<(~?)\s*\`(.*)\`$/ ) {
# ``-quoted command
$token->{_mode} = 'command';
$token->{_indented} = 1 if $1 eq '~';
$token->{_terminator} = $2;
$token->{_terminator} =~ s/\\`/`/g;
} elsif ( $content =~ /^\<\<(~?)\\(\w+)$/ ) {
# Legacy forward-slashed bareword
$token->{_mode} = 'literal';
$token->{_indented} = 1 if $1 eq '~';
$token->{_terminator} = $2;
} else {
# WTF?
return undef;
}
# Suck in the HEREDOC
$token->{_heredoc} = \my @heredoc;
my $terminator = $token->{_terminator} . "\n";
while ( defined( my $line = $t->_get_line ) ) {
if ( $self->_is_terminator( $terminator, $line, $token->{_indented} ) ) {
# Keep the actual termination line for consistency
# when we are re-assembling the file
$token->{_terminator_line} = $line;
if ( $token->{_indented} ) {
my $indent = $self->_indent( $token );
# Indentation of here-doc doesn't match delimiter
unless ( $self->_is_match_indent( $token, $indent ) ) {
push @heredoc, $line;
last;
}
s/^$indent// for @heredoc, $token->{_terminator_line};
}
# The HereDoc is now fully parsed
return $t->_finalize_token->__TOKENIZER__on_char( $t );
}
# Add the line
push @heredoc, $line;
}
# End of file.
# Error: Didn't reach end of here-doc before end of file.
# If the here-doc block is not empty, look at the last line to determine if
# the here-doc terminator is missing a newline (which Perl would fail to
# compile but is easy to detect) or if the here-doc block was just not
# terminated at all (which Perl would fail to compile as well).
$token->{_terminator_line} = undef;
if ( @heredoc and defined $heredoc[-1] ) {
# See PPI::Tokenizer, the algorithm there adds a space at the end of the
# document that we need to make sure we remove.
if ( $t->{source_eof_chop} ) {
chop $heredoc[-1];
$t->{source_eof_chop} = '';
}
# Check if the last line of the file matches the terminator without
# newline at the end. If so, remove it from the content and set it as
# the terminator line.
$token->{_terminator_line} = pop @heredoc
if $self->_is_terminator( $token->{_terminator}, $heredoc[-1], $token->{_indented} );
}
if ( $token->{_indented} && $token->{_terminator_line} ) {
my $indent = $self->_indent( $token );
if ( $self->_is_match_indent( $token, $indent ) ) {
# Remove indent from here-doc as much as possible
s/^$indent// for @heredoc;
}
s/^$indent// for $token->{_terminator_line};
}
# Set a hint for PPI::Document->serialize so it can
# inexpensively repair it if needed when writing back out.
$token->{_damaged} = 1;
# The HereDoc is not fully parsed
$t->_finalize_token->__TOKENIZER__on_char( $t );
}
1;
=pod
=head1 TO DO
- Implement PPI::Token::Quote interface compatibility
- Check CPAN for any use of the null here-doc or here-doc-in-s///e
- Add support for the null here-doc
- Add support for here-doc in s///e
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,58 @@
package PPI::Token::Label;
=pod
=head1 NAME
PPI::Token::Label - Token class for a statement label
=head1 INHERITANCE
PPI::Token::Label
isa PPI::Token
isa PPI::Element
=head1 DESCRIPTION
A label is an identifier attached to a line or statements, to allow for
various types of flow control. For example, a loop might have a label
attached so that a C<last> or C<next> flow control statement can be used
from multiple levels below to reference the loop directly.
=head1 METHODS
There are no additional methods beyond those provided by the parent
L<PPI::Token> and L<PPI::Element> classes.
=cut
use strict;
use PPI::Token ();
our $VERSION = '1.270'; # VERSION
our @ISA = "PPI::Token";
1;
=pod
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,200 @@
package PPI::Token::Magic;
=pod
=head1 NAME
PPI::Token::Magic - Tokens representing magic variables
=head1 INHERITANCE
PPI::Token::Magic
isa PPI::Token::Symbol
isa PPI::Token
isa PPI::Element
=head1 SYNOPSIS
# When we say magic variables, we mean these...
$1 $2 $3 $4 $5 $6 $7 $8 $9
$_ $& $` $' $+ @+ %+ $* $. $/ $|
$\ $" $; $% $= $- @- %- $) $#
$~ $^ $: $? $! %! $@ $$ $< $>
$( $0 $[ $] @_ @* $} $, $#+ $#-
$^L $^A $^E $^C $^D $^F $^H
$^I $^M $^N $^O $^P $^R $^S
$^T $^V $^W $^X %^H
=head1 DESCRIPTION
C<PPI::Token::Magic> is a sub-class of L<PPI::Token::Symbol> which
identifies the token as "magic variable", one of the strange and
unusual variables that are connected to "things" behind the scenes.
Some are extremely common, like C<$_>, and others you will quite
probably never encounter in your Perl career.
=head1 METHODS
The class provides no additional methods, beyond those provided by
L<PPI::Token::Symbol>, L<PPI::Token> and L<PPI::Element>.
=cut
use strict;
use PPI::Token::Symbol ();
use PPI::Token::Unknown ();
use PPI::Singletons qw' %MAGIC $CURLY_SYMBOL ';
our $VERSION = '1.270'; # VERSION
our @ISA = "PPI::Token::Symbol";
sub __TOKENIZER__on_char {
my $t = $_[1];
# $c is the candidate new content
my $c = $t->{token}->{content} . substr( $t->{line}, $t->{line_cursor}, 1 );
# Do a quick first test so we don't have to do more than this one.
# All of the tests below match this one, so it should provide a
# small speed up. This regex should be updated to match the inside
# tests if they are changed.
if ( $c =~ /^ \$ .* [ \w : \$ \{ ] $/x ) {
if ( $c =~ /^(\$(?:\_[\w:]|::))/ or $c =~ /^\$\'[\w]/ ) {
# If and only if we have $'\d, it is not a
# symbol. (this was apparently a conscious choice)
# Note that $::0 on the other hand is legal
if ( $c =~ /^\$\'\d$/ ) {
# In this case, we have a magic plus a digit.
# Save the CURRENT token, and rerun the on_char
return $t->_finalize_token->__TOKENIZER__on_char( $t );
}
# A symbol in the style $_foo or $::foo or $'foo.
# Overwrite the current token
$t->{class} = $t->{token}->set_class('Symbol');
return PPI::Token::Symbol->__TOKENIZER__on_char( $t );
}
if ( $c =~ /^\$\$\w/ ) {
# This is really a scalar dereference. ( $$foo )
# Add the current token as the cast...
$t->{token} = PPI::Token::Cast->new( '$' );
$t->_finalize_token;
# ... and create a new token for the symbol
return $t->_new_token( 'Symbol', '$' );
}
if ( $c eq '$${' ) {
# This _might_ be a dereference of one of the
# control-character symbols.
pos $t->{line} = $t->{line_cursor} + 1;
if ( $t->{line} =~ m/$CURLY_SYMBOL/gc ) {
# This is really a dereference. ( $${^_foo} )
# Add the current token as the cast...
$t->{token} = PPI::Token::Cast->new( '$' );
$t->_finalize_token;
# ... and create a new token for the symbol
return $t->_new_token( 'Magic', '$' );
}
}
if ( $c eq '$#$' or $c eq '$#{' ) {
# This is really an index dereferencing cast, although
# it has the same two chars as the magic variable $#.
$t->{class} = $t->{token}->set_class('Cast');
return $t->_finalize_token->__TOKENIZER__on_char( $t );
}
if ( $c =~ /^(\$\#)\w/ ) {
# This is really an array index thingy ( $#array )
$t->{token} = PPI::Token::ArrayIndex->new( "$1" );
return PPI::Token::ArrayIndex->__TOKENIZER__on_char( $t );
}
if ( $c =~ /^\$\^\w+$/o ) {
# It's an escaped char magic... maybe ( like $^M )
my $next = substr( $t->{line}, $t->{line_cursor}+1, 1 ); # Peek ahead
if ($MAGIC{$c} && (!$next || $next !~ /\w/)) {
$t->{token}->{content} = $c;
$t->{line_cursor}++;
} else {
# Maybe it's a long magic variable like $^WIDE_SYSTEM_CALLS
return 1;
}
}
if ( $c =~ /^\$\#\{/ ) {
# The $# is actually a cast, and { is its block
# Add the current token as the cast...
$t->{token} = PPI::Token::Cast->new( '$#' );
$t->_finalize_token;
# ... and create a new token for the block
return $t->_new_token( 'Structure', '{' );
}
} elsif ($c =~ /^%\^/) {
return 1 if $c eq '%^';
# It's an escaped char magic... maybe ( like %^H )
if ($MAGIC{$c}) {
$t->{token}->{content} = $c;
$t->{line_cursor}++;
} else {
# Back off, treat '%' as an operator
chop $t->{token}->{content};
bless $t->{token}, $t->{class} = 'PPI::Token::Operator';
$t->{line_cursor}--;
}
}
if ( $MAGIC{$c} ) {
# $#+ and $#-
$t->{line_cursor} += length( $c ) - length( $t->{token}->{content} );
$t->{token}->{content} = $c;
} else {
pos $t->{line} = $t->{line_cursor};
if ( $t->{line} =~ m/($CURLY_SYMBOL)/gc ) {
# control character symbol (e.g. ${^MATCH})
$t->{token}->{content} .= $1;
$t->{line_cursor} += length $1;
} elsif ( $c =~ /^\$\d+$/ and $t->{line} =~ /\G(\d+)/gc ) {
# Grab trailing digits of regex capture variables.
$t->{token}{content} .= $1;
$t->{line_cursor} += length $1;
}
}
# End the current magic token, and recheck
$t->_finalize_token->__TOKENIZER__on_char( $t );
}
# Our version of canonical is plain simple
sub canonical { $_[0]->content }
1;
=pod
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,163 @@
package PPI::Token::Number;
=pod
=head1 NAME
PPI::Token::Number - Token class for a number
=head1 SYNOPSIS
$n = 1234; # decimal integer
$n = 0b1110011; # binary integer
$n = 01234; # octal integer
$n = 0x1234; # hexadecimal integer
$n = 12.34e-56; # exponential notation ( currently not working )
=head1 INHERITANCE
PPI::Token::Number
isa PPI::Token
isa PPI::Element
=head1 DESCRIPTION
The C<PPI::Token::Number> class is used for tokens that represent numbers,
in the various types that Perl supports.
=head1 METHODS
=cut
use strict;
use PPI::Token ();
our $VERSION = '1.270'; # VERSION
our @ISA = "PPI::Token";
=pod
=head2 base
The C<base> method is provided by all of the ::Number subclasses.
This is 10 for decimal, 16 for hexadecimal, 2 for binary, etc.
=cut
sub base() { 10 }
=pod
=head2 literal
Return the numeric value of this token.
=cut
sub literal {
return 0 + $_[0]->_literal;
}
sub _literal {
# De-sugar the string representation
my $self = shift;
my $string = $self->content;
$string =~ s/^\+//;
$string =~ s/_//g;
return $string;
}
#####################################################################
# Tokenizer Methods
sub __TOKENIZER__on_char {
my $class = shift;
my $t = shift;
my $char = substr( $t->{line}, $t->{line_cursor}, 1 );
# Allow underscores straight through
return 1 if $char eq '_';
# Handle the conversion from an unknown to known type.
# The regex covers "potential" hex/bin/octal number.
my $token = $t->{token};
if ( $token->{content} =~ /^-?0_*$/ ) {
# This could be special
if ( $char eq 'x' || $char eq 'X' ) {
$t->{class} = $t->{token}->set_class( 'Number::Hex' );
return 1;
} elsif ( $char eq 'b' || $char eq 'B' ) {
$t->{class} = $t->{token}->set_class( 'Number::Binary' );
return 1;
} elsif ( $char =~ /\d/ ) {
# You cannot have 8s and 9s on octals
if ( $char eq '8' or $char eq '9' ) {
$token->{_error} = "Illegal character in octal number '$char'";
}
$t->{class} = $t->{token}->set_class( 'Number::Octal' );
return 1;
}
}
# Handle the easy case, integer or real.
return 1 if $char =~ /\d/o;
if ( $char eq '.' ) {
$t->{class} = $t->{token}->set_class( 'Number::Float' );
return 1;
}
if ( $char eq 'e' || $char eq 'E' ) {
$t->{class} = $t->{token}->set_class( 'Number::Exp' );
return 1;
}
# Doesn't fit a special case, or is after the end of the token
# End of token.
$t->_finalize_token->__TOKENIZER__on_char( $t );
}
1;
=pod
=head1 CAVEATS
Compared to Perl, the number tokenizer is too liberal about allowing
underscores anywhere. For example, the following is a syntax error in
Perl, but is allowed in PPI:
0_b10
=head1 TO DO
- Treat v-strings as binary strings or barewords, not as "base-256"
numbers
- Break out decimal integers into their own subclass?
- Implement literal()
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,117 @@
package PPI::Token::Number::Binary;
=pod
=head1 NAME
PPI::Token::Number::Binary - Token class for a binary number
=head1 SYNOPSIS
$n = 0b1110011; # binary integer
=head1 INHERITANCE
PPI::Token::Number::Binary
isa PPI::Token::Number
isa PPI::Token
isa PPI::Element
=head1 DESCRIPTION
The C<PPI::Token::Number::Binary> class is used for tokens that
represent base-2 numbers.
=head1 METHODS
=cut
use strict;
use PPI::Token::Number ();
our $VERSION = '1.270'; # VERSION
our @ISA = "PPI::Token::Number";
=pod
=head2 base
Returns the base for the number: 2.
=cut
sub base() { 2 }
=pod
=head2 literal
Return the numeric value of this token.
=cut
sub literal {
my $self = shift;
return if $self->{_error};
my $str = $self->_literal;
my $neg = $str =~ s/^\-//;
$str =~ s/^0[bB]//;
my $val = 0;
for my $bit ( $str =~ m/(.)/g ) {
$val = $val * 2 + $bit;
}
return $neg ? -$val : $val;
}
#####################################################################
# Tokenizer Methods
sub __TOKENIZER__on_char {
my $class = shift;
my $t = shift;
my $char = substr( $t->{line}, $t->{line_cursor}, 1 );
# Allow underscores straight through
return 1 if $char eq '_';
if ( $char =~ /[\w\d]/ ) {
unless ( $char eq '1' or $char eq '0' ) {
# Add a warning if it contains non-binary chars
$t->{token}->{_error} = "Illegal character in binary number '$char'";
}
return 1;
}
# Doesn't fit a special case, or is after the end of the token
# End of token.
$t->_finalize_token->__TOKENIZER__on_char( $t );
}
1;
=pod
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Chris Dolan E<lt>cdolan@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2006 Chris Dolan.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,146 @@
package PPI::Token::Number::Exp;
=pod
=head1 NAME
PPI::Token::Number::Exp - Token class for an exponential notation number
=head1 SYNOPSIS
$n = 1.0e-2;
$n = 1e+2;
=head1 INHERITANCE
PPI::Token::Number::Exp
isa PPI::Token::Number::Float
isa PPI::Token::Number
isa PPI::Token
isa PPI::Element
=head1 DESCRIPTION
The C<PPI::Token::Number::Exp> class is used for tokens that
represent floating point numbers with exponential notation.
=head1 METHODS
=cut
use strict;
use PPI::Token::Number::Float ();
our $VERSION = '1.270'; # VERSION
our @ISA = "PPI::Token::Number::Float";
=pod
=head2 literal
Return the numeric value of this token.
=cut
sub literal {
my $self = shift;
return if $self->{_error};
my ($mantissa, $exponent) = split m/e/i, $self->_literal;
my $neg = $mantissa =~ s/^\-//;
$mantissa =~ s/^\./0./;
$exponent =~ s/^\+//;
# Must cast exponent as numeric type, due to string type '00' exponent
# creating false positive condition in for() loop below, causing infinite loop
$exponent += 0;
# This algorithm is reasonably close to the S_mulexp10()
# algorithm from the Perl source code, so it should arrive
# at the same answer as Perl most of the time.
my $negpow = 0;
if ($exponent < 0) {
$negpow = 1;
$exponent *= -1;
}
my $result = 1;
my $power = 10;
for (my $bit = 1; $exponent; $bit = $bit << 1) {
if ($exponent & $bit) {
$exponent = $exponent ^ $bit;
$result *= $power;
}
$power *= $power;
}
my $val = $neg ? 0 - $mantissa : $mantissa;
return $negpow ? $val / $result : $val * $result;
}
#####################################################################
# Tokenizer Methods
sub __TOKENIZER__on_char {
my $class = shift;
my $t = shift;
my $char = substr( $t->{line}, $t->{line_cursor}, 1 );
# To get here, the token must have already encountered an 'E'
# Allow underscores straight through
return 1 if $char eq '_';
# Allow digits
return 1 if $char =~ /\d/o;
# Start of exponent is special
if ( $t->{token}->{content} =~ /e$/i ) {
# Allow leading +/- in exponent
return 1 if $char eq '-' || $char eq '+';
# Invalid character in exponent. Recover
if ( $t->{token}->{content} =~ s/\.(e)$//i ) {
my $word = $1;
$t->{class} = $t->{token}->set_class('Number');
$t->_new_token('Operator', '.');
$t->_new_token('Word', $word);
return $t->{class}->__TOKENIZER__on_char( $t );
}
else {
$t->{token}->{_error} = "Illegal character in exponent '$char'";
}
}
# Doesn't fit a special case, or is after the end of the token
# End of token.
$t->_finalize_token->__TOKENIZER__on_char( $t );
}
1;
=pod
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Chris Dolan E<lt>cdolan@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2006 Chris Dolan.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,140 @@
package PPI::Token::Number::Float;
=pod
=head1 NAME
PPI::Token::Number::Float - Token class for a floating-point number
=head1 SYNOPSIS
$n = 1.234;
=head1 INHERITANCE
PPI::Token::Number::Float
isa PPI::Token::Number
isa PPI::Token
isa PPI::Element
=head1 DESCRIPTION
The C<PPI::Token::Number::Float> class is used for tokens that
represent floating point numbers. A float is identified by n decimal
point. Exponential notation (the C<e> or C<E>) is handled by the
PPI::Token::Number::Exp class.
=head1 METHODS
=cut
use strict;
use PPI::Token::Number ();
our $VERSION = '1.270'; # VERSION
our @ISA = "PPI::Token::Number";
=pod
=head2 base
Returns the base for the number: 10.
=cut
sub base() { 10 }
=pod
=head2 literal
Return the numeric value of this token.
=cut
sub literal {
my $self = shift;
my $str = $self->_literal;
my $neg = $str =~ s/^\-//;
$str =~ s/^\./0./;
my $val = 0+$str;
return $neg ? -$val : $val;
}
#####################################################################
# Tokenizer Methods
sub __TOKENIZER__on_char {
my $class = shift;
my $t = shift;
my $char = substr( $t->{line}, $t->{line_cursor}, 1 );
# Allow underscores straight through
return 1 if $char eq '_';
# Allow digits
return 1 if $char =~ /\d/o;
if ( $char eq '.' ) { # A second decimal point? That gets complicated.
if ( $t->{token}{content} =~ /\.$/ ) {
# We have a .., which is an operator. Take the . off the end of the
# token and finish it, then make the .. operator.
chop $t->{token}{content};
$t->{class} = $t->{token}->set_class( 'Number' );
$t->_new_token('Operator', '..');
return 0;
} elsif ( $t->{token}{content} =~ /\._/ ) {
($t->{token}{content}, my $bareword)
= split /\./, $t->{token}{content};
$t->{class} = $t->{token}->set_class( 'Number' );
$t->_new_token('Operator', '.');
$t->_new_token('Word', $bareword);
$t->_new_token('Operator', '.');
return 0;
} else {
$t->{class} = $t->{token}->set_class( 'Number::Version' );
return 1;
}
}
# perl seems to regard pretty much anything that's not strictly an exp num
# as float + stuff
my $char2 = substr $t->{line}, $t->{line_cursor}+1, 1;
if ("$char$char2" =~ /[eE][0-9+-]/) {
$t->{class} = $t->{token}->set_class( 'Number::Exp' );
return 1;
}
# Doesn't fit a special case, or is after the end of the token
# End of token.
$t->_finalize_token->__TOKENIZER__on_char( $t );
}
1;
=pod
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Chris Dolan E<lt>cdolan@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2006 Chris Dolan.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,108 @@
package PPI::Token::Number::Hex;
=pod
=head1 NAME
PPI::Token::Number::Hex - Token class for a binary number
=head1 SYNOPSIS
$n = 0x1234; # hexadecimal integer
=head1 INHERITANCE
PPI::Token::Number::Hex
isa PPI::Token::Number
isa PPI::Token
isa PPI::Element
=head1 DESCRIPTION
The C<PPI::Token::Number::Hex> class is used for tokens that
represent base-16 numbers.
=head1 METHODS
=cut
use strict;
use PPI::Token::Number ();
our $VERSION = '1.270'; # VERSION
our @ISA = "PPI::Token::Number";
=pod
=head2 base
Returns the base for the number: 16.
=cut
sub base() { 16 }
=pod
=head2 literal
Return the numeric value of this token.
=cut
sub literal {
my $self = shift;
my $str = $self->_literal;
my $neg = $str =~ s/^\-//;
my $val = hex lc( $str ); # lc for compatibility with perls before 5.14
return $neg ? -$val : $val;
}
#####################################################################
# Tokenizer Methods
sub __TOKENIZER__on_char {
my $class = shift;
my $t = shift;
my $char = substr( $t->{line}, $t->{line_cursor}, 1 );
# Allow underscores straight through
return 1 if $char eq '_';
if ( $char =~ /[[:xdigit:]]/ ) {
return 1;
}
# Doesn't fit a special case, or is after the end of the token
# End of token.
$t->_finalize_token->__TOKENIZER__on_char( $t );
}
1;
=pod
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Chris Dolan E<lt>cdolan@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2006 Chris Dolan.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,113 @@
package PPI::Token::Number::Octal;
=pod
=head1 NAME
PPI::Token::Number::Octal - Token class for a binary number
=head1 SYNOPSIS
$n = 0777; # octal integer
=head1 INHERITANCE
PPI::Token::Number::Octal
isa PPI::Token::Number
isa PPI::Token
isa PPI::Element
=head1 DESCRIPTION
The C<PPI::Token::Number::Octal> class is used for tokens that
represent base-8 numbers.
=head1 METHODS
=cut
use strict;
use PPI::Token::Number ();
our $VERSION = '1.270'; # VERSION
our @ISA = "PPI::Token::Number";
=pod
=head2 base
Returns the base for the number: 8.
=cut
sub base() { 8 }
=pod
=head2 literal
Return the numeric value of this token.
=cut
sub literal {
my $self = shift;
return if $self->{_error};
my $str = $self->_literal;
my $neg = $str =~ s/^\-//;
my $val = oct $str;
return $neg ? -$val : $val;
}
#####################################################################
# Tokenizer Methods
sub __TOKENIZER__on_char {
my $class = shift;
my $t = shift;
my $char = substr( $t->{line}, $t->{line_cursor}, 1 );
# Allow underscores straight through
return 1 if $char eq '_';
if ( $char =~ /\d/ ) {
# You cannot have 8s and 9s on octals
if ( $char eq '8' or $char eq '9' ) {
$t->{token}->{_error} = "Illegal character in octal number '$char'";
}
return 1;
}
# Doesn't fit a special case, or is after the end of the token
# End of token.
$t->_finalize_token->__TOKENIZER__on_char( $t );
}
1;
=pod
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Chris Dolan E<lt>cdolan@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2006 Chris Dolan.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,164 @@
package PPI::Token::Number::Version;
=pod
=head1 NAME
PPI::Token::Number::Version - Token class for a byte-packed number
=head1 SYNOPSIS
$n = 1.1.0;
$n = 127.0.0.1;
$n = 10_000.10_000.10_000;
$n = v1.2.3.4
=head1 INHERITANCE
PPI::Token::Number::Version
isa PPI::Token::Number
isa PPI::Token
isa PPI::Element
=head1 DESCRIPTION
The C<PPI::Token::Number::Version> class is used for tokens that have
multiple decimal points. In truth, these aren't treated like numbers
at all by Perl, but they look like numbers to a parser.
=head1 METHODS
=cut
use strict;
use PPI::Token::Number ();
our $VERSION = '1.270'; # VERSION
our @ISA = "PPI::Token::Number";
=pod
=head2 base
Returns the base for the number: 256.
=cut
sub base() { 256 }
=pod
=head2 literal
Return the numeric value of this token.
=cut
sub literal {
my $self = shift;
my $content = $self->{content};
$content =~ s/^v//;
return join '', map { chr $_ } ( split /\./, $content );
}
#####################################################################
# Tokenizer Methods
sub __TOKENIZER__on_char {
my $class = shift;
my $t = shift;
my $char = substr( $t->{line}, $t->{line_cursor}, 1 );
# Allow digits
return 1 if $char =~ /\d/o;
if( $char eq '_' ) {
return 1 if $t->{token}{content} !~ /\.$/;
chop $t->{token}->{content};
$t->{class} = $t->{token}->set_class( 'Number::Float' )
if $t->{token}{content} !~ /\..+\./;
$t->_new_token('Operator', '.');
$t->_new_token('Word', '_');
return 0;
}
# Is this a second decimal point in a row? Then the '..' operator
if ( $char eq '.' ) {
if ( $t->{token}->{content} =~ /\.$/ ) {
# We have a .., which is an operator.
# Take the . off the end of the token..
# and finish it, then make the .. operator.
chop $t->{token}->{content};
$t->{class} = $t->{token}->set_class( 'Number::Float' )
if $t->{token}{content} !~ /\..+\./;
$t->_new_token('Operator', '..');
return 0;
} else {
return 1;
}
}
# Doesn't fit a special case, or is after the end of the token
# End of token.
$t->_finalize_token->__TOKENIZER__on_char( $t );
}
sub __TOKENIZER__commit {
my $t = $_[1];
# Capture the rest of the token
pos $t->{line} = $t->{line_cursor};
# This was not a v-string after all (it's a word);
return PPI::Token::Word->__TOKENIZER__commit($t)
if $t->{line} !~ m/\G(v\d[_\d]*(?:\.\d[_\d]*)+|v\d[_\d]*\b)/gc;
my $content = $1;
# If there are no periods this could be a word starting with v\d
# Forced to be a word. Done.
return PPI::Token::Word->__TOKENIZER__commit($t)
if $content !~ /\./ and $t->__current_token_is_forced_word($content);
# This is a v-string
$t->{line_cursor} += length $content;
$t->_new_token( 'Number::Version', $content );
$t->_finalize_token->__TOKENIZER__on_char($t);
}
1;
=pod
=head1 BUGS
- Does not handle leading minus sign correctly. Should translate to a DashedWord.
See L<http://perlmonks.org/?node_id=574573>
-95.0.1.0 --> "-_\000\cA\000"
-96.0.1.0 --> Argument "`\0^A\0" isn't numeric in negation (-)
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Chris Dolan E<lt>cdolan@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2006 Chris Dolan.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,124 @@
package PPI::Token::Operator;
=pod
=head1 NAME
PPI::Token::Operator - Token class for operators
=head1 INHERITANCE
PPI::Token::Operator
isa PPI::Token
isa PPI::Element
=head1 SYNOPSIS
# This is the list of valid operators
++ -- ** ! ~ + -
=~ !~ * / % x
<< >> lt gt le ge cmp ~~
== != <=> . .. ... ,
& | ^ && || //
? : **= += -= .= *= /=
%= x= &= |= ^= <<= >>= &&=
||= //= < > <= >= <> => ->
and or xor not eq ne <<>>
=head1 DESCRIPTION
All operators in PPI are created as C<PPI::Token::Operator> objects,
including the ones that may superficially look like a L<PPI::Token::Word>
object.
=head1 METHODS
There are no additional methods beyond those provided by the parent
L<PPI::Token> and L<PPI::Element> classes.
=cut
use strict;
use PPI::Token ();
use PPI::Singletons '%OPERATOR';
our $VERSION = '1.270'; # VERSION
our @ISA = "PPI::Token";
#####################################################################
# Tokenizer Methods
sub __TOKENIZER__on_char {
my $t = $_[1];
my $char = substr( $t->{line}, $t->{line_cursor}, 1 );
# Are we still an operator if we add the next character
my $content = $t->{token}->{content};
# special case for <<>> operator
if(length($content) < 4 &&
$content . substr( $t->{line}, $t->{line_cursor}, 4 - length($content) ) eq '<<>>') {
return 1;
}
return 1 if $OPERATOR{ $content . $char };
# Handle the special case of a .1234 decimal number
if ( $content eq '.' ) {
if ( $char =~ /^[0-9]$/ ) {
# This is a decimal number
$t->{class} = $t->{token}->set_class('Number::Float');
return $t->{class}->__TOKENIZER__on_char( $t );
}
}
# Handle the special case if we might be a here-doc
if ( $content eq '<<' ) {
pos $t->{line} = $t->{line_cursor};
# Either <<FOO or << 'FOO' or <<\FOO or
# <<~FOO or <<~ 'FOO' or <<~\FOO
### Is the zero-width look-ahead assertion really
### supposed to be there?
if ( $t->{line} =~ m/\G ~? (?: (?!\d)\w | \s*['"`] | \\\w ) /gcx ) {
# This is a here-doc.
# Change the class and move to the HereDoc's own __TOKENIZER__on_char method.
$t->{class} = $t->{token}->set_class('HereDoc');
return $t->{class}->__TOKENIZER__on_char( $t );
}
}
# Handle the special case of the null Readline
$t->{class} = $t->{token}->set_class('QuoteLike::Readline')
if $content eq '<>' or $content eq '<<>>';
# Finalize normally
$t->_finalize_token->__TOKENIZER__on_char( $t );
}
1;
=pod
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,159 @@
package PPI::Token::Pod;
=pod
=head1 NAME
PPI::Token::Pod - Sections of POD in Perl documents
=head1 INHERITANCE
PPI::Token::Pod
isa PPI::Token
isa PPI::Element
=head1 DESCRIPTION
A single C<PPI::Token::Pod> object represents a complete section of POD
documentation within a Perl document.
=head1 METHODS
This class provides some additional methods beyond those provided by its
L<PPI::Token> and L<PPI::Element> parent classes.
=cut
use strict;
use Params::Util qw{_INSTANCE};
use PPI::Token ();
our $VERSION = '1.270'; # VERSION
our @ISA = "PPI::Token";
#####################################################################
# PPI::Token::Pod Methods
=pod
=head2 merge @podtokens
The C<merge> constructor takes a number of C<PPI::Token::Pod> objects,
and returns a new object that represents one combined POD block with
the content of all of them.
Returns a new C<PPI::Token::Pod> object, or C<undef> on error.
=cut
sub merge {
my $class = (! ref $_[0]) ? shift : return undef;
# Check there are no bad arguments
if ( grep { ! _INSTANCE($_, 'PPI::Token::Pod') } @_ ) {
return undef;
}
# Get the tokens, and extract the lines
my @content = ( map { [ $_->lines ] } @_ ) or return undef;
# Remove the leading =pod tags, trailing =cut tags, and any empty lines
# between them and the pod contents.
foreach my $pod ( @content ) {
# Leading =pod tag
if ( @$pod and $pod->[0] =~ /^=pod\b/o ) {
shift @$pod;
}
# Trailing =cut tag
if ( @$pod and $pod->[-1] =~ /^=cut\b/o ) {
pop @$pod;
}
# Leading and trailing empty lines
while ( @$pod and $pod->[0] eq '' ) { shift @$pod }
while ( @$pod and $pod->[-1] eq '' ) { pop @$pod }
}
# Remove any empty pod sections, and add the =pod and =cut tags
# for the merged pod back to it.
@content = ( [ '=pod' ], grep { @$_ } @content, [ '=cut' ] );
# Create the new object
$class->new( join "\n", map { join( "\n", @$_ ) . "\n" } @content );
}
=pod
=head2 lines
The C<lines> method takes the string of POD and breaks it into lines,
returning them as a list.
=cut
sub lines {
split /(?:\015{1,2}\012|\015|\012)/, $_[0]->{content};
}
#####################################################################
# PPI::Element Methods
### XS -> PPI/XS.xs:_PPI_Token_Pod__significant 0.900+
sub significant() { '' }
#####################################################################
# Tokenizer Methods
sub __TOKENIZER__on_line_start {
my $t = $_[1];
# Add the line to the token first
$t->{token}->{content} .= $t->{line};
# Check the line to see if it is a =cut line
if ( $t->{line} =~ /^=(\w+)/ ) {
# End of the token
$t->_finalize_token if $1 eq 'cut';
}
0;
}
1;
=pod
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,115 @@
package PPI::Token::Prototype;
=pod
=head1 NAME
PPI::Token::Prototype - A subroutine prototype descriptor
=head1 INHERITANCE
PPI::Token::End
isa PPI::Token
isa PPI::Element
=head1 SYNOPSIS
sub ($@) prototype;
=head1 DESCRIPTION
Although it sort of looks like a list or condition, a subroutine
prototype is a lot more like a string. Its job is to provide hints
to the perl compiler on what type of arguments a particular subroutine
expects, which the compiler uses to validate parameters at compile-time,
and allows programmers to use the functions without explicit parameter
parens.
Due to the rise of OO Perl coding, which ignores these prototypes, they
are most often used to allow for constant-like things, and to "extend"
the language and create things that act like keywords and core functions.
# Create something that acts like a constant
sub MYCONSTANT () { 10 }
# Create the "any" core-looking function
sub any (&@) { ... }
if ( any { $_->cute } @babies ) {
...
}
=head1 METHODS
This class provides one additional method beyond those defined by the
L<PPI::Token> and L<PPI::Element> parent classes.
=cut
use strict;
use PPI::Token ();
our $VERSION = '1.270'; # VERSION
our @ISA = "PPI::Token";
sub __TOKENIZER__on_char {
my $class = shift;
my $t = shift;
# Suck in until we find the closing paren (or the end of line)
pos $t->{line} = $t->{line_cursor};
die "regex should always match" if $t->{line} !~ m/\G(.*?(?:\)|$))/gc;
$t->{token}->{content} .= $1;
$t->{line_cursor} += length $1;
# Shortcut if end of line
return 0 unless $1 =~ /\)$/;
# Found the closing paren
$t->_finalize_token->__TOKENIZER__on_char( $t );
}
=pod
=head2 prototype
The C<prototype> accessor returns the actual prototype pattern, stripped
of flanking parens and of all whitespace. This mirrors the behavior of
the Perl C<prototype> builtin function.
Note that stripping parens and whitespace means that the return of
C<prototype> can be an empty string.
=cut
sub prototype {
my $self = shift;
my $proto = $self->content;
$proto =~ s/(^\(|\)$|\s+)//g;
$proto;
}
1;
=pod
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,120 @@
package PPI::Token::Quote;
=pod
=head1 NAME
PPI::Token::Quote - String quote abstract base class
=head1 INHERITANCE
PPI::Token::Quote
isa PPI::Token
isa PPI::Element
=head1 DESCRIPTION
The C<PPI::Token::Quote> class is never instantiated, and simply
provides a common abstract base class for the four quote classes.
In PPI, a "quote" is limited to only the quote-like things that
themselves directly represent a string. (although this includes
double quotes with interpolated elements inside them, note that
L<String::InterpolatedVariables> allows to extract them).
The subclasses of C<PPI::Token::Quote> are:
=over 2
=item C<''> - L<PPI::Token::Quote::Single>
=item C<q{}> - L<PPI::Token::Quote::Literal>
=item C<""> - L<PPI::Token::Quote::Double>
=item C<qq{}> - L<PPI::Token::Quote::Interpolate>
=back
The names are hopefully obvious enough not to have to explain what
each class is here. See their respective pages for more details.
Please note that although the here-doc B<does> represent a literal
string, it is such a nasty piece of work that in L<PPI> it is given the
honor of its own token class (L<PPI::Token::HereDoc>).
=head1 METHODS
=cut
use strict;
use PPI::Token ();
our $VERSION = '1.270'; # VERSION
our @ISA = "PPI::Token";
#####################################################################
# PPI::Token::Quote Methods
=pod
=head2 string
The C<string> method is provided by all four ::Quote classes. It won't
get you the actual literal Perl value, but it will strip off the wrapping
of the quotes.
# The following all return foo from the ->string method
'foo'
"foo"
q{foo}
qq <foo>
=cut
#sub string {
# my $class = ref $_[0] || $_[0];
# die "$class does not implement method ->string";
#}
=pod
=head2 literal
The C<literal> method is provided by ::Quote::Literal and
::Quote::Single. This returns the value of the string as Perl sees
it: without the quote marks and with C<\\> and C<\'> resolved to C<\>
and C<'>.
The C<literal> method is not implemented by ::Quote::Double or
::Quote::Interpolate yet.
=cut
1;
=pod
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,138 @@
package PPI::Token::Quote::Double;
=pod
=head1 NAME
PPI::Token::Quote::Double - A standard "double quote" token
=head1 INHERITANCE
PPI::Token::Quote::Double
isa PPI::Token::Quote
isa PPI::Token
isa PPI::Element
=head1 DESCRIPTION
A C<PPI::Token::Quote::Double> object represents a double-quoted
interpolating string.
The string is treated as a single entity, L<PPI> will not try to
understand what is in the string during the parsing process.
=head1 METHODS
There are several methods available for C<PPI::Token::Quote::Double>, beyond
those provided by the parent L<PPI::Token::Quote>, L<PPI::Token> and
L<PPI::Element> classes.
=cut
use strict;
use Params::Util qw{_INSTANCE};
use PPI::Token::Quote ();
use PPI::Token::_QuoteEngine::Simple ();
our $VERSION = '1.270'; # VERSION
our @ISA = qw{
PPI::Token::_QuoteEngine::Simple
PPI::Token::Quote
};
#####################################################################
# PPI::Token::Quote::Double Methods
=pod
=head2 interpolations
The interpolations method checks to see if the double quote actually
contains any interpolated variables.
Returns true if the string contains interpolations, or false if not.
=cut
# Upgrade: Return the interpolated substrings.
# Upgrade: Returns parsed expressions.
sub interpolations {
# Are there any unescaped $things in the string
!! ($_[0]->content =~ /(?<!\\)(?:\\\\)*[\$\@]/);
}
=pod
=head2 simplify
For various reasons, some people find themselves compelled to have
their code in the simplest form possible.
The C<simplify> method will, if possible, modify a simple double-quoted
string token in place, turning it into the equivalent single-quoted
string. If the token is modified, it is reblessed into the
L<PPI::Token::Quote::Single> package.
Because the length of the content is not changed, there is no need
to call the document's C<flush_locations> method.
The object itself is returned as a convenience.
=cut
sub simplify {
# This only works on EXACTLY this class
my $self = _INSTANCE(shift, 'PPI::Token::Quote::Double') or return undef;
# Don't bother if there are characters that could complicate things
my $content = $self->content;
my $value = substr($content, 1, length($content) - 2);
return $self if $value =~ /[\\\$@\'\"]/;
# Change the token to a single string
$self->{content} = "'$value'";
bless $self, 'PPI::Token::Quote::Single';
}
#####################################################################
# PPI::Token::Quote Methods
sub string {
my $str = $_[0]->{content};
substr( $str, 1, length($str) - 2 );
}
1;
=pod
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,76 @@
package PPI::Token::Quote::Interpolate;
=pod
=head1 NAME
PPI::Token::Quote::Interpolate - The interpolation quote-like operator
=head1 INHERITANCE
PPI::Token::Quote::Interpolate
isa PPI::Token::Quote
isa PPI::Token
isa PPI::Element
=head1 DESCRIPTION
A C<PPI::Token::Quote::Interpolate> object represents a single
interpolation quote-like operator, such as C<qq{$foo bar $baz}>.
=head1 METHODS
There are no methods available for C<PPI::Token::Quote::Interpolate>
beyond those provided by the parent L<PPI::Token::Quote>, L<PPI::Token> and
L<PPI::Element> classes.
=cut
use strict;
use PPI::Token::Quote ();
use PPI::Token::_QuoteEngine::Full ();
our $VERSION = '1.270'; # VERSION
our @ISA = qw{
PPI::Token::_QuoteEngine::Full
PPI::Token::Quote
};
#####################################################################
# PPI::Token::Quote Methods
sub string {
my $self = shift;
my @sections = $self->_sections;
my $str = $sections[0];
substr( $self->{content}, $str->{position}, $str->{size} );
}
1;
=pod
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,80 @@
package PPI::Token::Quote::Literal;
=pod
=head1 NAME
PPI::Token::Quote::Literal - The literal quote-like operator
=head1 INHERITANCE
PPI::Token::Quote::Literal
isa PPI::Token::Quote
isa PPI::Token
isa PPI::Element
=head1 DESCRIPTION
A C<PPI::Token::Quote::Literal> object represents a single literal
quote-like operator, such as C<q{foo bar}>.
=head1 METHODS
There are no methods available for C<PPI::Token::Quote::Literal> beyond
those provided by the parent L<PPI::Token::Quote>, L<PPI::Token> and
L<PPI::Element> classes.
=cut
use strict;
use PPI::Token::Quote ();
use PPI::Token::_QuoteEngine::Full ();
our $VERSION = '1.270'; # VERSION
our @ISA = qw{
PPI::Token::_QuoteEngine::Full
PPI::Token::Quote
};
#####################################################################
# PPI::Token::Quote Methods
sub string {
my $self = shift;
my @sections = $self->_sections;
my $str = $sections[0];
substr( $self->{content}, $str->{position}, $str->{size} );
}
# Use the same implementation as another module
*literal = *PPI::Token::Quote::Single::literal;
1;
=pod
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,93 @@
package PPI::Token::Quote::Single;
=pod
=head1 NAME
PPI::Token::Quote::Single - A 'single quote' token
=head1 INHERITANCE
PPI::Token::Quote::Single
isa PPI::Token::Quote
isa PPI::Token
isa PPI::Element
=head1 SYNOPSIS
'This is a single quote'
q{This is a literal, but NOT a single quote}
=head1 DESCRIPTION
A C<PPI::Token::Quote::Single> object represents a single quoted string
literal.
=head1 METHODS
There are no methods available for C<PPI::Token::Quote::Single> beyond
those provided by the parent L<PPI::Token::Quote>, L<PPI::Token> and
L<PPI::Element> classes.
=cut
use strict;
use PPI::Token::Quote ();
use PPI::Token::_QuoteEngine::Simple ();
our $VERSION = '1.270'; # VERSION
our @ISA = qw{
PPI::Token::_QuoteEngine::Simple
PPI::Token::Quote
};
#####################################################################
# PPI::Token::Quote Methods
sub string {
my $str = $_[0]->{content};
substr( $str, 1, length($str) - 2 );
}
my %UNESCAPE = (
"\\'" => "'",
"\\\\" => "\\",
);
sub literal {
# Unescape \\ and \' ONLY
my $str = $_[0]->string;
$str =~ s/(\\.)/$UNESCAPE{$1} || $1/ge;
return $str;
}
1;
=pod
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,77 @@
package PPI::Token::QuoteLike;
=pod
=head1 NAME
PPI::Token::QuoteLike - Quote-like operator abstract base class
=head1 INHERITANCE
PPI::Token::QuoteLike
isa PPI::Token
isa PPI::Element
=head1 DESCRIPTION
The C<PPI::Token::QuoteLike> class is never instantiated, and simply
provides a common abstract base class for the five quote-like operator
classes. In PPI, a "quote-like" is the set of quote-like things that
exclude the string quotes and regular expressions.
The subclasses of C<PPI::Token::QuoteLike> are:
=over 2
=item qw{} - L<PPI::Token::QuoteLike::Words>
=item `` - L<PPI::Token::QuoteLike::Backtick>
=item qx{} - L<PPI::Token::QuoteLike::Command>
=item qr// - L<PPI::Token::QuoteLike::Regexp>
=item <FOO> - L<PPI::Token::QuoteLike::Readline>
=back
The names are hopefully obvious enough not to have to explain what
each class is. See their pages for more details.
You may note that the backtick and command quote-like are treated
separately, even though they do the same thing. This is intentional,
as the inherit from and are processed by two different parts of the
PPI's quote engine.
=cut
use strict;
use PPI::Token ();
our $VERSION = '1.270'; # VERSION
our @ISA = "PPI::Token";
1;
=pod
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,62 @@
package PPI::Token::QuoteLike::Backtick;
=pod
=head1 NAME
PPI::Token::QuoteLike::Backtick - A `backticks` command token
=head1 INHERITANCE
PPI::Token::QuoteLike::Backtick
isa PPI::Token::QuoteLike
isa PPI::Token
isa PPI::Element
=head1 DESCRIPTION
A C<PPI::Token::QuoteLike::Backtick> object represents a command output
capturing quote.
=head1 METHODS
There are no methods available for C<PPI::Token::QuoteLike::Backtick>
beyond those provided by the parent L<PPI::Token::QuoteLike>, L<PPI::Token>
and L<PPI::Element> classes.
=cut
use strict;
use PPI::Token::QuoteLike ();
use PPI::Token::_QuoteEngine::Simple ();
our $VERSION = '1.270'; # VERSION
our @ISA = qw{
PPI::Token::_QuoteEngine::Simple
PPI::Token::QuoteLike
};
1;
=pod
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,62 @@
package PPI::Token::QuoteLike::Command;
=pod
=head1 NAME
PPI::Token::QuoteLike::Command - The command quote-like operator
=head1 INHERITANCE
PPI::Token::QuoteLike::Command
isa PPI::Token::QuoteLike
isa PPI::Token
isa PPI::Element
=head1 DESCRIPTION
A C<PPI::Token::QuoteLike::Command> object represents a command output
capturing quote-like operator.
=head1 METHODS
There are no methods available for C<PPI::Token::QuoteLike::Command>
beyond those provided by the parent L<PPI::Token::QuoteLike>, L<PPI::Token>
and L<PPI::Element> classes.
=cut
use strict;
use PPI::Token::QuoteLike ();
use PPI::Token::_QuoteEngine::Full ();
our $VERSION = '1.270'; # VERSION
our @ISA = qw{
PPI::Token::_QuoteEngine::Full
PPI::Token::QuoteLike
};
1;
=pod
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,71 @@
package PPI::Token::QuoteLike::Readline;
=pod
=head1 NAME
PPI::Token::QuoteLike::Readline - The readline quote-like operator
=head1 INHERITANCE
PPI::Token::QuoteLike::Readline
isa PPI::Token::QuoteLike
isa PPI::Token
isa PPI::Element
=head1 DESCRIPTION
The C<readline> quote-like operator is used to read either a single
line from a file, or all the lines from a file, as follows.
# Read in a single line
$line = <FILE>;
# From a scalar handle
$line = <$filehandle>;
# Read all the lines
@lines = <FILE>;
=head1 METHODS
There are no methods available for C<PPI::Token::QuoteLike::Readline>
beyond those provided by the parent L<PPI::Token::QuoteLike>, L<PPI::Token>
and L<PPI::Element> classes.
=cut
use strict;
use PPI::Token::QuoteLike ();
use PPI::Token::_QuoteEngine::Full ();
our $VERSION = '1.270'; # VERSION
our @ISA = qw{
PPI::Token::_QuoteEngine::Full
PPI::Token::QuoteLike
};
1;
=pod
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,126 @@
package PPI::Token::QuoteLike::Regexp;
=pod
=head1 NAME
PPI::Token::QuoteLike::Regexp - Regexp constructor quote-like operator
=head1 INHERITANCE
PPI::Token::QuoteLike::Regexp
isa PPI::Token::QuoteLike
isa PPI::Token
isa PPI::Element
=head1 DESCRIPTION
A C<PPI::Token::QuoteLike::Regexp> object represents the quote-like
operator used to construct anonymous L<Regexp> objects, as follows.
# Create a Regexp object for a module filename
my $module = qr/\.pm$/;
=head1 METHODS
The following methods are provided by this class,
beyond those provided by the parent L<PPI::Token::QuoteLike>,
L<PPI::Token> and L<PPI::Element> classes.
=cut
use strict;
use PPI::Token::QuoteLike ();
use PPI::Token::_QuoteEngine::Full ();
our $VERSION = '1.270'; # VERSION
our @ISA = qw{
PPI::Token::_QuoteEngine::Full
PPI::Token::QuoteLike
};
#####################################################################
# PPI::Token::QuoteLike::Regexp Methods
=pod
=head2 get_match_string
The C<get_match_string> method returns the portion of the string that
will be compiled into the match portion of the regexp.
=cut
sub get_match_string {
return $_[0]->_section_content( 0 );
}
=pod
=head2 get_substitute_string
The C<get_substitute_string> method always returns C<undef>, since
the C<qr{}> construction provides no substitution string. This method
is provided for orthogonality with C<PPI::Token::Regexp>.
=cut
sub get_substitute_string {
return undef;
}
=pod
=head2 get_modifiers
The C<get_modifiers> method returns the modifiers that will be
compiled into the regexp.
=cut
sub get_modifiers {
return $_[0]->_modifiers();
}
=pod
=head2 get_delimiters
The C<get_delimiters> method returns the delimiters of the string as an
array. The first and only element is the delimiters of the string to be
compiled into a match string.
=cut
sub get_delimiters {
return $_[0]->_delimiters();
}
1;
=pod
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,88 @@
package PPI::Token::QuoteLike::Words;
=pod
=head1 NAME
PPI::Token::QuoteLike::Words - Word list constructor quote-like operator
=head1 INHERITANCE
PPI::Token::QuoteLike::Words
isa PPI::Token::QuoteLike
isa PPI::Token
isa PPI::Element
=head1 DESCRIPTION
A C<PPI::Token::QuoteLike::Words> object represents a quote-like operator
that acts as a constructor for a list of words.
# Create a list for a significant chunk of the alphabet
my @list = qw{a b c d e f g h i j k l};
=head1 METHODS
=cut
use strict;
use PPI::Token::QuoteLike ();
use PPI::Token::_QuoteEngine::Full ();
our $VERSION = '1.270'; # VERSION
our @ISA = qw{
PPI::Token::_QuoteEngine::Full
PPI::Token::QuoteLike
};
=pod
=head2 literal
Returns the words contained as a list. Note that this method does not check the
context that the token is in; it always returns the list and not merely
the last element if the token is in scalar context.
=cut
sub literal {
my ( $self ) = @_;
my $content = $self->_section_content(0);
return if !defined $content;
# Undo backslash escaping of '\', the left delimiter,
# and the right delimiter. The right delimiter will
# only exist with paired delimiters: qw() qw[] qw<> qw{}.
my ( $left, $right ) = ( $self->_delimiters, '', '' );
$content =~ s/\\([\Q$left$right\\\E])/$1/g;
my @words = split ' ', $content;
return @words;
}
1;
=pod
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,136 @@
package PPI::Token::Regexp;
=pod
=head1 NAME
PPI::Token::Regexp - Regular expression abstract base class
=head1 INHERITANCE
PPI::Token::Regexp
isa PPI::Token
isa PPI::Element
=head1 DESCRIPTION
The C<PPI::Token::Regexp> class is never instantiated, and simply
provides a common abstract base class for the three regular expression
classes. These being:
=over 2
=item m// - L<PPI::Token::Regexp::Match>
=item s/// - L<PPI::Token::Regexp::Substitute>
=item tr/// - L<PPI::Token::Regexp::Transliterate>
=back
The names are hopefully obvious enough not to have to explain what
each class is. See their pages for more details.
To save some confusion, it's worth pointing out here that C<qr//> is
B<not> a regular expression (which PPI takes to mean something that
will actually examine or modify a string), but rather a quote-like
operator that acts as a constructor for compiled L<Regexp> objects.
=head1 METHODS
The following methods are inherited by this class' offspring:
=cut
use strict;
use PPI::Token ();
our $VERSION = '1.270'; # VERSION
our @ISA = "PPI::Token";
#####################################################################
# PPI::Token::Regexp Methods
=pod
=head2 get_match_string
The C<get_match_string> method returns the portion of the regexp that
performs the match.
=cut
sub get_match_string {
return $_[0]->_section_content( 0 );
}
=pod
=head2 get_substitute_string
The C<get_substitute_string> method returns the portion of the regexp
that is substituted for the match, if any. If the regexp does not
substitute, C<undef> is returned.
=cut
sub get_substitute_string {
return $_[0]->_section_content( 1 );
}
=pod
=head2 get_modifiers
The C<get_modifiers> method returns the modifiers of the regexp.
=cut
sub get_modifiers {
return $_[0]->_modifiers();
}
=pod
=head2 get_delimiters
The C<get_delimiters> method returns the delimiters of the regexp as
an array. The first element is the delimiters of the match string, and
the second element (if any) is the delimiters of the substitute string
(if any).
=cut
sub get_delimiters {
return $_[0]->_delimiters();
}
1;
=pod
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,76 @@
package PPI::Token::Regexp::Match;
=pod
=head1 NAME
PPI::Token::Regexp::Match - A standard pattern match regex
=head1 INHERITANCE
PPI::Token::Regexp::Match
isa PPI::Token::Regexp
isa PPI::Token
isa PPI::Element
=head1 SYNOPSIS
$text =~ m/match regexp/;
$text =~ /match regexp/;
=head1 DESCRIPTION
A C<PPI::Token::Regexp::Match> object represents a single match regular
expression. Just to be doubly clear, here are things that are and
B<aren't> considered a match regexp.
# Is a match regexp
/This is a match regexp/;
m/Old McDonald had a farm/eieio;
# These are NOT match regexp
qr/This is a regexp quote-like operator/;
s/This is a/replace regexp/;
=head1 METHODS
There are no methods available for C<PPI::Token::Regexp::Match> beyond
those provided by the parent L<PPI::Token::Regexp>, L<PPI::Token> and
L<PPI::Element> classes.
=cut
use strict;
use PPI::Token::Regexp ();
use PPI::Token::_QuoteEngine::Full ();
our $VERSION = '1.270'; # VERSION
our @ISA = qw{
PPI::Token::_QuoteEngine::Full
PPI::Token::Regexp
};
1;
=pod
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,66 @@
package PPI::Token::Regexp::Substitute;
=pod
=head1 NAME
PPI::Token::Regexp::Substitute - A match and replace regular expression token
=head1 INHERITANCE
PPI::Token::Regexp::Substitute
isa PPI::Token::Regexp
isa PPI::Token
isa PPI::Element
=head1 SYNOPSIS
$text =~ s/find/$replace/;
=head1 DESCRIPTION
A C<PPI::Token::Regexp::Substitute> object represents a single substitution
regular expression.
=head1 METHODS
There are no methods available for C<PPI::Token::Regexp::Substitute>
beyond those provided by the parent L<PPI::Token::Regexp>, L<PPI::Token>
and L<PPI::Element> classes.
=cut
use strict;
use PPI::Token::Regexp ();
use PPI::Token::_QuoteEngine::Full ();
our $VERSION = '1.270'; # VERSION
our @ISA = qw{
PPI::Token::_QuoteEngine::Full
PPI::Token::Regexp
};
1;
=pod
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,70 @@
package PPI::Token::Regexp::Transliterate;
=pod
=head1 NAME
PPI::Token::Regexp::Transliterate - A transliteration regular expression token
=head1 INHERITANCE
PPI::Token::Regexp::Transliterate
isa PPI::Token::Regexp
isa PPI::Token
isa PPI::Element
=head1 SYNOPSIS
$text =~ tr/abc/xyz/;
=head1 DESCRIPTION
A C<PPI::Token::Regexp::Transliterate> object represents a single
transliteration regular expression.
I'm afraid you'll have to excuse the ridiculously long class name, but
when push came to shove I ended up going for pedantically correct
names for things (practically cut and paste from the various docs).
=head1 METHODS
There are no methods available for C<PPI::Token::Regexp::Transliterate>
beyond those provided by the parent L<PPI::Token::Regexp>, L<PPI::Token>
and L<PPI::Element> classes.
=cut
use strict;
use PPI::Token::Regexp ();
use PPI::Token::_QuoteEngine::Full ();
our $VERSION = '1.270'; # VERSION
our @ISA = qw{
PPI::Token::_QuoteEngine::Full
PPI::Token::Regexp
};
1;
=pod
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,64 @@
package PPI::Token::Separator;
=pod
=head1 NAME
PPI::Token::Separator - The __DATA__ and __END__ tags
=head1 INHERITANCE
PPI::Token::Separator
isa PPI::Token::Word
isa PPI::Token
isa PPI::Element
=head1 DESCRIPTION
Although superficially looking like a normal L<PPI::Token::Word> object,
when the C<__DATA__> and C<__END__> compiler tags appear at the beginning of
a line (on supposedly) their own line, these tags become file section
separators.
The indicate that the time for Perl code is over, and the rest of the
file is dedicated to something else (data in the case of C<__DATA__>) or
to nothing at all (in the case of C<__END__>).
=head1 METHODS
This class has no methods beyond what is provided by its
L<PPI::Token::Word>, L<PPI::Token> and L<PPI::Element>
parent classes.
=cut
use strict;
use PPI::Token::Word ();
our $VERSION = '1.270'; # VERSION
our @ISA = "PPI::Token::Word";
1;
=pod
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,209 @@
package PPI::Token::Structure;
=pod
=head1 NAME
PPI::Token::Structure - Token class for characters that define code structure
=head1 INHERITANCE
PPI::Token::Structure
isa PPI::Token
isa PPI::Element
=head1 DESCRIPTION
The C<PPI::Token::Structure> class is used for tokens that control the
general tree structure or code.
This consists of seven characters. These are the six brace characters from
the "round", "curly" and "square" pairs, plus the semi-colon statement
separator C<;>.
=head1 METHODS
This class has no methods beyond what is provided by its
L<PPI::Token> and L<PPI::Element> parent classes.
=cut
use strict;
use PPI::Token ();
our $VERSION = '1.270'; # VERSION
our @ISA = "PPI::Token";
# Set the matching braces, done as an array
# for slightly faster lookups.
my %MATCH = (
ord '{' => '}',
ord '}' => '{',
ord '[' => ']',
ord ']' => '[',
ord '(' => ')',
ord ')' => '(',
);
my %OPENS = (
ord '{' => 1,
ord '[' => 1,
ord '(' => 1,
);
my %CLOSES = (
ord '}' => 1,
ord ']' => 1,
ord ')' => 1,
);
#####################################################################
# Tokenizer Methods
sub __TOKENIZER__on_char {
# Structures are one character long, always.
# Finalize and process again.
$_[1]->_finalize_token->__TOKENIZER__on_char( $_[1] );
}
sub __TOKENIZER__commit {
my $t = $_[1];
$t->_new_token( 'Structure', substr( $t->{line}, $t->{line_cursor}, 1 ) );
$t->_finalize_token;
0;
}
#####################################################################
# Lexer Methods
# For a given brace, find its opposing pair
sub __LEXER__opposite {
$MATCH{ord $_[0]->{content}};
}
#####################################################################
# PPI::Element Methods
# There is a unusual situation in regards to "siblings".
#
# As an Element, braces sit outside the normal tree structure, and in
# this context they NEVER have siblings.
#
# However, as tokens they DO have siblings.
#
# As such, we need special versions of _all_ of the sibling methods to
# handle this.
#
# Statement terminators do not have these problems, and for them sibling
# calls work as normal, and so they can just be passed upwards.
sub next_sibling {
return $_[0]->SUPER::next_sibling if $_[0]->{content} eq ';';
return '';
}
sub snext_sibling {
return $_[0]->SUPER::snext_sibling if $_[0]->{content} eq ';';
return '';
}
sub previous_sibling {
return $_[0]->SUPER::previous_sibling if $_[0]->{content} eq ';';
return '';
}
sub sprevious_sibling {
return $_[0]->SUPER::sprevious_sibling if $_[0]->{content} eq ';';
return '';
}
sub next_token {
my $self = shift;
return $self->SUPER::next_token if $self->{content} eq ';';
my $structure = $self->parent or return '';
# If this is an opening brace, descend down into our parent
# structure, if it has children.
if ( $OPENS{ ord $self->{content} } ) {
my $child = $structure->child(0);
if ( $child ) {
# Decend deeper, or return if it is a token
return $child->isa('PPI::Token') ? $child : $child->first_token;
} elsif ( $structure->finish ) {
# Empty structure, so next is closing brace
return $structure->finish;
}
# Anything that slips through to here is a structure
# with an opening brace, but no closing brace, so we
# just have to go with it, and continue as we would
# if we started with a closing brace.
}
# We can use the default implement, if we call it from the
# parent structure of the closing brace.
$structure->next_token;
}
sub previous_token {
my $self = shift;
return $self->SUPER::previous_token if $self->{content} eq ';';
my $structure = $self->parent or return '';
# If this is a closing brace, descend down into our parent
# structure, if it has children.
if ( $CLOSES{ ord $self->{content} } ) {
my $child = $structure->child(-1);
if ( $child ) {
# Decend deeper, or return if it is a token
return $child->isa('PPI::Token') ? $child : $child->last_token;
} elsif ( $structure->start ) {
# Empty structure, so next is closing brace
return $structure->start;
}
# Anything that slips through to here is a structure
# with a closing brace, but no opening brace, so we
# just have to go with it, and continue as we would
# if we started with an opening brace.
}
# We can use the default implement, if we call it from the
# parent structure of the closing brace.
$structure->previous_token;
}
1;
=pod
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,241 @@
package PPI::Token::Symbol;
=pod
=head1 NAME
PPI::Token::Symbol - A token class for variables and other symbols
=head1 INHERITANCE
PPI::Token::Symbol
isa PPI::Token
isa PPI::Element
=head1 DESCRIPTION
The C<PPI::Token::Symbol> class is used to cover all tokens that represent
variables and other things that start with a sigil.
=head1 METHODS
This class has several methods beyond what is provided by its
L<PPI::Token> and L<PPI::Element> parent classes.
Most methods are provided to help work out what the object is actually
pointing at, rather than what it might appear to be pointing at.
=cut
use strict;
use Params::Util qw{_INSTANCE};
use PPI::Token ();
our $VERSION = '1.270'; # VERSION
our @ISA = "PPI::Token";
#####################################################################
# PPI::Token::Symbol Methods
=pod
=head2 canonical
The C<canonical> method returns a normalized, canonical version of the
symbol.
For example, it converts C<$ ::foo'bar::baz> to C<$main::foo::bar::baz>.
This does not fully resolve the symbol, but merely removes syntax
variations.
=cut
sub canonical {
my $symbol = shift->content;
$symbol =~ s/\s+//;
$symbol =~ s/\'/::/g;
$symbol =~ s/(?<=[\$\@\%\&\*])::/main::/;
$symbol;
}
=pod
=head2 symbol
The C<symbol> method returns the ACTUAL symbol this token refers to.
A token of C<$foo> might actually be referring to C<@foo>, if it is found
in the form C<$foo[1]>.
This method attempts to resolve these issues to determine the actual
symbol.
Returns the symbol as a string.
=cut
my %cast_which_trumps_braces = map { $_ => 1 } qw{ $ @ % };
sub symbol {
my $self = shift;
my $symbol = $self->canonical;
# Immediately return the cases where it can't be anything else
my $type = substr( $symbol, 0, 1 );
return $symbol if $type eq '&';
# Unless the next significant Element is a structure, it's correct.
my $after = $self->snext_sibling;
return $symbol unless _INSTANCE($after, 'PPI::Structure');
# Process the rest for cases where it might actually be something else
my $braces = $after->braces;
return $symbol unless defined $braces;
if ( $type eq '$' ) {
# If it is cast to '$' or '@', that trumps any braces
my $before = $self->sprevious_sibling;
return $symbol if $before &&
$before->isa( 'PPI::Token::Cast' ) &&
$cast_which_trumps_braces{ $before->content };
# Otherwise the braces rule
substr( $symbol, 0, 1, '@' ) if $braces eq '[]';
substr( $symbol, 0, 1, '%' ) if $braces eq '{}';
} elsif ( $type eq '@' ) {
substr( $symbol, 0, 1, '%' ) if $braces eq '{}';
} elsif ( $type eq '%' ) {
substr( $symbol, 0, 1, '@' ) if $braces eq '[]';
}
$symbol;
}
=pod
=head2 raw_type
The C<raw_type> method returns the B<apparent> type of the symbol in the
form of its sigil.
Returns the sigil as a string.
=cut
sub raw_type {
substr( $_[0]->content, 0, 1 );
}
=pod
=head2 symbol_type
The C<symbol_type> method returns the B<actual> type of the symbol in the
form of its sigil.
Returns the sigil as a string.
=cut
sub symbol_type {
substr( $_[0]->symbol, 0, 1 );
}
#####################################################################
# Tokenizer Methods
sub __TOKENIZER__on_char {
my $t = $_[1];
# Suck in till the end of the symbol
pos $t->{line} = $t->{line_cursor};
if ( $t->{line} =~ m/\G([\w:\']+)/gc ) {
$t->{token}->{content} .= $1;
$t->{line_cursor} += length $1;
}
# Handle magic things
my $content = $t->{token}->{content};
if ( $content eq '@_' or $content eq '$_' ) {
$t->{class} = $t->{token}->set_class( 'Magic' );
return $t->_finalize_token->__TOKENIZER__on_char( $t );
}
# Shortcut for most of the X:: symbols
if ( $content eq '$::' ) {
# May well be an alternate form of a Magic
my $nextchar = substr( $t->{line}, $t->{line_cursor}, 1 );
if ( $nextchar eq '|' ) {
$t->{token}->{content} .= $nextchar;
$t->{line_cursor}++;
$t->{class} = $t->{token}->set_class( 'Magic' );
}
return $t->_finalize_token->__TOKENIZER__on_char( $t );
}
if ( $content =~ /^[\$%*@&]::(?:[^\w]|$)/ ) {
my $current = substr( $content, 0, 3, '' );
$t->{token}->{content} = $current;
$t->{line_cursor} -= length( $content );
return $t->_finalize_token->__TOKENIZER__on_char( $t );
}
if ( $content =~ /^(?:\$|\@)\d+/ ) {
$t->{class} = $t->{token}->set_class( 'Magic' );
return $t->_finalize_token->__TOKENIZER__on_char( $t );
}
# Trim off anything we oversucked...
$content =~ /^(
[\$@%&*]
(?: : (?!:) | # Allow single-colon non-magic variables
(?: \w+ | \' (?!\d) \w+ | \:: \w+ )
(?:
# Allow both :: and ' in namespace separators
(?: \' (?!\d) \w+ | \:: \w+ )
)*
(?: :: )? # Technically a compiler-magic hash, but keep it here
)
)/x or return undef;
unless ( length $1 eq length $content ) {
$t->{line_cursor} += length($1) - length($content);
$t->{token}->{content} = $1;
}
$t->_finalize_token->__TOKENIZER__on_char( $t );
}
1;
=pod
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,479 @@
package PPI::Token::Unknown;
=pod
=head1 NAME
PPI::Token::Unknown - Token of unknown or as-yet undetermined type
=head1 INHERITANCE
PPI::Token::Unknown
isa PPI::Token
isa PPI::Element
=head1 DESCRIPTION
Object of the type C<PPI::Token::Unknown> exist primarily inside the
tokenizer, where they are temporarily brought into existing for a very
short time to represent a token that could be one of a number of types.
Generally, they only exist for a character or two, after which they are
resolved and converted into the correct type. For an object of this type
to survive the parsing process is considered a major bug.
Please report any C<PPI::Token::Unknown> you encounter in a L<PPI::Document>
object as a bug.
=cut
use strict;
use PPI::Token ();
use PPI::Exception ();
use PPI::Singletons qw' %MAGIC $CURLY_SYMBOL ';
our $VERSION = '1.270'; # VERSION
our @ISA = "PPI::Token";
#####################################################################
# Tokenizer Methods
sub __TOKENIZER__on_char {
my ( $self, $t ) = @_; # Self and Tokenizer
my $c = $t->{token}->{content}; # Current token
my $char = substr( $t->{line}, $t->{line_cursor}, 1 ); # Current character
# Now, we split on the different values of the current content
if ( $c eq '*' ) {
# Is it a number?
if ( $char =~ /\d/ ) {
# bitwise operator
$t->{class} = $t->{token}->set_class( 'Operator' );
return $t->_finalize_token->__TOKENIZER__on_char( $t );
}
if ( $char =~ /[\w:]/ ) {
# Symbol (unless the thing before it is a number
my ( $prev ) = $t->_previous_significant_tokens(1);
if ( not $prev or not $prev->isa('PPI::Token::Number') ) {
$t->{class} = $t->{token}->set_class( 'Symbol' );
return 1;
}
}
if ( $char eq '{' ) {
# Get rest of line
pos $t->{line} = $t->{line_cursor} + 1;
if ( $t->{line} =~ m/$CURLY_SYMBOL/gc ) {
# control-character symbol (e.g. *{^_Foo})
$t->{class} = $t->{token}->set_class( 'Magic' );
return 1;
}
}
# Postfix dereference: ->**
if ( $char eq '*' ) {
my ( $prev ) = $t->_previous_significant_tokens(1);
if ( $prev and $prev->isa('PPI::Token::Operator') and $prev->content eq '->' ) {
$t->{class} = $t->{token}->set_class( 'Cast' );
return 1;
}
}
if ( $char eq '*' || $char eq '=' ) {
# Power operator '**' or mult-assign '*='
$t->{class} = $t->{token}->set_class( 'Operator' );
return 1;
}
return $self->_as_cast_or_op($t) if $self->_is_cast_or_op($char);
$t->{class} = $t->{token}->set_class( 'Operator' );
return $t->_finalize_token->__TOKENIZER__on_char( $t );
} elsif ( $c eq '$' ) {
# Postfix dereference: ->$* ->$#*
if ( $char eq '*' || $char eq '#' ) {
my ( $prev ) = $t->_previous_significant_tokens(1);
if ( $prev and $prev->isa('PPI::Token::Operator') and $prev->content eq '->' ) {
$t->{class} = $t->{token}->set_class( 'Cast' );
return 1;
}
}
if ( $char =~ /[a-z_]/i ) {
# Symbol
$t->{class} = $t->{token}->set_class( 'Symbol' );
return 1;
}
if ( $MAGIC{ $c . $char } ) {
# Magic variable
$t->{class} = $t->{token}->set_class( 'Magic' );
return 1;
}
if ( $char eq '{' ) {
# Get rest of line
pos $t->{line} = $t->{line_cursor} + 1;
if ( $t->{line} =~ m/$CURLY_SYMBOL/gc ) {
# control-character symbol (e.g. ${^MATCH})
$t->{class} = $t->{token}->set_class( 'Magic' );
return 1;
}
}
# Must be a cast
$t->{class} = $t->{token}->set_class( 'Cast' );
return $t->_finalize_token->__TOKENIZER__on_char( $t );
} elsif ( $c eq '@' ) {
# Postfix dereference: ->@*
if ( $char eq '*' ) {
my ( $prev ) = $t->_previous_significant_tokens(1);
if ( $prev and $prev->isa('PPI::Token::Operator') and $prev->content eq '->' ) {
$t->{class} = $t->{token}->set_class( 'Cast' );
return 1;
}
}
if ( $char =~ /[\w:]/ ) {
# Symbol
$t->{class} = $t->{token}->set_class( 'Symbol' );
return 1;
}
if ( $MAGIC{ $c . $char } ) {
# Magic variable
$t->{class} = $t->{token}->set_class( 'Magic' );
return 1;
}
if ( $char eq '{' ) {
# Get rest of line
pos $t->{line} = $t->{line_cursor} + 1;
if ( $t->{line} =~ m/$CURLY_SYMBOL/gc ) {
# control-character symbol (e.g. @{^_Foo})
$t->{class} = $t->{token}->set_class( 'Magic' );
return 1;
}
}
# Must be a cast
$t->{class} = $t->{token}->set_class( 'Cast' );
return $t->_finalize_token->__TOKENIZER__on_char( $t );
} elsif ( $c eq '%' ) {
# Postfix dereference: ->%* ->%[...]
if ( $char eq '*' || $char eq '[' ) {
my ( $prev ) = $t->_previous_significant_tokens(1);
if ( $prev and $prev->isa('PPI::Token::Operator') and $prev->content eq '->' ) {
if ( $char eq '*' ) {
$t->{class} = $t->{token}->set_class( 'Cast' );
return 1;
}
if ( $char eq '[' ) {
$t->{class} = $t->{token}->set_class( 'Cast' );
return $t->_finalize_token->__TOKENIZER__on_char( $t );
}
}
}
# Is it a number?
if ( $char =~ /\d/ ) {
# bitwise operator
$t->{class} = $t->{token}->set_class( 'Operator' );
return $t->_finalize_token->__TOKENIZER__on_char( $t );
}
# Is it a magic variable?
if ( $char eq '^' || $MAGIC{ $c . $char } ) {
$t->{class} = $t->{token}->set_class( 'Magic' );
return 1;
}
if ( $char =~ /[\w:]/ ) {
# Symbol (unless the thing before it is a number
my ( $prev ) = $t->_previous_significant_tokens(1);
if ( not $prev or not $prev->isa('PPI::Token::Number') ) {
$t->{class} = $t->{token}->set_class( 'Symbol' );
return 1;
}
}
if ( $char eq '{' ) {
# Get rest of line
pos $t->{line} = $t->{line_cursor} + 1;
if ( $t->{line} =~ m/$CURLY_SYMBOL/gc ) {
# control-character symbol (e.g. %{^_Foo})
$t->{class} = $t->{token}->set_class( 'Magic' );
return 1;
}
}
return $self->_as_cast_or_op($t) if $self->_is_cast_or_op($char);
# Probably the mod operator
$t->{class} = $t->{token}->set_class( 'Operator' );
return $t->{class}->__TOKENIZER__on_char( $t );
} elsif ( $c eq '&' ) {
# Postfix dereference: ->&*
if ( $char eq '*' ) {
my ( $prev ) = $t->_previous_significant_tokens(1);
if ( $prev and $prev->isa('PPI::Token::Operator') and $prev->content eq '->' ) {
$t->{class} = $t->{token}->set_class( 'Cast' );
return 1;
}
}
# Is it a number?
if ( $char =~ /\d/ ) {
# bitwise operator
$t->{class} = $t->{token}->set_class( 'Operator' );
return $t->_finalize_token->__TOKENIZER__on_char( $t );
}
if ( $char =~ /[\w:]/ ) {
# Symbol (unless the thing before it is a number
my ( $prev ) = $t->_previous_significant_tokens(1);
if ( not $prev or not $prev->isa('PPI::Token::Number') ) {
$t->{class} = $t->{token}->set_class( 'Symbol' );
return 1;
}
}
return $self->_as_cast_or_op($t) if $self->_is_cast_or_op($char);
# Probably the binary and operator
$t->{class} = $t->{token}->set_class( 'Operator' );
return $t->{class}->__TOKENIZER__on_char( $t );
} elsif ( $c eq '-' ) {
if ( $char =~ /\d/o ) {
# Number
$t->{class} = $t->{token}->set_class( 'Number' );
return 1;
}
if ( $char eq '.' ) {
# Number::Float
$t->{class} = $t->{token}->set_class( 'Number::Float' );
return 1;
}
if ( $char =~ /[a-zA-Z]/ ) {
$t->{class} = $t->{token}->set_class( 'DashedWord' );
return 1;
}
# The numeric negative operator
$t->{class} = $t->{token}->set_class( 'Operator' );
return $t->{class}->__TOKENIZER__on_char( $t );
} elsif ( $c eq ':' ) {
if ( $char eq ':' ) {
# ::foo style bareword
$t->{class} = $t->{token}->set_class( 'Word' );
return 1;
}
# Now, : acts very very differently in different contexts.
# Mainly, we need to find out if this is a subroutine attribute.
# We'll leave a hint in the token to indicate that, if it is.
if ( $self->__TOKENIZER__is_an_attribute( $t ) ) {
# This : is an attribute indicator
$t->{class} = $t->{token}->set_class( 'Operator' );
$t->{token}->{_attribute} = 1;
return $t->_finalize_token->__TOKENIZER__on_char( $t );
}
# It MIGHT be a label, but it's probably the ?: trinary operator
$t->{class} = $t->{token}->set_class( 'Operator' );
return $t->{class}->__TOKENIZER__on_char( $t );
}
# erm...
PPI::Exception->throw('Unknown value in PPI::Token::Unknown token');
}
sub _is_cast_or_op {
my ( $self, $char ) = @_;
return 1 if $char eq '$';
return 1 if $char eq '@';
return 1 if $char eq '%';
return 1 if $char eq '*';
return 1 if $char eq '{';
return;
}
sub _as_cast_or_op {
my ( $self, $t ) = @_;
my $class = _cast_or_op( $t );
$t->{class} = $t->{token}->set_class( $class );
return $t->_finalize_token->__TOKENIZER__on_char( $t );
}
sub _prev_significant_w_cursor {
my ( $tokens, $cursor, $extra_check ) = @_;
while ( $cursor >= 0 ) {
my $token = $tokens->[ $cursor-- ];
next if !$token->significant;
next if $extra_check and !$extra_check->($token);
return ( $token, $cursor );
}
return ( undef, $cursor );
}
# Operator/operand-sensitive, multiple or GLOB cast
sub _cast_or_op {
my ( $t ) = @_;
my $tokens = $t->{tokens};
my $cursor = scalar( @$tokens ) - 1;
my $token;
( $token, $cursor ) = _prev_significant_w_cursor( $tokens, $cursor );
return 'Cast' if !$token; # token was first in the document
if ( $token->isa( 'PPI::Token::Structure' ) and $token->content eq '}' ) {
# Scan the token stream backwards an arbitrarily long way,
# looking for the matching opening curly brace.
my $structure_depth = 1;
( $token, $cursor ) = _prev_significant_w_cursor(
$tokens, $cursor,
sub {
my ( $token ) = @_;
return if !$token->isa( 'PPI::Token::Structure' );
if ( $token eq '}' ) {
$structure_depth++;
return;
}
if ( $token eq '{' ) {
$structure_depth--;
return if $structure_depth;
}
return 1;
}
);
return 'Operator' if !$token; # no matching '{', probably an unbalanced '}'
# Scan past any whitespace
( $token, $cursor ) = _prev_significant_w_cursor( $tokens, $cursor );
return 'Operator' if !$token; # Document began with what must be a hash constructor.
return 'Operator' if $token->isa( 'PPI::Token::Symbol' ); # subscript
my %meth_or_subscript_end = map { $_ => 1 } qw@ -> } ] @;
return 'Operator' if $meth_or_subscript_end{ $token->content }; # subscript
my $content = $token->content;
my $produces_or_wants_value =
( $token->isa( 'PPI::Token::Word' ) and ( $content eq 'do' or $content eq 'eval' ) );
return $produces_or_wants_value ? 'Operator' : 'Cast';
}
my %list_start_or_term_end = map { $_ => 1 } qw@ ; ( { [ @;
return 'Cast'
if $token->isa( 'PPI::Token::Structure' ) and $list_start_or_term_end{ $token->content }
or $token->isa( 'PPI::Token::Cast' )
or $token->isa( 'PPI::Token::Operator' )
or $token->isa( 'PPI::Token::Label' );
return 'Operator' if !$token->isa( 'PPI::Token::Word' );
( $token, $cursor ) = _prev_significant_w_cursor( $tokens, $cursor );
return 'Cast' if !$token || $token->content ne '->';
return 'Operator';
}
# Are we at a location where a ':' would indicate a subroutine attribute
sub __TOKENIZER__is_an_attribute {
my $t = $_[1]; # Tokenizer object
my @tokens = $t->_previous_significant_tokens(3);
my $p0 = $tokens[0];
return '' if not $p0;
# If we just had another attribute, we are also an attribute
return 1 if $p0->isa('PPI::Token::Attribute');
# If we just had a prototype, then we are an attribute
return 1 if $p0->isa('PPI::Token::Prototype');
# Other than that, we would need to have had a bareword
return '' unless $p0->isa('PPI::Token::Word');
# We could be an anonymous subroutine
if ( $p0->isa('PPI::Token::Word') and $p0->content eq 'sub' ) {
return 1;
}
# Or, we could be a named subroutine
my $p1 = $tokens[1];
my $p2 = $tokens[2];
if (
$p1
and
$p1->isa('PPI::Token::Word')
and
$p1->content eq 'sub'
and (
not $p2
or
$p2->isa('PPI::Token::Structure')
or (
$p2->isa('PPI::Token::Whitespace')
and
$p2->content eq ''
)
)
) {
return 1;
}
# We aren't an attribute
'';
}
1;
=pod
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,454 @@
package PPI::Token::Whitespace;
=pod
=head1 NAME
PPI::Token::Whitespace - Tokens representing ordinary white space
=head1 INHERITANCE
PPI::Token::Whitespace
isa PPI::Token
isa PPI::Element
=head1 DESCRIPTION
As a full "round-trip" parser, PPI records every last byte in a
file and ensure that it is included in the L<PPI::Document> object.
This even includes whitespace. In fact, Perl documents are seen
as "floating in a sea of whitespace", and thus any document will
contain vast quantities of C<PPI::Token::Whitespace> objects.
For the most part, you shouldn't notice them. Or at least, you
shouldn't B<have> to notice them.
This means doing things like consistently using the "S for significant"
series of L<PPI::Node> and L<PPI::Element> methods to do things.
If you want the nth child element, you should be using C<schild> rather
than C<child>, and likewise C<snext_sibling>, C<sprevious_sibling>, and
so on and so forth.
=head1 METHODS
Again, for the most part you should really B<not> need to do anything
very significant with whitespace.
But there are a couple of convenience methods provided, beyond those
provided by the parent L<PPI::Token> and L<PPI::Element> classes.
=cut
use strict;
use Clone ();
use PPI::Token ();
our $VERSION = '1.270'; # VERSION
our @ISA = "PPI::Token";
=pod
=head2 null
Because L<PPI> sees documents as sitting on a sort of substrate made of
whitespace, there are a couple of corner cases that get particularly
nasty if they don't find whitespace in certain places.
Imagine walking down the beach to go into the ocean, and then quite
unexpectedly falling off the side of the planet. Well it's somewhat
equivalent to that, including the whole screaming death bit.
The C<null> method is a convenience provided to get some internals
out of some of these corner cases.
Specifically it create a whitespace token that represents nothing,
or at least the null string C<''>. It's a handy way to have some
"whitespace" right where you need it, without having to have any
actual characters.
=cut
my $null;
sub null {
$null ||= $_[0]->new('');
Clone::clone($null);
}
### XS -> PPI/XS.xs:_PPI_Token_Whitespace__significant 0.900+
sub significant() { '' }
=pod
=head2 tidy
C<tidy> is a convenience method for removing unneeded whitespace.
Specifically, it removes any whitespace from the end of a line.
Note that this B<doesn't> include POD, where you may well need
to keep certain types of whitespace. The entire POD chunk lives
in its own L<PPI::Token::Pod> object.
=cut
sub tidy {
$_[0]->{content} =~ s/^\s+?(?>\n)//;
1;
}
#####################################################################
# Parsing Methods
# Build the class and commit maps
my %COMMITMAP = (
map( { ord $_ => 'PPI::Token::Word' } 'a' .. 'u', 'A' .. 'Z', qw" w y z _ " ), # no v or x
map( { ord $_ => 'PPI::Token::Structure' } qw" ; [ ] { } ) " ),
ord '#' => 'PPI::Token::Comment',
ord 'v' => 'PPI::Token::Number::Version',
);
my %CLASSMAP = (
map( { ord $_ => 'Number' } 0 .. 9 ),
map( { ord $_ => 'Operator' } qw" = ? | + > . ! ~ ^ " ),
map( { ord $_ => 'Unknown' } qw" * $ @ & : % " ),
ord ',' => 'PPI::Token::Operator',
ord "'" => 'Quote::Single',
ord '"' => 'Quote::Double',
ord '`' => 'QuoteLike::Backtick',
ord '\\' => 'Cast',
ord '_' => 'Word',
9 => 'Whitespace', # A horizontal tab
10 => 'Whitespace', # A newline
12 => 'Whitespace', # A form feed
13 => 'Whitespace', # A carriage return
32 => 'Whitespace', # A normal space
);
# Words (functions and keywords) after which a following / is
# almost certainly going to be a regex
my %MATCHWORD = map { $_ => 1 } qw{
return
split
if
unless
grep
map
};
sub __TOKENIZER__on_line_start {
my $t = $_[1];
my $line = $t->{line};
# Can we classify the entire line in one go
if ( $line =~ /^\s*$/ ) {
# A whitespace line
$t->_new_token( 'Whitespace', $line );
return 0;
} elsif ( $line =~ /^\s*#/ ) {
# A comment line
$t->_new_token( 'Comment', $line );
$t->_finalize_token;
return 0;
} elsif ( $line =~ /^=(\w+)/ ) {
# A Pod tag... change to pod mode
$t->_new_token( 'Pod', $line );
if ( $1 eq 'cut' ) {
# This is an error, but one we'll ignore
# Don't go into Pod mode, since =cut normally
# signals the end of Pod mode
} else {
$t->{class} = 'PPI::Token::Pod';
}
return 0;
} elsif ( $line =~ /^use v6\-alpha\;/ ) {
# Indicates a Perl 6 block. Make the initial
# implementation just suck in the entire rest of the
# file.
my @perl6;
while ( 1 ) {
my $line6 = $t->_get_line;
last unless defined $line6;
push @perl6, $line6;
}
push @{ $t->{perl6} }, join '', @perl6;
# We only sucked in the block, we don't actually do
# anything to the "use v6..." line. So return as if
# we didn't find anything at all.
return 1;
}
1;
}
sub __TOKENIZER__on_char {
my $t = $_[1];
my $c = substr $t->{line}, $t->{line_cursor}, 1;
my $char = ord $c;
# Do we definitely know what something is?
return $COMMITMAP{$char}->__TOKENIZER__commit($t) if $COMMITMAP{$char};
# Handle the simple option first
return $CLASSMAP{$char} if $CLASSMAP{$char};
if ( $char == 40 ) { # $char eq '('
# Finalise any whitespace token...
$t->_finalize_token if $t->{token};
# Is this the beginning of a sub prototype?
# We are a sub prototype IF
# 1. The previous significant token is a bareword.
# 2. The one before that is the word 'sub'.
# 3. The one before that is a 'structure'
# Get the three previous significant tokens
my @tokens = $t->_previous_significant_tokens(3);
# A normal subroutine declaration
my $p1 = $tokens[1];
my $p2 = $tokens[2];
if (
$tokens[0]
and
$tokens[0]->isa('PPI::Token::Word')
and
$p1
and
$p1->isa('PPI::Token::Word')
and
$p1->content eq 'sub'
and (
not $p2
or
$p2->isa('PPI::Token::Structure')
or (
$p2->isa('PPI::Token::Whitespace')
and
$p2->content eq ''
)
or (
# Lexical subroutine
$p2->isa('PPI::Token::Word')
and
$p2->content =~ /^(?:my|our|state)$/
)
)
) {
# This is a sub prototype
return 'Prototype';
}
# A prototyped anonymous subroutine
my $p0 = $tokens[0];
if ( $p0 and $p0->isa('PPI::Token::Word') and $p0->content eq 'sub'
# Maybe it's invoking a method named 'sub'
and not ( $p1 and $p1->isa('PPI::Token::Operator') and $p1->content eq '->')
) {
return 'Prototype';
}
# This is a normal open bracket
return 'Structure';
} elsif ( $char == 60 ) { # $char eq '<'
# Finalise any whitespace token...
$t->_finalize_token if $t->{token};
# This is either "less than" or "readline quote-like"
# Do some context stuff to guess which.
my $prev = $t->_last_significant_token;
# The most common group of less-thans are used like
# $foo < $bar
# 1 < $bar
# $#foo < $bar
return 'Operator' if $prev and $prev->isa('PPI::Token::Symbol');
return 'Operator' if $prev and $prev->isa('PPI::Token::Magic');
return 'Operator' if $prev and $prev->isa('PPI::Token::Number');
return 'Operator' if $prev and $prev->isa('PPI::Token::ArrayIndex');
# If it is <<... it's a here-doc instead
my $next_char = substr( $t->{line}, $t->{line_cursor} + 1, 2 );
return 'Operator' if $next_char =~ /<[^>]/;
return 'Operator' if not $prev;
# The most common group of readlines are used like
# while ( <...> )
# while <>;
my $prec = $prev->content;
return 'QuoteLike::Readline'
if ( $prev->isa('PPI::Token::Structure') and $prec eq '(' )
or ( $prev->isa('PPI::Token::Structure') and $prec eq ';' )
or ( $prev->isa('PPI::Token::Word') and $prec eq 'while' )
or ( $prev->isa('PPI::Token::Operator') and $prec eq '=' )
or ( $prev->isa('PPI::Token::Operator') and $prec eq ',' );
if ( $prev->isa('PPI::Token::Structure') and $prec eq '}' ) {
# Could go either way... do a regex check
# $foo->{bar} < 2;
# grep { .. } <foo>;
pos $t->{line} = $t->{line_cursor};
if ( $t->{line} =~ m/\G<(?!\d)\w+>/gc ) {
# Almost definitely readline
return 'QuoteLike::Readline';
}
}
# Otherwise, we guess operator, which has been the default up
# until this more comprehensive section was created.
return 'Operator';
} elsif ( $char == 47 ) { # $char eq '/'
# Finalise any whitespace token...
$t->_finalize_token if $t->{token};
# This is either a "divided by" or a "start regex"
# Do some context stuff to guess ( ack ) which.
# Hopefully the guess will be good enough.
my $prev = $t->_last_significant_token;
# Or as the very first thing in a file
return 'Regexp::Match' if not $prev;
my $prec = $prev->content;
# Most times following an operator, we are a regex.
# This includes cases such as:
# , - As an argument in a list
# .. - The second condition in a flip flop
# =~ - A bound regex
# !~ - Ditto
return 'Regexp::Match' if $prev->isa('PPI::Token::Operator');
# After a symbol
return 'Operator' if $prev->isa('PPI::Token::Symbol');
if ( $prec eq ']' and $prev->isa('PPI::Token::Structure') ) {
return 'Operator';
}
# After another number
return 'Operator' if $prev->isa('PPI::Token::Number');
# After going into scope/brackets
if (
$prev->isa('PPI::Token::Structure')
and (
$prec eq '('
or
$prec eq '{'
or
$prec eq ';'
)
) {
return 'Regexp::Match';
}
# Functions and keywords
if (
$MATCHWORD{$prec}
and
$prev->isa('PPI::Token::Word')
) {
return 'Regexp::Match';
}
# What about the char after the slash? There's some things
# that would be highly illogical to see if it's an operator.
my $next_char = substr $t->{line}, $t->{line_cursor} + 1, 1;
if ( defined $next_char and length $next_char ) {
if ( $next_char =~ /(?:\^|\[|\\)/ ) {
return 'Regexp::Match';
}
}
# Otherwise... erm... assume operator?
# Add more tests here as potential cases come to light
return 'Operator';
} elsif ( $char == 120 ) { # $char eq 'x'
# Could be a word, the x= operator, the x operator
# followed by whitespace, or the x operator without any
# space between itself and its operand, e.g.: '$a x3',
# which is the same as '$a x 3'. _current_x_is_operator
# assumes we have a complete 'x' token, but we don't
# yet. We may need to split this x character apart from
# what follows it.
if ( $t->_current_x_is_operator ) {
pos $t->{line} = $t->{line_cursor} + 1;
return 'Operator' if $t->{line} =~ m/\G(?:
\d # x op with no whitespace e.g. 'x3'
|
(?!( # negative lookahead
=> # not on left of fat comma
|
\w # not a word like "xyzzy"
|
\s # not x op plus whitespace
))
)/gcx;
}
# Otherwise, commit like a normal bareword, including x
# operator followed by whitespace.
return PPI::Token::Word->__TOKENIZER__commit($t);
} elsif ( $char == 45 ) { # $char eq '-'
# Look for an obvious operator operand context
my $context = $t->_opcontext;
if ( $context eq 'operator' ) {
return 'Operator';
} else {
# More logic needed
return 'Unknown';
}
} elsif ( $char >= 128 ) { # Outside ASCII
return 'PPI::Token::Word'->__TOKENIZER__commit($t) if $c =~ /\w/;
return 'Whitespace' if $c =~ /\s/;
}
# All the whitespaces are covered, so what to do
### For now, die
PPI::Exception->throw("Encountered unexpected character '$char'");
}
sub __TOKENIZER__on_line_end {
$_[1]->_finalize_token if $_[1]->{token};
}
1;
=pod
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,373 @@
package PPI::Token::Word;
=pod
=head1 NAME
PPI::Token::Word - The generic "word" Token
=head1 INHERITANCE
PPI::Token::Word
isa PPI::Token
isa PPI::Element
=head1 DESCRIPTION
A C<PPI::Token::Word> object is a PPI-specific representation of several
different types of word-like things, and is one of the most common Token
classes found in typical documents.
Specifically, it includes not only barewords, but also any other valid
Perl identifier including non-operator keywords and core functions, and
any include C<::> separators inside it, as long as it fits the
format of a class, function, etc.
=head1 METHODS
There are no methods available for C<PPI::Token::Word> beyond those
provided by its L<PPI::Token> and L<PPI::Element> parent
classes.
We expect to add additional methods to help further resolve a Word as
a function, method, etc over time. If you need such a thing right
now, look at L<Perl::Critic::Utils>.
=cut
use strict;
use PPI::Token ();
use PPI::Singletons qw' %OPERATOR %QUOTELIKE %KEYWORDS ';
our $VERSION = '1.270'; # VERSION
our @ISA = "PPI::Token";
=pod
=head2 literal
Returns the value of the Word as a string. This assumes (often
incorrectly) that the Word is a bareword and not a function, method,
keyword, etc. This differs from C<content> because C<Foo'Bar> expands
to C<Foo::Bar>.
=cut
sub literal {
my $self = shift;
my $word = $self->content;
# Expand Foo'Bar to Foo::Bar
$word =~ s/\'/::/g;
return $word;
}
=pod
=head2 method_call
Answers whether this is the name of a method in a method call. Returns true if
yes, false if no, and nothing if unknown.
=cut
sub method_call {
my $self = shift;
my $previous = $self->sprevious_sibling;
if (
$previous
and
$previous->isa('PPI::Token::Operator')
and
$previous->content eq '->'
) {
return 1;
}
my $snext = $self->snext_sibling;
return 0 unless $snext;
if (
$snext->isa('PPI::Structure::List')
or
$snext->isa('PPI::Token::Structure')
or
$snext->isa('PPI::Token::Operator')
and (
$snext->content eq ','
or
$snext->content eq '=>'
)
) {
return 0;
}
if (
$snext->isa('PPI::Token::Word')
and
$snext->content =~ m< \w :: \z >xms
) {
return 1;
}
return;
}
sub __TOKENIZER__on_char {
my $class = shift;
my $t = shift;
# Suck in till the end of the bareword
pos $t->{line} = $t->{line_cursor};
if ( $t->{line} =~ m/\G(\w+(?:(?:\'|::)\w+)*(?:::)?)/gc ) {
my $word = $1;
# Special Case: If we accidentally treat eq'foo' like
# the word "eq'foo", then just make 'eq' (or whatever
# else is in the %KEYWORDS hash.
if ( $word =~ /^(\w+)'/ && $KEYWORDS{$1} ) {
$word = $1;
}
$t->{token}->{content} .= $word;
$t->{line_cursor} += length $word;
}
# We might be a subroutine attribute.
if ( __current_token_is_attribute($t) ) {
$t->{class} = $t->{token}->set_class( 'Attribute' );
return $t->{class}->__TOKENIZER__commit( $t );
}
my $word = $t->{token}->{content};
if ( $KEYWORDS{$word} ) {
# Check for a Perl keyword that is forced to be a normal word instead
if ( $t->__current_token_is_forced_word ) {
$t->{class} = $t->{token}->set_class( 'Word' );
return $t->{class}->__TOKENIZER__on_char( $t );
}
# Check for a quote like operator. %QUOTELIKE must be subset of %KEYWORDS
if ( $QUOTELIKE{$word} ) {
$t->{class} = $t->{token}->set_class( $QUOTELIKE{$word} );
return $t->{class}->__TOKENIZER__on_char( $t );
}
# Or one of the word operators. %OPERATOR must be subset of %KEYWORDS
if ( $OPERATOR{$word} ) {
$t->{class} = $t->{token}->set_class( 'Operator' );
return $t->_finalize_token->__TOKENIZER__on_char( $t );
}
}
# Unless this is a simple identifier, at this point
# it has to be a normal bareword
if ( $word =~ /\:/ ) {
return $t->_finalize_token->__TOKENIZER__on_char( $t );
}
# If the NEXT character in the line is a colon, this
# is a label.
my $char = substr( $t->{line}, $t->{line_cursor}, 1 );
if ( $char eq ':' ) {
$t->{token}->{content} .= ':';
$t->{line_cursor}++;
$t->{class} = $t->{token}->set_class( 'Label' );
# If not a label, '_' on its own is the magic filehandle
} elsif ( $word eq '_' ) {
$t->{class} = $t->{token}->set_class( 'Magic' );
}
# Finalise and process the character again
$t->_finalize_token->__TOKENIZER__on_char( $t );
}
# We are committed to being a bareword.
# Or so we would like to believe.
sub __TOKENIZER__commit {
my ($class, $t) = @_;
# Our current position is the first character of the bareword.
# Capture the bareword.
pos $t->{line} = $t->{line_cursor};
unless ( $t->{line} =~ m/\G((?!\d)\w+(?:(?:\'|::)\w+)*(?:::)?)/gc ) {
# Programmer error
die sprintf "Fatal error... regex failed to match in '%s' when expected", substr $t->{line}, $t->{line_cursor};
}
# Special Case: If we accidentally treat eq'foo' like the word "eq'foo",
# then unwind it and just make it 'eq' (or the other stringy comparitors)
my $word = $1;
if ( $word =~ /^(\w+)'/ && $KEYWORDS{$1} ) {
$word = $1;
}
# Advance the position one after the end of the bareword
$t->{line_cursor} += length $word;
# We might be a subroutine attribute.
if ( __current_token_is_attribute($t) ) {
$t->_new_token( 'Attribute', $word );
return ($t->{line_cursor} >= $t->{line_length}) ? 0
: $t->{class}->__TOKENIZER__on_char($t);
}
# Check for the end of the file
if ( $word eq '__END__' ) {
# Create the token for the __END__ itself
$t->_new_token( 'Separator', $1 );
$t->_finalize_token;
# Move into the End zone (heh)
$t->{zone} = 'PPI::Token::End';
# Add the rest of the line as a comment, and a whitespace newline
# Anything after the __END__ on the line is "ignored". So we must
# also ignore it, by turning it into a comment.
my $end_rest = substr( $t->{line}, $t->{line_cursor} );
$t->{line_cursor} = length $t->{line};
if ( $end_rest =~ /\n$/ ) {
chomp $end_rest;
$t->_new_token( 'Comment', $end_rest ) if length $end_rest;
$t->_new_token( 'Whitespace', "\n" );
} else {
$t->_new_token( 'Comment', $end_rest ) if length $end_rest;
}
$t->_finalize_token;
return 0;
}
# Check for the data section
if ( $word eq '__DATA__' ) {
# Create the token for the __DATA__ itself
$t->_new_token( 'Separator', "$1" );
$t->_finalize_token;
# Move into the Data zone
$t->{zone} = 'PPI::Token::Data';
# Add the rest of the line as the Data token
my $data_rest = substr( $t->{line}, $t->{line_cursor} );
$t->{line_cursor} = length $t->{line};
if ( $data_rest =~ /\n$/ ) {
chomp $data_rest;
$t->_new_token( 'Comment', $data_rest ) if length $data_rest;
$t->_new_token( 'Whitespace', "\n" );
} else {
$t->_new_token( 'Comment', $data_rest ) if length $data_rest;
}
$t->_finalize_token;
return 0;
}
my $token_class;
if ( $word =~ /\:/ ) {
# Since it's not a simple identifier...
$token_class = 'Word';
} elsif ( $KEYWORDS{$word} and $t->__current_token_is_forced_word ) {
$token_class = 'Word';
} elsif ( $QUOTELIKE{$word} ) {
# Special Case: A Quote-like operator
$t->_new_token( $QUOTELIKE{$word}, $word );
return ($t->{line_cursor} >= $t->{line_length}) ? 0
: $t->{class}->__TOKENIZER__on_char( $t );
} elsif ( $OPERATOR{$word} && ($word ne 'x' || $t->_current_x_is_operator) ) {
# Word operator
$token_class = 'Operator';
} else {
# Get tokens early to be sure to not disturb state set up by pos and m//gc.
my @tokens = $t->_previous_significant_tokens(1);
# If the next character is a ':' then it's a label...
pos $t->{line} = $t->{line_cursor};
if ( $t->{line} =~ m/\G(\s*:)(?!:)/gc ) {
if ( $tokens[0] and $tokens[0]->{content} eq 'sub' ) {
# ... UNLESS it's after 'sub' in which
# case it is a sub name and an attribute
# operator.
# We COULD have checked this at the top
# level of checks, but this would impose
# an additional performance per-word
# penalty, and every other case where the
# attribute operator doesn't directly
# touch the object name already works.
$token_class = 'Word';
} else {
$word .= $1;
$t->{line_cursor} += length($1);
$token_class = 'Label';
}
} elsif ( $word eq '_' ) {
$token_class = 'Magic';
} else {
$token_class = 'Word';
}
}
# Create the new token and finalise
$t->_new_token( $token_class, $word );
if ( $t->{line_cursor} >= $t->{line_length} ) {
# End of the line
$t->_finalize_token;
return 0;
}
$t->_finalize_token->__TOKENIZER__on_char($t);
}
# Is the current Word really a subroutine attribute?
sub __current_token_is_attribute {
my ( $t ) = @_;
my @tokens = $t->_previous_significant_tokens(1);
return (
$tokens[0]
and (
# hint from tokenizer
$tokens[0]->{_attribute}
# nothing between attribute and us except whitespace
or $tokens[0]->isa('PPI::Token::Attribute')
)
);
}
1;
=pod
=head1 TO DO
- Add C<function>, C<method> etc detector methods
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,240 @@
package PPI::Token::_QuoteEngine;
=pod
=head1 NAME
PPI::Token::_QuoteEngine - The PPI Quote Engine
=head1 DESCRIPTION
The C<PPI::Token::_QuoteEngine> package is designed hold functionality
for processing quotes and quote like operators, including regexes.
These have special requirements in parsing.
The C<PPI::Token::_QuoteEngine> package itself provides various parsing
methods, which the L<PPI::Token::Quote>, L<PPI::Token::QuoteLike> and
L<PPI::Token::Regexp> can inherit from. In this sense, it serves
as a base class.
=head2 Using this class
I<(Refers only to internal uses. This class does not provide a
public interface)>
To use these, you should initialize them as normal C<'$Class-E<gt>new'>,
and then call the 'fill' method, which will cause the specialised
parser to scan forwards and parse the quote to its end point.
If -E<gt>fill returns true, finalise the token.
=cut
use strict;
use Carp ();
our $VERSION = '1.270'; # VERSION
# Hook for the __TOKENIZER__on_char token call
sub __TOKENIZER__on_char {
my $class = shift;
my $t = $_[0]->{token} ? shift : return undef;
# Call the fill method to process the quote
my $rv = $t->{token}->_fill( $t );
return undef unless defined $rv;
## Doesn't support "end of file" indicator
# Finalize the token and return 0 to tell the tokenizer
# to go to the next character.
$t->_finalize_token;
0;
}
#####################################################################
# Optimised character processors, used for quotes
# and quote like stuff, and accessible to the child classes
# An outright scan, raw and fast.
# Searches for a particular character, not escaped, loading in new
# lines as needed.
# When called, we start at the current position.
# When leaving, the position should be set to the position
# of the character, NOT the one after it.
sub _scan_for_unescaped_character {
my $class = shift;
my $t = shift;
my $char = (length $_[0] == 1) ? quotemeta shift : return undef;
# Create the search regex.
# Same as above but with a negative look-behind assertion.
my $search = qr/(.*?(?<!\\)(?:\\\\)*$char)/;
my $string = '';
while ( exists $t->{line} ) {
# Get the search area for the current line
pos $t->{line} = $t->{line_cursor};
# Can we find a match on this line
if ( $t->{line} =~ m/\G$search/gc ) {
# Found the character on this line
$t->{line_cursor} += length($1) - 1;
return $string . $1;
}
# Load in the next line
$string .= substr $t->{line}, $t->{line_cursor};
my $rv = $t->_fill_line('inscan');
if ( $rv ) {
# Push to first character
$t->{line_cursor} = 0;
} elsif ( defined $rv ) {
# We hit the End of File
return \$string;
} else {
# Unexpected error
return undef;
}
}
# We shouldn't be able to get here
return undef;
}
# Scan for a close braced, and take into account both escaping,
# and open close bracket pairs in the string. When complete, the
# method leaves the line cursor on the LAST character found.
sub _scan_for_brace_character {
my $class = shift;
my $t = shift;
my $close_brace = $_[0] =~ /^(?:\>|\)|\}|\])$/ ? shift : Carp::confess(''); # return undef;
my $open_brace = $close_brace;
$open_brace =~ tr/\>\)\}\]/\<\(\{\[/;
# Create the search string
$close_brace = quotemeta $close_brace;
$open_brace = quotemeta $open_brace;
my $search = qr/\G(.*?(?<!\\)(?:\\\\)*(?:$open_brace|$close_brace))/;
# Loop as long as we can get new lines
my $string = '';
my $depth = 1;
while ( exists $t->{line} ) {
# Get the search area
pos $t->{line} = $t->{line_cursor};
# Look for a match
unless ( $t->{line} =~ /$search/gc ) {
# Load in the next line
$string .= substr( $t->{line}, $t->{line_cursor} );
my $rv = $t->_fill_line('inscan');
if ( $rv ) {
# Push to first character
$t->{line_cursor} = 0;
next;
}
if ( defined $rv ) {
# We hit the End of File
return \$string;
}
# Unexpected error
return undef;
}
# Add to the string
$string .= $1;
$t->{line_cursor} += length $1;
# Alter the depth and continue if we aren't at the end
$depth += ($1 =~ /$open_brace$/) ? 1 : -1 and next;
# Rewind the cursor by one character ( cludgy hack )
$t->{line_cursor} -= 1;
return $string;
}
# Returning the string as a reference indicates EOF
\$string;
}
# Find all spaces and comments, up to, but not including
# the first non-whitespace character.
#
# Although it doesn't return it, it leaves the cursor
# on the character following the gap
sub _scan_quote_like_operator_gap {
my $t = $_[1];
my $string = '';
while ( exists $t->{line} ) {
# Get the search area for the current line
pos $t->{line} = $t->{line_cursor};
# Since this regex can match zero characters, it should always match
$t->{line} =~ /\G(\s*(?:\#.*)?)/gc or return undef;
# Add the chars found to the string
$string .= $1;
# Did we match the entire line?
unless ( $t->{line_cursor} + length $1 == length $t->{line} ) {
# Partial line match, which means we are at
# the end of the gap. Fix the cursor and return
# the string.
$t->{line_cursor} += length $1;
return $string;
}
# Load in the next line.
# If we reach the EOF, $t->{line} gets deleted,
# which is caught by the while.
my $rv = $t->_fill_line('inscan');
if ( $rv ) {
# Set the cursor to the first character
$t->{line_cursor} = 0;
} elsif ( defined $rv ) {
# Returning the string as a reference indicates EOF
return \$string;
} else {
return undef;
}
}
# Shouldn't be able to get here
return undef;
}
1;
=pod
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,448 @@
package PPI::Token::_QuoteEngine::Full;
# Full quote engine
use strict;
use Clone ();
use Carp ();
use PPI::Token::_QuoteEngine ();
our $VERSION = '1.270'; # VERSION
our @ISA = 'PPI::Token::_QuoteEngine';
# Prototypes for the different braced sections
my %SECTIONS = (
'(' => { type => '()', _close => ')' },
'<' => { type => '<>', _close => '>' },
'[' => { type => '[]', _close => ']' },
'{' => { type => '{}', _close => '}' },
);
# For each quote type, the extra fields that should be set.
# This should give us faster initialization.
my %QUOTES = (
'q' => { operator => 'q', braced => undef, separator => undef, _sections => 1 },
'qq' => { operator => 'qq', braced => undef, separator => undef, _sections => 1 },
'qx' => { operator => 'qx', braced => undef, separator => undef, _sections => 1 },
'qw' => { operator => 'qw', braced => undef, separator => undef, _sections => 1 },
'qr' => { operator => 'qr', braced => undef, separator => undef, _sections => 1, modifiers => 1 },
'm' => { operator => 'm', braced => undef, separator => undef, _sections => 1, modifiers => 1 },
's' => { operator => 's', braced => undef, separator => undef, _sections => 2, modifiers => 1 },
'tr' => { operator => 'tr', braced => undef, separator => undef, _sections => 2, modifiers => 1 },
# Y is the little-used variant of tr
'y' => { operator => 'y', braced => undef, separator => undef, _sections => 2, modifiers => 1 },
'/' => { operator => undef, braced => 0, separator => '/', _sections => 1, modifiers => 1 },
# Angle brackets quotes mean "readline(*FILEHANDLE)"
'<' => { operator => undef, braced => 1, separator => undef, _sections => 1, },
# The final ( and kind of depreciated ) "first match only" one is not
# used yet, since I'm not sure on the context differences between
# this and the trinary operator, but it's here for completeness.
'?' => { operator => undef, braced => 0, separator => '?', _sections => 1, modifiers => 1 },
);
sub new {
my $class = shift;
my $init = defined $_[0]
? shift
: Carp::croak("::Full->new called without init string");
# Create the token
### This manual SUPER'ing ONLY works because none of
### Token::Quote, Token::QuoteLike and Token::Regexp
### implement a new function of their own.
my $self = PPI::Token::new( $class, $init ) or return undef;
# Do we have a prototype for the initializer? If so, add the extra fields
my $options = $QUOTES{$init} or return $self->_error(
"Unknown quote type '$init'"
);
foreach ( keys %$options ) {
$self->{$_} = $options->{$_};
}
# Set up the modifiers hash if needed
$self->{modifiers} = {} if $self->{modifiers};
# Handle the special < base
if ( $init eq '<' ) {
$self->{sections}->[0] = Clone::clone( $SECTIONS{'<'} );
}
$self;
}
sub _fill {
my $class = shift;
my $t = shift;
my $self = $t->{token}
or Carp::croak("::Full->_fill called without current token");
# Load in the operator stuff if needed
if ( $self->{operator} ) {
# In an operator based quote-like, handle the gap between the
# operator and the opening separator.
if ( substr( $t->{line}, $t->{line_cursor}, 1 ) =~ /\s/ ) {
# Go past the gap
my $gap = $self->_scan_quote_like_operator_gap( $t );
return undef unless defined $gap;
if ( ref $gap ) {
# End of file
$self->{content} .= $$gap;
return 0;
}
$self->{content} .= $gap;
}
# The character we are now on is the separator. Capture,
# and advance into the first section.
my $sep = substr( $t->{line}, $t->{line_cursor}++, 1 );
$self->{content} .= $sep;
# Determine if these are normal or braced type sections
if ( my $section = $SECTIONS{$sep} ) {
$self->{braced} = 1;
$self->{sections}->[0] = Clone::clone($section);
} else {
$self->{braced} = 0;
$self->{separator} = $sep;
}
}
# Parse different based on whether we are normal or braced
my $rv = $self->{braced}
? $self->_fill_braced($t)
: $self->_fill_normal($t);
return $rv if !$rv;
# Return now unless it has modifiers ( i.e. s/foo//eieio )
return 1 unless $self->{modifiers};
# Check for modifiers
my $char;
my $len = 0;
while ( ($char = substr( $t->{line}, $t->{line_cursor} + 1, 1 )) =~ /[^\W\d_]/ ) {
$len++;
$self->{content} .= $char;
$self->{modifiers}->{lc $char} = 1;
$t->{line_cursor}++;
}
}
# Handle the content parsing path for normally separated
sub _fill_normal {
my $self = shift;
my $t = shift;
# Get the content up to the next separator
my $string = $self->_scan_for_unescaped_character( $t, $self->{separator} );
return undef unless defined $string;
if ( ref $string ) {
# End of file
if ( length($$string) > 1 ) {
# Complete the properties for the first section
my $str = $$string;
chop $str;
$self->{sections}->[0] = {
position => length($self->{content}),
size => length($$string) - 1,
type => "$self->{separator}$self->{separator}",
};
$self->{_sections} = 1;
} else {
# No sections at all
$self->{sections} = [ ];
$self->{_sections} = 0;
}
$self->{content} .= $$string;
return 0;
}
# Complete the properties of the first section
$self->{sections}->[0] = {
position => length $self->{content},
size => length($string) - 1,
type => "$self->{separator}$self->{separator}",
};
$self->{content} .= $string;
# We are done if there is only one section
return 1 if $self->{_sections} == 1;
# There are two sections.
# Advance into the next section
$t->{line_cursor}++;
# Get the content up to the end separator
$string = $self->_scan_for_unescaped_character( $t, $self->{separator} );
return undef unless defined $string;
if ( ref $string ) {
# End of file
if ( length($$string) > 1 ) {
# Complete the properties for the second section
my $str = $$string;
chop $str;
$self->{sections}->[1] = {
position => length($self->{content}),
size => length($$string) - 1,
type => "$self->{separator}$self->{separator}",
};
} else {
# No sections at all
$self->{_sections} = 1;
}
$self->{content} .= $$string;
return 0;
}
# Complete the properties of the second section
$self->{sections}->[1] = {
position => length($self->{content}),
size => length($string) - 1
};
$self->{content} .= $string;
1;
}
# Handle content parsing for matching brace separated
sub _fill_braced {
my $self = shift;
my $t = shift;
# Get the content up to the close character
my $section = $self->{sections}->[0];
my $brace_str = $self->_scan_for_brace_character( $t, $section->{_close} );
return undef unless defined $brace_str;
if ( ref $brace_str ) {
# End of file
if ( length($$brace_str) > 1 ) {
# Complete the properties for the first section
my $str = $$brace_str;
chop $str;
$self->{sections}->[0] = {
position => length($self->{content}),
size => length($$brace_str) - 1,
type => $section->{type},
};
$self->{_sections} = 1;
} else {
# No sections at all
$self->{sections} = [ ];
$self->{_sections} = 0;
}
$self->{content} .= $$brace_str;
return 0;
}
# Complete the properties of the first section
$section->{position} = length $self->{content};
$section->{size} = length($brace_str) - 1;
$self->{content} .= $brace_str;
delete $section->{_close};
# We are done if there is only one section
return 1 if $self->{_sections} == 1;
# There are two sections.
# Is there a gap between the sections.
my $char = substr( $t->{line}, ++$t->{line_cursor}, 1 );
if ( $char =~ /\s/ ) {
# Go past the gap
my $gap_str = $self->_scan_quote_like_operator_gap( $t );
return undef unless defined $gap_str;
if ( ref $gap_str ) {
# End of file
$self->{content} .= $$gap_str;
return 0;
}
$self->{content} .= $gap_str;
$char = substr( $t->{line}, $t->{line_cursor}, 1 );
}
$section = $SECTIONS{$char};
if ( $section ) {
# It's a brace
# Initialize the second section
$self->{content} .= $char;
$section = { %$section };
# Advance into the second section
$t->{line_cursor}++;
# Get the content up to the close character
$brace_str = $self->_scan_for_brace_character( $t, $section->{_close} );
return undef unless defined $brace_str;
if ( ref $brace_str ) {
# End of file
if ( length($$brace_str) > 1 ) {
# Complete the properties for the second section
my $str = $$brace_str;
chop $str;
$self->{sections}->[1] = {
position => length($self->{content}),
size => length($$brace_str) - 1,
type => $section->{type},
};
$self->{_sections} = 2;
} else {
# No sections at all
$self->{_sections} = 1;
}
$self->{content} .= $$brace_str;
return 0;
} else {
# Complete the properties for the second section
$self->{sections}->[1] = {
position => length($self->{content}),
size => length($brace_str) - 1,
type => $section->{type},
};
$self->{content} .= $brace_str;
}
} elsif ( $char =~ m/ \A [^\w\s] \z /smx ) {
# It is some other delimiter (weird, but possible)
# Add the delimiter to the content.
$self->{content} .= $char;
# Advance into the next section
$t->{line_cursor}++;
# Get the content up to the end separator
my $string = $self->_scan_for_unescaped_character( $t, $char );
return undef unless defined $string;
if ( ref $string ) {
# End of file
if ( length($$string) > 1 ) {
# Complete the properties for the second section
my $str = $$string;
chop $str;
$self->{sections}->[1] = {
position => length($self->{content}),
size => length($$string) - 1,
type => "$char$char",
};
} else {
# Only the one section
$self->{_sections} = 1;
}
$self->{content} .= $$string;
return 0;
}
# Complete the properties of the second section
$self->{sections}->[1] = {
position => length($self->{content}),
size => length($string) - 1,
type => "$char$char",
};
$self->{content} .= $string;
} else {
# Error, it has to be a delimiter of some sort.
# Although this will result in a REALLY illegal regexp,
# we allow it anyway.
# Create a null second section
$self->{sections}->[1] = {
position => length($self->{content}),
size => 0,
type => '',
};
# Attach an error to the token and move on
$self->{_error} = "No second section of regexp, or does not start with a balanced character";
# Roll back the cursor one char and return signalling end of regexp
$t->{line_cursor}--;
return 0;
}
1;
}
#####################################################################
# Additional methods to find out about the quote
# In a scalar context, get the number of sections
# In an array context, get the section information
sub _sections {
wantarray ? @{$_[0]->{sections}} : scalar @{$_[0]->{sections}}
}
# Get a section's content
sub _section_content {
my $self = shift;
my $i = shift;
$self->{sections} or return;
my $section = $self->{sections}->[$i] or return;
return substr( $self->content, $section->{position}, $section->{size} );
}
# Get the modifiers if any.
# In list context, return the modifier hash.
# In scalar context, clone the hash and return a reference to it.
# If there are no modifiers, simply return.
sub _modifiers {
my $self = shift;
$self->{modifiers} or return;
wantarray and return %{ $self->{modifiers} };
return +{ %{ $self->{modifiers} } };
}
# Get the delimiters, or at least give it a good try to get them.
sub _delimiters {
my $self = shift;
$self->{sections} or return;
my @delims;
foreach my $sect ( @{ $self->{sections} } ) {
if ( exists $sect->{type} ) {
push @delims, $sect->{type};
} else {
my $content = $self->content;
push @delims,
substr( $content, $sect->{position} - 1, 1 ) .
substr( $content, $sect->{position} + $sect->{size}, 1 );
}
}
return @delims;
}
1;
=pod
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,67 @@
package PPI::Token::_QuoteEngine::Simple;
# Simple quote engine
use strict;
use PPI::Token::_QuoteEngine ();
our $VERSION = '1.270'; # VERSION
our @ISA = 'PPI::Token::_QuoteEngine';
sub new {
my $class = shift;
my $separator = shift or return undef;
# Create a new token containing the separator
### This manual SUPER'ing ONLY works because none of
### Token::Quote, Token::QuoteLike and Token::Regexp
### implement a new function of their own.
my $self = PPI::Token::new( $class, $separator ) or return undef;
$self->{separator} = $separator;
$self;
}
sub _fill {
my $class = shift;
my $t = shift;
my $self = $t->{token} or return undef;
# Scan for the end separator
my $string = $self->_scan_for_unescaped_character( $t, $self->{separator} );
return undef unless defined $string;
if ( ref $string ) {
# End of file
$self->{content} .= $$string;
return 0;
} else {
# End of string
$self->{content} .= $string;
return $self;
}
}
1;
=pod
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

1093
database/perl/vendor/lib/PPI/Tokenizer.pm vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,240 @@
package PPI::Transform;
=pod
=head1 NAME
PPI::Transform - Abstract base class for document transformation classes
=head1 DESCRIPTION
C<PPI::Transform> provides an API for the creation of classes and objects
that modify or transform PPI documents.
=head1 METHODS
=cut
use strict;
use Carp ();
use List::Util ();
use PPI::Document ();
use Params::Util qw{_INSTANCE _CLASS _CODE _SCALAR0};
our $VERSION = '1.270'; # VERSION
#####################################################################
# Apply Handler Registration
my %HANDLER;
my @ORDER;
# Yes, you can use this yourself.
# I'm just leaving it undocumented for now.
sub register_apply_handler {
my $class = shift;
my $handler = _CLASS(shift) or Carp::croak("Invalid PPI::Transform->register_apply_handler param");
my $get = _CODE(shift) or Carp::croak("Invalid PPI::Transform->register_apply_handler param");
my $set = _CODE(shift) or Carp::croak("Invalid PPI::Transform->register_apply_handler param");
if ( $HANDLER{$handler} ) {
Carp::croak("PPI::Transform->apply handler '$handler' already exists");
}
# Register the handler
$HANDLER{$handler} = [ $get, $set ];
unshift @ORDER, $handler;
}
# Register the default handlers
__PACKAGE__->register_apply_handler( 'SCALAR', \&_SCALAR_get, \&_SCALAR_set );
__PACKAGE__->register_apply_handler( 'PPI::Document', sub { $_[0] }, sub() { 1 } );
#####################################################################
# Constructor
=pod
=head2 new
my $transform = PPI::Transform->new(
param1 => 'value1',
param2 => 'value2',
);
The C<new> constructor creates a new object for your C<PPI::Transform>
subclass. A default constructor is provided for you which takes no params
and creates a basic, empty, object.
If you wish to have your transform constructor take params, these B<must>
be in the form of a list of key/value pairs.
Returns a new C<PPI::Transform>-compatible object, or returns
C<undef> on error.
=cut
sub new {
my $class = shift;
bless { @_ }, $class;
}
=pod
=head2 document
The C<document> method should be implemented by each subclass, and
takes a single argument of a L<PPI::Document> object, modifying it
B<in place> as appropriate for the particular transform class.
That's right, this method B<will not clone> and B<should not clone>
the document object. If you do not want the original to be modified,
you need to clone it yourself before passing it in.
Returns the numbers of changes made to the document. If the transform
is unable to track the quantity (including the situation where it cannot
tell B<IF> it made a change) it should return 1. Returns zero if no
changes were made to the document, or C<undef> if an error occurs.
By default this error is likely to only mean that you passed in something
that wasn't a L<PPI::Document>, but may include additional errors
depending on the subclass.
=cut
sub document {
my $class = shift;
die "$class does not implement the required ->document method";
}
=pod
=head2 apply
The C<apply> method is used to apply the transform to something. The
argument must be a L<PPI::Document>, or something which can be turned
into one and then be written back to again.
Currently, this list is limited to a C<SCALAR> reference, although a
handler registration process is available for you to add support for
additional types of object should you wish (see the source for this module).
Returns true if the transform was applied, false if there is an error in the
transform process, or may die if there is a critical error in the apply
handler.
=cut
sub apply {
my $self = _SELF(shift);
my $it = defined $_[0] ? shift : return undef;
# Try to find an apply handler
my $class = _SCALAR0($it) ? 'SCALAR'
: List::Util::first { _INSTANCE($it, $_) } @ORDER
or return undef;
my $handler = $HANDLER{$class}
or die("->apply handler for $class missing! Panic");
# Get, change, set
my $Document = _INSTANCE($handler->[0]->($it), 'PPI::Document')
or Carp::croak("->apply handler for $class failed to get a PPI::Document");
$self->document( $Document ) or return undef;
$handler->[1]->($it, $Document)
or Carp::croak("->apply handler for $class failed to save the changed document");
1;
}
=pod
=head2 file
# Read from one file and write to another
$transform->file( 'Input.pm' => 'Output.pm' );
# Change a file in place
$transform->file( 'Change.pm' );
The C<file> method modifies a Perl document by filename. If passed a single
parameter, it modifies the file in-place. If provided a second parameter,
it will attempt to save the modified file to the alternative filename.
Returns true on success, or C<undef> on error.
=cut
sub file {
my $self = _SELF(shift);
# Where do we read from and write to
my $input = defined $_[0] ? shift : return undef;
my $output = @_ ? defined $_[0] ? "$_[0]" : undef : $input or return undef;
# Process the file
my $Document = PPI::Document->new( "$input" ) or return undef;
$self->document( $Document ) or return undef;
$Document->save( $output );
}
#####################################################################
# Apply Hander Methods
sub _SCALAR_get {
PPI::Document->new( $_[0] );
}
sub _SCALAR_set {
my $it = shift;
$$it = $_[0]->serialize;
1;
}
#####################################################################
# Support Functions
sub _SELF {
return shift if ref $_[0];
my $self = $_[0]->new or Carp::croak(
"Failed to auto-instantiate new $_[0] object"
);
$self;
}
1;
=pod
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2001 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

View File

@@ -0,0 +1,179 @@
package PPI::Transform::UpdateCopyright;
=pod
=head1 NAME
PPI::Transform::UpdateCopyright - Demonstration PPI::Transform class
=head1 SYNOPSIS
my $transform = PPI::Transform::UpdateCopyright->new(
name => 'Adam Kennedy'
);
$transform->file('Module.pm');
=head1 DESCRIPTION
B<PPI::Transform::UpdateCopyright> provides a demonstration of a typical
L<PPI::Transform> class.
This class implements a document transform that will take the name of an
author and update the copyright statement to refer to the current year,
if it does not already do so.
=head1 METHODS
=cut
use strict;
use Params::Util qw{_STRING};
use PPI::Transform ();
our $VERSION = '1.270'; # VERSION
#####################################################################
# Constructor and Accessors
=pod
=head2 new
my $transform = PPI::Transform::UpdateCopyright->new(
name => 'Adam Kennedy'
);
The C<new> constructor creates a new transform object for a specific
author. It takes a single C<name> parameter that should be the name
(or longer string) for the author.
Specifying the name is required to allow the changing of a subset of
copyright statements that refer to you from a larger set in a file.
=cut
sub new {
my $self = shift->SUPER::new(@_);
# Must provide a name
unless ( defined _STRING($self->name) ) {
PPI::Exception->throw("Did not provide a valid name param");
}
return $self;
}
=pod
=head2 name
The C<name> accessor returns the author name that the transform will be
searching for copyright statements of.
=cut
sub name {
$_[0]->{name};
}
#####################################################################
# Transform
sub document {
my $self = shift;
my $document = _INSTANCE(shift, 'PPI::Document') or return undef;
# Find things to transform
my $name = quotemeta $self->name;
my $regexp = qr/\bcopyright\b.*$name/m;
my $elements = $document->find( sub {
$_[1]->isa('PPI::Token::Pod') or return '';
$_[1]->content =~ $regexp or return '';
return 1;
} );
return undef unless defined $elements;
return 0 unless $elements;
# Try to transform any elements
my $changes = 0;
my $change = sub {
my $copyright = shift;
my $thisyear = (localtime time)[5] + 1900;
my @year = $copyright =~ m/(\d{4})/g;
if ( @year == 1 ) {
# Handle the single year format
if ( $year[0] == $thisyear ) {
# No change
return $copyright;
} else {
# Convert from single year to multiple year
$changes++;
$copyright =~ s/(\d{4})/$1 - $thisyear/;
return $copyright;
}
}
if ( @year == 2 ) {
# Handle the range format
if ( $year[1] == $thisyear ) {
# No change
return $copyright;
} else {
# Change the second year to the current one
$changes++;
$copyright =~ s/$year[1]/$thisyear/;
return $copyright;
}
}
# huh?
die "Invalid or unknown copyright line '$copyright'";
};
# Attempt to transform each element
my $pattern = qr/\b(copyright.*\d)({4}(?:\s*-\s*\d{4})?)(.*$name)/mi;
foreach my $element ( @$elements ) {
$element =~ s/$pattern/$1 . $change->($2) . $2/eg;
}
return $changes;
}
1;
=pod
=head1 TO DO
- May need to overload some methods to forcefully prevent Document
objects becoming children of another Node.
=head1 SUPPORT
See the L<support section|PPI/SUPPORT> in the main module.
=head1 AUTHOR
Adam Kennedy E<lt>adamk@cpan.orgE<gt>
=head1 COPYRIGHT
Copyright 2009 - 2011 Adam Kennedy.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.
The full text of the license can be found in the
LICENSE file included with this module.
=cut

72
database/perl/vendor/lib/PPI/Util.pm vendored Normal file
View File

@@ -0,0 +1,72 @@
package PPI::Util;
# Provides some common utility functions that can be imported
use strict;
use Exporter ();
use Digest::MD5 ();
use Params::Util qw{_INSTANCE _SCALAR0 _ARRAY0};
our $VERSION = '1.270'; # VERSION
our @ISA = 'Exporter';
our @EXPORT_OK = qw{ _Document _slurp };
# 5.8.7 was the first version to resolve the notorious
# "unicode length caching" bug.
use constant HAVE_UNICODE => !! ( $] >= 5.008007 );
# Common reusable true and false functions
# This makes it easy to upgrade many places in PPI::XS
sub TRUE () { 1 }
sub FALSE () { '' }
#####################################################################
# Functions
# Allows a sub that takes a L<PPI::Document> to handle the full range
# of different things, including file names, SCALAR source, etc.
sub _Document {
shift if @_ > 1;
return undef unless defined $_[0];
require PPI::Document;
return PPI::Document->new(shift) unless ref $_[0];
return PPI::Document->new(shift) if _SCALAR0($_[0]);
return PPI::Document->new(shift) if _ARRAY0($_[0]);
return shift if _INSTANCE($_[0], 'PPI::Document');
return undef;
}
# Provide a simple _slurp implementation
sub _slurp {
my $file = shift;
local $/ = undef;
local *FILE;
open( FILE, '<', $file ) or return "open($file) failed: $!";
my $source = <FILE>;
close( FILE ) or return "close($file) failed: $!";
return \$source;
}
# Provides a version of Digest::MD5's md5hex that explicitly
# works on the unix-newlined version of the content.
sub md5hex {
my $string = shift;
$string =~ s/(?:\015{1,2}\012|\015|\012)/\015/gs;
Digest::MD5::md5_hex($string);
}
# As above but slurps and calculates the id for a file by name
sub md5hex_file {
my $file = shift;
my $content = _slurp($file);
return undef unless ref $content;
$$content =~ s/(?:\015{1,2}\012|\015|\012)/\n/gs;
md5hex($$content);
}
1;

View File

@@ -0,0 +1,165 @@
package PPI::XSAccessor;
# This is an experimental prototype, use at your own risk.
# Provides optional enhancement of PPI with Class::XSAccessor (if installed)
use 5.006;
use strict;
use PPI ();
our $VERSION = '1.270'; # VERSION
######################################################################
# Replacement Methods
# Packages are implemented here in alphabetical order
package #hide from indexer
PPI::Document;
use Class::XSAccessor
replace => 1,
getters => {
readonly => 'readonly',
},
true => [
'scope'
];
package #hide from indexer
PPI::Document::File;
use Class::XSAccessor
replace => 1,
getters => {
filename => 'filename',
};
package #hide from indexer
PPI::Document::Fragment;
use Class::XSAccessor
replace => 1,
false => [
'scope',
];
package #hide from indexer
PPI::Document::Normalized;
use Class::XSAccessor
replace => 1,
getters => {
'_Document' => 'Document',
'version' => 'version',
'functions' => 'functions',
};
package #hide from indexer
PPI::Element;
use Class::XSAccessor
replace => 1,
true => [
'significant',
];
package #hide from indexer
PPI::Exception;
use Class::XSAccessor
replace => 1,
getters => {
message => 'message',
};
package #hide from indexer
PPI::Node;
use Class::XSAccessor
replace => 1,
false => [
'scope',
];
package #hide from indexer
PPI::Normal;
use Class::XSAccessor
replace => 1,
getters => {
'layer' => 'layer',
};
package #hide from indexer
PPI::Statement;
use Class::XSAccessor
replace => 1,
true => [
'__LEXER__normal',
];
package #hide from indexer
PPI::Statement::Compound;
use Class::XSAccessor
replace => 1,
true => [
'scope',
],
false => [
'__LEXER__normal',
];
package #hide from indexer
PPI::Statement::Data;
use Class::XSAccessor
replace => 1,
false => [
'_complete',
];
package #hide from indexer
PPI::Statement::End;
use Class::XSAccessor
replace => 1,
true => [
'_complete',
];
package #hide from indexer
PPI::Statement::Given;
use Class::XSAccessor
replace => 1,
true => [
'scope',
],
false => [
'__LEXER__normal',
];
package #hide from indexer
PPI::Token;
use Class::XSAccessor
replace => 1,
getters => {
content => 'content',
},
setters => {
set_content => 'content',
},
true => [
'__TOKENIZER__on_line_start',
'__TOKENIZER__on_line_end',
];
1;