File Coverage

blib/lib/PPI/Token/Unknown.pm
Criterion Covered Total %
statement 203 208 97.6
branch 146 154 94.8
condition 83 90 92.2
subroutine 11 11 100.0
pod n/a
total 443 463 95.6


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 65     65   377 use strict;
  65         112  
  65         1437  
31 65     65   270 use PPI::Token ();
  65         429  
  65         709  
32 65     65   238 use PPI::Exception ();
  65         105  
  65         1288  
33 65     65   292 use PPI::Singletons qw' %MAGIC $CURLY_SYMBOL ';
  65         147  
  65         137273  
34              
35             our $VERSION = '1.276';
36              
37             our @ISA = "PPI::Token";
38              
39              
40              
41              
42              
43              
44             #####################################################################
45             # Tokenizer Methods
46              
47             sub __TOKENIZER__on_char {
48 27283     27283   45287 my ( $self, $t ) = @_; # Self and Tokenizer
49 27283         41251 my $c = $t->{token}->{content}; # Current token
50 27283         47190 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 27283 100       72705 if ( $c eq '*' ) {
    100          
    100          
    100          
    100          
    100          
    50          
54             # Is it a number?
55 1355 100       3464 if ( $char =~ /\d/ ) {
56             # bitwise operator
57 83         203 $t->{class} = $t->{token}->set_class( 'Operator' );
58 83         213 return $t->_finalize_token->__TOKENIZER__on_char( $t );
59             }
60              
61 1272 100       3055 if ( $char =~ /[\w:]/ ) {
62             # Symbol (unless the thing before it is a number
63 419         987 my ( $prev ) = $t->_previous_significant_tokens(1);
64 419 100 100     2632 if ( not $prev or not $prev->isa('PPI::Token::Number') ) {
65 407         1029 $t->{class} = $t->{token}->set_class( 'Symbol' );
66 407         1147 return 1;
67             }
68             }
69              
70 865 100       1735 if ( $char eq '{' ) {
71             # Get rest of line
72 24         146 pos $t->{line} = $t->{line_cursor} + 1;
73 24 50       193 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 865 100       1595 if ( $char eq '*' ) {
82 40         114 my ( $prev ) = $t->_previous_significant_tokens(1);
83 40 100 100     334 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         5 return 1;
86             }
87             }
88              
89 864 100 100     2527 if ( $char eq '*' || $char eq '=' ) {
90             # Power operator '**' or mult-assign '*='
91 73         208 $t->{class} = $t->{token}->set_class( 'Operator' );
92 73         210 return 1;
93             }
94              
95 791 100       1931 return $self->_as_cast_or_op($t) if $self->_is_cast_or_op($char);
96              
97 686         1719 $t->{class} = $t->{token}->set_class( 'Operator' );
98 686         1512 return $t->_finalize_token->__TOKENIZER__on_char( $t );
99              
100              
101              
102             } elsif ( $c eq '$' ) {
103             # Postfix dereference: ->$* ->$#*
104 16923 100 100     46595 if ( $char eq '*' || $char eq '#' ) {
105 154         376 my ( $prev ) = $t->_previous_significant_tokens(1);
106 154 100 100     743 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 16921 100       50324 if ( $char =~ /[a-z_]/i ) {
113             # Symbol
114 15832         36565 $t->{class} = $t->{token}->set_class( 'Symbol' );
115 15832         41711 return 1;
116             }
117              
118 1089 100       3216 if ( $MAGIC{ $c . $char } ) {
119             # Magic variable
120 937         2658 $t->{class} = $t->{token}->set_class( 'Magic' );
121 937         2441 return 1;
122             }
123              
124 152 100       452 if ( $char eq '{' ) {
125             # Get rest of line
126 25         107 pos $t->{line} = $t->{line_cursor} + 1;
127 25 100       178 if ( $t->{line} =~ m/$CURLY_SYMBOL/gc ) {
128             # control-character symbol (e.g. ${^MATCH})
129 4         11 $t->{class} = $t->{token}->set_class( 'Magic' );
130 4         12 return 1;
131             }
132             }
133              
134             # Must be a cast
135 148         367 $t->{class} = $t->{token}->set_class( 'Cast' );
136 148         345 return $t->_finalize_token->__TOKENIZER__on_char( $t );
137              
138              
139              
140             } elsif ( $c eq '@' ) {
141             # Postfix dereference: ->@*
142 2770 100       5462 if ( $char eq '*' ) {
143 11         44 my ( $prev ) = $t->_previous_significant_tokens(1);
144 11 100 100     105 if ( $prev and $prev->isa('PPI::Token::Operator') and $prev->content eq '->' ) {
      100        
145 1         4 $t->{class} = $t->{token}->set_class( 'Cast' );
146 1         3 return 1;
147             }
148             }
149              
150 2769 100       9031 if ( $char =~ /[\w:]/ ) {
151             # Symbol
152 1933         4720 $t->{class} = $t->{token}->set_class( 'Symbol' );
153 1933         5160 return 1;
154             }
155              
156 836 100       2881 if ( $MAGIC{ $c . $char } ) {
157             # Magic variable
158 28         110 $t->{class} = $t->{token}->set_class( 'Magic' );
159 28         153 return 1;
160             }
161              
162 808 100       1581 if ( $char eq '{' ) {
163             # Get rest of line
164 253         771 pos $t->{line} = $t->{line_cursor} + 1;
165 253 100       1727 if ( $t->{line} =~ m/$CURLY_SYMBOL/gc ) {
166             # control-character symbol (e.g. @{^_Foo})
167 1         4 $t->{class} = $t->{token}->set_class( 'Magic' );
168 1         4 return 1;
169             }
170             }
171              
172             # Must be a cast
173 807         2104 $t->{class} = $t->{token}->set_class( 'Cast' );
174 807         2178 return $t->_finalize_token->__TOKENIZER__on_char( $t );
175              
176              
177              
178             } elsif ( $c eq '%' ) {
179             # Postfix dereference: ->%* ->%[...]
180 1432 100 100     4869 if ( $char eq '*' || $char eq '[' ) {
181 37         145 my ( $prev ) = $t->_previous_significant_tokens(1);
182 37 100 100     321 if ( $prev and $prev->isa('PPI::Token::Operator') and $prev->content eq '->' ) {
      100        
183 2 100       7 if ( $char eq '*' ) {
184 1         3 $t->{class} = $t->{token}->set_class( 'Cast' );
185 1         7 return 1;
186             }
187 1 50       4 if ( $char eq '[' ) {
188 1         5 $t->{class} = $t->{token}->set_class( 'Cast' );
189 1         3 return $t->_finalize_token->__TOKENIZER__on_char( $t );
190             }
191             }
192             }
193              
194             # Is it a number?
195 1430 100       3597 if ( $char =~ /\d/ ) {
196             # bitwise operator
197 103         313 $t->{class} = $t->{token}->set_class( 'Operator' );
198 103         294 return $t->_finalize_token->__TOKENIZER__on_char( $t );
199             }
200              
201             # Is it a magic variable?
202 1327 100 100     5193 if ( $char eq '^' || $MAGIC{ $c . $char } ) {
203 240         681 $t->{class} = $t->{token}->set_class( 'Magic' );
204 240         664 return 1;
205             }
206              
207 1087 100       2841 if ( $char =~ /[\w:]/ ) {
208             # Symbol (unless the thing before it is a number
209 402         1068 my ( $prev ) = $t->_previous_significant_tokens(1);
210 402 100 100     2751 if ( not $prev or not $prev->isa('PPI::Token::Number') ) {
211 397         1205 $t->{class} = $t->{token}->set_class( 'Symbol' );
212 397         1275 return 1;
213             }
214             }
215              
216 690 100       1331 if ( $char eq '{' ) {
217             # Get rest of line
218 45         224 pos $t->{line} = $t->{line_cursor} + 1;
219 45 100       338 if ( $t->{line} =~ m/$CURLY_SYMBOL/gc ) {
220             # control-character symbol (e.g. %{^_Foo})
221 1         9 $t->{class} = $t->{token}->set_class( 'Magic' );
222 1         8 return 1;
223             }
224             }
225              
226 689 100       1284 return $self->_as_cast_or_op($t) if $self->_is_cast_or_op($char);
227              
228             # Probably the mod operator
229 529         1297 $t->{class} = $t->{token}->set_class( 'Operator' );
230 529         1435 return $t->{class}->__TOKENIZER__on_char( $t );
231              
232              
233              
234             } elsif ( $c eq '&' ) {
235             # Postfix dereference: ->&*
236 1270 100       2418 if ( $char eq '*' ) {
237 9         40 my ( $prev ) = $t->_previous_significant_tokens(1);
238 9 100 100     97 if ( $prev and $prev->isa('PPI::Token::Operator') and $prev->content eq '->' ) {
      100        
239 1         4 $t->{class} = $t->{token}->set_class( 'Cast' );
240 1         4 return 1;
241             }
242             }
243              
244             # Is it a number?
245 1269 100       2990 if ( $char =~ /\d/ ) {
246             # bitwise operator
247 95         384 $t->{class} = $t->{token}->set_class( 'Operator' );
248 95         206 return $t->_finalize_token->__TOKENIZER__on_char( $t );
249             }
250              
251 1174 100       2605 if ( $char =~ /[\w:]/ ) {
252             # Symbol (unless the thing before it is a number
253 229         616 my ( $prev ) = $t->_previous_significant_tokens(1);
254 229 100 100     1399 if ( not $prev or not $prev->isa('PPI::Token::Number') ) {
255 216         599 $t->{class} = $t->{token}->set_class( 'Symbol' );
256 216         681 return 1;
257             }
258             }
259              
260 958 100       1961 return $self->_as_cast_or_op($t) if $self->_is_cast_or_op($char);
261              
262             # Probably the binary and operator
263 858         1889 $t->{class} = $t->{token}->set_class( 'Operator' );
264 858         2526 return $t->{class}->__TOKENIZER__on_char( $t );
265              
266              
267              
268             } elsif ( $c eq '-' ) {
269 1968 100       5427 if ( $char =~ /\d/o ) {
270             # Number
271 125         444 $t->{class} = $t->{token}->set_class( 'Number' );
272 125         386 return 1;
273             }
274              
275 1843 100       3437 if ( $char eq '.' ) {
276             # Number::Float
277 9         31 $t->{class} = $t->{token}->set_class( 'Number::Float' );
278 9         30 return 1;
279             }
280              
281 1834 100       3766 if ( $char =~ /[a-zA-Z]/ ) {
282 215         647 $t->{class} = $t->{token}->set_class( 'DashedWord' );
283 215         604 return 1;
284             }
285              
286             # The numeric negative operator
287 1619         3978 $t->{class} = $t->{token}->set_class( 'Operator' );
288 1619         4718 return $t->{class}->__TOKENIZER__on_char( $t );
289              
290              
291              
292             } elsif ( $c eq ':' ) {
293 1565 100       2743 if ( $char eq ':' ) {
294             # ::foo style bareword
295 9         36 $t->{class} = $t->{token}->set_class( 'Word' );
296 9         27 return 1;
297             }
298              
299             # Now, : acts very very differently in different contexts.
300             # Mainly, we need to find out if this is a subroutine attribute.
301             # We'll leave a hint in the token to indicate that, if it is.
302 1556 100       3396 if ( $self->__TOKENIZER__is_an_attribute( $t ) ) {
303             # This : is an attribute indicator
304 923         2005 $t->{class} = $t->{token}->set_class( 'Operator' );
305 923         1752 $t->{token}->{_attribute} = 1;
306 923         1911 return $t->_finalize_token->__TOKENIZER__on_char( $t );
307             }
308              
309             # It MIGHT be a label, but it's probably the ?: trinary operator
310 633         1636 $t->{class} = $t->{token}->set_class( 'Operator' );
311 633         1860 return $t->{class}->__TOKENIZER__on_char( $t );
312             }
313              
314             # erm...
315 0         0 PPI::Exception->throw('Unknown value in PPI::Token::Unknown token');
316             }
317              
318             sub _is_cast_or_op {
319 2438     2438   4228 my ( $self, $char ) = @_;
320 2438 100       4302 return 1 if $char eq '$';
321 2280 100       3408 return 1 if $char eq '@';
322 2247 100       3306 return 1 if $char eq '%';
323 2210 100       3106 return 1 if $char eq '*';
324 2175 100       3363 return 1 if $char eq '{';
325 2073         3760 return;
326             }
327              
328             sub _as_cast_or_op {
329 365     365   683 my ( $self, $t ) = @_;
330 365         864 my $class = _cast_or_op( $t );
331 365         978 $t->{class} = $t->{token}->set_class( $class );
332 365         966 return $t->_finalize_token->__TOKENIZER__on_char( $t );
333             }
334              
335             sub _prev_significant_w_cursor {
336 500     500   861 my ( $tokens, $cursor, $extra_check ) = @_;
337 500         969 while ( $cursor >= 0 ) {
338 649         879 my $token = $tokens->[ $cursor-- ];
339 649 100       1590 next if !$token->significant;
340 499 100 100     997 next if $extra_check and !$extra_check->($token);
341 465         1058 return ( $token, $cursor );
342             }
343 35         68 return ( undef, $cursor );
344             }
345              
346             # Operator/operand-sensitive, multiple or GLOB cast
347             sub _cast_or_op {
348 365     365   561 my ( $t ) = @_;
349              
350 365         591 my $tokens = $t->{tokens};
351 365         610 my $cursor = scalar( @$tokens ) - 1;
352 365         412 my $token;
353              
354 365         713 ( $token, $cursor ) = _prev_significant_w_cursor( $tokens, $cursor );
355 365 100       1183 return 'Cast' if !$token; # token was first in the document
356              
357 342 100 100     1542 if ( $token->isa( 'PPI::Token::Structure' ) and $token->content eq '}' ) {
358              
359             # Scan the token stream backwards an arbitrarily long way,
360             # looking for the matching opening curly brace.
361 35         54 my $structure_depth = 1;
362             ( $token, $cursor ) = _prev_significant_w_cursor(
363             $tokens, $cursor,
364             sub {
365 67     67   88 my ( $token ) = @_;
366 67 100       231 return if !$token->isa( 'PPI::Token::Structure' );
367 35 100       119 if ( $token eq '}' ) {
368 1         3 $structure_depth++;
369 1         4 return;
370             }
371 34 100       71 if ( $token eq '{' ) {
372 33         50 $structure_depth--;
373 33 100       112 return if $structure_depth;
374             }
375 33         94 return 1;
376             }
377 35         202 );
378 35 100       389 return 'Operator' if !$token; # no matching '{', probably an unbalanced '}'
379              
380             # Scan past any whitespace
381 33         57 ( $token, $cursor ) = _prev_significant_w_cursor( $tokens, $cursor );
382 33 50       211 return 'Operator' if !$token; # Document began with what must be a hash constructor.
383 33 50       117 return 'Operator' if $token->isa( 'PPI::Token::Symbol' ); # subscript
384              
385 33         54 my %meth_or_subscript_end = map { $_ => 1 } qw@ -> } ] @;
  99         200  
386 33 100       78 return 'Operator' if $meth_or_subscript_end{ $token->content }; # subscript
387              
388 24         48 my $content = $token->content;
389 24   66     150 my $produces_or_wants_value =
390             ( $token->isa( 'PPI::Token::Word' ) and ( $content eq 'do' or $content eq 'eval' ) );
391 24 100       81 return $produces_or_wants_value ? 'Operator' : 'Cast';
392             }
393              
394 307         589 my %list_start_or_term_end = map { $_ => 1 } qw@ ; ( { [ @;
  1228         2483  
395             return 'Cast'
396 307 100 100     3013 if $token->isa( 'PPI::Token::Structure' ) and $list_start_or_term_end{ $token->content }
      100        
      100        
      100        
397             or $token->isa( 'PPI::Token::Cast' )
398             or $token->isa( 'PPI::Token::Operator' )
399             or $token->isa( 'PPI::Token::Label' );
400              
401 166 100       679 return 'Operator' if !$token->isa( 'PPI::Token::Word' );
402              
403 67         175 ( $token, $cursor ) = _prev_significant_w_cursor( $tokens, $cursor );
404 67 50 66     306 return 'Cast' if !$token || $token->content ne '->';
405              
406 0         0 return 'Operator';
407             }
408              
409             # Are we at a location where a ':' would indicate a subroutine attribute
410             sub __TOKENIZER__is_an_attribute {
411 1556     1556   1905 my $t = $_[1]; # Tokenizer object
412 1556         3004 my @tokens = $t->_previous_significant_tokens(3);
413 1556         2262 my $p0 = $tokens[0];
414 1556 100       4000 return '' if not $p0;
415              
416             # If we just had another attribute, we are also an attribute
417 1481 100       5075 return 1 if $p0->isa('PPI::Token::Attribute');
418              
419             # If we just had a prototype, then we are an attribute
420 1305 100       3837 return 1 if $p0->isa('PPI::Token::Prototype');
421              
422             # Other than that, we would need to have had a bareword
423 1179 100       3746 return '' unless $p0->isa('PPI::Token::Word');
424              
425             # We could be an anonymous subroutine
426 647 50 33     1971 if ( $p0->isa('PPI::Token::Word') and $p0->content eq 'sub' ) {
427 0         0 return 1;
428             }
429              
430             # Or, we could be a named subroutine
431 647         1013 my $p1 = $tokens[1];
432 647         830 my $p2 = $tokens[2];
433 647 50 100     2984 if (
      100        
      33        
      66        
434             $p1
435             and
436             $p1->isa('PPI::Token::Word')
437             and
438             $p1->content eq 'sub'
439             and (
440             not $p2
441             or
442             $p2->isa('PPI::Token::Structure')
443             or (
444             $p2->isa('PPI::Token::Whitespace')
445             and
446             $p2->content eq ''
447             )
448             )
449             ) {
450 621         1712 return 1;
451             }
452              
453             # We aren't an attribute
454 26         98 '';
455             }
456              
457             1;
458              
459             =pod
460              
461             =head1 SUPPORT
462              
463             See the L in the main module.
464              
465             =head1 AUTHOR
466              
467             Adam Kennedy Eadamk@cpan.orgE
468              
469             =head1 COPYRIGHT
470              
471             Copyright 2001 - 2011 Adam Kennedy.
472              
473             This program is free software; you can redistribute
474             it and/or modify it under the same terms as Perl itself.
475              
476             The full text of the license can be found in the
477             LICENSE file included with this module.
478              
479             =cut