Filename | /usr/lib/perl5/Template/Parser.pm |
Statements | Executed 328231 statements in 582ms |
Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
---|---|---|---|---|---|
8 | 1 | 1 | 410ms | 549ms | _parse | Template::Parser::
8 | 1 | 1 | 58.1ms | 151ms | split_text | Template::Parser::
1035 | 1 | 1 | 52.9ms | 68.7ms | tokenise_directive | Template::Parser::
1 | 1 | 1 | 21.9ms | 22.0ms | BEGIN@41 | Template::Parser::
3759 | 9 | 1 | 19.7ms | 19.7ms | CORE:subst (opcode) | Template::Parser::
5916 | 7 | 1 | 17.4ms | 17.4ms | CORE:match (opcode) | Template::Parser::
626 | 1 | 1 | 10.3ms | 11.8ms | location | Template::Parser::
3116 | 1 | 1 | 9.01ms | 9.01ms | __ANON__[:864] | Template::Parser::
4148 | 5 | 1 | 4.73ms | 4.73ms | CORE:regcomp (opcode) | Template::Parser::
1 | 1 | 1 | 4.71ms | 5.00ms | BEGIN@40 | Template::Parser::
27 | 1 | 1 | 547µs | 923µs | leave_block | Template::Parser::
8 | 1 | 1 | 537µs | 701ms | parse | Template::Parser::
27 | 1 | 1 | 376µs | 376µs | block_label | Template::Parser::
27 | 1 | 1 | 335µs | 335µs | enter_block | Template::Parser::
1 | 1 | 1 | 173µs | 254µs | new | Template::Parser::
9 | 2 | 1 | 65µs | 65µs | CORE:qr (opcode) | Template::Parser::
1 | 1 | 1 | 42µs | 47µs | interpolate_text | Template::Parser::
1 | 1 | 1 | 36µs | 36µs | new_style | Template::Parser::
1 | 1 | 1 | 27µs | 34µs | BEGIN@35 | Template::Parser::
1 | 1 | 1 | 21µs | 104µs | BEGIN@44 | Template::Parser::
1 | 1 | 1 | 18µs | 181µs | BEGIN@37 | Template::Parser::
1 | 1 | 1 | 18µs | 48µs | BEGIN@36 | Template::Parser::
1 | 1 | 1 | 14µs | 440µs | BEGIN@39 | Template::Parser::
1 | 1 | 1 | 14µs | 55µs | BEGIN@46 | Template::Parser::
1 | 1 | 1 | 13µs | 58µs | BEGIN@45 | Template::Parser::
1 | 1 | 1 | 11µs | 48µs | BEGIN@47 | Template::Parser::
2 | 1 | 1 | 4µs | 4µs | CORE:substcont (opcode) | Template::Parser::
0 | 0 | 0 | 0s | 0s | _dump | Template::Parser::
0 | 0 | 0 | 0s | 0s | _parse_error | Template::Parser::
0 | 0 | 0 | 0s | 0s | add_metadata | Template::Parser::
0 | 0 | 0 | 0s | 0s | define_block | Template::Parser::
0 | 0 | 0 | 0s | 0s | in_block | Template::Parser::
0 | 0 | 0 | 0s | 0s | old_style | Template::Parser::
0 | 0 | 0 | 0s | 0s | pop_defblock | Template::Parser::
0 | 0 | 0 | 0s | 0s | push_defblock | Template::Parser::
Line | State ments |
Time on line |
Calls | Time in subs |
Code |
---|---|---|---|---|---|
1 | #============================================================= -*-Perl-*- | ||||
2 | # | ||||
3 | # Template::Parser | ||||
4 | # | ||||
5 | # DESCRIPTION | ||||
6 | # This module implements a LALR(1) parser and assocated support | ||||
7 | # methods to parse template documents into the appropriate "compiled" | ||||
8 | # format. Much of the parser DFA code (see _parse() method) is based | ||||
9 | # on Francois Desarmenien's Parse::Yapp module. Kudos to him. | ||||
10 | # | ||||
11 | # AUTHOR | ||||
12 | # Andy Wardley <abw@wardley.org> | ||||
13 | # | ||||
14 | # COPYRIGHT | ||||
15 | # Copyright (C) 1996-2007 Andy Wardley. All Rights Reserved. | ||||
16 | # | ||||
17 | # This module is free software; you can redistribute it and/or | ||||
18 | # modify it under the same terms as Perl itself. | ||||
19 | # | ||||
20 | # The following copyright notice appears in the Parse::Yapp | ||||
21 | # documentation. | ||||
22 | # | ||||
23 | # The Parse::Yapp module and its related modules and shell | ||||
24 | # scripts are copyright (c) 1998 Francois Desarmenien, | ||||
25 | # France. All rights reserved. | ||||
26 | # | ||||
27 | # You may use and distribute them under the terms of either | ||||
28 | # the GNU General Public License or the Artistic License, as | ||||
29 | # specified in the Perl README file. | ||||
30 | # | ||||
31 | #============================================================================ | ||||
32 | |||||
33 | package Template::Parser; | ||||
34 | |||||
35 | 3 | 38µs | 2 | 40µs | # spent 34µs (27+7) within Template::Parser::BEGIN@35 which was called:
# once (27µs+7µs) by Template::Config::load at line 35 # spent 34µs making 1 call to Template::Parser::BEGIN@35
# spent 7µs making 1 call to strict::import |
36 | 3 | 39µs | 2 | 78µs | # spent 48µs (18+30) within Template::Parser::BEGIN@36 which was called:
# once (18µs+30µs) by Template::Config::load at line 36 # spent 48µs making 1 call to Template::Parser::BEGIN@36
# spent 30µs making 1 call to warnings::import |
37 | 3 | 47µs | 2 | 344µs | # spent 181µs (18+163) within Template::Parser::BEGIN@37 which was called:
# once (18µs+163µs) by Template::Config::load at line 37 # spent 181µs making 1 call to Template::Parser::BEGIN@37
# spent 163µs making 1 call to base::import |
38 | |||||
39 | 3 | 48µs | 2 | 865µs | # spent 440µs (14+425) within Template::Parser::BEGIN@39 which was called:
# once (14µs+425µs) by Template::Config::load at line 39 # spent 440µs making 1 call to Template::Parser::BEGIN@39
# spent 425µs making 1 call to Exporter::import |
40 | 3 | 310µs | 2 | 5.01ms | # spent 5.00ms (4.71+297µs) within Template::Parser::BEGIN@40 which was called:
# once (4.71ms+297µs) by Template::Config::load at line 40 # spent 5.00ms making 1 call to Template::Parser::BEGIN@40
# spent 3µs making 1 call to UNIVERSAL::import |
41 | 3 | 305µs | 2 | 22.0ms | # spent 22.0ms (21.9+64µs) within Template::Parser::BEGIN@41 which was called:
# once (21.9ms+64µs) by Template::Config::load at line 41 # spent 22.0ms making 1 call to Template::Parser::BEGIN@41
# spent 7µs making 1 call to UNIVERSAL::import |
42 | |||||
43 | # parser state constants | ||||
44 | 3 | 86µs | 2 | 187µs | # spent 104µs (21+83) within Template::Parser::BEGIN@44 which was called:
# once (21µs+83µs) by Template::Config::load at line 44 # spent 104µs making 1 call to Template::Parser::BEGIN@44
# spent 83µs making 1 call to constant::import |
45 | 3 | 419µs | 2 | 104µs | # spent 58µs (13+45) within Template::Parser::BEGIN@45 which was called:
# once (13µs+45µs) by Template::Config::load at line 45 # spent 58µs making 1 call to Template::Parser::BEGIN@45
# spent 45µs making 1 call to constant::import |
46 | 3 | 45µs | 2 | 96µs | # spent 55µs (14+41) within Template::Parser::BEGIN@46 which was called:
# once (14µs+41µs) by Template::Config::load at line 46 # spent 55µs making 1 call to Template::Parser::BEGIN@46
# spent 41µs making 1 call to constant::import |
47 | 3 | 4.39ms | 2 | 85µs | # spent 48µs (11+37) within Template::Parser::BEGIN@47 which was called:
# once (11µs+37µs) by Template::Config::load at line 47 # spent 48µs making 1 call to Template::Parser::BEGIN@47
# spent 37µs making 1 call to constant::import |
48 | |||||
49 | 1 | 700ns | our $VERSION = 2.89; | ||
50 | 1 | 700ns | our $DEBUG = 0 unless defined $DEBUG; | ||
51 | 1 | 800ns | our $ERROR = ''; | ||
52 | |||||
53 | |||||
54 | #======================================================================== | ||||
55 | # -- COMMON TAG STYLES -- | ||||
56 | #======================================================================== | ||||
57 | |||||
58 | 1 | 18µs | our $TAG_STYLE = { | ||
59 | 'default' => [ '\[%', '%\]' ], | ||||
60 | 'template1' => [ '[\[%]%', '%[\]%]' ], | ||||
61 | 'metatext' => [ '%%', '%%' ], | ||||
62 | 'html' => [ '<!--', '-->' ], | ||||
63 | 'mason' => [ '<%', '>' ], | ||||
64 | 'asp' => [ '<%', '%>' ], | ||||
65 | 'php' => [ '<\?', '\?>' ], | ||||
66 | 'star' => [ '\[\*', '\*\]' ], | ||||
67 | }; | ||||
68 | 1 | 4µs | $TAG_STYLE->{ template } = $TAG_STYLE->{ tt2 } = $TAG_STYLE->{ default }; | ||
69 | |||||
70 | |||||
71 | our $DEFAULT_STYLE = { | ||||
72 | START_TAG => $TAG_STYLE->{ default }->[0], | ||||
73 | 1 | 8µs | END_TAG => $TAG_STYLE->{ default }->[1], | ||
74 | # TAG_STYLE => 'default', | ||||
75 | ANYCASE => 0, | ||||
76 | INTERPOLATE => 0, | ||||
77 | PRE_CHOMP => 0, | ||||
78 | POST_CHOMP => 0, | ||||
79 | V1DOLLAR => 0, | ||||
80 | EVAL_PERL => 0, | ||||
81 | }; | ||||
82 | |||||
83 | 1 | 4µs | our $QUOTED_ESCAPES = { | ||
84 | n => "\n", | ||||
85 | r => "\r", | ||||
86 | t => "\t", | ||||
87 | }; | ||||
88 | |||||
89 | # note that '-' must come first so Perl doesn't think it denotes a range | ||||
90 | 1 | 23µs | 1 | 6µs | our $CHOMP_FLAGS = qr/[-=~+]/; # spent 6µs making 1 call to Template::Parser::CORE:qr |
91 | |||||
- - | |||||
94 | #======================================================================== | ||||
95 | # ----- PUBLIC METHODS ----- | ||||
96 | #======================================================================== | ||||
97 | |||||
98 | #------------------------------------------------------------------------ | ||||
99 | # new(\%config) | ||||
100 | # | ||||
101 | # Constructor method. | ||||
102 | #------------------------------------------------------------------------ | ||||
103 | |||||
104 | # spent 254µs (173+81) within Template::Parser::new which was called:
# once (173µs+81µs) by Template::Config::parser at line 103 of Template/Config.pm | ||||
105 | 30 | 129µs | my $class = shift; | ||
106 | my $config = $_[0] && ref($_[0]) eq 'HASH' ? shift(@_) : { @_ }; | ||||
107 | my ($tagstyle, $debug, $start, $end, $defaults, $grammar, $hash, $key, $udef); | ||||
108 | |||||
109 | my $self = bless { | ||||
110 | START_TAG => undef, | ||||
111 | END_TAG => undef, | ||||
112 | TAG_STYLE => 'default', | ||||
113 | ANYCASE => 0, | ||||
114 | INTERPOLATE => 0, | ||||
115 | PRE_CHOMP => 0, | ||||
116 | POST_CHOMP => 0, | ||||
117 | V1DOLLAR => 0, | ||||
118 | EVAL_PERL => 0, | ||||
119 | FILE_INFO => 1, | ||||
120 | GRAMMAR => undef, | ||||
121 | _ERROR => '', | ||||
122 | IN_BLOCK => [ ], | ||||
123 | FACTORY => $config->{ FACTORY } || 'Template::Directive', | ||||
124 | }, $class; | ||||
125 | |||||
126 | # update self with any relevant keys in config | ||||
127 | foreach $key (keys %$self) { | ||||
128 | $self->{ $key } = $config->{ $key } if defined $config->{ $key }; | ||||
129 | } | ||||
130 | $self->{ FILEINFO } = [ ]; | ||||
131 | |||||
132 | # DEBUG config item can be a bitmask | ||||
133 | if (defined ($debug = $config->{ DEBUG })) { | ||||
134 | $self->{ DEBUG } = $debug & ( Template::Constants::DEBUG_PARSER | ||||
135 | | Template::Constants::DEBUG_FLAGS ); | ||||
136 | $self->{ DEBUG_DIRS } = $debug & Template::Constants::DEBUG_DIRS; | ||||
137 | } | ||||
138 | # package variable can be set to 1 to support previous behaviour | ||||
139 | elsif ($DEBUG == 1) { | ||||
140 | $self->{ DEBUG } = Template::Constants::DEBUG_PARSER; | ||||
141 | $self->{ DEBUG_DIRS } = 0; | ||||
142 | } | ||||
143 | # otherwise let $DEBUG be a bitmask | ||||
144 | else { | ||||
145 | $self->{ DEBUG } = $DEBUG & ( Template::Constants::DEBUG_PARSER | ||||
146 | | Template::Constants::DEBUG_FLAGS ); | ||||
147 | $self->{ DEBUG_DIRS } = $DEBUG & Template::Constants::DEBUG_DIRS; | ||||
148 | } | ||||
149 | |||||
150 | $grammar = $self->{ GRAMMAR } ||= do { | ||||
151 | require Template::Grammar; | ||||
152 | 1 | 45µs | Template::Grammar->new(); # spent 45µs making 1 call to Template::Grammar::new | ||
153 | }; | ||||
154 | |||||
155 | # build a FACTORY object to include any NAMESPACE definitions, | ||||
156 | # but only if FACTORY isn't already an object | ||||
157 | if ($config->{ NAMESPACE } && ! ref $self->{ FACTORY }) { | ||||
158 | my $fclass = $self->{ FACTORY }; | ||||
159 | $self->{ FACTORY } = $fclass->new( NAMESPACE => $config->{ NAMESPACE } ) | ||||
160 | || return $class->error($fclass->error()); | ||||
161 | } | ||||
162 | |||||
163 | # load grammar rules, states and lex table | ||||
164 | @$self{ qw( LEXTABLE STATES RULES ) } | ||||
165 | = @$grammar{ qw( LEXTABLE STATES RULES ) }; | ||||
166 | |||||
167 | 1 | 36µs | $self->new_style($config) # spent 36µs making 1 call to Template::Parser::new_style | ||
168 | || return $class->error($self->error()); | ||||
169 | |||||
170 | return $self; | ||||
171 | } | ||||
172 | |||||
173 | #----------------------------------------------------------------------- | ||||
174 | # These methods are used to track nested IF and WHILE blocks. Each | ||||
175 | # generated if/while block is given a label indicating the directive | ||||
176 | # type and nesting depth, e.g. FOR0, WHILE1, FOR2, WHILE3, etc. The | ||||
177 | # NEXT and LAST directives use the innermost label, e.g. last WHILE3; | ||||
178 | #----------------------------------------------------------------------- | ||||
179 | |||||
180 | # spent 335µs within Template::Parser::enter_block which was called 27 times, avg 12µs/call:
# 27 times (335µs+0s) by Template::Grammar::__ANON__[Parser.yp:167] at line 167 of Parser.yp, avg 12µs/call | ||||
181 | 81 | 413µs | my ($self, $name) = @_; | ||
182 | my $blocks = $self->{ IN_BLOCK }; | ||||
183 | push(@{ $self->{ IN_BLOCK } }, $name); | ||||
184 | } | ||||
185 | |||||
186 | # spent 923µs (547+376) within Template::Parser::leave_block which was called 27 times, avg 34µs/call:
# 27 times (547µs+376µs) by Template::Grammar::__ANON__[Parser.yp:168] at line 168 of Parser.yp, avg 34µs/call | ||||
187 | 108 | 382µs | my $self = shift; | ||
188 | 27 | 376µs | my $label = $self->block_label; # spent 376µs making 27 calls to Template::Parser::block_label, avg 14µs/call | ||
189 | pop(@{ $self->{ IN_BLOCK } }); | ||||
190 | return $label; | ||||
191 | } | ||||
192 | |||||
193 | sub in_block { | ||||
194 | my ($self, $name) = @_; | ||||
195 | my $blocks = $self->{ IN_BLOCK }; | ||||
196 | return @$blocks && $blocks->[-1] eq $name; | ||||
197 | } | ||||
198 | |||||
199 | # spent 376µs within Template::Parser::block_label which was called 27 times, avg 14µs/call:
# 27 times (376µs+0s) by Template::Parser::leave_block at line 188, avg 14µs/call | ||||
200 | 108 | 566µs | my ($self, $prefix, $suffix) = @_; | ||
201 | my $blocks = $self->{ IN_BLOCK }; | ||||
202 | my $name = @$blocks | ||||
203 | ? $blocks->[-1] . scalar @$blocks | ||||
204 | : undef; | ||||
205 | return join('', grep { defined $_ } $prefix, $name, $suffix); | ||||
206 | } | ||||
207 | |||||
- - | |||||
210 | #------------------------------------------------------------------------ | ||||
211 | # new_style(\%config) | ||||
212 | # | ||||
213 | # Install a new (stacked) parser style. This feature is currently | ||||
214 | # experimental but should mimic the previous behaviour with regard to | ||||
215 | # TAG_STYLE, START_TAG, END_TAG, etc. | ||||
216 | #------------------------------------------------------------------------ | ||||
217 | |||||
218 | # spent 36µs within Template::Parser::new_style which was called:
# once (36µs+0s) by Template::Parser::new at line 167 | ||||
219 | 16 | 74µs | my ($self, $config) = @_; | ||
220 | my $styles = $self->{ STYLE } ||= [ ]; | ||||
221 | my ($tagstyle, $tags, $start, $end, $key); | ||||
222 | |||||
223 | # clone new style from previous or default style | ||||
224 | my $style = { %{ $styles->[-1] || $DEFAULT_STYLE } }; | ||||
225 | |||||
226 | # expand START_TAG and END_TAG from specified TAG_STYLE | ||||
227 | if ($tagstyle = $config->{ TAG_STYLE }) { | ||||
228 | return $self->error("Invalid tag style: $tagstyle") | ||||
229 | unless defined ($tags = $TAG_STYLE->{ $tagstyle }); | ||||
230 | ($start, $end) = @$tags; | ||||
231 | $config->{ START_TAG } ||= $start; | ||||
232 | $config->{ END_TAG } ||= $end; | ||||
233 | } | ||||
234 | |||||
235 | foreach $key (keys %$DEFAULT_STYLE) { | ||||
236 | $style->{ $key } = $config->{ $key } if defined $config->{ $key }; | ||||
237 | } | ||||
238 | push(@$styles, $style); | ||||
239 | return $style; | ||||
240 | } | ||||
241 | |||||
242 | |||||
243 | #------------------------------------------------------------------------ | ||||
244 | # old_style() | ||||
245 | # | ||||
246 | # Pop the current parser style and revert to the previous one. See | ||||
247 | # new_style(). ** experimental ** | ||||
248 | #------------------------------------------------------------------------ | ||||
249 | |||||
250 | sub old_style { | ||||
251 | my $self = shift; | ||||
252 | my $styles = $self->{ STYLE }; | ||||
253 | return $self->error('only 1 parser style remaining') | ||||
254 | unless (@$styles > 1); | ||||
255 | pop @$styles; | ||||
256 | return $styles->[-1]; | ||||
257 | } | ||||
258 | |||||
259 | |||||
260 | #------------------------------------------------------------------------ | ||||
261 | # parse($text, $data) | ||||
262 | # | ||||
263 | # Parses the text string, $text and returns a hash array representing | ||||
264 | # the compiled template block(s) as Perl code, in the format expected | ||||
265 | # by Template::Document. | ||||
266 | #------------------------------------------------------------------------ | ||||
267 | |||||
268 | # spent 701ms (537µs+701) within Template::Parser::parse which was called 8 times, avg 87.7ms/call:
# 8 times (537µs+701ms) by Template::Provider::_compile at line 844 of Template/Provider.pm, avg 87.7ms/call | ||||
269 | 112 | 519µs | my ($self, $text, $info) = @_; | ||
270 | my ($tokens, $block); | ||||
271 | |||||
272 | $info->{ DEBUG } = $self->{ DEBUG_DIRS } | ||||
273 | unless defined $info->{ DEBUG }; | ||||
274 | |||||
275 | # print "info: { ", join(', ', map { "$_ => $info->{ $_ }" } keys %$info), " }\n"; | ||||
276 | |||||
277 | # store for blocks defined in the template (see define_block()) | ||||
278 | my $defblock = $self->{ DEFBLOCK } = { }; | ||||
279 | my $metadata = $self->{ METADATA } = [ ]; | ||||
280 | $self->{ DEFBLOCKS } = [ ]; | ||||
281 | |||||
282 | $self->{ _ERROR } = ''; | ||||
283 | |||||
284 | # split file into TEXT/DIRECTIVE chunks | ||||
285 | 16 | 151ms | $tokens = $self->split_text($text) # spent 151ms making 8 calls to Template::Parser::split_text, avg 18.9ms/call
# spent 18µs making 8 calls to Regexp::DESTROY, avg 2µs/call | ||
286 | || return undef; ## RETURN ## | ||||
287 | |||||
288 | push(@{ $self->{ FILEINFO } }, $info); | ||||
289 | |||||
290 | # parse chunks | ||||
291 | 8 | 549ms | $block = $self->_parse($tokens, $info); # spent 549ms making 8 calls to Template::Parser::_parse, avg 68.7ms/call | ||
292 | |||||
293 | pop(@{ $self->{ FILEINFO } }); | ||||
294 | |||||
295 | return undef unless $block; ## RETURN ## | ||||
296 | |||||
297 | $self->debug("compiled main template document block:\n$block") | ||||
298 | if $self->{ DEBUG } & Template::Constants::DEBUG_PARSER; | ||||
299 | |||||
300 | return { | ||||
301 | BLOCK => $block, | ||||
302 | DEFBLOCKS => $defblock, | ||||
303 | METADATA => { @$metadata }, | ||||
304 | }; | ||||
305 | } | ||||
306 | |||||
- - | |||||
309 | #------------------------------------------------------------------------ | ||||
310 | # split_text($text) | ||||
311 | # | ||||
312 | # Split input template text into directives and raw text chunks. | ||||
313 | #------------------------------------------------------------------------ | ||||
314 | |||||
315 | # spent 151ms (58.1+93.3) within Template::Parser::split_text which was called 8 times, avg 18.9ms/call:
# 8 times (58.1ms+93.3ms) by Template::Parser::parse at line 285, avg 18.9ms/call | ||||
316 | 22858 | 82.1ms | my ($self, $text) = @_; | ||
317 | my ($pre, $dir, $prelines, $dirlines, $postlines, $chomp, $tags, @tags); | ||||
318 | my $style = $self->{ STYLE }->[-1]; | ||||
319 | my ($start, $end, $prechomp, $postchomp, $interp ) = | ||||
320 | @$style{ qw( START_TAG END_TAG PRE_CHOMP POST_CHOMP INTERPOLATE ) }; | ||||
321 | 8 | 58µs | my $tags_dir = $self->{ANYCASE} ? qr<TAGS>i : qr<TAGS>; # spent 58µs making 8 calls to Template::Parser::CORE:qr, avg 7µs/call | ||
322 | |||||
323 | my @tokens = (); | ||||
324 | my $line = 1; | ||||
325 | |||||
326 | return \@tokens ## RETURN ## | ||||
327 | unless defined $text && length $text; | ||||
328 | |||||
329 | # extract all directives from the text | ||||
330 | 16 | 140µs | while ($text =~ s/ # spent 93µs making 8 calls to Template::Parser::CORE:subst, avg 12µs/call
# spent 47µs making 8 calls to Template::Parser::CORE:regcomp, avg 6µs/call | ||
331 | ^(.*?) # $1 - start of line up to directive | ||||
332 | (?: | ||||
333 | $start # start of tag | ||||
334 | (.*?) # $2 - tag contents | ||||
335 | $end # end of tag | ||||
336 | ) | ||||
337 | //sx) { | ||||
338 | |||||
339 | ($pre, $dir) = ($1, $2); | ||||
340 | $pre = '' unless defined $pre; | ||||
341 | $dir = '' unless defined $dir; | ||||
342 | |||||
343 | $prelines = ($pre =~ tr/\n//); # newlines in preceeding text | ||||
344 | $dirlines = ($dir =~ tr/\n//); # newlines in directive tag | ||||
345 | $postlines = 0; # newlines chomped after tag | ||||
346 | |||||
347 | for ($dir) { | ||||
348 | 1035 | 957µs | if (/^\#/) { # spent 957µs making 1035 calls to Template::Parser::CORE:match, avg 925ns/call | ||
349 | # comment out entire directive except for any end chomp flag | ||||
350 | $dir = ($dir =~ /($CHOMP_FLAGS)$/o) ? $1 : ''; | ||||
351 | } | ||||
352 | else { | ||||
353 | 2070 | 3.84ms | s/^($CHOMP_FLAGS)?\s*//so; # spent 3.30ms making 1035 calls to Template::Parser::CORE:subst, avg 3µs/call
# spent 538µs making 1035 calls to Template::Parser::CORE:regcomp, avg 520ns/call | ||
354 | # PRE_CHOMP: process whitespace before tag | ||||
355 | $chomp = $1 ? $1 : $prechomp; | ||||
356 | $chomp =~ tr/-=~+/1230/; | ||||
357 | if ($chomp && $pre) { | ||||
358 | # chomp off whitespace and newline preceding directive | ||||
359 | if ($chomp == CHOMP_ALL) { | ||||
360 | $pre =~ s{ (\r?\n|^) [^\S\n]* \z }{}mx; | ||||
361 | } | ||||
362 | elsif ($chomp == CHOMP_COLLAPSE) { | ||||
363 | $pre =~ s{ (\s+) \z }{ }x; | ||||
364 | } | ||||
365 | elsif ($chomp == CHOMP_GREEDY) { | ||||
366 | $pre =~ s{ (\s+) \z }{}x; | ||||
367 | } | ||||
368 | } | ||||
369 | } | ||||
370 | |||||
371 | # POST_CHOMP: process whitespace after tag | ||||
372 | 2070 | 9.42ms | s/\s*($CHOMP_FLAGS)?\s*$//so; # spent 9.02ms making 1035 calls to Template::Parser::CORE:subst, avg 9µs/call
# spent 405µs making 1035 calls to Template::Parser::CORE:regcomp, avg 391ns/call | ||
373 | $chomp = $1 ? $1 : $postchomp; | ||||
374 | $chomp =~ tr/-=~+/1230/; | ||||
375 | if ($chomp) { | ||||
376 | if ($chomp == CHOMP_ALL) { | ||||
377 | $text =~ s{ ^ ([^\S\n]* \n) }{}x | ||||
378 | && $postlines++; | ||||
379 | } | ||||
380 | elsif ($chomp == CHOMP_COLLAPSE) { | ||||
381 | $text =~ s{ ^ (\s+) }{ }x | ||||
382 | && ($postlines += $1=~y/\n//); | ||||
383 | } | ||||
384 | # any trailing whitespace | ||||
385 | elsif ($chomp == CHOMP_GREEDY) { | ||||
386 | $text =~ s{ ^ (\s+) }{}x | ||||
387 | && ($postlines += $1=~y/\n//); | ||||
388 | } | ||||
389 | } | ||||
390 | } | ||||
391 | |||||
392 | # any text preceding the directive can now be added | ||||
393 | if (length $pre) { | ||||
394 | push(@tokens, $interp | ||||
395 | ? [ $pre, $line, 'ITEXT' ] | ||||
396 | : ('TEXT', $pre) ); | ||||
397 | } | ||||
398 | $line += $prelines; | ||||
399 | |||||
400 | # and now the directive, along with line number information | ||||
401 | if (length $dir) { | ||||
402 | # the TAGS directive is a compile-time switch | ||||
403 | 2070 | 2.27ms | if ($dir =~ /^$tags_dir\s+(.*)/) { # spent 1.63ms making 1035 calls to Template::Parser::CORE:regcomp, avg 2µs/call
# spent 634µs making 1035 calls to Template::Parser::CORE:match, avg 613ns/call | ||
404 | my @tags = split(/\s+/, $1); | ||||
405 | if (scalar @tags > 1) { | ||||
406 | ($start, $end) = map { quotemeta($_) } @tags; | ||||
407 | } | ||||
408 | elsif ($tags = $TAG_STYLE->{ $tags[0] }) { | ||||
409 | ($start, $end) = @$tags; | ||||
410 | } | ||||
411 | else { | ||||
412 | warn "invalid TAGS style: $tags[0]\n"; | ||||
413 | } | ||||
414 | } | ||||
415 | else { | ||||
416 | # DIRECTIVE is pushed as: | ||||
417 | # [ $dirtext, $line_no(s), \@tokens ] | ||||
418 | 1035 | 68.7ms | push(@tokens, # spent 68.7ms making 1035 calls to Template::Parser::tokenise_directive, avg 66µs/call | ||
419 | [ $dir, | ||||
420 | ($dirlines | ||||
421 | ? sprintf("%d-%d", $line, $line + $dirlines) | ||||
422 | : $line), | ||||
423 | $self->tokenise_directive($dir) ]); | ||||
424 | } | ||||
425 | } | ||||
426 | |||||
427 | # update line counter to include directive lines and any extra | ||||
428 | # newline chomped off the start of the following text | ||||
429 | 2070 | 7.87ms | $line += $dirlines + $postlines; # spent 5.77ms making 1035 calls to Template::Parser::CORE:subst, avg 6µs/call
# spent 2.11ms making 1035 calls to Template::Parser::CORE:regcomp, avg 2µs/call | ||
430 | } | ||||
431 | |||||
432 | # anything remaining in the string is plain text | ||||
433 | push(@tokens, $interp | ||||
434 | ? [ $text, $line, 'ITEXT' ] | ||||
435 | : ( 'TEXT', $text) ) | ||||
436 | if length $text; | ||||
437 | |||||
438 | return \@tokens; ## RETURN ## | ||||
439 | } | ||||
440 | |||||
441 | |||||
442 | |||||
443 | #------------------------------------------------------------------------ | ||||
444 | # interpolate_text($text, $line) | ||||
445 | # | ||||
446 | # Examines $text looking for any variable references embedded like | ||||
447 | # $this or like ${ this }. | ||||
448 | #------------------------------------------------------------------------ | ||||
449 | |||||
450 | # spent 47µs (42+6) within Template::Parser::interpolate_text which was called:
# once (42µs+6µs) by Template::Parser::tokenise_directive at line 583 | ||||
451 | 11 | 51µs | my ($self, $text, $line) = @_; | ||
452 | my @tokens = (); | ||||
453 | my ($pre, $var, $dir); | ||||
454 | |||||
455 | |||||
456 | 1 | 4µs | while ($text =~ # spent 4µs making 1 call to Template::Parser::CORE:match | ||
457 | / | ||||
458 | ( (?: \\. | [^\$] ){1,3000} ) # escaped or non-'$' character [$1] | ||||
459 | | | ||||
460 | ( \$ (?: # embedded variable [$2] | ||||
461 | (?: \{ ([^\}]*) \} ) # ${ ... } [$3] | ||||
462 | | | ||||
463 | ([\w\.]+) # $word [$4] | ||||
464 | ) | ||||
465 | ) | ||||
466 | /gx) { | ||||
467 | |||||
468 | ($pre, $var, $dir) = ($1, $3 || $4, $2); | ||||
469 | |||||
470 | # preceding text | ||||
471 | if (defined($pre) && length($pre)) { | ||||
472 | $line += $pre =~ tr/\n//; | ||||
473 | 1 | 700ns | $pre =~ s/\\\$/\$/g; # spent 700ns making 1 call to Template::Parser::CORE:subst | ||
474 | push(@tokens, 'TEXT', $pre); | ||||
475 | } | ||||
476 | # $variable reference | ||||
477 | 1 | 700ns | if ($var) { # spent 700ns making 1 call to Template::Parser::CORE:match | ||
478 | $line += $dir =~ tr/\n/ /; | ||||
479 | push(@tokens, [ $dir, $line, $self->tokenise_directive($var) ]); | ||||
480 | } | ||||
481 | # other '$' reference - treated as text | ||||
482 | elsif ($dir) { | ||||
483 | $line += $dir =~ tr/\n//; | ||||
484 | push(@tokens, 'TEXT', $dir); | ||||
485 | } | ||||
486 | } | ||||
487 | |||||
488 | return \@tokens; | ||||
489 | } | ||||
490 | |||||
- - | |||||
493 | #------------------------------------------------------------------------ | ||||
494 | # tokenise_directive($text) | ||||
495 | # | ||||
496 | # Called by the private _parse() method when it encounters a DIRECTIVE | ||||
497 | # token in the list provided by the split_text() or interpolate_text() | ||||
498 | # methods. The directive text is passed by parameter. | ||||
499 | # | ||||
500 | # The method splits the directive into individual tokens as recognised | ||||
501 | # by the parser grammar (see Template::Grammar for details). It | ||||
502 | # constructs a list of tokens each represented by 2 elements, as per | ||||
503 | # split_text() et al. The first element contains the token type, the | ||||
504 | # second the token itself. | ||||
505 | # | ||||
506 | # The method tokenises the string using a complex (but fast) regex. | ||||
507 | # For a deeper understanding of the regex magic at work here, see | ||||
508 | # Jeffrey Friedl's excellent book "Mastering Regular Expressions", | ||||
509 | # from O'Reilly, ISBN 1-56592-257-3 | ||||
510 | # | ||||
511 | # Returns a reference to the list of chunks (each one being 2 elements) | ||||
512 | # identified in the directive text. On error, the internal _ERROR string | ||||
513 | # is set and undef is returned. | ||||
514 | #------------------------------------------------------------------------ | ||||
515 | |||||
516 | # spent 68.7ms (52.9+15.9) within Template::Parser::tokenise_directive which was called 1035 times, avg 66µs/call:
# 1035 times (52.9ms+15.9ms) by Template::Parser::split_text at line 418, avg 66µs/call | ||||
517 | 23314 | 69.3ms | my ($self, $text, $line) = @_; | ||
518 | my ($token, $uctoken, $type, $lookup); | ||||
519 | my $lextable = $self->{ LEXTABLE }; | ||||
520 | my $style = $self->{ STYLE }->[-1]; | ||||
521 | my ($anycase, $start, $end) = @$style{ qw( ANYCASE START_TAG END_TAG ) }; | ||||
522 | my @tokens = ( ); | ||||
523 | |||||
524 | 1036 | 5.94ms | while ($text =~ # spent 5.94ms making 1036 calls to Template::Parser::CORE:match, avg 6µs/call | ||
525 | / | ||||
526 | # strip out any comments | ||||
527 | (\#[^\n]*) | ||||
528 | | | ||||
529 | # a quoted phrase matches in $3 | ||||
530 | (["']) # $2 - opening quote, ' or " | ||||
531 | ( # $3 - quoted text buffer | ||||
532 | (?: # repeat group (no backreference) | ||||
533 | \\\\ # an escaped backslash \\ | ||||
534 | | # ...or... | ||||
535 | \\\2 # an escaped quote \" or \' (match $1) | ||||
536 | | # ...or... | ||||
537 | . # any other character | ||||
538 | | \n | ||||
539 | )*? # non-greedy repeat | ||||
540 | ) # end of $3 | ||||
541 | \2 # match opening quote | ||||
542 | | | ||||
543 | # an unquoted number matches in $4 | ||||
544 | (-?\d+(?:\.\d+)?) # numbers | ||||
545 | | | ||||
546 | # filename matches in $5 | ||||
547 | ( \/?\w+(?:(?:\/|::?)\w*)+ | \/\w+) | ||||
548 | | | ||||
549 | # an identifier matches in $6 | ||||
550 | (\w+) # variable identifier | ||||
551 | | | ||||
552 | # an unquoted word or symbol matches in $7 | ||||
553 | ( [(){}\[\]:;,\/\\] # misc parenthesis and symbols | ||||
554 | # | \-> # arrow operator (for future?) | ||||
555 | | [+\-*] # math operations | ||||
556 | | \$\{? # dollar with option left brace | ||||
557 | | => # like '=' | ||||
558 | | [=!<>]?= | [!<>] # eqality tests | ||||
559 | | &&? | \|\|? # boolean ops | ||||
560 | | \.\.? # n..n sequence | ||||
561 | | \S+ # something unquoted | ||||
562 | ) # end of $7 | ||||
563 | /gmxo) { | ||||
564 | |||||
565 | # ignore comments to EOL | ||||
566 | next if $1; | ||||
567 | |||||
568 | # quoted string | ||||
569 | if (defined ($token = $3)) { | ||||
570 | # double-quoted string may include $variable references | ||||
571 | if ($2 eq '"') { | ||||
572 | 18 | 17µs | if ($token =~ /[\$\\]/) { # spent 17µs making 18 calls to Template::Parser::CORE:match, avg 933ns/call | ||
573 | $type = 'QUOTED'; | ||||
574 | # unescape " and \ but leave \$ escaped so that | ||||
575 | # interpolate_text() doesn't incorrectly treat it | ||||
576 | # as a variable reference | ||||
577 | # $token =~ s/\\([\\"])/$1/g; | ||||
578 | for ($token) { | ||||
579 | 3 | 10µs | s/\\([^\$nrt])/$1/g; # spent 5µs making 1 call to Template::Parser::CORE:subst
# spent 4µs making 2 calls to Template::Parser::CORE:substcont, avg 2µs/call | ||
580 | 1 | 600ns | s/\\([nrt])/$QUOTED_ESCAPES->{ $1 }/ge; # spent 600ns making 1 call to Template::Parser::CORE:subst | ||
581 | } | ||||
582 | push(@tokens, ('"') x 2, | ||||
583 | 1 | 47µs | @{ $self->interpolate_text($token) }, # spent 47µs making 1 call to Template::Parser::interpolate_text | ||
584 | ('"') x 2); | ||||
585 | next; | ||||
586 | } | ||||
587 | else { | ||||
588 | $type = 'LITERAL'; | ||||
589 | 17 | 17µs | $token =~ s['][\\']g; # spent 17µs making 17 calls to Template::Parser::CORE:subst, avg 988ns/call | ||
590 | $token = "'$token'"; | ||||
591 | } | ||||
592 | } | ||||
593 | else { | ||||
594 | $type = 'LITERAL'; | ||||
595 | $token = "'$token'"; | ||||
596 | } | ||||
597 | } | ||||
598 | # number | ||||
599 | elsif (defined ($token = $4)) { | ||||
600 | $type = 'NUMBER'; | ||||
601 | } | ||||
602 | elsif (defined($token = $5)) { | ||||
603 | $type = 'FILENAME'; | ||||
604 | } | ||||
605 | elsif (defined($token = $6)) { | ||||
606 | # Fold potential keywords to UPPER CASE if the ANYCASE option is | ||||
607 | # set, unless (we've got some preceeding tokens and) the previous | ||||
608 | # token is a DOT op. This prevents the 'last' in 'data.last' | ||||
609 | # from being interpreted as the LAST keyword. | ||||
610 | $uctoken = | ||||
611 | ($anycase && (! @tokens || $tokens[-2] ne 'DOT')) | ||||
612 | ? uc $token | ||||
613 | : $token; | ||||
614 | if (defined ($type = $lextable->{ $uctoken })) { | ||||
615 | $token = $uctoken; | ||||
616 | } | ||||
617 | else { | ||||
618 | $type = 'IDENT'; | ||||
619 | } | ||||
620 | } | ||||
621 | elsif (defined ($token = $7)) { | ||||
622 | # reserved words may be in lower case unless case sensitive | ||||
623 | $uctoken = $anycase ? uc $token : $token; | ||||
624 | unless (defined ($type = $lextable->{ $uctoken })) { | ||||
625 | $type = 'UNQUOTED'; | ||||
626 | } | ||||
627 | } | ||||
628 | |||||
629 | 2790 | 9.83ms | push(@tokens, $type, $token); # spent 9.83ms making 2790 calls to Template::Parser::CORE:match, avg 4µs/call | ||
630 | |||||
631 | # print(STDERR " +[ $type, $token ]\n") | ||||
632 | # if $DEBUG; | ||||
633 | } | ||||
634 | |||||
635 | # print STDERR "tokenise directive() returning:\n [ @tokens ]\n" | ||||
636 | # if $DEBUG; | ||||
637 | |||||
638 | return \@tokens; ## RETURN ## | ||||
639 | } | ||||
640 | |||||
641 | |||||
642 | #------------------------------------------------------------------------ | ||||
643 | # define_block($name, $block) | ||||
644 | # | ||||
645 | # Called by the parser 'defblock' rule when a BLOCK definition is | ||||
646 | # encountered in the template. The name of the block is passed in the | ||||
647 | # first parameter and a reference to the compiled block is passed in | ||||
648 | # the second. This method stores the block in the $self->{ DEFBLOCK } | ||||
649 | # hash which has been initialised by parse() and will later be used | ||||
650 | # by the same method to call the store() method on the calling cache | ||||
651 | # to define the block "externally". | ||||
652 | #------------------------------------------------------------------------ | ||||
653 | |||||
654 | sub define_block { | ||||
655 | my ($self, $name, $block) = @_; | ||||
656 | my $defblock = $self->{ DEFBLOCK } | ||||
657 | || return undef; | ||||
658 | |||||
659 | $self->debug("compiled block '$name':\n$block") | ||||
660 | if $self->{ DEBUG } & Template::Constants::DEBUG_PARSER; | ||||
661 | |||||
662 | $defblock->{ $name } = $block; | ||||
663 | |||||
664 | return undef; | ||||
665 | } | ||||
666 | |||||
667 | sub push_defblock { | ||||
668 | my $self = shift; | ||||
669 | my $stack = $self->{ DEFBLOCK_STACK } ||= []; | ||||
670 | push(@$stack, $self->{ DEFBLOCK } ); | ||||
671 | $self->{ DEFBLOCK } = { }; | ||||
672 | } | ||||
673 | |||||
674 | sub pop_defblock { | ||||
675 | my $self = shift; | ||||
676 | my $defs = $self->{ DEFBLOCK }; | ||||
677 | my $stack = $self->{ DEFBLOCK_STACK } || return $defs; | ||||
678 | return $defs unless @$stack; | ||||
679 | $self->{ DEFBLOCK } = pop @$stack; | ||||
680 | return $defs; | ||||
681 | } | ||||
682 | |||||
683 | |||||
684 | #------------------------------------------------------------------------ | ||||
685 | # add_metadata(\@setlist) | ||||
686 | #------------------------------------------------------------------------ | ||||
687 | |||||
688 | sub add_metadata { | ||||
689 | my ($self, $setlist) = @_; | ||||
690 | my $metadata = $self->{ METADATA } | ||||
691 | || return undef; | ||||
692 | |||||
693 | push(@$metadata, @$setlist); | ||||
694 | |||||
695 | return undef; | ||||
696 | } | ||||
697 | |||||
698 | |||||
699 | #------------------------------------------------------------------------ | ||||
700 | # location() | ||||
701 | # | ||||
702 | # Return Perl comment indicating current parser file and line | ||||
703 | #------------------------------------------------------------------------ | ||||
704 | |||||
705 | # spent 11.8ms (10.3+1.53) within Template::Parser::location which was called 626 times, avg 19µs/call:
# 626 times (10.3ms+1.53ms) by Template::Grammar::__ANON__[Parser.yp:79] at line 78 of Parser.yp, avg 19µs/call | ||||
706 | 5008 | 12.6ms | my $self = shift; | ||
707 | return "\n" unless $self->{ FILE_INFO }; | ||||
708 | my $line = ${ $self->{ LINE } }; | ||||
709 | my $info = $self->{ FILEINFO }->[-1]; | ||||
710 | my $file = $info->{ path } || $info->{ name } | ||||
711 | || '(unknown template)'; | ||||
712 | 626 | 1.53ms | $line =~ s/\-.*$//; # might be 'n-n' # spent 1.53ms making 626 calls to Template::Parser::CORE:subst, avg 2µs/call | ||
713 | $line ||= 1; | ||||
714 | return "#line $line \"$file\"\n"; | ||||
715 | } | ||||
716 | |||||
717 | |||||
718 | #======================================================================== | ||||
719 | # ----- PRIVATE METHODS ----- | ||||
720 | #======================================================================== | ||||
721 | |||||
722 | #------------------------------------------------------------------------ | ||||
723 | # _parse(\@tokens, \@info) | ||||
724 | # | ||||
725 | # Parses the list of input tokens passed by reference and returns a | ||||
726 | # Template::Directive::Block object which contains the compiled | ||||
727 | # representation of the template. | ||||
728 | # | ||||
729 | # This is the main parser DFA loop. See embedded comments for | ||||
730 | # further details. | ||||
731 | # | ||||
732 | # On error, undef is returned and the internal _ERROR field is set to | ||||
733 | # indicate the error. This can be retrieved by calling the error() | ||||
734 | # method. | ||||
735 | #------------------------------------------------------------------------ | ||||
736 | |||||
737 | # spent 549ms (410+139) within Template::Parser::_parse which was called 8 times, avg 68.7ms/call:
# 8 times (410ms+139ms) by Template::Parser::parse at line 291, avg 68.7ms/call | ||||
738 | 273430 | 395ms | my ($self, $tokens, $info) = @_; | ||
739 | my ($token, $value, $text, $line, $inperl); | ||||
740 | my ($state, $stateno, $status, $action, $lookup, $coderet, @codevars); | ||||
741 | my ($lhs, $len, $code); # rule contents | ||||
742 | my $stack = [ [ 0, undef ] ]; # DFA stack | ||||
743 | |||||
744 | # DEBUG | ||||
745 | # local $" = ', '; | ||||
746 | |||||
747 | # retrieve internal rule and state tables | ||||
748 | my ($states, $rules) = @$self{ qw( STATES RULES ) }; | ||||
749 | |||||
750 | # call the grammar set_factory method to install emitter factory | ||||
751 | 8 | 83µs | $self->{ GRAMMAR }->install_factory($self->{ FACTORY }); # spent 83µs making 8 calls to Template::Grammar::install_factory, avg 10µs/call | ||
752 | |||||
753 | $line = $inperl = 0; | ||||
754 | $self->{ LINE } = \$line; | ||||
755 | $self->{ FILE } = $info->{ name }; | ||||
756 | $self->{ INPERL } = \$inperl; | ||||
757 | |||||
758 | $status = CONTINUE; | ||||
759 | my $in_string = 0; | ||||
760 | |||||
761 | while(1) { | ||||
762 | # get state number and state | ||||
763 | $stateno = $stack->[-1]->[0]; | ||||
764 | $state = $states->[$stateno]; | ||||
765 | |||||
766 | # see if any lookaheads exist for the current state | ||||
767 | if (exists $state->{'ACTIONS'}) { | ||||
768 | |||||
769 | # get next token and expand any directives (i.e. token is an | ||||
770 | # array ref) onto the front of the token list | ||||
771 | while (! defined $token && @$tokens) { | ||||
772 | $token = shift(@$tokens); | ||||
773 | if (ref $token) { | ||||
774 | ($text, $line, $token) = @$token; | ||||
775 | if (ref $token) { | ||||
776 | if ($info->{ DEBUG } && ! $in_string) { | ||||
777 | # - - - - - - - - - - - - - - - - - - - - - - - - - | ||||
778 | # This is gnarly. Look away now if you're easily | ||||
779 | # frightened. We're pushing parse tokens onto the | ||||
780 | # pending list to simulate a DEBUG directive like so: | ||||
781 | # [% DEBUG msg line='20' text='INCLUDE foo' %] | ||||
782 | # - - - - - - - - - - - - - - - - - - - - - - - - - | ||||
783 | my $dtext = $text; | ||||
784 | $dtext =~ s[(['\\])][\\$1]g; | ||||
785 | unshift(@$tokens, | ||||
786 | DEBUG => 'DEBUG', | ||||
787 | IDENT => 'msg', | ||||
788 | IDENT => 'line', | ||||
789 | ASSIGN => '=', | ||||
790 | LITERAL => "'$line'", | ||||
791 | IDENT => 'text', | ||||
792 | ASSIGN => '=', | ||||
793 | LITERAL => "'$dtext'", | ||||
794 | IDENT => 'file', | ||||
795 | ASSIGN => '=', | ||||
796 | LITERAL => "'$info->{ name }'", | ||||
797 | (';') x 2, | ||||
798 | @$token, | ||||
799 | (';') x 2); | ||||
800 | } | ||||
801 | else { | ||||
802 | unshift(@$tokens, @$token, (';') x 2); | ||||
803 | } | ||||
804 | $token = undef; # force redo | ||||
805 | } | ||||
806 | elsif ($token eq 'ITEXT') { | ||||
807 | if ($inperl) { | ||||
808 | # don't perform interpolation in PERL blocks | ||||
809 | $token = 'TEXT'; | ||||
810 | $value = $text; | ||||
811 | } | ||||
812 | else { | ||||
813 | unshift(@$tokens, | ||||
814 | @{ $self->interpolate_text($text, $line) }); | ||||
815 | $token = undef; # force redo | ||||
816 | } | ||||
817 | } | ||||
818 | } | ||||
819 | else { | ||||
820 | # toggle string flag to indicate if we're crossing | ||||
821 | # a string boundary | ||||
822 | $in_string = ! $in_string if $token eq '"'; | ||||
823 | $value = shift(@$tokens); | ||||
824 | } | ||||
825 | }; | ||||
826 | # clear undefined token to avoid 'undefined variable blah blah' | ||||
827 | # warnings and let the parser logic pick it up in a minute | ||||
828 | $token = '' unless defined $token; | ||||
829 | |||||
830 | # get the next state for the current lookahead token | ||||
831 | $action = defined ($lookup = $state->{'ACTIONS'}->{ $token }) | ||||
832 | ? $lookup | ||||
833 | : defined ($lookup = $state->{'DEFAULT'}) | ||||
834 | ? $lookup | ||||
835 | : undef; | ||||
836 | } | ||||
837 | else { | ||||
838 | # no lookahead actions | ||||
839 | $action = $state->{'DEFAULT'}; | ||||
840 | } | ||||
841 | |||||
842 | # ERROR: no ACTION | ||||
843 | last unless defined $action; | ||||
844 | |||||
845 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - | ||||
846 | # shift (+ive ACTION) | ||||
847 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - | ||||
848 | if ($action > 0) { | ||||
849 | push(@$stack, [ $action, $value ]); | ||||
850 | $token = $value = undef; | ||||
851 | redo; | ||||
852 | }; | ||||
853 | |||||
854 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - | ||||
855 | # reduce (-ive ACTION) | ||||
856 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - | ||||
857 | ($lhs, $len, $code) = @{ $rules->[ -$action ] }; | ||||
858 | |||||
859 | # no action imples ACCEPTance | ||||
860 | $action | ||||
861 | or $status = ACCEPT; | ||||
862 | |||||
863 | # use dummy sub if code ref doesn't exist | ||||
864 | 3116 | 15.1ms | # spent 9.01ms within Template::Parser::__ANON__[/usr/lib/perl5/Template/Parser.pm:864] which was called 3116 times, avg 3µs/call:
# 3116 times (9.01ms+0s) by Template::Parser::_parse at line 872, avg 3µs/call | ||
865 | unless $code; | ||||
866 | |||||
867 | @codevars = $len | ||||
868 | ? map { $_->[1] } @$stack[ -$len .. -1 ] | ||||
869 | : (); | ||||
870 | |||||
871 | eval { | ||||
872 | 11115 | 139ms | $coderet = &$code( $self, @codevars ); # spent 48.2ms making 914 calls to Template::Grammar::__ANON__[Parser.yp:76], avg 53µs/call
# spent 19.0ms making 626 calls to Template::Grammar::__ANON__[Parser.yp:79], avg 30µs/call
# spent 12.1ms making 643 calls to Template::Grammar::__ANON__[Parser.yp:305], avg 19µs/call
# spent 9.01ms making 3116 calls to Template::Parser::__ANON__[Template/Parser.pm:864], avg 3µs/call
# spent 8.50ms making 416 calls to Template::Grammar::__ANON__[Parser.yp:67], avg 20µs/call
# spent 7.36ms making 273 calls to Template::Grammar::__ANON__[Parser.yp:141], avg 27µs/call
# spent 6.05ms making 1124 calls to Template::Grammar::__ANON__[Parser.yp:72], avg 5µs/call
# spent 4.42ms making 919 calls to Template::Grammar::__ANON__[Parser.yp:345], avg 5µs/call
# spent 4.05ms making 915 calls to Template::Grammar::__ANON__[Parser.yp:341], avg 4µs/call
# spent 3.74ms making 236 calls to Template::Grammar::__ANON__[Parser.yp:90], avg 16µs/call
# spent 2.86ms making 27 calls to Template::Grammar::__ANON__[Parser.yp:168], avg 106µs/call
# spent 2.70ms making 74 calls to Template::Grammar::__ANON__[Parser.yp:229], avg 36µs/call
# spent 1.78ms making 416 calls to Template::Grammar::__ANON__[Parser.yp:73], avg 4µs/call
# spent 1.74ms making 273 calls to Template::Grammar::__ANON__[Parser.yp:334], avg 6µs/call
# spent 1.20ms making 8 calls to Template::Grammar::__ANON__[Parser.yp:64], avg 150µs/call
# spent 1.17ms making 276 calls to Template::Grammar::__ANON__[Parser.yp:364], avg 4µs/call
# spent 1.07ms making 67 calls to Template::Grammar::__ANON__[Parser.yp:109], avg 16µs/call
# spent 729µs making 192 calls to Template::Grammar::__ANON__[Parser.yp:152], avg 4µs/call
# spent 610µs making 27 calls to Template::Grammar::__ANON__[Parser.yp:167], avg 23µs/call
# spent 403µs making 93 calls to Template::Grammar::__ANON__[Parser.yp:151], avg 4µs/call
# spent 371µs making 113 calls to Template::Grammar::__ANON__[Parser.yp:387], avg 3µs/call
# spent 329µs making 79 calls to Template::Grammar::__ANON__[Parser.yp:407], avg 4µs/call
# spent 327µs making 74 calls to Template::Grammar::__ANON__[Parser.yp:416], avg 4µs/call
# spent 320µs making 12 calls to Template::Grammar::__ANON__[Parser.yp:144], avg 27µs/call
# spent 315µs making 82 calls to Template::Grammar::__ANON__[Parser.yp:412], avg 4µs/call
# spent 251µs making 8 calls to Template::Grammar::__ANON__[Parser.yp:118], avg 31µs/call
# spent 143µs making 27 calls to Template::Grammar::__ANON__[Parser.yp:176], avg 5µs/call
# spent 109µs making 3 calls to Template::Grammar::__ANON__[Parser.yp:115], avg 36µs/call
# spent 97µs making 23 calls to Template::Grammar::__ANON__[Parser.yp:357], avg 4µs/call
# spent 85µs making 4 calls to Template::Grammar::__ANON__[Parser.yp:342], avg 21µs/call
# spent 78µs making 12 calls to Template::Grammar::__ANON__[Parser.yp:359], avg 7µs/call
# spent 71µs making 9 calls to Template::Grammar::__ANON__[Parser.yp:382], avg 8µs/call
# spent 37µs making 9 calls to Template::Grammar::__ANON__[Parser.yp:360], avg 4µs/call
# spent 30µs making 1 call to Template::Grammar::__ANON__[Parser.yp:440]
# spent 28µs making 4 calls to Template::Grammar::__ANON__[Parser.yp:150], avg 7µs/call
# spent 21µs making 1 call to Template::Grammar::__ANON__[Parser.yp:307]
# spent 19µs making 1 call to Template::Grammar::__ANON__[Parser.yp:145]
# spent 17µs making 4 calls to Template::Grammar::__ANON__[Parser.yp:312], avg 4µs/call
# spent 14µs making 1 call to Template::Grammar::__ANON__[Parser.yp:68]
# spent 13µs making 3 calls to Template::Grammar::__ANON__[Parser.yp:408], avg 4µs/call
# spent 9µs making 3 calls to Template::Grammar::__ANON__[Parser.yp:374], avg 3µs/call
# spent 9µs making 3 calls to Template::Grammar::__ANON__[Parser.yp:386], avg 3µs/call
# spent 7µs making 1 call to Template::Grammar::__ANON__[Parser.yp:361]
# spent 7µs making 1 call to Template::Grammar::__ANON__[Parser.yp:299]
# spent 6µs making 1 call to Template::Grammar::__ANON__[Parser.yp:435]
# spent 4µs making 1 call to Template::Grammar::__ANON__[Parser.yp:436] | ||
873 | }; | ||||
874 | if ($@) { | ||||
875 | my $err = $@; | ||||
876 | chomp $err; | ||||
877 | return $self->_parse_error($err); | ||||
878 | } | ||||
879 | |||||
880 | # reduce stack by $len | ||||
881 | splice(@$stack, -$len, $len); | ||||
882 | |||||
883 | # ACCEPT | ||||
884 | return $coderet ## RETURN ## | ||||
885 | if $status == ACCEPT; | ||||
886 | |||||
887 | # ABORT | ||||
888 | return undef ## RETURN ## | ||||
889 | if $status == ABORT; | ||||
890 | |||||
891 | # ERROR | ||||
892 | last | ||||
893 | if $status == ERROR; | ||||
894 | } | ||||
895 | continue { | ||||
896 | push(@$stack, [ $states->[ $stack->[-1][0] ]->{'GOTOS'}->{ $lhs }, | ||||
897 | $coderet ]), | ||||
898 | } | ||||
899 | |||||
900 | # ERROR ## RETURN ## | ||||
901 | return $self->_parse_error('unexpected end of input') | ||||
902 | unless defined $value; | ||||
903 | |||||
904 | # munge text of last directive to make it readable | ||||
905 | # $text =~ s/\n/\\n/g; | ||||
906 | |||||
907 | return $self->_parse_error("unexpected end of directive", $text) | ||||
908 | if $value eq ';'; # end of directive SEPARATOR | ||||
909 | |||||
910 | return $self->_parse_error("unexpected token ($value)", $text); | ||||
911 | } | ||||
912 | |||||
- - | |||||
915 | #------------------------------------------------------------------------ | ||||
916 | # _parse_error($msg, $dirtext) | ||||
917 | # | ||||
918 | # Method used to handle errors encountered during the parse process | ||||
919 | # in the _parse() method. | ||||
920 | #------------------------------------------------------------------------ | ||||
921 | |||||
922 | sub _parse_error { | ||||
923 | my ($self, $msg, $text) = @_; | ||||
924 | my $line = $self->{ LINE }; | ||||
925 | $line = ref($line) ? $$line : $line; | ||||
926 | $line = 'unknown' unless $line; | ||||
927 | |||||
928 | $msg .= "\n [% $text %]" | ||||
929 | if defined $text; | ||||
930 | |||||
931 | return $self->error("line $line: $msg"); | ||||
932 | } | ||||
933 | |||||
934 | |||||
935 | #------------------------------------------------------------------------ | ||||
936 | # _dump() | ||||
937 | # | ||||
938 | # Debug method returns a string representing the internal state of the | ||||
939 | # object. | ||||
940 | #------------------------------------------------------------------------ | ||||
941 | |||||
942 | sub _dump { | ||||
943 | my $self = shift; | ||||
944 | my $output = "[Template::Parser] {\n"; | ||||
945 | my $format = " %-16s => %s\n"; | ||||
946 | my $key; | ||||
947 | |||||
948 | foreach $key (qw( START_TAG END_TAG TAG_STYLE ANYCASE INTERPOLATE | ||||
949 | PRE_CHOMP POST_CHOMP V1DOLLAR )) { | ||||
950 | my $val = $self->{ $key }; | ||||
951 | $val = '<undef>' unless defined $val; | ||||
952 | $output .= sprintf($format, $key, $val); | ||||
953 | } | ||||
954 | |||||
955 | $output .= '}'; | ||||
956 | return $output; | ||||
957 | } | ||||
958 | |||||
959 | |||||
960 | 1 | 22µs | 1; | ||
961 | |||||
962 | __END__ | ||||
# spent 17.4ms within Template::Parser::CORE:match which was called 5916 times, avg 3µs/call:
# 2790 times (9.83ms+0s) by Template::Parser::tokenise_directive at line 629, avg 4µs/call
# 1036 times (5.94ms+0s) by Template::Parser::tokenise_directive at line 524, avg 6µs/call
# 1035 times (957µs+0s) by Template::Parser::split_text at line 348, avg 925ns/call
# 1035 times (634µs+0s) by Template::Parser::split_text at line 403, avg 613ns/call
# 18 times (17µs+0s) by Template::Parser::tokenise_directive at line 572, avg 933ns/call
# once (4µs+0s) by Template::Parser::interpolate_text at line 456
# once (700ns+0s) by Template::Parser::interpolate_text at line 477 | |||||
sub Template::Parser::CORE:qr; # opcode | |||||
# spent 4.73ms within Template::Parser::CORE:regcomp which was called 4148 times, avg 1µs/call:
# 1035 times (2.11ms+0s) by Template::Parser::split_text at line 429, avg 2µs/call
# 1035 times (1.63ms+0s) by Template::Parser::split_text at line 403, avg 2µs/call
# 1035 times (538µs+0s) by Template::Parser::split_text at line 353, avg 520ns/call
# 1035 times (405µs+0s) by Template::Parser::split_text at line 372, avg 391ns/call
# 8 times (47µs+0s) by Template::Parser::split_text at line 330, avg 6µs/call | |||||
# spent 19.7ms within Template::Parser::CORE:subst which was called 3759 times, avg 5µs/call:
# 1035 times (9.02ms+0s) by Template::Parser::split_text at line 372, avg 9µs/call
# 1035 times (5.77ms+0s) by Template::Parser::split_text at line 429, avg 6µs/call
# 1035 times (3.30ms+0s) by Template::Parser::split_text at line 353, avg 3µs/call
# 626 times (1.53ms+0s) by Template::Parser::location at line 712, avg 2µs/call
# 17 times (17µs+0s) by Template::Parser::tokenise_directive at line 589, avg 988ns/call
# 8 times (93µs+0s) by Template::Parser::split_text at line 330, avg 12µs/call
# once (5µs+0s) by Template::Parser::tokenise_directive at line 579
# once (700ns+0s) by Template::Parser::interpolate_text at line 473
# once (600ns+0s) by Template::Parser::tokenise_directive at line 580 | |||||
# spent 4µs within Template::Parser::CORE:substcont which was called 2 times, avg 2µs/call:
# 2 times (4µs+0s) by Template::Parser::tokenise_directive at line 579, avg 2µs/call |