Filename | /var/www/foswikidev/core/lib/Foswiki/Infix/Parser.pm |
Statements | Executed 6110 statements in 15.7ms |
Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
---|---|---|---|---|---|
84 | 1 | 1 | 5.69ms | 11.4ms | __ANON__[:306] | Foswiki::Infix::Parser::
84 | 1 | 1 | 5.13ms | 18.2ms | _parse | Foswiki::Infix::Parser::
197 | 2 | 1 | 1.60ms | 1.99ms | _apply | Foswiki::Infix::Parser::
84 | 1 | 1 | 989µs | 989µs | _initialise | Foswiki::Infix::Parser::
84 | 3 | 3 | 828µs | 20.0ms | parse | Foswiki::Infix::Parser::
1 | 1 | 1 | 616µs | 3.88ms | BEGIN@26 | Foswiki::Infix::Parser::
89 | 2 | 2 | 184µs | 184µs | addOperator | Foswiki::Infix::Parser::
3 | 1 | 1 | 50µs | 50µs | new | Foswiki::Infix::Parser::
1 | 1 | 1 | 12µs | 24µs | BEGIN@21 | Foswiki::Infix::Parser::
1 | 1 | 1 | 10µs | 41µs | BEGIN@36 | Foswiki::Infix::Parser::
1 | 1 | 1 | 9µs | 13µs | BEGIN@22 | Foswiki::Infix::Parser::
1 | 1 | 1 | 9µs | 110µs | BEGIN@24 | Foswiki::Infix::Parser::
1 | 1 | 1 | 9µs | 32µs | BEGIN@23 | Foswiki::Infix::Parser::
1 | 1 | 1 | 6µs | 6µs | BEGIN@25 | Foswiki::Infix::Parser::
1 | 1 | 1 | 5µs | 5µs | BEGIN@28 | Foswiki::Infix::Parser::
1 | 1 | 1 | 2µs | 2µs | finish | Foswiki::Infix::Parser::
0 | 0 | 0 | 0s | 0s | __ANON__[:312] | Foswiki::Infix::Parser::
0 | 0 | 0 | 0s | 0s | __ANON__[:317] | Foswiki::Infix::Parser::
0 | 0 | 0 | 0s | 0s | onCloseExpr | Foswiki::Infix::Parser::
Line | State ments |
Time on line |
Calls | Time in subs |
Code |
---|---|---|---|---|---|
1 | # See bottom of file for license and copyright information | ||||
2 | |||||
3 | =begin TML | ||||
4 | |||||
5 | ---+ package Foswiki::Infix::Parser | ||||
6 | |||||
7 | A simple [[http://en.wikipedia.org/wiki/LL_parser][LL(1) parser]] that parses infix expressions with nonary, | ||||
8 | unary and binary operators specified using an operator table. | ||||
9 | |||||
10 | The parser works by examining each token in the input stream from left to right, and constructs | ||||
11 | _parse nodes_ as soon as they are identified. The parser doesn't dictate the type of the parse nodes, | ||||
12 | instead using a _factory_ to generate them. the output from the parser is a | ||||
13 | [[http://en.wikipedia.org/wiki/Parse_tree][parse tree]] built using nodes generated by the node factory. | ||||
14 | |||||
15 | Escapes are supported in strings, using backslash. | ||||
16 | |||||
17 | =cut | ||||
18 | |||||
19 | package Foswiki::Infix::Parser; | ||||
20 | |||||
21 | 2 | 30µs | 2 | 36µs | # spent 24µs (12+12) within Foswiki::Infix::Parser::BEGIN@21 which was called:
# once (12µs+12µs) by Foswiki::Query::Parser::BEGIN@28 at line 21 # spent 24µs making 1 call to Foswiki::Infix::Parser::BEGIN@21
# spent 12µs making 1 call to strict::import |
22 | 2 | 23µs | 2 | 17µs | # spent 13µs (9+4) within Foswiki::Infix::Parser::BEGIN@22 which was called:
# once (9µs+4µs) by Foswiki::Query::Parser::BEGIN@28 at line 22 # spent 13µs making 1 call to Foswiki::Infix::Parser::BEGIN@22
# spent 4µs making 1 call to warnings::import |
23 | 2 | 31µs | 2 | 56µs | # spent 32µs (9+24) within Foswiki::Infix::Parser::BEGIN@23 which was called:
# once (9µs+24µs) by Foswiki::Query::Parser::BEGIN@28 at line 23 # spent 32µs making 1 call to Foswiki::Infix::Parser::BEGIN@23
# spent 24µs making 1 call to Exporter::import |
24 | 2 | 30µs | 2 | 211µs | # spent 110µs (9+101) within Foswiki::Infix::Parser::BEGIN@24 which was called:
# once (9µs+101µs) by Foswiki::Query::Parser::BEGIN@28 at line 24 # spent 110µs making 1 call to Foswiki::Infix::Parser::BEGIN@24
# spent 101µs making 1 call to Error::import |
25 | 2 | 20µs | 1 | 6µs | # spent 6µs within Foswiki::Infix::Parser::BEGIN@25 which was called:
# once (6µs+0s) by Foswiki::Query::Parser::BEGIN@28 at line 25 # spent 6µs making 1 call to Foswiki::Infix::Parser::BEGIN@25 |
26 | 2 | 114µs | 1 | 3.88ms | # spent 3.88ms (616µs+3.27) within Foswiki::Infix::Parser::BEGIN@26 which was called:
# once (616µs+3.27ms) by Foswiki::Query::Parser::BEGIN@28 at line 26 # spent 3.88ms making 1 call to Foswiki::Infix::Parser::BEGIN@26 |
27 | |||||
28 | # spent 5µs within Foswiki::Infix::Parser::BEGIN@28 which was called:
# once (5µs+0s) by Foswiki::Query::Parser::BEGIN@28 at line 33 | ||||
29 | 1 | 5µs | if ( $Foswiki::cfg{UseLocale} ) { | ||
30 | require locale; | ||||
31 | import locale(); | ||||
32 | } | ||||
33 | 1 | 29µs | 1 | 5µs | } # spent 5µs making 1 call to Foswiki::Infix::Parser::BEGIN@28 |
34 | |||||
35 | # Set to 1 for debug | ||||
36 | 2 | 1.45ms | 2 | 72µs | # spent 41µs (10+31) within Foswiki::Infix::Parser::BEGIN@36 which was called:
# once (10µs+31µs) by Foswiki::Query::Parser::BEGIN@28 at line 36 # spent 41µs making 1 call to Foswiki::Infix::Parser::BEGIN@36
# spent 31µs making 1 call to constant::import |
37 | |||||
38 | =begin TML | ||||
39 | |||||
40 | ---++ ClassMethod new($node_factory, \%options) -> $parser_object | ||||
41 | |||||
42 | Creates a new infix parser. Operators must be added for it to be useful. | ||||
43 | |||||
44 | The tokeniser matches tokens in the following order: operators, | ||||
45 | quotes (" and '), numbers, words, brackets. If you have any overlaps (e.g. | ||||
46 | an operator '<' and a bracket operator '<<') then the first choice | ||||
47 | will match. | ||||
48 | |||||
49 | =$node_factory= needs to be ( the name of a package | an object ) that supports the | ||||
50 | following two functions: | ||||
51 | * =newLeaf($val, $type)= - create a terminal. $type will be: | ||||
52 | 1 if the terminal matched the =words= specification (see below). | ||||
53 | 2 if it is a number matched the =numbers= specification (see below) | ||||
54 | 3 if it is a quoted string | ||||
55 | * =newNode($op, @params)= - create a new operator node. @params | ||||
56 | is a variable-length list of parameters, left to right. $op | ||||
57 | is a reference to the operator hash in the \@opers list. | ||||
58 | These functions should throw Error::Simple in the event of errors. | ||||
59 | Foswiki::Infix::Node is such a class, ripe for subclassing. | ||||
60 | |||||
61 | The remaining parameters are named, and specify options that affect the | ||||
62 | behaviour of the parser: | ||||
63 | 1 =words=>qr//= - should be an RE specifying legal words (unquoted | ||||
64 | terminals that are not operators i.e. names and numbers). By default | ||||
65 | this is =\w+=. | ||||
66 | It's ok if operator names match this RE; operators always have precedence | ||||
67 | over names. | ||||
68 | 2 =numbers=>qr//= - should be an RE specifying legal numbers (unquoted | ||||
69 | terminals that are not operators or words). By default | ||||
70 | this is =qr/[+-]?(?:\d+\.\d+|\d+\.|\.\d+|\d+)(?:[eE][+-]?\d+)?/=, | ||||
71 | which matches integers and floating-point numbers. Number | ||||
72 | matching always takes precedence over word matching (i.e. "1xy" will | ||||
73 | be parsed as a number followed by a word. A typical usage of this option | ||||
74 | is when you only want to recognise integers, in which case you would set | ||||
75 | this to =numbers => qr/\d+/=. | ||||
76 | Strings should always be surrounded by 'single-quotes'. Single quotes in values may | ||||
77 | be escaped using backslash (\). | ||||
78 | |||||
79 | =cut | ||||
80 | |||||
81 | # spent 50µs within Foswiki::Infix::Parser::new which was called 3 times, avg 17µs/call:
# 3 times (50µs+0s) by Foswiki::Query::Parser::new at line 109 of /var/www/foswikidev/core/lib/Foswiki/Query/Parser.pm, avg 17µs/call | ||||
82 | 3 | 4µs | my ( $class, $options ) = @_; | ||
83 | |||||
84 | 3 | 29µs | my $this = bless( | ||
85 | { | ||||
86 | node_factory => $options->{nodeClass}, | ||||
87 | operators => [], | ||||
88 | initialised => 0, | ||||
89 | }, | ||||
90 | $class | ||||
91 | ); | ||||
92 | |||||
93 | 3 | 8µs | $this->{numbers} = | ||
94 | defined( $options->{numbers} ) | ||||
95 | ? $options->{numbers} | ||||
96 | : qr/(\d+\.\d+|\d+\.|\.\d+|\d+)([eE][+-]?\d+)?/; | ||||
97 | |||||
98 | 3 | 3µs | $this->{words} = | ||
99 | defined( $options->{words} ) | ||||
100 | ? $options->{words} | ||||
101 | : qr/\w+/; | ||||
102 | |||||
103 | 3 | 13µs | return $this; | ||
104 | } | ||||
105 | |||||
106 | # Break circular references. | ||||
107 | # spent 2µs within Foswiki::Infix::Parser::finish which was called:
# once (2µs+0s) by Foswiki::Search::finish at line 70 of /var/www/foswikidev/core/lib/Foswiki/Search.pm | ||||
108 | 1 | 5µs | my $self = shift; | ||
109 | |||||
110 | } | ||||
111 | |||||
112 | =begin TML | ||||
113 | |||||
114 | ---++ ObjectMethod addOperator($oper) | ||||
115 | Add an operator to the parser. | ||||
116 | |||||
117 | =$oper= is an object that implements the Foswiki::Infix::OP interface. | ||||
118 | |||||
119 | =cut | ||||
120 | |||||
121 | # spent 184µs within Foswiki::Infix::Parser::addOperator which was called 89 times, avg 2µs/call:
# 81 times (165µs+0s) by Foswiki::Query::Parser::new at line 112 of /var/www/foswikidev/core/lib/Foswiki/Query/Parser.pm, avg 2µs/call
# 8 times (19µs+0s) by Foswiki::If::Parser::new at line 49 of /var/www/foswikidev/core/lib/Foswiki/If/Parser.pm, avg 2µs/call | ||||
122 | 89 | 39µs | my ( $this, $op ) = @_; | ||
123 | 89 | 42µs | push( @{ $this->{operators} }, $op ); | ||
124 | 89 | 164µs | $this->{initialised} = 0; | ||
125 | } | ||||
126 | |||||
127 | # Initialise on demand before a first parse | ||||
128 | # spent 989µs within Foswiki::Infix::Parser::_initialise which was called 84 times, avg 12µs/call:
# 84 times (989µs+0s) by Foswiki::Infix::Parser::parse at line 197, avg 12µs/call | ||||
129 | 84 | 30µs | my $this = shift; | ||
130 | |||||
131 | 84 | 321µs | return if $this->{initialised}; | ||
132 | |||||
133 | # Build operator lists | ||||
134 | 3 | 700ns | my @stdOpsRE; | ||
135 | 3 | 1µs | my @bracketOpsRE; | ||
136 | 3 | 4µs | foreach my $op ( @{ $this->{operators} } ) { | ||
137 | |||||
138 | # Build a RE for the operator. Note that operators | ||||
139 | # that end in \w are terminated with \b | ||||
140 | 89 | 28µs | my $opre = quotemeta( $op->{name} ); | ||
141 | 89 | 85µs | $opre .= ( $op->{name} =~ m/\w$/ ) ? '\b' : ''; | ||
142 | 89 | 33µs | if ( $op->{casematters} ) { | ||
143 | 4 | 16µs | $op->{InfixParser_RE} = qr/$opre/; | ||
144 | } | ||||
145 | else { | ||||
146 | 85 | 288µs | $op->{InfixParser_RE} = qr/$opre/i; | ||
147 | } | ||||
148 | 89 | 54µs | if ( defined( $op->{close} ) ) { | ||
149 | |||||
150 | # bracket op | ||||
151 | 6 | 12µs | $this->{bracket_ops}->{ lc( $op->{name} ) } = $op; | ||
152 | |||||
153 | 6 | 3µs | $opre = quotemeta( $op->{close} ); | ||
154 | 6 | 6µs | $opre .= ( $op->{close} =~ m/\w$/ ) ? '\b' : ''; | ||
155 | 6 | 3µs | if ( $op->{casematters} ) { | ||
156 | $op->{InfixParser_closeRE} = qr/$opre/; | ||||
157 | } | ||||
158 | else { | ||||
159 | 6 | 18µs | $op->{InfixParser_closeRE} = qr/$opre/i; | ||
160 | } | ||||
161 | 6 | 3µs | push( @bracketOpsRE, $op->{InfixParser_RE} ); | ||
162 | } | ||||
163 | else { | ||||
164 | 83 | 45µs | if ( $op->{arity} == 1 ) { | ||
165 | $this->{unary_ops}->{ lc( $op->{name} ) } = $op; | ||||
166 | } | ||||
167 | else { | ||||
168 | 56 | 58µs | $this->{standard_ops}->{ lc( $op->{name} ) } = $op; | ||
169 | } | ||||
170 | 83 | 36µs | push( @stdOpsRE, $op->{InfixParser_RE} ); | ||
171 | } | ||||
172 | } | ||||
173 | |||||
174 | # Build regular expression of all standard operators. | ||||
175 | 3 | 10µs | $this->{standard_op_REs} = join( '|', @stdOpsRE ); | ||
176 | |||||
177 | # and repeat for bracket operators | ||||
178 | 3 | 3µs | $this->{bracket_op_REs} = join( '|', @bracketOpsRE ); | ||
179 | |||||
180 | 3 | 14µs | $this->{initialised} = 1; | ||
181 | } | ||||
182 | |||||
183 | =begin TML | ||||
184 | |||||
185 | ---++ ObjectMethod parse($string) -> $parseTree | ||||
186 | Parses =$string=, calling =newLeaf= and =newNode= in the client class | ||||
187 | as necessary to create a parse tree. Returns the result of calling =newNode= | ||||
188 | on the root of the parse. | ||||
189 | |||||
190 | Throws Foswiki::Infix::Error in the event of parse errors. | ||||
191 | |||||
192 | =cut | ||||
193 | |||||
194 | # spent 20.0ms (828µs+19.1) within Foswiki::Infix::Parser::parse which was called 84 times, avg 238µs/call:
# 43 times (266µs+8.32ms) by Foswiki::__ANON__[/var/www/foswikidev/core/lib/Foswiki/Macros/IF.pm:50] at line 41 of /var/www/foswikidev/core/lib/Foswiki/Macros/IF.pm, avg 200µs/call
# 40 times (555µs+10.4ms) by Foswiki::Search::__ANON__[/var/www/foswikidev/core/lib/Foswiki/Search.pm:134] at line 133 of /var/www/foswikidev/core/lib/Foswiki/Search.pm, avg 275µs/call
# once (7µs+393µs) by Foswiki::__ANON__[/var/www/foswikidev/core/lib/Foswiki/Macros/QUERY.pm:68] at line 65 of /var/www/foswikidev/core/lib/Foswiki/Macros/QUERY.pm | ||||
195 | 84 | 62µs | my ( $this, $expr ) = @_; | ||
196 | 84 | 34µs | my $data = $expr; | ||
197 | 84 | 208µs | 84 | 989µs | $this->_initialise(); # spent 989µs making 84 calls to Foswiki::Infix::Parser::_initialise, avg 12µs/call |
198 | 84 | 427µs | 84 | 18.2ms | return _parse( $this, $expr, \$data ); # spent 18.2ms making 84 calls to Foswiki::Infix::Parser::_parse, avg 216µs/call |
199 | } | ||||
200 | |||||
201 | # Simple stack parser, after Knuth | ||||
202 | # spent 18.2ms (5.13+13.0) within Foswiki::Infix::Parser::_parse which was called 84 times, avg 216µs/call:
# 84 times (5.13ms+13.0ms) by Foswiki::Infix::Parser::parse at line 198, avg 216µs/call | ||||
203 | 84 | 73µs | my ( $this, $expr, $input, $term ) = @_; | ||
204 | |||||
205 | 84 | 20µs | throw Foswiki::Infix::Error("Empty expression") | ||
206 | unless defined($expr); | ||||
207 | 84 | 156µs | $$input = "()" unless $$input =~ m/\S/; | ||
208 | |||||
209 | 84 | 43µs | my @opers = (); | ||
210 | 84 | 28µs | my @opands = (); | ||
211 | |||||
212 | 84 | 18µs | $input ||= \$expr; | ||
213 | |||||
214 | print STDERR "Parse: $$input\n" if MONITOR_PARSER; | ||||
215 | 84 | 24µs | my $lastTokWasOper = 1; | ||
216 | # spent 11.4ms (5.69+5.67) within Foswiki::Infix::Parser::__ANON__[/var/www/foswikidev/core/lib/Foswiki/Infix/Parser.pm:306] which was called 84 times, avg 135µs/call:
# 84 times (5.69ms+5.67ms) by Error::subs::try at line 419 of Error.pm, avg 135µs/call | ||||
217 | 84 | 173µs | while ( $$input =~ m/\S/ ) { | ||
218 | 260 | 2.79ms | 1 | 383µs | if ( $$input =~ s/^\s*($this->{standard_op_REs})// ) { # spent 383µs making 1 call to utf8::SWASHNEW |
219 | 113 | 102µs | my $opname = $1; | ||
220 | 113 | 261µs | my $op = $this->{unary_ops}->{ lc($opname) } | ||
221 | || $this->{standard_ops}->{ lc($opname) }; | ||||
222 | 113 | 174µs | if ( $lastTokWasOper | ||
223 | && $opname =~ $this->{words} | ||||
224 | && $op->{arity} > 1 ) | ||||
225 | { | ||||
226 | |||||
227 | # op is a word name, and is in an operand position, | ||||
228 | # and is not unary. Treat it as an operand. | ||||
229 | push( @opands, | ||||
230 | $this->{node_factory} | ||||
231 | ->newLeaf( $opname, Foswiki::Infix::Node::NAME ) ); | ||||
232 | print STDERR "Operand: name '$opname'\n" if MONITOR_PARSER; | ||||
233 | $lastTokWasOper = 0; | ||||
234 | next; | ||||
235 | } | ||||
236 | 113 | 104µs | if ( $lastTokWasOper && $this->{unary_ops}->{ lc($opname) } ) { | ||
237 | |||||
238 | # Op immediately follows another op, and allows unary. | ||||
239 | $op = $this->{unary_ops}->{ lc($opname) }; | ||||
240 | } | ||||
241 | else { | ||||
242 | 63 | 68µs | $op = $this->{standard_ops}->{ lc($opname) } | ||
243 | || $this->{unary_ops}->{ lc($opname) }; | ||||
244 | } | ||||
245 | print STDERR "Operator: $op\n" if MONITOR_PARSER; | ||||
246 | 113 | 10µs | ASSERT( $op, $opname ) if DEBUG; | ||
247 | 113 | 261µs | 113 | 565µs | _apply( $this, $op->{prec}, \@opers, \@opands ); # spent 565µs making 113 calls to Foswiki::Infix::Parser::_apply, avg 5µs/call |
248 | 113 | 42µs | push( @opers, $op ); | ||
249 | 113 | 47µs | $lastTokWasOper = 1; | ||
250 | } | ||||
251 | elsif ( $$input =~ s/^\s*(['"])(|(?:\\.|[^\\])+?)\1// ) { | ||||
252 | 60 | 52µs | my $q = $1; | ||
253 | 60 | 32µs | my $val = $2; | ||
254 | print STDERR "Operand: qs '$val'\n" if MONITOR_PARSER; | ||||
255 | |||||
256 | # Handle escaped characters in the string. This is where | ||||
257 | # expansions such as \n are handled | ||||
258 | $val =~ | ||||
259 | 60 | 160µs | s/(?<!\\)\\(0[0-7]{2}|x[a-fA-F0-9]{2}|x\{[a-fA-F0-9]+\}|n|t|\\|$q)/eval('"\\'.$1.'"')/ge; | ||
260 | 60 | 197µs | 60 | 891µs | push( @opands, # spent 891µs making 60 calls to Foswiki::Query::Node::newLeaf, avg 15µs/call |
261 | $this->{node_factory} | ||||
262 | ->newLeaf( $val, Foswiki::Infix::Node::STRING ) ); | ||||
263 | 60 | 28µs | $lastTokWasOper = 0; | ||
264 | } | ||||
265 | elsif ( $$input =~ s/^\s*($this->{numbers})// ) { | ||||
266 | my $val = 0 + $1; | ||||
267 | print STDERR "Operand: number $val\n" if MONITOR_PARSER; | ||||
268 | push( @opands, | ||||
269 | $this->{node_factory} | ||||
270 | ->newLeaf( $val, Foswiki::Infix::Node::NUMBER ) ); | ||||
271 | $lastTokWasOper = 0; | ||||
272 | } | ||||
273 | elsif ( $$input =~ s/^\s*($this->{words})// ) { | ||||
274 | print STDERR "Operand: word '$1'\n" if MONITOR_PARSER; | ||||
275 | 87 | 91µs | my $val = $1; | ||
276 | 87 | 475µs | 87 | 2.40ms | push( @opands, # spent 2.40ms making 87 calls to Foswiki::Query::Node::newLeaf, avg 28µs/call |
277 | $this->{node_factory} | ||||
278 | ->newLeaf( $val, Foswiki::Infix::Node::NAME ) ); | ||||
279 | 87 | 43µs | $lastTokWasOper = 0; | ||
280 | } | ||||
281 | elsif ( $$input =~ s/^\s*($this->{bracket_op_REs})// ) { | ||||
282 | my $opname = $1; | ||||
283 | print STDERR "Tok: open bracket $opname\n" if MONITOR_PARSER; | ||||
284 | my $op = $this->{bracket_ops}->{ lc($opname) }; | ||||
285 | ASSERT($op) if DEBUG; | ||||
286 | _apply( $this, $op->{prec}, \@opers, \@opands ); | ||||
287 | push( @opers, $op ); | ||||
288 | push( @opands, | ||||
289 | $this->_parse( $expr, $input, $op->{InfixParser_closeRE} ) | ||||
290 | ); | ||||
291 | $lastTokWasOper = 0; | ||||
292 | } | ||||
293 | elsif ( defined($term) && $$input =~ s/^\s*$term// ) { | ||||
294 | print STDERR "Tok: close bracket $term\n" if MONITOR_PARSER; | ||||
295 | |||||
296 | # if the operand stack is empty, push an empty array | ||||
297 | # nonary operator | ||||
298 | $this->onCloseExpr( \@opands ); | ||||
299 | last; | ||||
300 | } | ||||
301 | else { | ||||
302 | throw Foswiki::Infix::Error( 'Syntax error', $expr, $$input ); | ||||
303 | } | ||||
304 | } | ||||
305 | 84 | 284µs | 84 | 1.43ms | _apply( $this, 0, \@opers, \@opands ); # spent 1.43ms making 84 calls to Foswiki::Infix::Parser::_apply, avg 17µs/call |
306 | } | ||||
307 | catch Foswiki::Infix::Error with { | ||||
308 | my $e = shift; | ||||
309 | |||||
310 | # Don't need to construct a new Foswiki::Infix::Error | ||||
311 | throw $e; | ||||
312 | 84 | 3.30ms | 252 | 393µs | }; # spent 290µs making 84 calls to Error::catch, avg 3µs/call
# spent 103µs making 84 calls to Error::subs::with, avg 1µs/call
# spent 12.2ms making 84 calls to Error::subs::try, avg 145µs/call, recursion: max depth 4, sum of overlapping time 12.2ms |
313 | catch Error with { | ||||
314 | |||||
315 | # Catch errors thrown during the tree building process | ||||
316 | throw Foswiki::Infix::Error( shift, $expr, $$input ); | ||||
317 | 84 | 731µs | 168 | 448µs | }; # spent 332µs making 84 calls to Error::catch, avg 4µs/call
# spent 116µs making 84 calls to Error::subs::with, avg 1µs/call |
318 | 84 | 47µs | throw Foswiki::Infix::Error( 'Missing operator', $expr, $$input ) | ||
319 | unless scalar(@opands) == 1; | ||||
320 | throw Foswiki::Infix::Error( | ||||
321 | 84 | 27µs | 'Excess operators (' . join( ' ', map { $_->{name} } @opers ) . ')', | ||
322 | $expr, $$input ) | ||||
323 | if scalar(@opers); | ||||
324 | 84 | 42µs | my $result = pop(@opands); | ||
325 | print STDERR "Return " . $result->stringify() . "\n" if MONITOR_PARSER; | ||||
326 | 84 | 274µs | return $result; | ||
327 | } | ||||
328 | |||||
329 | # Apply ops on the stack while their precedence is higher than $prec | ||||
330 | # For each operator on the stack with precedence >= $prec, pop the | ||||
331 | # required number of operands, construct a new parse node and push | ||||
332 | # the node back onto the operand stack. | ||||
333 | # spent 1.99ms (1.60+390µs) within Foswiki::Infix::Parser::_apply which was called 197 times, avg 10µs/call:
# 113 times (490µs+75µs) by Foswiki::Infix::Parser::__ANON__[/var/www/foswikidev/core/lib/Foswiki/Infix/Parser.pm:306] at line 247, avg 5µs/call
# 84 times (1.11ms+315µs) by Foswiki::Infix::Parser::__ANON__[/var/www/foswikidev/core/lib/Foswiki/Infix/Parser.pm:306] at line 305, avg 17µs/call | ||||
334 | 197 | 128µs | my ( $this, $prec, $opers, $opands ) = @_; | ||
335 | |||||
336 | 197 | 704µs | while (scalar(@$opers) | ||
337 | && $opers->[-1]->{prec} >= $prec | ||||
338 | && scalar(@$opands) >= $opers->[-1]->{arity} ) | ||||
339 | { | ||||
340 | 113 | 52µs | my $op = pop(@$opers); | ||
341 | 113 | 35µs | my $arity = $op->{arity}; | ||
342 | 113 | 19µs | my @prams; | ||
343 | 113 | 64µs | while ( $arity-- ) { | ||
344 | 176 | 76µs | unshift( @prams, pop(@$opands) ); | ||
345 | |||||
346 | # Should never get thrown, but just in case... | ||||
347 | 176 | 77µs | throw Foswiki::Infix::Error("Missing operand to '$op->{name}'") | ||
348 | unless $prams[0]; | ||||
349 | } | ||||
350 | if (MONITOR_PARSER) { | ||||
351 | print STDERR "Apply $op->{name}(" | ||||
352 | . join( ', ', map { $_->stringify() } @prams ) . ")\n"; | ||||
353 | } | ||||
354 | 113 | 18µs | my $folded; | ||
355 | 113 | 215µs | if ( ref( $prams[0]->{op} ) | ||
356 | && $op == $prams[0]->{op} | ||||
357 | && $op->{canfold} ) | ||||
358 | { | ||||
359 | 1 | 500ns | push( @{ $prams[0]->{params} }, $prams[1] ); | ||
360 | 1 | 200ns | push( @$opands, $prams[0] ); | ||
361 | } | ||||
362 | else { | ||||
363 | 112 | 315µs | 112 | 390µs | push( @$opands, $this->{node_factory}->newNode( $op, @prams ) ); # spent 390µs making 112 calls to Foswiki::Infix::Node::newNode, avg 3µs/call |
364 | } | ||||
365 | } | ||||
366 | } | ||||
367 | |||||
368 | =begin TML | ||||
369 | |||||
370 | ---++ ObjectMethod onCloseExpr($@opands) | ||||
371 | Designed to be overridden by subclasses that need to perform an action on the | ||||
372 | operand stack (such as pushing) when a sub-expression is closed. Also called | ||||
373 | when the root expression is closed. The default is a no-op. | ||||
374 | |||||
375 | =cut | ||||
376 | |||||
377 | sub onCloseExpr { | ||||
378 | my ( $this, $opands ) = @_; | ||||
379 | } | ||||
380 | |||||
381 | 1 | 2µs | 1; | ||
382 | __END__ |