File Coverage

blib/lib/PPI/Token/Symbol.pm
Criterion Covered Total %
statement 63 63 100.0
branch 34 38 89.4
condition 7 9 77.7
subroutine 8 8 100.0
pod 4 4 100.0
total 116 122 95.0


line stmt bran cond sub pod time code
1             package PPI::Token::Symbol;
2              
3             =pod
4              
5             =head1 NAME
6              
7             PPI::Token::Symbol - A token class for variables and other symbols
8              
9             =head1 INHERITANCE
10              
11             PPI::Token::Symbol
12             isa PPI::Token
13             isa PPI::Element
14              
15             =head1 DESCRIPTION
16              
17             The C<PPI::Token::Symbol> class is used to cover all tokens that represent
18             variables and other things that start with a sigil.
19              
20             =head1 METHODS
21              
22             This class has several methods beyond what is provided by its
23             L<PPI::Token> and L<PPI::Element> parent classes.
24              
25             Most methods are provided to help work out what the object is actually
26             pointing at, rather than what it might appear to be pointing at.
27              
28             =cut
29            
30 67     67   382 use strict;
  67         96  
  67         2300  
31 67     67   257 use Params::Util qw{_INSTANCE};
  67         90  
  67         3266  
32 67     67   459 use PPI::Token ();
  67         228  
  67         69265  
33              
34             our $VERSION = '1.284';
35              
36             our @ISA = "PPI::Token";
37              
38              
39              
40              
41              
42             #####################################################################
43             # PPI::Token::Symbol Methods
44              
45             =pod
46              
47             =head2 canonical
48              
49             The C<canonical> method returns a normalized, canonical version of the
50             symbol.
51              
52             For example, it converts C<$ ::foo'bar::baz> to C<$main::foo::bar::baz>.
53              
54             This does not fully resolve the symbol, but merely removes syntax
55             variations.
56              
57             =cut
58              
59             sub canonical {
60 96     96 1 470 my $symbol = shift->content;
61 96         303 $symbol =~ s/\s+//;
62 96         213 $symbol =~ s/\'/::/g;
63 96         237 $symbol =~ s/(?<=[\$\@\%\&\*])::/main::/;
64 96         352 $symbol;
65             }
66              
67             =pod
68              
69             =head2 symbol
70              
71             The C<symbol> method returns the ACTUAL symbol this token refers to.
72              
73             A token of C<$foo> might actually be referring to C<@foo>, if it is found
74             in the form C<$foo[1]>.
75              
76             This method attempts to resolve these issues to determine the actual
77             symbol.
78              
79             Returns the symbol as a string.
80              
81             =cut
82              
83             my %cast_which_trumps_braces = map { $_ => 1 } qw{ $ @ % };
84              
85             sub symbol {
86 74     74 1 7325 my $self = shift;
87 74         210 my $symbol = $self->canonical;
88              
89             # Immediately return the cases where it can't be anything else
90 74         162 my $type = substr( $symbol, 0, 1 );
91 74 100       253 return $symbol if $type eq '&';
92              
93             # Unless the next significant Element is a structure, it's correct.
94 70         223 my $after = $self->snext_sibling;
95 70 100       683 return $symbol unless _INSTANCE($after, 'PPI::Structure');
96              
97             # Process the rest for cases where it might actually be something else
98 30         131 my $braces = $after->braces;
99 30 50       86 return $symbol unless defined $braces;
100 30 100       93 if ( $type eq '$' ) {
    100          
    50          
101              
102             # If it is cast to '$' or '@', that trumps any braces
103 18         48 my $before = $self->sprevious_sibling;
104             return $symbol if $before &&
105             $before->isa( 'PPI::Token::Cast' ) &&
106 18 50 66     119 $cast_which_trumps_braces{ $before->content };
      66        
107              
108             # Otherwise the braces rule
109 6 100       20 substr( $symbol, 0, 1, '@' ) if $braces eq '[]';
110 6 100       17 substr( $symbol, 0, 1, '%' ) if $braces eq '{}';
111              
112             } elsif ( $type eq '@' ) {
113 6 100       23 substr( $symbol, 0, 1, '%' ) if $braces eq '{}';
114              
115             } elsif ( $type eq '%' ) {
116 6 100       25 substr( $symbol, 0, 1, '@' ) if $braces eq '[]';
117              
118             }
119              
120 18         121 $symbol;
121             }
122              
123             =pod
124              
125             =head2 raw_type
126              
127             The C<raw_type> method returns the B<apparent> type of the symbol in the
128             form of its sigil.
129              
130             Returns the sigil as a string.
131              
132             =cut
133              
134             sub raw_type {
135 28     28 1 19739 substr( $_[0]->content, 0, 1 );
136             }
137              
138             =pod
139              
140             =head2 symbol_type
141              
142             The C<symbol_type> method returns the B<actual> type of the symbol in the
143             form of its sigil.
144              
145             Returns the sigil as a string.
146              
147             =cut
148              
149             sub symbol_type {
150 28     28 1 101 substr( $_[0]->symbol, 0, 1 );
151             }
152              
153              
154              
155              
156              
157             #####################################################################
158             # Tokenizer Methods
159              
160             sub __TOKENIZER__on_char {
161 19772     19772   26427 my $t = $_[1];
162              
163             # Suck in till the end of the symbol
164 19772         50801 pos $t->{line} = $t->{line_cursor};
165 19772 100       78451 if ( $t->{line} =~ m/\G([\w:\']+)/gc ) {
166 14889         39951 $t->{token}->{content} .= $1;
167 14889         26089 $t->{line_cursor} += length $1;
168             }
169              
170             # Handle magic things
171 19772         29882 my $content = $t->{token}->{content};
172 19772 100 100     58329 if ( $content eq '@_' or $content eq '$_' ) {
173 1254         3111 $t->{class} = $t->{token}->set_class( 'Magic' );
174 1254         2953 return $t->_finalize_token->__TOKENIZER__on_char( $t );
175             }
176              
177             # Shortcut for most of the X:: symbols
178 18518 100       33474 if ( $content eq '$::' ) {
179             # May well be an alternate form of a Magic
180 19         60 my $nextchar = substr( $t->{line}, $t->{line_cursor}, 1 );
181 19 100       49 if ( $nextchar eq '|' ) {
182 17         28 $t->{token}->{content} .= $nextchar;
183 17         27 $t->{line_cursor}++;
184 17         52 $t->{class} = $t->{token}->set_class( 'Magic' );
185             }
186 19         51 return $t->_finalize_token->__TOKENIZER__on_char( $t );
187             }
188 18499 100       37609 if ( $content =~ /^[\$%*@&]::(?:[^\w]|$)/ ) {
189 52         119 my $current = substr( $content, 0, 3, '' );
190 52         93 $t->{token}->{content} = $current;
191 52         79 $t->{line_cursor} -= length( $content );
192 52         112 return $t->_finalize_token->__TOKENIZER__on_char( $t );
193             }
194 18447 100       52923 if ( $content =~ /^(?:\$|\@)\d+/ ) {
195 55         212 $t->{class} = $t->{token}->set_class( 'Magic' );
196 55         146 return $t->_finalize_token->__TOKENIZER__on_char( $t );
197             }
198              
199             # Verify and extract actual full symbol name from sigil to end
200 18392         60413 my $sep = qr/ (?: :: | '(?!\d) ) /x; # :: and ' are namespace separators
201 18392         144923 my $pattern = qr/
202             ^(
203             [\$@%&*]
204             (?:
205             : (?! : ) # allow single-colon non-magic variables
206             |
207             $sep? # optional separator
208             \w+ # a word
209             (?: $sep \w+ )* # optionally more separator+word pairs
210             (?: :: )? # optionally what's technically a
211             # compiler-magic hash, but keep it here
212             )
213             )
214             /x;
215 18392 50       126959 return undef if $content !~ $pattern;
216              
217 18392 100       51154 unless ( length $1 eq length $content ) {
218 64         232 $t->{line_cursor} += length($1) - length($content);
219 64         168 $t->{token}->{content} = $1;
220             }
221              
222 18392         38322 $t->_finalize_token->__TOKENIZER__on_char( $t );
223             }
224              
225             1;
226              
227             =pod
228              
229             =head1 SUPPORT
230              
231             See the L<support section|PPI/SUPPORT> in the main module.
232              
233             =head1 AUTHOR
234              
235             Adam Kennedy E<lt>adamk@cpan.orgE<gt>
236              
237             =head1 COPYRIGHT
238              
239             Copyright 2001 - 2011 Adam Kennedy.
240              
241             This program is free software; you can redistribute
242             it and/or modify it under the same terms as Perl itself.
243              
244             The full text of the license can be found in the
245             LICENSE file included with this module.
246              
247             =cut