File Coverage

blib/lib/PPI/Token/Unknown.pm
Criterion Covered Total %
statement 211 218 96.7
branch 150 160 93.7
condition 93 99 93.9
subroutine 11 11 100.0
pod n/a
total 465 488 95.2


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 69     69   332 use strict;
  69         93  
  69         1931  
31 69     69   238 use PPI::Token ();
  69         81  
  69         863  
32 69     69   212 use PPI::Exception ();
  69         173  
  69         1177  
33 69     69   218 use PPI::Singletons qw' %MAGIC $CURLY_SYMBOL ';
  69         100  
  69         162115  
34              
35             our $VERSION = '1.291';
36              
37             our @ISA = "PPI::Token";
38              
39              
40              
41              
42              
43              
44             #####################################################################
45             # Tokenizer Methods
46              
47             sub __TOKENIZER__on_char {
48 36394     36394   54383 my ( $self, $t ) = @_; # Self and Tokenizer
49 36394         59597 my $c = $t->{token}->{content}; # Current token
50 36394         62199 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 36394 100       92271 if ( $c eq '*' ) {
    100          
    100          
    100          
    100          
    100          
    50          
54             # Is it a number?
55 1354 100       3534 if ( $char =~ /\d/ ) {
56             # bitwise operator
57 82         252 $t->{class} = $t->{token}->set_class( 'Operator' );
58 82         177 return $t->_finalize_token->__TOKENIZER__on_char( $t );
59             }
60              
61 1272 100       3295 if ( $char =~ /[\w:]/ ) {
62             # Symbol (unless the thing before it is a number
63 417         896 my ( $prev ) = $t->_previous_significant_tokens(1);
64 417 100 100     2679 if ( not $prev or not $prev->isa('PPI::Token::Number') ) {
65 400         1092 $t->{class} = $t->{token}->set_class( 'Symbol' );
66 400         1049 return 1;
67             }
68             }
69              
70 872 100       1666 if ( $char eq '{' ) {
71             # Get rest of line
72 24         106 pos $t->{line} = $t->{line_cursor} + 1;
73 24 50       213 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 872 100       1558 if ( $char eq '*' ) {
82 36         117 my ( $prev ) = $t->_previous_significant_tokens(1);
83 36 100 100     305 if ( $prev and $prev->isa('PPI::Token::Operator') and $prev->content eq '->' ) {
      100        
84 1         3 $t->{class} = $t->{token}->set_class( 'Cast' );
85 1         4 return 1;
86             }
87             }
88              
89 871 100 100     2763 if ( $char eq '*' || $char eq '=' ) {
90             # Power operator '**' or mult-assign '*='
91 68         203 $t->{class} = $t->{token}->set_class( 'Operator' );
92 68         200 return 1;
93             }
94              
95 803 100       1600 return $self->_as_cast_or_op($t) if $self->_is_cast_or_op($char);
96              
97 707         1767 $t->{class} = $t->{token}->set_class( 'Operator' );
98 707         1325 return $t->_finalize_token->__TOKENIZER__on_char( $t );
99              
100              
101              
102             } elsif ( $c eq '$' ) {
103             # Postfix dereference: ->$* ->$#*
104 25369 100 100     64745 if ( $char eq '*' || $char eq '#' ) {
105 130         320 my ( $prev ) = $t->_previous_significant_tokens(1);
106 130 100 100     665 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 25367 100       83557 if ( $char =~ /[a-z_]/i ) {
113             # Symbol
114 24239         51081 $t->{class} = $t->{token}->set_class( 'Symbol' );
115 24239         55209 return 1;
116             }
117              
118             # Is it a nameless arg in a signature?
119 1128         3899 my %noname = ( ')' => 1, '=' => 1, ',' => 1 );
120 1128 100 100     3181 if ( $noname{$char} and $t->_current_token_has_signatures_active ) {
121 12         55 $t->{class} = $t->{token}->set_class('Symbol');
122 12         30 return $t->_finalize_token->__TOKENIZER__on_char($t);
123             }
124              
125 1116 100       3167 if ( $MAGIC{ $c . $char } ) {
126             # Magic variable
127 939         2435 $t->{class} = $t->{token}->set_class( 'Magic' );
128 939         3009 return 1;
129             }
130              
131 177 100       386 if ( $char eq '{' ) {
132             # Get rest of line
133 26         92 pos $t->{line} = $t->{line_cursor} + 1;
134 26 100       196 if ( $t->{line} =~ m/$CURLY_SYMBOL/gc ) {
135             # control-character symbol (e.g. ${^MATCH})
136 4         10 $t->{class} = $t->{token}->set_class( 'Magic' );
137 4         10 return 1;
138             }
139             }
140              
141             # Must be a cast
142 173         577 $t->{class} = $t->{token}->set_class( 'Cast' );
143 173         387 return $t->_finalize_token->__TOKENIZER__on_char( $t );
144              
145              
146              
147             } elsif ( $c eq '@' ) {
148             # Postfix dereference: ->@*
149 2957 100       5611 if ( $char eq '*' ) {
150 10         28 my ( $prev ) = $t->_previous_significant_tokens(1);
151 10 100 100     124 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 2956 100       9551 if ( $char =~ /[\w:]/ ) {
158             # Symbol
159 2079         5083 $t->{class} = $t->{token}->set_class( 'Symbol' );
160 2079         5399 return 1;
161             }
162              
163             # Is it a nameless arg in a signature?
164 877 100 100     2336 if ( $char eq ')' and $t->_current_token_has_signatures_active ) {
165 1         5 $t->{class} = $t->{token}->set_class('Symbol');
166 1         4 return $t->_finalize_token->__TOKENIZER__on_char($t);
167             }
168              
169 876 100       2764 if ( $MAGIC{ $c . $char } ) {
170             # Magic variable
171 34         128 $t->{class} = $t->{token}->set_class( 'Magic' );
172 34         97 return 1;
173             }
174              
175 842 100       1723 if ( $char eq '{' ) {
176             # Get rest of line
177 274         857 pos $t->{line} = $t->{line_cursor} + 1;
178 274 100       2005 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         2 return 1;
182             }
183             }
184              
185             # Must be a cast
186 841         2184 $t->{class} = $t->{token}->set_class( 'Cast' );
187 841         1989 return $t->_finalize_token->__TOKENIZER__on_char( $t );
188              
189              
190              
191             } elsif ( $c eq '%' ) {
192             # Postfix dereference: ->%* ->%[...]
193 1477 100 100     4835 if ( $char eq '*' || $char eq '[' ) {
194 40         114 my ( $prev ) = $t->_previous_significant_tokens(1);
195 40 100 100     368 if ( $prev and $prev->isa('PPI::Token::Operator') and $prev->content eq '->' ) {
      100        
196 2 100       5 if ( $char eq '*' ) {
197 1         3 $t->{class} = $t->{token}->set_class( 'Cast' );
198 1         3 return 1;
199             }
200 1 50       4 if ( $char eq '[' ) {
201 1         4 $t->{class} = $t->{token}->set_class( 'Cast' );
202 1         4 return $t->_finalize_token->__TOKENIZER__on_char( $t );
203             }
204             }
205             }
206              
207             # Is it a number?
208 1475 100       3829 if ( $char =~ /\d/ ) {
209             # bitwise operator
210 99         250 $t->{class} = $t->{token}->set_class( 'Operator' );
211 99         218 return $t->_finalize_token->__TOKENIZER__on_char( $t );
212             }
213              
214             # Is it a nameless arg in a signature?
215 1376 100 100     2986 if ( $char eq ')' and $t->_current_token_has_signatures_active ) {
216 2         10 $t->{class} = $t->{token}->set_class('Symbol');
217 2         7 return $t->_finalize_token->__TOKENIZER__on_char($t);
218             }
219              
220             # Is it a magic variable?
221 1374 100 100     5542 if ( $char eq '^' || $MAGIC{ $c . $char } ) {
222 229         608 $t->{class} = $t->{token}->set_class( 'Magic' );
223 229         560 return 1;
224             }
225              
226 1145 100       3108 if ( $char =~ /[\w:]/ ) {
227             # Symbol (unless the thing before it is a number
228 444         1103 my ( $prev ) = $t->_previous_significant_tokens(1);
229 444 100 100     2929 if ( not $prev or not $prev->isa('PPI::Token::Number') ) {
230 432         1215 $t->{class} = $t->{token}->set_class( 'Symbol' );
231 432         1203 return 1;
232             }
233             }
234              
235 713 100       1363 if ( $char eq '{' ) {
236             # Get rest of line
237 55         235 pos $t->{line} = $t->{line_cursor} + 1;
238 55 100       460 if ( $t->{line} =~ m/$CURLY_SYMBOL/gc ) {
239             # control-character symbol (e.g. %{^_Foo})
240 1         4 $t->{class} = $t->{token}->set_class( 'Magic' );
241 1         3 return 1;
242             }
243             }
244              
245 712 100       1488 return $self->_as_cast_or_op($t) if $self->_is_cast_or_op($char);
246              
247             # Probably the mod operator
248 543         1456 $t->{class} = $t->{token}->set_class( 'Operator' );
249 543         1549 return $t->{class}->__TOKENIZER__on_char( $t );
250              
251              
252              
253             } elsif ( $c eq '&' ) {
254             # Postfix dereference: ->&*
255 1329 100       2699 if ( $char eq '*' ) {
256 13         48 my ( $prev ) = $t->_previous_significant_tokens(1);
257 13 100 100     117 if ( $prev and $prev->isa('PPI::Token::Operator') and $prev->content eq '->' ) {
      100        
258 1         4 $t->{class} = $t->{token}->set_class( 'Cast' );
259 1         3 return 1;
260             }
261             }
262              
263             # Is it a number?
264 1328 100       3557 if ( $char =~ /\d/ ) {
265             # bitwise operator
266 105         300 $t->{class} = $t->{token}->set_class( 'Operator' );
267 105         251 return $t->_finalize_token->__TOKENIZER__on_char( $t );
268             }
269              
270 1223 100       2907 if ( $char =~ /[\w:]/ ) {
271             # Symbol (unless the thing before it is a number
272 220         715 my ( $prev ) = $t->_previous_significant_tokens(1);
273 220 100 100     1663 if ( not $prev or not $prev->isa('PPI::Token::Number') ) {
274 207         597 $t->{class} = $t->{token}->set_class( 'Symbol' );
275 207         554 return 1;
276             }
277             }
278              
279 1016 100       2057 return $self->_as_cast_or_op($t) if $self->_is_cast_or_op($char);
280              
281             # Probably the binary and operator
282 910         2392 $t->{class} = $t->{token}->set_class( 'Operator' );
283 910         2458 return $t->{class}->__TOKENIZER__on_char( $t );
284              
285              
286              
287             } elsif ( $c eq '-' ) {
288 2022 100       5187 if ( $char =~ /\d/o ) {
289             # Number
290 149         478 $t->{class} = $t->{token}->set_class( 'Number' );
291 149         448 return 1;
292             }
293              
294 1873 100       3697 if ( $char eq '.' ) {
295             # Number::Float
296 6         21 $t->{class} = $t->{token}->set_class( 'Number::Float' );
297 6         17 return 1;
298             }
299              
300 1867 100       4343 if ( $char =~ /[a-zA-Z]/ ) {
301 224         667 $t->{class} = $t->{token}->set_class( 'DashedWord' );
302 224         635 return 1;
303             }
304              
305             # The numeric negative operator
306 1643         3745 $t->{class} = $t->{token}->set_class( 'Operator' );
307 1643         4310 return $t->{class}->__TOKENIZER__on_char( $t );
308              
309              
310              
311             } elsif ( $c eq ':' ) {
312 1886 100       3518 if ( $char eq ':' ) {
313             # ::foo style bareword
314 5         38 $t->{class} = $t->{token}->set_class( 'Word' );
315 5         14 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 1881 100       4464 if ( $self->__TOKENIZER__is_an_attribute( $t ) ) {
322             # This : is an attribute indicator
323 926         2044 $t->{class} = $t->{token}->set_class( 'Operator' );
324 926         1719 $t->{token}->{_attribute} = 1;
325 926         1557 return $t->_finalize_token->__TOKENIZER__on_char( $t );
326             }
327              
328             # It MIGHT be a label, but it's probably the ?: trinary operator
329 955         2563 $t->{class} = $t->{token}->set_class( 'Operator' );
330 955         3033 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 2531     2531   3738 my ( $self, $char ) = @_;
339 2531 100       4646 return 1 if $char eq '$';
340 2374 100       3577 return 1 if $char eq '@';
341 2332 100       3591 return 1 if $char eq '%';
342 2309 100       3867 return 1 if $char eq '*';
343 2272 100       3791 return 1 if $char eq '{';
344 2160         4560 return;
345             }
346              
347             sub _as_cast_or_op {
348 371     371   738 my ( $self, $t ) = @_;
349 371         952 my $class = _cast_or_op( $t );
350 371         1101 $t->{class} = $t->{token}->set_class( $class );
351 371         798 return $t->_finalize_token->__TOKENIZER__on_char( $t );
352             }
353              
354             sub _prev_significant_w_cursor {
355 525     525   983 my ( $tokens, $cursor, $extra_check ) = @_;
356 525         967 while ( $cursor >= 0 ) {
357 684         910 my $token = $tokens->[ $cursor-- ];
358 684 100       1783 next if !$token->significant;
359 523 100 100     1218 next if $extra_check and !$extra_check->($token);
360 486         1092 return ( $token, $cursor );
361             }
362 39         83 return ( undef, $cursor );
363             }
364              
365             # Operator/operand-sensitive, multiple or GLOB cast
366             sub _cast_or_op {
367 371     371   664 my ( $t ) = @_;
368              
369 371         701 my $tokens = $t->{tokens};
370 371         673 my $cursor = scalar( @$tokens ) - 1;
371 371         392 my $token;
372              
373 371         830 ( $token, $cursor ) = _prev_significant_w_cursor( $tokens, $cursor );
374 371 100       1319 return 'Cast' if !$token; # token was first in the document
375              
376 347 100 100     1758 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 38         72 my $structure_depth = 1;
381             ( $token, $cursor ) = _prev_significant_w_cursor(
382             $tokens, $cursor,
383             sub {
384 74     74   130 my ( $token ) = @_;
385 74 100       286 return if !$token->isa( 'PPI::Token::Structure' );
386 37 50       116 if ( $token eq '}' ) {
387 0         0 $structure_depth++;
388 0         0 return;
389             }
390 37 100       64 if ( $token eq '{' ) {
391 32         46 $structure_depth--;
392 32 50       62 return if $structure_depth;
393             }
394 37         84 return 1;
395             }
396 38         207 );
397 38 100       225 return 'Operator' if !$token; # no matching '{', probably an unbalanced '}'
398              
399             # Scan past any whitespace
400 37         77 ( $token, $cursor ) = _prev_significant_w_cursor( $tokens, $cursor );
401 37 50       91 return 'Operator' if !$token; # Document began with what must be a hash constructor.
402 37 50       157 return 'Operator' if $token->isa( 'PPI::Token::Symbol' ); # subscript
403              
404 37         91 my %meth_or_subscript_end = map { $_ => 1 } qw@ -> } ] @;
  111         254  
405 37 100       97 return 'Operator' if $meth_or_subscript_end{ $token->content }; # subscript
406              
407 28         55 my $content = $token->content;
408 28   100     130 my $produces_or_wants_value =
409             ( $token->isa( 'PPI::Token::Word' ) and ( $content eq 'do' or $content eq 'eval' ) );
410 28 100       97 return $produces_or_wants_value ? 'Operator' : 'Cast';
411             }
412              
413 309         668 my %list_start_or_term_end = map { $_ => 1 } qw@ ; ( { [ @;
  1236         2666  
414             return 'Cast'
415 309 100 100     3311 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 167 100       689 return 'Operator' if !$token->isa( 'PPI::Token::Word' );
421              
422 79         147 ( $token, $cursor ) = _prev_significant_w_cursor( $tokens, $cursor );
423 79 50 66     350 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 1881     1881   2325 my $t = $_[1]; # Tokenizer object
431 1881         3484 my @tokens = $t->_previous_significant_tokens(3);
432 1881         2265 my $p0 = $tokens[0];
433 1881 100       4377 return '' if not $p0;
434              
435             # If we just had another attribute, we are also an attribute
436 1803 100       6536 return 1 if $p0->isa('PPI::Token::Attribute');
437              
438             # If we just had a prototype, then we are an attribute
439 1627 100       5032 return 1 if $p0->isa('PPI::Token::Prototype');
440              
441             # Other than that, we would need to have had a bareword
442 1501 100       4630 return '' unless $p0->isa('PPI::Token::Word');
443              
444             # We could be an anonymous subroutine
445 944 50 33     3005 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 944         1301 my $p1 = $tokens[1];
451 944         1128 my $p2 = $tokens[2];
452 944 50 100     3981 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         1547 return 1;
470             }
471              
472             # We aren't an attribute
473 320         751 '';
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