File Coverage

lib/Async/Template/Parser.pm
Criterion Covered Total %
statement 87 102 85.2
branch 29 46 63.0
condition 4 6 66.6
subroutine 11 11 100.0
pod 0 2 0.0
total 131 167 78.4


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;