File Coverage

blib/lib/PPI/Token/Unknown.pm
Criterion Covered Total %
statement 203 208 97.6
branch 146 154 94.8
condition 84 90 93.3
subroutine 11 11 100.0
pod n/a
total 444 463 95.9


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   466 use strict;
  65         146  
  65         1852  
31 65     65   341 use PPI::Token ();
  65         153  
  65         913  
32 65     65   350 use PPI::Exception ();
  65         136  
  65         1638  
33 65     65   360 use PPI::Singletons qw' %MAGIC $CURLY_SYMBOL ';
  65         151  
  65         170908  
34              
35             our $VERSION = '1.277';
36              
37             our @ISA = "PPI::Token";
38              
39              
40              
41              
42              
43              
44             #####################################################################
45             # Tokenizer Methods
46              
47             sub __TOKENIZER__on_char {
48 27602     27602   53504 my ( $self, $t ) = @_; # Self and Tokenizer
49 27602         51571 my $c = $t->{token}->{content}; # Current token
50 27602         58140 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 27602 100       90235 if ( $c eq '*' ) {
    100          
    100          
    100          
    100          
    100          
    50          
54             # Is it a number?
55 1358 100       4558 if ( $char =~ /\d/ ) {
56             # bitwise operator
57 83         326 $t->{class} = $t->{token}->set_class( 'Operator' );
58 83         354 return $t->_finalize_token->__TOKENIZER__on_char( $t );
59             }
60              
61 1275 100       3905 if ( $char =~ /[\w:]/ ) {
62             # Symbol (unless the thing before it is a number
63 423         1427 my ( $prev ) = $t->_previous_significant_tokens(1);
64 423 100 100     3533 if ( not $prev or not $prev->isa('PPI::Token::Number') ) {
65 402         1304 $t->{class} = $t->{token}->set_class( 'Symbol' );
66 402         1519 return 1;
67             }
68             }
69              
70 873 100       2076 if ( $char eq '{' ) {
71             # Get rest of line
72 31         212 pos $t->{line} = $t->{line_cursor} + 1;
73 31 50       293 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       1969 if ( $char eq '*' ) {
82 36         203 my ( $prev ) = $t->_previous_significant_tokens(1);
83 36 100 100     461 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 872 100 100     3411 if ( $char eq '*' || $char eq '=' ) {
90             # Power operator '**' or mult-assign '*='
91 68         266 $t->{class} = $t->{token}->set_class( 'Operator' );
92 68         309 return 1;
93             }
94              
95 804 100       1972 return $self->_as_cast_or_op($t) if $self->_is_cast_or_op($char);
96              
97 707         2321 $t->{class} = $t->{token}->set_class( 'Operator' );
98 707         2161 return $t->_finalize_token->__TOKENIZER__on_char( $t );
99              
100              
101              
102             } elsif ( $c eq '$' ) {
103             # Postfix dereference: ->$* ->$#*
104 16992 100 100     58423 if ( $char eq '*' || $char eq '#' ) {
105 148         587 my ( $prev ) = $t->_previous_significant_tokens(1);
106 148 100 100     1035 if ( $prev and $prev->isa('PPI::Token::Operator') and $prev->content eq '->' ) {
      100        
107 2         7 $t->{class} = $t->{token}->set_class( 'Cast' );
108 2         9 return 1;
109             }
110             }
111              
112 16990 100       61234 if ( $char =~ /[a-z_]/i ) {
113             # Symbol
114 15950         44740 $t->{class} = $t->{token}->set_class( 'Symbol' );
115 15950         48387 return 1;
116             }
117              
118 1040 100       4719 if ( $MAGIC{ $c . $char } ) {
119             # Magic variable
120 894         2912 $t->{class} = $t->{token}->set_class( 'Magic' );
121 894         2987 return 1;
122             }
123              
124 146 100       575 if ( $char eq '{' ) {
125             # Get rest of line
126 13         59 pos $t->{line} = $t->{line_cursor} + 1;
127 13 100       131 if ( $t->{line} =~ m/$CURLY_SYMBOL/gc ) {
128             # control-character symbol (e.g. ${^MATCH})
129 4         13 $t->{class} = $t->{token}->set_class( 'Magic' );
130 4         12 return 1;
131             }
132             }
133              
134             # Must be a cast
135 142         500 $t->{class} = $t->{token}->set_class( 'Cast' );
136 142         502 return $t->_finalize_token->__TOKENIZER__on_char( $t );
137              
138              
139              
140             } elsif ( $c eq '@' ) {
141             # Postfix dereference: ->@*
142 2801 100       7102 if ( $char eq '*' ) {
143 13         70 my ( $prev ) = $t->_previous_significant_tokens(1);
144 13 100 100     201 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         9 return 1;
147             }
148             }
149              
150 2800 100       11076 if ( $char =~ /[\w:]/ ) {
151             # Symbol
152 1966         6269 $t->{class} = $t->{token}->set_class( 'Symbol' );
153 1966         6400 return 1;
154             }
155              
156 834 100       3636 if ( $MAGIC{ $c . $char } ) {
157             # Magic variable
158 45         219 $t->{class} = $t->{token}->set_class( 'Magic' );
159 45         186 return 1;
160             }
161              
162 789 100       2320 if ( $char eq '{' ) {
163             # Get rest of line
164 250         939 pos $t->{line} = $t->{line_cursor} + 1;
165 250 100       2094 if ( $t->{line} =~ m/$CURLY_SYMBOL/gc ) {
166             # control-character symbol (e.g. @{^_Foo})
167 1         7 $t->{class} = $t->{token}->set_class( 'Magic' );
168 1         5 return 1;
169             }
170             }
171              
172             # Must be a cast
173 788         2666 $t->{class} = $t->{token}->set_class( 'Cast' );
174 788         3270 return $t->_finalize_token->__TOKENIZER__on_char( $t );
175              
176              
177              
178             } elsif ( $c eq '%' ) {
179             # Postfix dereference: ->%* ->%[...]
180 1476 100 100     6649 if ( $char eq '*' || $char eq '[' ) {
181 30         137 my ( $prev ) = $t->_previous_significant_tokens(1);
182 30 100 100     360 if ( $prev and $prev->isa('PPI::Token::Operator') and $prev->content eq '->' ) {
      100        
183 2 100       8 if ( $char eq '*' ) {
184 1         7 $t->{class} = $t->{token}->set_class( 'Cast' );
185 1         7 return 1;
186             }
187 1 50       13 if ( $char eq '[' ) {
188 1         8 $t->{class} = $t->{token}->set_class( 'Cast' );
189 1         12 return $t->_finalize_token->__TOKENIZER__on_char( $t );
190             }
191             }
192             }
193              
194             # Is it a number?
195 1474 100       4526 if ( $char =~ /\d/ ) {
196             # bitwise operator
197 102         329 $t->{class} = $t->{token}->set_class( 'Operator' );
198 102         314 return $t->_finalize_token->__TOKENIZER__on_char( $t );
199             }
200              
201             # Is it a magic variable?
202 1372 100 100     6789 if ( $char eq '^' || $MAGIC{ $c . $char } ) {
203 235         797 $t->{class} = $t->{token}->set_class( 'Magic' );
204 235         887 return 1;
205             }
206              
207 1137 100       4101 if ( $char =~ /[\w:]/ ) {
208             # Symbol (unless the thing before it is a number
209 433         1695 my ( $prev ) = $t->_previous_significant_tokens(1);
210 433 100 100     4116 if ( not $prev or not $prev->isa('PPI::Token::Number') ) {
211 426         1500 $t->{class} = $t->{token}->set_class( 'Symbol' );
212 426         1635 return 1;
213             }
214             }
215              
216 711 100       1706 if ( $char eq '{' ) {
217             # Get rest of line
218 43         273 pos $t->{line} = $t->{line_cursor} + 1;
219 43 100       415 if ( $t->{line} =~ m/$CURLY_SYMBOL/gc ) {
220             # control-character symbol (e.g. %{^_Foo})
221 1         8 $t->{class} = $t->{token}->set_class( 'Magic' );
222 1         6 return 1;
223             }
224             }
225              
226 710 100       1679 return $self->_as_cast_or_op($t) if $self->_is_cast_or_op($char);
227              
228             # Probably the mod operator
229 544         1713 $t->{class} = $t->{token}->set_class( 'Operator' );
230 544         2207 return $t->{class}->__TOKENIZER__on_char( $t );
231              
232              
233              
234             } elsif ( $c eq '&' ) {
235             # Postfix dereference: ->&*
236 1262 100       3317 if ( $char eq '*' ) {
237 10         59 my ( $prev ) = $t->_previous_significant_tokens(1);
238 10 100 100     163 if ( $prev and $prev->isa('PPI::Token::Operator') and $prev->content eq '->' ) {
      100        
239 1         8 $t->{class} = $t->{token}->set_class( 'Cast' );
240 1         5 return 1;
241             }
242             }
243              
244             # Is it a number?
245 1261 100       3925 if ( $char =~ /\d/ ) {
246             # bitwise operator
247 105         355 $t->{class} = $t->{token}->set_class( 'Operator' );
248 105         381 return $t->_finalize_token->__TOKENIZER__on_char( $t );
249             }
250              
251 1156 100       3478 if ( $char =~ /[\w:]/ ) {
252             # Symbol (unless the thing before it is a number
253 198         780 my ( $prev ) = $t->_previous_significant_tokens(1);
254 198 100 100     2007 if ( not $prev or not $prev->isa('PPI::Token::Number') ) {
255 192         754 $t->{class} = $t->{token}->set_class( 'Symbol' );
256 192         764 return 1;
257             }
258             }
259              
260 964 100       2477 return $self->_as_cast_or_op($t) if $self->_is_cast_or_op($char);
261              
262             # Probably the binary and operator
263 857         2734 $t->{class} = $t->{token}->set_class( 'Operator' );
264 857         3217 return $t->{class}->__TOKENIZER__on_char( $t );
265              
266              
267              
268             } elsif ( $c eq '-' ) {
269 1871 100       6563 if ( $char =~ /\d/o ) {
270             # Number
271 115         613 $t->{class} = $t->{token}->set_class( 'Number' );
272 115         489 return 1;
273             }
274              
275 1756 100       4087 if ( $char eq '.' ) {
276             # Number::Float
277 9         41 $t->{class} = $t->{token}->set_class( 'Number::Float' );
278 9         41 return 1;
279             }
280              
281 1747 100       4572 if ( $char =~ /[a-zA-Z]/ ) {
282 182         679 $t->{class} = $t->{token}->set_class( 'DashedWord' );
283 182         820 return 1;
284             }
285              
286             # The numeric negative operator
287 1565         4642 $t->{class} = $t->{token}->set_class( 'Operator' );
288 1565         5850 return $t->{class}->__TOKENIZER__on_char( $t );
289              
290              
291              
292             } elsif ( $c eq ':' ) {
293 1842 100       4354 if ( $char eq ':' ) {
294             # ::foo style bareword
295 5         41 $t->{class} = $t->{token}->set_class( 'Word' );
296 5         20 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 1837 100       5042 if ( $self->__TOKENIZER__is_an_attribute( $t ) ) {
303             # This : is an attribute indicator
304 923         2493 $t->{class} = $t->{token}->set_class( 'Operator' );
305 923         1966 $t->{token}->{_attribute} = 1;
306 923         2031 return $t->_finalize_token->__TOKENIZER__on_char( $t );
307             }
308              
309             # It MIGHT be a label, but it's probably the ?: trinary operator
310 914         3208 $t->{class} = $t->{token}->set_class( 'Operator' );
311 914         3732 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 2478     2478   5464 my ( $self, $char ) = @_;
320 2478 100       5781 return 1 if $char eq '$';
321 2315 100       4654 return 1 if $char eq '@';
322 2284 100       4365 return 1 if $char eq '%';
323 2243 100       4296 return 1 if $char eq '*';
324 2211 100       4581 return 1 if $char eq '{';
325 2108         5008 return;
326             }
327              
328             sub _as_cast_or_op {
329 370     370   1011 my ( $self, $t ) = @_;
330 370         1093 my $class = _cast_or_op( $t );
331 370         1401 $t->{class} = $t->{token}->set_class( $class );
332 370         1305 return $t->_finalize_token->__TOKENIZER__on_char( $t );
333             }
334              
335             sub _prev_significant_w_cursor {
336 510     510   1114 my ( $tokens, $cursor, $extra_check ) = @_;
337 510         1399 while ( $cursor >= 0 ) {
338 669         1235 my $token = $tokens->[ $cursor-- ];
339 669 100       1969 next if !$token->significant;
340 517 100 100     1406 next if $extra_check and !$extra_check->($token);
341 467         1272 return ( $token, $cursor );
342             }
343 43         153 return ( undef, $cursor );
344             }
345              
346             # Operator/operand-sensitive, multiple or GLOB cast
347             sub _cast_or_op {
348 370     370   807 my ( $t ) = @_;
349              
350 370         837 my $tokens = $t->{tokens};
351 370         732 my $cursor = scalar( @$tokens ) - 1;
352 370         562 my $token;
353              
354 370         996 ( $token, $cursor ) = _prev_significant_w_cursor( $tokens, $cursor );
355 370 100       1650 return 'Cast' if !$token; # token was first in the document
356              
357 343 100 100     2153 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 37         82 my $structure_depth = 1;
362             ( $token, $cursor ) = _prev_significant_w_cursor(
363             $tokens, $cursor,
364             sub {
365 84     84   164 my ( $token ) = @_;
366 84 100       385 return if !$token->isa( 'PPI::Token::Structure' );
367 35 100       166 if ( $token eq '}' ) {
368 1         3 $structure_depth++;
369 1         4 return;
370             }
371 34 100       233 if ( $token eq '{' ) {
372 33         61 $structure_depth--;
373 33 50       176 return if $structure_depth;
374             }
375 34         118 return 1;
376             }
377 37         258 );
378 37 100       267 return 'Operator' if !$token; # no matching '{', probably an unbalanced '}'
379              
380             # Scan past any whitespace
381 34         87 ( $token, $cursor ) = _prev_significant_w_cursor( $tokens, $cursor );
382 34 100       125 return 'Operator' if !$token; # Document began with what must be a hash constructor.
383 33 50       157 return 'Operator' if $token->isa( 'PPI::Token::Symbol' ); # subscript
384              
385 33         85 my %meth_or_subscript_end = map { $_ => 1 } qw@ -> } ] @;
  99         268  
386 33 100       110 return 'Operator' if $meth_or_subscript_end{ $token->content }; # subscript
387              
388 24         58 my $content = $token->content;
389 24   100     144 my $produces_or_wants_value =
390             ( $token->isa( 'PPI::Token::Word' ) and ( $content eq 'do' or $content eq 'eval' ) );
391 24 100       134 return $produces_or_wants_value ? 'Operator' : 'Cast';
392             }
393              
394 306         855 my %list_start_or_term_end = map { $_ => 1 } qw@ ; ( { [ @;
  1224         3054  
395             return 'Cast'
396 306 100 100     4067 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       928 return 'Operator' if !$token->isa( 'PPI::Token::Word' );
402              
403 69         182 ( $token, $cursor ) = _prev_significant_w_cursor( $tokens, $cursor );
404 69 50 66     531 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 1837     1837   2835 my $t = $_[1]; # Tokenizer object
412 1837         4696 my @tokens = $t->_previous_significant_tokens(3);
413 1837         3395 my $p0 = $tokens[0];
414 1837 100       5957 return '' if not $p0;
415              
416             # If we just had another attribute, we are also an attribute
417 1760 100       7532 return 1 if $p0->isa('PPI::Token::Attribute');
418              
419             # If we just had a prototype, then we are an attribute
420 1584 100       5784 return 1 if $p0->isa('PPI::Token::Prototype');
421              
422             # Other than that, we would need to have had a bareword
423 1458 100       5242 return '' unless $p0->isa('PPI::Token::Word');
424              
425             # We could be an anonymous subroutine
426 936 50 33     3594 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 936         1636 my $p1 = $tokens[1];
432 936         1403 my $p2 = $tokens[2];
433 936 50 100     5306 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         1902 return 1;
451             }
452              
453             # We aren't an attribute
454 315         958 '';
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