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