File Coverage

blib/lib/PPI/Token/Unknown.pm
Criterion Covered Total %
statement 213 218 97.7
branch 151 160 94.3
condition 92 99 92.9
subroutine 11 11 100.0
pod n/a
total 467 488 95.7


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   333 use strict;
  69         93  
  69         1827  
31 69     69   231 use PPI::Token ();
  69         97  
  69         924  
32 69     69   235 use PPI::Exception ();
  69         84  
  69         1150  
33 69     69   220 use PPI::Singletons qw' %MAGIC $CURLY_SYMBOL ';
  69         105  
  69         161002  
34              
35             our $VERSION = '1.290';
36              
37             our @ISA = "PPI::Token";
38              
39              
40              
41              
42              
43              
44             #####################################################################
45             # Tokenizer Methods
46              
47             sub __TOKENIZER__on_char {
48 36148     36148   51740 my ( $self, $t ) = @_; # Self and Tokenizer
49 36148         58788 my $c = $t->{token}->{content}; # Current token
50 36148         60070 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 36148 100       91018 if ( $c eq '*' ) {
    100          
    100          
    100          
    100          
    100          
    50          
54             # Is it a number?
55 1379 100       3773 if ( $char =~ /\d/ ) {
56             # bitwise operator
57 77         215 $t->{class} = $t->{token}->set_class( 'Operator' );
58 77         172 return $t->_finalize_token->__TOKENIZER__on_char( $t );
59             }
60              
61 1302 100       3949 if ( $char =~ /[\w:]/ ) {
62             # Symbol (unless the thing before it is a number
63 450         1037 my ( $prev ) = $t->_previous_significant_tokens(1);
64 450 100 100     3002 if ( not $prev or not $prev->isa('PPI::Token::Number') ) {
65 429         1159 $t->{class} = $t->{token}->set_class( 'Symbol' );
66 429         1313 return 1;
67             }
68             }
69              
70 873 100       2260 if ( $char eq '{' ) {
71             # Get rest of line
72 24         111 pos $t->{line} = $t->{line_cursor} + 1;
73 24 50       199 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 873 100       1658 if ( $char eq '*' ) {
82 35         97 my ( $prev ) = $t->_previous_significant_tokens(1);
83 35 100 100     312 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 872 100 100     3002 if ( $char eq '*' || $char eq '=' ) {
90             # Power operator '**' or mult-assign '*='
91 69         205 $t->{class} = $t->{token}->set_class( 'Operator' );
92 69         199 return 1;
93             }
94              
95 803 100       1799 return $self->_as_cast_or_op($t) if $self->_is_cast_or_op($char);
96              
97 720         1781 $t->{class} = $t->{token}->set_class( 'Operator' );
98 720         1541 return $t->_finalize_token->__TOKENIZER__on_char( $t );
99              
100              
101              
102             } elsif ( $c eq '$' ) {
103             # Postfix dereference: ->$* ->$#*
104 25263 100 100     66189 if ( $char eq '*' || $char eq '#' ) {
105 130         353 my ( $prev ) = $t->_previous_significant_tokens(1);
106 130 100 100     688 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         5 return 1;
109             }
110             }
111              
112 25261 100       78745 if ( $char =~ /[a-z_]/i ) {
113             # Symbol
114 24226         52587 $t->{class} = $t->{token}->set_class( 'Symbol' );
115 24226         56177 return 1;
116             }
117              
118             # Is it a nameless arg in a signature?
119 1035         4014 my %noname = ( ')' => 1, '=' => 1, ',' => 1 );
120 1035 100 100     2826 if ( $noname{$char} and $t->_current_token_has_signatures_active ) {
121 12         30 $t->{class} = $t->{token}->set_class('Symbol');
122 12         24 return $t->_finalize_token->__TOKENIZER__on_char($t);
123             }
124              
125 1023 100       3292 if ( $MAGIC{ $c . $char } ) {
126             # Magic variable
127 865         2478 $t->{class} = $t->{token}->set_class( 'Magic' );
128 865         2825 return 1;
129             }
130              
131 158 100       369 if ( $char eq '{' ) {
132             # Get rest of line
133 17         57 pos $t->{line} = $t->{line_cursor} + 1;
134 17 100       133 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         11 return 1;
138             }
139             }
140              
141             # Must be a cast
142 154         445 $t->{class} = $t->{token}->set_class( 'Cast' );
143 154         323 return $t->_finalize_token->__TOKENIZER__on_char( $t );
144              
145              
146              
147             } elsif ( $c eq '@' ) {
148             # Postfix dereference: ->@*
149 2914 100       5752 if ( $char eq '*' ) {
150 8         27 my ( $prev ) = $t->_previous_significant_tokens(1);
151 8 100 100     80 if ( $prev and $prev->isa('PPI::Token::Operator') and $prev->content eq '->' ) {
      66        
152 1         3 $t->{class} = $t->{token}->set_class( 'Cast' );
153 1         3 return 1;
154             }
155             }
156              
157 2913 100       9462 if ( $char =~ /[\w:]/ ) {
158             # Symbol
159 2073         5227 $t->{class} = $t->{token}->set_class( 'Symbol' );
160 2073         4866 return 1;
161             }
162              
163             # Is it a nameless arg in a signature?
164 840 100 100     2580 if ( $char eq ')' and $t->_current_token_has_signatures_active ) {
165 1         3 $t->{class} = $t->{token}->set_class('Symbol');
166 1         2 return $t->_finalize_token->__TOKENIZER__on_char($t);
167             }
168              
169 839 100       2621 if ( $MAGIC{ $c . $char } ) {
170             # Magic variable
171 24         77 $t->{class} = $t->{token}->set_class( 'Magic' );
172 24         71 return 1;
173             }
174              
175 815 100       1921 if ( $char eq '{' ) {
176             # Get rest of line
177 270         796 pos $t->{line} = $t->{line_cursor} + 1;
178 270 100       2402 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 814         2149 $t->{class} = $t->{token}->set_class( 'Cast' );
187 814         2038 return $t->_finalize_token->__TOKENIZER__on_char( $t );
188              
189              
190              
191             } elsif ( $c eq '%' ) {
192             # Postfix dereference: ->%* ->%[...]
193 1521 100 100     5572 if ( $char eq '*' || $char eq '[' ) {
194 37         122 my ( $prev ) = $t->_previous_significant_tokens(1);
195 37 100 100     358 if ( $prev and $prev->isa('PPI::Token::Operator') and $prev->content eq '->' ) {
      100        
196 2 100       6 if ( $char eq '*' ) {
197 1         4 $t->{class} = $t->{token}->set_class( 'Cast' );
198 1         3 return 1;
199             }
200 1 50       3 if ( $char eq '[' ) {
201 1         5 $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 1519 100       4640 if ( $char =~ /\d/ ) {
209             # bitwise operator
210 103         279 $t->{class} = $t->{token}->set_class( 'Operator' );
211 103         211 return $t->_finalize_token->__TOKENIZER__on_char( $t );
212             }
213              
214             # Is it a nameless arg in a signature?
215 1416 100 100     3401 if ( $char eq ')' and $t->_current_token_has_signatures_active ) {
216 2         5 $t->{class} = $t->{token}->set_class('Symbol');
217 2         5 return $t->_finalize_token->__TOKENIZER__on_char($t);
218             }
219              
220             # Is it a magic variable?
221 1414 100 100     6536 if ( $char eq '^' || $MAGIC{ $c . $char } ) {
222 228         650 $t->{class} = $t->{token}->set_class( 'Magic' );
223 228         552 return 1;
224             }
225              
226 1186 100       3337 if ( $char =~ /[\w:]/ ) {
227             # Symbol (unless the thing before it is a number
228 462         1234 my ( $prev ) = $t->_previous_significant_tokens(1);
229 462 100 100     3227 if ( not $prev or not $prev->isa('PPI::Token::Number') ) {
230 451         1322 $t->{class} = $t->{token}->set_class( 'Symbol' );
231 451         1276 return 1;
232             }
233             }
234              
235 735 100       1506 if ( $char eq '{' ) {
236             # Get rest of line
237 53         198 pos $t->{line} = $t->{line_cursor} + 1;
238 53 100       429 if ( $t->{line} =~ m/$CURLY_SYMBOL/gc ) {
239             # control-character symbol (e.g. %{^_Foo})
240 1         5 $t->{class} = $t->{token}->set_class( 'Magic' );
241 1         3 return 1;
242             }
243             }
244              
245 734 100       1772 return $self->_as_cast_or_op($t) if $self->_is_cast_or_op($char);
246              
247             # Probably the mod operator
248 565         1554 $t->{class} = $t->{token}->set_class( 'Operator' );
249 565         1708 return $t->{class}->__TOKENIZER__on_char( $t );
250              
251              
252              
253             } elsif ( $c eq '&' ) {
254             # Postfix dereference: ->&*
255 1228 100       2737 if ( $char eq '*' ) {
256 12         36 my ( $prev ) = $t->_previous_significant_tokens(1);
257 12 100 100     119 if ( $prev and $prev->isa('PPI::Token::Operator') and $prev->content eq '->' ) {
      100        
258 1         5 $t->{class} = $t->{token}->set_class( 'Cast' );
259 1         3 return 1;
260             }
261             }
262              
263             # Is it a number?
264 1227 100       3363 if ( $char =~ /\d/ ) {
265             # bitwise operator
266 96         248 $t->{class} = $t->{token}->set_class( 'Operator' );
267 96         244 return $t->_finalize_token->__TOKENIZER__on_char( $t );
268             }
269              
270 1131 100       2691 if ( $char =~ /[\w:]/ ) {
271             # Symbol (unless the thing before it is a number
272 185         499 my ( $prev ) = $t->_previous_significant_tokens(1);
273 185 100 100     1475 if ( not $prev or not $prev->isa('PPI::Token::Number') ) {
274 175         575 $t->{class} = $t->{token}->set_class( 'Symbol' );
275 175         510 return 1;
276             }
277             }
278              
279 956 100       2079 return $self->_as_cast_or_op($t) if $self->_is_cast_or_op($char);
280              
281             # Probably the binary and operator
282 863         2443 $t->{class} = $t->{token}->set_class( 'Operator' );
283 863         2471 return $t->{class}->__TOKENIZER__on_char( $t );
284              
285              
286              
287             } elsif ( $c eq '-' ) {
288 1934 100       5101 if ( $char =~ /\d/o ) {
289             # Number
290 126         409 $t->{class} = $t->{token}->set_class( 'Number' );
291 126         377 return 1;
292             }
293              
294 1808 100       3880 if ( $char eq '.' ) {
295             # Number::Float
296 7         24 $t->{class} = $t->{token}->set_class( 'Number::Float' );
297 7         23 return 1;
298             }
299              
300 1801 100       4171 if ( $char =~ /[a-zA-Z]/ ) {
301 200         620 $t->{class} = $t->{token}->set_class( 'DashedWord' );
302 200         565 return 1;
303             }
304              
305             # The numeric negative operator
306 1601         3861 $t->{class} = $t->{token}->set_class( 'Operator' );
307 1601         4898 return $t->{class}->__TOKENIZER__on_char( $t );
308              
309              
310              
311             } elsif ( $c eq ':' ) {
312 1909 100       3764 if ( $char eq ':' ) {
313             # ::foo style bareword
314 8         33 $t->{class} = $t->{token}->set_class( 'Word' );
315 8         24 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 1901 100       4477 if ( $self->__TOKENIZER__is_an_attribute( $t ) ) {
322             # This : is an attribute indicator
323 926         2081 $t->{class} = $t->{token}->set_class( 'Operator' );
324 926         1623 $t->{token}->{_attribute} = 1;
325 926         1543 return $t->_finalize_token->__TOKENIZER__on_char( $t );
326             }
327              
328             # It MIGHT be a label, but it's probably the ?: trinary operator
329 975         2577 $t->{class} = $t->{token}->set_class( 'Operator' );
330 975         2690 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 2493     2493   4101 my ( $self, $char ) = @_;
339 2493 100       4801 return 1 if $char eq '$';
340 2339 100       3932 return 1 if $char eq '@';
341 2309 100       3861 return 1 if $char eq '%';
342 2282 100       3681 return 1 if $char eq '*';
343 2251 100       4243 return 1 if $char eq '{';
344 2148         4502 return;
345             }
346              
347             sub _as_cast_or_op {
348 345     345   748 my ( $self, $t ) = @_;
349 345         857 my $class = _cast_or_op( $t );
350 345         1070 $t->{class} = $t->{token}->set_class( $class );
351 345         829 return $t->_finalize_token->__TOKENIZER__on_char( $t );
352             }
353              
354             sub _prev_significant_w_cursor {
355 483     483   890 my ( $tokens, $cursor, $extra_check ) = @_;
356 483         964 while ( $cursor >= 0 ) {
357 657         890 my $token = $tokens->[ $cursor-- ];
358 657 100       1570 next if !$token->significant;
359 498 100 100     1007 next if $extra_check and !$extra_check->($token);
360 448         991 return ( $token, $cursor );
361             }
362 35         81 return ( undef, $cursor );
363             }
364              
365             # Operator/operand-sensitive, multiple or GLOB cast
366             sub _cast_or_op {
367 345     345   648 my ( $t ) = @_;
368              
369 345         604 my $tokens = $t->{tokens};
370 345         630 my $cursor = scalar( @$tokens ) - 1;
371 345         467 my $token;
372              
373 345         804 ( $token, $cursor ) = _prev_significant_w_cursor( $tokens, $cursor );
374 345 100       1323 return 'Cast' if !$token; # token was first in the document
375              
376 322 100 100     1662 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         62 my $structure_depth = 1;
381             ( $token, $cursor ) = _prev_significant_w_cursor(
382             $tokens, $cursor,
383             sub {
384 87     87   124 my ( $token ) = @_;
385 87 100       330 return if !$token->isa( 'PPI::Token::Structure' );
386 38 100       139 if ( $token eq '}' ) {
387 1         2 $structure_depth++;
388 1         3 return;
389             }
390 37 100       85 if ( $token eq '{' ) {
391 34         46 $structure_depth--;
392 34 50       85 return if $structure_depth;
393             }
394 37         87 return 1;
395             }
396 38         233 );
397 38 100       254 return 'Operator' if !$token; # no matching '{', probably an unbalanced '}'
398              
399             # Scan past any whitespace
400 37         83 ( $token, $cursor ) = _prev_significant_w_cursor( $tokens, $cursor );
401 37 50       99 return 'Operator' if !$token; # Document began with what must be a hash constructor.
402 37 50       177 return 'Operator' if $token->isa( 'PPI::Token::Symbol' ); # subscript
403              
404 37         83 my %meth_or_subscript_end = map { $_ => 1 } qw@ -> } ] @;
  111         288  
405 37 100       112 return 'Operator' if $meth_or_subscript_end{ $token->content }; # subscript
406              
407 28         61 my $content = $token->content;
408 28   100     154 my $produces_or_wants_value =
409             ( $token->isa( 'PPI::Token::Word' ) and ( $content eq 'do' or $content eq 'eval' ) );
410 28 100       149 return $produces_or_wants_value ? 'Operator' : 'Cast';
411             }
412              
413 284         598 my %list_start_or_term_end = map { $_ => 1 } qw@ ; ( { [ @;
  1136         2441  
414             return 'Cast'
415 284 100 100     3248 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 155 100       728 return 'Operator' if !$token->isa( 'PPI::Token::Word' );
421              
422 63         157 ( $token, $cursor ) = _prev_significant_w_cursor( $tokens, $cursor );
423 63 50 66     296 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 1901     1901   2356 my $t = $_[1]; # Tokenizer object
431 1901         3282 my @tokens = $t->_previous_significant_tokens(3);
432 1901         2362 my $p0 = $tokens[0];
433 1901 100       5082 return '' if not $p0;
434              
435             # If we just had another attribute, we are also an attribute
436 1822 100       6889 return 1 if $p0->isa('PPI::Token::Attribute');
437              
438             # If we just had a prototype, then we are an attribute
439 1646 100       5318 return 1 if $p0->isa('PPI::Token::Prototype');
440              
441             # Other than that, we would need to have had a bareword
442 1520 100       4998 return '' unless $p0->isa('PPI::Token::Word');
443              
444             # We could be an anonymous subroutine
445 959 50 33     3288 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 959         1268 my $p1 = $tokens[1];
451 959         1211 my $p2 = $tokens[2];
452 959 50 100     4170 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         1640 return 1;
470             }
471              
472             # We aren't an attribute
473 335         897 '';
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