File: | lib/XML/Parser/Lite/Tree/XPath/Tree.pm |
Coverage: | 84.0% |
line | stmt | bran | cond | sub | time | code |
---|---|---|---|---|---|---|
1 | package XML::Parser::Lite::Tree::XPath::Tree; | |||||
2 | ||||||
3 | 30 30 30 | 116 47 123 | use strict; | |||
4 | 30 30 30 | 145 49 135 | use XML::Parser::Lite::Tree::XPath::Tokener; | |||
5 | ||||||
6 | sub new { | |||||
7 | 179 | 530 | my ($class) = @_; | |||
8 | 179 | 759 | my $self = bless {}, $class; | |||
9 | 179 | 556 | $self->{error} = 0; | |||
10 | 179 | 500 | return $self; | |||
11 | } | |||||
12 | ||||||
13 | sub build_tree { | |||||
14 | 179 | 642 | my ($self, $tokens) = @_; | |||
15 | ||||||
16 | 179 | 470 | $self->{error} = 0; | |||
17 | 179 | 470 | $self->{tokens} = $tokens; | |||
18 | ||||||
19 | # | |||||
20 | # build a basic tree using the brackets | |||||
21 | # | |||||
22 | ||||||
23 | 179 | 575 | return 0 unless $self->make_groups(); | |||
24 | 179 | 823 | $self->recurse_before($self, 'del_links'); | |||
25 | ||||||
26 | ||||||
27 | # | |||||
28 | # simple groupings | |||||
29 | # | |||||
30 | ||||||
31 | 179 | 577 | return 0 unless $self->recurse_before($self, 'clean_axis_and_abbreviations'); | |||
32 | 179 | 562 | return 0 unless $self->recurse_before($self, 'claim_groups'); | |||
33 | 176 | 637 | return 0 unless $self->recurse_after($self, 'build_steps'); | |||
34 | 176 | 545 | return 0 unless $self->recurse_after($self, 'build_paths'); | |||
35 | ||||||
36 | ||||||
37 | # | |||||
38 | # get operator oprands | |||||
39 | # | |||||
40 | ||||||
41 | 176 | 712 | return 0 unless $self->binops(['|'], 'UnionExpr'); | |||
42 | 176 | 608 | return 0 unless $self->recurse_before($self, 'unary_minus'); | |||
43 | 176 | 831 | return 0 unless $self->binops(['*','div','mod'], 'MultiplicativeExpr'); | |||
44 | 176 | 765 | return 0 unless $self->binops(['+','-'], 'AdditiveExpr'); | |||
45 | 176 | 900 | return 0 unless $self->binops(['<','<=','>','>='], 'RelationalExpr'); | |||
46 | 176 | 785 | return 0 unless $self->binops(['=','!='], 'EqualityExpr'); | |||
47 | 176 | 777 | return 0 unless $self->binops(['and'], 'AndExpr'); | |||
48 | 176 | 676 | return 0 unless $self->binops(['or'], 'OrExpr'); | |||
49 | ||||||
50 | #return 0 unless $self->find_expressions(['UnionExpr', 'MultiplicativeExpr', 'AdditiveExpr', 'RelationalExpr', 'EqualityExpr', 'AndExpr', 'OrExpr']); | |||||
51 | ||||||
52 | ||||||
53 | 176 | 738 | return 1; | |||
54 | } | |||||
55 | ||||||
56 | sub dump_flat { | |||||
57 | 41 | 105 | my ($self) = @_; | |||
58 | 41 | 114 | $self->{dump} = ''; | |||
59 | ||||||
60 | 41 41 | 69 136 | for my $token(@{$self->{tokens}}){ | |||
61 | 41 | 124 | $self->dump_flat_go($token); | |||
62 | } | |||||
63 | ||||||
64 | 41 | 111 | my $dump = $self->{dump}; | |||
65 | 41 | 91 | delete $self->{dump}; | |||
66 | 41 | 125 | return $dump; | |||
67 | } | |||||
68 | ||||||
69 | sub dump_flat_go { | |||||
70 | 279 | 696 | my ($self, $node) = @_; | |||
71 | ||||||
72 | 279 | 1024 | $self->{dump} .= '['.$node->dump(); | |||
73 | ||||||
74 | 279 279 | 531 1058 | for my $token(@{$node->{tokens}}){ | |||
75 | ||||||
76 | 238 | 702 | $self->dump_flat_go($token); | |||
77 | } | |||||
78 | ||||||
79 | 279 | 1025 | $self->{dump} .= ']'; | |||
80 | } | |||||
81 | ||||||
82 | sub dump_tree { | |||||
83 | 0 | 0 | my ($self) = @_; | |||
84 | 0 | 0 | $self->{dump} = ''; | |||
85 | 0 | 0 | $self->{indent} = ['']; | |||
86 | ||||||
87 | 0 0 | 0 0 | for my $token(@{$self->{tokens}}){ | |||
88 | 0 | 0 | $self->dump_tree_go($token); | |||
89 | } | |||||
90 | ||||||
91 | 0 | 0 | my $dump = $self->{dump}; | |||
92 | 0 | 0 | delete $self->{dump}; | |||
93 | 0 | 0 | delete $self->{indent}; | |||
94 | 0 | 0 | return $dump; | |||
95 | } | |||||
96 | ||||||
97 | sub dump_tree_go { | |||||
98 | 0 | 0 | my ($self, $node) = @_; | |||
99 | ||||||
100 | 0 0 | 0 0 | $self->{dump} .= @{$self->{indent}}[-1].$node->dump()."\n"; | |||
101 | ||||||
102 | 0 0 0 | 0 0 0 | push @{$self->{indent}}, @{$self->{indent}}[-1].' - '; | |||
103 | ||||||
104 | 0 0 | 0 0 | for my $token(@{$node->{tokens}}){ | |||
105 | ||||||
106 | 0 | 0 | $self->dump_tree_go($token); | |||
107 | } | |||||
108 | ||||||
109 | 0 0 | 0 0 | pop @{$self->{indent}}; | |||
110 | } | |||||
111 | ||||||
112 | sub make_groups { | |||||
113 | 179 | 524 | my ($self) = @_; | |||
114 | ||||||
115 | 179 | 549 | my $tokens = $self->{tokens}; | |||
116 | 179 | 503 | $self->{tokens} = []; | |||
117 | ||||||
118 | 179 | 469 | my $parent = $self; | |||
119 | ||||||
120 | 179 179 | 316 521 | for my $token(@{$tokens}){ | |||
121 | ||||||
122 | 1223 | 3905 | if ($token->match('Symbol', '(')){ | |||
123 | ||||||
124 | 112 | 366 | my $group = XML::Parser::Lite::Tree::XPath::Token->new(); | |||
125 | 112 | 337 | $group->{type} = 'Group()'; | |||
126 | 112 | 317 | $group->{tokens} = []; | |||
127 | 112 | 290 | $group->{parent} = $parent; | |||
128 | ||||||
129 | 112 112 | 241 384 | push @{$parent->{tokens}}, $group; | |||
130 | 112 | 335 | $parent = $group; | |||
131 | ||||||
132 | }elsif ($token->match('Symbol', '[')){ | |||||
133 | ||||||
134 | 50 | 331 | my $group = XML::Parser::Lite::Tree::XPath::Token->new(); | |||
135 | 50 | 272 | $group->{type} = 'Predicate'; | |||
136 | 50 | 152 | $group->{tokens} = []; | |||
137 | 50 | 127 | $group->{parent} = $parent; | |||
138 | ||||||
139 | 50 50 | 304 165 | push @{$parent->{tokens}}, $group; | |||
140 | 50 | 152 | $parent = $group; | |||
141 | ||||||
142 | }elsif ($token->match('Symbol', ')')){ | |||||
143 | ||||||
144 | 112 | 432 | if ($parent->{type} ne 'Group()'){ | |||
145 | 0 | 0 | $self->{error} = "Found unexpected closing bracket ')'."; | |||
146 | 0 | 0 | return 0; | |||
147 | } | |||||
148 | ||||||
149 | 112 | 417 | $parent = $parent->{parent}; | |||
150 | ||||||
151 | }elsif ($token->match('Symbol', ']')){ | |||||
152 | ||||||
153 | 50 | 201 | if ($parent->{type} ne 'Predicate'){ | |||
154 | 0 | 0 | $self->{error} = "Found unexpected closing bracket ']'."; | |||
155 | 0 | 0 | return 0; | |||
156 | } | |||||
157 | ||||||
158 | 50 | 209 | $parent = $parent->{parent}; | |||
159 | ||||||
160 | }else{ | |||||
161 | 899 | 2508 | $token->{parent} = $parent; | |||
162 | 899 899 | 1501 4260 | push @{$parent->{tokens}}, $token; | |||
163 | } | |||||
164 | } | |||||
165 | ||||||
166 | 179 | 928 | return 1; | |||
167 | } | |||||
168 | ||||||
169 | sub recurse_before { | |||||
170 | 4300 | 12898 | my ($self, $root, $method) = @_; | |||
171 | ||||||
172 | 4300 | 13205 | return 0 unless $self->$method($root); | |||
173 | ||||||
174 | 4118 4118 | 6767 14342 | for my $token(@{$root->{tokens}}){ | |||
175 | ||||||
176 | 3587 | 10666 | return 0 unless $self->recurse_before($token, $method); | |||
177 | } | |||||
178 | ||||||
179 | 4118 | 16258 | return 1; | |||
180 | } | |||||
181 | ||||||
182 | sub recurse_after { | |||||
183 | 12633 | 35922 | my ($self, $root, $method) = @_; | |||
184 | ||||||
185 | 12633 12633 | 20041 42890 | for my $token(@{$root->{tokens}}){ | |||
186 | ||||||
187 | 11049 | 31461 | return 0 unless $self->recurse_after($token, $method); | |||
188 | } | |||||
189 | ||||||
190 | 12633 | 39186 | return 0 unless $self->$method($root); | |||
191 | ||||||
192 | 12633 | 49986 | return 1; | |||
193 | } | |||||
194 | ||||||
195 | sub binops { | |||||
196 | 1232 | 3645 | my ($self, $ops, $production) = @_; | |||
197 | 1232 | 3240 | $self->{binops} = $ops; | |||
198 | 1232 | 3265 | $self->{binop_production} = $production; | |||
199 | ||||||
200 | 1232 | 3564 | my $ret = $self->recurse_after($self, 'do_binops'); | |||
201 | ||||||
202 | 1232 | 2822 | delete $self->{binops}; | |||
203 | 1232 | 2797 | delete $self->{binop_production}; | |||
204 | ||||||
205 | 1232 | 4263 | return $ret; | |||
206 | } | |||||
207 | ||||||
208 | sub claim_groups { | |||||
209 | 1443 | 3816 | my ($self, $root) = @_; | |||
210 | ||||||
211 | 1443 | 3655 | my $tokens = $root->{tokens}; | |||
212 | 1443 | 4028 | $root->{tokens} = []; | |||
213 | ||||||
214 | 1443 2802 | 2702 10296 | while(my $token = shift @{$tokens}){ | |||
215 | ||||||
216 | ||||||
217 | # | |||||
218 | # makes claims | |||||
219 | # | |||||
220 | ||||||
221 | 1362 | 4306 | if ($token->match('NodeType')){ | |||
222 | ||||||
223 | # node type's claim the follow group node | |||||
224 | ||||||
225 | 6 6 | 9 15 | my $next = shift @{$tokens}; | |||
226 | ||||||
227 | 6 | 21 | if (!$next->match('Group()')){ | |||
228 | 0 | 0 | $self->{error} = "Found NodeType '$token->{content}' without a following '(' (found a following '$next->{type}')."; | |||
229 | 0 | 0 | return 0; | |||
230 | } | |||||
231 | ||||||
232 | 6 6 | 8 19 | my $childs = scalar(@{$next->{tokens}}); | |||
233 | ||||||
234 | 6 | 77 | if ($token->{content} eq 'processing-instruction'){ | |||
235 | ||||||
236 | 4 | 17 | if ($childs == 0){ | |||
237 | ||||||
238 | #ok | |||||
239 | ||||||
240 | }elsif ($childs == 1){ | |||||
241 | ||||||
242 | 2 | 10 | if ($next->{tokens}->[0]->{type} eq 'Literal'){ | |||
243 | ||||||
244 | 1 | 7 | $token->{argument} = $next->{tokens}->[0]->{content}; | |||
245 | ||||||
246 | }else{ | |||||
247 | 1 | 8 | $self->{error} = "processing-instruction node has a non-Literal child node (of type '$next->{tokens}->[0]->{type}')."; | |||
248 | 1 | 9 | return 0; | |||
249 | } | |||||
250 | }else{ | |||||
251 | 1 | 4 | $self->{error} = "processing-instruction node has more than one child node."; | |||
252 | 1 | 8 | return 0; | |||
253 | } | |||||
254 | ||||||
255 | }else{ | |||||
256 | 2 | 7 | if ($childs > 0){ | |||
257 | 1 | 6 | $self->{error} = "NodeType $token->{content} node has unexpected children."; | |||
258 | 1 | 8 | return 0; | |||
259 | } | |||||
260 | } | |||||
261 | ||||||
262 | 3 | 9 | $token->{type} = 'NodeTypeTest'; | |||
263 | 3 3 | 6 19 | push @{$root->{tokens}}, $token; | |||
264 | ||||||
265 | }elsif ($token->match('FunctionName')){ | |||||
266 | ||||||
267 | # FunctionNames's claim the follow group node - it should be an arglist | |||||
268 | ||||||
269 | 106 106 | 169 276 | my $next = shift @{$tokens}; | |||
270 | ||||||
271 | 106 | 338 | if (!$next->match('Group()')){ | |||
272 | 0 | 0 | $self->{error} = "Found FunctionName '$token->{content}' without a following '(' (found a following '$next->{type}')."; | |||
273 | 0 | 0 | return 0; | |||
274 | } | |||||
275 | ||||||
276 | # | |||||
277 | # recurse manually - this node will never be scanned by this loop | |||||
278 | # | |||||
279 | ||||||
280 | 106 | 386 | return 0 unless $self->claim_groups($next); | |||
281 | ||||||
282 | ||||||
283 | # | |||||
284 | # organise it into an arg list | |||||
285 | # | |||||
286 | ||||||
287 | 106 | 336 | return 0 unless $self->make_arg_list($token, $next); | |||
288 | ||||||
289 | ||||||
290 | ||||||
291 | 106 106 | 185 510 | push @{$root->{tokens}}, $token; | |||
292 | ||||||
293 | ||||||
294 | }elsif ($token->match('Group()')){ | |||||
295 | ||||||
296 | 0 | 0 | $token->{type} = 'PrimaryExpr'; | |||
297 | ||||||
298 | 0 0 | 0 0 | push @{$root->{tokens}}, $token; | |||
299 | ||||||
300 | }else{ | |||||
301 | ||||||
302 | 1250 1250 | 2027 4778 | push @{$root->{tokens}}, $token; | |||
303 | } | |||||
304 | ||||||
305 | } | |||||
306 | ||||||
307 | 1440 | 4827 | return 1; | |||
308 | } | |||||
309 | ||||||
310 | sub make_arg_list { | |||||
311 | 106 | 318 | my ($self, $root, $arg_group) = @_; | |||
312 | ||||||
313 | 106 | 269 | $root->{type} = 'FunctionCall'; | |||
314 | 106 | 286 | $root->{tokens} = []; | |||
315 | ||||||
316 | # no need to construct an arg list if there aren't any args | |||||
317 | 106 106 | 174 477 | return 1 unless scalar @{$arg_group->{tokens}}; | |||
318 | ||||||
319 | 71 | 236 | my $arg = XML::Parser::Lite::Tree::XPath::Token->new(); | |||
320 | 71 | 217 | $arg->{type} = 'FunctionArg'; | |||
321 | 71 | 207 | $arg->{tokens} = []; | |||
322 | ||||||
323 | 71 272 | 128 1142 | while(my $token = shift @{$arg_group->{tokens}}){ | |||
324 | ||||||
325 | 201 | 633 | if ($token->match('Symbol', ',')){ | |||
326 | ||||||
327 | 25 25 | 41 83 | push @{$root->{tokens}}, $arg; | |||
328 | ||||||
329 | 25 | 76 | $arg = XML::Parser::Lite::Tree::XPath::Token->new(); | |||
330 | 25 | 73 | $arg->{type} = 'FunctionArg'; | |||
331 | 25 | 85 | $arg->{tokens} = []; | |||
332 | ||||||
333 | }else{ | |||||
334 | ||||||
335 | 176 | 456 | $token->{parent} = $arg; | |||
336 | 176 176 | 273 699 | push @{$arg->{tokens}}, $token; | |||
337 | } | |||||
338 | } | |||||
339 | ||||||
340 | 71 | 191 | $arg->{parent} = $root; | |||
341 | 71 71 | 123 210 | push @{$root->{tokens}}, $arg; | |||
342 | ||||||
343 | ||||||
344 | 71 | 243 | return 1; | |||
345 | } | |||||
346 | ||||||
347 | sub clean_axis_and_abbreviations { | |||||
348 | ||||||
349 | 1386 | 3787 | my ($self, $root) = @_; | |||
350 | ||||||
351 | 1386 | 3345 | my $tokens = $root->{tokens}; | |||
352 | 1386 | 3851 | $root->{tokens} = []; | |||
353 | ||||||
354 | 1386 2408 | 2480 9187 | while(my $token = shift @{$tokens}){ | |||
355 | ||||||
356 | 1022 | 3305 | if ($token->match('AxisName')){ | |||
357 | ||||||
358 | 39 39 | 63 108 | my $next = shift @{$tokens}; | |||
359 | ||||||
360 | 39 | 241 | unless ($next->match('Symbol', '::')){ | |||
361 | ||||||
362 | 0 | 0 | $self->{error} = "Found an AxisName '$token->{content}' without a following ::"; | |||
363 | 0 | 0 | return 0; | |||
364 | } | |||||
365 | ||||||
366 | 39 | 114 | $token->{type} = 'AxisSpecifier'; | |||
367 | ||||||
368 | 39 39 | 68 154 | push @{$root->{tokens}}, $token; | |||
369 | ||||||
370 | ||||||
371 | }elsif ($token->match('Symbol', '@')){ | |||||
372 | ||||||
373 | 15 | 44 | $token->{type} = 'AxisSpecifier'; | |||
374 | 15 | 41 | $token->{content} = 'attribute'; | |||
375 | ||||||
376 | 15 15 | 23 58 | push @{$root->{tokens}}, $token; | |||
377 | ||||||
378 | ||||||
379 | }elsif ($token->match('Operator', '//')){ | |||||
380 | ||||||
381 | # // == /descendant-or-self::node()/ | |||||
382 | ||||||
383 | 61 | 217 | $token = XML::Parser::Lite::Tree::XPath::Token->new(); | |||
384 | 61 | 203 | $token->{type} = 'Operator'; | |||
385 | 61 | 180 | $token->{content} = '/'; | |||
386 | 61 61 | 104 201 | push @{$root->{tokens}}, $token; | |||
387 | ||||||
388 | 61 | 199 | $token = XML::Parser::Lite::Tree::XPath::Token->new(); | |||
389 | 61 | 242 | $token->{type} = 'AxisSpecifier'; | |||
390 | 61 | 168 | $token->{content} = 'descendant-or-self'; | |||
391 | 61 61 | 107 186 | push @{$root->{tokens}}, $token; | |||
392 | ||||||
393 | 61 | 195 | $token = XML::Parser::Lite::Tree::XPath::Token->new(); | |||
394 | 61 | 182 | $token->{type} = 'NodeTypeTest'; | |||
395 | 61 | 166 | $token->{content} = 'node'; | |||
396 | 61 61 | 111 195 | push @{$root->{tokens}}, $token; | |||
397 | ||||||
398 | 61 | 191 | $token = XML::Parser::Lite::Tree::XPath::Token->new(); | |||
399 | 61 | 176 | $token->{type} = 'Operator'; | |||
400 | 61 | 165 | $token->{content} = '/'; | |||
401 | 61 61 | 107 295 | push @{$root->{tokens}}, $token; | |||
402 | ||||||
403 | ||||||
404 | }elsif ($token->match('Symbol', '.')){ | |||||
405 | ||||||
406 | 1 | 5 | $token = XML::Parser::Lite::Tree::XPath::Token->new(); | |||
407 | 1 | 3 | $token->{type} = 'AxisSpecifier'; | |||
408 | 1 | 4 | $token->{content} = 'self'; | |||
409 | 1 1 | 2 3 | push @{$root->{tokens}}, $token; | |||
410 | ||||||
411 | 1 | 3 | $token = XML::Parser::Lite::Tree::XPath::Token->new(); | |||
412 | 1 | 3 | $token->{type} = 'NodeTypeTest'; | |||
413 | 1 | 3 | $token->{content} = 'node'; | |||
414 | 1 1 | 2 4 | push @{$root->{tokens}}, $token; | |||
415 | ||||||
416 | ||||||
417 | }elsif ($token->match('Symbol', '..')){ | |||||
418 | ||||||
419 | 1 | 4 | $token = XML::Parser::Lite::Tree::XPath::Token->new(); | |||
420 | 1 | 4 | $token->{type} = 'AxisSpecifier'; | |||
421 | 1 | 3 | $token->{content} = 'parent'; | |||
422 | 1 1 | 3 4 | push @{$root->{tokens}}, $token; | |||
423 | ||||||
424 | 1 | 3 | $token = XML::Parser::Lite::Tree::XPath::Token->new(); | |||
425 | 1 | 3 | $token->{type} = 'NodeTypeTest'; | |||
426 | 1 | 3 | $token->{content} = 'node'; | |||
427 | 1 1 | 2 3 | push @{$root->{tokens}}, $token; | |||
428 | ||||||
429 | ||||||
430 | }else{ | |||||
431 | ||||||
432 | 905 905 | 1493 3523 | push @{$root->{tokens}}, $token; | |||
433 | } | |||||
434 | } | |||||
435 | ||||||
436 | 1386 | 4405 | return 1; | |||
437 | } | |||||
438 | ||||||
439 | sub build_steps { | |||||
440 | 1334 | 3522 | my ($self, $root) = @_; | |||
441 | ||||||
442 | 1334 | 3308 | my $tokens = $root->{tokens}; | |||
443 | 1334 | 3637 | $root->{tokens} = []; | |||
444 | ||||||
445 | 1334 2325 | 2204 8796 | while(my $token = shift @{$tokens}){ | |||
446 | ||||||
447 | 991 | 3125 | if ($token->match('AxisSpecifier')){ | |||
448 | ||||||
449 | 117 117 | 240 302 | my $next = shift @{$tokens}; | |||
450 | ||||||
451 | 117 | 356 | unless (defined $next){ | |||
452 | ||||||
453 | 0 | 0 | $self->{error} = "AxisSpecifier found without following NodeTest."; | |||
454 | 0 | 0 | return 0; | |||
455 | } | |||||
456 | ||||||
457 | 117 | 368 | unless ($next->match('NodeTypeTest') || $next->match('NameTest')){ | |||
458 | ||||||
459 | 0 | 0 | $self->{error} = "AxisSpecifier found without following NodeTest (NodeTypeTest | NameTest) (found $next->{type} instead)."; | |||
460 | 0 | 0 | return 0; | |||
461 | } | |||||
462 | ||||||
463 | 117 | 405 | my $step = XML::Parser::Lite::Tree::XPath::Token->new(); | |||
464 | 117 | 365 | $step->{type} = 'Step'; | |||
465 | 117 | 458 | $step->{axis} = $token->{content}; | |||
466 | 117 | 402 | $step->{tokens} = []; | |||
467 | ||||||
468 | 117 117 | 220 377 | push @{$step->{tokens}}, $next; | |||
469 | ||||||
470 | ||||||
471 | 117 117 | 210 474 | while(my $token = shift @{$tokens}){ | |||
472 | ||||||
473 | 73 | 243 | if ($token->match('Predicate')){ | |||
474 | ||||||
475 | 0 0 | 0 0 | push @{$step->{tokens}}, $token; | |||
476 | }else{ | |||||
477 | 73 73 | 142 196 | unshift @{$tokens}, $token; | |||
478 | 73 | 154 | last; | |||
479 | } | |||||
480 | } | |||||
481 | ||||||
482 | 117 117 | 315 551 | push @{$root->{tokens}}, $step; | |||
483 | ||||||
484 | ||||||
485 | }elsif ($token->match('NodeTypeTest') || $token->match('NameTest')){ | |||||
486 | ||||||
487 | 179 | 668 | my $step = XML::Parser::Lite::Tree::XPath::Token->new(); | |||
488 | 179 | 556 | $step->{type} = 'Step'; | |||
489 | 179 | 575 | $step->{tokens} = []; | |||
490 | ||||||
491 | 179 179 | 366 589 | push @{$step->{tokens}}, $token; | |||
492 | ||||||
493 | ||||||
494 | 179 229 | 381 1001 | while(my $token = shift @{$tokens}){ | |||
495 | ||||||
496 | 134 | 431 | if ($token->match('Predicate')){ | |||
497 | ||||||
498 | 50 50 | 88 188 | push @{$step->{tokens}}, $token; | |||
499 | }else{ | |||||
500 | 84 84 | 151 218 | unshift @{$tokens}, $token; | |||
501 | 84 | 161 | last; | |||
502 | } | |||||
503 | } | |||||
504 | ||||||
505 | 179 179 | 314 685 | push @{$root->{tokens}}, $step; | |||
506 | ||||||
507 | ||||||
508 | }elsif ($token->match('Predicate')){ | |||||
509 | ||||||
510 | 0 | 0 | $self->{error} = "Predicate found without preceeding NodeTest."; | |||
511 | 0 | 0 | return 0; | |||
512 | ||||||
513 | }else{ | |||||
514 | ||||||
515 | 695 695 | 1157 2738 | push @{$root->{tokens}}, $token; | |||
516 | } | |||||
517 | } | |||||
518 | ||||||
519 | 1334 | 4379 | return 1; | |||
520 | } | |||||
521 | ||||||
522 | sub build_paths { | |||||
523 | 1513 | 4380 | my ($self, $root) = @_; | |||
524 | ||||||
525 | 1513 | 3727 | my $tokens = $root->{tokens}; | |||
526 | 1513 | 4070 | $root->{tokens} = []; | |||
527 | ||||||
528 | 1513 2439 | 2518 9253 | while(my $token = shift @{$tokens}){ | |||
529 | ||||||
530 | 926 | 2915 | if ($token->match('Step')){ | |||
531 | ||||||
532 | 25 | 81 | my $path = XML::Parser::Lite::Tree::XPath::Token->new(); | |||
533 | 25 | 78 | $path->{type} = 'LocationPath'; | |||
534 | 25 | 68 | $path->{absolute} = 0; | |||
535 | 25 | 81 | $path->{tokens} = [$token]; | |||
536 | ||||||
537 | 25 | 88 | return 0 unless $self->slurp_path($path, $tokens); | |||
538 | ||||||
539 | 25 25 | 42 101 | push @{$root->{tokens}}, $path; | |||
540 | ||||||
541 | }elsif ($token->match('Operator', '/')){ | |||||
542 | ||||||
543 | 131 131 | 240 340 | unshift @{$tokens}, $token; | |||
544 | ||||||
545 | 131 | 420 | my $path = XML::Parser::Lite::Tree::XPath::Token->new(); | |||
546 | 131 | 409 | $path->{type} = 'LocationPath'; | |||
547 | 131 | 363 | $path->{absolute} = 1; | |||
548 | 131 | 382 | $path->{tokens} = []; | |||
549 | ||||||
550 | 131 | 438 | return 0 unless $self->slurp_path($path, $tokens); | |||
551 | ||||||
552 | 131 131 | 256 514 | unless (scalar @{$path->{tokens}}){ | |||
553 | 0 | 0 | $self->{error} = "Slash found at end of path."; | |||
554 | 0 | 0 | return 0; | |||
555 | } | |||||
556 | ||||||
557 | 131 131 | 226 601 | push @{$root->{tokens}}, $path; | |||
558 | ||||||
559 | }else{ | |||||
560 | ||||||
561 | 770 770 | 1262 2968 | push @{$root->{tokens}}, $token; | |||
562 | } | |||||
563 | } | |||||
564 | ||||||
565 | 1513 | 4971 | return 1; | |||
566 | } | |||||
567 | ||||||
568 | sub slurp_path { | |||||
569 | 156 | 470 | my ($self, $path, $tokens) = @_; | |||
570 | ||||||
571 | 156 | 256 | while(1){ | |||
572 | ||||||
573 | 427 427 | 663 1078 | my $t1 = shift @{$tokens}; | |||
574 | ||||||
575 | 427 | 1222 | if (defined $t1){ | |||
576 | 288 | 925 | if ($t1->match('Operator', '/')){ | |||
577 | ||||||
578 | 271 271 | 399 749 | my $t2 = shift @{$tokens}; | |||
579 | ||||||
580 | 271 | 797 | if (defined $t2){ | |||
581 | 271 | 859 | if ($t2->match('Step')){ | |||
582 | ||||||
583 | 271 271 | 453 1245 | push @{$path->{tokens}}, $t2; | |||
584 | }else{ | |||||
585 | 0 | 0 | $self->{error} = "Non Step token ($t2->{type}) found after slash."; | |||
586 | 0 | 0 | return 0; | |||
587 | } | |||||
588 | }else{ | |||||
589 | 0 | 0 | $self->{error} = "Slash found at end of path."; | |||
590 | 0 | 0 | return 0; | |||
591 | } | |||||
592 | }else{ | |||||
593 | 17 17 | 35 43 | unshift @{$tokens}, $t1; | |||
594 | 17 | 61 | return 1; | |||
595 | } | |||||
596 | }else{ | |||||
597 | 139 | 519 | return 1; | |||
598 | } | |||||
599 | } | |||||
600 | } | |||||
601 | ||||||
602 | sub do_binops { | |||||
603 | 9786 | 25388 | my ($self, $root) = @_; | |||
604 | ||||||
605 | 9786 | 24466 | my $tokens = $root->{tokens}; | |||
606 | 9786 | 26191 | $root->{tokens} = []; | |||
607 | ||||||
608 | 9786 18264 | 16950 68826 | while(my $token = shift @{$tokens}){ | |||
609 | ||||||
610 | ||||||
611 | 8478 8478 | 14547 27654 | for my $op(@{$self->{binops}}){ | |||
612 | ||||||
613 | 16895 | 52587 | if ($token->match('Operator', $op)){ | |||
614 | ||||||
615 | 76 76 | 118 313 | if (!scalar(@{$root->{tokens}})){ | |||
616 | 0 | 0 | $self->{error} = "Found a binop $token->{content} with no preceeding token"; | |||
617 | 0 | 0 | return 0; | |||
618 | } | |||||
619 | ||||||
620 | 76 76 | 119 244 | if (!scalar(@{$tokens})){ | |||
621 | 0 | 0 | $self->{error} = "Found a binop $token->{content} with no following token"; | |||
622 | 0 | 0 | return 0; | |||
623 | } | |||||
624 | ||||||
625 | 76 76 | 121 252 | my $prev = pop @{$root->{tokens}}; | |||
626 | 76 76 | 116 198 | my $next = shift @{$tokens}; | |||
627 | ||||||
628 | 76 76 | 124 239 | push @{$token->{tokens}}, $prev; | |||
629 | 76 76 | 137 226 | push @{$token->{tokens}}, $next; | |||
630 | 76 | 244 | $token->{type} = $self->{binop_production}; | |||
631 | ||||||
632 | 76 | 146 | last; | |||
633 | } | |||||
634 | } | |||||
635 | ||||||
636 | 8478 8478 | 15232 30265 | push @{$root->{tokens}}, $token; | |||
637 | } | |||||
638 | ||||||
639 | 9786 | 31888 | return 1; | |||
640 | } | |||||
641 | ||||||
642 | sub add_links { | |||||
643 | 1398 | 3628 | my ($self, $root) = @_; | |||
644 | ||||||
645 | 1398 | 2537 | my $prev = undef; | |||
646 | ||||||
647 | 1398 1398 | 2263 4900 | for my $token(@{$root->{tokens}}){ | |||
648 | ||||||
649 | 1230 | 3167 | $token->{prev} = $prev; | |||
650 | 1230 | 3737 | $prev->{next} = $token if defined $prev; | |||
651 | ||||||
652 | 1230 | 3732 | $prev = $token; | |||
653 | } | |||||
654 | } | |||||
655 | ||||||
656 | sub del_links { | |||||
657 | 179 | 513 | my ($self, $root) = @_; | |||
658 | ||||||
659 | 179 179 | 315 663 | for my $token(@{$root->{tokens}}){ | |||
660 | ||||||
661 | 683 | 1664 | delete $token->{parent}; | |||
662 | 683 | 1383 | delete $token->{prev}; | |||
663 | 683 | 2519 | delete $token->{next}; | |||
664 | } | |||||
665 | } | |||||
666 | ||||||
667 | sub unary_minus { | |||||
668 | 1398 | 3573 | my ($self, $root) = @_; | |||
669 | ||||||
670 | 1398 | 3802 | $self->add_links($root); | |||
671 | ||||||
672 | 1398 | 3644 | my $tokens = $root->{tokens}; | |||
673 | 1398 | 3697 | $root->{tokens} = []; | |||
674 | ||||||
675 | 1398 2620 | 2360 9804 | while(my $token = shift @{$tokens}){ | |||
676 | ||||||
677 | 1222 | 3983 | if ($token->match('Operator', '-')){ | |||
678 | ||||||
679 | 13 | 136 | if (defined($token->{next}) && defined($token->{prev}) && $token->{prev}->is_expression){ | |||
680 | ||||||
681 | # not unary | |||||
682 | }else{ | |||||
683 | # unary minus | |||||
684 | ||||||
685 | 8 | 23 | $token->{type} = 'UnaryExpr'; | |||
686 | 8 8 8 | 13 23 24 | push @{$token->{tokens}}, shift @{$tokens}; | |||
687 | } | |||||
688 | } | |||||
689 | ||||||
690 | 1222 1222 | 2148 4375 | push @{$root->{tokens}}, $token; | |||
691 | } | |||||
692 | ||||||
693 | 1398 | 4538 | return 1; | |||
694 | } | |||||
695 | ||||||
696 | 1; |