| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package Async::Template::Parser; | 
| 2 |  |  |  |  |  |  |  | 
| 3 |  |  |  |  |  |  | #! @file | 
| 4 |  |  |  |  |  |  | #! @author: Serguei Okladnikov | 
| 5 |  |  |  |  |  |  | #! @date 15.10.2012 | 
| 6 |  |  |  |  |  |  |  | 
| 7 | 4 |  |  | 4 |  | 25 | use strict; | 
|  | 4 |  |  |  |  | 10 |  | 
|  | 4 |  |  |  |  | 110 |  | 
| 8 | 4 |  |  | 4 |  | 20 | use warnings; | 
|  | 4 |  |  |  |  | 7 |  | 
|  | 4 |  |  |  |  | 100 |  | 
| 9 | 4 |  |  | 4 |  | 17 | use base 'Template::Parser'; | 
|  | 4 |  |  |  |  | 9 |  | 
|  | 4 |  |  |  |  | 2555 |  | 
| 10 |  |  |  |  |  |  |  | 
| 11 |  |  |  |  |  |  | # parser state constants | 
| 12 | 4 |  |  | 4 |  | 126056 | use constant CONTINUE => Template::Parser::CONTINUE; | 
|  | 4 |  |  |  |  | 11 |  | 
|  | 4 |  |  |  |  | 233 |  | 
| 13 | 4 |  |  | 4 |  | 25 | use constant ACCEPT   => Template::Parser::ACCEPT; | 
|  | 4 |  |  |  |  | 8 |  | 
|  | 4 |  |  |  |  | 182 |  | 
| 14 | 4 |  |  | 4 |  | 26 | use constant ERROR    => Template::Parser::ERROR; | 
|  | 4 |  |  |  |  | 12 |  | 
|  | 4 |  |  |  |  | 174 |  | 
| 15 | 4 |  |  | 4 |  | 23 | use constant ABORT    => Template::Parser::ABORT; | 
|  | 4 |  |  |  |  | 10 |  | 
|  | 4 |  |  |  |  | 3271 |  | 
| 16 |  |  |  |  |  |  |  | 
| 17 |  |  |  |  |  |  |  | 
| 18 |  |  |  |  |  |  | sub rollback_token { | 
| 19 | 47 |  |  | 47 | 0 | 187 | my $self = shift; | 
| 20 | 47 | 50 |  |  |  | 118 | die unless $self->{ _EVENT_LAST_TOKEN }; | 
| 21 | 47 |  |  |  |  | 59 | unshift @{ $self->{_EVENT_TOKENS} }, ';'; | 
|  | 47 |  |  |  |  | 106 |  | 
| 22 | 47 |  |  |  |  | 86 | unshift @{ $self->{_EVENT_TOKENS} }, ';'; | 
|  | 47 |  |  |  |  | 82 |  | 
| 23 | 47 |  |  |  |  | 64 | unshift @{ $self->{_EVENT_TOKENS} }, $self->{_EVENT_LAST_TOKEN}->[1]; | 
|  | 47 |  |  |  |  | 109 |  | 
| 24 | 47 |  |  |  |  | 59 | unshift @{ $self->{_EVENT_TOKENS} }, $self->{_EVENT_LAST_TOKEN}->[0]; | 
|  | 47 |  |  |  |  | 120 |  | 
| 25 |  |  |  |  |  |  | } | 
| 26 |  |  |  |  |  |  |  | 
| 27 |  |  |  |  |  |  |  | 
| 28 |  |  |  |  |  |  | sub location { | 
| 29 | 387 |  |  | 387 | 0 | 2122 | '' | 
| 30 |  |  |  |  |  |  | } | 
| 31 |  |  |  |  |  |  |  | 
| 32 |  |  |  |  |  |  |  | 
| 33 |  |  |  |  |  |  | #------------------------------------------------------------------------ | 
| 34 |  |  |  |  |  |  | # _parse(\@tokens, \@info) | 
| 35 |  |  |  |  |  |  | # | 
| 36 |  |  |  |  |  |  | # TODO: merge every Template Toolkit release with original source | 
| 37 |  |  |  |  |  |  | # ( see base class Template::Parser ) | 
| 38 |  |  |  |  |  |  | # | 
| 39 |  |  |  |  |  |  | # Parses the list of input tokens passed by reference and returns a | 
| 40 |  |  |  |  |  |  | # Template::Directive::Block object which contains the compiled | 
| 41 |  |  |  |  |  |  | # representation of the template. | 
| 42 |  |  |  |  |  |  | # | 
| 43 |  |  |  |  |  |  | # This is the main parser DFA loop.  See embedded comments for | 
| 44 |  |  |  |  |  |  | # further details. | 
| 45 |  |  |  |  |  |  | # | 
| 46 |  |  |  |  |  |  | # On error, undef is returned and the internal _ERROR field is set to | 
| 47 |  |  |  |  |  |  | # indicate the error.  This can be retrieved by calling the error() | 
| 48 |  |  |  |  |  |  | # method. | 
| 49 |  |  |  |  |  |  | #------------------------------------------------------------------------ | 
| 50 |  |  |  |  |  |  |  | 
| 51 |  |  |  |  |  |  | sub _parse { | 
| 52 | 37 |  |  | 37 |  | 86918 | my ($self, $tokens, $info) = @_; | 
| 53 | 37 |  |  |  |  | 192 | my ($token, $value, $text, $line, $inperl); | 
| 54 | 37 |  |  |  |  | 0 | my ($state, $stateno, $status, $action, $lookup, $coderet, @codevars); | 
| 55 | 37 |  |  |  |  | 0 | my ($lhs, $len, $code);         # rule contents | 
| 56 | 37 |  |  |  |  | 91 | my $stack = [ [ 0, undef ] ];   # DFA stack | 
| 57 |  |  |  |  |  |  |  | 
| 58 |  |  |  |  |  |  | # DEBUG | 
| 59 |  |  |  |  |  |  | #   local $" = ', '; | 
| 60 |  |  |  |  |  |  |  | 
| 61 |  |  |  |  |  |  | # retrieve internal rule and state tables | 
| 62 | 37 |  |  |  |  | 119 | my ($states, $rules) = @$self{ qw( STATES RULES ) }; | 
| 63 |  |  |  |  |  |  |  | 
| 64 |  |  |  |  |  |  | # If we're tracing variable usage then we need to give the factory a | 
| 65 |  |  |  |  |  |  | # reference to our $self->{ VARIABLES } for it to fill in.  This is a | 
| 66 |  |  |  |  |  |  | # bit of a hack to back-patch this functionality into TT2. | 
| 67 |  |  |  |  |  |  | $self->{ FACTORY }->trace_vars($self->{ VARIABLES }) | 
| 68 | 37 | 50 |  |  |  | 168 | if $self->{ TRACE_VARS }; | 
| 69 |  |  |  |  |  |  |  | 
| 70 |  |  |  |  |  |  | # call the grammar set_factory method to install emitter factory | 
| 71 | 37 |  |  |  |  | 193 | $self->{ GRAMMAR }->install_factory($self->{ FACTORY }); | 
| 72 |  |  |  |  |  |  |  | 
| 73 | 37 |  |  |  |  | 63 | $line = $inperl = 0; | 
| 74 | 37 |  |  |  |  | 87 | $self->{ LINE   } = \$line; | 
| 75 | 37 |  |  |  |  | 93 | $self->{ FILE   } = $info->{ name }; | 
| 76 | 37 |  |  |  |  | 66 | $self->{ INPERL } = \$inperl; | 
| 77 |  |  |  |  |  |  |  | 
| 78 | 37 |  |  |  |  | 78 | $status = CONTINUE; | 
| 79 | 37 |  |  |  |  | 55 | my $in_string = 0; | 
| 80 |  |  |  |  |  |  |  | 
| 81 | 37 |  |  |  |  | 56 | while(1) { | 
| 82 |  |  |  |  |  |  | # get state number and state | 
| 83 | 9626 |  |  |  |  | 11248 | $stateno =  $stack->[-1]->[0]; | 
| 84 | 9626 |  |  |  |  | 10723 | $state   = $states->[$stateno]; | 
| 85 |  |  |  |  |  |  |  | 
| 86 |  |  |  |  |  |  | # see if any lookaheads exist for the current state | 
| 87 | 9626 | 100 |  |  |  | 14199 | if (exists $state->{'ACTIONS'}) { | 
| 88 |  |  |  |  |  |  |  | 
| 89 |  |  |  |  |  |  | # get next token and expand any directives (i.e. token is an | 
| 90 |  |  |  |  |  |  | # array ref) onto the front of the token list | 
| 91 | 4668 |  |  |  |  | 5241 | $self->{ _EVENT_TOKENS } = $tokens; | 
| 92 | 4668 |  | 100 |  |  | 11380 | while (! defined $token && @$tokens) { | 
| 93 | 2775 |  |  |  |  | 3864 | $token = shift(@$tokens); | 
| 94 | 2775 |  |  |  |  | 5901 | $self->{ _EVENT_LAST_TOKEN } = [$token]; | 
| 95 | 2775 | 100 |  |  |  | 3982 | if (ref $token) { | 
| 96 | 69 |  |  |  |  | 179 | ($text, $line, $token) = @$token; | 
| 97 | 69 | 50 |  |  |  | 133 | if (ref $token) { | 
|  |  | 0 |  |  |  |  |  | 
| 98 | 69 | 50 | 33 |  |  | 211 | if ($info->{ DEBUG } && ! $in_string) { | 
| 99 |  |  |  |  |  |  | # - - - - - - - - - - - - - - - - - - - - - - - - - | 
| 100 |  |  |  |  |  |  | # This is gnarly.  Look away now if you're easily | 
| 101 |  |  |  |  |  |  | # frightened.  We're pushing parse tokens onto the | 
| 102 |  |  |  |  |  |  | # pending list to simulate a DEBUG directive like so: | 
| 103 |  |  |  |  |  |  | # [% DEBUG msg line='20' text='INCLUDE foo' %] | 
| 104 |  |  |  |  |  |  | # - - - - - - - - - - - - - - - - - - - - - - - - - | 
| 105 | 0 |  |  |  |  | 0 | my $dtext = $text; | 
| 106 | 0 |  |  |  |  | 0 | $dtext =~ s[(['\\])][\\$1]g; | 
| 107 | 0 |  |  |  |  | 0 | unshift(@$tokens, | 
| 108 |  |  |  |  |  |  | DEBUG   => 'DEBUG', | 
| 109 |  |  |  |  |  |  | IDENT   => 'msg', | 
| 110 |  |  |  |  |  |  | IDENT   => 'line', | 
| 111 |  |  |  |  |  |  | ASSIGN  => '=', | 
| 112 |  |  |  |  |  |  | LITERAL => "'$line'", | 
| 113 |  |  |  |  |  |  | IDENT   => 'text', | 
| 114 |  |  |  |  |  |  | ASSIGN  => '=', | 
| 115 |  |  |  |  |  |  | LITERAL => "'$dtext'", | 
| 116 |  |  |  |  |  |  | IDENT   => 'file', | 
| 117 |  |  |  |  |  |  | ASSIGN  => '=', | 
| 118 |  |  |  |  |  |  | LITERAL => "'$info->{ name }'", | 
| 119 |  |  |  |  |  |  | (';') x 2, | 
| 120 |  |  |  |  |  |  | @$token, | 
| 121 |  |  |  |  |  |  | (';') x 2); | 
| 122 |  |  |  |  |  |  | } | 
| 123 |  |  |  |  |  |  | else { | 
| 124 | 69 |  |  |  |  | 1642 | unshift(@$tokens, @$token, (';') x 2); | 
| 125 |  |  |  |  |  |  | } | 
| 126 | 69 |  |  |  |  | 253 | $token = undef;  # force redo | 
| 127 |  |  |  |  |  |  | } | 
| 128 |  |  |  |  |  |  | elsif ($token eq 'ITEXT') { | 
| 129 | 0 | 0 |  |  |  | 0 | if ($inperl) { | 
| 130 |  |  |  |  |  |  | # don't perform interpolation in PERL blocks | 
| 131 | 0 |  |  |  |  | 0 | $token = 'TEXT'; | 
| 132 | 0 |  |  |  |  | 0 | $value = $text; | 
| 133 |  |  |  |  |  |  | } | 
| 134 |  |  |  |  |  |  | else { | 
| 135 |  |  |  |  |  |  | unshift(@$tokens, | 
| 136 | 0 |  |  |  |  | 0 | @{ $self->interpolate_text($text, $line) }); | 
|  | 0 |  |  |  |  | 0 |  | 
| 137 | 0 |  |  |  |  | 0 | $token = undef; # force redo | 
| 138 |  |  |  |  |  |  | } | 
| 139 |  |  |  |  |  |  | } | 
| 140 |  |  |  |  |  |  | } | 
| 141 |  |  |  |  |  |  | else { | 
| 142 |  |  |  |  |  |  | # toggle string flag to indicate if we're crossing | 
| 143 |  |  |  |  |  |  | # a string boundary | 
| 144 | 2706 | 100 |  |  |  | 4873 | $in_string = ! $in_string if $token eq '"'; | 
| 145 | 2706 |  |  |  |  | 3235 | $value = shift(@$tokens); | 
| 146 | 2706 |  |  |  |  | 3001 | push @{ $self->{ _EVENT_LAST_TOKEN } }, $value; | 
|  | 2706 |  |  |  |  | 7240 |  | 
| 147 |  |  |  |  |  |  | } | 
| 148 |  |  |  |  |  |  | }; | 
| 149 |  |  |  |  |  |  | # clear undefined token to avoid 'undefined variable blah blah' | 
| 150 |  |  |  |  |  |  | # warnings and let the parser logic pick it up in a minute | 
| 151 | 4668 | 100 |  |  |  | 7096 | $token = '' unless defined $token; | 
| 152 |  |  |  |  |  |  |  | 
| 153 |  |  |  |  |  |  | # get the next state for the current lookahead token | 
| 154 |  |  |  |  |  |  | $action = defined ($lookup = $state->{'ACTIONS'}->{ $token }) | 
| 155 |  |  |  |  |  |  | ? $lookup | 
| 156 | 4668 | 50 |  |  |  | 9167 | : defined ($lookup = $state->{'DEFAULT'}) | 
|  |  | 100 |  |  |  |  |  | 
| 157 |  |  |  |  |  |  | ? $lookup | 
| 158 |  |  |  |  |  |  | : undef; | 
| 159 |  |  |  |  |  |  | } | 
| 160 |  |  |  |  |  |  | else { | 
| 161 |  |  |  |  |  |  | # no lookahead actions | 
| 162 | 4958 |  |  |  |  | 5518 | $action = $state->{'DEFAULT'}; | 
| 163 |  |  |  |  |  |  | } | 
| 164 |  |  |  |  |  |  |  | 
| 165 |  |  |  |  |  |  | #warn "$stateno ".($token||'').' '.($value||'').' '.($action||'')."\n"; | 
| 166 |  |  |  |  |  |  | # ERROR: no ACTION | 
| 167 | 9626 | 50 |  |  |  | 13187 | last unless defined $action; | 
| 168 |  |  |  |  |  |  |  | 
| 169 |  |  |  |  |  |  | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - | 
| 170 |  |  |  |  |  |  | # shift (+ive ACTION) | 
| 171 |  |  |  |  |  |  | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - | 
| 172 | 9626 | 100 |  |  |  | 13028 | if ($action > 0) { | 
| 173 | 2743 |  |  |  |  | 4673 | push(@$stack, [ $action, $value ]); | 
| 174 | 2743 |  |  |  |  | 3639 | $token = $value = undef; | 
| 175 | 2743 |  |  |  |  | 3059 | redo; | 
| 176 |  |  |  |  |  |  | }; | 
| 177 |  |  |  |  |  |  |  | 
| 178 |  |  |  |  |  |  | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - | 
| 179 |  |  |  |  |  |  | # reduce (-ive ACTION) | 
| 180 |  |  |  |  |  |  | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - | 
| 181 | 6883 |  |  |  |  | 6999 | ($lhs, $len, $code) = @{ $rules->[ -$action ] }; | 
|  | 6883 |  |  |  |  | 14909 |  | 
| 182 |  |  |  |  |  |  |  | 
| 183 |  |  |  |  |  |  | # no action imples ACCEPTance | 
| 184 | 6883 | 100 |  |  |  | 10138 | $action | 
| 185 |  |  |  |  |  |  | or $status = ACCEPT; | 
| 186 |  |  |  |  |  |  |  | 
| 187 |  |  |  |  |  |  | # use dummy sub if code ref doesn't exist | 
| 188 | 2595 |  |  | 2595 |  | 4047 | $code = sub { $_[1] } | 
| 189 | 6883 | 100 |  |  |  | 13424 | unless $code; | 
| 190 |  |  |  |  |  |  |  | 
| 191 |  |  |  |  |  |  | @codevars = $len | 
| 192 | 6883 | 100 |  |  |  | 12838 | ?   map { $_->[1] } @$stack[ -$len .. -1 ] | 
|  | 9589 |  |  |  |  | 18725 |  | 
| 193 |  |  |  |  |  |  | :   (); | 
| 194 |  |  |  |  |  |  |  | 
| 195 | 6883 |  |  |  |  | 8432 | eval { | 
| 196 | 6883 |  |  |  |  | 11776 | $coderet = &$code( $self, @codevars ); | 
| 197 |  |  |  |  |  |  | }; | 
| 198 | 6883 | 50 |  |  |  | 34057 | if ($@) { | 
| 199 | 0 |  |  |  |  | 0 | my $err = $@; | 
| 200 | 0 |  |  |  |  | 0 | chomp $err; | 
| 201 | 0 |  |  |  |  | 0 | return $self->_parse_error($err); | 
| 202 |  |  |  |  |  |  | } | 
| 203 |  |  |  |  |  |  |  | 
| 204 |  |  |  |  |  |  | # reduce stack by $len | 
| 205 | 6883 |  |  |  |  | 9439 | splice(@$stack, -$len, $len); | 
| 206 |  |  |  |  |  |  |  | 
| 207 |  |  |  |  |  |  | # ACCEPT | 
| 208 | 6883 | 100 |  |  |  | 11179 | return $coderet                                     ## RETURN ## | 
| 209 |  |  |  |  |  |  | if $status == ACCEPT; | 
| 210 |  |  |  |  |  |  |  | 
| 211 |  |  |  |  |  |  | # ABORT | 
| 212 |  |  |  |  |  |  | return undef                                        ## RETURN ## | 
| 213 | 6846 | 50 |  |  |  | 9063 | if $status == ABORT; | 
| 214 |  |  |  |  |  |  |  | 
| 215 |  |  |  |  |  |  | # ERROR | 
| 216 |  |  |  |  |  |  | last | 
| 217 | 6846 | 50 |  |  |  | 9296 | if $status == ERROR; | 
| 218 |  |  |  |  |  |  | } | 
| 219 |  |  |  |  |  |  | continue { | 
| 220 | 6846 |  |  |  |  | 16703 | push(@$stack, [ $states->[ $stack->[-1][0] ]->{'GOTOS'}->{ $lhs }, | 
| 221 |  |  |  |  |  |  | $coderet ]), | 
| 222 |  |  |  |  |  |  | } | 
| 223 |  |  |  |  |  |  |  | 
| 224 |  |  |  |  |  |  | # ERROR                                                 ## RETURN ## | 
| 225 | 0 | 0 |  |  |  |  | return $self->_parse_error('unexpected end of input') | 
| 226 |  |  |  |  |  |  | unless defined $value; | 
| 227 |  |  |  |  |  |  |  | 
| 228 |  |  |  |  |  |  | # munge text of last directive to make it readable | 
| 229 |  |  |  |  |  |  | #    $text =~ s/\n/\\n/g; | 
| 230 |  |  |  |  |  |  |  | 
| 231 | 0 | 0 |  |  |  |  | return $self->_parse_error("unexpected end of directive", $text) | 
| 232 |  |  |  |  |  |  | if $value eq ';';   # end of directive SEPARATOR | 
| 233 |  |  |  |  |  |  |  | 
| 234 | 0 |  |  |  |  |  | return $self->_parse_error("unexpected token ($value)", $text); | 
| 235 |  |  |  |  |  |  | } | 
| 236 |  |  |  |  |  |  |  | 
| 237 |  |  |  |  |  |  |  | 
| 238 |  |  |  |  |  |  | 1; |