| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package Language::P::Parser; | 
| 2 |  |  |  |  |  |  |  | 
| 3 | 86 |  |  | 86 |  | 2210711 | use strict; | 
|  | 86 |  |  |  |  | 1134 |  | 
|  | 86 |  |  |  |  | 3674 |  | 
| 4 | 86 |  |  | 86 |  | 481 | use warnings; | 
|  | 86 |  |  |  |  | 179 |  | 
|  | 86 |  |  |  |  | 3100 |  | 
| 5 | 86 |  |  | 86 |  | 555 | use base qw(Class::Accessor::Fast); | 
|  | 86 |  |  |  |  | 159 |  | 
|  | 86 |  |  |  |  | 91328 |  | 
| 6 |  |  |  |  |  |  |  | 
| 7 | 86 |  |  | 86 |  | 382840 | use Language::P::Lexer qw(:all); | 
|  | 86 |  |  |  |  | 334 |  | 
|  | 86 |  |  |  |  | 57002 |  | 
| 8 | 86 |  |  | 86 |  | 1251 | use Language::P::ParseTree qw(:all); | 
|  | 86 |  |  |  |  | 364 |  | 
|  | 86 |  |  |  |  | 102595 |  | 
| 9 | 86 |  |  | 86 |  | 78873 | use Language::P::Parser::Regex; | 
|  | 86 |  |  |  |  | 288 |  | 
|  | 86 |  |  |  |  | 811 |  | 
| 10 | 86 |  |  | 86 |  | 57343 | use Language::P::Parser::Lexicals; | 
|  | 86 |  |  |  |  | 281 |  | 
|  | 86 |  |  |  |  | 697 |  | 
| 11 | 86 |  |  | 86 |  | 3137 | use Language::P::Keywords; | 
|  | 86 |  |  |  |  | 180 |  | 
|  | 86 |  |  |  |  | 35962 |  | 
| 12 |  |  |  |  |  |  |  | 
| 13 |  |  |  |  |  |  | __PACKAGE__->mk_ro_accessors( qw(lexer generator runtime) ); | 
| 14 |  |  |  |  |  |  | __PACKAGE__->mk_accessors( qw(_package _lexicals _pending_lexicals | 
| 15 |  |  |  |  |  |  | _in_declaration _lexical_state | 
| 16 |  |  |  |  |  |  | _options) ); | 
| 17 |  |  |  |  |  |  |  | 
| 18 | 7 |  |  | 7 |  | 64 | sub _lexical_sub_state { $_[0]->{_lexical_state}->[-1]->{sub} } | 
| 19 |  |  |  |  |  |  |  | 
| 20 |  |  |  |  |  |  | use constant | 
| 21 | 86 |  |  |  |  | 1378942 | { PREC_HIGHEST       => 0, | 
| 22 |  |  |  |  |  |  | PREC_NAMED_UNOP    => 10, | 
| 23 |  |  |  |  |  |  | PREC_TERNARY       => 18, | 
| 24 |  |  |  |  |  |  | PREC_TERNARY_COLON => 40, | 
| 25 |  |  |  |  |  |  | PREC_LISTEXPR      => 19, | 
| 26 |  |  |  |  |  |  | PREC_COMMA         => 20, | 
| 27 |  |  |  |  |  |  | PREC_LISTOP        => 21, | 
| 28 |  |  |  |  |  |  | PREC_LOWEST        => 50, | 
| 29 |  |  |  |  |  |  |  | 
| 30 |  |  |  |  |  |  | BLOCK_OPEN_SCOPE      => 1, | 
| 31 |  |  |  |  |  |  | BLOCK_IMPLICIT_RETURN => 2, | 
| 32 |  |  |  |  |  |  | BLOCK_BARE            => 4, | 
| 33 |  |  |  |  |  |  |  | 
| 34 |  |  |  |  |  |  | ASSOC_LEFT         => 1, | 
| 35 |  |  |  |  |  |  | ASSOC_RIGHT        => 2, | 
| 36 |  |  |  |  |  |  | ASSOC_NON          => 3, | 
| 37 | 86 |  |  | 86 |  | 575 | }; | 
|  | 86 |  |  |  |  | 188 |  | 
| 38 |  |  |  |  |  |  |  | 
| 39 |  |  |  |  |  |  | my %token_to_sigil = | 
| 40 |  |  |  |  |  |  | ( T_DOLLAR()    => VALUE_SCALAR, | 
| 41 |  |  |  |  |  |  | T_AT()        => VALUE_ARRAY, | 
| 42 |  |  |  |  |  |  | T_PERCENT()   => VALUE_HASH, | 
| 43 |  |  |  |  |  |  | T_STAR()      => VALUE_GLOB, | 
| 44 |  |  |  |  |  |  | T_AMPERSAND() => VALUE_SUB, | 
| 45 |  |  |  |  |  |  | T_ARYLEN()    => VALUE_ARRAY_LENGTH, | 
| 46 |  |  |  |  |  |  | ); | 
| 47 |  |  |  |  |  |  |  | 
| 48 |  |  |  |  |  |  | my %declaration_to_flags = | 
| 49 |  |  |  |  |  |  | ( OP_MY()       => DECLARATION_MY, | 
| 50 |  |  |  |  |  |  | OP_OUR()      => DECLARATION_OUR, | 
| 51 |  |  |  |  |  |  | OP_STATE()    => DECLARATION_STATE, | 
| 52 |  |  |  |  |  |  | ); | 
| 53 |  |  |  |  |  |  |  | 
| 54 |  |  |  |  |  |  | my %prec_assoc_bin = | 
| 55 |  |  |  |  |  |  | ( # T_ARROW()       => [ 2,  ASSOC_LEFT ], | 
| 56 |  |  |  |  |  |  | T_POWER()       => [ 4,  ASSOC_RIGHT, OP_POWER ], | 
| 57 |  |  |  |  |  |  | T_MATCH()       => [ 6,  ASSOC_LEFT,  OP_MATCH ], | 
| 58 |  |  |  |  |  |  | T_NOTMATCH()    => [ 6,  ASSOC_LEFT,  OP_NOT_MATCH ], | 
| 59 |  |  |  |  |  |  | T_STAR()        => [ 7,  ASSOC_LEFT,  OP_MULTIPLY ], | 
| 60 |  |  |  |  |  |  | T_SLASH()       => [ 7,  ASSOC_LEFT,  OP_DIVIDE ], | 
| 61 |  |  |  |  |  |  | T_PERCENT()     => [ 7,  ASSOC_LEFT,  OP_MODULUS ], | 
| 62 |  |  |  |  |  |  | T_SSTAR()       => [ 7,  ASSOC_LEFT,  OP_REPEAT ], | 
| 63 |  |  |  |  |  |  | T_PLUS()        => [ 8,  ASSOC_LEFT,  OP_ADD ], | 
| 64 |  |  |  |  |  |  | T_MINUS()       => [ 8,  ASSOC_LEFT,  OP_SUBTRACT ], | 
| 65 |  |  |  |  |  |  | T_DOT()         => [ 8,  ASSOC_LEFT,  OP_CONCATENATE ], | 
| 66 |  |  |  |  |  |  | T_OPAN()        => [ 11, ASSOC_NON,   OP_NUM_LT ], | 
| 67 |  |  |  |  |  |  | T_CLAN()        => [ 11, ASSOC_NON,   OP_NUM_GT ], | 
| 68 |  |  |  |  |  |  | T_LESSEQUAL()   => [ 11, ASSOC_NON,   OP_NUM_LE ], | 
| 69 |  |  |  |  |  |  | T_GREATEQUAL()  => [ 11, ASSOC_NON,   OP_NUM_GE ], | 
| 70 |  |  |  |  |  |  | T_SLESS()       => [ 11, ASSOC_NON,   OP_STR_LT ], | 
| 71 |  |  |  |  |  |  | T_SGREAT()      => [ 11, ASSOC_NON,   OP_STR_GT ], | 
| 72 |  |  |  |  |  |  | T_SLESSEQUAL()  => [ 11, ASSOC_NON,   OP_STR_LE ], | 
| 73 |  |  |  |  |  |  | T_SGREATEQUAL() => [ 11, ASSOC_NON,   OP_STR_GE ], | 
| 74 |  |  |  |  |  |  | T_EQUALEQUAL()  => [ 12, ASSOC_NON,   OP_NUM_EQ ], | 
| 75 |  |  |  |  |  |  | T_NOTEQUAL()    => [ 12, ASSOC_NON,   OP_NUM_NE ], | 
| 76 |  |  |  |  |  |  | T_CMP()         => [ 12, ASSOC_NON,   OP_NUM_CMP ], | 
| 77 |  |  |  |  |  |  | T_SEQUALEQUAL() => [ 12, ASSOC_NON,   OP_STR_EQ ], | 
| 78 |  |  |  |  |  |  | T_SNOTEQUAL()   => [ 12, ASSOC_NON,   OP_STR_NE ], | 
| 79 |  |  |  |  |  |  | T_SCMP()        => [ 12, ASSOC_NON,   OP_STR_CMP ], | 
| 80 |  |  |  |  |  |  | T_AMPERSAND()   => [ 13, ASSOC_LEFT,  OP_BIT_AND ], | 
| 81 |  |  |  |  |  |  | T_OR()          => [ 14, ASSOC_LEFT,  OP_BIT_OR ], | 
| 82 |  |  |  |  |  |  | T_XOR()         => [ 14, ASSOC_LEFT,  OP_BIT_XOR ], | 
| 83 |  |  |  |  |  |  | T_ANDAND()      => [ 15, ASSOC_LEFT,  OP_LOG_AND ], | 
| 84 |  |  |  |  |  |  | T_OROR()        => [ 16, ASSOC_LEFT,  OP_LOG_OR ], | 
| 85 |  |  |  |  |  |  | T_DOTDOT()      => [ 17, ASSOC_NON,   OP_DOT_DOT ], | 
| 86 |  |  |  |  |  |  | T_DOTDOTDOT()   => [ 17, ASSOC_NON,   OP_DOT_DOT_DOT ], | 
| 87 |  |  |  |  |  |  | T_INTERR()      => [ 18, ASSOC_RIGHT ], # ternary | 
| 88 |  |  |  |  |  |  | T_EQUAL()       => [ 19, ASSOC_RIGHT, OP_ASSIGN ], | 
| 89 |  |  |  |  |  |  | T_PLUSEQUAL()   => [ 19, ASSOC_RIGHT, OP_ADD_ASSIGN ], | 
| 90 |  |  |  |  |  |  | T_MINUSEQUAL()  => [ 19, ASSOC_RIGHT, OP_SUBTRACT_ASSIGN ], | 
| 91 |  |  |  |  |  |  | T_STAREQUAL()   => [ 19, ASSOC_RIGHT, OP_MULTIPLY_ASSIGN ], | 
| 92 |  |  |  |  |  |  | T_SLASHEQUAL()  => [ 19, ASSOC_RIGHT, OP_DIVIDE_ASSIGN ], | 
| 93 |  |  |  |  |  |  | T_COMMA()       => [ 20, ASSOC_LEFT ], | 
| 94 |  |  |  |  |  |  | # 21, list ops | 
| 95 |  |  |  |  |  |  | T_ANDANDLOW()   => [ 23, ASSOC_LEFT,  OP_LOG_AND ], | 
| 96 |  |  |  |  |  |  | T_ORORLOW()     => [ 24, ASSOC_LEFT,  OP_LOG_OR ], | 
| 97 |  |  |  |  |  |  | T_XORLOW()      => [ 24, ASSOC_LEFT,  OP_LOG_XOR ], | 
| 98 |  |  |  |  |  |  | T_COLON()       => [ 40, ASSOC_RIGHT ], # ternary, must be lowest, | 
| 99 |  |  |  |  |  |  | ); | 
| 100 |  |  |  |  |  |  |  | 
| 101 |  |  |  |  |  |  | my %prec_assoc_un = | 
| 102 |  |  |  |  |  |  | ( T_PLUSPLUS()    => [ 3,  ASSOC_NON,   OP_PREINC ], | 
| 103 |  |  |  |  |  |  | T_MINUSMINUS()  => [ 3,  ASSOC_NON,   OP_PREDEC ], | 
| 104 |  |  |  |  |  |  | T_PLUS()        => [ 5,  ASSOC_RIGHT, OP_PLUS ], | 
| 105 |  |  |  |  |  |  | T_MINUS()       => [ 5,  ASSOC_RIGHT, OP_MINUS ], | 
| 106 |  |  |  |  |  |  | T_NOT()         => [ 5,  ASSOC_RIGHT, OP_LOG_NOT ], | 
| 107 |  |  |  |  |  |  | T_TILDE()       => [ 5,  ASSOC_RIGHT, OP_BIT_NOT ], | 
| 108 |  |  |  |  |  |  | T_BACKSLASH()   => [ 5,  ASSOC_RIGHT, OP_REFERENCE ], | 
| 109 |  |  |  |  |  |  | T_NOTLOW()      => [ 22, ASSOC_RIGHT, OP_LOG_NOT ], | 
| 110 |  |  |  |  |  |  | ); | 
| 111 |  |  |  |  |  |  |  | 
| 112 |  |  |  |  |  |  | my %dereference_type = | 
| 113 |  |  |  |  |  |  | ( VALUE_SCALAR()       => OP_DEREFERENCE_SCALAR, | 
| 114 |  |  |  |  |  |  | VALUE_ARRAY()        => OP_DEREFERENCE_ARRAY, | 
| 115 |  |  |  |  |  |  | VALUE_HASH()         => OP_DEREFERENCE_HASH, | 
| 116 |  |  |  |  |  |  | VALUE_SUB()          => OP_DEREFERENCE_SUB, | 
| 117 |  |  |  |  |  |  | VALUE_GLOB()         => OP_DEREFERENCE_GLOB, | 
| 118 |  |  |  |  |  |  | VALUE_ARRAY_LENGTH() => OP_ARRAY_LENGTH, | 
| 119 |  |  |  |  |  |  | ); | 
| 120 |  |  |  |  |  |  |  | 
| 121 |  |  |  |  |  |  | sub new { | 
| 122 | 61 |  |  | 61 | 1 | 634 | my( $class, $args ) = @_; | 
| 123 | 61 |  |  |  |  | 477 | my $self = $class->SUPER::new( $args ); | 
| 124 |  |  |  |  |  |  |  | 
| 125 | 61 | 50 |  |  |  | 826 | $self->_options( {} ) unless $self->_options; | 
| 126 |  |  |  |  |  |  |  | 
| 127 | 61 |  |  |  |  | 1115 | return $self; | 
| 128 |  |  |  |  |  |  | } | 
| 129 |  |  |  |  |  |  |  | 
| 130 |  |  |  |  |  |  | sub set_option { | 
| 131 | 0 |  |  | 0 | 0 | 0 | my( $self, $option, $value ) = @_; | 
| 132 |  |  |  |  |  |  |  | 
| 133 | 0 | 0 |  |  |  | 0 | if( $option eq 'dump-parse-tree' ) { | 
| 134 | 0 |  |  |  |  | 0 | $self->_options->{$option} = 1; | 
| 135 |  |  |  |  |  |  | } | 
| 136 |  |  |  |  |  |  |  | 
| 137 | 0 |  |  |  |  | 0 | return 0; | 
| 138 |  |  |  |  |  |  | } | 
| 139 |  |  |  |  |  |  |  | 
| 140 |  |  |  |  |  |  | sub parse_string { | 
| 141 | 46 |  |  | 46 | 0 | 443 | my( $self, $string, $package ) = @_; | 
| 142 |  |  |  |  |  |  |  | 
| 143 | 46 |  |  | 20 |  | 1734 | open my $fh, '<', \$string; | 
|  | 20 |  |  |  |  | 243 |  | 
|  | 20 |  |  |  |  | 46 |  | 
|  | 20 |  |  |  |  | 184 |  | 
| 144 |  |  |  |  |  |  |  | 
| 145 | 46 |  |  |  |  | 31021 | $self->_package( $package ); | 
| 146 | 46 |  |  |  |  | 613 | $self->parse_stream( $fh, '' ); | 
| 147 |  |  |  |  |  |  | } | 
| 148 |  |  |  |  |  |  |  | 
| 149 |  |  |  |  |  |  | sub parse_file { | 
| 150 | 15 |  |  | 15 | 0 | 210 | my( $self, $file ) = @_; | 
| 151 |  |  |  |  |  |  |  | 
| 152 | 15 | 50 |  |  |  | 1188 | open my $fh, '<', $file or die "open '$file': $!"; | 
| 153 |  |  |  |  |  |  |  | 
| 154 | 15 |  |  |  |  | 107 | $self->_package( 'main' ); | 
| 155 | 15 |  |  |  |  | 156 | $self->parse_stream( $fh, $file ); | 
| 156 |  |  |  |  |  |  | } | 
| 157 |  |  |  |  |  |  |  | 
| 158 |  |  |  |  |  |  | sub parse_stream { | 
| 159 | 61 |  |  | 61 | 0 | 166 | my( $self, $stream, $filename ) = @_; | 
| 160 |  |  |  |  |  |  |  | 
| 161 | 61 |  |  |  |  | 436 | $self->{lexer} = Language::P::Lexer->new | 
| 162 |  |  |  |  |  |  | ( { stream       => $stream, | 
| 163 |  |  |  |  |  |  | file         => $filename, | 
| 164 |  |  |  |  |  |  | symbol_table => $self->runtime->symbol_table, | 
| 165 |  |  |  |  |  |  | } ); | 
| 166 | 61 |  |  |  |  | 274 | $self->{_lexical_state} = []; | 
| 167 | 61 |  |  |  |  | 545 | $self->_parse; | 
| 168 |  |  |  |  |  |  | } | 
| 169 |  |  |  |  |  |  |  | 
| 170 |  |  |  |  |  |  | sub _qualify { | 
| 171 | 387 |  |  | 387 |  | 639 | my( $self, $name, $type ) = @_; | 
| 172 | 387 | 50 |  |  |  | 1096 | if( $type == T_FQ_ID ) { | 
| 173 | 0 |  |  |  |  | 0 | ( my $normalized = $name ) =~ s/^(?:::)?(?:main::)?//; | 
| 174 | 0 |  |  |  |  | 0 | return $normalized; | 
| 175 |  |  |  |  |  |  | } | 
| 176 | 387 | 50 |  |  |  | 1095 | my $prefix = $self->_package eq 'main' ? '' : $self->_package . '::'; | 
| 177 | 387 |  |  |  |  | 5137 | return $prefix . $name; | 
| 178 |  |  |  |  |  |  | } | 
| 179 |  |  |  |  |  |  |  | 
| 180 |  |  |  |  |  |  | sub _parse { | 
| 181 | 61 |  |  | 61 |  | 147 | my( $self ) = @_; | 
| 182 |  |  |  |  |  |  |  | 
| 183 | 61 |  |  |  |  | 124 | my $dumper; | 
| 184 | 61 | 50 | 33 |  |  | 251 | if(    $self->_options->{'dump-parse-tree'} | 
| 185 |  |  |  |  |  |  | && -f $self->lexer->file ) { | 
| 186 | 0 |  |  |  |  | 0 | require Language::P::ParseTree::DumpYAML; | 
| 187 | 0 |  |  |  |  | 0 | ( my $outfile = $self->lexer->file ) =~ s/(\.\w+)?$/.pt/; | 
| 188 | 0 |  | 0 |  |  | 0 | open my $out, '>', $outfile || die "Can't open '$outfile': $!"; | 
| 189 | 0 |  |  |  |  | 0 | my $dumpyml = Language::P::ParseTree::DumpYAML->new; | 
| 190 |  |  |  |  |  |  | $dumper = sub { | 
| 191 | 0 |  |  | 0 |  | 0 | print $out $dumpyml->dump( $_[0] ); | 
| 192 | 0 |  |  |  |  | 0 | }; | 
| 193 |  |  |  |  |  |  | } | 
| 194 |  |  |  |  |  |  |  | 
| 195 | 61 |  |  |  |  | 774 | $self->_pending_lexicals( [] ); | 
| 196 | 61 |  |  |  |  | 554 | $self->_lexicals( undef ); | 
| 197 | 61 |  |  |  |  | 582 | $self->_enter_scope( 0, 1 ); # FIXME eval | 
| 198 |  |  |  |  |  |  |  | 
| 199 | 61 |  |  |  |  | 647 | $self->generator->start_code_generation( { file_name => $self->lexer->file, | 
| 200 |  |  |  |  |  |  | } ); | 
| 201 | 61 |  |  |  |  | 1119 | while( my $line = _parse_line( $self ) ) { | 
| 202 | 202 | 50 |  |  |  | 1218 | $dumper->( $line ) if $dumper; | 
| 203 | 202 |  |  |  |  | 671 | $self->generator->process( $line ); | 
| 204 |  |  |  |  |  |  | } | 
| 205 | 61 |  |  |  |  | 276 | $self->_leave_scope; | 
| 206 | 61 |  |  |  |  | 1196 | my $code = $self->generator->end_code_generation; | 
| 207 |  |  |  |  |  |  |  | 
| 208 | 61 |  |  |  |  | 610 | return $code; | 
| 209 |  |  |  |  |  |  | } | 
| 210 |  |  |  |  |  |  |  | 
| 211 |  |  |  |  |  |  | sub _enter_scope { | 
| 212 | 172 |  |  | 172 |  | 319 | my( $self, $is_sub, $top_level ) = @_; | 
| 213 |  |  |  |  |  |  |  | 
| 214 | 172 |  |  |  |  | 254 | push @{$self->{_lexical_state}}, { package  => $self->_package, | 
|  | 172 |  |  |  |  | 822 |  | 
| 215 |  |  |  |  |  |  | lexicals => $self->_lexicals, | 
| 216 |  |  |  |  |  |  | is_sub   => $is_sub, | 
| 217 |  |  |  |  |  |  | top_level=> $top_level, | 
| 218 |  |  |  |  |  |  | }; | 
| 219 | 172 | 100 | 100 |  |  | 2850 | if( $is_sub || $top_level ) { | 
|  | 90 | 50 |  |  |  | 313 |  | 
| 220 | 82 |  |  |  |  | 528 | $self->{_lexical_state}[-1]{sub} = { labels  => {}, | 
| 221 |  |  |  |  |  |  | jumps   => [], | 
| 222 |  |  |  |  |  |  | }; | 
| 223 |  |  |  |  |  |  | } elsif( @{$self->{_lexical_state}} > 1 ) { | 
| 224 | 90 |  |  |  |  | 328 | $self->{_lexical_state}[-1]{sub} = $self->{_lexical_state}[-2]{sub}; | 
| 225 |  |  |  |  |  |  | } | 
| 226 | 172 |  | 100 |  |  | 654 | $self->_lexicals( Language::P::Parser::Lexicals->new | 
| 227 |  |  |  |  |  |  | ( { outer         => $self->_lexicals, | 
| 228 |  |  |  |  |  |  | is_subroutine => $is_sub || 0, | 
| 229 |  |  |  |  |  |  | top_level     => $top_level, | 
| 230 |  |  |  |  |  |  | } ) ); | 
| 231 |  |  |  |  |  |  | } | 
| 232 |  |  |  |  |  |  |  | 
| 233 |  |  |  |  |  |  | sub _leave_scope { | 
| 234 | 172 |  |  | 172 |  | 306 | my( $self ) = @_; | 
| 235 |  |  |  |  |  |  |  | 
| 236 | 172 |  |  |  |  | 247 | my $state = pop @{$self->{_lexical_state}}; | 
|  | 172 |  |  |  |  | 441 |  | 
| 237 | 172 |  |  |  |  | 777 | $self->_package( $state->{package} ); | 
| 238 | 172 |  |  |  |  | 1235 | $self->_lexicals( $state->{lexicals} ); | 
| 239 | 172 | 100 | 100 |  |  | 2566 | _patch_gotos( $self, $state ) if $state->{is_sub} || $state->{top_level}; | 
| 240 |  |  |  |  |  |  | } | 
| 241 |  |  |  |  |  |  |  | 
| 242 |  |  |  |  |  |  | sub _patch_gotos { | 
| 243 | 82 |  |  | 82 |  | 1228 | my( $self, $state ) = @_; | 
| 244 | 82 |  |  |  |  | 281 | my $labels = $state->{sub}{labels}; | 
| 245 |  |  |  |  |  |  |  | 
| 246 | 82 |  |  |  |  | 139 | foreach my $goto ( @{$state->{sub}{jumps}} ) { | 
|  | 82 |  |  |  |  | 437 |  | 
| 247 | 3 | 50 |  |  |  | 13 | if( $labels->{$goto->left} ) { | 
| 248 | 3 |  |  |  |  | 28 | $goto->set_attribute( 'target', $labels->{$goto->left}, 1 ); | 
| 249 |  |  |  |  |  |  | } | 
| 250 |  |  |  |  |  |  | } | 
| 251 |  |  |  |  |  |  | } | 
| 252 |  |  |  |  |  |  |  | 
| 253 |  |  |  |  |  |  | sub _syntax_error { | 
| 254 | 0 |  |  | 0 |  | 0 | my( $self, $token ) = @_; | 
| 255 |  |  |  |  |  |  |  | 
| 256 | 0 |  |  |  |  | 0 | Carp::confess( sprintf "Unexpected token '%s' (%s) at %s:%d\n       ", | 
| 257 |  |  |  |  |  |  | $token->[O_VALUE], $token->[O_TYPE], | 
| 258 |  |  |  |  |  |  | $token->[O_POS][0], $token->[O_POS][1] ); | 
| 259 |  |  |  |  |  |  | } | 
| 260 |  |  |  |  |  |  |  | 
| 261 |  |  |  |  |  |  | sub _lex_token { | 
| 262 | 443 |  |  | 443 |  | 797 | my( $self, $type, $value, $expect ) = @_; | 
| 263 | 443 |  | 100 |  |  | 1252 | my $token = $self->lexer->lex( $expect || X_NOTHING ); | 
| 264 |  |  |  |  |  |  |  | 
| 265 | 443 | 100 | 33 |  |  | 2789 | return if !$value && !$type; | 
| 266 |  |  |  |  |  |  |  | 
| 267 | 433 | 50 | 33 |  |  | 2716 | if(    ( $type && $type != $token->[O_TYPE] ) | 
|  |  |  | 33 |  |  |  |  | 
|  |  |  | 33 |  |  |  |  | 
| 268 |  |  |  |  |  |  | || ( $value && $value eq $token->[O_VALUE] ) ) { | 
| 269 | 0 |  |  |  |  | 0 | _syntax_error( $self, $token ); | 
| 270 |  |  |  |  |  |  | } | 
| 271 |  |  |  |  |  |  |  | 
| 272 | 433 |  |  |  |  | 954 | return $token; | 
| 273 |  |  |  |  |  |  | } | 
| 274 |  |  |  |  |  |  |  | 
| 275 |  |  |  |  |  |  | sub _lex_semicolon { | 
| 276 | 339 |  |  | 339 |  | 484 | my( $self ) = @_; | 
| 277 | 339 |  |  |  |  | 985 | my $token = $self->lexer->lex; | 
| 278 |  |  |  |  |  |  |  | 
| 279 | 339 | 100 | 100 |  |  | 2920 | if( $token->[O_TYPE] == T_EOF || $token->[O_TYPE] == T_SEMICOLON ) { | 
|  |  | 50 |  |  |  |  |  | 
| 280 | 328 |  |  |  |  | 565 | return; | 
| 281 |  |  |  |  |  |  | } elsif( $token->[O_TYPE] == T_CLBRK ) { | 
| 282 | 11 |  |  |  |  | 40 | $self->lexer->unlex( $token ); | 
| 283 | 11 |  |  |  |  | 72 | return; | 
| 284 |  |  |  |  |  |  | } | 
| 285 |  |  |  |  |  |  |  | 
| 286 | 0 |  |  |  |  | 0 | _syntax_error( $self, $token ); | 
| 287 |  |  |  |  |  |  | } | 
| 288 |  |  |  |  |  |  |  | 
| 289 |  |  |  |  |  |  | my %special_sub = map { $_ => 1 } | 
| 290 |  |  |  |  |  |  | ( qw(AUTOLOAD DESTROY BEGIN UNITCHECK CHECK INIT END) ); | 
| 291 |  |  |  |  |  |  |  | 
| 292 |  |  |  |  |  |  | sub _parse_line { | 
| 293 | 396 |  |  | 396 |  | 1105 | my( $self ) = @_; | 
| 294 | 396 |  |  |  |  | 1159 | my $label = $self->lexer->peek( X_STATE ); | 
| 295 |  |  |  |  |  |  |  | 
| 296 | 396 | 100 |  |  |  | 1062 | if( $label->[O_TYPE] != T_LABEL ) { | 
| 297 | 389 |  |  |  |  | 1088 | return _parse_line_rest( $self, 1 ); | 
| 298 |  |  |  |  |  |  | } else { | 
| 299 | 7 |  |  |  |  | 46 | _lex_token( $self, T_LABEL ); | 
| 300 | 7 |  | 33 |  |  | 22 | my $statement =    _parse_line_rest( $self, 0 ) | 
| 301 |  |  |  |  |  |  | || Language::P::ParseTree::Empty->new; | 
| 302 |  |  |  |  |  |  |  | 
| 303 | 7 |  |  |  |  | 87 | $statement->set_attribute( 'label', $label->[O_VALUE] ); | 
| 304 | 7 |  | 66 |  |  | 31 | $self->_lexical_sub_state->{labels}{$label->[O_VALUE]} ||= $statement; | 
| 305 |  |  |  |  |  |  |  | 
| 306 | 7 |  |  |  |  | 26 | return $statement; | 
| 307 |  |  |  |  |  |  | } | 
| 308 |  |  |  |  |  |  | } | 
| 309 |  |  |  |  |  |  |  | 
| 310 |  |  |  |  |  |  | sub _parse_line_rest { | 
| 311 | 396 |  |  | 396 |  | 611 | my( $self, $no_empty ) = @_; | 
| 312 | 396 |  |  |  |  | 1262 | my $token = $self->lexer->peek( X_STATE ); | 
| 313 | 396 |  |  |  |  | 880 | my $tokidt = $token->[O_ID_TYPE]; | 
| 314 |  |  |  |  |  |  |  | 
| 315 | 396 | 50 | 100 |  |  | 3210 | if( $token->[O_TYPE] == T_SEMICOLON ) { | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 316 | 0 |  |  |  |  | 0 | _lex_semicolon( $self ); | 
| 317 |  |  |  |  |  |  |  | 
| 318 | 0 | 0 |  |  |  | 0 | return $no_empty ? _parse_line_rest( $self, 1 ) : undef; | 
| 319 |  |  |  |  |  |  | } elsif( $token->[O_TYPE] == T_OPBRK ) { | 
| 320 | 7 |  |  |  |  | 31 | _lex_token( $self, T_OPBRK ); | 
| 321 |  |  |  |  |  |  |  | 
| 322 | 7 |  |  |  |  | 42 | return _parse_block_rest( $self, BLOCK_OPEN_SCOPE|BLOCK_BARE ); | 
| 323 |  |  |  |  |  |  | } elsif( $token->[O_TYPE] == T_ID && is_keyword( $tokidt ) ) { | 
| 324 | 87 | 100 | 100 |  |  | 2021 | if( $tokidt == KEY_SUB ) { | 
|  |  | 100 | 100 |  |  |  |  | 
|  |  | 100 | 100 |  |  |  |  | 
|  |  | 100 | 66 |  |  |  |  | 
|  |  | 50 | 66 |  |  |  |  | 
|  |  | 50 | 66 |  |  |  |  | 
|  |  |  | 100 |  |  |  |  | 
|  |  |  | 100 |  |  |  |  | 
|  |  |  | 100 |  |  |  |  | 
|  |  |  | 66 |  |  |  |  | 
| 325 | 17 |  |  |  |  | 61 | return _parse_sub( $self, 1 | 2 ); | 
| 326 |  |  |  |  |  |  | } elsif( $tokidt == KEY_IF || $tokidt == KEY_UNLESS ) { | 
| 327 | 10 |  |  |  |  | 40 | return _parse_cond( $self ); | 
| 328 |  |  |  |  |  |  | } elsif( $tokidt == KEY_WHILE || $tokidt == KEY_UNTIL ) { | 
| 329 | 12 |  |  |  |  | 55 | return _parse_while( $self ); | 
| 330 |  |  |  |  |  |  | } elsif( $tokidt == KEY_FOR || $tokidt == KEY_FOREACH ) { | 
| 331 | 11 |  |  |  |  | 43 | return _parse_for( $self ); | 
| 332 |  |  |  |  |  |  | } elsif( $tokidt == KEY_PACKAGE ) { | 
| 333 | 0 |  |  |  |  | 0 | _lex_token( $self, T_ID ); | 
| 334 | 0 |  |  |  |  | 0 | my $id = $self->lexer->lex_identifier( 0 ); | 
| 335 | 0 |  |  |  |  | 0 | _lex_semicolon( $self ); | 
| 336 |  |  |  |  |  |  |  | 
| 337 | 0 |  |  |  |  | 0 | $self->_package( $id->[O_VALUE] ); | 
| 338 |  |  |  |  |  |  |  | 
| 339 | 0 |  |  |  |  | 0 | return Language::P::ParseTree::Package->new | 
| 340 |  |  |  |  |  |  | ( { name => $id->[O_VALUE], | 
| 341 |  |  |  |  |  |  | } ); | 
| 342 |  |  |  |  |  |  | } elsif(    $tokidt == OP_MY | 
| 343 |  |  |  |  |  |  | || $tokidt == OP_OUR | 
| 344 |  |  |  |  |  |  | || $tokidt == OP_STATE | 
| 345 |  |  |  |  |  |  | || $tokidt == OP_GOTO | 
| 346 |  |  |  |  |  |  | || $tokidt == OP_LAST | 
| 347 |  |  |  |  |  |  | || $tokidt == OP_NEXT | 
| 348 |  |  |  |  |  |  | || $tokidt == OP_REDO | 
| 349 |  |  |  |  |  |  | || $tokidt == KEY_LOCAL ) { | 
| 350 | 37 |  |  |  |  | 127 | return _parse_sideff( $self ); | 
| 351 |  |  |  |  |  |  | } | 
| 352 |  |  |  |  |  |  | } elsif( $special_sub{$token->[O_VALUE]} ) { | 
| 353 | 0 |  |  |  |  | 0 | return _parse_sub( $self, 1, 1 ); | 
| 354 |  |  |  |  |  |  | } else { | 
| 355 | 302 |  |  |  |  | 873 | return _parse_sideff( $self ); | 
| 356 |  |  |  |  |  |  | } | 
| 357 |  |  |  |  |  |  |  | 
| 358 | 0 |  |  |  |  | 0 | _syntax_error( $self, $token ); | 
| 359 |  |  |  |  |  |  | } | 
| 360 |  |  |  |  |  |  |  | 
| 361 |  |  |  |  |  |  | sub _add_pending_lexicals { | 
| 362 | 380 |  |  | 380 |  | 575 | my( $self ) = @_; | 
| 363 |  |  |  |  |  |  |  | 
| 364 |  |  |  |  |  |  | # FIXME our() is different | 
| 365 | 380 |  |  |  |  | 492 | foreach my $lexical ( @{$self->_pending_lexicals} ) { | 
|  | 380 |  |  |  |  | 1293 |  | 
| 366 | 11 |  |  |  |  | 82 | $self->_lexicals->add_lexical( $lexical ); | 
| 367 |  |  |  |  |  |  | } | 
| 368 |  |  |  |  |  |  |  | 
| 369 | 380 |  |  |  |  | 2895 | $self->_pending_lexicals( [] ); | 
| 370 |  |  |  |  |  |  | } | 
| 371 |  |  |  |  |  |  |  | 
| 372 |  |  |  |  |  |  | sub _parse_sub { | 
| 373 | 21 |  |  | 21 |  | 45 | my( $self, $flags, $no_sub_token ) = @_; | 
| 374 | 21 | 100 |  |  |  | 93 | _lex_token( $self, T_ID ) unless $no_sub_token; | 
| 375 | 21 |  |  |  |  | 76 | my $name = $self->lexer->lex_alphabetic_identifier( 0 ); | 
| 376 | 21 | 100 |  |  |  | 99 | my $fqname = $name ? _qualify( $self, $name->[O_VALUE], $name->[O_ID_TYPE] ) : undef; | 
| 377 |  |  |  |  |  |  |  | 
| 378 |  |  |  |  |  |  | # TODO prototypes | 
| 379 | 21 | 100 |  |  |  | 52 | if( $fqname ) { | 
| 380 | 17 | 50 |  |  |  | 63 | die "Syntax error: named sub '$fqname'" unless $flags & 1; | 
| 381 |  |  |  |  |  |  |  | 
| 382 | 17 |  |  |  |  | 56 | my $next = $self->lexer->lex( X_OPERATOR ); | 
| 383 |  |  |  |  |  |  |  | 
| 384 | 17 | 50 |  |  |  | 114 | if( $next->[O_TYPE] == T_SEMICOLON ) { | 
|  |  | 50 |  |  |  |  |  | 
| 385 | 0 |  |  |  |  | 0 | $self->generator->add_declaration( $fqname ); | 
| 386 |  |  |  |  |  |  |  | 
| 387 | 0 |  |  |  |  | 0 | return Language::P::ParseTree::SubroutineDeclaration->new | 
| 388 |  |  |  |  |  |  | ( { name => $fqname, | 
| 389 |  |  |  |  |  |  | } ); | 
| 390 |  |  |  |  |  |  | } elsif( $next->[O_TYPE] != T_OPBRK ) { | 
| 391 | 0 |  |  |  |  | 0 | _syntax_error( $self, $next ); | 
| 392 |  |  |  |  |  |  | } | 
| 393 |  |  |  |  |  |  | } else { | 
| 394 | 4 |  |  |  |  | 12 | _lex_token( $self, T_OPBRK ); | 
| 395 | 4 | 50 |  |  |  | 15 | die 'Syntax error: anonymous sub' unless $flags & 2; | 
| 396 |  |  |  |  |  |  | } | 
| 397 |  |  |  |  |  |  |  | 
| 398 | 21 |  |  |  |  | 71 | $self->_enter_scope( 1 ); | 
| 399 | 21 | 100 |  |  |  | 366 | my $sub = $fqname ? Language::P::ParseTree::NamedSubroutine->new | 
| 400 |  |  |  |  |  |  | ( { name     => $fqname, | 
| 401 |  |  |  |  |  |  | } ) : | 
| 402 |  |  |  |  |  |  | Language::P::ParseTree::AnonymousSubroutine->new; | 
| 403 |  |  |  |  |  |  | # add @_ to lexical scope | 
| 404 | 21 |  |  |  |  | 89 | $self->_lexicals->add_name( VALUE_ARRAY, '_' ); | 
| 405 |  |  |  |  |  |  |  | 
| 406 | 21 |  |  |  |  | 320 | my $block = _parse_block_rest( $self, BLOCK_IMPLICIT_RETURN ); | 
| 407 | 21 |  |  |  |  | 86 | $sub->{lines} = $block->{lines}; # FIXME encapsulation | 
| 408 | 21 |  |  |  |  | 80 | $sub->set_parent_for_all_childs; | 
| 409 | 21 |  |  |  |  | 90 | $self->_leave_scope; | 
| 410 |  |  |  |  |  |  |  | 
| 411 |  |  |  |  |  |  | # add a subroutine declaration, the generator might | 
| 412 |  |  |  |  |  |  | # not create it until later | 
| 413 | 21 | 100 |  |  |  | 90 | if( $fqname ) { | 
| 414 | 17 |  |  |  |  | 65 | $self->generator->add_declaration( $fqname ); | 
| 415 |  |  |  |  |  |  | } | 
| 416 |  |  |  |  |  |  |  | 
| 417 | 21 |  |  |  |  | 300 | return $sub; | 
| 418 |  |  |  |  |  |  | } | 
| 419 |  |  |  |  |  |  |  | 
| 420 |  |  |  |  |  |  | sub _parse_cond { | 
| 421 | 10 |  |  | 10 |  | 17 | my( $self ) = @_; | 
| 422 | 10 |  |  |  |  | 49 | my $cond = _lex_token( $self, T_ID ); | 
| 423 |  |  |  |  |  |  |  | 
| 424 | 10 |  |  |  |  | 22 | _lex_token( $self, T_OPPAR ); | 
| 425 |  |  |  |  |  |  |  | 
| 426 | 10 |  |  |  |  | 29 | $self->_enter_scope; | 
| 427 | 10 |  |  |  |  | 78 | my $expr = _parse_expr( $self ); | 
| 428 | 10 |  |  |  |  | 46 | $self->_add_pending_lexicals; | 
| 429 |  |  |  |  |  |  |  | 
| 430 | 10 |  |  |  |  | 69 | _lex_token( $self, T_CLPAR ); | 
| 431 | 10 |  |  |  |  | 28 | _lex_token( $self, T_OPBRK, undef, X_BLOCK ); | 
| 432 |  |  |  |  |  |  |  | 
| 433 | 10 |  |  |  |  | 50 | my $block = _parse_block_rest( $self, BLOCK_OPEN_SCOPE ); | 
| 434 |  |  |  |  |  |  |  | 
| 435 | 10 |  |  |  |  | 124 | my $if = Language::P::ParseTree::Conditional->new | 
| 436 |  |  |  |  |  |  | ( { iftrues => [ Language::P::ParseTree::ConditionalBlock->new | 
| 437 |  |  |  |  |  |  | ( { block_type => $cond->[O_VALUE], | 
| 438 |  |  |  |  |  |  | condition  => $expr, | 
| 439 |  |  |  |  |  |  | block      => $block, | 
| 440 |  |  |  |  |  |  | } ) | 
| 441 |  |  |  |  |  |  | ], | 
| 442 |  |  |  |  |  |  | } ); | 
| 443 |  |  |  |  |  |  |  | 
| 444 | 10 |  |  |  |  | 35 | for(;;) { | 
| 445 | 20 |  |  |  |  | 68 | my $else = $self->lexer->peek( X_STATE ); | 
| 446 | 20 | 100 | 100 |  |  | 128 | last if    $else->[O_TYPE] != T_ID | 
|  |  |  | 66 |  |  |  |  | 
| 447 |  |  |  |  |  |  | || ( $else->[O_ID_TYPE] != KEY_ELSE && $else->[O_ID_TYPE] != KEY_ELSIF ); | 
| 448 | 10 |  |  |  |  | 37 | _lex_token( $self ); | 
| 449 |  |  |  |  |  |  |  | 
| 450 | 10 |  |  |  |  | 16 | my $expr; | 
| 451 | 10 | 100 |  |  |  | 32 | if( $else->[O_ID_TYPE] == KEY_ELSIF ) { | 
| 452 | 4 |  |  |  |  | 12 | _lex_token( $self, T_OPPAR ); | 
| 453 | 4 |  |  |  |  | 16 | $expr = _parse_expr( $self ); | 
| 454 | 4 |  |  |  |  | 11 | _lex_token( $self, T_CLPAR ); | 
| 455 |  |  |  |  |  |  | } | 
| 456 | 10 |  |  |  |  | 23 | _lex_token( $self, T_OPBRK, undef, X_BLOCK ); | 
| 457 | 10 |  |  |  |  | 24 | my $block = _parse_block_rest( $self, BLOCK_OPEN_SCOPE ); | 
| 458 |  |  |  |  |  |  |  | 
| 459 | 10 | 100 |  |  |  | 37 | if( $expr ) { | 
| 460 | 4 |  |  |  |  | 8 | push @{$if->iftrues}, Language::P::ParseTree::ConditionalBlock->new | 
|  | 4 |  |  |  |  | 18 |  | 
| 461 |  |  |  |  |  |  | ( { block_type => 'if', | 
| 462 |  |  |  |  |  |  | condition  => $expr, | 
| 463 |  |  |  |  |  |  | block      => $block, | 
| 464 |  |  |  |  |  |  | } ) | 
| 465 |  |  |  |  |  |  | } else { | 
| 466 |  |  |  |  |  |  | # FIXME encapsulation | 
| 467 | 6 |  |  |  |  | 44 | $if->{iffalse} = Language::P::ParseTree::ConditionalBlock->new | 
| 468 |  |  |  |  |  |  | ( { block_type => 'else', | 
| 469 |  |  |  |  |  |  | condition  => undef, | 
| 470 |  |  |  |  |  |  | block      => $block, | 
| 471 |  |  |  |  |  |  | } ); | 
| 472 |  |  |  |  |  |  | } | 
| 473 |  |  |  |  |  |  | } | 
| 474 |  |  |  |  |  |  |  | 
| 475 | 10 |  |  |  |  | 39 | $if->set_parent_for_all_childs; | 
| 476 | 10 |  |  |  |  | 38 | $self->_leave_scope; | 
| 477 |  |  |  |  |  |  |  | 
| 478 | 10 |  |  |  |  | 70 | return $if; | 
| 479 |  |  |  |  |  |  | } | 
| 480 |  |  |  |  |  |  |  | 
| 481 |  |  |  |  |  |  | sub _parse_for { | 
| 482 | 11 |  |  | 11 |  | 24 | my( $self ) = @_; | 
| 483 | 11 |  |  |  |  | 44 | my $keyword = _lex_token( $self, T_ID ); | 
| 484 | 11 |  |  |  |  | 44 | my $token = $self->lexer->lex( X_OPERATOR ); | 
| 485 | 11 |  |  |  |  | 44 | my( $foreach_var, $foreach_expr ); | 
| 486 |  |  |  |  |  |  |  | 
| 487 | 11 |  |  |  |  | 39 | $self->_enter_scope; | 
| 488 |  |  |  |  |  |  |  | 
| 489 | 11 | 100 | 33 |  |  | 158 | if( $token->[O_TYPE] == T_OPPAR ) { | 
|  |  | 100 | 66 |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 490 | 4 |  |  |  |  | 27 | my $expr = _parse_expr( $self ); | 
| 491 | 4 |  |  |  |  | 36 | my $sep = $self->lexer->lex( X_OPERATOR ); | 
| 492 |  |  |  |  |  |  |  | 
| 493 | 4 | 50 |  |  |  | 150 | if( $sep->[O_TYPE] == T_CLPAR ) { | 
|  |  | 50 |  |  |  |  |  | 
| 494 | 0 |  |  |  |  | 0 | $foreach_var = _find_symbol( $self, VALUE_SCALAR, '_', T_FQ_ID ); | 
| 495 | 0 |  |  |  |  | 0 | $foreach_expr = $expr; | 
| 496 |  |  |  |  |  |  | } elsif( $sep->[O_TYPE] == T_SEMICOLON ) { | 
| 497 |  |  |  |  |  |  | # C-style for | 
| 498 | 4 |  |  |  |  | 19 | $self->_add_pending_lexicals; | 
| 499 |  |  |  |  |  |  |  | 
| 500 | 4 |  |  |  |  | 26 | my $cond = _parse_expr( $self ); | 
| 501 | 4 |  |  |  |  | 16 | _lex_token( $self, T_SEMICOLON ); | 
| 502 | 4 |  |  |  |  | 15 | $self->_add_pending_lexicals; | 
| 503 |  |  |  |  |  |  |  | 
| 504 | 4 |  |  |  |  | 29 | my $incr = _parse_expr( $self ); | 
| 505 | 4 |  |  |  |  | 12 | _lex_token( $self, T_CLPAR ); | 
| 506 | 4 |  |  |  |  | 14 | $self->_add_pending_lexicals; | 
| 507 |  |  |  |  |  |  |  | 
| 508 | 4 |  |  |  |  | 29 | _lex_token( $self, T_OPBRK, undef, X_BLOCK ); | 
| 509 | 4 |  |  |  |  | 17 | my $block = _parse_block_rest( $self, BLOCK_OPEN_SCOPE ); | 
| 510 |  |  |  |  |  |  |  | 
| 511 | 4 |  |  |  |  | 95 | my $for = Language::P::ParseTree::For->new | 
| 512 |  |  |  |  |  |  | ( { block_type  => 'for', | 
| 513 |  |  |  |  |  |  | initializer => $expr, | 
| 514 |  |  |  |  |  |  | condition   => $cond, | 
| 515 |  |  |  |  |  |  | step        => $incr, | 
| 516 |  |  |  |  |  |  | block       => $block, | 
| 517 |  |  |  |  |  |  | } ); | 
| 518 |  |  |  |  |  |  |  | 
| 519 | 4 |  |  |  |  | 21 | $self->_leave_scope; | 
| 520 |  |  |  |  |  |  |  | 
| 521 | 4 |  |  |  |  | 39 | return $for; | 
| 522 |  |  |  |  |  |  | } else { | 
| 523 | 0 |  |  |  |  | 0 | _syntax_error( $self, $sep ); | 
| 524 |  |  |  |  |  |  | } | 
| 525 |  |  |  |  |  |  | } elsif( $token->[O_TYPE] == T_ID && (    $token->[O_ID_TYPE] == OP_MY | 
| 526 |  |  |  |  |  |  | || $token->[O_ID_TYPE] == OP_OUR | 
| 527 |  |  |  |  |  |  | || $token->[O_ID_TYPE] == OP_STATE ) ) { | 
| 528 | 3 |  |  |  |  | 14 | _lex_token( $self, T_DOLLAR ); | 
| 529 | 3 |  |  |  |  | 30 | my $name = $self->lexer->lex_identifier( 0 ); | 
| 530 | 3 | 50 |  |  |  | 25 | die "No name" unless $name; | 
| 531 |  |  |  |  |  |  |  | 
| 532 |  |  |  |  |  |  | # FIXME our() variable refers to package it was declared in | 
| 533 | 3 |  |  |  |  | 31 | $foreach_var = Language::P::ParseTree::Symbol->new | 
| 534 |  |  |  |  |  |  | ( { name    => $name->[O_VALUE], | 
| 535 |  |  |  |  |  |  | sigil   => VALUE_SCALAR, | 
| 536 |  |  |  |  |  |  | } ); | 
| 537 | 3 |  |  |  |  | 23 | $foreach_var = _process_declaration( $self, $foreach_var, | 
| 538 |  |  |  |  |  |  | $token->[O_ID_TYPE] ); | 
| 539 |  |  |  |  |  |  | } elsif( $token->[O_TYPE] == T_DOLLAR ) { | 
| 540 | 4 |  |  |  |  | 19 | my $id = $self->lexer->lex_identifier( 0 ); | 
| 541 | 4 |  |  |  |  | 25 | $foreach_var = _find_symbol( $self, VALUE_SCALAR, $id->[O_VALUE], $id->[O_ID_TYPE] ); | 
| 542 |  |  |  |  |  |  | } else { | 
| 543 | 0 |  |  |  |  | 0 | _syntax_error( $self, $token ); | 
| 544 |  |  |  |  |  |  | } | 
| 545 |  |  |  |  |  |  |  | 
| 546 |  |  |  |  |  |  | # if we get there it is not C-style for | 
| 547 | 7 | 50 |  |  |  | 62 | if( !$foreach_expr ) { | 
| 548 | 7 |  |  |  |  | 28 | _lex_token( $self, T_OPPAR ); | 
| 549 | 7 |  |  |  |  | 24 | $foreach_expr = _parse_expr( $self ); | 
| 550 | 7 |  |  |  |  | 20 | _lex_token( $self, T_CLPAR ); | 
| 551 |  |  |  |  |  |  | } | 
| 552 |  |  |  |  |  |  |  | 
| 553 | 7 |  |  |  |  | 31 | $self->_add_pending_lexicals; | 
| 554 | 7 |  |  |  |  | 52 | _lex_token( $self, T_OPBRK, undef, X_BLOCK ); | 
| 555 |  |  |  |  |  |  |  | 
| 556 | 7 |  |  |  |  | 34 | my $block = _parse_block_rest( $self, BLOCK_OPEN_SCOPE ); | 
| 557 | 7 |  |  |  |  | 33 | my $continue = _parse_continue( $self ); | 
| 558 | 7 |  |  |  |  | 107 | my $for = Language::P::ParseTree::Foreach->new | 
| 559 |  |  |  |  |  |  | ( { expression => $foreach_expr, | 
| 560 |  |  |  |  |  |  | block      => $block, | 
| 561 |  |  |  |  |  |  | variable   => $foreach_var, | 
| 562 |  |  |  |  |  |  | continue   => $continue, | 
| 563 |  |  |  |  |  |  | } ); | 
| 564 |  |  |  |  |  |  |  | 
| 565 | 7 |  |  |  |  | 38 | $self->_leave_scope; | 
| 566 |  |  |  |  |  |  |  | 
| 567 | 7 |  |  |  |  | 52 | return $for; | 
| 568 |  |  |  |  |  |  | } | 
| 569 |  |  |  |  |  |  |  | 
| 570 |  |  |  |  |  |  | sub _parse_while { | 
| 571 | 12 |  |  | 12 |  | 23 | my( $self ) = @_; | 
| 572 | 12 |  |  |  |  | 175 | my $keyword = _lex_token( $self, T_ID ); | 
| 573 |  |  |  |  |  |  |  | 
| 574 | 12 |  |  |  |  | 34 | _lex_token( $self, T_OPPAR ); | 
| 575 |  |  |  |  |  |  |  | 
| 576 | 12 |  |  |  |  | 43 | $self->_enter_scope; | 
| 577 | 12 |  |  |  |  | 105 | my $expr = _parse_expr( $self ); | 
| 578 | 12 |  |  |  |  | 42 | $self->_add_pending_lexicals; | 
| 579 |  |  |  |  |  |  |  | 
| 580 | 12 |  |  |  |  | 80 | _lex_token( $self, T_CLPAR ); | 
| 581 | 12 |  |  |  |  | 87 | _lex_token( $self, T_OPBRK, undef, X_BLOCK ); | 
| 582 |  |  |  |  |  |  |  | 
| 583 | 12 |  |  |  |  | 40 | my $block = _parse_block_rest( $self, BLOCK_OPEN_SCOPE ); | 
| 584 | 12 |  |  |  |  | 53 | my $continue = _parse_continue( $self ); | 
| 585 | 12 |  |  |  |  | 163 | my $while = Language::P::ParseTree::ConditionalLoop | 
| 586 |  |  |  |  |  |  | ->new( { condition  => $expr, | 
| 587 |  |  |  |  |  |  | block      => $block, | 
| 588 |  |  |  |  |  |  | block_type => $keyword->[O_VALUE], | 
| 589 |  |  |  |  |  |  | continue   => $continue, | 
| 590 |  |  |  |  |  |  | } ); | 
| 591 |  |  |  |  |  |  |  | 
| 592 | 12 |  |  |  |  | 52 | $self->_leave_scope; | 
| 593 |  |  |  |  |  |  |  | 
| 594 | 12 |  |  |  |  | 82 | return $while; | 
| 595 |  |  |  |  |  |  | } | 
| 596 |  |  |  |  |  |  |  | 
| 597 |  |  |  |  |  |  | sub _parse_continue { | 
| 598 | 26 |  |  | 26 |  | 56 | my( $self ) = @_; | 
| 599 | 26 |  |  |  |  | 134 | my $token = $self->lexer->peek( X_STATE ); | 
| 600 | 26 | 100 | 100 |  |  | 223 | return unless $token->[O_TYPE] == T_ID && $token->[O_ID_TYPE] == KEY_CONTINUE; | 
| 601 |  |  |  |  |  |  |  | 
| 602 | 7 |  |  |  |  | 72 | _lex_token( $self, T_ID ); | 
| 603 | 7 |  |  |  |  | 34 | _lex_token( $self, T_OPBRK, undef, X_BLOCK ); | 
| 604 |  |  |  |  |  |  |  | 
| 605 | 7 |  |  |  |  | 23 | return _parse_block_rest( $self, BLOCK_OPEN_SCOPE ); | 
| 606 |  |  |  |  |  |  | } | 
| 607 |  |  |  |  |  |  |  | 
| 608 |  |  |  |  |  |  | sub _parse_sideff { | 
| 609 | 339 |  |  | 339 |  | 542 | my( $self ) = @_; | 
| 610 | 339 |  |  |  |  | 790 | my $expr = _parse_expr( $self ); | 
| 611 | 339 |  |  |  |  | 1124 | my $keyword = $self->lexer->peek( X_TERM ); | 
| 612 |  |  |  |  |  |  |  | 
| 613 | 339 | 100 | 66 |  |  | 1687 | if( $keyword->[O_TYPE] == T_ID && is_keyword( $keyword->[O_ID_TYPE] ) ) { | 
| 614 | 10 |  |  |  |  | 20 | my $keyidt = $keyword->[O_ID_TYPE]; | 
| 615 |  |  |  |  |  |  |  | 
| 616 | 10 | 50 | 33 |  |  | 44 | if( $keyidt == KEY_IF || $keyidt == KEY_UNLESS ) { | 
|  |  | 0 | 0 |  |  |  |  | 
|  |  | 0 | 0 |  |  |  |  | 
| 617 | 10 |  |  |  |  | 27 | _lex_token( $self, T_ID ); | 
| 618 | 10 |  |  |  |  | 21 | my $cond = _parse_expr( $self ); | 
| 619 |  |  |  |  |  |  |  | 
| 620 | 10 |  |  |  |  | 167 | $expr = Language::P::ParseTree::Conditional->new | 
| 621 |  |  |  |  |  |  | ( { iftrues => [ Language::P::ParseTree::ConditionalBlock->new | 
| 622 |  |  |  |  |  |  | ( { block_type => $keyword->[O_VALUE], | 
| 623 |  |  |  |  |  |  | condition  => $cond, | 
| 624 |  |  |  |  |  |  | block      => $expr, | 
| 625 |  |  |  |  |  |  | } ) | 
| 626 |  |  |  |  |  |  | ], | 
| 627 |  |  |  |  |  |  | } ); | 
| 628 |  |  |  |  |  |  | } elsif( $keyidt == KEY_WHILE || $keyidt == KEY_UNTIL ) { | 
| 629 | 0 |  |  |  |  | 0 | _lex_token( $self, T_ID ); | 
| 630 | 0 |  |  |  |  | 0 | my $cond = _parse_expr( $self ); | 
| 631 |  |  |  |  |  |  |  | 
| 632 | 0 |  |  |  |  | 0 | $expr = Language::P::ParseTree::ConditionalLoop->new | 
| 633 |  |  |  |  |  |  | ( { condition  => $cond, | 
| 634 |  |  |  |  |  |  | block      => $expr, | 
| 635 |  |  |  |  |  |  | block_type => $keyword->[O_VALUE], | 
| 636 |  |  |  |  |  |  | } ); | 
| 637 |  |  |  |  |  |  | } elsif( $keyidt == KEY_FOR || $keyidt == KEY_FOREACH ) { | 
| 638 | 0 |  |  |  |  | 0 | _lex_token( $self, T_ID ); | 
| 639 | 0 |  |  |  |  | 0 | my $cond = _parse_expr( $self ); | 
| 640 |  |  |  |  |  |  |  | 
| 641 | 0 |  |  |  |  | 0 | $expr = Language::P::ParseTree::Foreach->new | 
| 642 |  |  |  |  |  |  | ( { expression => $cond, | 
| 643 |  |  |  |  |  |  | block      => $expr, | 
| 644 |  |  |  |  |  |  | variable   => _find_symbol( $self, VALUE_SCALAR, '_', T_FQ_ID ), | 
| 645 |  |  |  |  |  |  | } ); | 
| 646 |  |  |  |  |  |  | } | 
| 647 |  |  |  |  |  |  | } | 
| 648 |  |  |  |  |  |  |  | 
| 649 | 339 |  |  |  |  | 998 | _lex_semicolon( $self ); | 
| 650 | 339 |  |  |  |  | 903 | $self->_add_pending_lexicals; | 
| 651 |  |  |  |  |  |  |  | 
| 652 | 339 |  |  |  |  | 3494 | return $expr; | 
| 653 |  |  |  |  |  |  | } | 
| 654 |  |  |  |  |  |  |  | 
| 655 |  |  |  |  |  |  | sub _parse_expr { | 
| 656 | 415 |  |  | 415 |  | 560 | my( $self ) = @_; | 
| 657 |  |  |  |  |  |  |  | 
| 658 | 415 |  |  |  |  | 1108 | return _parse_term( $self, PREC_LOWEST ); | 
| 659 |  |  |  |  |  |  | } | 
| 660 |  |  |  |  |  |  |  | 
| 661 |  |  |  |  |  |  | sub _find_symbol { | 
| 662 | 321 |  |  | 321 |  | 1226 | my( $self, $sigil, $name, $type ) = @_; | 
| 663 |  |  |  |  |  |  |  | 
| 664 | 321 | 100 |  |  |  | 1054 | if( $self->_in_declaration ) { | 
|  |  | 50 |  |  |  |  |  | 
| 665 | 8 |  |  |  |  | 115 | return Language::P::ParseTree::Symbol->new | 
| 666 |  |  |  |  |  |  | ( { name  => $name, | 
| 667 |  |  |  |  |  |  | sigil => $sigil, | 
| 668 |  |  |  |  |  |  | } ); | 
| 669 |  |  |  |  |  |  | } elsif( $type == T_FQ_ID ) { | 
| 670 | 0 |  |  |  |  | 0 | return Language::P::ParseTree::Symbol->new | 
| 671 |  |  |  |  |  |  | ( { name  => _qualify( $self, $name, $type ), | 
| 672 |  |  |  |  |  |  | sigil => $sigil, | 
| 673 |  |  |  |  |  |  | } ); | 
| 674 |  |  |  |  |  |  | } | 
| 675 |  |  |  |  |  |  |  | 
| 676 | 313 |  |  |  |  | 2776 | my( $level, $lex ) = $self->_lexicals->find_name( $sigil . "\0" . $name ); | 
| 677 |  |  |  |  |  |  |  | 
| 678 | 313 | 100 |  |  |  | 1120 | if( $lex ) { | 
| 679 | 22 | 100 |  |  |  | 77 | $lex->set_closed_over if $level > 0; | 
| 680 |  |  |  |  |  |  |  | 
| 681 | 22 |  |  |  |  | 246 | return Language::P::ParseTree::LexicalSymbol->new | 
| 682 |  |  |  |  |  |  | ( { declaration => $lex, | 
| 683 |  |  |  |  |  |  | level       => $level, | 
| 684 |  |  |  |  |  |  | } ); | 
| 685 |  |  |  |  |  |  | } | 
| 686 |  |  |  |  |  |  |  | 
| 687 | 291 |  |  |  |  | 808 | return Language::P::ParseTree::Symbol->new | 
| 688 |  |  |  |  |  |  | ( { name  => _qualify( $self, $name, $type ), | 
| 689 |  |  |  |  |  |  | sigil => $sigil, | 
| 690 |  |  |  |  |  |  | } ); | 
| 691 |  |  |  |  |  |  | } | 
| 692 |  |  |  |  |  |  |  | 
| 693 |  |  |  |  |  |  | sub _parse_maybe_subscript_rest { | 
| 694 | 658 |  |  | 658 |  | 950 | my( $self, $subscripted, $arrow_only ) = @_; | 
| 695 | 658 |  |  |  |  | 1923 | my $next = $self->lexer->peek( X_OPERATOR ); | 
| 696 |  |  |  |  |  |  |  | 
| 697 |  |  |  |  |  |  | # array/hash element | 
| 698 | 658 | 100 | 33 |  |  | 2286 | if( $next->[O_TYPE] == T_ARROW ) { | 
|  |  | 100 | 33 |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 699 | 8 |  |  |  |  | 20 | _lex_token( $self, T_ARROW ); | 
| 700 | 8 |  |  |  |  | 25 | my $bracket = $self->lexer->peek( X_OPERATOR ); | 
| 701 |  |  |  |  |  |  |  | 
| 702 | 8 | 50 | 33 |  |  | 39 | if(    $bracket->[O_TYPE] == T_OPPAR | 
|  |  |  | 33 |  |  |  |  | 
| 703 |  |  |  |  |  |  | || $bracket->[O_TYPE] == T_OPSQ | 
| 704 |  |  |  |  |  |  | || $bracket->[O_TYPE] == T_OPBRK ) { | 
| 705 | 8 |  |  |  |  | 57 | return _parse_dereference_rest( $self, $subscripted, $bracket ); | 
| 706 |  |  |  |  |  |  | } else { | 
| 707 | 0 |  |  |  |  | 0 | return _parse_maybe_direct_method_call( $self, $subscripted ); | 
| 708 |  |  |  |  |  |  | } | 
| 709 |  |  |  |  |  |  | } elsif( $arrow_only ) { | 
| 710 | 638 |  |  |  |  | 1731 | return $subscripted; | 
| 711 |  |  |  |  |  |  | } elsif(    $next->[O_TYPE] == T_OPPAR | 
| 712 |  |  |  |  |  |  | || $next->[O_TYPE] == T_OPSQ | 
| 713 |  |  |  |  |  |  | || $next->[O_TYPE] == T_OPBRK ) { | 
| 714 | 0 |  |  |  |  | 0 | return _parse_dereference_rest( $self, $subscripted, $next ); | 
| 715 |  |  |  |  |  |  | } else { | 
| 716 | 12 |  |  |  |  | 73 | return $subscripted; | 
| 717 |  |  |  |  |  |  | } | 
| 718 |  |  |  |  |  |  | } | 
| 719 |  |  |  |  |  |  |  | 
| 720 |  |  |  |  |  |  | sub _parse_indirect_function_call { | 
| 721 | 8 |  |  | 8 |  | 17 | my( $self, $subscripted, $with_arguments, $ampersand ) = @_; | 
| 722 |  |  |  |  |  |  |  | 
| 723 | 8 |  |  |  |  | 9 | my $args; | 
| 724 | 8 | 50 |  |  |  | 16 | if( $with_arguments ) { | 
| 725 | 8 |  |  |  |  | 16 | _lex_token( $self, T_OPPAR ); | 
| 726 | 8 |  |  |  |  | 16 | ( $args, undef ) = _parse_arglist( $self, PREC_LOWEST, 0, 0 ); | 
| 727 | 8 |  |  |  |  | 21 | _lex_token( $self, T_CLPAR ); | 
| 728 |  |  |  |  |  |  | } | 
| 729 |  |  |  |  |  |  |  | 
| 730 |  |  |  |  |  |  | # $foo->() requires an additional dereference, while | 
| 731 |  |  |  |  |  |  | # &{...}(...) does not construct a reference but might need it | 
| 732 | 8 | 50 | 66 |  |  | 42 | if( !$subscripted->is_symbol || $subscripted->sigil != VALUE_SUB ) { | 
| 733 | 8 |  |  |  |  | 84 | $subscripted = Language::P::ParseTree::Dereference->new | 
| 734 |  |  |  |  |  |  | ( { left => $subscripted, | 
| 735 |  |  |  |  |  |  | op   => OP_DEREFERENCE_SUB, | 
| 736 |  |  |  |  |  |  | } ); | 
| 737 |  |  |  |  |  |  | } | 
| 738 |  |  |  |  |  |  |  | 
| 739 |  |  |  |  |  |  | # treat &foo; separately from all other cases | 
| 740 | 8 | 50 | 33 |  |  | 40 | if( $ampersand && !$with_arguments ) { | 
| 741 | 0 |  |  |  |  | 0 | return Language::P::ParseTree::SpecialFunctionCall->new | 
| 742 |  |  |  |  |  |  | ( { function    => $subscripted, | 
| 743 |  |  |  |  |  |  | flags       => FLAG_IMPLICITARGUMENTS, | 
| 744 |  |  |  |  |  |  | } ); | 
| 745 |  |  |  |  |  |  | } else { | 
| 746 | 8 |  |  |  |  | 47 | return Language::P::ParseTree::FunctionCall->new | 
| 747 |  |  |  |  |  |  | ( { function    => $subscripted, | 
| 748 |  |  |  |  |  |  | arguments   => $args, | 
| 749 |  |  |  |  |  |  | } ); | 
| 750 |  |  |  |  |  |  | } | 
| 751 |  |  |  |  |  |  | } | 
| 752 |  |  |  |  |  |  |  | 
| 753 |  |  |  |  |  |  | sub _parse_dereference_rest { | 
| 754 | 8 |  |  | 8 |  | 13 | my( $self, $subscripted, $bracket ) = @_; | 
| 755 | 8 |  |  |  |  | 11 | my $term; | 
| 756 |  |  |  |  |  |  |  | 
| 757 | 8 | 50 |  |  |  | 21 | if( $bracket->[O_TYPE] == T_OPPAR ) { | 
| 758 | 8 |  |  |  |  | 21 | $term = _parse_indirect_function_call( $self, $subscripted, 1, 0 ); | 
| 759 |  |  |  |  |  |  | } else { | 
| 760 | 0 |  |  |  |  | 0 | my $subscript = _parse_bracketed_expr( $self, $bracket->[O_TYPE], 0 ); | 
| 761 | 0 | 0 |  |  |  | 0 | $term = Language::P::ParseTree::Subscript->new | 
| 762 |  |  |  |  |  |  | ( { subscripted => $subscripted, | 
| 763 |  |  |  |  |  |  | subscript   => $subscript, | 
| 764 |  |  |  |  |  |  | type        => $bracket->[O_TYPE] == T_OPBRK ? | 
| 765 |  |  |  |  |  |  | VALUE_HASH : VALUE_ARRAY, | 
| 766 |  |  |  |  |  |  | reference   => 1, | 
| 767 |  |  |  |  |  |  | } ); | 
| 768 |  |  |  |  |  |  | } | 
| 769 |  |  |  |  |  |  |  | 
| 770 | 8 |  |  |  |  | 28 | return _parse_maybe_subscript_rest( $self, $term ); | 
| 771 |  |  |  |  |  |  | } | 
| 772 |  |  |  |  |  |  |  | 
| 773 |  |  |  |  |  |  | sub _parse_bracketed_expr { | 
| 774 | 6 |  |  | 6 |  | 13 | my( $self, $bracket, $allow_empty, $no_consume_opening ) = @_; | 
| 775 | 6 | 50 |  |  |  | 97 | my $close = $bracket == T_OPBRK ? T_CLBRK : | 
|  |  | 50 |  |  |  |  |  | 
| 776 |  |  |  |  |  |  | $bracket == T_OPSQ  ? T_CLSQ : | 
| 777 |  |  |  |  |  |  | T_CLPAR; | 
| 778 |  |  |  |  |  |  |  | 
| 779 | 6 | 50 |  |  |  | 25 | _lex_token( $self, $bracket ) unless $no_consume_opening; | 
| 780 | 6 | 50 |  |  |  | 22 | if( $allow_empty ) { | 
| 781 | 0 |  |  |  |  | 0 | my $next = $self->lexer->peek( X_TERM ); | 
| 782 | 0 | 0 |  |  |  | 0 | if( $next->[O_TYPE] == $close ) { | 
| 783 | 0 |  |  |  |  | 0 | _lex_token( $self, $close ); | 
| 784 | 0 |  |  |  |  | 0 | return undef; | 
| 785 |  |  |  |  |  |  | } | 
| 786 |  |  |  |  |  |  | } | 
| 787 | 6 |  |  |  |  | 23 | my $subscript = _parse_expr( $self ); | 
| 788 | 6 |  |  |  |  | 26 | _lex_token( $self, $close ); | 
| 789 |  |  |  |  |  |  |  | 
| 790 | 6 |  |  |  |  | 19 | return $subscript; | 
| 791 |  |  |  |  |  |  | } | 
| 792 |  |  |  |  |  |  |  | 
| 793 |  |  |  |  |  |  | sub _parse_maybe_indirect_method_call { | 
| 794 | 0 |  |  | 0 |  | 0 | my( $self, $op, $next ) = @_; | 
| 795 | 0 |  |  |  |  | 0 | my $indir = _parse_indirobj( $self, 1 ); | 
| 796 |  |  |  |  |  |  |  | 
| 797 | 0 | 0 |  |  |  | 0 | if( $indir ) { | 
| 798 |  |  |  |  |  |  | # if FH -> no method | 
| 799 |  |  |  |  |  |  | # proto FH -> no method | 
| 800 |  |  |  |  |  |  | # Foo $bar (?) -> no method | 
| 801 |  |  |  |  |  |  | # foo $bar -> method | 
| 802 |  |  |  |  |  |  |  | 
| 803 |  |  |  |  |  |  | # print xxx -> no method, but print is handled before getting | 
| 804 |  |  |  |  |  |  | # there, since it is a non-overridable builtin | 
| 805 |  |  |  |  |  |  |  | 
| 806 |  |  |  |  |  |  | # foo pack:: -> method | 
| 807 |  |  |  |  |  |  |  | 
| 808 |  |  |  |  |  |  | # use Data::Dumper; | 
| 809 |  |  |  |  |  |  | # print Dumper( $indir ) . ' ' . Dumper( $next ); | 
| 810 |  |  |  |  |  |  |  | 
| 811 | 0 |  |  |  |  | 0 | my $args = _parse_term( $self, PREC_COMMA ); | 
| 812 | 0 | 0 |  |  |  | 0 | if( $args ) { | 
| 813 | 0 | 0 |  |  |  | 0 | if( $args->isa( 'Language::P::ParseTree::List' ) ) { | 
| 814 | 0 | 0 |  |  |  | 0 | $args = @{$args->expressions} ? $args->expressions : undef; | 
|  | 0 |  |  |  |  | 0 |  | 
| 815 |  |  |  |  |  |  | } else { | 
| 816 | 0 |  |  |  |  | 0 | $args = [ $args ]; | 
| 817 |  |  |  |  |  |  | } | 
| 818 |  |  |  |  |  |  | } | 
| 819 | 0 | 0 |  |  |  | 0 | $indir = Language::P::ParseTree::Constant->new | 
| 820 |  |  |  |  |  |  | ( { flags => CONST_STRING, | 
| 821 |  |  |  |  |  |  | value => $indir->[O_VALUE], | 
| 822 |  |  |  |  |  |  | } ) | 
| 823 |  |  |  |  |  |  | if ref( $indir ) eq 'ARRAY'; | 
| 824 | 0 |  |  |  |  | 0 | my $term = Language::P::ParseTree::MethodCall->new | 
| 825 |  |  |  |  |  |  | ( { invocant  => $indir, | 
| 826 |  |  |  |  |  |  | method    => $op->[O_VALUE], | 
| 827 |  |  |  |  |  |  | arguments => $args, | 
| 828 |  |  |  |  |  |  | indirect  => 0, | 
| 829 |  |  |  |  |  |  | } ); | 
| 830 |  |  |  |  |  |  |  | 
| 831 | 0 |  |  |  |  | 0 | return _parse_maybe_subscript_rest( $self, $term ); | 
| 832 |  |  |  |  |  |  | } | 
| 833 |  |  |  |  |  |  |  | 
| 834 | 0 |  |  |  |  | 0 | return Language::P::ParseTree::Constant->new | 
| 835 |  |  |  |  |  |  | ( { value => $op->[O_VALUE], | 
| 836 |  |  |  |  |  |  | flags => CONST_STRING|STRING_BARE | 
| 837 |  |  |  |  |  |  | } ); | 
| 838 |  |  |  |  |  |  | } | 
| 839 |  |  |  |  |  |  |  | 
| 840 |  |  |  |  |  |  | sub _parse_maybe_direct_method_call { | 
| 841 | 0 |  |  | 0 |  | 0 | my( $self, $invocant ) = @_; | 
| 842 | 0 |  |  |  |  | 0 | my $token = $self->lexer->lex( X_TERM ); | 
| 843 | 0 |  |  |  |  | 0 | my( $method, $indirect ); | 
| 844 |  |  |  |  |  |  |  | 
| 845 | 0 | 0 |  |  |  | 0 | if( $token->[O_TYPE] == T_ID ) { | 
|  |  | 0 |  |  |  |  |  | 
| 846 | 0 |  |  |  |  | 0 | ( $method, $indirect ) = ( $token->[O_VALUE], 0 ); | 
| 847 |  |  |  |  |  |  | } elsif( $token->[O_TYPE] == T_DOLLAR ) { | 
| 848 | 0 |  |  |  |  | 0 | my $id = $self->lexer->lex_identifier( 0 ); | 
| 849 | 0 |  |  |  |  | 0 | $method = _find_symbol( $self, VALUE_SCALAR, $id->[O_VALUE], $id->[O_ID_TYPE] ); | 
| 850 | 0 |  |  |  |  | 0 | $indirect = 1; | 
| 851 |  |  |  |  |  |  | } else { | 
| 852 | 0 |  |  |  |  | 0 | _syntax_error( $self, $token ); | 
| 853 |  |  |  |  |  |  | } | 
| 854 |  |  |  |  |  |  |  | 
| 855 | 0 |  |  |  |  | 0 | my $oppar = $self->lexer->peek( X_OPERATOR ); | 
| 856 | 0 |  |  |  |  | 0 | my $args; | 
| 857 | 0 | 0 |  |  |  | 0 | if( $oppar->[O_TYPE] == T_OPPAR ) { | 
| 858 | 0 |  |  |  |  | 0 | _lex_token( $self, T_OPPAR ); | 
| 859 | 0 |  |  |  |  | 0 | ( $args ) = _parse_arglist( $self, PREC_LOWEST, 0, 0 ); | 
| 860 | 0 |  |  |  |  | 0 | _lex_token( $self, T_CLPAR ); | 
| 861 |  |  |  |  |  |  | } | 
| 862 |  |  |  |  |  |  |  | 
| 863 | 0 |  |  |  |  | 0 | my $term = Language::P::ParseTree::MethodCall->new | 
| 864 |  |  |  |  |  |  | ( { invocant  => $invocant, | 
| 865 |  |  |  |  |  |  | method    => $method, | 
| 866 |  |  |  |  |  |  | arguments => $args, | 
| 867 |  |  |  |  |  |  | indirect  => $indirect, | 
| 868 |  |  |  |  |  |  | } ); | 
| 869 |  |  |  |  |  |  |  | 
| 870 | 0 |  |  |  |  | 0 | return _parse_maybe_subscript_rest( $self, $term ); | 
| 871 |  |  |  |  |  |  | } | 
| 872 |  |  |  |  |  |  |  | 
| 873 |  |  |  |  |  |  | sub _parse_match { | 
| 874 | 0 |  |  | 0 |  | 0 | my( $self, $token ) = @_; | 
| 875 |  |  |  |  |  |  |  | 
| 876 | 0 | 0 |  |  |  | 0 | if( $token->[O_RX_INTERPOLATED] ) { | 
| 877 | 0 |  |  |  |  | 0 | my $string = _parse_string_rest( $self, $token, 1 ); | 
| 878 | 0 |  |  |  |  | 0 | my $match = Language::P::ParseTree::InterpolatedPattern->new | 
| 879 |  |  |  |  |  |  | ( { string     => $string, | 
| 880 |  |  |  |  |  |  | op         => $token->[O_VALUE], | 
| 881 |  |  |  |  |  |  | flags      => $token->[O_RX_FLAGS], | 
| 882 |  |  |  |  |  |  | } ); | 
| 883 |  |  |  |  |  |  |  | 
| 884 | 0 |  |  |  |  | 0 | return $match; | 
| 885 |  |  |  |  |  |  | } else { | 
| 886 | 0 |  |  |  |  | 0 | my $parts = Language::P::Parser::Regex->new | 
| 887 |  |  |  |  |  |  | ( { generator   => $self->generator, | 
| 888 |  |  |  |  |  |  | runtime     => $self->runtime, | 
| 889 |  |  |  |  |  |  | interpolate => $token->[O_QS_INTERPOLATE], | 
| 890 |  |  |  |  |  |  | } )->parse_string( $token->[O_QS_BUFFER] ); | 
| 891 | 0 |  |  |  |  | 0 | my $match = Language::P::ParseTree::Pattern->new | 
| 892 |  |  |  |  |  |  | ( { components => $parts, | 
| 893 |  |  |  |  |  |  | op         => $token->[O_VALUE], | 
| 894 |  |  |  |  |  |  | flags      => $token->[O_RX_FLAGS], | 
| 895 |  |  |  |  |  |  | } ); | 
| 896 |  |  |  |  |  |  |  | 
| 897 | 0 |  |  |  |  | 0 | return $match; | 
| 898 |  |  |  |  |  |  | } | 
| 899 |  |  |  |  |  |  | } | 
| 900 |  |  |  |  |  |  |  | 
| 901 |  |  |  |  |  |  | sub _parse_substitution { | 
| 902 | 0 |  |  | 0 |  | 0 | my( $self, $token ) = @_; | 
| 903 | 0 |  |  |  |  | 0 | my $match = _parse_match( $self, $token ); | 
| 904 |  |  |  |  |  |  |  | 
| 905 | 0 |  |  |  |  | 0 | my $replace; | 
| 906 | 0 | 0 |  |  |  | 0 | if( $match->flags & FLAG_RX_EVAL ) { | 
| 907 | 0 |  |  |  |  | 0 | local $self->{lexer} = Language::P::Lexer->new | 
| 908 |  |  |  |  |  |  | ( { string       => $token->[O_RX_SECOND_HALF]->[O_QS_BUFFER], | 
| 909 |  |  |  |  |  |  | symbol_table => $self->runtime->symbol_table, | 
| 910 |  |  |  |  |  |  | _heredoc_lexer => $self->lexer, | 
| 911 |  |  |  |  |  |  | } ); | 
| 912 | 0 |  |  |  |  | 0 | $replace = _parse_block_rest( $self, BLOCK_OPEN_SCOPE, T_EOF ); | 
| 913 |  |  |  |  |  |  | } else { | 
| 914 | 0 |  |  |  |  | 0 | $replace = _parse_string_rest( $self, $token->[O_RX_SECOND_HALF], 0 ); | 
| 915 |  |  |  |  |  |  | } | 
| 916 |  |  |  |  |  |  |  | 
| 917 | 0 |  |  |  |  | 0 | my $sub = Language::P::ParseTree::Substitution->new | 
| 918 |  |  |  |  |  |  | ( { pattern     => $match, | 
| 919 |  |  |  |  |  |  | replacement => $replace, | 
| 920 |  |  |  |  |  |  | } ); | 
| 921 |  |  |  |  |  |  |  | 
| 922 | 0 |  |  |  |  | 0 | return $sub; | 
| 923 |  |  |  |  |  |  | } | 
| 924 |  |  |  |  |  |  |  | 
| 925 |  |  |  |  |  |  | sub _parse_string_rest { | 
| 926 | 171 |  |  | 171 |  | 243 | my( $self, $token, $pattern ) = @_; | 
| 927 | 171 |  |  |  |  | 211 | my @values; | 
| 928 | 171 |  |  |  |  | 755 | local $self->{lexer} = Language::P::Lexer->new | 
| 929 |  |  |  |  |  |  | ( { string       => $token->[O_QS_BUFFER], | 
| 930 |  |  |  |  |  |  | symbol_table => $self->runtime->symbol_table, | 
| 931 |  |  |  |  |  |  | } ); | 
| 932 |  |  |  |  |  |  |  | 
| 933 | 171 |  |  |  |  | 724 | $self->lexer->quote( { interpolate          => $token->[O_QS_INTERPOLATE], | 
| 934 |  |  |  |  |  |  | pattern              => 0, | 
| 935 |  |  |  |  |  |  | interpolated_pattern => $pattern, | 
| 936 |  |  |  |  |  |  | } ); | 
| 937 | 171 |  |  |  |  | 1989 | for(;;) { | 
| 938 | 392 |  |  |  |  | 1076 | my $value = $self->lexer->lex_quote; | 
| 939 |  |  |  |  |  |  |  | 
| 940 | 392 | 100 | 33 |  |  | 2198 | if( $value->[O_TYPE] == T_STRING ) { | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 941 | 192 |  |  |  |  | 1707 | push @values, Language::P::ParseTree::Constant->new | 
| 942 |  |  |  |  |  |  | ( { flags => CONST_STRING, | 
| 943 |  |  |  |  |  |  | value => $value->[O_VALUE], | 
| 944 |  |  |  |  |  |  | } ); | 
| 945 |  |  |  |  |  |  | } elsif( $value->[O_TYPE] == T_EOF ) { | 
| 946 | 171 |  |  |  |  | 380 | last; | 
| 947 |  |  |  |  |  |  | } elsif( $value->[O_TYPE] == T_DOLLAR || $value->[O_TYPE] == T_AT ) { | 
| 948 | 29 |  |  |  |  | 85 | push @values, _parse_indirobj_maybe_subscripts( $self, $value ); | 
| 949 |  |  |  |  |  |  | } else { | 
| 950 | 0 |  |  |  |  | 0 | _syntax_error( $self, $value ); | 
| 951 |  |  |  |  |  |  | } | 
| 952 |  |  |  |  |  |  | } | 
| 953 |  |  |  |  |  |  |  | 
| 954 | 171 |  |  |  |  | 496 | $self->lexer->quote( undef ); | 
| 955 |  |  |  |  |  |  |  | 
| 956 | 171 |  |  |  |  | 1411 | my $string; | 
| 957 | 171 | 100 | 66 |  |  | 889 | if( @values == 1 && $values[0]->is_constant ) { | 
|  |  | 100 |  |  |  |  |  | 
| 958 | 144 |  |  |  |  | 228 | $string = $values[0]; | 
| 959 |  |  |  |  |  |  | } elsif( @values == 0 ) { | 
| 960 | 2 |  |  |  |  | 23 | $string = Language::P::ParseTree::Constant->new | 
| 961 |  |  |  |  |  |  | ( { value => "", | 
| 962 |  |  |  |  |  |  | flags => CONST_STRING, | 
| 963 |  |  |  |  |  |  | } ); | 
| 964 |  |  |  |  |  |  | } else { | 
| 965 | 25 |  |  |  |  | 207 | $string = Language::P::ParseTree::QuotedString->new | 
| 966 |  |  |  |  |  |  | ( { components => \@values, | 
| 967 |  |  |  |  |  |  | } ); | 
| 968 |  |  |  |  |  |  | } | 
| 969 |  |  |  |  |  |  |  | 
| 970 | 171 |  |  |  |  | 424 | my $quote = $token->[O_VALUE]; | 
| 971 | 171 | 50 |  |  |  | 574 | if( $quote == OP_QL_QX ) { | 
|  |  | 50 |  |  |  |  |  | 
| 972 | 0 |  |  |  |  | 0 | $string = Language::P::ParseTree::UnOp->new | 
| 973 |  |  |  |  |  |  | ( { op   => OP_BACKTICK, | 
| 974 |  |  |  |  |  |  | left => $string, | 
| 975 |  |  |  |  |  |  | } ); | 
| 976 |  |  |  |  |  |  | } elsif( $quote == OP_QL_QW ) { | 
| 977 | 0 |  |  |  |  | 0 | my @words = map Language::P::ParseTree::Constant->new | 
| 978 |  |  |  |  |  |  | ( { value => $_, | 
| 979 |  |  |  |  |  |  | flags => CONST_STRING, | 
| 980 |  |  |  |  |  |  | } ), | 
| 981 |  |  |  |  |  |  | split /[\s\r\n]+/, $string->value; | 
| 982 |  |  |  |  |  |  |  | 
| 983 | 0 |  |  |  |  | 0 | $string = Language::P::ParseTree::List->new | 
| 984 |  |  |  |  |  |  | ( { expressions => \@words, | 
| 985 |  |  |  |  |  |  | } ); | 
| 986 |  |  |  |  |  |  | } | 
| 987 |  |  |  |  |  |  |  | 
| 988 | 171 |  |  |  |  | 1219 | return $string; | 
| 989 |  |  |  |  |  |  | } | 
| 990 |  |  |  |  |  |  |  | 
| 991 |  |  |  |  |  |  | sub _parse_term_terminal { | 
| 992 | 1048 |  |  | 1048 |  | 1471 | my( $self, $token, $is_bind ) = @_; | 
| 993 |  |  |  |  |  |  |  | 
| 994 | 1048 | 100 | 100 |  |  | 12459 | if( $token->[O_TYPE] == T_QUOTE ) { | 
|  |  | 50 | 66 |  |  |  |  | 
|  |  | 100 | 66 |  |  |  |  | 
|  |  | 50 | 33 |  |  |  |  | 
|  |  | 50 | 66 |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 995 | 171 |  |  |  |  | 530 | my $qstring = _parse_string_rest( $self, $token, 0 ); | 
| 996 |  |  |  |  |  |  |  | 
| 997 | 171 | 50 |  |  |  | 540 | if( $token->[O_VALUE] == OP_QL_LT ) { | 
| 998 |  |  |  |  |  |  | # simple scalar: readline, anything else: glob | 
| 999 | 0 | 0 | 0 |  |  | 0 | if(    $qstring->isa( 'Language::P::ParseTree::QuotedString' ) | 
|  | 0 | 0 | 0 |  |  | 0 |  | 
| 1000 |  |  |  |  |  |  | && $#{$qstring->components} == 0 | 
| 1001 |  |  |  |  |  |  | && $qstring->components->[0]->is_symbol ) { | 
| 1002 | 0 |  |  |  |  | 0 | return Language::P::ParseTree::Overridable | 
| 1003 |  |  |  |  |  |  | ->new( { function  => OP_READLINE, | 
| 1004 |  |  |  |  |  |  | arguments => [ $qstring->components->[0] ] } ); | 
| 1005 |  |  |  |  |  |  | } elsif( $qstring->is_constant ) { | 
| 1006 | 0 | 0 |  |  |  | 0 | if( $qstring->value =~ /^[a-zA-Z_]/ ) { | 
| 1007 |  |  |  |  |  |  | # FIXME simpler method, make lex_identifier static | 
| 1008 | 0 |  |  |  |  | 0 | my $lexer = Language::P::Lexer->new | 
| 1009 |  |  |  |  |  |  | ( { string => $qstring->value } ); | 
| 1010 | 0 |  |  |  |  | 0 | my $id = $lexer->lex_identifier( 0 ); | 
| 1011 |  |  |  |  |  |  |  | 
| 1012 | 0 | 0 | 0 |  |  | 0 | if( $id && !length( ${$lexer->buffer} ) ) { | 
|  | 0 |  |  |  |  | 0 |  | 
| 1013 | 0 |  |  |  |  | 0 | my $glob = Language::P::ParseTree::Symbol->new | 
| 1014 |  |  |  |  |  |  | ( { name  => _qualify( $self, $id->[O_VALUE], $id->[O_ID_TYPE] ), | 
| 1015 |  |  |  |  |  |  | sigil => VALUE_GLOB, | 
| 1016 |  |  |  |  |  |  | } ); | 
| 1017 | 0 |  |  |  |  | 0 | return Language::P::ParseTree::Overridable | 
| 1018 |  |  |  |  |  |  | ->new( { function  => OP_READLINE, | 
| 1019 |  |  |  |  |  |  | arguments => [ $glob ], | 
| 1020 |  |  |  |  |  |  | } ); | 
| 1021 |  |  |  |  |  |  | } | 
| 1022 |  |  |  |  |  |  | } | 
| 1023 | 0 |  |  |  |  | 0 | return Language::P::ParseTree::Glob | 
| 1024 |  |  |  |  |  |  | ->new( { arguments => [ $qstring ] } ); | 
| 1025 |  |  |  |  |  |  | } else { | 
| 1026 | 0 |  |  |  |  | 0 | return Language::P::ParseTree::Glob | 
| 1027 |  |  |  |  |  |  | ->new( { arguments => [ $qstring ] } ); | 
| 1028 |  |  |  |  |  |  | } | 
| 1029 |  |  |  |  |  |  | } | 
| 1030 |  |  |  |  |  |  |  | 
| 1031 | 171 |  |  |  |  | 393 | return $qstring; | 
| 1032 |  |  |  |  |  |  | } elsif( $token->[O_TYPE] == T_PATTERN ) { | 
| 1033 | 0 |  |  |  |  | 0 | my $pattern; | 
| 1034 | 0 | 0 | 0 |  |  | 0 | if( $token->[O_VALUE] == OP_QL_M || $token->[O_VALUE] == OP_QL_QR ) { | 
|  |  | 0 |  |  |  |  |  | 
| 1035 | 0 |  |  |  |  | 0 | $pattern = _parse_match( $self, $token ); | 
| 1036 |  |  |  |  |  |  | } elsif( $token->[O_VALUE] == OP_QL_S ) { | 
| 1037 | 0 |  |  |  |  | 0 | $pattern = _parse_substitution( $self, $token ); | 
| 1038 |  |  |  |  |  |  | } else { | 
| 1039 | 0 |  |  |  |  | 0 | die; | 
| 1040 |  |  |  |  |  |  | } | 
| 1041 |  |  |  |  |  |  |  | 
| 1042 | 0 | 0 | 0 |  |  | 0 | if( !$is_bind && $token->[O_VALUE] != OP_QL_QR ) { | 
| 1043 | 0 |  |  |  |  | 0 | $pattern = Language::P::ParseTree::BinOp->new | 
| 1044 |  |  |  |  |  |  | ( { op    => OP_MATCH, | 
| 1045 |  |  |  |  |  |  | left  => _find_symbol( $self, VALUE_SCALAR, '_', T_FQ_ID ), | 
| 1046 |  |  |  |  |  |  | right => $pattern, | 
| 1047 |  |  |  |  |  |  | } ); | 
| 1048 |  |  |  |  |  |  | } | 
| 1049 |  |  |  |  |  |  |  | 
| 1050 | 0 |  |  |  |  | 0 | return $pattern; | 
| 1051 |  |  |  |  |  |  | } elsif( $token->[O_TYPE] == T_NUMBER ) { | 
| 1052 | 244 |  |  |  |  | 1868 | return Language::P::ParseTree::Constant->new | 
| 1053 |  |  |  |  |  |  | ( { value => $token->[O_VALUE], | 
| 1054 |  |  |  |  |  |  | flags => $token->[O_NUM_FLAGS]|CONST_NUMBER, | 
| 1055 |  |  |  |  |  |  | } ); | 
| 1056 |  |  |  |  |  |  | } elsif( $token->[O_TYPE] == T_PACKAGE ) { | 
| 1057 | 0 |  |  |  |  | 0 | return Language::P::ParseTree::Constant->new | 
| 1058 |  |  |  |  |  |  | ( { value => $self->_package, | 
| 1059 |  |  |  |  |  |  | flags => CONST_STRING, | 
| 1060 |  |  |  |  |  |  | } ); | 
| 1061 |  |  |  |  |  |  | } elsif( $token->[O_TYPE] == T_STRING ) { | 
| 1062 | 0 |  |  |  |  | 0 | return Language::P::ParseTree::Constant->new | 
| 1063 |  |  |  |  |  |  | ( { value => $token->[O_VALUE], | 
| 1064 |  |  |  |  |  |  | flags => CONST_STRING, | 
| 1065 |  |  |  |  |  |  | } ); | 
| 1066 |  |  |  |  |  |  | } elsif(    $token->[O_TYPE] == T_DOLLAR | 
| 1067 |  |  |  |  |  |  | || $token->[O_TYPE] == T_AT | 
| 1068 |  |  |  |  |  |  | || $token->[O_TYPE] == T_PERCENT | 
| 1069 |  |  |  |  |  |  | || $token->[O_TYPE] == T_STAR | 
| 1070 |  |  |  |  |  |  | || $token->[O_TYPE] == T_AMPERSAND | 
| 1071 |  |  |  |  |  |  | || $token->[O_TYPE] == T_ARYLEN ) { | 
| 1072 | 288 |  |  |  |  | 756 | return ( _parse_indirobj_maybe_subscripts( $self, $token ), 1 ); | 
| 1073 |  |  |  |  |  |  | } elsif(    $token->[O_TYPE] == T_ID ) { | 
| 1074 | 230 |  |  |  |  | 366 | my $tokidt = $token->[O_ID_TYPE]; | 
| 1075 |  |  |  |  |  |  |  | 
| 1076 | 230 | 100 | 66 |  |  | 656 | if( !is_keyword( $token->[O_ID_TYPE] ) ) { | 
|  |  | 100 | 66 |  |  |  |  | 
|  |  | 100 | 100 |  |  |  |  | 
|  |  | 100 | 100 |  |  |  |  | 
|  |  | 100 | 100 |  |  |  |  | 
| 1077 | 183 |  |  |  |  | 593 | return _parse_listop( $self, $token ); | 
| 1078 |  |  |  |  |  |  | } elsif(    $tokidt == OP_MY | 
| 1079 |  |  |  |  |  |  | || $tokidt == OP_OUR | 
| 1080 |  |  |  |  |  |  | || $tokidt == OP_STATE ) { | 
| 1081 | 8 |  |  |  |  | 30 | return _parse_lexical( $self, $token->[O_ID_TYPE] ); | 
| 1082 |  |  |  |  |  |  | } elsif( $tokidt == KEY_SUB ) { | 
| 1083 | 4 |  |  |  |  | 36 | return _parse_sub( $self, 2, 1 ); | 
| 1084 |  |  |  |  |  |  | } elsif(    $tokidt == OP_GOTO | 
| 1085 |  |  |  |  |  |  | || $tokidt == OP_LAST | 
| 1086 |  |  |  |  |  |  | || $tokidt == OP_NEXT | 
| 1087 |  |  |  |  |  |  | || $tokidt == OP_REDO ) { | 
| 1088 | 15 |  |  |  |  | 53 | my $id = $self->lexer->lex; | 
| 1089 | 15 |  |  |  |  | 36 | my $dest; | 
| 1090 | 15 | 100 | 100 |  |  | 99 | if( $id->[O_TYPE] == T_ID && $id->[O_ID_TYPE] == T_ID ) { | 
| 1091 | 6 |  |  |  |  | 15 | $dest = $id->[O_VALUE]; | 
| 1092 |  |  |  |  |  |  | } else { | 
| 1093 | 9 |  |  |  |  | 31 | $self->lexer->unlex( $id ); | 
| 1094 | 9 |  |  |  |  | 67 | $dest = _parse_term( $self, PREC_LOWEST ); | 
| 1095 | 9 | 50 |  |  |  | 25 | if( $dest ) { | 
| 1096 | 0 | 0 |  |  |  | 0 | $dest = $dest->left | 
| 1097 |  |  |  |  |  |  | if $dest->isa( 'Language::P::ParseTree::Parentheses' ); | 
| 1098 | 0 | 0 |  |  |  | 0 | $dest = $dest->value if $dest->is_constant; | 
| 1099 |  |  |  |  |  |  | } | 
| 1100 |  |  |  |  |  |  | } | 
| 1101 |  |  |  |  |  |  |  | 
| 1102 | 15 |  |  |  |  | 183 | my $jump = Language::P::ParseTree::Jump->new | 
| 1103 |  |  |  |  |  |  | ( { op   => $tokidt, | 
| 1104 |  |  |  |  |  |  | left => $dest, | 
| 1105 |  |  |  |  |  |  | } ); | 
| 1106 | 15 | 100 | 66 |  |  | 90 | push @{$self->_lexical_state->[-1]{sub}{jumps}}, $jump | 
|  | 3 |  |  |  |  | 17 |  | 
| 1107 |  |  |  |  |  |  | if $tokidt == OP_GOTO && !ref( $dest ); | 
| 1108 |  |  |  |  |  |  |  | 
| 1109 | 15 |  |  |  |  | 70 | return $jump; | 
| 1110 |  |  |  |  |  |  | } elsif( $tokidt == KEY_LOCAL ) { | 
| 1111 | 14 |  |  |  |  | 65 | return Language::P::ParseTree::Local->new | 
| 1112 |  |  |  |  |  |  | ( { left => _parse_term_list_if_parens( $self, PREC_NAMED_UNOP ), | 
| 1113 |  |  |  |  |  |  | } ); | 
| 1114 |  |  |  |  |  |  | } | 
| 1115 |  |  |  |  |  |  | } elsif( $token->[O_TYPE] == T_OPHASH ) { | 
| 1116 | 0 |  |  |  |  | 0 | my $expr = _parse_bracketed_expr( $self, T_OPBRK, 1, 1 ); | 
| 1117 |  |  |  |  |  |  |  | 
| 1118 | 0 |  |  |  |  | 0 | return Language::P::ParseTree::ReferenceConstructor->new | 
| 1119 |  |  |  |  |  |  | ( { expression => $expr, | 
| 1120 |  |  |  |  |  |  | type       => VALUE_HASH, | 
| 1121 |  |  |  |  |  |  | } ); | 
| 1122 |  |  |  |  |  |  | } elsif( $token->[O_TYPE] == T_OPSQ ) { | 
| 1123 | 0 |  |  |  |  | 0 | my $expr = _parse_bracketed_expr( $self, T_OPSQ, 1, 1 ); | 
| 1124 |  |  |  |  |  |  |  | 
| 1125 | 0 |  |  |  |  | 0 | return Language::P::ParseTree::ReferenceConstructor->new | 
| 1126 |  |  |  |  |  |  | ( { expression => $expr, | 
| 1127 |  |  |  |  |  |  | type       => VALUE_ARRAY, | 
| 1128 |  |  |  |  |  |  | } ); | 
| 1129 |  |  |  |  |  |  | } | 
| 1130 |  |  |  |  |  |  |  | 
| 1131 | 121 |  |  |  |  | 309 | return undef; | 
| 1132 |  |  |  |  |  |  | } | 
| 1133 |  |  |  |  |  |  |  | 
| 1134 |  |  |  |  |  |  | sub _parse_term_terminal_maybe_subscripts { | 
| 1135 | 1048 |  |  | 1048 |  | 1417 | my( $self, $token, $is_bind ) = @_; | 
| 1136 | 1048 |  |  |  |  | 3057 | my( $term, $no_subscr ) = _parse_term_terminal( $self, $token, $is_bind ); | 
| 1137 |  |  |  |  |  |  |  | 
| 1138 | 1048 | 100 | 100 |  |  | 5697 | return $term if $no_subscr || !$term; | 
| 1139 | 639 |  |  |  |  | 1488 | return _parse_maybe_subscript_rest( $self, $term, 1 ); | 
| 1140 |  |  |  |  |  |  | } | 
| 1141 |  |  |  |  |  |  |  | 
| 1142 |  |  |  |  |  |  | sub _parse_indirobj_maybe_subscripts { | 
| 1143 | 317 |  |  | 317 |  | 477 | my( $self, $token ) = @_; | 
| 1144 | 317 |  |  |  |  | 779 | my $indir = _parse_indirobj( $self, 0 ); | 
| 1145 | 317 |  |  |  |  | 1036 | my $sigil = $token_to_sigil{$token->[O_TYPE]}; | 
| 1146 | 317 |  | 33 |  |  | 2112 | my $is_id = ref( $indir ) eq 'ARRAY' && $indir->[O_TYPE] == T_ID; | 
| 1147 |  |  |  |  |  |  |  | 
| 1148 |  |  |  |  |  |  | # no subscripting/slicing possible for '%' | 
| 1149 | 317 | 50 |  |  |  | 755 | if( $sigil == VALUE_HASH ) { | 
| 1150 | 0 | 0 |  |  |  | 0 | return $is_id ? _find_symbol( $self, $sigil, $indir->[O_VALUE], $indir->[O_ID_TYPE] ) : | 
| 1151 |  |  |  |  |  |  | Language::P::ParseTree::Dereference->new | 
| 1152 |  |  |  |  |  |  | ( { left  => $indir, | 
| 1153 |  |  |  |  |  |  | op    => OP_DEREFERENCE_HASH, | 
| 1154 |  |  |  |  |  |  | } ); | 
| 1155 |  |  |  |  |  |  | } | 
| 1156 |  |  |  |  |  |  |  | 
| 1157 | 317 |  |  |  |  | 978 | my $next = $self->lexer->peek( X_OPERATOR ); | 
| 1158 |  |  |  |  |  |  |  | 
| 1159 | 317 | 50 |  |  |  | 860 | if( $sigil == VALUE_SUB ) { | 
| 1160 | 0 | 0 |  |  |  | 0 | my $deref = $is_id ? _find_symbol( $self, $sigil, $indir->[O_VALUE], $indir->[O_ID_TYPE] ) : | 
| 1161 |  |  |  |  |  |  | $indir; | 
| 1162 |  |  |  |  |  |  |  | 
| 1163 | 0 |  |  |  |  | 0 | return _parse_indirect_function_call( $self, $deref, | 
| 1164 |  |  |  |  |  |  | $next->[O_TYPE] == T_OPPAR, 1 ); | 
| 1165 |  |  |  |  |  |  | } | 
| 1166 |  |  |  |  |  |  |  | 
| 1167 |  |  |  |  |  |  | # simplify the code below by resolving the symbol here, so a | 
| 1168 |  |  |  |  |  |  | # dereference will be constructed below (probably an unary | 
| 1169 |  |  |  |  |  |  | # operator would be more consistent) | 
| 1170 | 317 | 100 | 66 |  |  | 980 | if( $sigil == VALUE_ARRAY_LENGTH && $is_id ) { | 
| 1171 | 1 |  |  |  |  | 21 | $indir = _find_symbol( $self, VALUE_ARRAY, $indir->[O_VALUE], $indir->[O_ID_TYPE] ); | 
| 1172 | 1 |  |  |  |  | 5 | $is_id = 0; | 
| 1173 |  |  |  |  |  |  | } | 
| 1174 |  |  |  |  |  |  |  | 
| 1175 | 317 | 100 |  |  |  | 859 | if( $next->[O_TYPE] == T_ARROW ) { | 
| 1176 | 5 | 50 |  |  |  | 24 | my $deref = $is_id ? _find_symbol( $self, $sigil, $indir->[O_VALUE], $indir->[O_ID_TYPE] ) : | 
| 1177 |  |  |  |  |  |  | Language::P::ParseTree::Dereference->new | 
| 1178 |  |  |  |  |  |  | ( { left  => $indir, | 
| 1179 |  |  |  |  |  |  | op    => $dereference_type{$sigil}, | 
| 1180 |  |  |  |  |  |  | } ); | 
| 1181 |  |  |  |  |  |  |  | 
| 1182 | 5 |  |  |  |  | 18 | return _parse_maybe_subscript_rest( $self, $deref ); | 
| 1183 |  |  |  |  |  |  | } | 
| 1184 |  |  |  |  |  |  |  | 
| 1185 | 312 |  |  |  |  | 453 | my( $is_slice, $sym_sigil ); | 
| 1186 | 312 | 100 | 100 |  |  | 3676 | if(    ( $sigil == VALUE_ARRAY || $sigil == VALUE_SCALAR ) | 
|  |  | 50 | 66 |  |  |  |  | 
|  |  |  | 66 |  |  |  |  | 
|  |  |  | 33 |  |  |  |  | 
| 1187 |  |  |  |  |  |  | && ( $next->[O_TYPE] == T_OPSQ || $next->[O_TYPE] == T_OPBRK ) ) { | 
| 1188 | 6 | 50 |  |  |  | 23 | $sym_sigil = $next->[O_TYPE] == T_OPBRK ? VALUE_HASH : VALUE_ARRAY; | 
| 1189 | 6 |  |  |  |  | 23 | $is_slice = $sigil == VALUE_ARRAY; | 
| 1190 |  |  |  |  |  |  | } elsif( $sigil == VALUE_GLOB && $next->[O_TYPE] == T_OPBRK ) { | 
| 1191 | 0 |  |  |  |  | 0 | $sym_sigil = VALUE_GLOB; | 
| 1192 |  |  |  |  |  |  | } else { | 
| 1193 | 306 | 100 |  |  |  | 1183 | return $is_id ? _find_symbol( $self, $sigil, $indir->[O_VALUE], $indir->[O_ID_TYPE] ) : | 
| 1194 |  |  |  |  |  |  | Language::P::ParseTree::Dereference->new | 
| 1195 |  |  |  |  |  |  | ( { left  => $indir, | 
| 1196 |  |  |  |  |  |  | op    => $dereference_type{$sigil}, | 
| 1197 |  |  |  |  |  |  | } ); | 
| 1198 |  |  |  |  |  |  | } | 
| 1199 |  |  |  |  |  |  |  | 
| 1200 | 6 |  |  |  |  | 31 | my $subscript = _parse_bracketed_expr( $self, $next->[O_TYPE], 0 ); | 
| 1201 | 6 | 50 |  |  |  | 34 | my $subscripted = $is_id ? _find_symbol( $self, $sym_sigil, $indir->[O_VALUE], $indir->[O_ID_TYPE] ) : | 
| 1202 |  |  |  |  |  |  | $indir; | 
| 1203 | 6 | 50 |  |  |  | 31 | my $subscript_type = $next->[O_TYPE] == T_OPBRK ? VALUE_HASH : VALUE_ARRAY; | 
| 1204 |  |  |  |  |  |  |  | 
| 1205 | 6 | 50 |  |  |  | 24 | if( $is_slice ) { | 
| 1206 | 0 | 0 |  |  |  | 0 | return Language::P::ParseTree::Slice->new | 
| 1207 |  |  |  |  |  |  | ( { subscripted => $subscripted, | 
| 1208 |  |  |  |  |  |  | subscript   => $subscript, | 
| 1209 |  |  |  |  |  |  | type        => $subscript_type, | 
| 1210 |  |  |  |  |  |  | reference   => $is_id ? 0 : 1, | 
| 1211 |  |  |  |  |  |  | } ); | 
| 1212 |  |  |  |  |  |  | } else { | 
| 1213 | 6 | 50 |  |  |  | 85 | my $term = Language::P::ParseTree::Subscript->new | 
| 1214 |  |  |  |  |  |  | ( { subscripted => $subscripted, | 
| 1215 |  |  |  |  |  |  | subscript   => $subscript, | 
| 1216 |  |  |  |  |  |  | type        => $subscript_type, | 
| 1217 |  |  |  |  |  |  | reference   => $is_id ? 0 : 1, | 
| 1218 |  |  |  |  |  |  | } ); | 
| 1219 |  |  |  |  |  |  |  | 
| 1220 | 6 |  |  |  |  | 26 | return _parse_maybe_subscript_rest( $self, $term ); | 
| 1221 |  |  |  |  |  |  | } | 
| 1222 |  |  |  |  |  |  | } | 
| 1223 |  |  |  |  |  |  |  | 
| 1224 |  |  |  |  |  |  | sub _parse_lexical { | 
| 1225 | 8 |  |  | 8 |  | 17 | my( $self, $keyword ) = @_; | 
| 1226 |  |  |  |  |  |  |  | 
| 1227 | 8 | 50 | 33 |  |  | 29 | die $keyword unless $keyword == OP_MY || $keyword == OP_OUR; | 
| 1228 |  |  |  |  |  |  |  | 
| 1229 | 8 |  |  |  |  | 25 | local $self->{_in_declaration} = 1; | 
| 1230 | 8 |  |  |  |  | 24 | my $term = _parse_term_list_if_parens( $self, PREC_NAMED_UNOP ); | 
| 1231 |  |  |  |  |  |  |  | 
| 1232 | 8 |  |  |  |  | 33 | return _process_declaration( $self, $term, $keyword ); | 
| 1233 |  |  |  |  |  |  | } | 
| 1234 |  |  |  |  |  |  |  | 
| 1235 |  |  |  |  |  |  | sub _process_declaration { | 
| 1236 | 15 |  |  | 15 |  | 43 | my( $self, $decl, $keyword ) = @_; | 
| 1237 |  |  |  |  |  |  |  | 
| 1238 | 15 | 100 |  |  |  | 161 | if( $decl->isa( 'Language::P::ParseTree::List' ) ) { | 
|  |  | 50 |  |  |  |  |  | 
| 1239 | 4 |  |  |  |  | 19 | foreach my $e ( @{$decl->expressions} ) { | 
|  | 4 |  |  |  |  | 13 |  | 
| 1240 | 4 |  |  |  |  | 26 | $e = _process_declaration( $self, $e, $keyword ); | 
| 1241 |  |  |  |  |  |  | } | 
| 1242 |  |  |  |  |  |  |  | 
| 1243 | 4 |  |  |  |  | 18 | return $decl; | 
| 1244 |  |  |  |  |  |  | } elsif( $decl->isa( 'Language::P::ParseTree::Symbol' ) ) { | 
| 1245 | 11 |  |  |  |  | 54 | my $decl = Language::P::ParseTree::LexicalDeclaration->new | 
| 1246 |  |  |  |  |  |  | ( { name    => $decl->name, | 
| 1247 |  |  |  |  |  |  | sigil   => $decl->sigil, | 
| 1248 |  |  |  |  |  |  | flags   => $declaration_to_flags{$keyword}, | 
| 1249 |  |  |  |  |  |  | } ); | 
| 1250 | 11 |  |  |  |  | 44 | push @{$self->_pending_lexicals}, $decl; | 
|  | 11 |  |  |  |  | 45 |  | 
| 1251 |  |  |  |  |  |  |  | 
| 1252 | 11 |  |  |  |  | 142 | return $decl; | 
| 1253 |  |  |  |  |  |  | } else { | 
| 1254 | 0 |  |  |  |  | 0 | die 'Invalid node ', ref( $decl ), ' in declaration'; | 
| 1255 |  |  |  |  |  |  | } | 
| 1256 |  |  |  |  |  |  | } | 
| 1257 |  |  |  |  |  |  |  | 
| 1258 |  |  |  |  |  |  | sub _parse_term_p { | 
| 1259 | 1048 |  |  | 1048 |  | 1701 | my( $self, $prec, $token, $lookahead, $is_bind ) = @_; | 
| 1260 | 1048 |  |  |  |  | 2385 | my $terminal = _parse_term_terminal_maybe_subscripts( $self, $token, $is_bind ); | 
| 1261 |  |  |  |  |  |  |  | 
| 1262 | 1048 | 100 | 100 |  |  | 5040 | return $terminal if $terminal && !$lookahead; | 
| 1263 |  |  |  |  |  |  |  | 
| 1264 | 710 | 100 |  |  |  | 2157 | if( $terminal ) { | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
| 1265 | 589 |  |  |  |  | 1689 | my $la = $self->lexer->peek( X_OPERATOR ); | 
| 1266 | 589 |  |  |  |  | 1946 | my $binprec = $prec_assoc_bin{$la->[O_TYPE]}; | 
| 1267 |  |  |  |  |  |  |  | 
| 1268 | 589 | 100 | 100 |  |  | 2763 | if( !$binprec || $binprec->[0] > $prec ) { | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 1269 | 370 |  |  |  |  | 852 | return $terminal; | 
| 1270 |  |  |  |  |  |  | } elsif( $la->[O_TYPE] == T_INTERR ) { | 
| 1271 | 9 |  |  |  |  | 25 | _lex_token( $self, T_INTERR ); | 
| 1272 | 9 |  |  |  |  | 31 | return _parse_ternary( $self, PREC_TERNARY, $terminal ); | 
| 1273 |  |  |  |  |  |  | } elsif( $binprec ) { | 
| 1274 | 210 |  |  |  |  | 650 | return _parse_term_n( $self, $binprec->[0], | 
| 1275 |  |  |  |  |  |  | $terminal ); | 
| 1276 |  |  |  |  |  |  | } else { | 
| 1277 | 0 |  |  |  |  | 0 | _syntax_error( $self, $la ); | 
| 1278 |  |  |  |  |  |  | } | 
| 1279 |  |  |  |  |  |  | } elsif( $token->[O_TYPE] == T_FILETEST ) { | 
| 1280 | 0 |  |  |  |  | 0 | return _parse_listop_like( $self, undef, 1, | 
| 1281 |  |  |  |  |  |  | Language::P::ParseTree::Builtin->new | 
| 1282 |  |  |  |  |  |  | ( { function => $token->[O_FT_OP], | 
| 1283 |  |  |  |  |  |  | } ) ); | 
| 1284 |  |  |  |  |  |  | } elsif( my $p = $prec_assoc_un{$token->[O_TYPE]} ) { | 
| 1285 | 6 |  |  |  |  | 44 | my $rest = _parse_term_n( $self, $p->[0] ); | 
| 1286 |  |  |  |  |  |  |  | 
| 1287 | 6 |  |  |  |  | 94 | return Language::P::ParseTree::UnOp->new | 
| 1288 |  |  |  |  |  |  | ( { op    => $p->[2], | 
| 1289 |  |  |  |  |  |  | left  => $rest, | 
| 1290 |  |  |  |  |  |  | } ); | 
| 1291 |  |  |  |  |  |  | } elsif( $token->[O_TYPE] == T_OPPAR ) { | 
| 1292 | 15 |  |  |  |  | 47 | my $term = _parse_expr( $self ); | 
| 1293 | 15 |  |  |  |  | 51 | _lex_token( $self, T_CLPAR ); | 
| 1294 |  |  |  |  |  |  |  | 
| 1295 | 15 | 50 |  |  |  | 193 | if( !$term ) { | 
|  |  | 100 |  |  |  |  |  | 
| 1296 |  |  |  |  |  |  | # empty list | 
| 1297 | 0 |  |  |  |  | 0 | return Language::P::ParseTree::List->new | 
| 1298 |  |  |  |  |  |  | ( { expressions => [], | 
| 1299 |  |  |  |  |  |  | } ); | 
| 1300 |  |  |  |  |  |  | } elsif( !$term->isa( 'Language::P::ParseTree::List' ) ) { | 
| 1301 |  |  |  |  |  |  | # record that there were prentheses, unless it is a list | 
| 1302 | 9 |  |  |  |  | 83 | return Language::P::ParseTree::Parentheses->new | 
| 1303 |  |  |  |  |  |  | ( { left => $term, | 
| 1304 |  |  |  |  |  |  | } ); | 
| 1305 |  |  |  |  |  |  | } else { | 
| 1306 | 6 |  |  |  |  | 18 | return $term; | 
| 1307 |  |  |  |  |  |  | } | 
| 1308 |  |  |  |  |  |  | } | 
| 1309 |  |  |  |  |  |  |  | 
| 1310 | 100 |  |  |  |  | 223 | return undef; | 
| 1311 |  |  |  |  |  |  | } | 
| 1312 |  |  |  |  |  |  |  | 
| 1313 |  |  |  |  |  |  | sub _parse_ternary { | 
| 1314 | 67 |  |  | 67 |  | 165 | my( $self, $prec, $terminal ) = @_; | 
| 1315 |  |  |  |  |  |  |  | 
| 1316 | 67 |  |  |  |  | 197 | my $iftrue = _parse_term_n( $self, PREC_TERNARY_COLON - 1 ); | 
| 1317 | 67 |  |  |  |  | 234 | _lex_token( $self, T_COLON ); | 
| 1318 | 67 |  |  |  |  | 196 | my $iffalse = _parse_term( $self, $prec ); | 
| 1319 |  |  |  |  |  |  |  | 
| 1320 | 67 |  |  |  |  | 638 | return Language::P::ParseTree::Ternary->new | 
| 1321 |  |  |  |  |  |  | ( { condition => $terminal, | 
| 1322 |  |  |  |  |  |  | iftrue    => $iftrue, | 
| 1323 |  |  |  |  |  |  | iffalse   => $iffalse, | 
| 1324 |  |  |  |  |  |  | } ); | 
| 1325 |  |  |  |  |  |  | } | 
| 1326 |  |  |  |  |  |  |  | 
| 1327 |  |  |  |  |  |  | sub _parse_term_n { | 
| 1328 | 1310 |  |  | 1310 |  | 2024 | my( $self, $prec, $terminal, $is_bind ) = @_; | 
| 1329 |  |  |  |  |  |  |  | 
| 1330 | 1310 | 100 |  |  |  | 2652 | if( !$terminal ) { | 
| 1331 | 347 |  |  |  |  | 1028 | my $token = $self->lexer->lex( X_TERM ); | 
| 1332 | 347 |  |  |  |  | 1078 | $terminal = _parse_term_p( $self, $prec, $token, undef, $is_bind ); | 
| 1333 |  |  |  |  |  |  |  | 
| 1334 | 347 | 50 |  |  |  | 1310 | if( !$terminal ) { | 
| 1335 | 0 |  |  |  |  | 0 | $self->lexer->unlex( $token ); | 
| 1336 | 0 |  |  |  |  | 0 | return undef; | 
| 1337 |  |  |  |  |  |  | } | 
| 1338 |  |  |  |  |  |  | } | 
| 1339 |  |  |  |  |  |  |  | 
| 1340 | 1310 |  |  |  |  | 1384 | for(;;) { | 
| 1341 | 1642 |  |  |  |  | 4438 | my $token = $self->lexer->lex( X_OPERATOR ); | 
| 1342 |  |  |  |  |  |  |  | 
| 1343 | 1642 | 50 | 33 |  |  | 13735 | if(    $token->[O_TYPE] == T_PLUSPLUS | 
| 1344 |  |  |  |  |  |  | || $token->[O_TYPE] == T_MINUSMINUS ) { | 
| 1345 | 0 | 0 |  |  |  | 0 | my $op = $token->[O_TYPE] == T_PLUSPLUS ? OP_POSTINC : OP_POSTDEC; | 
| 1346 | 0 |  |  |  |  | 0 | $terminal = Language::P::ParseTree::UnOp->new | 
| 1347 |  |  |  |  |  |  | ( { op    => $op, | 
| 1348 |  |  |  |  |  |  | left  => $terminal, | 
| 1349 |  |  |  |  |  |  | } ); | 
| 1350 | 0 |  |  |  |  | 0 | $token = $self->lexer->lex( X_OPERATOR ); | 
| 1351 |  |  |  |  |  |  | } | 
| 1352 |  |  |  |  |  |  |  | 
| 1353 | 1642 |  |  |  |  | 3092 | my $bin = $prec_assoc_bin{$token->[O_TYPE]}; | 
| 1354 | 1642 | 100 | 100 |  |  | 5617 | if( !$bin || $bin->[0] > $prec ) { | 
|  |  | 100 |  |  |  |  |  | 
| 1355 | 1310 |  |  |  |  | 3423 | $self->lexer->unlex( $token ); | 
| 1356 | 1310 |  |  |  |  | 6298 | last; | 
| 1357 |  |  |  |  |  |  | } elsif( $token->[O_TYPE] == T_INTERR ) { | 
| 1358 | 58 |  |  |  |  | 204 | $terminal = _parse_ternary( $self, PREC_TERNARY, $terminal ); | 
| 1359 |  |  |  |  |  |  | } else { | 
| 1360 |  |  |  |  |  |  | # do not try to use colon as binary | 
| 1361 | 274 | 50 |  |  |  | 749 | _syntax_error( $self, $token ) | 
| 1362 |  |  |  |  |  |  | if $token->[O_TYPE] == T_COLON; | 
| 1363 |  |  |  |  |  |  |  | 
| 1364 | 274 | 100 |  |  |  | 795 | my $q = $bin->[1] == ASSOC_RIGHT ? $bin->[0] : $bin->[0] - 1; | 
| 1365 | 274 |  | 33 |  |  | 1939 | my $rterm = _parse_term_n( $self, $q, undef, | 
| 1366 |  |  |  |  |  |  | (    $token->[O_TYPE] == T_MATCH | 
| 1367 |  |  |  |  |  |  | || $token->[O_TYPE] == T_NOTMATCH ) ); | 
| 1368 |  |  |  |  |  |  |  | 
| 1369 | 274 | 100 |  |  |  | 730 | if( $token->[O_TYPE] == T_COMMA ) { | 
| 1370 | 21 | 100 |  |  |  | 206 | if( $terminal->isa( 'Language::P::ParseTree::List' ) ) { | 
| 1371 | 7 | 50 |  |  |  | 24 | if( $rterm ) { | 
| 1372 | 7 |  |  |  |  | 11 | push @{$terminal->expressions}, $rterm; | 
|  | 7 |  |  |  |  | 26 |  | 
| 1373 | 7 |  |  |  |  | 128 | $rterm->set_parent( $terminal ); | 
| 1374 |  |  |  |  |  |  | } | 
| 1375 |  |  |  |  |  |  | } else { | 
| 1376 | 14 | 50 |  |  |  | 149 | $terminal = Language::P::ParseTree::List->new | 
| 1377 |  |  |  |  |  |  | ( { expressions => [ $terminal, $rterm ? $rterm : () ], | 
| 1378 |  |  |  |  |  |  | } ); | 
| 1379 |  |  |  |  |  |  | } | 
| 1380 |  |  |  |  |  |  | } else { | 
| 1381 | 253 |  |  |  |  | 2386 | $terminal = Language::P::ParseTree::BinOp->new | 
| 1382 |  |  |  |  |  |  | ( { op    => $bin->[2], | 
| 1383 |  |  |  |  |  |  | left  => $terminal, | 
| 1384 |  |  |  |  |  |  | right => $rterm, | 
| 1385 |  |  |  |  |  |  | } ); | 
| 1386 |  |  |  |  |  |  | } | 
| 1387 |  |  |  |  |  |  | } | 
| 1388 |  |  |  |  |  |  | } | 
| 1389 |  |  |  |  |  |  |  | 
| 1390 | 1310 |  |  |  |  | 2639 | return $terminal; | 
| 1391 |  |  |  |  |  |  | } | 
| 1392 |  |  |  |  |  |  |  | 
| 1393 |  |  |  |  |  |  | sub _parse_term { | 
| 1394 | 701 |  |  | 701 |  | 1080 | my( $self, $prec ) = @_; | 
| 1395 | 701 |  |  |  |  | 1862 | my $token = $self->lexer->lex( X_TERM ); | 
| 1396 | 701 |  |  |  |  | 3654 | my $terminal = _parse_term_p( $self, $prec, $token, 1, 0 ); | 
| 1397 |  |  |  |  |  |  |  | 
| 1398 | 701 | 100 |  |  |  | 1758 | if( $terminal ) { | 
| 1399 | 601 |  |  |  |  | 1272 | $terminal = _parse_term_n( $self, $prec, $terminal ); | 
| 1400 |  |  |  |  |  |  |  | 
| 1401 | 601 |  |  |  |  | 1714 | return $terminal; | 
| 1402 |  |  |  |  |  |  | } | 
| 1403 |  |  |  |  |  |  |  | 
| 1404 | 100 |  |  |  |  | 346 | $self->lexer->unlex( $token ); | 
| 1405 |  |  |  |  |  |  |  | 
| 1406 | 100 |  |  |  |  | 606 | return undef; | 
| 1407 |  |  |  |  |  |  | } | 
| 1408 |  |  |  |  |  |  |  | 
| 1409 |  |  |  |  |  |  | sub _parse_term_list_if_parens { | 
| 1410 | 22 |  |  | 22 |  | 53 | my( $self, $prec ) = @_; | 
| 1411 | 22 |  |  |  |  | 63 | my $term = _parse_term( $self, $prec ); | 
| 1412 |  |  |  |  |  |  |  | 
| 1413 | 22 | 100 |  |  |  | 244 | if( $term->isa( 'Language::P::ParseTree::Parentheses' ) ) { | 
| 1414 | 4 |  |  |  |  | 13 | return Language::P::ParseTree::List->new | 
| 1415 |  |  |  |  |  |  | ( { expressions => [ $term->left ], | 
| 1416 |  |  |  |  |  |  | } ); | 
| 1417 |  |  |  |  |  |  | } | 
| 1418 |  |  |  |  |  |  |  | 
| 1419 | 18 |  |  |  |  | 121 | return $term; | 
| 1420 |  |  |  |  |  |  | } | 
| 1421 |  |  |  |  |  |  |  | 
| 1422 |  |  |  |  |  |  | sub _add_implicit_return { | 
| 1423 | 28 |  |  | 28 |  | 91 | my( $line ) = @_; | 
| 1424 |  |  |  |  |  |  |  | 
| 1425 | 28 | 100 |  |  |  | 139 | return $line unless $line->can_implicit_return; | 
| 1426 | 14 | 100 |  |  |  | 146 | if( !$line->is_compound ) { | 
| 1427 | 8 |  |  |  |  | 60 | return Language::P::ParseTree::Builtin->new | 
| 1428 |  |  |  |  |  |  | ( { arguments => [ $line ], | 
| 1429 |  |  |  |  |  |  | function  => OP_RETURN, | 
| 1430 |  |  |  |  |  |  | } ); | 
| 1431 |  |  |  |  |  |  | } | 
| 1432 |  |  |  |  |  |  |  | 
| 1433 |  |  |  |  |  |  | # compound and can implicitly return | 
| 1434 | 6 | 100 | 66 |  |  | 79 | if( $line->isa( 'Language::P::ParseTree::Block' ) && @{$line->lines} ) { | 
|  | 3 | 100 |  |  |  | 44 |  | 
|  |  | 50 |  |  |  |  |  | 
| 1435 | 3 |  |  |  |  | 27 | $line->lines->[-1] = _add_implicit_return( $line->lines->[-1] ); | 
| 1436 |  |  |  |  |  |  | } elsif( $line->isa( 'Language::P::ParseTree::Conditional' ) ) { | 
| 1437 | 1 |  |  |  |  | 3 | _add_implicit_return( $_ ) foreach @{$line->iftrues}; | 
|  | 1 |  |  |  |  | 5 |  | 
| 1438 | 1 | 50 |  |  |  | 4 | _add_implicit_return( $line->iffalse ) if $line->iffalse; | 
| 1439 |  |  |  |  |  |  | } elsif( $line->isa( 'Language::P::ParseTree::ConditionalBlock' ) ) { | 
| 1440 | 2 |  |  |  |  | 6 | _add_implicit_return( $line->block ) | 
| 1441 |  |  |  |  |  |  | } else { | 
| 1442 | 0 |  |  |  |  | 0 | Carp::confess( "Unhandled statement type: ", ref( $line ) ); | 
| 1443 |  |  |  |  |  |  | } | 
| 1444 |  |  |  |  |  |  |  | 
| 1445 | 6 |  |  |  |  | 43 | return $line; | 
| 1446 |  |  |  |  |  |  | } | 
| 1447 |  |  |  |  |  |  |  | 
| 1448 |  |  |  |  |  |  | sub _parse_block_rest { | 
| 1449 | 78 |  |  | 78 |  | 162 | my( $self, $flags, $end_token ) = @_; | 
| 1450 |  |  |  |  |  |  |  | 
| 1451 | 78 |  | 50 |  |  | 335 | $end_token ||= T_CLBRK; | 
| 1452 | 78 | 100 |  |  |  | 296 | $self->_enter_scope if $flags & BLOCK_OPEN_SCOPE; | 
| 1453 |  |  |  |  |  |  |  | 
| 1454 | 78 |  |  |  |  | 404 | my @lines; | 
| 1455 | 78 |  |  |  |  | 115 | for(;;) { | 
| 1456 | 211 |  |  |  |  | 669 | my $token = $self->lexer->lex( X_STATE ); | 
| 1457 | 211 | 100 |  |  |  | 711 | if( $token->[O_TYPE] == $end_token ) { | 
| 1458 | 78 | 100 | 66 |  |  | 419 | if( $flags & BLOCK_IMPLICIT_RETURN && @lines ) { | 
| 1459 | 21 |  |  |  |  | 84 | for( my $i = $#lines; $i >= 0; --$i ) { | 
| 1460 | 21 | 50 |  |  |  | 193 | next if $lines[$i]->is_declaration; | 
| 1461 | 21 |  |  |  |  | 158 | $lines[$i] = _add_implicit_return( $lines[$i] ); | 
| 1462 | 21 |  |  |  |  | 155 | last; | 
| 1463 |  |  |  |  |  |  | } | 
| 1464 |  |  |  |  |  |  | } | 
| 1465 |  |  |  |  |  |  |  | 
| 1466 | 78 | 100 |  |  |  | 359 | $self->_leave_scope if $flags & BLOCK_OPEN_SCOPE; | 
| 1467 | 78 | 100 |  |  |  | 197 | if( $flags & BLOCK_BARE ) { | 
| 1468 | 7 |  |  |  |  | 32 | my $continue = _parse_continue( $self ); | 
| 1469 | 7 |  |  |  |  | 132 | return Language::P::ParseTree::BareBlock->new | 
| 1470 |  |  |  |  |  |  | ( { lines    => \@lines, | 
| 1471 |  |  |  |  |  |  | continue => $continue, | 
| 1472 |  |  |  |  |  |  | } ); | 
| 1473 |  |  |  |  |  |  | } else { | 
| 1474 | 71 |  |  |  |  | 591 | return Language::P::ParseTree::Block->new | 
| 1475 |  |  |  |  |  |  | ( { lines => \@lines, | 
| 1476 |  |  |  |  |  |  | } ); | 
| 1477 |  |  |  |  |  |  | } | 
| 1478 |  |  |  |  |  |  | } else { | 
| 1479 | 133 |  |  |  |  | 419 | $self->lexer->unlex( $token ); | 
| 1480 | 133 |  |  |  |  | 847 | my $line = _parse_line( $self ); | 
| 1481 |  |  |  |  |  |  |  | 
| 1482 | 133 | 50 |  |  |  | 710 | push @lines, $line if $line; # skip empty satements | 
| 1483 |  |  |  |  |  |  | } | 
| 1484 |  |  |  |  |  |  | } | 
| 1485 |  |  |  |  |  |  | } | 
| 1486 |  |  |  |  |  |  |  | 
| 1487 |  |  |  |  |  |  | sub _parse_indirobj { | 
| 1488 | 317 |  |  | 317 |  | 600 | my( $self, $allow_fail ) = @_; | 
| 1489 | 317 |  |  |  |  | 923 | my $id = $self->lexer->lex_identifier( 0 ); | 
| 1490 |  |  |  |  |  |  |  | 
| 1491 | 317 | 50 |  |  |  | 786 | if( $id ) { | 
| 1492 | 317 |  |  |  |  | 625 | return $id; | 
| 1493 |  |  |  |  |  |  | } | 
| 1494 |  |  |  |  |  |  |  | 
| 1495 | 0 |  |  |  |  | 0 | my $token = $self->lexer->lex( X_OPERATOR ); | 
| 1496 |  |  |  |  |  |  |  | 
| 1497 | 0 | 0 |  |  |  | 0 | if( $token->[O_TYPE] == T_OPBRK ) { | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 1498 | 0 |  |  |  |  | 0 | my $block = _parse_block_rest( $self, BLOCK_OPEN_SCOPE ); | 
| 1499 |  |  |  |  |  |  |  | 
| 1500 | 0 |  |  |  |  | 0 | return $block; | 
| 1501 |  |  |  |  |  |  | } elsif( $token->[O_TYPE] == T_DOLLAR ) { | 
| 1502 | 0 |  |  |  |  | 0 | my $indir = _parse_indirobj( $self, 0 ); | 
| 1503 |  |  |  |  |  |  |  | 
| 1504 | 0 | 0 | 0 |  |  | 0 | if( ref( $indir ) eq 'ARRAY' && $indir->[O_TYPE] == T_ID ) { | 
| 1505 | 0 |  |  |  |  | 0 | return _find_symbol( $self, VALUE_SCALAR, $indir->[O_VALUE], $indir->[O_ID_TYPE] ); | 
| 1506 |  |  |  |  |  |  | } else { | 
| 1507 | 0 |  |  |  |  | 0 | return Language::P::ParseTree::Dereference->new | 
| 1508 |  |  |  |  |  |  | ( { left  => $indir, | 
| 1509 |  |  |  |  |  |  | op    => OP_DEREFERENCE_SCALAR, | 
| 1510 |  |  |  |  |  |  | } ); | 
| 1511 |  |  |  |  |  |  | } | 
| 1512 |  |  |  |  |  |  | } elsif( $allow_fail ) { | 
| 1513 | 0 |  |  |  |  | 0 | $self->lexer->unlex( $token ); | 
| 1514 |  |  |  |  |  |  |  | 
| 1515 | 0 |  |  |  |  | 0 | return undef; | 
| 1516 |  |  |  |  |  |  | } else { | 
| 1517 | 0 |  |  |  |  | 0 | _syntax_error( $self, $token ); | 
| 1518 |  |  |  |  |  |  | } | 
| 1519 |  |  |  |  |  |  | } | 
| 1520 |  |  |  |  |  |  |  | 
| 1521 |  |  |  |  |  |  | sub _declared_id { | 
| 1522 | 183 |  |  | 183 |  | 258 | my( $self, $op ) = @_; | 
| 1523 | 183 |  |  |  |  | 336 | my $call; | 
| 1524 | 183 |  |  |  |  | 287 | my $opidt = $op->[O_ID_TYPE]; | 
| 1525 |  |  |  |  |  |  |  | 
| 1526 | 183 | 100 |  |  |  | 535 | if( is_overridable( $opidt ) ) { | 
|  |  | 100 |  |  |  |  |  | 
| 1527 | 7 |  |  |  |  | 24 | my $st = $self->runtime->symbol_table; | 
| 1528 |  |  |  |  |  |  |  | 
| 1529 | 7 | 50 |  |  |  | 57 | if( $st->get_symbol( _qualify( $self, $op->[O_VALUE], $opidt ), '&' ) ) { | 
| 1530 | 0 |  |  |  |  | 0 | die "Overriding '" . $op->[O_VALUE] . "' not implemented"; | 
| 1531 |  |  |  |  |  |  | } | 
| 1532 | 7 |  |  |  |  | 70 | $call = Language::P::ParseTree::Overridable->new | 
| 1533 |  |  |  |  |  |  | ( { function  => $KEYWORD_TO_OP{$opidt}, | 
| 1534 |  |  |  |  |  |  | } ); | 
| 1535 |  |  |  |  |  |  |  | 
| 1536 | 7 |  |  |  |  | 25 | return ( $call, 1 ); | 
| 1537 |  |  |  |  |  |  | } elsif( is_builtin( $opidt ) ) { | 
| 1538 | 140 |  |  |  |  | 1450 | $call = Language::P::ParseTree::Builtin->new | 
| 1539 |  |  |  |  |  |  | ( { function  => $KEYWORD_TO_OP{$opidt}, | 
| 1540 |  |  |  |  |  |  | } ); | 
| 1541 |  |  |  |  |  |  |  | 
| 1542 | 140 |  |  |  |  | 511 | return ( $call, 1 ); | 
| 1543 |  |  |  |  |  |  | } else { | 
| 1544 | 36 |  |  |  |  | 120 | my $st = $self->runtime->symbol_table; | 
| 1545 |  |  |  |  |  |  |  | 
| 1546 | 36 | 100 |  |  |  | 304 | if( $st->get_symbol( _qualify( $self, $op->[O_VALUE], $opidt ), '&' ) ) { | 
| 1547 | 33 |  |  |  |  | 84 | return ( undef, 1 ); | 
| 1548 |  |  |  |  |  |  | } | 
| 1549 |  |  |  |  |  |  | } | 
| 1550 |  |  |  |  |  |  |  | 
| 1551 | 3 |  |  |  |  | 9 | return ( undef, 0 ); | 
| 1552 |  |  |  |  |  |  | } | 
| 1553 |  |  |  |  |  |  |  | 
| 1554 |  |  |  |  |  |  | sub _parse_listop { | 
| 1555 | 183 |  |  | 183 |  | 270 | my( $self, $op ) = @_; | 
| 1556 | 183 |  |  |  |  | 438 | my( $call, $declared ) = _declared_id( $self, $op ); | 
| 1557 |  |  |  |  |  |  |  | 
| 1558 | 183 |  |  |  |  | 684 | return _parse_listop_like( $self, $op, $declared, $call ); | 
| 1559 |  |  |  |  |  |  | } | 
| 1560 |  |  |  |  |  |  |  | 
| 1561 |  |  |  |  |  |  | sub _parse_listop_like { | 
| 1562 | 183 |  |  | 183 |  | 337 | my( $self, $op, $declared, $call ) = @_; | 
| 1563 | 183 | 100 |  |  |  | 705 | my $proto = $call ? $call->parsing_prototype : undef; | 
| 1564 | 183 | 50 |  |  |  | 1487 | my $expect = !$proto                                         ? X_TERM : | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
| 1565 |  |  |  |  |  |  | $proto->[2] & (PROTO_FILEHANDLE|PROTO_INDIROBJ) ? X_REF : | 
| 1566 |  |  |  |  |  |  | $proto->[2] & (PROTO_BLOCK|PROTO_SUB)           ? X_BLOCK : | 
| 1567 |  |  |  |  |  |  | X_TERM; | 
| 1568 | 183 |  |  |  |  | 566 | my $next = $self->lexer->peek( $expect ); | 
| 1569 | 183 |  |  |  |  | 299 | my( $args, $fh ); | 
| 1570 |  |  |  |  |  |  |  | 
| 1571 | 183 | 100 | 66 |  |  | 714 | if( !$call || !$declared ) { | 
| 1572 | 36 |  |  |  |  | 107 | my $st = $self->runtime->symbol_table; | 
| 1573 |  |  |  |  |  |  |  | 
| 1574 | 36 | 50 | 66 |  |  | 440 | if( $next->[O_TYPE] == T_ARROW ) { | 
|  |  | 50 |  |  |  |  |  | 
| 1575 | 0 |  |  |  |  | 0 | _lex_token( $self, T_ARROW ); | 
| 1576 | 0 |  |  |  |  | 0 | my $la = $self->lexer->peek( X_OPERATOR ); | 
| 1577 |  |  |  |  |  |  |  | 
| 1578 | 0 | 0 | 0 |  |  | 0 | if( $la->[O_TYPE] == T_ID || $la->[O_TYPE] == T_DOLLAR ) { | 
|  |  | 0 |  |  |  |  |  | 
| 1579 |  |  |  |  |  |  | # here we are calling the method on a bareword | 
| 1580 | 0 |  |  |  |  | 0 | my $invocant = Language::P::ParseTree::Constant->new | 
| 1581 |  |  |  |  |  |  | ( { value => $op->[O_VALUE], | 
| 1582 |  |  |  |  |  |  | flags => CONST_STRING, | 
| 1583 |  |  |  |  |  |  | } ); | 
| 1584 |  |  |  |  |  |  |  | 
| 1585 | 0 |  |  |  |  | 0 | return _parse_maybe_direct_method_call( $self, $invocant ); | 
| 1586 |  |  |  |  |  |  | } elsif( $la->[O_TYPE] == T_OPPAR ) { | 
| 1587 |  |  |  |  |  |  | # parsed as a normal sub call; go figure | 
| 1588 | 0 |  |  |  |  | 0 | $next = $la; | 
| 1589 |  |  |  |  |  |  | } else { | 
| 1590 | 0 |  |  |  |  | 0 | _syntax_error( $self, $la ); | 
| 1591 |  |  |  |  |  |  | } | 
| 1592 |  |  |  |  |  |  | } elsif( !$declared && $next->[O_TYPE] != T_OPPAR ) { | 
| 1593 |  |  |  |  |  |  | # not a declared subroutine, nor followed by parenthesis | 
| 1594 |  |  |  |  |  |  | # try to see if it is some sort of (indirect) method call | 
| 1595 | 0 |  |  |  |  | 0 | return _parse_maybe_indirect_method_call( $self, $op, $next ); | 
| 1596 |  |  |  |  |  |  | } | 
| 1597 |  |  |  |  |  |  |  | 
| 1598 |  |  |  |  |  |  | # foo Bar:: is always a method call | 
| 1599 | 36 | 50 | 33 |  |  | 129 | if(    $next->[O_TYPE] == T_ID | 
| 1600 |  |  |  |  |  |  | && $st->get_package( $next->[O_VALUE] ) ) { | 
| 1601 | 0 |  |  |  |  | 0 | return _parse_maybe_indirect_method_call( $self, $op, $next ); | 
| 1602 |  |  |  |  |  |  | } | 
| 1603 |  |  |  |  |  |  |  | 
| 1604 | 36 |  |  |  |  | 107 | my $symbol = Language::P::ParseTree::Symbol->new | 
| 1605 |  |  |  |  |  |  | ( { name  => _qualify( $self, $op->[O_VALUE], $op->[O_ID_TYPE] ), | 
| 1606 |  |  |  |  |  |  | sigil => VALUE_SUB, | 
| 1607 |  |  |  |  |  |  | } ); | 
| 1608 | 36 |  |  |  |  | 341 | $call = Language::P::ParseTree::FunctionCall->new | 
| 1609 |  |  |  |  |  |  | ( { function  => $symbol, | 
| 1610 |  |  |  |  |  |  | arguments => undef, | 
| 1611 |  |  |  |  |  |  | } ); | 
| 1612 | 36 |  |  |  |  | 143 | $proto = $call->parsing_prototype; | 
| 1613 |  |  |  |  |  |  | } | 
| 1614 |  |  |  |  |  |  |  | 
| 1615 | 183 | 100 |  |  |  | 934 | if( $next->[O_TYPE] == T_OPPAR ) { | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
| 1616 | 47 |  |  |  |  | 165 | _lex_token( $self, T_OPPAR ); | 
| 1617 | 47 |  |  |  |  | 190 | ( $args, $fh ) = _parse_arglist( $self, PREC_LOWEST, 0, $proto->[2] ); | 
| 1618 | 47 |  |  |  |  | 115 | _lex_token( $self, T_CLPAR ); | 
| 1619 |  |  |  |  |  |  | } elsif( $proto->[1] == 1 ) { | 
| 1620 | 5 |  |  |  |  | 37 | ( $args, undef ) = _parse_arglist( $self, PREC_NAMED_UNOP, 1, $proto->[2] ); | 
| 1621 |  |  |  |  |  |  | } elsif( $proto->[1] != 0 ) { | 
| 1622 | 125 | 50 |  |  |  | 291 | Carp::confess( "Undeclared identifier '" . $op->[O_VALUE] . "'" ) | 
| 1623 |  |  |  |  |  |  | unless $declared; | 
| 1624 | 125 |  |  |  |  | 759 | ( $args, $fh ) = _parse_arglist( $self, PREC_COMMA, 0, $proto->[2] ); | 
| 1625 |  |  |  |  |  |  | } | 
| 1626 |  |  |  |  |  |  |  | 
| 1627 |  |  |  |  |  |  | # FIXME avoid reconstructing the call? | 
| 1628 | 183 | 100 |  |  |  | 700 | if( $proto->[2] & (PROTO_INDIROBJ|PROTO_FILEHANDLE) ) { | 
|  |  | 100 |  |  |  |  |  | 
| 1629 | 112 |  |  |  |  | 1264 | $call = Language::P::ParseTree::BuiltinIndirect->new | 
| 1630 |  |  |  |  |  |  | ( { function  => $KEYWORD_TO_OP{$op->[O_ID_TYPE]}, | 
| 1631 |  |  |  |  |  |  | arguments => $args, | 
| 1632 |  |  |  |  |  |  | indirect  => $fh, | 
| 1633 |  |  |  |  |  |  | } ); | 
| 1634 |  |  |  |  |  |  | } elsif( $args ) { | 
| 1635 |  |  |  |  |  |  | # FIXME encapsulation | 
| 1636 | 38 |  |  |  |  | 84 | $call->{arguments} = $args; | 
| 1637 | 38 |  |  |  |  | 212 | $_->set_parent( $call ) foreach @$args; | 
| 1638 |  |  |  |  |  |  | } | 
| 1639 |  |  |  |  |  |  |  | 
| 1640 | 183 |  |  |  |  | 643 | _apply_prototype( $self, $call ); | 
| 1641 |  |  |  |  |  |  |  | 
| 1642 | 183 |  |  |  |  | 1302 | return $call; | 
| 1643 |  |  |  |  |  |  | } | 
| 1644 |  |  |  |  |  |  |  | 
| 1645 |  |  |  |  |  |  | sub _apply_prototype { | 
| 1646 | 183 |  |  | 183 |  | 336 | my( $self, $call ) = @_; | 
| 1647 | 183 |  |  |  |  | 649 | my $proto = $call->parsing_prototype; | 
| 1648 | 183 |  | 100 |  |  | 1125 | my $args = $call->arguments || []; | 
| 1649 |  |  |  |  |  |  |  | 
| 1650 | 183 | 50 |  |  |  | 1286 | if( @$args < $proto->[0] ) { | 
| 1651 | 0 |  |  |  |  | 0 | die "Too few arguments for call"; | 
| 1652 |  |  |  |  |  |  | } | 
| 1653 | 183 | 50 | 66 |  |  | 1614 | if( $proto->[1] != -1 && @$args > $proto->[1] ) { | 
| 1654 | 0 |  |  |  |  | 0 | die "Too many arguments for call"; | 
| 1655 |  |  |  |  |  |  | } | 
| 1656 |  |  |  |  |  |  |  | 
| 1657 | 183 |  |  |  |  | 577 | foreach my $i ( 3 .. $#$proto ) { | 
| 1658 | 177 | 100 |  |  |  | 556 | last if $i - 3 > $#$args; | 
| 1659 | 150 |  |  |  |  | 280 | my $proto_char = $proto->[$i]; | 
| 1660 | 150 |  |  |  |  | 246 | my $term = $args->[$i - 3]; | 
| 1661 |  |  |  |  |  |  |  | 
| 1662 |  |  |  |  |  |  | # defined/exists &foo | 
| 1663 | 150 | 100 |  |  |  | 374 | if( $proto_char & PROTO_AMPER ) { | 
| 1664 | 11 | 50 | 33 |  |  | 113 | if(    $term->isa( 'Language::P::ParseTree::SpecialFunctionCall' ) | 
| 1665 |  |  |  |  |  |  | && $term->flags & FLAG_IMPLICITARGUMENTS ) { | 
| 1666 | 0 |  |  |  |  | 0 | $args->[$i - 3] = $term->function; | 
| 1667 |  |  |  |  |  |  | } | 
| 1668 |  |  |  |  |  |  | } | 
| 1669 | 150 | 50 | 66 |  |  | 769 | if( $proto_char & PROTO_MAKE_GLOB && $term->is_bareword ) { | 
| 1670 | 0 |  |  |  |  | 0 | $args->[$i - 3] = Language::P::ParseTree::Symbol->new | 
| 1671 |  |  |  |  |  |  | ( { name  => $term->value, | 
| 1672 |  |  |  |  |  |  | sigil => VALUE_GLOB, | 
| 1673 |  |  |  |  |  |  | } ); | 
| 1674 |  |  |  |  |  |  | } | 
| 1675 |  |  |  |  |  |  | } | 
| 1676 |  |  |  |  |  |  | } | 
| 1677 |  |  |  |  |  |  |  | 
| 1678 |  |  |  |  |  |  | sub _parse_arglist { | 
| 1679 | 188 |  |  | 188 |  | 383 | my( $self, $prec, $is_unary, $proto_char ) = @_; | 
| 1680 | 188 |  |  |  |  | 294 | my $indirect_term = $proto_char & (PROTO_INDIROBJ|PROTO_FILEHANDLE); | 
| 1681 | 188 | 100 |  |  |  | 566 | my $la = $self->lexer->peek( $indirect_term ? X_REF : X_TERM ); | 
| 1682 | 188 | 100 |  |  |  | 501 | my $term_prec = $prec > PREC_LISTEXPR ? PREC_LISTEXPR : $prec; | 
| 1683 |  |  |  |  |  |  |  | 
| 1684 | 188 |  |  |  |  | 266 | my $term; | 
| 1685 | 188 | 100 | 33 |  |  | 639 | if( $indirect_term ) { | 
|  |  | 50 |  |  |  |  |  | 
| 1686 | 112 | 50 | 66 |  |  | 929 | if( $la->[O_TYPE] == T_OPBRK ) { | 
|  |  | 50 | 66 |  |  |  |  | 
| 1687 | 0 |  |  |  |  | 0 | $term = _parse_indirobj( $self, 0 ); | 
| 1688 |  |  |  |  |  |  | } elsif(    $proto_char & PROTO_FILEHANDLE | 
| 1689 |  |  |  |  |  |  | && $la->[O_TYPE] == T_ID | 
| 1690 |  |  |  |  |  |  | && $la->[O_ID_TYPE] == T_ID ) { | 
| 1691 |  |  |  |  |  |  | # check if it is a declared id | 
| 1692 | 0 |  |  |  |  | 0 | my $declared = $self->runtime->symbol_table | 
| 1693 |  |  |  |  |  |  | ->get_symbol( _qualify( $self, $la->[O_VALUE], $la->[O_ID_TYPE] ), '&' ); | 
| 1694 |  |  |  |  |  |  | # look ahead one more token | 
| 1695 | 0 |  |  |  |  | 0 | _lex_token( $self ); | 
| 1696 | 0 |  |  |  |  | 0 | my $la2 = $self->lexer->peek( X_TERM ); | 
| 1697 |  |  |  |  |  |  |  | 
| 1698 |  |  |  |  |  |  | # approximate what would happen in Perl LALR parser | 
| 1699 | 0 |  |  |  |  | 0 | my $tt = $la2->[O_TYPE]; | 
| 1700 | 0 | 0 | 0 |  |  | 0 | if( $declared ) { | 
|  |  | 0 | 0 |  |  |  |  | 
|  |  | 0 | 0 |  |  |  |  | 
|  |  |  | 0 |  |  |  |  | 
|  |  |  | 0 |  |  |  |  | 
|  |  |  | 0 |  |  |  |  | 
| 1701 | 0 |  |  |  |  | 0 | $self->lexer->unlex( $la ); | 
| 1702 | 0 |  |  |  |  | 0 | $indirect_term = 0; | 
| 1703 |  |  |  |  |  |  | } elsif(    $prec_assoc_bin{$tt} | 
| 1704 |  |  |  |  |  |  | && !$prec_assoc_un{$tt} | 
| 1705 |  |  |  |  |  |  | && $tt != T_STAR | 
| 1706 |  |  |  |  |  |  | && $tt != T_PERCENT | 
| 1707 |  |  |  |  |  |  | && $tt != T_DOLLAR | 
| 1708 |  |  |  |  |  |  | && $tt != T_AMPERSAND | 
| 1709 |  |  |  |  |  |  | ) { | 
| 1710 | 0 |  |  |  |  | 0 | $self->lexer->unlex( $la ); | 
| 1711 | 0 |  |  |  |  | 0 | $indirect_term = 0; | 
| 1712 |  |  |  |  |  |  | } elsif( $tt == T_ID && is_id( $la2->[O_ID_TYPE] ) ) { | 
| 1713 | 0 |  |  |  |  | 0 | $self->lexer->unlex( $la ); | 
| 1714 | 0 |  |  |  |  | 0 | $indirect_term = 0; | 
| 1715 |  |  |  |  |  |  | } else { | 
| 1716 | 0 |  |  |  |  | 0 | $term = Language::P::ParseTree::Symbol->new | 
| 1717 |  |  |  |  |  |  | ( { name  => $la->[O_VALUE], | 
| 1718 |  |  |  |  |  |  | sigil => VALUE_GLOB, | 
| 1719 |  |  |  |  |  |  | } ); | 
| 1720 |  |  |  |  |  |  | } | 
| 1721 |  |  |  |  |  |  | } else { | 
| 1722 | 112 |  |  |  |  | 1761 | $term = _parse_term( $self, $term_prec ); | 
| 1723 |  |  |  |  |  |  |  | 
| 1724 | 112 | 50 | 66 |  |  | 820 | if( !$term ) { | 
|  |  | 100 |  |  |  |  |  | 
| 1725 | 0 |  |  |  |  | 0 | $indirect_term = 0; | 
| 1726 |  |  |  |  |  |  | } elsif(    !( $term->is_symbol && $term->sigil == VALUE_SCALAR ) | 
| 1727 |  |  |  |  |  |  | && !$term->isa( 'Language::P::ParseTree::Block' ) ) { | 
| 1728 | 109 |  |  |  |  | 196 | $indirect_term = 0; | 
| 1729 |  |  |  |  |  |  | } | 
| 1730 |  |  |  |  |  |  | } | 
| 1731 |  |  |  |  |  |  | } elsif(    $proto_char & (PROTO_BLOCK|PROTO_SUB) | 
| 1732 |  |  |  |  |  |  | && $la->[O_TYPE] == T_OPBRK ) { | 
| 1733 | 0 |  |  |  |  | 0 | _lex_token( $self ); | 
| 1734 | 0 |  |  |  |  | 0 | $term = _parse_block_rest( $self, BLOCK_OPEN_SCOPE ); | 
| 1735 |  |  |  |  |  |  | } | 
| 1736 |  |  |  |  |  |  |  | 
| 1737 | 188 |  | 100 |  |  | 652 | $term ||= _parse_term( $self, $term_prec ); | 
| 1738 |  |  |  |  |  |  |  | 
| 1739 | 188 | 100 |  |  |  | 1478 | return unless $term; | 
| 1740 | 158 | 100 |  |  |  | 1230 | return [ $term ] if $is_unary; | 
| 1741 |  |  |  |  |  |  |  | 
| 1742 | 155 | 100 |  |  |  | 472 | if( $indirect_term ) { | 
| 1743 | 3 |  |  |  |  | 12 | my $la = $self->lexer->peek( X_TERM ); | 
| 1744 |  |  |  |  |  |  |  | 
| 1745 | 3 | 50 |  |  |  | 10 | if( $la->[O_TYPE] != T_COMMA ) { | 
| 1746 | 3 |  |  |  |  | 16 | my $args = _parse_arglist( $self, $prec, 0, 0 ); | 
| 1747 |  |  |  |  |  |  |  | 
| 1748 | 3 | 50 | 33 |  |  | 16 | if( !$args && $term->is_symbol && $term->sigil == VALUE_SCALAR ) { | 
|  |  |  | 33 |  |  |  |  | 
| 1749 | 3 |  |  |  |  | 24 | return ( [ $term ] ); | 
| 1750 |  |  |  |  |  |  | } else { | 
| 1751 | 0 |  |  |  |  | 0 | return ( $args, $term ); | 
| 1752 |  |  |  |  |  |  | } | 
| 1753 |  |  |  |  |  |  | } | 
| 1754 |  |  |  |  |  |  | } | 
| 1755 |  |  |  |  |  |  |  | 
| 1756 | 152 |  |  |  |  | 384 | $term = _parse_term_n( $self, $prec, $term, 0 ); | 
| 1757 |  |  |  |  |  |  |  | 
| 1758 | 152 | 50 | 33 |  |  | 1981 | return $term && $term->isa( 'Language::P::ParseTree::List' ) ? | 
| 1759 |  |  |  |  |  |  | $term->expressions : [ $term ]; | 
| 1760 |  |  |  |  |  |  | } | 
| 1761 |  |  |  |  |  |  |  | 
| 1762 |  |  |  |  |  |  | 1; |