File Coverage

blib/lib/PPI/Token/Unknown.pm
Criterion Covered Total %
statement 216 223 96.8
branch 157 166 94.5
condition 89 96 92.7
subroutine 11 11 100.0
pod n/a
total 473 496 95.3


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<PPI::Token::Unknown> 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<PPI::Token::Unknown> you encounter in a L<PPI::Document>
26             object as a bug.
27              
28             =cut
29              
30 67     67   329 use strict;
  67         126  
  67         1867  
31 67     67   225 use PPI::Token ();
  67         92  
  67         844  
32 67     67   238 use PPI::Exception ();
  67         81  
  67         1133  
33 67     67   184 use PPI::Singletons qw' %MAGIC $CURLY_SYMBOL ';
  67         98  
  67         166727  
34              
35             our $VERSION = '1.284';
36              
37             our @ISA = "PPI::Token";
38              
39              
40              
41              
42              
43              
44             #####################################################################
45             # Tokenizer Methods
46              
47             sub __TOKENIZER__on_char {
48 28517     28517   44492 my ( $self, $t ) = @_; # Self and Tokenizer
49 28517         50028 my $c = $t->{token}->{content}; # Current token
50 28517         53816 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 28517 100       104336 if ( $c eq '*' ) {
    100          
    100          
    100          
    100          
    100          
    50          
54             # Is it a number?
55 1398 100       3922 if ( $char =~ /\d/ ) {
56             # bitwise operator
57 89         245 $t->{class} = $t->{token}->set_class( 'Operator' );
58 89         244 return $t->_finalize_token->__TOKENIZER__on_char( $t );
59             }
60              
61 1309 100       3888 if ( $char =~ /[\w:]/ ) {
62             # Symbol (unless the thing before it is a number
63 429         1082 my ( $prev ) = $t->_previous_significant_tokens(1);
64 429 100 100     2799 if ( not $prev or not $prev->isa('PPI::Token::Number') ) {
65 414         1181 $t->{class} = $t->{token}->set_class( 'Symbol' );
66 414         1256 return 1;
67             }
68             }
69              
70 895 100       2439 if ( $char eq '{' ) {
71             # Get rest of line
72 27         97 pos $t->{line} = $t->{line_cursor} + 1;
73 27 50       260 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 895 100       1830 if ( $char eq '*' ) {
82 38         102 my ( $prev ) = $t->_previous_significant_tokens(1);
83 38 100 100     328 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 894 100 100     3158 if ( $char eq '*' || $char eq '=' ) {
90             # Power operator '**' or mult-assign '*='
91 74         250 $t->{class} = $t->{token}->set_class( 'Operator' );
92 74         201 return 1;
93             }
94              
95 820 100       1790 return $self->_as_cast_or_op($t) if $self->_is_cast_or_op($char);
96              
97 706         2083 $t->{class} = $t->{token}->set_class( 'Operator' );
98 706         1548 return $t->_finalize_token->__TOKENIZER__on_char( $t );
99              
100              
101              
102             } elsif ( $c eq '$' ) {
103             # Postfix dereference: ->$* ->$#*
104 17565 100 100     58838 if ( $char eq '*' || $char eq '#' ) {
105 119         301 my ( $prev ) = $t->_previous_significant_tokens(1);
106 119 100 100     611 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 17563 100       70918 if ( $char =~ /[a-z_]/i ) {
113             # Symbol
114 16483         44916 $t->{class} = $t->{token}->set_class( 'Symbol' );
115 16483         43822 return 1;
116             }
117              
118             # Is it a nameless arg in a signature?
119 1080 100 100     5376 if ( $char eq ')' or $char eq '=' or $char eq ',' ) {
      100        
120 47         139 my ($has_sig) = $t->_current_token_has_signatures_active;
121 47 100       165 if ($has_sig) {
122 12         33 $t->{class} = $t->{token}->set_class('Symbol');
123 12         29 return $t->_finalize_token->__TOKENIZER__on_char($t);
124             }
125             }
126              
127 1068 100       3587 if ( $MAGIC{ $c . $char } ) {
128             # Magic variable
129 920         2868 $t->{class} = $t->{token}->set_class( 'Magic' );
130 920         2650 return 1;
131             }
132              
133 148 100       344 if ( $char eq '{' ) {
134             # Get rest of line
135 17         59 pos $t->{line} = $t->{line_cursor} + 1;
136 17 100       147 if ( $t->{line} =~ m/$CURLY_SYMBOL/gc ) {
137             # control-character symbol (e.g. ${^MATCH})
138 4         8 $t->{class} = $t->{token}->set_class( 'Magic' );
139 4         9 return 1;
140             }
141             }
142              
143             # Must be a cast
144 144         405 $t->{class} = $t->{token}->set_class( 'Cast' );
145 144         349 return $t->_finalize_token->__TOKENIZER__on_char( $t );
146              
147              
148              
149             } elsif ( $c eq '@' ) {
150             # Postfix dereference: ->@*
151 2946 100       6745 if ( $char eq '*' ) {
152 12         38 my ( $prev ) = $t->_previous_significant_tokens(1);
153 12 100 100     129 if ( $prev and $prev->isa('PPI::Token::Operator') and $prev->content eq '->' ) {
      100        
154 1         3 $t->{class} = $t->{token}->set_class( 'Cast' );
155 1         3 return 1;
156             }
157             }
158              
159 2945 100       10792 if ( $char =~ /[\w:]/ ) {
160             # Symbol
161 2088         6013 $t->{class} = $t->{token}->set_class( 'Symbol' );
162 2088         5459 return 1;
163             }
164              
165             # Is it a nameless arg in a signature?
166 857 100       2044 if ( $char eq ')' ) {
167 7         23 my ($has_sig) = $t->_current_token_has_signatures_active;
168 7 100       25 if ($has_sig) {
169 1         3 $t->{class} = $t->{token}->set_class('Symbol');
170 1         2 return $t->_finalize_token->__TOKENIZER__on_char($t);
171             }
172             }
173              
174 856 100       4608 if ( $MAGIC{ $c . $char } ) {
175             # Magic variable
176 29         120 $t->{class} = $t->{token}->set_class( 'Magic' );
177 29         97 return 1;
178             }
179              
180 827 100       1875 if ( $char eq '{' ) {
181             # Get rest of line
182 249         936 pos $t->{line} = $t->{line_cursor} + 1;
183 249 100       2551 if ( $t->{line} =~ m/$CURLY_SYMBOL/gc ) {
184             # control-character symbol (e.g. @{^_Foo})
185 1         3 $t->{class} = $t->{token}->set_class( 'Magic' );
186 1         3 return 1;
187             }
188             }
189              
190             # Must be a cast
191 826         2533 $t->{class} = $t->{token}->set_class( 'Cast' );
192 826         2243 return $t->_finalize_token->__TOKENIZER__on_char( $t );
193              
194              
195              
196             } elsif ( $c eq '%' ) {
197             # Postfix dereference: ->%* ->%[...]
198 1493 100 100     6059 if ( $char eq '*' || $char eq '[' ) {
199 29         83 my ( $prev ) = $t->_previous_significant_tokens(1);
200 29 100 100     327 if ( $prev and $prev->isa('PPI::Token::Operator') and $prev->content eq '->' ) {
      100        
201 2 100       5 if ( $char eq '*' ) {
202 1         4 $t->{class} = $t->{token}->set_class( 'Cast' );
203 1         3 return 1;
204             }
205 1 50       3 if ( $char eq '[' ) {
206 1         4 $t->{class} = $t->{token}->set_class( 'Cast' );
207 1         3 return $t->_finalize_token->__TOKENIZER__on_char( $t );
208             }
209             }
210             }
211              
212             # Is it a number?
213 1491 100       4440 if ( $char =~ /\d/ ) {
214             # bitwise operator
215 106         305 $t->{class} = $t->{token}->set_class( 'Operator' );
216 106         260 return $t->_finalize_token->__TOKENIZER__on_char( $t );
217             }
218              
219             # Is it a nameless arg in a signature?
220 1385 100       2889 if ( $char eq ')' ) {
221 10         28 my ($has_sig) = $t->_current_token_has_signatures_active;
222 10 100       39 if ($has_sig) {
223 2         7 $t->{class} = $t->{token}->set_class('Symbol');
224 2         5 return $t->_finalize_token->__TOKENIZER__on_char($t);
225             }
226             }
227              
228             # Is it a magic variable?
229 1383 100 100     5770 if ( $char eq '^' || $MAGIC{ $c . $char } ) {
230 243         749 $t->{class} = $t->{token}->set_class( 'Magic' );
231 243         716 return 1;
232             }
233              
234 1140 100       3879 if ( $char =~ /[\w:]/ ) {
235             # Symbol (unless the thing before it is a number
236 452         1196 my ( $prev ) = $t->_previous_significant_tokens(1);
237 452 100 100     3240 if ( not $prev or not $prev->isa('PPI::Token::Number') ) {
238 444         1455 $t->{class} = $t->{token}->set_class( 'Symbol' );
239 444         1377 return 1;
240             }
241             }
242              
243 696 100       1534 if ( $char eq '{' ) {
244             # Get rest of line
245 49         169 pos $t->{line} = $t->{line_cursor} + 1;
246 49 100       449 if ( $t->{line} =~ m/$CURLY_SYMBOL/gc ) {
247             # control-character symbol (e.g. %{^_Foo})
248 1         4 $t->{class} = $t->{token}->set_class( 'Magic' );
249 1         3 return 1;
250             }
251             }
252              
253 695 100       1668 return $self->_as_cast_or_op($t) if $self->_is_cast_or_op($char);
254              
255             # Probably the mod operator
256 526         1556 $t->{class} = $t->{token}->set_class( 'Operator' );
257 526         1777 return $t->{class}->__TOKENIZER__on_char( $t );
258              
259              
260              
261             } elsif ( $c eq '&' ) {
262             # Postfix dereference: ->&*
263 1240 100       2973 if ( $char eq '*' ) {
264 7         27 my ( $prev ) = $t->_previous_significant_tokens(1);
265 7 100 100     96 if ( $prev and $prev->isa('PPI::Token::Operator') and $prev->content eq '->' ) {
      100        
266 1         3 $t->{class} = $t->{token}->set_class( 'Cast' );
267 1         3 return 1;
268             }
269             }
270              
271             # Is it a number?
272 1239 100       3523 if ( $char =~ /\d/ ) {
273             # bitwise operator
274 90         305 $t->{class} = $t->{token}->set_class( 'Operator' );
275 90         200 return $t->_finalize_token->__TOKENIZER__on_char( $t );
276             }
277              
278 1149 100       3345 if ( $char =~ /[\w:]/ ) {
279             # Symbol (unless the thing before it is a number
280 191         483 my ( $prev ) = $t->_previous_significant_tokens(1);
281 191 100 100     1696 if ( not $prev or not $prev->isa('PPI::Token::Number') ) {
282 182         600 $t->{class} = $t->{token}->set_class( 'Symbol' );
283 182         512 return 1;
284             }
285             }
286              
287 967 100       2380 return $self->_as_cast_or_op($t) if $self->_is_cast_or_op($char);
288              
289             # Probably the binary and operator
290 873         2600 $t->{class} = $t->{token}->set_class( 'Operator' );
291 873         3209 return $t->{class}->__TOKENIZER__on_char( $t );
292              
293              
294              
295             } elsif ( $c eq '-' ) {
296 1967 100       6770 if ( $char =~ /\d/o ) {
297             # Number
298 128         459 $t->{class} = $t->{token}->set_class( 'Number' );
299 128         374 return 1;
300             }
301              
302 1839 100       4341 if ( $char eq '.' ) {
303             # Number::Float
304 10         32 $t->{class} = $t->{token}->set_class( 'Number::Float' );
305 10         29 return 1;
306             }
307              
308 1829 100       4792 if ( $char =~ /[a-zA-Z]/ ) {
309 204         626 $t->{class} = $t->{token}->set_class( 'DashedWord' );
310 204         957 return 1;
311             }
312              
313             # The numeric negative operator
314 1625         4272 $t->{class} = $t->{token}->set_class( 'Operator' );
315 1625         5807 return $t->{class}->__TOKENIZER__on_char( $t );
316              
317              
318              
319             } elsif ( $c eq ':' ) {
320 1908 100       3592 if ( $char eq ':' ) {
321             # ::foo style bareword
322 10         31 $t->{class} = $t->{token}->set_class( 'Word' );
323 10         28 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 1898 100       5379 if ( $self->__TOKENIZER__is_an_attribute( $t ) ) {
330             # This : is an attribute indicator
331 926         1973 $t->{class} = $t->{token}->set_class( 'Operator' );
332 926         1820 $t->{token}->{_attribute} = 1;
333 926         1650 return $t->_finalize_token->__TOKENIZER__on_char( $t );
334             }
335              
336             # It MIGHT be a label, but it's probably the ?: trinary operator
337 972         2596 $t->{class} = $t->{token}->set_class( 'Operator' );
338 972         3010 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 2482     2482   4354 my ( $self, $char ) = @_;
347 2482 100       5143 return 1 if $char eq '$';
348 2318 100       4153 return 1 if $char eq '@';
349 2277 100       3606 return 1 if $char eq '%';
350 2226 100       3918 return 1 if $char eq '*';
351 2203 100       4227 return 1 if $char eq '{';
352 2105         4667 return;
353             }
354              
355             sub _as_cast_or_op {
356 377     377   769 my ( $self, $t ) = @_;
357 377         976 my $class = _cast_or_op( $t );
358 377         1295 $t->{class} = $t->{token}->set_class( $class );
359 377         920 return $t->_finalize_token->__TOKENIZER__on_char( $t );
360             }
361              
362             sub _prev_significant_w_cursor {
363 519     519   1033 my ( $tokens, $cursor, $extra_check ) = @_;
364 519         1110 while ( $cursor >= 0 ) {
365 663         1024 my $token = $tokens->[ $cursor-- ];
366 663 100       1707 next if !$token->significant;
367 508 100 100     1312 next if $extra_check and !$extra_check->($token);
368 483         1190 return ( $token, $cursor );
369             }
370 36         84 return ( undef, $cursor );
371             }
372              
373             # Operator/operand-sensitive, multiple or GLOB cast
374             sub _cast_or_op {
375 377     377   711 my ( $t ) = @_;
376              
377 377         737 my $tokens = $t->{tokens};
378 377         637 my $cursor = scalar( @$tokens ) - 1;
379 377         493 my $token;
380              
381 377         868 ( $token, $cursor ) = _prev_significant_w_cursor( $tokens, $cursor );
382 377 100       1782 return 'Cast' if !$token; # token was first in the document
383              
384 354 100 100     1902 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 35         75 my $structure_depth = 1;
389             ( $token, $cursor ) = _prev_significant_w_cursor(
390             $tokens, $cursor,
391             sub {
392 59     59   91 my ( $token ) = @_;
393 59 100       254 return if !$token->isa( 'PPI::Token::Structure' );
394 34 50       108 if ( $token eq '}' ) {
395 0         0 $structure_depth++;
396 0         0 return;
397             }
398 34 100       71 if ( $token eq '{' ) {
399 32         39 $structure_depth--;
400 32 50       109 return if $structure_depth;
401             }
402 34         91 return 1;
403             }
404 35         206 );
405 35 100       197 return 'Operator' if !$token; # no matching '{', probably an unbalanced '}'
406              
407             # Scan past any whitespace
408 34         71 ( $token, $cursor ) = _prev_significant_w_cursor( $tokens, $cursor );
409 34 100       101 return 'Operator' if !$token; # Document began with what must be a hash constructor.
410 33 50       131 return 'Operator' if $token->isa( 'PPI::Token::Symbol' ); # subscript
411              
412 33         65 my %meth_or_subscript_end = map { $_ => 1 } qw@ -> } ] @;
  99         244  
413 33 100       93 return 'Operator' if $meth_or_subscript_end{ $token->content }; # subscript
414              
415 24         44 my $content = $token->content;
416 24   100     127 my $produces_or_wants_value =
417             ( $token->isa( 'PPI::Token::Word' ) and ( $content eq 'do' or $content eq 'eval' ) );
418 24 100       80 return $produces_or_wants_value ? 'Operator' : 'Cast';
419             }
420              
421 319         658 my %list_start_or_term_end = map { $_ => 1 } qw@ ; ( { [ @;
  1276         2675  
422             return 'Cast'
423 319 100 100     3420 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 165 100       726 return 'Operator' if !$token->isa( 'PPI::Token::Word' );
429              
430 73         156 ( $token, $cursor ) = _prev_significant_w_cursor( $tokens, $cursor );
431 73 50 66     334 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 1898     1898   2178 my $t = $_[1]; # Tokenizer object
439 1898         3953 my @tokens = $t->_previous_significant_tokens(3);
440 1898         2793 my $p0 = $tokens[0];
441 1898 100       4745 return '' if not $p0;
442              
443             # If we just had another attribute, we are also an attribute
444 1823 100       7352 return 1 if $p0->isa('PPI::Token::Attribute');
445              
446             # If we just had a prototype, then we are an attribute
447 1647 100       5982 return 1 if $p0->isa('PPI::Token::Prototype');
448              
449             # Other than that, we would need to have had a bareword
450 1521 100       5672 return '' unless $p0->isa('PPI::Token::Word');
451              
452             # We could be an anonymous subroutine
453 956 50 33     3319 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         1354 my $p1 = $tokens[1];
459 956         1282 my $p2 = $tokens[2];
460 956 50 66     4152 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         1624 return 1;
478             }
479              
480             # We aren't an attribute
481 332         876 '';
482             }
483              
484             1;
485              
486             =pod
487              
488             =head1 SUPPORT
489              
490             See the L<support section|PPI/SUPPORT> in the main module.
491              
492             =head1 AUTHOR
493              
494             Adam Kennedy E<lt>adamk@cpan.orgE<gt>
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