Filename | /usr/local/src/github.com/foswiki/core/lib/Foswiki/Infix/Parser.pm |
Statements | Executed 5683 statements in 38.0ms |
Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
---|---|---|---|---|---|
77 | 1 | 1 | 12.1ms | 30.1ms | __ANON__[:299] | Foswiki::Infix::Parser::
77 | 1 | 1 | 6.58ms | 41.6ms | _parse | Foswiki::Infix::Parser::
182 | 2 | 1 | 4.69ms | 6.50ms | _apply | Foswiki::Infix::Parser::
77 | 1 | 1 | 3.49ms | 4.61ms | _initialise | Foswiki::Infix::Parser::
557 | 6 | 1 | 2.07ms | 2.07ms | CORE:regcomp (opcode) | Foswiki::Infix::Parser::
510 | 2 | 1 | 1.99ms | 1.99ms | CORE:subst (opcode) | Foswiki::Infix::Parser::
77 | 3 | 3 | 1.68ms | 47.9ms | parse | Foswiki::Infix::Parser::
536 | 6 | 1 | 1.51ms | 1.51ms | CORE:match (opcode) | Foswiki::Infix::Parser::
89 | 2 | 2 | 994µs | 994µs | addOperator | Foswiki::Infix::Parser::
1 | 1 | 1 | 936µs | 7.29ms | BEGIN@26 | Foswiki::Infix::Parser::
98 | 4 | 1 | 276µs | 276µs | CORE:qr (opcode) | Foswiki::Infix::Parser::
3 | 1 | 1 | 153µs | 165µs | new | Foswiki::Infix::Parser::
1 | 1 | 1 | 29µs | 129µs | BEGIN@29 | Foswiki::Infix::Parser::
1 | 1 | 1 | 28µs | 35µs | BEGIN@21 | Foswiki::Infix::Parser::
1 | 1 | 1 | 21µs | 423µs | BEGIN@24 | Foswiki::Infix::Parser::
1 | 1 | 1 | 16µs | 33µs | BEGIN@22 | Foswiki::Infix::Parser::
1 | 1 | 1 | 16µs | 55µs | BEGIN@23 | Foswiki::Infix::Parser::
1 | 1 | 1 | 11µs | 11µs | BEGIN@25 | Foswiki::Infix::Parser::
1 | 1 | 1 | 8µs | 8µs | finish | Foswiki::Infix::Parser::
0 | 0 | 0 | 0s | 0s | __ANON__[:304] | 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 | 49µs | 2 | 42µs | # spent 35µs (28+7) within Foswiki::Infix::Parser::BEGIN@21 which was called:
# once (28µs+7µs) by Foswiki::Query::Parser::BEGIN@21 at line 21 # spent 35µs making 1 call to Foswiki::Infix::Parser::BEGIN@21
# spent 7µs making 1 call to strict::import |
22 | 2 | 42µs | 2 | 50µs | # spent 33µs (16+17) within Foswiki::Infix::Parser::BEGIN@22 which was called:
# once (16µs+17µs) by Foswiki::Query::Parser::BEGIN@21 at line 22 # spent 33µs making 1 call to Foswiki::Infix::Parser::BEGIN@22
# spent 17µs making 1 call to warnings::import |
23 | 2 | 47µs | 2 | 93µs | # spent 55µs (16+39) within Foswiki::Infix::Parser::BEGIN@23 which was called:
# once (16µs+39µs) by Foswiki::Query::Parser::BEGIN@21 at line 23 # spent 55µs making 1 call to Foswiki::Infix::Parser::BEGIN@23
# spent 38µs making 1 call to Assert::import |
24 | 2 | 52µs | 2 | 825µs | # spent 423µs (21+402) within Foswiki::Infix::Parser::BEGIN@24 which was called:
# once (21µs+402µs) by Foswiki::Query::Parser::BEGIN@21 at line 24 # spent 423µs making 1 call to Foswiki::Infix::Parser::BEGIN@24
# spent 402µs making 1 call to Error::import |
25 | 2 | 40µs | 1 | 11µs | # spent 11µs within Foswiki::Infix::Parser::BEGIN@25 which was called:
# once (11µs+0s) by Foswiki::Query::Parser::BEGIN@21 at line 25 # spent 11µs making 1 call to Foswiki::Infix::Parser::BEGIN@25 |
26 | 2 | 163µs | 1 | 7.29ms | # spent 7.29ms (936µs+6.36) within Foswiki::Infix::Parser::BEGIN@26 which was called:
# once (936µs+6.36ms) by Foswiki::Query::Parser::BEGIN@21 at line 26 # spent 7.29ms making 1 call to Foswiki::Infix::Parser::BEGIN@26 |
27 | |||||
28 | # Set to 1 for debug | ||||
29 | 2 | 2.53ms | 2 | 229µs | # spent 129µs (29+100) within Foswiki::Infix::Parser::BEGIN@29 which was called:
# once (29µs+100µs) by Foswiki::Query::Parser::BEGIN@21 at line 29 # spent 129µs making 1 call to Foswiki::Infix::Parser::BEGIN@29
# spent 100µs making 1 call to constant::import |
30 | |||||
31 | =begin TML | ||||
32 | |||||
33 | ---++ ClassMethod new($node_factory, \%options) -> $parser_object | ||||
34 | |||||
35 | Creates a new infix parser. Operators must be added for it to be useful. | ||||
36 | |||||
37 | The tokeniser matches tokens in the following order: operators, | ||||
38 | quotes (" and '), numbers, words, brackets. If you have any overlaps (e.g. | ||||
39 | an operator '<' and a bracket operator '<<') then the first choice | ||||
40 | will match. | ||||
41 | |||||
42 | =$node_factory= needs to be ( the name of a package | an object ) that supports the | ||||
43 | following two functions: | ||||
44 | * =newLeaf($val, $type)= - create a terminal. $type will be: | ||||
45 | 1 if the terminal matched the =words= specification (see below). | ||||
46 | 2 if it is a number matched the =numbers= specification (see below) | ||||
47 | 3 if it is a quoted string | ||||
48 | * =newNode($op, @params)= - create a new operator node. @params | ||||
49 | is a variable-length list of parameters, left to right. $op | ||||
50 | is a reference to the operator hash in the \@opers list. | ||||
51 | These functions should throw Error::Simple in the event of errors. | ||||
52 | Foswiki::Infix::Node is such a class, ripe for subclassing. | ||||
53 | |||||
54 | The remaining parameters are named, and specify options that affect the | ||||
55 | behaviour of the parser: | ||||
56 | 1 =words=>qr//= - should be an RE specifying legal words (unquoted | ||||
57 | terminals that are not operators i.e. names and numbers). By default | ||||
58 | this is =\w+=. | ||||
59 | It's ok if operator names match this RE; operators always have precedence | ||||
60 | over names. | ||||
61 | 2 =numbers=>qr//= - should be an RE specifying legal numbers (unquoted | ||||
62 | terminals that are not operators or words). By default | ||||
63 | this is =qr/[+-]?(?:\d+\.\d+|\d+\.|\.\d+|\d+)(?:[eE][+-]?\d+)?/=, | ||||
64 | which matches integers and floating-point numbers. Number | ||||
65 | matching always takes precedence over word matching (i.e. "1xy" will | ||||
66 | be parsed as a number followed by a word. A typical usage of this option | ||||
67 | is when you only want to recognise integers, in which case you would set | ||||
68 | this to =numbers => qr/\d+/=. | ||||
69 | Strings should always be surrounded by 'single-quotes'. Single quotes in values may | ||||
70 | be escaped using backslash (\). | ||||
71 | |||||
72 | =cut | ||||
73 | |||||
74 | # spent 165µs (153+13) within Foswiki::Infix::Parser::new which was called 3 times, avg 55µs/call:
# 3 times (153µs+13µs) by Foswiki::Query::Parser::new at line 102 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Query/Parser.pm, avg 55µs/call | ||||
75 | 15 | 158µs | my ( $class, $options ) = @_; | ||
76 | |||||
77 | my $this = bless( | ||||
78 | { | ||||
79 | node_factory => $options->{nodeClass}, | ||||
80 | operators => [], | ||||
81 | initialised => 0, | ||||
82 | }, | ||||
83 | $class | ||||
84 | ); | ||||
85 | |||||
86 | 3 | 13µs | $this->{numbers} = # spent 13µs making 3 calls to Foswiki::Infix::Parser::CORE:qr, avg 4µs/call | ||
87 | defined( $options->{numbers} ) | ||||
88 | ? $options->{numbers} | ||||
89 | : qr/(\d+\.\d+|\d+\.|\.\d+|\d+)([eE][+-]?\d+)?/; | ||||
90 | |||||
91 | $this->{words} = | ||||
92 | defined( $options->{words} ) | ||||
93 | ? $options->{words} | ||||
94 | : qr/\w+/; | ||||
95 | |||||
96 | return $this; | ||||
97 | } | ||||
98 | |||||
99 | # Break circular references. | ||||
100 | # spent 8µs within Foswiki::Infix::Parser::finish which was called:
# once (8µs+0s) by Foswiki::Search::finish at line 73 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Search.pm | ||||
101 | 1 | 9µs | my $self = shift; | ||
102 | |||||
103 | } | ||||
104 | |||||
105 | =begin TML | ||||
106 | |||||
107 | ---++ ObjectMethod addOperator($oper) | ||||
108 | Add an operator to the parser. | ||||
109 | |||||
110 | =$oper= is an object that implements the Foswiki::Infix::OP interface. | ||||
111 | |||||
112 | =cut | ||||
113 | |||||
114 | # spent 994µs within Foswiki::Infix::Parser::addOperator which was called 89 times, avg 11µs/call:
# 81 times (909µs+0s) by Foswiki::Query::Parser::new at line 105 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Query/Parser.pm, avg 11µs/call
# 8 times (85µs+0s) by Foswiki::If::Parser::new at line 42 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/If/Parser.pm, avg 11µs/call | ||||
115 | 356 | 1.07ms | my ( $this, $op ) = @_; | ||
116 | push( @{ $this->{operators} }, $op ); | ||||
117 | $this->{initialised} = 0; | ||||
118 | } | ||||
119 | |||||
120 | # Initialise on demand before a first parse | ||||
121 | # spent 4.61ms (3.49+1.12) within Foswiki::Infix::Parser::_initialise which was called 77 times, avg 60µs/call:
# 77 times (3.49ms+1.12ms) by Foswiki::Infix::Parser::parse at line 190, avg 60µs/call | ||||
122 | 905 | 4.77ms | my $this = shift; | ||
123 | |||||
124 | return if $this->{initialised}; | ||||
125 | |||||
126 | # Build operator lists | ||||
127 | my @stdOpsRE; | ||||
128 | my @bracketOpsRE; | ||||
129 | foreach my $op ( @{ $this->{operators} } ) { | ||||
130 | |||||
131 | # Build a RE for the operator. Note that operators | ||||
132 | # that end in \w are terminated with \b | ||||
133 | my $opre = quotemeta( $op->{name} ); | ||||
134 | 89 | 267µs | $opre .= ( $op->{name} =~ /\w$/ ) ? '\b' : ''; # spent 267µs making 89 calls to Foswiki::Infix::Parser::CORE:match, avg 3µs/call | ||
135 | if ( $op->{casematters} ) { | ||||
136 | 8 | 37µs | $op->{InfixParser_RE} = qr/$opre/; # spent 24µs making 4 calls to Foswiki::Infix::Parser::CORE:regcomp, avg 6µs/call
# spent 13µs making 4 calls to Foswiki::Infix::Parser::CORE:qr, avg 3µs/call | ||
137 | } | ||||
138 | else { | ||||
139 | 170 | 740µs | $op->{InfixParser_RE} = qr/$opre/i; # spent 507µs making 85 calls to Foswiki::Infix::Parser::CORE:regcomp, avg 6µs/call
# spent 233µs making 85 calls to Foswiki::Infix::Parser::CORE:qr, avg 3µs/call | ||
140 | } | ||||
141 | if ( defined( $op->{close} ) ) { | ||||
142 | |||||
143 | # bracket op | ||||
144 | $this->{bracket_ops}->{ lc( $op->{name} ) } = $op; | ||||
145 | |||||
146 | $opre = quotemeta( $op->{close} ); | ||||
147 | 6 | 19µs | $opre .= ( $op->{close} =~ /\w$/ ) ? '\b' : ''; # spent 19µs making 6 calls to Foswiki::Infix::Parser::CORE:match, avg 3µs/call | ||
148 | if ( $op->{casematters} ) { | ||||
149 | $op->{InfixParser_closeRE} = qr/$opre/; | ||||
150 | } | ||||
151 | else { | ||||
152 | 12 | 55µs | $op->{InfixParser_closeRE} = qr/$opre/i; # spent 37µs making 6 calls to Foswiki::Infix::Parser::CORE:regcomp, avg 6µs/call
# spent 18µs making 6 calls to Foswiki::Infix::Parser::CORE:qr, avg 3µs/call | ||
153 | } | ||||
154 | push( @bracketOpsRE, $op->{InfixParser_RE} ); | ||||
155 | } | ||||
156 | else { | ||||
157 | if ( $op->{arity} == 1 ) { | ||||
158 | $this->{unary_ops}->{ lc( $op->{name} ) } = $op; | ||||
159 | } | ||||
160 | else { | ||||
161 | $this->{standard_ops}->{ lc( $op->{name} ) } = $op; | ||||
162 | } | ||||
163 | push( @stdOpsRE, $op->{InfixParser_RE} ); | ||||
164 | } | ||||
165 | } | ||||
166 | |||||
167 | # Build regular expression of all standard operators. | ||||
168 | $this->{standard_op_REs} = join( '|', @stdOpsRE ); | ||||
169 | |||||
170 | # and repeat for bracket operators | ||||
171 | $this->{bracket_op_REs} = join( '|', @bracketOpsRE ); | ||||
172 | |||||
173 | $this->{initialised} = 1; | ||||
174 | } | ||||
175 | |||||
176 | =begin TML | ||||
177 | |||||
178 | ---++ ObjectMethod parse($string) -> $parseTree | ||||
179 | Parses =$string=, calling =newLeaf= and =newNode= in the client class | ||||
180 | as necessary to create a parse tree. Returns the result of calling =newNode= | ||||
181 | on the root of the parse. | ||||
182 | |||||
183 | Throws Foswiki::Infix::Error in the event of parse errors. | ||||
184 | |||||
185 | =cut | ||||
186 | |||||
187 | # spent 47.9ms (1.68+46.2) within Foswiki::Infix::Parser::parse which was called 77 times, avg 622µs/call:
# 75 times (1.63ms+42.6ms) by Foswiki::__ANON__[/usr/local/src/github.com/foswiki/core/lib/Foswiki/Macros/IF.pm:43] at line 34 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Macros/IF.pm, avg 589µs/call
# once (23µs+2.00ms) by Foswiki::__ANON__[/usr/local/src/github.com/foswiki/core/lib/Foswiki/Macros/QUERY.pm:56] at line 53 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Macros/QUERY.pm
# once (27µs+1.68ms) by Foswiki::Search::__ANON__[/usr/local/src/github.com/foswiki/core/lib/Foswiki/Search.pm:137] at line 136 of /usr/local/src/github.com/foswiki/core/lib/Foswiki/Search.pm | ||||
188 | 308 | 1.52ms | my ( $this, $expr ) = @_; | ||
189 | my $data = $expr; | ||||
190 | 77 | 4.61ms | $this->_initialise(); # spent 4.61ms making 77 calls to Foswiki::Infix::Parser::_initialise, avg 60µs/call | ||
191 | 77 | 41.6ms | return _parse( $this, $expr, \$data ); # spent 41.6ms making 77 calls to Foswiki::Infix::Parser::_parse, avg 541µs/call | ||
192 | } | ||||
193 | |||||
194 | # Simple stack parser, after Knuth | ||||
195 | # spent 41.6ms (6.58+35.1) within Foswiki::Infix::Parser::_parse which was called 77 times, avg 541µs/call:
# 77 times (6.58ms+35.1ms) by Foswiki::Infix::Parser::parse at line 191, avg 541µs/call | ||||
196 | 924 | 6.60ms | my ( $this, $expr, $input, $term ) = @_; | ||
197 | |||||
198 | throw Foswiki::Infix::Error("Empty expression") | ||||
199 | unless defined($expr); | ||||
200 | 77 | 263µs | $$input = "()" unless $$input =~ /\S/; # spent 263µs making 77 calls to Foswiki::Infix::Parser::CORE:match, avg 3µs/call | ||
201 | |||||
202 | my @opers = (); | ||||
203 | my @opands = (); | ||||
204 | |||||
205 | $input ||= \$expr; | ||||
206 | |||||
207 | print STDERR "Parse: $$input\n" if MONITOR_PARSER; | ||||
208 | my $lastTokWasOper = 1; | ||||
209 | # spent 30.1ms (12.1+18.1) within Foswiki::Infix::Parser::__ANON__[/usr/local/src/github.com/foswiki/core/lib/Foswiki/Infix/Parser.pm:299] which was called 77 times, avg 391µs/call:
# 77 times (12.1ms+18.1ms) by Error::subs::try at line 416 of Error.pm, avg 391µs/call | ||||
210 | 1772 | 16.2ms | 77 | 229µs | while ( $$input =~ /\S/ ) { # spent 229µs making 77 calls to Foswiki::Infix::Parser::CORE:match, avg 3µs/call |
211 | 1017 | 3.50ms | if ( $$input =~ s/^\s*($this->{standard_op_REs})// ) { # spent 1.87ms making 456 calls to Foswiki::Infix::Parser::CORE:subst, avg 4µs/call
# spent 1.14ms making 341 calls to Foswiki::Infix::Parser::CORE:regcomp, avg 3µs/call
# spent 494µs making 220 calls to Foswiki::Infix::Parser::CORE:match, avg 2µs/call | ||
212 | my $opname = $1; | ||||
213 | my $op = $this->{unary_ops}->{ lc($opname) } | ||||
214 | || $this->{standard_ops}->{ lc($opname) }; | ||||
215 | 134 | 431µs | if ( $lastTokWasOper # spent 236µs making 67 calls to Foswiki::Infix::Parser::CORE:match, avg 4µs/call
# spent 195µs making 67 calls to Foswiki::Infix::Parser::CORE:regcomp, avg 3µs/call | ||
216 | && $opname =~ $this->{words} | ||||
217 | && $op->{arity} > 1 ) | ||||
218 | { | ||||
219 | |||||
220 | # op is a word name, and is in an operand position, | ||||
221 | # and is not unary. Treat it as an operand. | ||||
222 | push( @opands, | ||||
223 | $this->{node_factory} | ||||
224 | ->newLeaf( $opname, Foswiki::Infix::Node::NAME ) ); | ||||
225 | print STDERR "Operand: name '$opname'\n" if MONITOR_PARSER; | ||||
226 | $lastTokWasOper = 0; | ||||
227 | next; | ||||
228 | } | ||||
229 | if ( $lastTokWasOper && $this->{unary_ops}->{ lc($opname) } ) { | ||||
230 | |||||
231 | # Op immediately follows another op, and allows unary. | ||||
232 | $op = $this->{unary_ops}->{ lc($opname) }; | ||||
233 | } | ||||
234 | else { | ||||
235 | $op = $this->{standard_ops}->{ lc($opname) } | ||||
236 | || $this->{unary_ops}->{ lc($opname) }; | ||||
237 | } | ||||
238 | print STDERR "Operator: $op\n" if MONITOR_PARSER; | ||||
239 | 105 | 429µs | ASSERT( $op, $opname ) if DEBUG; # spent 429µs making 105 calls to Assert::ASSERTS_OFF, avg 4µs/call | ||
240 | 105 | 1.59ms | _apply( $this, $op->{prec}, \@opers, \@opands ); # spent 1.59ms making 105 calls to Foswiki::Infix::Parser::_apply, avg 15µs/call | ||
241 | push( @opers, $op ); | ||||
242 | $lastTokWasOper = 1; | ||||
243 | } | ||||
244 | elsif ( $$input =~ s/^\s*(['"])(|.*?[^\\])\1// ) { | ||||
245 | my $q = $1; | ||||
246 | my $val = $2; | ||||
247 | print STDERR "Operand: qs '$val'\n" if MONITOR_PARSER; | ||||
248 | |||||
249 | # Handle escaped characters in the string. This is where | ||||
250 | # expansions such as \n are handled | ||||
251 | $val =~ | ||||
252 | 108 | 281µs | s/(?<!\\)\\(0[0-7]{2}|x[a-fA-F0-9]{2}|x{[a-fA-F0-9]+}|n|t|\\|$q)/eval('"\\'.$1.'"')/ge; # spent 164µs making 54 calls to Foswiki::Infix::Parser::CORE:regcomp, avg 3µs/call
# spent 116µs making 54 calls to Foswiki::Infix::Parser::CORE:subst, avg 2µs/call | ||
253 | 54 | 2.90ms | push( @opands, # spent 2.90ms making 54 calls to Foswiki::Query::Node::newLeaf, avg 54µs/call | ||
254 | $this->{node_factory} | ||||
255 | ->newLeaf( $val, Foswiki::Infix::Node::STRING ) ); | ||||
256 | $lastTokWasOper = 0; | ||||
257 | } | ||||
258 | elsif ( $$input =~ s/^\s*($this->{numbers})// ) { | ||||
259 | my $val = 0 + $1; | ||||
260 | print STDERR "Operand: number $val\n" if MONITOR_PARSER; | ||||
261 | 1 | 86µs | push( @opands, # spent 86µs making 1 call to Foswiki::Query::Node::newLeaf | ||
262 | $this->{node_factory} | ||||
263 | ->newLeaf( $val, Foswiki::Infix::Node::NUMBER ) ); | ||||
264 | $lastTokWasOper = 0; | ||||
265 | } | ||||
266 | elsif ( $$input =~ s/^\s*($this->{words})// ) { | ||||
267 | print STDERR "Operand: word '$1'\n" if MONITOR_PARSER; | ||||
268 | my $val = $1; | ||||
269 | 60 | 3.69ms | push( @opands, # spent 3.69ms making 60 calls to Foswiki::Query::Node::newLeaf, avg 62µs/call | ||
270 | $this->{node_factory} | ||||
271 | ->newLeaf( $val, Foswiki::Infix::Node::NAME ) ); | ||||
272 | $lastTokWasOper = 0; | ||||
273 | } | ||||
274 | elsif ( $$input =~ s/^\s*($this->{bracket_op_REs})// ) { | ||||
275 | my $opname = $1; | ||||
276 | print STDERR "Tok: open bracket $opname\n" if MONITOR_PARSER; | ||||
277 | my $op = $this->{bracket_ops}->{ lc($opname) }; | ||||
278 | ASSERT($op) if DEBUG; | ||||
279 | _apply( $this, $op->{prec}, \@opers, \@opands ); | ||||
280 | push( @opers, $op ); | ||||
281 | push( @opands, | ||||
282 | $this->_parse( $expr, $input, $op->{InfixParser_closeRE} ) | ||||
283 | ); | ||||
284 | $lastTokWasOper = 0; | ||||
285 | } | ||||
286 | elsif ( defined($term) && $$input =~ s/^\s*$term// ) { | ||||
287 | print STDERR "Tok: close bracket $term\n" if MONITOR_PARSER; | ||||
288 | |||||
289 | # if the operand stack is empty, push an empty array | ||||
290 | # nonary operator | ||||
291 | $this->onCloseExpr( \@opands ); | ||||
292 | last; | ||||
293 | } | ||||
294 | else { | ||||
295 | throw Foswiki::Infix::Error( 'Syntax error', $expr, $$input ); | ||||
296 | } | ||||
297 | } | ||||
298 | 77 | 4.91ms | _apply( $this, 0, \@opers, \@opands ); # spent 4.91ms making 77 calls to Foswiki::Infix::Parser::_apply, avg 64µs/call | ||
299 | } | ||||
300 | catch Error::Simple with { | ||||
301 | |||||
302 | # Catch errors thrown during the tree building process | ||||
303 | throw Foswiki::Infix::Error( shift, $expr, $$input ); | ||||
304 | 231 | 1.40ms | }; # spent 1.07ms making 77 calls to Error::catch, avg 14µs/call
# spent 326µs making 77 calls to Error::subs::with, avg 4µs/call
# spent 33.4ms making 77 calls to Error::subs::try, avg 434µs/call, recursion: max depth 4, sum of overlapping time 33.4ms | ||
305 | throw Foswiki::Infix::Error( 'Missing operator', $expr, $$input ) | ||||
306 | unless scalar(@opands) == 1; | ||||
307 | throw Foswiki::Infix::Error( | ||||
308 | 'Excess operators (' . join( ' ', map { $_->{name} } @opers ) . ')', | ||||
309 | $expr, $$input ) | ||||
310 | if scalar(@opers); | ||||
311 | my $result = pop(@opands); | ||||
312 | print STDERR "Return " . $result->stringify() . "\n" if MONITOR_PARSER; | ||||
313 | return $result; | ||||
314 | } | ||||
315 | |||||
316 | # Apply ops on the stack while their precedence is higher than $prec | ||||
317 | # For each operator on the stack with precedence >= $prec, pop the | ||||
318 | # required number of operands, construct a new parse node and push | ||||
319 | # the node back onto the operand stack. | ||||
320 | # spent 6.50ms (4.69+1.81) within Foswiki::Infix::Parser::_apply which was called 182 times, avg 36µs/call:
# 105 times (1.33ms+261µs) by Foswiki::Infix::Parser::__ANON__[/usr/local/src/github.com/foswiki/core/lib/Foswiki/Infix/Parser.pm:299] at line 240, avg 15µs/call
# 77 times (3.37ms+1.55ms) by Foswiki::Infix::Parser::__ANON__[/usr/local/src/github.com/foswiki/core/lib/Foswiki/Infix/Parser.pm:299] at line 298, avg 64µs/call | ||||
321 | 1387 | 4.76ms | my ( $this, $prec, $opers, $opands ) = @_; | ||
322 | |||||
323 | while (scalar(@$opers) | ||||
324 | && $opers->[-1]->{prec} >= $prec | ||||
325 | && scalar(@$opands) >= $opers->[-1]->{arity} ) | ||||
326 | { | ||||
327 | my $op = pop(@$opers); | ||||
328 | my $arity = $op->{arity}; | ||||
329 | my @prams; | ||||
330 | while ( $arity-- ) { | ||||
331 | unshift( @prams, pop(@$opands) ); | ||||
332 | |||||
333 | # Should never get thrown, but just in case... | ||||
334 | throw Foswiki::Infix::Error("Missing operand to '$op->{name}'") | ||||
335 | unless $prams[0]; | ||||
336 | } | ||||
337 | if (MONITOR_PARSER) { | ||||
338 | print STDERR "Apply $op->{name}(" | ||||
339 | . join( ', ', map { $_->stringify() } @prams ) . ")\n"; | ||||
340 | } | ||||
341 | my $folded; | ||||
342 | if ( ref( $prams[0]->{op} ) | ||||
343 | && $op == $prams[0]->{op} | ||||
344 | && $op->{canfold} ) | ||||
345 | { | ||||
346 | push( @{ $prams[0]->{params} }, $prams[1] ); | ||||
347 | push( @$opands, $prams[0] ); | ||||
348 | } | ||||
349 | else { | ||||
350 | 104 | 1.81ms | push( @$opands, $this->{node_factory}->newNode( $op, @prams ) ); # spent 1.81ms making 104 calls to Foswiki::Infix::Node::newNode, avg 17µs/call | ||
351 | } | ||||
352 | } | ||||
353 | } | ||||
354 | |||||
355 | =begin TML | ||||
356 | |||||
357 | ---++ ObjectMethod onCloseExpr($@opands) | ||||
358 | Designed to be overridden by subclasses that need to perform an action on the | ||||
359 | operand stack (such as pushing) when a sub-expression is closed. Also called | ||||
360 | when the root expression is closed. The default is a no-op. | ||||
361 | |||||
362 | =cut | ||||
363 | |||||
364 | sub onCloseExpr { | ||||
365 | my ( $this, $opands ) = @_; | ||||
366 | } | ||||
367 | |||||
368 | 1 | 4µs | 1; | ||
369 | __END__ | ||||
# spent 1.51ms within Foswiki::Infix::Parser::CORE:match which was called 536 times, avg 3µs/call:
# 220 times (494µs+0s) by Foswiki::Infix::Parser::__ANON__[/usr/local/src/github.com/foswiki/core/lib/Foswiki/Infix/Parser.pm:299] at line 211, avg 2µs/call
# 89 times (267µs+0s) by Foswiki::Infix::Parser::_initialise at line 134, avg 3µs/call
# 77 times (263µs+0s) by Foswiki::Infix::Parser::_parse at line 200, avg 3µs/call
# 77 times (229µs+0s) by Foswiki::Infix::Parser::__ANON__[/usr/local/src/github.com/foswiki/core/lib/Foswiki/Infix/Parser.pm:299] at line 210, avg 3µs/call
# 67 times (236µs+0s) by Foswiki::Infix::Parser::__ANON__[/usr/local/src/github.com/foswiki/core/lib/Foswiki/Infix/Parser.pm:299] at line 215, avg 4µs/call
# 6 times (19µs+0s) by Foswiki::Infix::Parser::_initialise at line 147, avg 3µs/call | |||||
# spent 276µs within Foswiki::Infix::Parser::CORE:qr which was called 98 times, avg 3µs/call:
# 85 times (233µs+0s) by Foswiki::Infix::Parser::_initialise at line 139, avg 3µs/call
# 6 times (18µs+0s) by Foswiki::Infix::Parser::_initialise at line 152, avg 3µs/call
# 4 times (13µs+0s) by Foswiki::Infix::Parser::_initialise at line 136, avg 3µs/call
# 3 times (13µs+0s) by Foswiki::Infix::Parser::new at line 86, avg 4µs/call | |||||
# spent 2.07ms within Foswiki::Infix::Parser::CORE:regcomp which was called 557 times, avg 4µs/call:
# 341 times (1.14ms+0s) by Foswiki::Infix::Parser::__ANON__[/usr/local/src/github.com/foswiki/core/lib/Foswiki/Infix/Parser.pm:299] at line 211, avg 3µs/call
# 85 times (507µs+0s) by Foswiki::Infix::Parser::_initialise at line 139, avg 6µs/call
# 67 times (195µs+0s) by Foswiki::Infix::Parser::__ANON__[/usr/local/src/github.com/foswiki/core/lib/Foswiki/Infix/Parser.pm:299] at line 215, avg 3µs/call
# 54 times (164µs+0s) by Foswiki::Infix::Parser::__ANON__[/usr/local/src/github.com/foswiki/core/lib/Foswiki/Infix/Parser.pm:299] at line 252, avg 3µs/call
# 6 times (37µs+0s) by Foswiki::Infix::Parser::_initialise at line 152, avg 6µs/call
# 4 times (24µs+0s) by Foswiki::Infix::Parser::_initialise at line 136, avg 6µs/call | |||||
# spent 1.99ms within Foswiki::Infix::Parser::CORE:subst which was called 510 times, avg 4µs/call:
# 456 times (1.87ms+0s) by Foswiki::Infix::Parser::__ANON__[/usr/local/src/github.com/foswiki/core/lib/Foswiki/Infix/Parser.pm:299] at line 211, avg 4µs/call
# 54 times (116µs+0s) by Foswiki::Infix::Parser::__ANON__[/usr/local/src/github.com/foswiki/core/lib/Foswiki/Infix/Parser.pm:299] at line 252, avg 2µs/call |