File Coverage

blib/lib/Language/P/Lexer.pm
Criterion Covered Total %
statement 268 468 57.2
branch 178 352 50.5
condition 70 157 44.5
subroutine 21 25 84.0
pod 1 10 10.0
total 538 1012 53.1


line stmt bran cond sub pod time code
1             package Language::P::Lexer;
2              
3 90     90   563 use strict;
  90         215  
  90         4095  
4 90     90   734 use warnings;
  90         173  
  90         3404  
5 90     90   476 use base qw(Class::Accessor::Fast);
  90         189  
  90         9952  
6              
7             __PACKAGE__->mk_ro_accessors( qw(stream buffer tokens symbol_table
8             file line _start_of_line _heredoc_lexer
9             ) );
10             __PACKAGE__->mk_accessors( qw(quote) );
11              
12 90     90   56226 use Language::P::ParseTree qw(:all);
  90         302  
  90         129150  
13 90     90   984 use Language::P::Keywords;
  90         200  
  90         42711  
14              
15             our @TOKENS;
16             BEGIN {
17 90     90   9715 our @TOKENS =
18             qw(T_ID T_FQ_ID T_SUB_ID T_EOF T_PACKAGE T_FILETEST
19             T_PATTERN T_STRING T_NUMBER T_QUOTE T_OR T_XOR
20             T_SEMICOLON T_COLON T_COMMA T_OPPAR T_CLPAR T_OPSQ T_CLSQ
21             T_OPBRK T_CLBRK T_OPHASH T_OPAN T_CLPAN T_INTERR
22             T_NOT T_SLESS T_CLAN T_SGREAT T_EQUAL T_LESSEQUAL T_SLESSEQUAL
23             T_GREATEQUAL T_SGREATEQUAL T_EQUALEQUAL T_SEQUALEQUAL T_NOTEQUAL
24             T_SNOTEQUAL T_SLASH T_BACKSLASH T_DOT T_DOTDOT T_DOTDOTDOT T_PLUS
25             T_MINUS T_STAR T_DOLLAR T_PERCENT T_AT T_AMPERSAND T_PLUSPLUS
26             T_MINUSMINUS T_ANDAND T_OROR T_ARYLEN T_ARROW T_MATCH T_NOTMATCH
27             T_ANDANDLOW T_ORORLOW T_NOTLOW T_XORLOW T_CMP T_SCMP T_SSTAR T_POWER
28             T_PLUSEQUAL T_MINUSEQUAL T_STAREQUAL T_SLASHEQUAL T_LABEL T_TILDE
29              
30             T_CLASS_START T_CLASS_END T_CLASS T_QUANTIFIER T_ASSERTION T_ALTERNATE
31             T_CLGROUP
32             );
33             };
34              
35             use constant
36 7110         98512 { X_NOTHING => 0,
37             X_STATE => 1,
38             X_TERM => 2,
39             X_OPERATOR => 3,
40             X_BLOCK => 4,
41             X_REF => 5,
42              
43             O_POS => 0,
44             O_TYPE => 1,
45             O_VALUE => 2,
46             O_ID_TYPE => 3,
47             O_FT_OP => 3,
48             O_QS_INTERPOLATE => 3,
49             O_QS_BUFFER => 4,
50             O_RX_REST => 3,
51             O_RX_SECOND_HALF => 5,
52             O_RX_FLAGS => 6,
53             O_RX_INTERPOLATED => 7,
54             O_NUM_FLAGS => 3,
55              
56             LEX_NO_PACKAGE => 1,
57              
58 90         789 map { $TOKENS[$_] => $_ + 1 } 0 .. $#TOKENS,
59 90     90   596 };
  90         205  
60              
61 90     90   680 use Exporter qw(import);
  90         190  
  90         1248652  
62              
63             our @EXPORT_OK =
64             ( qw(X_NOTHING X_STATE X_TERM X_OPERATOR X_BLOCK X_REF
65             O_POS O_TYPE O_VALUE O_ID_TYPE O_FT_OP O_QS_INTERPOLATE O_QS_BUFFER
66             O_RX_REST O_RX_SECOND_HALF O_RX_FLAGS O_RX_INTERPOLATED O_NUM_FLAGS
67             LEX_NO_PACKAGE
68             ), @TOKENS );
69             our %EXPORT_TAGS =
70             ( all => \@EXPORT_OK,
71             );
72              
73             sub new {
74 249     249 1 3235 my( $class, $args ) = @_;
75 249         1222 my $self = $class->SUPER::new( $args );
76 249   100     3645 my $a = delete $self->{string} || "";
77              
78 249 100       1249 $self->{buffer} = ref $a ? $a : \$a;
79 249         676 $self->{tokens} = [];
80 249         551 $self->{brackets} = 0;
81 249         579 $self->{pending_brackets} = [];
82 249         651 $self->{line} = 1;
83 249         519 $self->{_start_of_line} = 1;
84 249         1060 $self->{pos} = [ $self->file, $self->line ];
85              
86 249         3026 return $self;
87             }
88              
89             sub peek {
90 3134     3134 0 15194 my( $self, $expect ) = ( @_, X_NOTHING );
91 3134         6502 my $token = $self->lex( $expect );
92              
93 3134         12394 $self->unlex( $token );
94              
95 3134         17615 return $token;
96             }
97              
98             sub unlex {
99 4949     4949 0 11499 my( $self, $token ) = @_;
100              
101 4949         5472 push @{$self->tokens}, $token;
  4949         11446  
102             }
103              
104             my %ops =
105             ( ';' => T_SEMICOLON,
106             ':' => T_COLON,
107             ',' => T_COMMA,
108             '=>' => T_COMMA,
109             '(' => T_OPPAR,
110             ')' => T_CLPAR,
111             '[' => T_OPSQ,
112             ']' => T_CLSQ,
113             '{' => T_OPBRK,
114             '}' => T_CLBRK,
115             '?' => T_INTERR,
116             '!' => T_NOT,
117             '<' => T_OPAN,
118             'lt' => T_SLESS,
119             '>' => T_CLAN,
120             'gt' => T_SGREAT,
121             '=' => T_EQUAL,
122             '<=' => T_LESSEQUAL,
123             'le' => T_SLESSEQUAL,
124             '>=' => T_GREATEQUAL,
125             'ge' => T_SGREATEQUAL,
126             '==' => T_EQUALEQUAL,
127             'eq' => T_SEQUALEQUAL,
128             '!=' => T_NOTEQUAL,
129             'ne' => T_SNOTEQUAL,
130             '<=>' => T_CMP,
131             'cmp' => T_SCMP,
132             '/' => T_SLASH,
133             '\\' => T_BACKSLASH,
134             '.' => T_DOT,
135             '..' => T_DOTDOT,
136             '...' => T_DOTDOTDOT,
137             '~' => T_TILDE,
138             '+' => T_PLUS,
139             '-' => T_MINUS,
140             '*' => T_STAR,
141             'x' => T_SSTAR,
142             '$' => T_DOLLAR,
143             '%' => T_PERCENT,
144             '**' => T_POWER,
145             '@' => T_AT,
146             '&' => T_AMPERSAND,
147             '|' => T_OR,
148             '^' => T_XOR,
149             '++' => T_PLUSPLUS,
150             '--' => T_MINUSMINUS,
151             '&&' => T_ANDAND,
152             '||' => T_OROR,
153             '$#' => T_ARYLEN,
154             '->' => T_ARROW,
155             '=~' => T_MATCH,
156             '!~' => T_NOTMATCH,
157             'and' => T_ANDANDLOW,
158             'or' => T_ORORLOW,
159             'not' => T_NOTLOW,
160             'xor' => T_XORLOW,
161             );
162              
163             my %filetest =
164             ( r => OP_FT_EREADABLE,
165             w => OP_FT_EWRITABLE,
166             x => OP_FT_EEXECUTABLE,
167             o => OP_FT_EOWNED,
168             R => OP_FT_RREADABLE,
169             W => OP_FT_RWRITABLE,
170             X => OP_FT_REXECUTABLE,
171             O => OP_FT_ROWNED,
172             e => OP_FT_EXISTS,
173             z => OP_FT_EMPTY,
174             s => OP_FT_NONEMPTY,
175             f => OP_FT_ISFILE,
176             d => OP_FT_ISDIR,
177             l => OP_FT_ISSYMLINK,
178             p => OP_FT_ISPIPE,
179             S => OP_FT_ISSOCKET,
180             b => OP_FT_ISBLOCKSPECIAL,
181             c => OP_FT_ISCHARSPECIAL,
182             t => OP_FT_ISTTY,
183             u => OP_FT_SETUID,
184             g => OP_FT_SETGID,
185             k => OP_FT_STICKY,
186             T => OP_FT_ISASCII,
187             B => OP_FT_ISBINARY,
188             M => OP_FT_MTIME,
189             A => OP_FT_ATIME,
190             C => OP_FT_CTIME,
191             );
192              
193             my %quoted_chars =
194             ( 'n' => "\n",
195             't' => "\t",
196             'r' => "\r",
197             'f' => "\f",
198             'b' => "\b",
199             'a' => "\a",
200             'e' => "\e",
201             );
202              
203             my %quoted_pattern =
204             ( w => [ T_CLASS, 'WORDS' ],
205             W => [ T_CLASS, 'NON_WORDS' ],
206             s => [ T_CLASS, 'SPACES' ],
207             S => [ T_CLASS, 'NOT_SPACES' ],
208             d => [ T_CLASS, 'DIGITS' ],
209             D => [ T_CLASS, 'NOT_DIGITS' ],
210             b => [ T_ASSERTION, 'WORD_BOUNDARY' ],
211             B => [ T_ASSERTION, 'NON_WORD_BOUNDARY' ],
212             A => [ T_ASSERTION, 'BEGINNING' ],
213             Z => [ T_ASSERTION, 'END_OR_NEWLINE' ],
214             z => [ T_ASSERTION, 'END' ],
215             G => [ T_ASSERTION, 'POS' ],
216             );
217              
218             my %pattern_special =
219             ( '^' => [ T_ASSERTION, 'START_SPECIAL' ],
220             '$' => [ T_ASSERTION, 'END_SPECIAL' ],
221             '*' => [ T_QUANTIFIER, 0, -1, 1 ],
222             '+' => [ T_QUANTIFIER, 1, -1, 1 ],
223             '?' => [ T_QUANTIFIER, 0, 1, 1 ],
224             '*?' => [ T_QUANTIFIER, 0, -1, 0 ],
225             '+?' => [ T_QUANTIFIER, 1, -1, 0 ],
226             '??' => [ T_QUANTIFIER, 0, 1, 0 ],
227             ')' => [ T_CLGROUP ],
228             '|' => [ T_ALTERNATE ],
229             '[' => [ T_CLASS_START ],
230             ']' => [ T_CLASS_END ],
231             );
232              
233             sub _skip_space {
234 2517     2517   3209 my( $self ) = @_;
235 2517         7876 my $buffer = $self->buffer;
236 2517         9895 my $retval = '';
237 2517         3136 my $reset_pos = 0;
238              
239 2517         2863 for(;;) {
240 3073 100       8874 $self->_fill_buffer unless length $$buffer;
241 3073 100       6313 return unless length $$buffer;
242              
243 3009 100 66     15181 if( $self->{_start_of_line}
244             && $$buffer =~ s/^#[ \t]*line[ \t]+([0-9]+)(?:[ \t]+"([^"]+)")?[ \t]*[\r\n]// ) {
245 1         4 $self->{line} = $1;
246 1 50       7 $self->{file} = $2 if $2;
247 1         2 $reset_pos = 1;
248 1         38 next;
249             }
250              
251 3008 50 66     15709 $$buffer =~ s/^([ \t]+)// && defined wantarray and $retval .= $1;
252 3008 100       8799 if( $$buffer =~ s/^([\r\n])// ) {
253 536 50       1337 $retval .= $1 if defined wantarray;
254 536         901 $self->{_start_of_line} = 1;
255 536         760 ++$self->{line};
256 536         630 $reset_pos = 1;
257 536         759 next;
258             }
259 2472 100       6373 if( $$buffer =~ s/^(#.*\n)// ) {
260 19 50       75 $retval .= $1 if defined wantarray;
261 19         49 $self->{_start_of_line} = 1;
262 19         44 ++$self->{line};
263 19         34 $reset_pos = 1;
264 19         47 next;
265             }
266              
267 2453 50       5515 last if length $$buffer;
268             }
269              
270 2453 100       4284 if( $reset_pos ) {
271 372         1268 $self->{pos} = [ $self->{file}, $self->{line} ];
272             }
273              
274 2453         4763 return $retval;
275             }
276              
277             # taken from intuit_more in toke.c
278             sub _character_class_insanity {
279 0     0   0 my( $self ) = @_;
280 0         0 my $buffer = $self->buffer;
281              
282 0 0       0 if( $$buffer =~ /^\]|^\^/ ) {
283 0         0 return 1;
284             }
285              
286 0         0 my( $t ) = $$buffer =~ /^(.*\])/;
287 0         0 my $w = 2;
288 0         0 my( $un_char, $last_un_char, @seen ) = ( 255 );
289              
290 0 0       0 return 1 if !defined $t;
291              
292 0 0       0 if( $t =~ /^\$/ ) {
    0          
    0          
    0          
293 0         0 $w -= 3;
294             } elsif( $t =~ /^[0-9][0-9]\]/ ) {
295 0         0 $w -= 10
296             } elsif( $t =~ /^[0-9]\]/ ) {
297 0         0 $w -= 100;
298             } elsif( $t =~ /^\$\w+/ ) {
299             # HACK, not in original
300 0         0 $w -= 100;
301             }
302              
303 0         0 for(;;) {
304 0         0 last;
305             }
306              
307 0 0       0 return $w >= 0 ? 1 : 0;
308             }
309              
310             # taken from intuit_more in toke.c
311             sub _quoted_code_lookahead {
312 32     32   54 my( $self ) = @_;
313 32         98 my $buffer = $self->buffer;
314              
315 32 50       319 if( $$buffer =~ s/^->([{[])// ) {
    50          
    100          
316 0         0 ++$self->{brackets};
317 0         0 $self->unlex( [ $self->{pos}, $ops{$1}, $1 ] );
318 0         0 $self->unlex( [ $self->{pos}, T_ARROW, '->' ] );
319             } elsif( $$buffer =~ s/^{// ) {
320 0 0       0 if( !$self->quote->{interpolated_pattern} ) {
    0          
321 0         0 ++$self->{brackets};
322 0         0 $self->unlex( [ $self->{pos}, T_OPBRK, '{' ] );
323             } elsif( $$buffer =~ /^[0-9]+,[0-9]*}/ ) {
324 0         0 die 'Quantifier!';
325             } else {
326 0         0 ++$self->{brackets};
327 0         0 $self->unlex( [ $self->{pos}, T_OPBRK, '{' ] );
328             }
329             } elsif( $$buffer =~ s/^\[// ) {
330 3 50       14 if( !$self->quote->{interpolated_pattern} ) {
331 3         27 ++$self->{brackets};
332 3         17 $self->unlex( [ $self->{pos}, T_OPSQ, '[' ] );
333             } else {
334 0 0       0 if( _character_class_insanity( $self ) ) {
335 0         0 $$buffer = '[' . $$buffer;
336 0         0 my $token = $self->lex_quote;
337 0         0 $self->unlex( $token );
338             } else {
339 0         0 ++$self->{brackets};
340 0         0 $self->unlex( [ $self->{pos}, T_OPSQ, '[' ] );
341             }
342             }
343             } else {
344 29         73 my $token = $self->lex_quote;
345 29         70 $self->unlex( $token );
346             }
347             }
348              
349             sub lex_pattern_group {
350 0     0 0 0 my( $self ) = @_;
351 0         0 my $buffer = $self->buffer;
352              
353 0 0       0 die unless length $$buffer; # no whitespace allowed after '(?'
354              
355 0 0       0 $$buffer =~ s/^(\#|:|[imsx]*\-[imsx]*:?|!|=|<=|)//x
356             or die "Invalid character after (?";
357              
358 0         0 return [ $self->{pos}, T_PATTERN, $1 ];
359             }
360              
361             sub lex_charclass {
362 0     0 0 0 my( $self ) = @_;
363              
364 0         0 my $buffer = $self->buffer;
365 0         0 my $c = substr $$buffer, 0, 1, '';
366 0 0       0 if( $c eq '\\' ) {
    0          
    0          
367 0         0 my $qc = substr $$buffer, 0, 1, '';
368              
369 0 0       0 if( my $qp = $quoted_pattern{$qc} ) {
370 0         0 return [ $self->{pos}, $qp->[0], $qp->[1] ];
371             }
372              
373 0         0 return [ $self->{pos}, T_STRING, $qc ];
374             } elsif( $c eq '-' ) {
375 0         0 return [ $self->{pos}, T_MINUS, '-' ];
376             } elsif( $c eq ']' ) {
377 0         0 return [ $self->{pos}, T_CLASS_END ];
378             } else {
379 0         0 return [ $self->{pos}, T_STRING, $c ];
380             }
381             }
382              
383             sub lex_quote {
384 501     501 0 2388 my( $self ) = @_;
385              
386 501 100       570 return pop @{$self->tokens} if @{$self->tokens};
  249         1824  
  501         1379  
387              
388 252         1854 my $buffer = $self->buffer;
389 252         1015 my $v = '';
390 252         298 for(;;) {
391 423 100       934 unless( length $$buffer ) {
392 187 100       557 if( length $v ) {
393 171         912 $self->unlex( [ $self->{pos}, T_EOF, '' ] );
394 171         2084 return [ $self->{pos}, T_STRING, $v, 1 ];
395             } else {
396 16         80 return [ $self->{pos}, T_EOF, '' ];
397             }
398             }
399              
400 236         296 my $to_return;
401 236         921 my $pattern = $self->quote->{pattern};
402 236         1491 my $interpolated_pattern = $self->quote->{interpolated_pattern};
403 236         1323 while( length $$buffer ) {
404 1254         2178 my $c = substr $$buffer, 0, 1, '';
405              
406 1254 100 66     6933 if( $pattern || $interpolated_pattern ) {
407 91 100 66     746 if( $c eq '\\' ) {
    100 66        
    100          
408 1         4 my $qc = substr $$buffer, 0, 1;
409              
410 1 50       7 if( my $qp = $quoted_pattern{$qc} ) {
411 0         0 substr $$buffer, 0, 1, ''; # eat character
412 0 0       0 if( $pattern ) {
413 0         0 $to_return = [ $self->{pos}, T_PATTERN, $qc, $qp ];
414             } else {
415 0         0 $v .= $c . $qc;
416 0         0 next;
417             }
418             }
419             } elsif( $c eq '(' && !$interpolated_pattern ) {
420 7         16 my $nc = substr $$buffer, 0, 1;
421              
422 7 50       16 if( $nc eq '?' ) {
423 0         0 substr $$buffer, 0, 1, ''; # eat character
424 0         0 $to_return = [ $self->{pos}, T_PATTERN, '(?' ];
425             } else {
426 7         29 $to_return = [ $self->{pos}, T_PATTERN, '(' ];
427             }
428             } elsif( !$interpolated_pattern
429             and my $special = $pattern_special{$c} ) {
430             # check nongreedy quantifiers
431 29 100       100 if( $special->[0] == T_QUANTIFIER ) {
432 11         23 my $qc = substr $$buffer, 0, 1;
433              
434 11 100       33 if( $qc eq '?' ) {
435 1         3 substr $$buffer, 0, 1, '';
436 1         4 $special = $pattern_special{$c . $qc};
437             }
438             }
439              
440 29         108 $to_return = [ $self->{pos}, T_PATTERN, $c, $special ];
441             }
442             }
443              
444 1254 100       6378 if( $to_return ) {
445 36 100       81 if( length $v ) {
446 24         76 $self->unlex( $to_return );
447 24         237 return [ $self->{pos}, T_STRING, $v, 1 ];
448             } else {
449 12         42 return $to_return;
450             }
451             }
452              
453 1218 100 66     6053 if( $c eq '\\' && $self->quote->{interpolate} ) {
    100 66        
454 165         1420 my $qc = substr $$buffer, 0, 1, '';
455              
456 165 50       931 if( $qc =~ /^[a-zA-Z]$/ ) {
    0          
457 165 50       616 if( $quoted_chars{$qc} ) {
458 165         1050 $v .= $quoted_chars{$qc};
459             } else {
460 0         0 die "Invalid escape '$qc'";
461             }
462             } elsif( $qc =~ /^[0-9]$/ ) {
463 0         0 die "Unsupported numeric escape";
464             } else {
465 0         0 $v .= $qc;
466             }
467             } elsif( $c =~ /^[\$\@]$/ && $self->quote->{interpolate} ) {
468 29 50 0     306 if( $interpolated_pattern
    100 33        
469             && ( !length( $$buffer )
470             || index( "()| \r\n\t",
471             substr( $$buffer, 0, 1 ) ) != -1 ) ) {
472 0         0 $v .= $c;
473             } elsif( length $v ) {
474 25         128 $self->unlex( [ $self->{pos}, $ops{$c}, $c ] );
475              
476 25         206 return [ $self->{pos}, T_STRING, $v ];
477             } else {
478 4         25 return [ $self->{pos}, $ops{$c}, $c ];
479             }
480             } else {
481 1024         2195 $v .= $c;
482             }
483             }
484             }
485              
486 0         0 die "Can't get there";
487             }
488              
489             sub lex_alphabetic_identifier {
490 21     21 0 158 my( $self, $flags ) = @_;
491              
492 21 50       33 if( @{$self->tokens} ) {
  21         66  
493 0 0       0 return undef if $self->tokens->[-1]->[O_TYPE] != T_ID;
494 0         0 return pop @{$self->tokens};
  0         0  
495             }
496              
497 21         187 local $_ = $self->buffer;
498              
499 21 50       122 if( $flags & LEX_NO_PACKAGE ) {
500 0 0       0 return undef unless $$_ =~ /^[ \t\r\n]*\w/;
501             } else {
502 21 100       108 return undef unless $$_ =~ /^[ \t\r\n]*[':\w]/;
503             }
504              
505 17         67 return lex_identifier( $self, $flags );
506             }
507              
508             sub lex_identifier {
509 342     342 0 1822 my( $self, $flags ) = @_;
510              
511 342 50       472 if( @{$self->tokens} ) {
  342         940  
512 0 0       0 return undef if $self->tokens->[-1]->[O_TYPE] != T_ID;
513 0         0 return pop @{$self->tokens};
  0         0  
514             }
515              
516 342         2314 local $_ = $self->buffer;
517              
518 342 50 33     3221 _skip_space( $self )
519             if defined( $$_ ) && $$_ =~ /^[ \t\r\n]/;
520              
521 342 50       845 return [ $self->{pos}, T_EOF, '' ] unless length $$_;
522              
523 342         423 my $id;
524 342 50       970 $$_ =~ s/^\^([A-Z\[\\\]^_?])//x and do {
525 0         0 $id = [ $self->{pos}, T_ID, chr( ord( $1 ) - ord( 'A' ) + 1 ), T_FQ_ID ];
526             };
527 342 50 33     1188 $id or $$_ =~ s/^::(?=\W)//x and do {
528 0         0 $id = [ $self->{pos}, T_ID, 'main::', T_FQ_ID ];
529             };
530 342 50 66     2295 $id or $$_ =~ s/^(\'|::)?(\w+)//x and do {
531 341 50       805 if( $flags & LEX_NO_PACKAGE ) {
532 0         0 return [ $self->{pos}, T_ID, $2, T_ID ];
533             }
534              
535 341 50       1352 my $ids = defined $1 ? '::' . $2 : $2;
536 341 50       794 my $idt = defined $1 ? T_FQ_ID : T_ID;
537              
538 341         2300 while( $$_ =~ s/^::(\w*)|^\'(\w+)// ) {
539 0 0       0 $ids .= '::' . ( defined $1 ? $1 : $2 );
540 0         0 $idt = T_FQ_ID;
541             }
542              
543 341         1311 $id = [ $self->{pos}, T_ID, $ids, $idt ];
544             };
545 342 100 33     1180 $id or $$_ =~ s/^{\^([A-Z\[\\\]^_?])(\w*)}//x and do {
546 0         0 $id = [ $self->{pos}, T_ID, chr( ord( $1 ) - ord( 'A' ) + 1 ) . $2, T_FQ_ID ];
547             };
548 342 100 33     1992 $id or $$_ =~ s/^{//x and do {
549 0         0 my $spcbef = _skip_space( $self );
550 0         0 my $maybe_id;
551 0 0       0 if( $$_ =~ s/^(\w+)//x ) {
552 0         0 $maybe_id = $1;
553             } else {
554 0         0 $$_ = '{' . $spcbef . $$_;
555 0         0 return undef;
556             }
557 0         0 my $spcaft = _skip_space( $self );
558              
559 0 0       0 if( $$_ =~ s/^}//x ) {
    0          
560 0         0 $id = [ $self->{pos}, T_ID, $maybe_id, T_ID ];
561             } elsif( $$_ =~ /^\[|^\{/ ) {
562 0         0 ++$self->{brackets};
563 0         0 push @{$self->{pending_brackets}}, $self->{brackets};
  0         0  
564 0         0 $id = [ $self->{pos}, T_ID, $maybe_id, T_ID ];
565             } else {
566             # not a simple identifier
567 0         0 $$_ = '{' . $spcbef . $maybe_id . $spcaft . $$_;
568 0         0 return undef;
569             }
570             };
571 342 100 33     738 $id or $$_ =~ /^\$[\${:]/ and do {
572 0         0 return;
573             };
574 342 100 33     661 $id or $$_ =~ s/^(\W)(?=\W)// and do {
575 0         0 $id = [ $self->{pos}, T_ID, $1, T_FQ_ID ];
576             };
577              
578 342 100 100     1594 if( $id && $self->quote && $self->{brackets} == 0 ) {
      66        
579 29         322 _quoted_code_lookahead( $self );
580             }
581              
582 342         3528 return $id;
583             }
584              
585             sub lex_number {
586 247     247 0 417 my( $self ) = @_;
587 247         679 local $_ = $self->buffer;
588 247         1205 my( $num, $flags ) = ( '', 0 );
589              
590 247 100       879 $$_ =~ s/^0([xb]?)//x and do {
591 14 50       99 if( $1 eq 'b' ) {
    50          
592             # binary number
593 0 0       0 if( $$_ =~ s/^([01]+)// ) {
594 0         0 $flags = NUM_BINARY;
595 0         0 $num .= $1;
596              
597 0         0 return [ $self->{pos}, T_NUMBER, $num, $flags ];
598             } else {
599 0         0 die "Invalid binary digit";
600             }
601             } elsif( $1 eq 'x' ) {
602             # hexadecimal number
603 0 0       0 if( $$_ =~ s/^([0-9a-fA-F]+)// ) {
604 0         0 $flags = NUM_HEXADECIMAL;
605 0         0 $num .= $1;
606              
607 0         0 return [ $self->{pos}, T_NUMBER, $num, $flags ];
608             } else {
609 0         0 die "Invalid hexadecimal digit";
610             }
611             } else {
612             # maybe octal number
613 14 50       61 if( $$_ =~ s/^([0-7]+)// ) {
614 0         0 $flags = NUM_OCTAL;
615 0         0 $num .= $1;
616 0 0       0 $$_ =~ /^[89]/ and die "Invalid octal digit";
617              
618 0         0 return [ $self->{pos}, T_NUMBER, $num, $flags ];
619             } else {
620 14         27 $flags = NUM_INTEGER;
621 14         2000 $num = '0'
622             }
623             }
624             };
625 247 100       1102 $$_ =~ s/^(\d+)//x and do {
626 231         404 $flags = NUM_INTEGER;
627 231         491 $num .= $1;
628             };
629             # '..' operator (es. 12..15)
630 247 50       876 $$_ =~ /^\.\./ and return [ $self->{pos}, T_NUMBER, $num, $flags ];
631 247 100       670 $$_ =~ s/^\.(\d*)//x and do {
632 2         5 $flags = NUM_FLOAT;
633 2 50       6 $num = '0' unless length $num;
634 2 50       10 $num .= ".$1" if length $1;
635             };
636 247 50       720 $$_ =~ s/^[eE]([+-]?\d+)//x and do {
637 0         0 $flags = NUM_FLOAT;
638 0         0 $num .= "e$1";
639             };
640              
641 247         1279 return [ $self->{pos}, T_NUMBER, $num, $flags ];
642             }
643              
644             my %quote_end = qw!( ) { } [ ] < >!;
645             my @rx_flags =
646             ( FLAG_RX_MULTI_LINE, FLAG_RX_SINGLE_LINE, FLAG_RX_CASE_INSENSITIVE,
647             FLAG_RX_FREE_FORMAT, FLAG_RX_ONCE, FLAG_RX_GLOBAL, FLAG_RX_KEEP,
648             FLAG_RX_EVAL );
649             my @tr_flags = ( FLAG_RX_COMPLEMENT, FLAG_RX_DELETE, FLAG_RX_SQUEEZE );
650             my %regex_flags =
651             ( m => [ OP_QL_M, 'msixogc', @rx_flags ],
652             qr => [ OP_QL_QR, 'msixo', @rx_flags ],
653             s => [ OP_QL_S, 'msixogce', @rx_flags ],
654             tr => [ OP_QL_TR, 'cds', @tr_flags ],
655             y => [ OP_QL_TR, 'cds', @tr_flags ],
656             );
657              
658             sub _find_end {
659 171     171   304 my( $self, $op, $quote_start ) = @_;
660              
661 171         477 local $_ = $self->buffer;
662              
663 171 50 33     2281 if( $op && !$quote_start ) {
664 0 0       0 if( $$_ =~ /^[ \t\r\n]/ ) {
665 0         0 _skip_space( $self );
666             }
667             # if we find a fat comma, we got a string constant, not the
668             # start of a quoted string!
669 0 0       0 $$_ =~ /^=>/ and return ( undef, [ $self->{pos}, T_STRING, $op ] );
670 0 0       0 $$_ =~ s/^([^ \t\r\n])// or die;
671 0         0 $quote_start = $1;
672             }
673              
674 171   33     833 my $quote_end = $quote_end{$quote_start} || $quote_start;
675 171 50       393 my $paired = $quote_start eq $quote_end ? 0 : 1;
676 171         693 my $is_regex = $regex_flags{$op};
677 171         296 my $pos = $self->{pos};
678              
679 171         472 my( $interpolated, $delim_count, $str ) = ( 0, 1, '' );
680 171         203 SCAN_END: for(;;) {
681 171 50       476 $self->_fill_buffer unless length $$_;
682 171 50       347 die "EOF while parsing quoted string" unless length $$_;
683              
684 171         501 while( length $$_ ) {
685 1372         2066 my $c = substr $$_, 0, 1, '';
686              
687 1372 100 33     5866 if( $c eq '\\' ) {
    50 0        
    100 33        
    50 0        
688 164         270 my $qc = substr $$_, 0, 1, '';
689              
690 164 50 33     735 if( $qc eq $quote_start || $qc eq $quote_end ) {
691 0         0 $str .= $qc;
692             } else {
693 164         389 $str .= "\\" . $qc;
694             }
695              
696 164         388 next;
697             } elsif( $paired && $c eq $quote_start ) {
698 0         0 ++$delim_count;
699             } elsif( $c eq $quote_end ) {
700 171         492 --$delim_count;
701              
702 171 50       530 last SCAN_END unless $delim_count;
703             } elsif( $is_regex
704             && ( $c eq '$' || $c eq '@' )
705             && $quote_start ne "'" ) {
706 0         0 my $nc = substr $$_, 0, 1;
707              
708 0 0 0     0 if( length( $nc )
      0        
709             && $nc ne $quote_end
710             && index( "()| \r\n\t", $nc ) == -1 ) {
711 0         0 $interpolated = 1;
712             }
713             }
714              
715 1037         2291 $str .= $c;
716             }
717             }
718              
719 171 100       864 my $interpolate = $op eq 'qq' ? 1 :
    50          
    50          
    50          
720             $op eq 'q' ? 0 :
721             $op eq 'qw' ? 0 :
722             $quote_start eq "'" ? 0 :
723             1;
724 171 50       995 return ( $quote_start,
725             [ $pos, $is_regex ? T_PATTERN : T_QUOTE,
726             0, $interpolate, \$str, undef, undef, $interpolated ] );
727             }
728              
729             sub _prepare_sublex {
730 171     171   483 my( $self, $op, $quote_start ) = @_;
731 171         474 my( $quote, $token ) = _find_end( $self, $op, $quote_start );
732              
733             # oops, found fat comma: not a quote-like operator
734 171 50       633 return $token if $token->[O_TYPE] == T_STRING;
735              
736 171 50 33     1388 if( my $op_descr = $regex_flags{$op} ) {
    50          
    50          
    50          
737             # scan second part of substitution/transliteration
738 0 0 0     0 if( $op eq 's' || $op eq 'tr' || $op eq 'y' ) {
      0        
739 0 0       0 my $quote_char = $quote_end{$quote} ? undef : $quote;
740 0         0 my( undef, $rest ) = _find_end( $self, $op, $quote_char );
741 0         0 $token->[O_RX_SECOND_HALF] = $rest;
742             }
743              
744             # scan regexp flags
745 0         0 $token->[O_VALUE] = $op_descr->[0];
746 0         0 my $fl_str = $op_descr->[1];
747 0         0 local $_ = $self->buffer;
748              
749 0         0 my $flags = 0;
750 0   0     0 while( length( $$_ )
751             and ( my $idx = index( $fl_str, substr( $$_, 0, 1 ) ) ) >= 0 ) {
752 0         0 substr $$_, 0, 1, '';
753 0         0 $flags |= $op_descr->[$idx + 2];
754             }
755 0         0 $token->[O_RX_FLAGS] = $flags;
756             } elsif( $op eq 'qx' || $op eq "`" ) {
757 0         0 $token->[O_VALUE] = OP_QL_QX;
758             } elsif( $op eq 'qw' ) {
759 0         0 $token->[O_VALUE] = OP_QL_QW;
760             } elsif( $op eq '<' ) {
761 0         0 $token->[O_VALUE] = OP_QL_LT;
762             }
763              
764 171         559 return $token;
765             }
766              
767             sub _prepare_sublex_heredoc {
768 0     0   0 my( $self ) = @_;
769 0         0 my( $quote, $str, $end ) = ( '"', '' );
770              
771 0         0 local $_ = $self->buffer;
772 0         0 my $pos = $self->{pos};
773              
774 0 0       0 if( $$_ =~ s/^[ \t]*(['"`])// ) {
775             # << "EOT", << 'EOT', << `EOT`
776 0         0 $quote = $1;
777              
778 0         0 while( $$_ =~ s/^(.*?)(\\)?($quote)// ) {
779 0         0 $end .= $1;
780 0 0       0 if( !$2 ) {
781 0         0 last;
782             } else {
783 0         0 $end .= $quote;
784             }
785             }
786             } else {
787             # <<\EOT, <
788 0 0       0 if( $$_ =~ s/\\// ) {
789 0         0 $quote = "'";
790             }
791              
792 0         0 $$_ =~ s/^(\w*)//;
793 0 0       0 warn "Deprecated" unless $1;
794 0         0 $end = $1;
795             }
796 0         0 $end .= "\n";
797              
798 0   0     0 my $lex = $self->_heredoc_lexer || $self;
799 0         0 my $finished = 0;
800 0 0       0 if( !$lex->stream ) {
801 0         0 $_ = $lex->buffer;
802 0 0       0 if( $$_ =~ s/(.*)^$end//m ) {
803 0         0 $str .= $1;
804 0         0 $finished = 1;
805             }
806             } else {
807             # if the lexer reads from a stream, it buffers at most one line,
808             # so by not using the buffer we skip the rest of the line
809 0         0 my $stream = $lex->stream;
810 0         0 while( defined( my $line = readline $stream ) ) {
811 0 0       0 if( $line eq $end ) {
812 0         0 $finished = 1;
813 0         0 last;
814             }
815 0         0 $str .= $line;
816             }
817             }
818              
819 0 0       0 Carp::confess "EOF while looking for terminator '$end'" unless $finished;
820              
821 0 0       0 return [ $pos, T_QUOTE, $quote eq "`" ? OP_QL_QX : 0, $quote ne "'", \$str ];
822             }
823              
824             sub lex {
825 6874     6874 0 25969 my( $self, $expect ) = ( @_, X_NOTHING );
826              
827 6874 100       7630 return pop @{$self->tokens} if @{$self->tokens};
  4700         27305  
  6874         16272  
828              
829             # skip blanks and comments
830 2174         12211 _skip_space( $self );
831              
832 2174         5863 local $_ = $self->buffer;
833 2174 100       17446 return [ $self->{pos}, T_EOF, '' ] unless length $$_;
834              
835             # numbers
836 2110 100       11185 $$_ =~ /^\d|^\.\d/ and return $self->lex_number;
837             # quote and quote-like operators
838 1863 50       4859 $$_ =~ s/^(q|qq|qx|qw|m|qr|s|tr|y)(?=\W)//x and
839             return _prepare_sublex( $self, $1, undef );
840             # 'x' operator special case
841 1863 50 33     4657 $$_ =~ /^x[0-9]/ && $expect == X_OPERATOR and do {
842 0         0 $$_ =~ s/^.//;
843 0         0 return [ $self->{pos}, T_SSTAR, 'x' ];
844             };
845             # anything that can start with alphabetic character: package name,
846             # label, identifier, fully qualified identifier, keyword, named
847             # operator
848 1863 100       6626 $$_ =~ s/^(::)?(\w+)//x and do {
849 321   50     2005 my $ids = ( $1 || '' ) . $2;
850 321 50       927 my $fqual = $1 ? 1 : 0;
851 321         899 my $no_space = $$_ !~ /^[ \t\r\n]/;
852              
853 321         736 my $op = $ops{$ids};
854 321 100 66     1867 my $kw = $op || $fqual ? undef : $Language::P::Keywords::KEYWORDS{$ids};
855 321 100       1088 my $type = $fqual ? T_FQ_ID :
    100          
    50          
856             $op ? -1 :
857             $kw ? $kw :
858             T_ID;
859              
860 321 50 33     1653 if( $no_space && ( $$_ =~ /^::/
      66        
861             || ( ( $type == T_ID || $type == T_FQ_ID )
862             && $$_ =~ /^'\w/ ) ) ) {
863 0         0 while( $$_ =~ s/^::(\w*)|^\'(\w+)// ) {
864 0 0       0 $ids .= '::' . ( defined $1 ? $1 : $2 );
865             }
866 0 0       0 if( $ids =~ s/::$// ) {
867             # warn for nonexistent package
868             }
869 0         0 $op = undef;
870 0         0 $type = T_FQ_ID;
871             }
872             # force subroutine call
873 321 100 100     1311 if( $no_space && $type == T_ID && $$_ =~ /^\(/ ) {
      100        
874 37         73 $type = T_SUB_ID;
875             }
876              
877             # look ahead for fat comma, save the original value for __LINE__
878 321         1034 my $line = $self->line;
879 321         1776 my $pos = $self->{pos};
880 321         624 _skip_space( $self );
881 321 50 66     9565 if( $$_ =~ /^=>/ ) {
    100 100        
882             # fully qualified name (foo::moo) is quoted only if not declared
883 0 0 0     0 if( $type == T_FQ_ID
884             && $self->symbol_table->get_symbol( $ids, '*' ) ) {
885 0         0 return [ $pos, T_ID, $ids, $type ];
886             } else {
887 0         0 return [ $pos, T_STRING, $ids ];
888             }
889             } elsif( $expect == X_STATE && $type != T_FQ_ID
890             && $$_ =~ s/^:(?!:)// ) {
891 7         38 return [ $pos, T_LABEL, $ids ];
892             }
893              
894 314 50 66     946 if( $type == T_ID && $ids =~ /^__/ ) {
895 0 0       0 if( $ids eq '__FILE__' ) {
    0          
    0          
896 0         0 return [ $pos, T_STRING, $self->file ];
897             } elsif( $ids eq '__LINE__' ) {
898 0         0 return [ $pos, T_NUMBER, $line, NUM_INTEGER ];
899             } elsif( $ids eq '__PACKAGE__' ) {
900 0         0 return [ $pos, T_PACKAGE, '' ];
901             }
902             }
903              
904 314 100       620 if( $op ) {
905             # 'x' is an operator only when we expect it
906 2 50 33     10 if( $op == T_SSTAR && $expect != X_OPERATOR ) {
907 0         0 return [ $pos, T_ID, $ids, T_ID ];
908             }
909              
910 2         8 return [ $pos, $op, $ids ];
911             }
912 312         1442 return [ $pos, T_ID, $ids, $type ];
913             };
914 1542 100       4583 $$_ =~ s/^(["'`])//x and return _prepare_sublex( $self, $1, $1 );
915             # < when not operator (<> glob, <> file read, << here doc)
916 1371 50 66     3646 $$_ =~ /^
917 0         0 $$_ =~ s/^(<<|<)//x;
918              
919 0 0       0 if( $1 eq '<' ) {
    0          
920 0         0 return _prepare_sublex( $self, '<', '<' );
921             } elsif( $1 eq '<<' ) {
922 0         0 return _prepare_sublex_heredoc( $self );
923             }
924             };
925             # multi char operators
926 1371 100       4891 $$_ =~ s/^(<=|>=|==|!=|=>|->
927             |=~|!~
928             |\.\.|\.\.\.
929             |\+\+|\-\-
930             |\+=|\-=|\*=|\/=
931             |\&\&|\|\|)//x and return [ $self->{pos}, $ops{$1}, $1 ];
932 1295 100       3395 $$_ =~ s/^\$//x and do {
933 287 100       758 if( $$_ =~ /^\#/ ) {
934 1         6 my $id = $self->lex_identifier( 0 );
935              
936 1 50       4 if( $id ) {
937 0         0 $self->unlex( $id );
938             } else {
939 1         5 $$_ =~ s/^\#//x;
940 1         5 return [ $self->{pos}, $ops{'$#'}, '$#' ];
941             }
942             }
943 286         1450 return [ $self->{pos}, $ops{'$'}, '$' ];
944             };
945             # brackets (block, subscripting, anonymous ref constructors)
946 1008 100       2615 $$_ =~ s/^([{}\[\]])// and do {
947 165         396 my $brack = $1;
948              
949 165 100 100     854 if( $brack eq '[' || $brack eq '{' ) {
950 81         165 ++$self->{brackets};
951             } else {
952 84 50 66     316 if( $brack eq '}'
  78   33     602  
953             && @{$self->{pending_brackets}}
954             && $self->{pending_brackets}[-1] == $self->{brackets} ) {
955 0         0 pop @{$self->{pending_brackets}};
  0         0  
956 0         0 --$self->{brackets};
957              
958 0         0 return $self->lex( $expect );
959             }
960              
961 84         182 --$self->{brackets};
962              
963 84 100 100     441 if( $self->{brackets} == 0 && $self->quote ) {
964 3         25 _quoted_code_lookahead( $self );
965             }
966             }
967              
968             # disambiguate start of block from anonymous hash
969 165 100       786 if( $brack eq '{' ) {
970 78 50       386 if( $expect == X_TERM ) {
    100          
    100          
971 0         0 return [ $self->{pos}, T_OPHASH, '{' ];
972             } elsif( $expect == X_OPERATOR ) {
973             # autoquote literal strings in hash subscripts
974 17 50       74 if( $$_ =~ s/^[ \t]*([[:alpha:]_]+)[ \t]*\}// ) {
975 0         0 $self->unlex( [ $self->{pos}, T_CLBRK, '}' ] );
976 0         0 $self->unlex( [ $self->{pos}, T_STRING, $1 ] );
977             }
978             } elsif( $expect != X_BLOCK ) {
979             # try to guess if it is a block or anonymous hash
980 11         48 $self->_skip_space;
981              
982 11 50       43 if( $$_ =~ /^}/ ) {
983 0         0 return [ $self->{pos}, T_OPHASH, '{' ];
984             }
985              
986             # treat ' =>', ' ,/=>' lookahead
987             # as indicators of anonymous hash
988 11 50       93 if( $$_ =~ /^([\w"'`])/ ) {
989 11         33 my $first = $1;
990              
991             # can only be a string literal, quote like operator
992             # or identifier
993 11         59 my $next = $self->peek( X_NOTHING );
994              
995 11         39 $self->_skip_space;
996 11 50 33     106 if( $$_ =~ /^=>/
      33        
997             || ( $$_ =~ /^,/ && $next->[O_TYPE] != T_ID ) ) {
998 0         0 return [ $self->{pos}, T_OPHASH, '{' ];
999             }
1000             }
1001             }
1002             }
1003              
1004 165         837 return [ $self->{pos}, $ops{$brack}, $brack ];
1005             };
1006             # / (either regex start or division operator)
1007 843 50       1904 $$_ =~ s/^\///x and do {
1008 0 0 0     0 if( $expect == X_TERM || $expect == X_STATE ) {
1009 0         0 return _prepare_sublex( $self, 'm', '/' );
1010             } else {
1011 0         0 return [ $self->{pos}, T_SLASH, '/' ];
1012             }
1013             };
1014             # filetest operators
1015 843 50       9927 $$_ =~ s/^-([rwxoRWXOezsfdlpSugkbctTBMMAC])(?=\W)// and do {
1016 0         0 my $op = $1;
1017 0 0       0 if( $$_ =~ /^[ \t]*=>/ ) {
1018 0         0 $self->unlex( [ 'STRING', $1 ] );
1019 0         0 return [ $self->{pos}, T_MINUS, '-' ];
1020             }
1021              
1022 0         0 return [ $self->{pos}, T_FILETEST, $op, $filetest{$op} ];
1023             };
1024             # single char operators
1025 843 50       7076 $$_ =~ s/^([:;,()\?<>!~=\/\\\+\-\.\|^\*%@&])//x and return [ $self->{pos}, $ops{$1}, $1 ];
1026              
1027 0         0 die "Lexer error: '$$_'";
1028             }
1029              
1030             sub _fill_buffer {
1031 615     615   835 my( $self ) = @_;
1032 615         1834 my $stream = $self->stream;
1033 615 50       3322 return unless $stream;
1034 615         1526 my $buffer = $self->buffer;
1035 615         3980 my $l = readline $stream;
1036              
1037 615 100       1637 if( defined $l ) {
1038 551         1474 $$buffer .= $l;
1039             }
1040             }
1041              
1042             1;