File Coverage

blib/lib/PPI/Token/Unknown.pm
Criterion Covered Total %
statement 201 208 96.6
branch 144 154 93.5
condition 84 90 93.3
subroutine 11 11 100.0
pod n/a
total 440 463 95.0


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 64     64   332 use strict;
  64         103  
  64         1438  
31 64     64   252 use PPI::Token ();
  64         103  
  64         646  
32 64     64   255 use PPI::Exception ();
  64         104  
  64         1317  
33 64     64   317 use PPI::Singletons qw' %MAGIC $CURLY_SYMBOL ';
  64         119  
  64         134190  
34              
35             our $VERSION = '1.275';
36              
37             our @ISA = "PPI::Token";
38              
39              
40              
41              
42              
43              
44             #####################################################################
45             # Tokenizer Methods
46              
47             sub __TOKENIZER__on_char {
48 27096     27096   43389 my ( $self, $t ) = @_; # Self and Tokenizer
49 27096         39803 my $c = $t->{token}->{content}; # Current token
50 27096         45914 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 27096 100       66879 if ( $c eq '*' ) {
    100          
    100          
    100          
    100          
    100          
    50          
54             # Is it a number?
55 1353 100       3230 if ( $char =~ /\d/ ) {
56             # bitwise operator
57 83         282 $t->{class} = $t->{token}->set_class( 'Operator' );
58 83         246 return $t->_finalize_token->__TOKENIZER__on_char( $t );
59             }
60              
61 1270 100       2780 if ( $char =~ /[\w:]/ ) {
62             # Symbol (unless the thing before it is a number
63 415         949 my ( $prev ) = $t->_previous_significant_tokens(1);
64 415 100 100     2870 if ( not $prev or not $prev->isa('PPI::Token::Number') ) {
65 396         1058 $t->{class} = $t->{token}->set_class( 'Symbol' );
66 396         1120 return 1;
67             }
68             }
69              
70 874 100       1509 if ( $char eq '{' ) {
71             # Get rest of line
72 28         142 pos $t->{line} = $t->{line_cursor} + 1;
73 28 50       194 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 874 100       1469 if ( $char eq '*' ) {
82 37         119 my ( $prev ) = $t->_previous_significant_tokens(1);
83 37 100 100     333 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         4 return 1;
86             }
87             }
88              
89 873 100 100     2501 if ( $char eq '*' || $char eq '=' ) {
90             # Power operator '**' or mult-assign '*='
91 72         184 $t->{class} = $t->{token}->set_class( 'Operator' );
92 72         186 return 1;
93             }
94              
95 801 100       1444 return $self->_as_cast_or_op($t) if $self->_is_cast_or_op($char);
96              
97 709         1584 $t->{class} = $t->{token}->set_class( 'Operator' );
98 709         1467 return $t->_finalize_token->__TOKENIZER__on_char( $t );
99              
100              
101              
102             } elsif ( $c eq '$' ) {
103             # Postfix dereference: ->$* ->$#*
104 16744 100 100     46295 if ( $char eq '*' || $char eq '#' ) {
105 155         435 my ( $prev ) = $t->_previous_significant_tokens(1);
106 155 100 100     790 if ( $prev and $prev->isa('PPI::Token::Operator') and $prev->content eq '->' ) {
      100        
107 2         8 $t->{class} = $t->{token}->set_class( 'Cast' );
108 2         5 return 1;
109             }
110             }
111              
112 16742 100       50201 if ( $char =~ /[a-z_]/i ) {
113             # Symbol
114 15698         33520 $t->{class} = $t->{token}->set_class( 'Symbol' );
115 15698         38318 return 1;
116             }
117              
118 1044 100       2858 if ( $MAGIC{ $c . $char } ) {
119             # Magic variable
120 905         2252 $t->{class} = $t->{token}->set_class( 'Magic' );
121 905         2336 return 1;
122             }
123              
124 139 100       370 if ( $char eq '{' ) {
125             # Get rest of line
126 18         75 pos $t->{line} = $t->{line_cursor} + 1;
127 18 100       113 if ( $t->{line} =~ m/$CURLY_SYMBOL/gc ) {
128             # control-character symbol (e.g. ${^MATCH})
129 4         12 $t->{class} = $t->{token}->set_class( 'Magic' );
130 4         11 return 1;
131             }
132             }
133              
134             # Must be a cast
135 135         408 $t->{class} = $t->{token}->set_class( 'Cast' );
136 135         364 return $t->_finalize_token->__TOKENIZER__on_char( $t );
137              
138              
139              
140             } elsif ( $c eq '@' ) {
141             # Postfix dereference: ->@*
142 2774 100       4915 if ( $char eq '*' ) {
143 8         56 my ( $prev ) = $t->_previous_significant_tokens(1);
144 8 100 100     83 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         4 return 1;
147             }
148             }
149              
150 2773 100       8412 if ( $char =~ /[\w:]/ ) {
151             # Symbol
152 1924         4948 $t->{class} = $t->{token}->set_class( 'Symbol' );
153 1924         4976 return 1;
154             }
155              
156 849 100       2422 if ( $MAGIC{ $c . $char } ) {
157             # Magic variable
158 28         150 $t->{class} = $t->{token}->set_class( 'Magic' );
159 28         91 return 1;
160             }
161              
162 821 100       1544 if ( $char eq '{' ) {
163             # Get rest of line
164 249         673 pos $t->{line} = $t->{line_cursor} + 1;
165 249 100       1488 if ( $t->{line} =~ m/$CURLY_SYMBOL/gc ) {
166             # control-character symbol (e.g. @{^_Foo})
167 1         5 $t->{class} = $t->{token}->set_class( 'Magic' );
168 1         4 return 1;
169             }
170             }
171              
172             # Must be a cast
173 820         2117 $t->{class} = $t->{token}->set_class( 'Cast' );
174 820         1929 return $t->_finalize_token->__TOKENIZER__on_char( $t );
175              
176              
177              
178             } elsif ( $c eq '%' ) {
179             # Postfix dereference: ->%* ->%[...]
180 1427 100 100     4748 if ( $char eq '*' || $char eq '[' ) {
181 32         103 my ( $prev ) = $t->_previous_significant_tokens(1);
182 32 100 100     300 if ( $prev and $prev->isa('PPI::Token::Operator') and $prev->content eq '->' ) {
      100        
183 2 100       17 if ( $char eq '*' ) {
184 1         7 $t->{class} = $t->{token}->set_class( 'Cast' );
185 1         4 return 1;
186             }
187 1 50       4 if ( $char eq '[' ) {
188 1         5 $t->{class} = $t->{token}->set_class( 'Cast' );
189 1         4 return $t->_finalize_token->__TOKENIZER__on_char( $t );
190             }
191             }
192             }
193              
194             # Is it a number?
195 1425 100       3405 if ( $char =~ /\d/ ) {
196             # bitwise operator
197 97         284 $t->{class} = $t->{token}->set_class( 'Operator' );
198 97         233 return $t->_finalize_token->__TOKENIZER__on_char( $t );
199             }
200              
201             # Is it a magic variable?
202 1328 100 100     4625 if ( $char eq '^' || $MAGIC{ $c . $char } ) {
203 227         637 $t->{class} = $t->{token}->set_class( 'Magic' );
204 227         615 return 1;
205             }
206              
207 1101 100       2676 if ( $char =~ /[\w:]/ ) {
208             # Symbol (unless the thing before it is a number
209 423         1006 my ( $prev ) = $t->_previous_significant_tokens(1);
210 423 100 100     2755 if ( not $prev or not $prev->isa('PPI::Token::Number') ) {
211 416         1220 $t->{class} = $t->{token}->set_class( 'Symbol' );
212 416         1293 return 1;
213             }
214             }
215              
216 685 100       1321 if ( $char eq '{' ) {
217             # Get rest of line
218 46         189 pos $t->{line} = $t->{line_cursor} + 1;
219 46 100       337 if ( $t->{line} =~ m/$CURLY_SYMBOL/gc ) {
220             # control-character symbol (e.g. %{^_Foo})
221 1         11 $t->{class} = $t->{token}->set_class( 'Magic' );
222 1         4 return 1;
223             }
224             }
225              
226 684 100       1316 return $self->_as_cast_or_op($t) if $self->_is_cast_or_op($char);
227              
228             # Probably the mod operator
229 537         1252 $t->{class} = $t->{token}->set_class( 'Operator' );
230 537         1440 return $t->{class}->__TOKENIZER__on_char( $t );
231              
232              
233              
234             } elsif ( $c eq '&' ) {
235             # Postfix dereference: ->&*
236 1305 100       2549 if ( $char eq '*' ) {
237 14         77 my ( $prev ) = $t->_previous_significant_tokens(1);
238 14 100 100     132 if ( $prev and $prev->isa('PPI::Token::Operator') and $prev->content eq '->' ) {
      100        
239 1         5 $t->{class} = $t->{token}->set_class( 'Cast' );
240 1         4 return 1;
241             }
242             }
243              
244             # Is it a number?
245 1304 100       3463 if ( $char =~ /\d/ ) {
246             # bitwise operator
247 93         332 $t->{class} = $t->{token}->set_class( 'Operator' );
248 93         265 return $t->_finalize_token->__TOKENIZER__on_char( $t );
249             }
250              
251 1211 100       2538 if ( $char =~ /[\w:]/ ) {
252             # Symbol (unless the thing before it is a number
253 219         546 my ( $prev ) = $t->_previous_significant_tokens(1);
254 219 100 100     1279 if ( not $prev or not $prev->isa('PPI::Token::Number') ) {
255 206         512 $t->{class} = $t->{token}->set_class( 'Symbol' );
256 206         586 return 1;
257             }
258             }
259              
260 1005 100       1967 return $self->_as_cast_or_op($t) if $self->_is_cast_or_op($char);
261              
262             # Probably the binary and operator
263 895         2050 $t->{class} = $t->{token}->set_class( 'Operator' );
264 895         2368 return $t->{class}->__TOKENIZER__on_char( $t );
265              
266              
267              
268             } elsif ( $c eq '-' ) {
269 1933 100       5144 if ( $char =~ /\d/o ) {
270             # Number
271 117         439 $t->{class} = $t->{token}->set_class( 'Number' );
272 117         379 return 1;
273             }
274              
275 1816 100       3369 if ( $char eq '.' ) {
276             # Number::Float
277 10         51 $t->{class} = $t->{token}->set_class( 'Number::Float' );
278 10         35 return 1;
279             }
280              
281 1806 100       3596 if ( $char =~ /[a-zA-Z]/ ) {
282 210         633 $t->{class} = $t->{token}->set_class( 'DashedWord' );
283 210         564 return 1;
284             }
285              
286             # The numeric negative operator
287 1596         3574 $t->{class} = $t->{token}->set_class( 'Operator' );
288 1596         4288 return $t->{class}->__TOKENIZER__on_char( $t );
289              
290              
291              
292             } elsif ( $c eq ':' ) {
293 1560 100       2876 if ( $char eq ':' ) {
294             # ::foo style bareword
295 7         43 $t->{class} = $t->{token}->set_class( 'Word' );
296 7         25 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 1553 100       3440 if ( $self->__TOKENIZER__is_an_attribute( $t ) ) {
303             # This : is an attribute indicator
304 923         1964 $t->{class} = $t->{token}->set_class( 'Operator' );
305 923         1617 $t->{token}->{_attribute} = 1;
306 923         1844 return $t->_finalize_token->__TOKENIZER__on_char( $t );
307             }
308              
309             # It MIGHT be a label, but it's probably the ?: trinary operator
310 630         1656 $t->{class} = $t->{token}->set_class( 'Operator' );
311 630         1739 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 2490     2490   4185 my ( $self, $char ) = @_;
320 2490 100       4540 return 1 if $char eq '$';
321 2336 100       3609 return 1 if $char eq '@';
322 2313 100       3237 return 1 if $char eq '%';
323 2279 100       3558 return 1 if $char eq '*';
324 2245 100       3474 return 1 if $char eq '{';
325 2141         3842 return;
326             }
327              
328             sub _as_cast_or_op {
329 349     349   637 my ( $self, $t ) = @_;
330 349         826 my $class = _cast_or_op( $t );
331 349         938 $t->{class} = $t->{token}->set_class( $class );
332 349         858 return $t->_finalize_token->__TOKENIZER__on_char( $t );
333             }
334              
335             sub _prev_significant_w_cursor {
336 490     490   837 my ( $tokens, $cursor, $extra_check ) = @_;
337 490         889 while ( $cursor >= 0 ) {
338 626         872 my $token = $tokens->[ $cursor-- ];
339 626 100       1457 next if !$token->significant;
340 478 100 100     985 next if $extra_check and !$extra_check->($token);
341 455         967 return ( $token, $cursor );
342             }
343 35         89 return ( undef, $cursor );
344             }
345              
346             # Operator/operand-sensitive, multiple or GLOB cast
347             sub _cast_or_op {
348 349     349   528 my ( $t ) = @_;
349              
350 349         579 my $tokens = $t->{tokens};
351 349         547 my $cursor = scalar( @$tokens ) - 1;
352 349         374 my $token;
353              
354 349         660 ( $token, $cursor ) = _prev_significant_w_cursor( $tokens, $cursor );
355 349 100       1074 return 'Cast' if !$token; # token was first in the document
356              
357 326 100 100     1447 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         94 my $structure_depth = 1;
362             ( $token, $cursor ) = _prev_significant_w_cursor(
363             $tokens, $cursor,
364             sub {
365 57     57   122 my ( $token ) = @_;
366 57 100       215 return if !$token->isa( 'PPI::Token::Structure' );
367 34 50       137 if ( $token eq '}' ) {
368 0         0 $structure_depth++;
369 0         0 return;
370             }
371 34 100       63 if ( $token eq '{' ) {
372 33         46 $structure_depth--;
373 33 50       81 return if $structure_depth;
374             }
375 34         88 return 1;
376             }
377 35         184 );
378 35 100       375 return 'Operator' if !$token; # no matching '{', probably an unbalanced '}'
379              
380             # Scan past any whitespace
381 34         63 ( $token, $cursor ) = _prev_significant_w_cursor( $tokens, $cursor );
382 34 50       218 return 'Operator' if !$token; # Document began with what must be a hash constructor.
383 34 50       142 return 'Operator' if $token->isa( 'PPI::Token::Symbol' ); # subscript
384              
385 34         63 my %meth_or_subscript_end = map { $_ => 1 } qw@ -> } ] @;
  102         224  
386 34 100       92 return 'Operator' if $meth_or_subscript_end{ $token->content }; # subscript
387              
388 25         58 my $content = $token->content;
389 25   100     131 my $produces_or_wants_value =
390             ( $token->isa( 'PPI::Token::Word' ) and ( $content eq 'do' or $content eq 'eval' ) );
391 25 100       82 return $produces_or_wants_value ? 'Operator' : 'Cast';
392             }
393              
394 291         561 my %list_start_or_term_end = map { $_ => 1 } qw@ ; ( { [ @;
  1164         2321  
395             return 'Cast'
396 291 100 100     2763 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 156 100       582 return 'Operator' if !$token->isa( 'PPI::Token::Word' );
402              
403 72         172 ( $token, $cursor ) = _prev_significant_w_cursor( $tokens, $cursor );
404 72 50 66     334 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 1553     1553   1913 my $t = $_[1]; # Tokenizer object
412 1553         2941 my @tokens = $t->_previous_significant_tokens(3);
413 1553         2312 my $p0 = $tokens[0];
414 1553 100       3815 return '' if not $p0;
415              
416             # If we just had another attribute, we are also an attribute
417 1476 100       4809 return 1 if $p0->isa('PPI::Token::Attribute');
418              
419             # If we just had a prototype, then we are an attribute
420 1300 100       3678 return 1 if $p0->isa('PPI::Token::Prototype');
421              
422             # Other than that, we would need to have had a bareword
423 1174 100       3527 return '' unless $p0->isa('PPI::Token::Word');
424              
425             # We could be an anonymous subroutine
426 645 50 33     1961 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 645         932 my $p1 = $tokens[1];
432 645         736 my $p2 = $tokens[2];
433 645 50 100     2684 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         1572 return 1;
451             }
452              
453             # We aren't an attribute
454 24         70 '';
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