File Coverage

blib/lib/PPI/Token/Unknown.pm
Criterion Covered Total %
statement 216 223 96.8
branch 156 166 93.9
condition 89 96 92.7
subroutine 11 11 100.0
pod n/a
total 472 496 95.1


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 67     67   499 use strict;
  67         134  
  67         2473  
31 67     67   354 use PPI::Token ();
  67         123  
  67         1389  
32 67     67   519 use PPI::Exception ();
  67         138  
  67         1704  
33 67     67   326 use PPI::Singletons qw' %MAGIC $CURLY_SYMBOL ';
  67         112  
  67         238329  
34              
35             our $VERSION = '1.28401'; # TRIAL
36              
37             our @ISA = "PPI::Token";
38              
39              
40              
41              
42              
43              
44             #####################################################################
45             # Tokenizer Methods
46              
47             sub __TOKENIZER__on_char {
48 28683     28683   69497 my ( $self, $t ) = @_; # Self and Tokenizer
49 28683         91193 my $c = $t->{token}->{content}; # Current token
50 28683         87046 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 28683 100       140964 if ( $c eq '*' ) {
    100          
    100          
    100          
    100          
    100          
    50          
54             # Is it a number?
55 1385 100       7121 if ( $char =~ /\d/ ) {
56             # bitwise operator
57 97         443 $t->{class} = $t->{token}->set_class( 'Operator' );
58 97         383 return $t->_finalize_token->__TOKENIZER__on_char( $t );
59             }
60              
61 1288 100       6199 if ( $char =~ /[\w:]/ ) {
62             # Symbol (unless the thing before it is a number
63 428         2072 my ( $prev ) = $t->_previous_significant_tokens(1);
64 428 100 100     5535 if ( not $prev or not $prev->isa('PPI::Token::Number') ) {
65 408         2345 $t->{class} = $t->{token}->set_class( 'Symbol' );
66 408         2013 return 1;
67             }
68             }
69              
70 880 100       3092 if ( $char eq '{' ) {
71             # Get rest of line
72 34         152 pos $t->{line} = $t->{line_cursor} + 1;
73 34 50       397 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 880 100       2799 if ( $char eq '*' ) {
82 36         170 my ( $prev ) = $t->_previous_significant_tokens(1);
83 36 100 100     498 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 879 100 100     4662 if ( $char eq '*' || $char eq '=' ) {
90             # Power operator '**' or mult-assign '*='
91 70         397 $t->{class} = $t->{token}->set_class( 'Operator' );
92 70         342 return 1;
93             }
94              
95 809 100       2998 return $self->_as_cast_or_op($t) if $self->_is_cast_or_op($char);
96              
97 708         3086 $t->{class} = $t->{token}->set_class( 'Operator' );
98 708         2582 return $t->_finalize_token->__TOKENIZER__on_char( $t );
99              
100              
101              
102             } elsif ( $c eq '$' ) {
103             # Postfix dereference: ->$* ->$#*
104 17607 100 100     78481 if ( $char eq '*' || $char eq '#' ) {
105 125         603 my ( $prev ) = $t->_previous_significant_tokens(1);
106 125 100 100     1063 if ( $prev and $prev->isa('PPI::Token::Operator') and $prev->content eq '->' ) {
      100        
107 2         6 $t->{class} = $t->{token}->set_class( 'Cast' );
108 2         6 return 1;
109             }
110             }
111              
112 17605 100       96963 if ( $char =~ /[a-z_]/i ) {
113             # Symbol
114 16546         59717 $t->{class} = $t->{token}->set_class( 'Symbol' );
115 16546         79955 return 1;
116             }
117              
118             # Is it a nameless arg in a signature?
119 1059 100 100     7498 if ( $char eq ')' or $char eq '=' or $char eq ',' ) {
      100        
120 41         147 my $has_sig = $t->_current_token_has_signatures_active;
121 41 100       137 if ($has_sig) {
122 12         48 $t->{class} = $t->{token}->set_class('Symbol');
123 12         44 return $t->_finalize_token->__TOKENIZER__on_char($t);
124             }
125             }
126              
127 1047 100       5050 if ( $MAGIC{ $c . $char } ) {
128             # Magic variable
129 896         3723 $t->{class} = $t->{token}->set_class( 'Magic' );
130 896         3746 return 1;
131             }
132              
133 151 100       458 if ( $char eq '{' ) {
134             # Get rest of line
135 23         108 pos $t->{line} = $t->{line_cursor} + 1;
136 23 100       248 if ( $t->{line} =~ m/$CURLY_SYMBOL/gc ) {
137             # control-character symbol (e.g. ${^MATCH})
138 4         17 $t->{class} = $t->{token}->set_class( 'Magic' );
139 4         17 return 1;
140             }
141             }
142              
143             # Must be a cast
144 147         633 $t->{class} = $t->{token}->set_class( 'Cast' );
145 147         518 return $t->_finalize_token->__TOKENIZER__on_char( $t );
146              
147              
148              
149             } elsif ( $c eq '@' ) {
150             # Postfix dereference: ->@*
151 2977 100       9416 if ( $char eq '*' ) {
152 11         44 my ( $prev ) = $t->_previous_significant_tokens(1);
153 11 100 100     130 if ( $prev and $prev->isa('PPI::Token::Operator') and $prev->content eq '->' ) {
      66        
154 1         4 $t->{class} = $t->{token}->set_class( 'Cast' );
155 1         3 return 1;
156             }
157             }
158              
159 2976 100       16062 if ( $char =~ /[\w:]/ ) {
160             # Symbol
161 2088         9938 $t->{class} = $t->{token}->set_class( 'Symbol' );
162 2088         8604 return 1;
163             }
164              
165             # Is it a nameless arg in a signature?
166 888 100       3150 if ( $char eq ')' ) {
167 8         30 my $has_sig = $t->_current_token_has_signatures_active;
168 8 100       32 if ($has_sig) {
169 1         3 $t->{class} = $t->{token}->set_class('Symbol');
170 1         3 return $t->_finalize_token->__TOKENIZER__on_char($t);
171             }
172             }
173              
174 887 100       3760 if ( $MAGIC{ $c . $char } ) {
175             # Magic variable
176 34         187 $t->{class} = $t->{token}->set_class( 'Magic' );
177 34         147 return 1;
178             }
179              
180 853 100       3142 if ( $char eq '{' ) {
181             # Get rest of line
182 266         1320 pos $t->{line} = $t->{line_cursor} + 1;
183 266 100       3180 if ( $t->{line} =~ m/$CURLY_SYMBOL/gc ) {
184             # control-character symbol (e.g. @{^_Foo})
185 1         6 $t->{class} = $t->{token}->set_class( 'Magic' );
186 1         5 return 1;
187             }
188             }
189              
190             # Must be a cast
191 852         3545 $t->{class} = $t->{token}->set_class( 'Cast' );
192 852         3295 return $t->_finalize_token->__TOKENIZER__on_char( $t );
193              
194              
195              
196             } elsif ( $c eq '%' ) {
197             # Postfix dereference: ->%* ->%[...]
198 1502 100 100     8891 if ( $char eq '*' || $char eq '[' ) {
199 33         157 my ( $prev ) = $t->_previous_significant_tokens(1);
200 33 100 100     475 if ( $prev and $prev->isa('PPI::Token::Operator') and $prev->content eq '->' ) {
      100        
201 2 100       11 if ( $char eq '*' ) {
202 1         5 $t->{class} = $t->{token}->set_class( 'Cast' );
203 1         6 return 1;
204             }
205 1 50       3 if ( $char eq '[' ) {
206 1         4 $t->{class} = $t->{token}->set_class( 'Cast' );
207 1         4 return $t->_finalize_token->__TOKENIZER__on_char( $t );
208             }
209             }
210             }
211              
212             # Is it a number?
213 1500 100       7285 if ( $char =~ /\d/ ) {
214             # bitwise operator
215 109         507 $t->{class} = $t->{token}->set_class( 'Operator' );
216 109         397 return $t->_finalize_token->__TOKENIZER__on_char( $t );
217             }
218              
219             # Is it a nameless arg in a signature?
220 1391 100       4491 if ( $char eq ')' ) {
221 15         62 my $has_sig = $t->_current_token_has_signatures_active;
222 15 100       49 if ($has_sig) {
223 2         8 $t->{class} = $t->{token}->set_class('Symbol');
224 2         6 return $t->_finalize_token->__TOKENIZER__on_char($t);
225             }
226             }
227              
228             # Is it a magic variable?
229 1389 100 100     9589 if ( $char eq '^' || $MAGIC{ $c . $char } ) {
230 246         1561 $t->{class} = $t->{token}->set_class( 'Magic' );
231 246         1270 return 1;
232             }
233              
234 1143 100       5047 if ( $char =~ /[\w:]/ ) {
235             # Symbol (unless the thing before it is a number
236 420         1772 my ( $prev ) = $t->_previous_significant_tokens(1);
237 420 100 100     4344 if ( not $prev or not $prev->isa('PPI::Token::Number') ) {
238 411         1939 $t->{class} = $t->{token}->set_class( 'Symbol' );
239 411         1834 return 1;
240             }
241             }
242              
243 732 100       2415 if ( $char eq '{' ) {
244             # Get rest of line
245 49         319 pos $t->{line} = $t->{line_cursor} + 1;
246 49 100       631 if ( $t->{line} =~ m/$CURLY_SYMBOL/gc ) {
247             # control-character symbol (e.g. %{^_Foo})
248 1         7 $t->{class} = $t->{token}->set_class( 'Magic' );
249 1         5 return 1;
250             }
251             }
252              
253 731 100       2687 return $self->_as_cast_or_op($t) if $self->_is_cast_or_op($char);
254              
255             # Probably the mod operator
256 563         2413 $t->{class} = $t->{token}->set_class( 'Operator' );
257 563         2757 return $t->{class}->__TOKENIZER__on_char( $t );
258              
259              
260              
261             } elsif ( $c eq '&' ) {
262             # Postfix dereference: ->&*
263 1345 100       5430 if ( $char eq '*' ) {
264 15         77 my ( $prev ) = $t->_previous_significant_tokens(1);
265 15 100 100     456 if ( $prev and $prev->isa('PPI::Token::Operator') and $prev->content eq '->' ) {
      100        
266 1         4 $t->{class} = $t->{token}->set_class( 'Cast' );
267 1         3 return 1;
268             }
269             }
270              
271             # Is it a number?
272 1344 100       6395 if ( $char =~ /\d/ ) {
273             # bitwise operator
274 112         489 $t->{class} = $t->{token}->set_class( 'Operator' );
275 112         525 return $t->_finalize_token->__TOKENIZER__on_char( $t );
276             }
277              
278 1232 100       7010 if ( $char =~ /[\w:]/ ) {
279             # Symbol (unless the thing before it is a number
280 228         1020 my ( $prev ) = $t->_previous_significant_tokens(1);
281 228 100 100     2524 if ( not $prev or not $prev->isa('PPI::Token::Number') ) {
282 217         987 $t->{class} = $t->{token}->set_class( 'Symbol' );
283 217         905 return 1;
284             }
285             }
286              
287 1015 100       4237 return $self->_as_cast_or_op($t) if $self->_is_cast_or_op($char);
288              
289             # Probably the binary and operator
290 906         4352 $t->{class} = $t->{token}->set_class( 'Operator' );
291 906         5074 return $t->{class}->__TOKENIZER__on_char( $t );
292              
293              
294              
295             } elsif ( $c eq '-' ) {
296 1971 100       8897 if ( $char =~ /\d/o ) {
297             # Number
298 139         654 $t->{class} = $t->{token}->set_class( 'Number' );
299 139         917 return 1;
300             }
301              
302 1832 100       5607 if ( $char eq '.' ) {
303             # Number::Float
304 9         48 $t->{class} = $t->{token}->set_class( 'Number::Float' );
305 9         40 return 1;
306             }
307              
308 1823 100       6874 if ( $char =~ /[a-zA-Z]/ ) {
309 210         1054 $t->{class} = $t->{token}->set_class( 'DashedWord' );
310 210         951 return 1;
311             }
312              
313             # The numeric negative operator
314 1613         6725 $t->{class} = $t->{token}->set_class( 'Operator' );
315 1613         7038 return $t->{class}->__TOKENIZER__on_char( $t );
316              
317              
318              
319             } elsif ( $c eq ':' ) {
320 1896 100       9329 if ( $char eq ':' ) {
321             # ::foo style bareword
322 11         59 $t->{class} = $t->{token}->set_class( 'Word' );
323 11         43 return 1;
324             }
325              
326             # Now, : acts very very differently in different contexts.
327             # Mainly, we need to find out if this is a subroutine attribute.
328             # We'll leave a hint in the token to indicate that, if it is.
329 1885 100       8755 if ( $self->__TOKENIZER__is_an_attribute( $t ) ) {
330             # This : is an attribute indicator
331 926         4455 $t->{class} = $t->{token}->set_class( 'Operator' );
332 926         3109 $t->{token}->{_attribute} = 1;
333 926         3418 return $t->_finalize_token->__TOKENIZER__on_char( $t );
334             }
335              
336             # It MIGHT be a label, but it's probably the ?: trinary operator
337 959         3977 $t->{class} = $t->{token}->set_class( 'Operator' );
338 959         4810 return $t->{class}->__TOKENIZER__on_char( $t );
339             }
340              
341             # erm...
342 0         0 PPI::Exception->throw('Unknown value in PPI::Token::Unknown token');
343             }
344              
345             sub _is_cast_or_op {
346 2555     2555   6863 my ( $self, $char ) = @_;
347 2555 100       8638 return 1 if $char eq '$';
348 2391 100       7280 return 1 if $char eq '@';
349 2357 100       6123 return 1 if $char eq '%';
350 2324 100       6198 return 1 if $char eq '*';
351 2291 100       10270 return 1 if $char eq '{';
352 2177         7565 return;
353             }
354              
355             sub _as_cast_or_op {
356 378     378   1049 my ( $self, $t ) = @_;
357 378         1462 my $class = _cast_or_op( $t );
358 378         1825 $t->{class} = $t->{token}->set_class( $class );
359 378         34513 return $t->_finalize_token->__TOKENIZER__on_char( $t );
360             }
361              
362             sub _prev_significant_w_cursor {
363 525     525   1238 my ( $tokens, $cursor, $extra_check ) = @_;
364 525         1570 while ( $cursor >= 0 ) {
365 687         1298 my $token = $tokens->[ $cursor-- ];
366 687 100       2632 next if !$token->significant;
367 522 100 100     1724 next if $extra_check and !$extra_check->($token);
368 488         1613 return ( $token, $cursor );
369             }
370 37         134 return ( undef, $cursor );
371             }
372              
373             # Operator/operand-sensitive, multiple or GLOB cast
374             sub _cast_or_op {
375 378     378   937 my ( $t ) = @_;
376              
377 378         958 my $tokens = $t->{tokens};
378 378         994 my $cursor = scalar( @$tokens ) - 1;
379 378         640 my $token;
380              
381 378         1428 ( $token, $cursor ) = _prev_significant_w_cursor( $tokens, $cursor );
382 378 100       1848 return 'Cast' if !$token; # token was first in the document
383              
384 354 100 100     2947 if ( $token->isa( 'PPI::Token::Structure' ) and $token->content eq '}' ) {
385              
386             # Scan the token stream backwards an arbitrarily long way,
387             # looking for the matching opening curly brace.
388 37         88 my $structure_depth = 1;
389             ( $token, $cursor ) = _prev_significant_w_cursor(
390             $tokens, $cursor,
391             sub {
392 69     69   114 my ( $token ) = @_;
393 69 100       409 return if !$token->isa( 'PPI::Token::Structure' );
394 35 50       148 if ( $token eq '}' ) {
395 0         0 $structure_depth++;
396 0         0 return;
397             }
398 35 100       90 if ( $token eq '{' ) {
399 32         53 $structure_depth--;
400 32 50       83 return if $structure_depth;
401             }
402 35         148 return 1;
403             }
404 37         292 );
405 37 100       288 return 'Operator' if !$token; # no matching '{', probably an unbalanced '}'
406              
407             # Scan past any whitespace
408 35         245 ( $token, $cursor ) = _prev_significant_w_cursor( $tokens, $cursor );
409 35 50       114 return 'Operator' if !$token; # Document began with what must be a hash constructor.
410 35 50       234 return 'Operator' if $token->isa( 'PPI::Token::Symbol' ); # subscript
411              
412 35         86 my %meth_or_subscript_end = map { $_ => 1 } qw@ -> } ] @;
  105         310  
413 35 100       133 return 'Operator' if $meth_or_subscript_end{ $token->content }; # subscript
414              
415 26         69 my $content = $token->content;
416 26   100     228 my $produces_or_wants_value =
417             ( $token->isa( 'PPI::Token::Word' ) and ( $content eq 'do' or $content eq 'eval' ) );
418 26 100       136 return $produces_or_wants_value ? 'Operator' : 'Cast';
419             }
420              
421 317         1002 my %list_start_or_term_end = map { $_ => 1 } qw@ ; ( { [ @;
  1268         3799  
422             return 'Cast'
423 317 100 100     5015 if $token->isa( 'PPI::Token::Structure' ) and $list_start_or_term_end{ $token->content }
      100        
      100        
      100        
424             or $token->isa( 'PPI::Token::Cast' )
425             or $token->isa( 'PPI::Token::Operator' )
426             or $token->isa( 'PPI::Token::Label' );
427              
428 177 100       971 return 'Operator' if !$token->isa( 'PPI::Token::Word' );
429              
430 75         208 ( $token, $cursor ) = _prev_significant_w_cursor( $tokens, $cursor );
431 75 50 66     565 return 'Cast' if !$token || $token->content ne '->';
432              
433 0         0 return 'Operator';
434             }
435              
436             # Are we at a location where a ':' would indicate a subroutine attribute
437             sub __TOKENIZER__is_an_attribute {
438 1885     1885   4099 my $t = $_[1]; # Tokenizer object
439 1885         6349 my @tokens = $t->_previous_significant_tokens(3);
440 1885         4604 my $p0 = $tokens[0];
441 1885 100       16322 return '' if not $p0;
442              
443             # If we just had another attribute, we are also an attribute
444 1813 100       12365 return 1 if $p0->isa('PPI::Token::Attribute');
445              
446             # If we just had a prototype, then we are an attribute
447 1637 100       10422 return 1 if $p0->isa('PPI::Token::Prototype');
448              
449             # Other than that, we would need to have had a bareword
450 1511 100       7713 return '' unless $p0->isa('PPI::Token::Word');
451              
452             # We could be an anonymous subroutine
453 956 50 33     6439 if ( $p0->isa('PPI::Token::Word') and $p0->content eq 'sub' ) {
454 0         0 return 1;
455             }
456              
457             # Or, we could be a named subroutine
458 956         2481 my $p1 = $tokens[1];
459 956         1881 my $p2 = $tokens[2];
460 956 50 100     8282 if (
      100        
      33        
      66        
461             $p1
462             and
463             $p1->isa('PPI::Token::Word')
464             and
465             $p1->content eq 'sub'
466             and (
467             not $p2
468             or
469             $p2->isa('PPI::Token::Structure')
470             or (
471             $p2->isa('PPI::Token::Whitespace')
472             and
473             $p2->content eq ''
474             )
475             )
476             ) {
477 624         2972 return 1;
478             }
479              
480             # We aren't an attribute
481 332         1449 '';
482             }
483              
484             1;
485              
486             =pod
487              
488             =head1 SUPPORT
489              
490             See the L in the main module.
491              
492             =head1 AUTHOR
493              
494             Adam Kennedy Eadamk@cpan.orgE
495              
496             =head1 COPYRIGHT
497              
498             Copyright 2001 - 2011 Adam Kennedy.
499              
500             This program is free software; you can redistribute
501             it and/or modify it under the same terms as Perl itself.
502              
503             The full text of the license can be found in the
504             LICENSE file included with this module.
505              
506             =cut