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