File Coverage

blib/lib/PPI/Token/Unknown.pm
Criterion Covered Total %
statement 211 218 96.7
branch 151 160 94.3
condition 91 99 91.9
subroutine 11 11 100.0
pod n/a
total 464 488 95.0


line stmt bran cond sub pod time code
1             package PPI::Token::Unknown;
2              
3             =pod
4              
5             =head1 NAME
6              
7             PPI::Token::Unknown - Token of unknown or as-yet undetermined type
8              
9             =head1 INHERITANCE
10              
11             PPI::Token::Unknown
12             isa PPI::Token
13             isa PPI::Element
14              
15             =head1 DESCRIPTION
16              
17             Object of the type C exist primarily inside the
18             tokenizer, where they are temporarily brought into existing for a very
19             short time to represent a token that could be one of a number of types.
20              
21             Generally, they only exist for a character or two, after which they are
22             resolved and converted into the correct type. For an object of this type
23             to survive the parsing process is considered a major bug.
24              
25             Please report any C you encounter in a L
26             object as a bug.
27              
28             =cut
29              
30 68     68   342 use strict;
  68         89  
  68         1889  
31 68     68   241 use PPI::Token ();
  68         86  
  68         787  
32 68     68   216 use PPI::Exception ();
  68         79  
  68         1132  
33 68     68   221 use PPI::Singletons qw' %MAGIC $CURLY_SYMBOL ';
  68         108  
  68         163450  
34              
35             our $VERSION = '1.287';
36              
37             our @ISA = "PPI::Token";
38              
39              
40              
41              
42              
43              
44             #####################################################################
45             # Tokenizer Methods
46              
47             sub __TOKENIZER__on_char {
48 36245     36245   53727 my ( $self, $t ) = @_; # Self and Tokenizer
49 36245         59360 my $c = $t->{token}->{content}; # Current token
50 36245         64969 my $char = substr( $t->{line}, $t->{line_cursor}, 1 ); # Current character
51              
52             # Now, we split on the different values of the current content
53 36245 100       95023 if ( $c eq '*' ) {
    100          
    100          
    100          
    100          
    100          
    50          
54             # Is it a number?
55 1385 100       3855 if ( $char =~ /\d/ ) {
56             # bitwise operator
57 91         261 $t->{class} = $t->{token}->set_class( 'Operator' );
58 91         214 return $t->_finalize_token->__TOKENIZER__on_char( $t );
59             }
60              
61 1294 100       3442 if ( $char =~ /[\w:]/ ) {
62             # Symbol (unless the thing before it is a number
63 445         1125 my ( $prev ) = $t->_previous_significant_tokens(1);
64 445 100 100     3145 if ( not $prev or not $prev->isa('PPI::Token::Number') ) {
65 431         1266 $t->{class} = $t->{token}->set_class( 'Symbol' );
66 431         1252 return 1;
67             }
68             }
69              
70 863 100       1754 if ( $char eq '{' ) {
71             # Get rest of line
72 26         76 pos $t->{line} = $t->{line_cursor} + 1;
73 26 50       226 if ( $t->{line} =~ m/$CURLY_SYMBOL/gc ) {
74             # control-character symbol (e.g. *{^_Foo})
75 0         0 $t->{class} = $t->{token}->set_class( 'Magic' );
76 0         0 return 1;
77             }
78             }
79              
80             # Postfix dereference: ->**
81 863 100       1781 if ( $char eq '*' ) {
82 43         132 my ( $prev ) = $t->_previous_significant_tokens(1);
83 43 100 100     425 if ( $prev and $prev->isa('PPI::Token::Operator') and $prev->content eq '->' ) {
      100        
84 1         4 $t->{class} = $t->{token}->set_class( 'Cast' );
85 1         3 return 1;
86             }
87             }
88              
89 862 100 100     2837 if ( $char eq '*' || $char eq '=' ) {
90             # Power operator '**' or mult-assign '*='
91 75         217 $t->{class} = $t->{token}->set_class( 'Operator' );
92 75         218 return 1;
93             }
94              
95 787 100       1820 return $self->_as_cast_or_op($t) if $self->_is_cast_or_op($char);
96              
97 693         1712 $t->{class} = $t->{token}->set_class( 'Operator' );
98 693         1491 return $t->_finalize_token->__TOKENIZER__on_char( $t );
99              
100              
101              
102             } elsif ( $c eq '$' ) {
103             # Postfix dereference: ->$* ->$#*
104 25203 100 100     64052 if ( $char eq '*' || $char eq '#' ) {
105 127         378 my ( $prev ) = $t->_previous_significant_tokens(1);
106 127 100 100     716 if ( $prev and $prev->isa('PPI::Token::Operator') and $prev->content eq '->' ) {
      100        
107 2         5 $t->{class} = $t->{token}->set_class( 'Cast' );
108 2         5 return 1;
109             }
110             }
111              
112 25201 100       80065 if ( $char =~ /[a-z_]/i ) {
113             # Symbol
114 24153         53848 $t->{class} = $t->{token}->set_class( 'Symbol' );
115 24153         56624 return 1;
116             }
117              
118             # Is it a nameless arg in a signature?
119 1048         3892 my %noname = ( ')' => 1, '=' => 1, ',' => 1 );
120 1048 100 100     2680 if ( $noname{$char} and $t->_current_token_has_signatures_active ) {
121 12         31 $t->{class} = $t->{token}->set_class('Symbol');
122 12         28 return $t->_finalize_token->__TOKENIZER__on_char($t);
123             }
124              
125 1036 100       3247 if ( $MAGIC{ $c . $char } ) {
126             # Magic variable
127 891         2440 $t->{class} = $t->{token}->set_class( 'Magic' );
128 891         3054 return 1;
129             }
130              
131 145 100       323 if ( $char eq '{' ) {
132             # Get rest of line
133 19         62 pos $t->{line} = $t->{line_cursor} + 1;
134 19 100       172 if ( $t->{line} =~ m/$CURLY_SYMBOL/gc ) {
135             # control-character symbol (e.g. ${^MATCH})
136 4         9 $t->{class} = $t->{token}->set_class( 'Magic' );
137 4         13 return 1;
138             }
139             }
140              
141             # Must be a cast
142 141         438 $t->{class} = $t->{token}->set_class( 'Cast' );
143 141         396 return $t->_finalize_token->__TOKENIZER__on_char( $t );
144              
145              
146              
147             } elsif ( $c eq '@' ) {
148             # Postfix dereference: ->@*
149 2952 100       5739 if ( $char eq '*' ) {
150 13         47 my ( $prev ) = $t->_previous_significant_tokens(1);
151 13 100 100     127 if ( $prev and $prev->isa('PPI::Token::Operator') and $prev->content eq '->' ) {
      100        
152 1         4 $t->{class} = $t->{token}->set_class( 'Cast' );
153 1         3 return 1;
154             }
155             }
156              
157 2951 100       10061 if ( $char =~ /[\w:]/ ) {
158             # Symbol
159 2066         5521 $t->{class} = $t->{token}->set_class( 'Symbol' );
160 2066         5078 return 1;
161             }
162              
163             # Is it a nameless arg in a signature?
164 885 100 100     2632 if ( $char eq ')' and $t->_current_token_has_signatures_active ) {
165 1         3 $t->{class} = $t->{token}->set_class('Symbol');
166 1         3 return $t->_finalize_token->__TOKENIZER__on_char($t);
167             }
168              
169 884 100       3062 if ( $MAGIC{ $c . $char } ) {
170             # Magic variable
171 30         104 $t->{class} = $t->{token}->set_class( 'Magic' );
172 30         104 return 1;
173             }
174              
175 854 100       1765 if ( $char eq '{' ) {
176             # Get rest of line
177 264         857 pos $t->{line} = $t->{line_cursor} + 1;
178 264 100       2276 if ( $t->{line} =~ m/$CURLY_SYMBOL/gc ) {
179             # control-character symbol (e.g. @{^_Foo})
180 1         3 $t->{class} = $t->{token}->set_class( 'Magic' );
181 1         3 return 1;
182             }
183             }
184              
185             # Must be a cast
186 853         2288 $t->{class} = $t->{token}->set_class( 'Cast' );
187 853         2173 return $t->_finalize_token->__TOKENIZER__on_char( $t );
188              
189              
190              
191             } elsif ( $c eq '%' ) {
192             # Postfix dereference: ->%* ->%[...]
193 1509 100 100     5268 if ( $char eq '*' || $char eq '[' ) {
194 33         106 my ( $prev ) = $t->_previous_significant_tokens(1);
195 33 100 100     308 if ( $prev and $prev->isa('PPI::Token::Operator') and $prev->content eq '->' ) {
      100        
196 2 100       5 if ( $char eq '*' ) {
197 1         4 $t->{class} = $t->{token}->set_class( 'Cast' );
198 1         3 return 1;
199             }
200 1 50       5 if ( $char eq '[' ) {
201 1         4 $t->{class} = $t->{token}->set_class( 'Cast' );
202 1         3 return $t->_finalize_token->__TOKENIZER__on_char( $t );
203             }
204             }
205             }
206              
207             # Is it a number?
208 1507 100       4227 if ( $char =~ /\d/ ) {
209             # bitwise operator
210 110         289 $t->{class} = $t->{token}->set_class( 'Operator' );
211 110         264 return $t->_finalize_token->__TOKENIZER__on_char( $t );
212             }
213              
214             # Is it a nameless arg in a signature?
215 1397 100 100     3446 if ( $char eq ')' and $t->_current_token_has_signatures_active ) {
216 2         6 $t->{class} = $t->{token}->set_class('Symbol');
217 2         6 return $t->_finalize_token->__TOKENIZER__on_char($t);
218             }
219              
220             # Is it a magic variable?
221 1395 100 100     5788 if ( $char eq '^' || $MAGIC{ $c . $char } ) {
222 240         730 $t->{class} = $t->{token}->set_class( 'Magic' );
223 240         609 return 1;
224             }
225              
226 1155 100       3124 if ( $char =~ /[\w:]/ ) {
227             # Symbol (unless the thing before it is a number
228 448         1272 my ( $prev ) = $t->_previous_significant_tokens(1);
229 448 100 100     3164 if ( not $prev or not $prev->isa('PPI::Token::Number') ) {
230 436         1342 $t->{class} = $t->{token}->set_class( 'Symbol' );
231 436         1355 return 1;
232             }
233             }
234              
235 719 100       1697 if ( $char eq '{' ) {
236             # Get rest of line
237 54         204 pos $t->{line} = $t->{line_cursor} + 1;
238 54 100       463 if ( $t->{line} =~ m/$CURLY_SYMBOL/gc ) {
239             # control-character symbol (e.g. %{^_Foo})
240 1         3 $t->{class} = $t->{token}->set_class( 'Magic' );
241 1         3 return 1;
242             }
243             }
244              
245 718 100       1744 return $self->_as_cast_or_op($t) if $self->_is_cast_or_op($char);
246              
247             # Probably the mod operator
248 552         1573 $t->{class} = $t->{token}->set_class( 'Operator' );
249 552         1630 return $t->{class}->__TOKENIZER__on_char( $t );
250              
251              
252              
253             } elsif ( $c eq '&' ) {
254             # Postfix dereference: ->&*
255 1305 100       4290 if ( $char eq '*' ) {
256 12         57 my ( $prev ) = $t->_previous_significant_tokens(1);
257 12 100 100     116 if ( $prev and $prev->isa('PPI::Token::Operator') and $prev->content eq '->' ) {
      100        
258 1         3 $t->{class} = $t->{token}->set_class( 'Cast' );
259 1         3 return 1;
260             }
261             }
262              
263             # Is it a number?
264 1304 100       3602 if ( $char =~ /\d/ ) {
265             # bitwise operator
266 103         266 $t->{class} = $t->{token}->set_class( 'Operator' );
267 103         247 return $t->_finalize_token->__TOKENIZER__on_char( $t );
268             }
269              
270 1201 100       3011 if ( $char =~ /[\w:]/ ) {
271             # Symbol (unless the thing before it is a number
272 233         724 my ( $prev ) = $t->_previous_significant_tokens(1);
273 233 100 100     1663 if ( not $prev or not $prev->isa('PPI::Token::Number') ) {
274 214         653 $t->{class} = $t->{token}->set_class( 'Symbol' );
275 214         662 return 1;
276             }
277             }
278              
279 987 100       2246 return $self->_as_cast_or_op($t) if $self->_is_cast_or_op($char);
280              
281             # Probably the binary and operator
282 874         2293 $t->{class} = $t->{token}->set_class( 'Operator' );
283 874         2642 return $t->{class}->__TOKENIZER__on_char( $t );
284              
285              
286              
287             } elsif ( $c eq '-' ) {
288 1974 100       5481 if ( $char =~ /\d/o ) {
289             # Number
290 135         423 $t->{class} = $t->{token}->set_class( 'Number' );
291 135         366 return 1;
292             }
293              
294 1839 100       3819 if ( $char eq '.' ) {
295             # Number::Float
296 15         53 $t->{class} = $t->{token}->set_class( 'Number::Float' );
297 15         40 return 1;
298             }
299              
300 1824 100       4156 if ( $char =~ /[a-zA-Z]/ ) {
301 204         672 $t->{class} = $t->{token}->set_class( 'DashedWord' );
302 204         537 return 1;
303             }
304              
305             # The numeric negative operator
306 1620         4153 $t->{class} = $t->{token}->set_class( 'Operator' );
307 1620         5061 return $t->{class}->__TOKENIZER__on_char( $t );
308              
309              
310              
311             } elsif ( $c eq ':' ) {
312 1917 100       4047 if ( $char eq ':' ) {
313             # ::foo style bareword
314 9         33 $t->{class} = $t->{token}->set_class( 'Word' );
315 9         27 return 1;
316             }
317              
318             # Now, : acts very very differently in different contexts.
319             # Mainly, we need to find out if this is a subroutine attribute.
320             # We'll leave a hint in the token to indicate that, if it is.
321 1908 100       4869 if ( $self->__TOKENIZER__is_an_attribute( $t ) ) {
322             # This : is an attribute indicator
323 926         2107 $t->{class} = $t->{token}->set_class( 'Operator' );
324 926         1684 $t->{token}->{_attribute} = 1;
325 926         1625 return $t->_finalize_token->__TOKENIZER__on_char( $t );
326             }
327              
328             # It MIGHT be a label, but it's probably the ?: trinary operator
329 982         2760 $t->{class} = $t->{token}->set_class( 'Operator' );
330 982         2654 return $t->{class}->__TOKENIZER__on_char( $t );
331             }
332              
333             # erm...
334 0         0 PPI::Exception->throw('Unknown value in PPI::Token::Unknown token');
335             }
336              
337             sub _is_cast_or_op {
338 2492     2492   4130 my ( $self, $char ) = @_;
339 2492 100       5177 return 1 if $char eq '$';
340 2341 100       3924 return 1 if $char eq '@';
341 2309 100       3402 return 1 if $char eq '%';
342 2265 100       3823 return 1 if $char eq '*';
343 2231 100       3550 return 1 if $char eq '{';
344 2119         4355 return;
345             }
346              
347             sub _as_cast_or_op {
348 373     373   783 my ( $self, $t ) = @_;
349 373         1041 my $class = _cast_or_op( $t );
350 373         1069 $t->{class} = $t->{token}->set_class( $class );
351 373         964 return $t->_finalize_token->__TOKENIZER__on_char( $t );
352             }
353              
354             sub _prev_significant_w_cursor {
355 515     515   955 my ( $tokens, $cursor, $extra_check ) = @_;
356 515         1055 while ( $cursor >= 0 ) {
357 665         992 my $token = $tokens->[ $cursor-- ];
358 665 100       1680 next if !$token->significant;
359 508 100 100     1233 next if $extra_check and !$extra_check->($token);
360 480         1070 return ( $token, $cursor );
361             }
362 35         78 return ( undef, $cursor );
363             }
364              
365             # Operator/operand-sensitive, multiple or GLOB cast
366             sub _cast_or_op {
367 373     373   680 my ( $t ) = @_;
368              
369 373         707 my $tokens = $t->{tokens};
370 373         679 my $cursor = scalar( @$tokens ) - 1;
371 373         527 my $token;
372              
373 373         932 ( $token, $cursor ) = _prev_significant_w_cursor( $tokens, $cursor );
374 373 100       1353 return 'Cast' if !$token; # token was first in the document
375              
376 353 100 100     1933 if ( $token->isa( 'PPI::Token::Structure' ) and $token->content eq '}' ) {
377              
378             # Scan the token stream backwards an arbitrarily long way,
379             # looking for the matching opening curly brace.
380 36         76 my $structure_depth = 1;
381             ( $token, $cursor ) = _prev_significant_w_cursor(
382             $tokens, $cursor,
383             sub {
384 62     62   91 my ( $token ) = @_;
385 62 100       235 return if !$token->isa( 'PPI::Token::Structure' );
386 34 50       97 if ( $token eq '}' ) {
387 0         0 $structure_depth++;
388 0         0 return;
389             }
390 34 100       53 if ( $token eq '{' ) {
391 32         35 $structure_depth--;
392 32 50       65 return if $structure_depth;
393             }
394 34         75 return 1;
395             }
396 36         191 );
397 36 100       203 return 'Operator' if !$token; # no matching '{', probably an unbalanced '}'
398              
399             # Scan past any whitespace
400 34         63 ( $token, $cursor ) = _prev_significant_w_cursor( $tokens, $cursor );
401 34 50       82 return 'Operator' if !$token; # Document began with what must be a hash constructor.
402 34 100       116 return 'Operator' if $token->isa( 'PPI::Token::Symbol' ); # subscript
403              
404 33         64 my %meth_or_subscript_end = map { $_ => 1 } qw@ -> } ] @;
  99         221  
405 33 100       85 return 'Operator' if $meth_or_subscript_end{ $token->content }; # subscript
406              
407 24         43 my $content = $token->content;
408 24   66     121 my $produces_or_wants_value =
409             ( $token->isa( 'PPI::Token::Word' ) and ( $content eq 'do' or $content eq 'eval' ) );
410 24 100       82 return $produces_or_wants_value ? 'Operator' : 'Cast';
411             }
412              
413 317         645 my %list_start_or_term_end = map { $_ => 1 } qw@ ; ( { [ @;
  1268         2672  
414             return 'Cast'
415 317 100 100     3446 if $token->isa( 'PPI::Token::Structure' ) and $list_start_or_term_end{ $token->content }
      100        
      100        
      100        
416             or $token->isa( 'PPI::Token::Cast' )
417             or $token->isa( 'PPI::Token::Operator' )
418             or $token->isa( 'PPI::Token::Label' );
419              
420 171 100       690 return 'Operator' if !$token->isa( 'PPI::Token::Word' );
421              
422 72         144 ( $token, $cursor ) = _prev_significant_w_cursor( $tokens, $cursor );
423 72 50 66     365 return 'Cast' if !$token || $token->content ne '->';
424              
425 0         0 return 'Operator';
426             }
427              
428             # Are we at a location where a ':' would indicate a subroutine attribute
429             sub __TOKENIZER__is_an_attribute {
430 1908     1908   2205 my $t = $_[1]; # Tokenizer object
431 1908         3687 my @tokens = $t->_previous_significant_tokens(3);
432 1908         2511 my $p0 = $tokens[0];
433 1908 100       4900 return '' if not $p0;
434              
435             # If we just had another attribute, we are also an attribute
436 1829 100       7255 return 1 if $p0->isa('PPI::Token::Attribute');
437              
438             # If we just had a prototype, then we are an attribute
439 1653 100       5859 return 1 if $p0->isa('PPI::Token::Prototype');
440              
441             # Other than that, we would need to have had a bareword
442 1527 100       5341 return '' unless $p0->isa('PPI::Token::Word');
443              
444             # We could be an anonymous subroutine
445 947 50 33     3149 if ( $p0->isa('PPI::Token::Word') and $p0->content eq 'sub' ) {
446 0         0 return 1;
447             }
448              
449             # Or, we could be a named subroutine
450 947         1424 my $p1 = $tokens[1];
451 947         1188 my $p2 = $tokens[2];
452 947 50 66     4161 if (
      100        
      33        
      66        
453             $p1
454             and
455             $p1->isa('PPI::Token::Word')
456             and
457             $p1->content eq 'sub'
458             and (
459             not $p2
460             or
461             $p2->isa('PPI::Token::Structure')
462             or (
463             $p2->isa('PPI::Token::Whitespace')
464             and
465             $p2->content eq ''
466             )
467             )
468             ) {
469 624         1672 return 1;
470             }
471              
472             # We aren't an attribute
473 323         822 '';
474             }
475              
476             1;
477              
478             =pod
479              
480             =head1 SUPPORT
481              
482             See the L in the main module.
483              
484             =head1 AUTHOR
485              
486             Adam Kennedy Eadamk@cpan.orgE
487              
488             =head1 COPYRIGHT
489              
490             Copyright 2001 - 2011 Adam Kennedy.
491              
492             This program is free software; you can redistribute
493             it and/or modify it under the same terms as Perl itself.
494              
495             The full text of the license can be found in the
496             LICENSE file included with this module.
497              
498             =cut