Mercurial > genshi > genshi-test
annotate genshi/path.py @ 902:09cc3627654c experimental-inline
Sync `experimental/inline` branch with [source:trunk@1126].
author | cmlenz |
---|---|
date | Fri, 23 Apr 2010 21:08:26 +0000 |
parents | de82830f8816 |
children |
rev | line source |
---|---|
1 | 1 # -*- coding: utf-8 -*- |
2 # | |
902
09cc3627654c
Sync `experimental/inline` branch with [source:trunk@1126].
cmlenz
parents:
830
diff
changeset
|
3 # Copyright (C) 2006-2009 Edgewall Software |
1 | 4 # All rights reserved. |
5 # | |
6 # This software is licensed as described in the file COPYING, which | |
7 # you should have received as part of this distribution. The terms | |
230 | 8 # are also available at http://genshi.edgewall.org/wiki/License. |
1 | 9 # |
10 # This software consists of voluntary contributions made by many | |
11 # individuals. For the exact contribution history, see the revision | |
230 | 12 # history and logs, available at http://genshi.edgewall.org/log/. |
1 | 13 |
111
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
14 """Basic support for evaluating XPath expressions against streams. |
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
15 |
230 | 16 >>> from genshi.input import XML |
111
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
17 >>> doc = XML('''<doc> |
820
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
18 ... <items count="4"> |
111
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
19 ... <item status="new"> |
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
20 ... <summary>Foo</summary> |
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
21 ... </item> |
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
22 ... <item status="closed"> |
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
23 ... <summary>Bar</summary> |
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
24 ... </item> |
820
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
25 ... <item status="closed" resolution="invalid"> |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
26 ... <summary>Baz</summary> |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
27 ... </item> |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
28 ... <item status="closed" resolution="fixed"> |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
29 ... <summary>Waz</summary> |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
30 ... </item> |
111
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
31 ... </items> |
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
32 ... </doc>''') |
902
09cc3627654c
Sync `experimental/inline` branch with [source:trunk@1126].
cmlenz
parents:
830
diff
changeset
|
33 >>> print(doc.select('items/item[@status="closed" and ' |
09cc3627654c
Sync `experimental/inline` branch with [source:trunk@1126].
cmlenz
parents:
830
diff
changeset
|
34 ... '(@resolution="invalid" or not(@resolution))]/summary/text()')) |
820
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
35 BarBaz |
111
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
36 |
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
37 Because the XPath engine operates on markup streams (as opposed to tree |
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
38 structures), it only implements a subset of the full XPath 1.0 language. |
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
39 """ |
1 | 40 |
820
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
41 from collections import deque |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
42 try: |
902
09cc3627654c
Sync `experimental/inline` branch with [source:trunk@1126].
cmlenz
parents:
830
diff
changeset
|
43 reduce # builtin in Python < 3 |
09cc3627654c
Sync `experimental/inline` branch with [source:trunk@1126].
cmlenz
parents:
830
diff
changeset
|
44 except NameError: |
820
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
45 from functools import reduce |
155
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
46 from math import ceil, floor |
820
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
47 import operator |
1 | 48 import re |
820
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
49 from itertools import chain |
1 | 50 |
230 | 51 from genshi.core import Stream, Attrs, Namespace, QName |
820
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
52 from genshi.core import START, END, TEXT, START_NS, END_NS, COMMENT, PI, \ |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
53 START_CDATA, END_CDATA |
1 | 54 |
106
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
55 __all__ = ['Path', 'PathSyntaxError'] |
500 | 56 __docformat__ = 'restructuredtext en' |
1 | 57 |
58 | |
114 | 59 class Axis(object): |
60 """Defines constants for the various supported XPath axes.""" | |
61 | |
62 ATTRIBUTE = 'attribute' | |
63 CHILD = 'child' | |
64 DESCENDANT = 'descendant' | |
65 DESCENDANT_OR_SELF = 'descendant-or-self' | |
66 SELF = 'self' | |
67 | |
830 | 68 @classmethod |
114 | 69 def forname(cls, name): |
70 """Return the axis constant for the given name, or `None` if no such | |
71 axis was defined. | |
72 """ | |
73 return getattr(cls, name.upper().replace('-', '_'), None) | |
74 | |
75 | |
76 ATTRIBUTE = Axis.ATTRIBUTE | |
77 CHILD = Axis.CHILD | |
78 DESCENDANT = Axis.DESCENDANT | |
79 DESCENDANT_OR_SELF = Axis.DESCENDANT_OR_SELF | |
80 SELF = Axis.SELF | |
81 | |
82 | |
820
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
83 class GenericStrategy(object): |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
84 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
85 @classmethod |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
86 def supports(cls, path): |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
87 return True |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
88 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
89 def __init__(self, path): |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
90 self.path = path |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
91 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
92 def test(self, ignore_context): |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
93 p = self.path |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
94 if ignore_context: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
95 if p[0][0] is ATTRIBUTE: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
96 steps = [_DOTSLASHSLASH] + p |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
97 else: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
98 steps = [(DESCENDANT_OR_SELF, p[0][1], p[0][2])] + p[1:] |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
99 elif p[0][0] is CHILD or p[0][0] is ATTRIBUTE \ |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
100 or p[0][0] is DESCENDANT: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
101 steps = [_DOTSLASH] + p |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
102 else: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
103 steps = p |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
104 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
105 # for node it contains all positions of xpath expression |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
106 # where its child should start checking for matches |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
107 # with list of corresponding context counters |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
108 # there can be many of them, because position that is from |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
109 # descendant-like axis can be achieved from different nodes |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
110 # for example <a><a><b/></a></a> should match both //a//b[1] |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
111 # and //a//b[2] |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
112 # positions always form increasing sequence (invariant) |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
113 stack = [[(0, [[]])]] |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
114 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
115 def _test(event, namespaces, variables, updateonly=False): |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
116 kind, data, pos = event[:3] |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
117 retval = None |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
118 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
119 # Manage the stack that tells us "where we are" in the stream |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
120 if kind is END: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
121 if stack: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
122 stack.pop() |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
123 return None |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
124 if kind is START_NS or kind is END_NS \ |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
125 or kind is START_CDATA or kind is END_CDATA: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
126 # should we make namespaces work? |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
127 return None |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
128 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
129 pos_queue = deque([(pos, cou, []) for pos, cou in stack[-1]]) |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
130 next_pos = [] |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
131 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
132 # length of real part of path - we omit attribute axis |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
133 real_len = len(steps) - ((steps[-1][0] == ATTRIBUTE) or 1 and 0) |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
134 last_checked = -1 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
135 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
136 # places where we have to check for match, are these |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
137 # provided by parent |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
138 while pos_queue: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
139 x, pcou, mcou = pos_queue.popleft() |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
140 axis, nodetest, predicates = steps[x] |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
141 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
142 # we need to push descendant-like positions from parent |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
143 # further |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
144 if (axis is DESCENDANT or axis is DESCENDANT_OR_SELF) and pcou: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
145 if next_pos and next_pos[-1][0] == x: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
146 next_pos[-1][1].extend(pcou) |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
147 else: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
148 next_pos.append((x, pcou)) |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
149 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
150 # nodetest first |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
151 if not nodetest(kind, data, pos, namespaces, variables): |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
152 continue |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
153 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
154 # counters packs that were already bad |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
155 missed = set() |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
156 counters_len = len(pcou) + len(mcou) |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
157 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
158 # number of counters - we have to create one |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
159 # for every context position based predicate |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
160 cnum = 0 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
161 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
162 # tells if we have match with position x |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
163 matched = True |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
164 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
165 if predicates: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
166 for predicate in predicates: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
167 pretval = predicate(kind, data, pos, |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
168 namespaces, |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
169 variables) |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
170 if type(pretval) is float: # FIXME <- need to check |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
171 # this for other types that |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
172 # can be coerced to float |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
173 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
174 # each counter pack needs to be checked |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
175 for i, cou in enumerate(chain(pcou, mcou)): |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
176 # it was bad before |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
177 if i in missed: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
178 continue |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
179 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
180 if len(cou) < cnum + 1: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
181 cou.append(0) |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
182 cou[cnum] += 1 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
183 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
184 # it is bad now |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
185 if cou[cnum] != int(pretval): |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
186 missed.add(i) |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
187 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
188 # none of counters pack was good |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
189 if len(missed) == counters_len: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
190 pretval = False |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
191 cnum += 1 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
192 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
193 if not pretval: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
194 matched = False |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
195 break |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
196 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
197 if not matched: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
198 continue |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
199 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
200 # counter for next position with current node as context node |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
201 child_counter = [] |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
202 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
203 if x + 1 == real_len: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
204 # we reached end of expression, because x + 1 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
205 # is equal to the length of expression |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
206 matched = True |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
207 axis, nodetest, predicates = steps[-1] |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
208 if axis is ATTRIBUTE: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
209 matched = nodetest(kind, data, pos, namespaces, |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
210 variables) |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
211 if matched: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
212 retval = matched |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
213 else: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
214 next_axis = steps[x + 1][0] |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
215 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
216 # if next axis allows matching self we have |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
217 # to add next position to our queue |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
218 if next_axis is DESCENDANT_OR_SELF or next_axis is SELF: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
219 if not pos_queue or pos_queue[0][0] > x + 1: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
220 pos_queue.appendleft((x + 1, [], [child_counter])) |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
221 else: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
222 pos_queue[0][2].append(child_counter) |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
223 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
224 # if axis is not self we have to add it to child's list |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
225 if next_axis is not SELF: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
226 next_pos.append((x + 1, [child_counter])) |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
227 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
228 if kind is START: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
229 stack.append(next_pos) |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
230 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
231 return retval |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
232 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
233 return _test |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
234 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
235 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
236 class SimplePathStrategy(object): |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
237 """Strategy for path with only local names, attributes and text nodes.""" |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
238 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
239 @classmethod |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
240 def supports(cls, path): |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
241 if path[0][0] is ATTRIBUTE: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
242 return False |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
243 allowed_tests = (LocalNameTest, CommentNodeTest, TextNodeTest) |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
244 for _, nodetest, predicates in path: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
245 if predicates: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
246 return False |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
247 if not isinstance(nodetest, allowed_tests): |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
248 return False |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
249 return True |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
250 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
251 def __init__(self, path): |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
252 # fragments is list of tuples (fragment, pi, attr, self_beginning) |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
253 # fragment is list of nodetests for fragment of path with only |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
254 # child:: axes between |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
255 # pi is KMP partial match table for this fragment |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
256 # attr is attribute nodetest if fragment ends with @ and None otherwise |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
257 # self_beginning is True if axis for first fragment element |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
258 # was self (first fragment) or descendant-or-self (farther fragment) |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
259 self.fragments = [] |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
260 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
261 self_beginning = False |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
262 fragment = [] |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
263 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
264 def nodes_equal(node1, node2): |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
265 """Tests if two node tests are equal""" |
902
09cc3627654c
Sync `experimental/inline` branch with [source:trunk@1126].
cmlenz
parents:
830
diff
changeset
|
266 if type(node1) is not type(node2): |
820
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
267 return False |
902
09cc3627654c
Sync `experimental/inline` branch with [source:trunk@1126].
cmlenz
parents:
830
diff
changeset
|
268 if type(node1) == LocalNameTest: |
820
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
269 return node1.name == node2.name |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
270 return True |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
271 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
272 def calculate_pi(f): |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
273 """KMP prefix calculation for table""" |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
274 # the indexes in prefix table are shifted by one |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
275 # in comparision with common implementations |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
276 # pi[i] = NORMAL_PI[i + 1] |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
277 if len(f) == 0: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
278 return [] |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
279 pi = [0] |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
280 s = 0 |
902
09cc3627654c
Sync `experimental/inline` branch with [source:trunk@1126].
cmlenz
parents:
830
diff
changeset
|
281 for i in range(1, len(f)): |
820
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
282 while s > 0 and not nodes_equal(f[s], f[i]): |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
283 s = pi[s-1] |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
284 if nodes_equal(f[s], f[i]): |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
285 s += 1 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
286 pi.append(s) |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
287 return pi |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
288 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
289 for axis in path: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
290 if axis[0] is SELF: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
291 if len(fragment) != 0: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
292 # if element is not first in fragment it has to be |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
293 # the same as previous one |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
294 # for example child::a/self::b is always wrong |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
295 if axis[1] != fragment[-1][1]: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
296 self.fragments = None |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
297 return |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
298 else: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
299 self_beginning = True |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
300 fragment.append(axis[1]) |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
301 elif axis[0] is CHILD: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
302 fragment.append(axis[1]) |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
303 elif axis[0] is ATTRIBUTE: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
304 pi = calculate_pi(fragment) |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
305 self.fragments.append((fragment, pi, axis[1], self_beginning)) |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
306 # attribute has always to be at the end, so we can jump out |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
307 return |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
308 else: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
309 pi = calculate_pi(fragment) |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
310 self.fragments.append((fragment, pi, None, self_beginning)) |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
311 fragment = [axis[1]] |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
312 if axis[0] is DESCENDANT: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
313 self_beginning = False |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
314 else: # DESCENDANT_OR_SELF |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
315 self_beginning = True |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
316 pi = calculate_pi(fragment) |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
317 self.fragments.append((fragment, pi, None, self_beginning)) |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
318 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
319 def test(self, ignore_context): |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
320 # stack of triples (fid, p, ic) |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
321 # fid is index of current fragment |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
322 # p is position in this fragment |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
323 # ic is if we ignore context in this fragment |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
324 stack = [] |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
325 stack_push = stack.append |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
326 stack_pop = stack.pop |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
327 frags = self.fragments |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
328 frags_len = len(frags) |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
329 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
330 def _test(event, namespaces, variables, updateonly=False): |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
331 # expression found impossible during init |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
332 if frags is None: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
333 return None |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
334 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
335 kind, data, pos = event[:3] |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
336 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
337 # skip events we don't care about |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
338 if kind is END: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
339 if stack: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
340 stack_pop() |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
341 return None |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
342 if kind is START_NS or kind is END_NS \ |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
343 or kind is START_CDATA or kind is END_CDATA: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
344 return None |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
345 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
346 if not stack: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
347 # root node, nothing on stack, special case |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
348 fid = 0 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
349 # skip empty fragments (there can be actually only one) |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
350 while not frags[fid][0]: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
351 fid += 1 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
352 p = 0 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
353 # empty fragment means descendant node at beginning |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
354 ic = ignore_context or (fid > 0) |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
355 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
356 # expression can match first node, if first axis is self::, |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
357 # descendant-or-self:: or if ignore_context is True and |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
358 # axis is not descendant:: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
359 if not frags[fid][3] and (not ignore_context or fid > 0): |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
360 # axis is not self-beggining, we have to skip this node |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
361 stack_push((fid, p, ic)) |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
362 return None |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
363 else: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
364 # take position of parent |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
365 fid, p, ic = stack[-1] |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
366 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
367 if fid is not None and not ic: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
368 # fragment not ignoring context - we can't jump back |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
369 frag, pi, attrib, _ = frags[fid] |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
370 frag_len = len(frag) |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
371 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
372 if p == frag_len: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
373 # that probably means empty first fragment |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
374 pass |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
375 elif frag[p](kind, data, pos, namespaces, variables): |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
376 # match, so we can go further |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
377 p += 1 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
378 else: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
379 # not matched, so there will be no match in subtree |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
380 fid, p = None, None |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
381 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
382 if p == frag_len and fid + 1 != frags_len: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
383 # we made it to end of fragment, we can go to following |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
384 fid += 1 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
385 p = 0 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
386 ic = True |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
387 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
388 if fid is None: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
389 # there was no match in fragment not ignoring context |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
390 if kind is START: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
391 stack_push((fid, p, ic)) |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
392 return None |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
393 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
394 if ic: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
395 # we are in fragment ignoring context |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
396 while True: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
397 frag, pi, attrib, _ = frags[fid] |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
398 frag_len = len(frag) |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
399 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
400 # KMP new "character" |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
401 while p > 0 and (p >= frag_len or not \ |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
402 frag[p](kind, data, pos, namespaces, variables)): |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
403 p = pi[p-1] |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
404 if frag[p](kind, data, pos, namespaces, variables): |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
405 p += 1 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
406 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
407 if p == frag_len: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
408 # end of fragment reached |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
409 if fid + 1 == frags_len: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
410 # that was last fragment |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
411 break |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
412 else: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
413 fid += 1 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
414 p = 0 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
415 ic = True |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
416 if not frags[fid][3]: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
417 # next fragment not self-beginning |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
418 break |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
419 else: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
420 break |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
421 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
422 if kind is START: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
423 # we have to put new position on stack, for children |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
424 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
425 if not ic and fid + 1 == frags_len and p == frag_len: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
426 # it is end of the only, not context ignoring fragment |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
427 # so there will be no matches in subtree |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
428 stack_push((None, None, ic)) |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
429 else: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
430 stack_push((fid, p, ic)) |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
431 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
432 # have we reached the end of the last fragment? |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
433 if fid + 1 == frags_len and p == frag_len: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
434 if attrib: # attribute ended path, return value |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
435 return attrib(kind, data, pos, namespaces, variables) |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
436 return True |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
437 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
438 return None |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
439 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
440 return _test |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
441 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
442 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
443 class SingleStepStrategy(object): |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
444 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
445 @classmethod |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
446 def supports(cls, path): |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
447 return len(path) == 1 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
448 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
449 def __init__(self, path): |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
450 self.path = path |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
451 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
452 def test(self, ignore_context): |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
453 steps = self.path |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
454 if steps[0][0] is ATTRIBUTE: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
455 steps = [_DOTSLASH] + steps |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
456 select_attr = steps[-1][0] is ATTRIBUTE and steps[-1][1] or None |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
457 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
458 # for every position in expression stores counters' list |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
459 # it is used for position based predicates |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
460 counters = [] |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
461 depth = [0] |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
462 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
463 def _test(event, namespaces, variables, updateonly=False): |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
464 kind, data, pos = event[:3] |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
465 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
466 # Manage the stack that tells us "where we are" in the stream |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
467 if kind is END: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
468 if not ignore_context: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
469 depth[0] -= 1 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
470 return None |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
471 elif kind is START_NS or kind is END_NS \ |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
472 or kind is START_CDATA or kind is END_CDATA: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
473 # should we make namespaces work? |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
474 return None |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
475 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
476 if not ignore_context: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
477 outside = (steps[0][0] is SELF and depth[0] != 0) \ |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
478 or (steps[0][0] is CHILD and depth[0] != 1) \ |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
479 or (steps[0][0] is DESCENDANT and depth[0] < 1) |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
480 if kind is START: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
481 depth[0] += 1 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
482 if outside: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
483 return None |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
484 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
485 axis, nodetest, predicates = steps[0] |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
486 if not nodetest(kind, data, pos, namespaces, variables): |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
487 return None |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
488 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
489 if predicates: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
490 cnum = 0 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
491 for predicate in predicates: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
492 pretval = predicate(kind, data, pos, namespaces, variables) |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
493 if type(pretval) is float: # FIXME <- need to check this |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
494 # for other types that can be |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
495 # coerced to float |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
496 if len(counters) < cnum + 1: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
497 counters.append(0) |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
498 counters[cnum] += 1 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
499 if counters[cnum] != int(pretval): |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
500 pretval = False |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
501 cnum += 1 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
502 if not pretval: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
503 return None |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
504 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
505 if select_attr: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
506 return select_attr(kind, data, pos, namespaces, variables) |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
507 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
508 return True |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
509 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
510 return _test |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
511 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
512 |
1 | 513 class Path(object): |
26
039fc5b87405
* Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents:
25
diff
changeset
|
514 """Implements basic XPath support on streams. |
1 | 515 |
820
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
516 Instances of this class represent a "compiled" XPath expression, and |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
517 provide methods for testing the path against a stream, as well as |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
518 extracting a substream matching that path. |
1 | 519 """ |
520 | |
820
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
521 STRATEGIES = (SingleStepStrategy, SimplePathStrategy, GenericStrategy) |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
522 |
139
54131cbb91a5
Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents:
137
diff
changeset
|
523 def __init__(self, text, filename=None, lineno=-1): |
26
039fc5b87405
* Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents:
25
diff
changeset
|
524 """Create the path object from a string. |
039fc5b87405
* Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents:
25
diff
changeset
|
525 |
500 | 526 :param text: the path expression |
527 :param filename: the name of the file in which the path expression was | |
528 found (used in error messages) | |
529 :param lineno: the line on which the expression was found | |
26
039fc5b87405
* Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents:
25
diff
changeset
|
530 """ |
1 | 531 self.source = text |
139
54131cbb91a5
Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents:
137
diff
changeset
|
532 self.paths = PathParser(text, filename, lineno).parse() |
820
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
533 self.strategies = [] |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
534 for path in self.paths: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
535 for strategy_class in self.STRATEGIES: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
536 if strategy_class.supports(path): |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
537 self.strategies.append(strategy_class(path)) |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
538 break |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
539 else: |
902
09cc3627654c
Sync `experimental/inline` branch with [source:trunk@1126].
cmlenz
parents:
830
diff
changeset
|
540 raise NotImplemented('No strategy found for path') |
1 | 541 |
542 def __repr__(self): | |
137 | 543 paths = [] |
544 for path in self.paths: | |
545 steps = [] | |
546 for axis, nodetest, predicates in path: | |
547 steps.append('%s::%s' % (axis, nodetest)) | |
548 for predicate in predicates: | |
224
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
549 steps[-1] += '[%s]' % predicate |
137 | 550 paths.append('/'.join(steps)) |
902
09cc3627654c
Sync `experimental/inline` branch with [source:trunk@1126].
cmlenz
parents:
830
diff
changeset
|
551 return '<%s "%s">' % (type(self).__name__, '|'.join(paths)) |
1 | 552 |
224
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
553 def select(self, stream, namespaces=None, variables=None): |
26
039fc5b87405
* Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents:
25
diff
changeset
|
554 """Returns a substream of the given stream that matches the path. |
039fc5b87405
* Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents:
25
diff
changeset
|
555 |
039fc5b87405
* Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents:
25
diff
changeset
|
556 If there are no matches, this method returns an empty stream. |
039fc5b87405
* Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents:
25
diff
changeset
|
557 |
230 | 558 >>> from genshi.input import XML |
33 | 559 >>> xml = XML('<root><elem><child>Text</child></elem></root>') |
61 | 560 |
902
09cc3627654c
Sync `experimental/inline` branch with [source:trunk@1126].
cmlenz
parents:
830
diff
changeset
|
561 >>> print(Path('.//child').select(xml)) |
33 | 562 <child>Text</child> |
563 | |
902
09cc3627654c
Sync `experimental/inline` branch with [source:trunk@1126].
cmlenz
parents:
830
diff
changeset
|
564 >>> print(Path('.//child/text()').select(xml)) |
33 | 565 Text |
566 | |
500 | 567 :param stream: the stream to select from |
568 :param namespaces: (optional) a mapping of namespace prefixes to URIs | |
569 :param variables: (optional) a mapping of variable names to values | |
570 :return: the substream matching the path, or an empty stream | |
571 :rtype: `Stream` | |
26
039fc5b87405
* Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents:
25
diff
changeset
|
572 """ |
224
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
573 if namespaces is None: |
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
574 namespaces = {} |
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
575 if variables is None: |
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
576 variables = {} |
1 | 577 stream = iter(stream) |
820
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
578 def _generate(stream=stream, ns=namespaces, vs=variables): |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
579 next = stream.next |
1 | 580 test = self.test() |
305 | 581 for event in stream: |
820
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
582 result = test(event, ns, vs) |
1 | 583 if result is True: |
305 | 584 yield event |
330
1dbeaef463e7
XPath tests should never return event tuples, just values or booleans.
cmlenz
parents:
326
diff
changeset
|
585 if event[0] is START: |
1dbeaef463e7
XPath tests should never return event tuples, just values or booleans.
cmlenz
parents:
326
diff
changeset
|
586 depth = 1 |
1dbeaef463e7
XPath tests should never return event tuples, just values or booleans.
cmlenz
parents:
326
diff
changeset
|
587 while depth > 0: |
820
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
588 subevent = next() |
330
1dbeaef463e7
XPath tests should never return event tuples, just values or booleans.
cmlenz
parents:
326
diff
changeset
|
589 if subevent[0] is START: |
1dbeaef463e7
XPath tests should never return event tuples, just values or booleans.
cmlenz
parents:
326
diff
changeset
|
590 depth += 1 |
1dbeaef463e7
XPath tests should never return event tuples, just values or booleans.
cmlenz
parents:
326
diff
changeset
|
591 elif subevent[0] is END: |
1dbeaef463e7
XPath tests should never return event tuples, just values or booleans.
cmlenz
parents:
326
diff
changeset
|
592 depth -= 1 |
1dbeaef463e7
XPath tests should never return event tuples, just values or booleans.
cmlenz
parents:
326
diff
changeset
|
593 yield subevent |
820
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
594 test(subevent, ns, vs, updateonly=True) |
1 | 595 elif result: |
596 yield result | |
820
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
597 return Stream(_generate(), |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
598 serializer=getattr(stream, 'serializer', None)) |
1 | 599 |
38
fec9f4897415
Fix for #2 (incorrect context node in path expressions). Still some paths that produce incorrect results, but the common case seems to work now.
cmlenz
parents:
37
diff
changeset
|
600 def test(self, ignore_context=False): |
26
039fc5b87405
* Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents:
25
diff
changeset
|
601 """Returns a function that can be used to track whether the path matches |
039fc5b87405
* Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents:
25
diff
changeset
|
602 a specific stream event. |
039fc5b87405
* Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents:
25
diff
changeset
|
603 |
500 | 604 The function returned expects the positional arguments ``event``, |
605 ``namespaces`` and ``variables``. The first is a stream event, while the | |
305 | 606 latter two are a mapping of namespace prefixes to URIs, and a mapping |
306 | 607 of variable names to values, respectively. In addition, the function |
500 | 608 accepts an ``updateonly`` keyword argument that default to ``False``. If |
609 it is set to ``True``, the function only updates its internal state, | |
306 | 610 but does not perform any tests or return a result. |
224
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
611 |
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
612 If the path matches the event, the function returns the match (for |
500 | 613 example, a `START` or `TEXT` event.) Otherwise, it returns ``None``. |
33 | 614 |
230 | 615 >>> from genshi.input import XML |
33 | 616 >>> xml = XML('<root><elem><child id="1"/></elem><child id="2"/></root>') |
617 >>> test = Path('child').test() | |
820
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
618 >>> namespaces, variables = {}, {} |
305 | 619 >>> for event in xml: |
820
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
620 ... if test(event, namespaces, variables): |
902
09cc3627654c
Sync `experimental/inline` branch with [source:trunk@1126].
cmlenz
parents:
830
diff
changeset
|
621 ... print('%s %r' % (event[0], event[1])) |
09cc3627654c
Sync `experimental/inline` branch with [source:trunk@1126].
cmlenz
parents:
830
diff
changeset
|
622 START (QName('child'), Attrs([(QName('id'), u'2')])) |
500 | 623 |
624 :param ignore_context: if `True`, the path is interpreted like a pattern | |
625 in XSLT, meaning for example that it will match | |
626 at any depth | |
627 :return: a function that can be used to test individual events in a | |
628 stream against the path | |
629 :rtype: ``function`` | |
26
039fc5b87405
* Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents:
25
diff
changeset
|
630 """ |
820
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
631 tests = [s.test(ignore_context) for s in self.strategies] |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
632 if len(tests) == 1: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
633 return tests[0] |
216
0a01371cecbc
Many fixes to XPath evaluation. Among other things, this should get rid of the bug that attributes were getting ?pulled up? by `py:match` directives using `py:attrs="select('@*')"` (see #50).
cmlenz
parents:
215
diff
changeset
|
634 |
820
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
635 def _multi(event, namespaces, variables, updateonly=False): |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
636 retval = None |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
637 for test in tests: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
638 val = test(event, namespaces, variables, updateonly=updateonly) |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
639 if retval is None: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
640 retval = val |
259
6f11ad260890
Fix bug in evaluating XPath expressions using the union operator `|`, which caused any path but the first to get out of sync with the event stream, and the whole thing returning too few results.
cmlenz
parents:
250
diff
changeset
|
641 return retval |
820
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
642 return _multi |
1 | 643 |
644 | |
106
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
645 class PathSyntaxError(Exception): |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
646 """Exception raised when an XPath expression is syntactically incorrect.""" |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
647 |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
648 def __init__(self, message, filename=None, lineno=-1, offset=-1): |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
649 if filename: |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
650 message = '%s (%s, line %d)' % (message, filename, lineno) |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
651 Exception.__init__(self, message) |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
652 self.filename = filename |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
653 self.lineno = lineno |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
654 self.offset = offset |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
655 |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
656 |
137 | 657 class PathParser(object): |
106
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
658 """Tokenizes and parses an XPath expression.""" |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
659 |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
660 _QUOTES = (("'", "'"), ('"', '"')) |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
661 _TOKENS = ('::', ':', '..', '.', '//', '/', '[', ']', '()', '(', ')', '@', |
179
a2e0a7986d19
Implemented support for XPath variables in predicates (#31).
cmlenz
parents:
169
diff
changeset
|
662 '=', '!=', '!', '|', ',', '>=', '>', '<=', '<', '$') |
163
9df6f057efd3
Support for XPath number literals including decimal places.
cmlenz
parents:
162
diff
changeset
|
663 _tokenize = re.compile('("[^"]*")|(\'[^\']*\')|((?:\d+)?\.\d+)|(%s)|([^%s\s]+)|\s+' % ( |
106
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
664 '|'.join([re.escape(t) for t in _TOKENS]), |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
665 ''.join([re.escape(t[0]) for t in _TOKENS]))).findall |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
666 |
139
54131cbb91a5
Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents:
137
diff
changeset
|
667 def __init__(self, text, filename=None, lineno=-1): |
54131cbb91a5
Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents:
137
diff
changeset
|
668 self.filename = filename |
54131cbb91a5
Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents:
137
diff
changeset
|
669 self.lineno = lineno |
902
09cc3627654c
Sync `experimental/inline` branch with [source:trunk@1126].
cmlenz
parents:
830
diff
changeset
|
670 self.tokens = [t for t in [dqstr or sqstr or number or token or name |
09cc3627654c
Sync `experimental/inline` branch with [source:trunk@1126].
cmlenz
parents:
830
diff
changeset
|
671 for dqstr, sqstr, number, token, name in |
09cc3627654c
Sync `experimental/inline` branch with [source:trunk@1126].
cmlenz
parents:
830
diff
changeset
|
672 self._tokenize(text)] if t] |
106
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
673 self.pos = 0 |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
674 |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
675 # Tokenizer |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
676 |
830 | 677 @property |
678 def at_end(self): | |
679 return self.pos == len(self.tokens) - 1 | |
680 | |
681 @property | |
682 def cur_token(self): | |
683 return self.tokens[self.pos] | |
106
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
684 |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
685 def next_token(self): |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
686 self.pos += 1 |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
687 return self.tokens[self.pos] |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
688 |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
689 def peek_token(self): |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
690 if not self.at_end: |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
691 return self.tokens[self.pos + 1] |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
692 return None |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
693 |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
694 # Recursive descent parser |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
695 |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
696 def parse(self): |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
697 """Parses the XPath expression and returns a list of location path |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
698 tests. |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
699 |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
700 For union expressions (such as `*|text()`), this function returns one |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
701 test for each operand in the union. For patch expressions that don't |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
702 use the union operator, the function always returns a list of size 1. |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
703 |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
704 Each path test in turn is a sequence of tests that correspond to the |
111
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
705 location steps, each tuples of the form `(axis, testfunc, predicates)` |
106
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
706 """ |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
707 paths = [self._location_path()] |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
708 while self.cur_token == '|': |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
709 self.next_token() |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
710 paths.append(self._location_path()) |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
711 if not self.at_end: |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
712 raise PathSyntaxError('Unexpected token %r after end of expression' |
139
54131cbb91a5
Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents:
137
diff
changeset
|
713 % self.cur_token, self.filename, self.lineno) |
106
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
714 return paths |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
715 |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
716 def _location_path(self): |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
717 steps = [] |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
718 while True: |
215 | 719 if self.cur_token.startswith('/'): |
820
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
720 if not steps: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
721 if self.cur_token == '//': |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
722 # hack to make //* match every node - also root |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
723 self.next_token() |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
724 axis, nodetest, predicates = self._location_step() |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
725 steps.append((DESCENDANT_OR_SELF, nodetest, |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
726 predicates)) |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
727 if self.at_end or not self.cur_token.startswith('/'): |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
728 break |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
729 continue |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
730 else: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
731 raise PathSyntaxError('Absolute location paths not ' |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
732 'supported', self.filename, |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
733 self.lineno) |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
734 elif self.cur_token == '//': |
215 | 735 steps.append((DESCENDANT_OR_SELF, NodeTest(), [])) |
111
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
736 self.next_token() |
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
737 |
137 | 738 axis, nodetest, predicates = self._location_step() |
739 if not axis: | |
145
56d534eb53f9
* Fix error in expression evaluation when the expression evaluates to an iterable that does not produce event tuples.
cmlenz
parents:
139
diff
changeset
|
740 axis = CHILD |
137 | 741 steps.append((axis, nodetest, predicates)) |
111
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
742 if self.at_end or not self.cur_token.startswith('/'): |
106
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
743 break |
111
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
744 |
106
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
745 return steps |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
746 |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
747 def _location_step(self): |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
748 if self.cur_token == '@': |
114 | 749 axis = ATTRIBUTE |
106
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
750 self.next_token() |
111
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
751 elif self.cur_token == '.': |
114 | 752 axis = SELF |
137 | 753 elif self.cur_token == '..': |
139
54131cbb91a5
Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents:
137
diff
changeset
|
754 raise PathSyntaxError('Unsupported axis "parent"', self.filename, |
54131cbb91a5
Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents:
137
diff
changeset
|
755 self.lineno) |
111
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
756 elif self.peek_token() == '::': |
114 | 757 axis = Axis.forname(self.cur_token) |
758 if axis is None: | |
139
54131cbb91a5
Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents:
137
diff
changeset
|
759 raise PathSyntaxError('Unsupport axis "%s"' % axis, |
54131cbb91a5
Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents:
137
diff
changeset
|
760 self.filename, self.lineno) |
111
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
761 self.next_token() |
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
762 self.next_token() |
106
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
763 else: |
137 | 764 axis = None |
765 nodetest = self._node_test(axis or CHILD) | |
111
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
766 predicates = [] |
106
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
767 while self.cur_token == '[': |
111
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
768 predicates.append(self._predicate()) |
137 | 769 return axis, nodetest, predicates |
106
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
770 |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
771 def _node_test(self, axis=None): |
224
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
772 test = prefix = None |
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
773 next_token = self.peek_token() |
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
774 if next_token in ('(', '()'): # Node type test |
106
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
775 test = self._node_type() |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
776 |
224
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
777 elif next_token == ':': # Namespace prefix |
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
778 prefix = self.cur_token |
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
779 self.next_token() |
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
780 localname = self.next_token() |
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
781 if localname == '*': |
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
782 test = QualifiedPrincipalTypeTest(axis, prefix) |
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
783 else: |
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
784 test = QualifiedNameTest(axis, prefix, localname) |
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
785 |
106
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
786 else: # Name test |
137 | 787 if self.cur_token == '*': |
788 test = PrincipalTypeTest(axis) | |
789 elif self.cur_token == '.': | |
790 test = NodeTest() | |
106
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
791 else: |
137 | 792 test = LocalNameTest(axis, self.cur_token) |
106
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
793 |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
794 if not self.at_end: |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
795 self.next_token() |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
796 return test |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
797 |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
798 def _node_type(self): |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
799 name = self.cur_token |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
800 self.next_token() |
137 | 801 |
802 args = [] | |
803 if self.cur_token != '()': | |
804 # The processing-instruction() function optionally accepts the | |
805 # name of the PI as argument, which must be a literal string | |
806 self.next_token() # ( | |
807 if self.cur_token != ')': | |
808 string = self.cur_token | |
809 if (string[0], string[-1]) in self._QUOTES: | |
810 string = string[1:-1] | |
811 args.append(string) | |
812 | |
813 cls = _nodetest_map.get(name) | |
814 if not cls: | |
139
54131cbb91a5
Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents:
137
diff
changeset
|
815 raise PathSyntaxError('%s() not allowed here' % name, self.filename, |
54131cbb91a5
Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents:
137
diff
changeset
|
816 self.lineno) |
137 | 817 return cls(*args) |
106
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
818 |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
819 def _predicate(self): |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
820 assert self.cur_token == '[' |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
821 self.next_token() |
111
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
822 expr = self._or_expr() |
121
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
823 if self.cur_token != ']': |
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
824 raise PathSyntaxError('Expected "]" to close predicate, ' |
139
54131cbb91a5
Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents:
137
diff
changeset
|
825 'but found "%s"' % self.cur_token, |
54131cbb91a5
Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents:
137
diff
changeset
|
826 self.filename, self.lineno) |
111
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
827 if not self.at_end: |
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
828 self.next_token() |
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
829 return expr |
106
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
830 |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
831 def _or_expr(self): |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
832 expr = self._and_expr() |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
833 while self.cur_token == 'or': |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
834 self.next_token() |
137 | 835 expr = OrOperator(expr, self._and_expr()) |
106
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
836 return expr |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
837 |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
838 def _and_expr(self): |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
839 expr = self._equality_expr() |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
840 while self.cur_token == 'and': |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
841 self.next_token() |
137 | 842 expr = AndOperator(expr, self._equality_expr()) |
106
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
843 return expr |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
844 |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
845 def _equality_expr(self): |
162
f767cf98e3e3
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
846 expr = self._relational_expr() |
f767cf98e3e3
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
847 while self.cur_token in ('=', '!='): |
f767cf98e3e3
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
848 op = _operator_map[self.cur_token] |
f767cf98e3e3
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
849 self.next_token() |
f767cf98e3e3
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
850 expr = op(expr, self._relational_expr()) |
f767cf98e3e3
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
851 return expr |
f767cf98e3e3
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
852 |
f767cf98e3e3
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
853 def _relational_expr(self): |
820
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
854 expr = self._sub_expr() |
162
f767cf98e3e3
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
855 while self.cur_token in ('>', '>=', '<', '>='): |
f767cf98e3e3
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
856 op = _operator_map[self.cur_token] |
106
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
857 self.next_token() |
820
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
858 expr = op(expr, self._sub_expr()) |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
859 return expr |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
860 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
861 def _sub_expr(self): |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
862 token = self.cur_token |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
863 if token != '(': |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
864 return self._primary_expr() |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
865 self.next_token() |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
866 expr = self._or_expr() |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
867 if self.cur_token != ')': |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
868 raise PathSyntaxError('Expected ")" to close sub-expression, ' |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
869 'but found "%s"' % self.cur_token, |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
870 self.filename, self.lineno) |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
871 self.next_token() |
106
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
872 return expr |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
873 |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
874 def _primary_expr(self): |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
875 token = self.cur_token |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
876 if len(token) > 1 and (token[0], token[-1]) in self._QUOTES: |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
877 self.next_token() |
137 | 878 return StringLiteral(token[1:-1]) |
163
9df6f057efd3
Support for XPath number literals including decimal places.
cmlenz
parents:
162
diff
changeset
|
879 elif token[0].isdigit() or token[0] == '.': |
106
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
880 self.next_token() |
820
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
881 return NumberLiteral(as_float(token)) |
179
a2e0a7986d19
Implemented support for XPath variables in predicates (#31).
cmlenz
parents:
169
diff
changeset
|
882 elif token == '$': |
a2e0a7986d19
Implemented support for XPath variables in predicates (#31).
cmlenz
parents:
169
diff
changeset
|
883 token = self.next_token() |
a2e0a7986d19
Implemented support for XPath variables in predicates (#31).
cmlenz
parents:
169
diff
changeset
|
884 self.next_token() |
a2e0a7986d19
Implemented support for XPath variables in predicates (#31).
cmlenz
parents:
169
diff
changeset
|
885 return VariableReference(token) |
121
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
886 elif not self.at_end and self.peek_token().startswith('('): |
155
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
887 return self._function_call() |
106
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
888 else: |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
889 axis = None |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
890 if token == '@': |
114 | 891 axis = ATTRIBUTE |
106
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
892 self.next_token() |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
893 return self._node_test(axis) |
137 | 894 |
155
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
895 def _function_call(self): |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
896 name = self.cur_token |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
897 if self.next_token() == '()': |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
898 args = [] |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
899 else: |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
900 assert self.cur_token == '(' |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
901 self.next_token() |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
902 args = [self._or_expr()] |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
903 while self.cur_token == ',': |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
904 self.next_token() |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
905 args.append(self._or_expr()) |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
906 if not self.cur_token == ')': |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
907 raise PathSyntaxError('Expected ")" to close function argument ' |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
908 'list, but found "%s"' % self.cur_token, |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
909 self.filename, self.lineno) |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
910 self.next_token() |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
911 cls = _function_map.get(name) |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
912 if not cls: |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
913 raise PathSyntaxError('Unsupported function "%s"' % name, |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
914 self.filename, self.lineno) |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
915 return cls(*args) |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
916 |
137 | 917 |
820
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
918 # Type coercion |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
919 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
920 def as_scalar(value): |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
921 """Convert value to a scalar. If a single element Attrs() object is passed |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
922 the value of the single attribute will be returned.""" |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
923 if isinstance(value, Attrs): |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
924 assert len(value) == 1 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
925 return value[0][1] |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
926 else: |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
927 return value |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
928 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
929 def as_float(value): |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
930 # FIXME - if value is a bool it will be coerced to 0.0 and consequently |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
931 # compared as a float. This is probably not ideal. |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
932 return float(as_scalar(value)) |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
933 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
934 def as_long(value): |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
935 return long(as_scalar(value)) |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
936 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
937 def as_string(value): |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
938 value = as_scalar(value) |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
939 if value is False: |
902
09cc3627654c
Sync `experimental/inline` branch with [source:trunk@1126].
cmlenz
parents:
830
diff
changeset
|
940 return '' |
820
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
941 return unicode(value) |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
942 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
943 def as_bool(value): |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
944 return bool(as_scalar(value)) |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
945 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
946 |
137 | 947 # Node tests |
948 | |
949 class PrincipalTypeTest(object): | |
161
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
950 """Node test that matches any event with the given principal type.""" |
137 | 951 __slots__ = ['principal_type'] |
952 def __init__(self, principal_type): | |
953 self.principal_type = principal_type | |
224
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
954 def __call__(self, kind, data, pos, namespaces, variables): |
137 | 955 if kind is START: |
956 if self.principal_type is ATTRIBUTE: | |
957 return data[1] or None | |
958 else: | |
959 return True | |
960 def __repr__(self): | |
961 return '*' | |
962 | |
224
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
963 class QualifiedPrincipalTypeTest(object): |
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
964 """Node test that matches any event with the given principal type in a |
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
965 specific namespace.""" |
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
966 __slots__ = ['principal_type', 'prefix'] |
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
967 def __init__(self, principal_type, prefix): |
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
968 self.principal_type = principal_type |
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
969 self.prefix = prefix |
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
970 def __call__(self, kind, data, pos, namespaces, variables): |
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
971 namespace = Namespace(namespaces.get(self.prefix)) |
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
972 if kind is START: |
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
973 if self.principal_type is ATTRIBUTE and data[1]: |
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
974 return Attrs([(name, value) for name, value in data[1] |
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
975 if name in namespace]) or None |
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
976 else: |
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
977 return data[0] in namespace |
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
978 def __repr__(self): |
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
979 return '%s:*' % self.prefix |
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
980 |
137 | 981 class LocalNameTest(object): |
395 | 982 """Node test that matches any event with the given principal type and |
161
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
983 local name. |
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
984 """ |
137 | 985 __slots__ = ['principal_type', 'name'] |
986 def __init__(self, principal_type, name): | |
987 self.principal_type = principal_type | |
988 self.name = name | |
224
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
989 def __call__(self, kind, data, pos, namespaces, variables): |
137 | 990 if kind is START: |
991 if self.principal_type is ATTRIBUTE and self.name in data[1]: | |
820
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
992 return Attrs([(self.name, data[1].get(self.name))]) |
137 | 993 else: |
994 return data[0].localname == self.name | |
995 def __repr__(self): | |
996 return self.name | |
997 | |
224
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
998 class QualifiedNameTest(object): |
395 | 999 """Node test that matches any event with the given principal type and |
224
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1000 qualified name. |
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1001 """ |
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1002 __slots__ = ['principal_type', 'prefix', 'name'] |
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1003 def __init__(self, principal_type, prefix, name): |
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1004 self.principal_type = principal_type |
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1005 self.prefix = prefix |
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1006 self.name = name |
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1007 def __call__(self, kind, data, pos, namespaces, variables): |
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1008 qname = QName('%s}%s' % (namespaces.get(self.prefix), self.name)) |
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1009 if kind is START: |
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1010 if self.principal_type is ATTRIBUTE and qname in data[1]: |
820
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
1011 return Attrs([(self.name, data[1].get(self.name))]) |
224
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1012 else: |
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1013 return data[0] == qname |
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1014 def __repr__(self): |
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1015 return '%s:%s' % (self.prefix, self.name) |
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1016 |
137 | 1017 class CommentNodeTest(object): |
161
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1018 """Node test that matches any comment events.""" |
137 | 1019 __slots__ = [] |
224
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1020 def __call__(self, kind, data, pos, namespaces, variables): |
330
1dbeaef463e7
XPath tests should never return event tuples, just values or booleans.
cmlenz
parents:
326
diff
changeset
|
1021 return kind is COMMENT |
137 | 1022 def __repr__(self): |
1023 return 'comment()' | |
1024 | |
1025 class NodeTest(object): | |
161
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1026 """Node test that matches any node.""" |
137 | 1027 __slots__ = [] |
224
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1028 def __call__(self, kind, data, pos, namespaces, variables): |
137 | 1029 if kind is START: |
1030 return True | |
1031 return kind, data, pos | |
1032 def __repr__(self): | |
1033 return 'node()' | |
1034 | |
1035 class ProcessingInstructionNodeTest(object): | |
161
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1036 """Node test that matches any processing instruction event.""" |
137 | 1037 __slots__ = ['target'] |
1038 def __init__(self, target=None): | |
1039 self.target = target | |
224
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1040 def __call__(self, kind, data, pos, namespaces, variables): |
330
1dbeaef463e7
XPath tests should never return event tuples, just values or booleans.
cmlenz
parents:
326
diff
changeset
|
1041 return kind is PI and (not self.target or data[0] == self.target) |
137 | 1042 def __repr__(self): |
1043 arg = '' | |
1044 if self.target: | |
1045 arg = '"' + self.target + '"' | |
1046 return 'processing-instruction(%s)' % arg | |
1047 | |
1048 class TextNodeTest(object): | |
161
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1049 """Node test that matches any text event.""" |
137 | 1050 __slots__ = [] |
224
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1051 def __call__(self, kind, data, pos, namespaces, variables): |
330
1dbeaef463e7
XPath tests should never return event tuples, just values or booleans.
cmlenz
parents:
326
diff
changeset
|
1052 return kind is TEXT |
137 | 1053 def __repr__(self): |
1054 return 'text()' | |
1055 | |
1056 _nodetest_map = {'comment': CommentNodeTest, 'node': NodeTest, | |
1057 'processing-instruction': ProcessingInstructionNodeTest, | |
1058 'text': TextNodeTest} | |
1059 | |
1060 # Functions | |
1061 | |
155
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1062 class Function(object): |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1063 """Base class for function nodes in XPath expressions.""" |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1064 |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1065 class BooleanFunction(Function): |
161
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1066 """The `boolean` function, which converts its argument to a boolean |
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1067 value. |
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1068 """ |
155
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1069 __slots__ = ['expr'] |
820
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
1070 _return_type = bool |
155
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1071 def __init__(self, expr): |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1072 self.expr = expr |
224
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1073 def __call__(self, kind, data, pos, namespaces, variables): |
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1074 val = self.expr(kind, data, pos, namespaces, variables) |
820
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
1075 return as_bool(val) |
155
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1076 def __repr__(self): |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1077 return 'boolean(%r)' % self.expr |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1078 |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1079 class CeilingFunction(Function): |
161
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1080 """The `ceiling` function, which returns the nearest lower integer number |
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1081 for the given number. |
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1082 """ |
155
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1083 __slots__ = ['number'] |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1084 def __init__(self, number): |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1085 self.number = number |
224
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1086 def __call__(self, kind, data, pos, namespaces, variables): |
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1087 number = self.number(kind, data, pos, namespaces, variables) |
820
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
1088 return ceil(as_float(number)) |
155
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1089 def __repr__(self): |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1090 return 'ceiling(%r)' % self.number |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1091 |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1092 class ConcatFunction(Function): |
161
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1093 """The `concat` function, which concatenates (joins) the variable number of |
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1094 strings it gets as arguments. |
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1095 """ |
155
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1096 __slots__ = ['exprs'] |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1097 def __init__(self, *exprs): |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1098 self.exprs = exprs |
224
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1099 def __call__(self, kind, data, pos, namespaces, variables): |
155
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1100 strings = [] |
224
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1101 for item in [expr(kind, data, pos, namespaces, variables) |
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1102 for expr in self.exprs]: |
820
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
1103 strings.append(as_string(item)) |
902
09cc3627654c
Sync `experimental/inline` branch with [source:trunk@1126].
cmlenz
parents:
830
diff
changeset
|
1104 return ''.join(strings) |
155
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1105 def __repr__(self): |
169 | 1106 return 'concat(%s)' % ', '.join([repr(expr) for expr in self.exprs]) |
155
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1107 |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1108 class ContainsFunction(Function): |
161
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1109 """The `contains` function, which returns whether a string contains a given |
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1110 substring. |
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1111 """ |
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1112 __slots__ = ['string1', 'string2'] |
155
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1113 def __init__(self, string1, string2): |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1114 self.string1 = string1 |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1115 self.string2 = string2 |
224
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1116 def __call__(self, kind, data, pos, namespaces, variables): |
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1117 string1 = self.string1(kind, data, pos, namespaces, variables) |
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1118 string2 = self.string2(kind, data, pos, namespaces, variables) |
820
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
1119 return as_string(string2) in as_string(string1) |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
1120 def __repr__(self): |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
1121 return 'contains(%r, %r)' % (self.string1, self.string2) |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
1122 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
1123 class MatchesFunction(Function): |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
1124 """The `matches` function, which returns whether a string matches a regular |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
1125 expression. |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
1126 """ |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
1127 __slots__ = ['string1', 'string2'] |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
1128 flag_mapping = {'s': re.S, 'm': re.M, 'i': re.I, 'x': re.X} |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
1129 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
1130 def __init__(self, string1, string2, flags=''): |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
1131 self.string1 = string1 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
1132 self.string2 = string2 |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
1133 self.flags = self._map_flags(flags) |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
1134 def __call__(self, kind, data, pos, namespaces, variables): |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
1135 string1 = as_string(self.string1(kind, data, pos, namespaces, variables)) |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
1136 string2 = as_string(self.string2(kind, data, pos, namespaces, variables)) |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
1137 return re.search(string2, string1, self.flags) |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
1138 def _map_flags(self, flags): |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
1139 return reduce(operator.or_, |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
1140 [self.flag_map[flag] for flag in flags], re.U) |
155
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1141 def __repr__(self): |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1142 return 'contains(%r, %r)' % (self.string1, self.string2) |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1143 |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1144 class FalseFunction(Function): |
161
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1145 """The `false` function, which always returns the boolean `false` value.""" |
155
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1146 __slots__ = [] |
224
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1147 def __call__(self, kind, data, pos, namespaces, variables): |
155
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1148 return False |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1149 def __repr__(self): |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1150 return 'false()' |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1151 |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1152 class FloorFunction(Function): |
161
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1153 """The `ceiling` function, which returns the nearest higher integer number |
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1154 for the given number. |
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1155 """ |
155
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1156 __slots__ = ['number'] |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1157 def __init__(self, number): |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1158 self.number = number |
224
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1159 def __call__(self, kind, data, pos, namespaces, variables): |
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1160 number = self.number(kind, data, pos, namespaces, variables) |
820
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
1161 return floor(as_float(number)) |
155
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1162 def __repr__(self): |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1163 return 'floor(%r)' % self.number |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1164 |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1165 class LocalNameFunction(Function): |
161
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1166 """The `local-name` function, which returns the local name of the current |
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1167 element. |
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1168 """ |
137 | 1169 __slots__ = [] |
224
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1170 def __call__(self, kind, data, pos, namespaces, variables): |
137 | 1171 if kind is START: |
234 | 1172 return data[0].localname |
137 | 1173 def __repr__(self): |
1174 return 'local-name()' | |
1175 | |
155
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1176 class NameFunction(Function): |
161
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1177 """The `name` function, which returns the qualified name of the current |
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1178 element. |
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1179 """ |
137 | 1180 __slots__ = [] |
224
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1181 def __call__(self, kind, data, pos, namespaces, variables): |
137 | 1182 if kind is START: |
234 | 1183 return data[0] |
137 | 1184 def __repr__(self): |
1185 return 'name()' | |
1186 | |
155
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1187 class NamespaceUriFunction(Function): |
161
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1188 """The `namespace-uri` function, which returns the namespace URI of the |
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1189 current element. |
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1190 """ |
137 | 1191 __slots__ = [] |
224
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1192 def __call__(self, kind, data, pos, namespaces, variables): |
137 | 1193 if kind is START: |
234 | 1194 return data[0].namespace |
137 | 1195 def __repr__(self): |
1196 return 'namespace-uri()' | |
1197 | |
155
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1198 class NotFunction(Function): |
161
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1199 """The `not` function, which returns the negated boolean value of its |
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1200 argument. |
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1201 """ |
137 | 1202 __slots__ = ['expr'] |
1203 def __init__(self, expr): | |
1204 self.expr = expr | |
224
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1205 def __call__(self, kind, data, pos, namespaces, variables): |
820
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
1206 return not as_bool(self.expr(kind, data, pos, namespaces, variables)) |
137 | 1207 def __repr__(self): |
1208 return 'not(%s)' % self.expr | |
1209 | |
155
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1210 class NormalizeSpaceFunction(Function): |
161
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1211 """The `normalize-space` function, which removes leading and trailing |
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1212 whitespace in the given string, and replaces multiple adjacent whitespace |
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1213 characters inside the string with a single space. |
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1214 """ |
155
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1215 __slots__ = ['expr'] |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1216 _normalize = re.compile(r'\s{2,}').sub |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1217 def __init__(self, expr): |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1218 self.expr = expr |
224
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1219 def __call__(self, kind, data, pos, namespaces, variables): |
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1220 string = self.expr(kind, data, pos, namespaces, variables) |
820
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
1221 return self._normalize(' ', as_string(string).strip()) |
155
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1222 def __repr__(self): |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1223 return 'normalize-space(%s)' % repr(self.expr) |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1224 |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1225 class NumberFunction(Function): |
161
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1226 """The `number` function that converts its argument to a number.""" |
155
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1227 __slots__ = ['expr'] |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1228 def __init__(self, expr): |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1229 self.expr = expr |
224
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1230 def __call__(self, kind, data, pos, namespaces, variables): |
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1231 val = self.expr(kind, data, pos, namespaces, variables) |
820
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
1232 return as_float(val) |
155
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1233 def __repr__(self): |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1234 return 'number(%r)' % self.expr |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1235 |
162
f767cf98e3e3
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1236 class RoundFunction(Function): |
f767cf98e3e3
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1237 """The `round` function, which returns the nearest integer number for the |
f767cf98e3e3
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1238 given number. |
f767cf98e3e3
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1239 """ |
f767cf98e3e3
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1240 __slots__ = ['number'] |
f767cf98e3e3
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1241 def __init__(self, number): |
f767cf98e3e3
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1242 self.number = number |
224
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1243 def __call__(self, kind, data, pos, namespaces, variables): |
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1244 number = self.number(kind, data, pos, namespaces, variables) |
820
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
1245 return round(as_float(number)) |
162
f767cf98e3e3
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1246 def __repr__(self): |
f767cf98e3e3
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1247 return 'round(%r)' % self.number |
f767cf98e3e3
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1248 |
155
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1249 class StartsWithFunction(Function): |
161
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1250 """The `starts-with` function that returns whether one string starts with |
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1251 a given substring. |
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1252 """ |
155
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1253 __slots__ = ['string1', 'string2'] |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1254 def __init__(self, string1, string2): |
282
db5bdeca9192
Fix `starts-with()` XPath function so that it actually compares the two strings. Closes #61.
cmlenz
parents:
259
diff
changeset
|
1255 self.string1 = string1 |
155
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1256 self.string2 = string2 |
224
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1257 def __call__(self, kind, data, pos, namespaces, variables): |
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1258 string1 = self.string1(kind, data, pos, namespaces, variables) |
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1259 string2 = self.string2(kind, data, pos, namespaces, variables) |
820
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
1260 return as_string(string1).startswith(as_string(string2)) |
155
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1261 def __repr__(self): |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1262 return 'starts-with(%r, %r)' % (self.string1, self.string2) |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1263 |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1264 class StringLengthFunction(Function): |
161
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1265 """The `string-length` function that returns the length of the given |
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1266 string. |
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1267 """ |
155
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1268 __slots__ = ['expr'] |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1269 def __init__(self, expr): |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1270 self.expr = expr |
224
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1271 def __call__(self, kind, data, pos, namespaces, variables): |
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1272 string = self.expr(kind, data, pos, namespaces, variables) |
820
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
1273 return len(as_string(string)) |
155
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1274 def __repr__(self): |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1275 return 'string-length(%r)' % self.expr |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1276 |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1277 class SubstringFunction(Function): |
161
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1278 """The `substring` function that returns the part of a string that starts |
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1279 at the given offset, and optionally limited to the given length. |
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1280 """ |
155
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1281 __slots__ = ['string', 'start', 'length'] |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1282 def __init__(self, string, start, length=None): |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1283 self.string = string |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1284 self.start = start |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1285 self.length = length |
224
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1286 def __call__(self, kind, data, pos, namespaces, variables): |
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1287 string = self.string(kind, data, pos, namespaces, variables) |
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1288 start = self.start(kind, data, pos, namespaces, variables) |
155
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1289 length = 0 |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1290 if self.length is not None: |
224
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1291 length = self.length(kind, data, pos, namespaces, variables) |
820
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
1292 return string[as_long(start):len(as_string(string)) - as_long(length)] |
155
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1293 def __repr__(self): |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1294 if self.length is not None: |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1295 return 'substring(%r, %r, %r)' % (self.string, self.start, |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1296 self.length) |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1297 else: |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1298 return 'substring(%r, %r)' % (self.string, self.start) |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1299 |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1300 class SubstringAfterFunction(Function): |
161
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1301 """The `substring-after` function that returns the part of a string that |
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1302 is found after the given substring. |
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1303 """ |
155
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1304 __slots__ = ['string1', 'string2'] |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1305 def __init__(self, string1, string2): |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1306 self.string1 = string1 |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1307 self.string2 = string2 |
224
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1308 def __call__(self, kind, data, pos, namespaces, variables): |
820
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
1309 string1 = as_string(self.string1(kind, data, pos, namespaces, variables)) |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
1310 string2 = as_string(self.string2(kind, data, pos, namespaces, variables)) |
155
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1311 index = string1.find(string2) |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1312 if index >= 0: |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1313 return string1[index + len(string2):] |
902
09cc3627654c
Sync `experimental/inline` branch with [source:trunk@1126].
cmlenz
parents:
830
diff
changeset
|
1314 return '' |
155
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1315 def __repr__(self): |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1316 return 'substring-after(%r, %r)' % (self.string1, self.string2) |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1317 |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1318 class SubstringBeforeFunction(Function): |
161
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1319 """The `substring-before` function that returns the part of a string that |
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1320 is found before the given substring. |
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1321 """ |
155
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1322 __slots__ = ['string1', 'string2'] |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1323 def __init__(self, string1, string2): |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1324 self.string1 = string1 |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1325 self.string2 = string2 |
224
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1326 def __call__(self, kind, data, pos, namespaces, variables): |
820
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
1327 string1 = as_string(self.string1(kind, data, pos, namespaces, variables)) |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
1328 string2 = as_string(self.string2(kind, data, pos, namespaces, variables)) |
155
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1329 index = string1.find(string2) |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1330 if index >= 0: |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1331 return string1[:index] |
902
09cc3627654c
Sync `experimental/inline` branch with [source:trunk@1126].
cmlenz
parents:
830
diff
changeset
|
1332 return '' |
155
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1333 def __repr__(self): |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1334 return 'substring-after(%r, %r)' % (self.string1, self.string2) |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1335 |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1336 class TranslateFunction(Function): |
161
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1337 """The `translate` function that translates a set of characters in a |
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1338 string to target set of characters. |
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1339 """ |
155
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1340 __slots__ = ['string', 'fromchars', 'tochars'] |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1341 def __init__(self, string, fromchars, tochars): |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1342 self.string = string |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1343 self.fromchars = fromchars |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1344 self.tochars = tochars |
224
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1345 def __call__(self, kind, data, pos, namespaces, variables): |
820
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
1346 string = as_string(self.string(kind, data, pos, namespaces, variables)) |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
1347 fromchars = as_string(self.fromchars(kind, data, pos, namespaces, variables)) |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
1348 tochars = as_string(self.tochars(kind, data, pos, namespaces, variables)) |
155
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1349 table = dict(zip([ord(c) for c in fromchars], |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1350 [ord(c) for c in tochars])) |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1351 return string.translate(table) |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1352 def __repr__(self): |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1353 return 'translate(%r, %r, %r)' % (self.string, self.fromchars, |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1354 self.tochars) |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1355 |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1356 class TrueFunction(Function): |
161
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1357 """The `true` function, which always returns the boolean `true` value.""" |
155
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1358 __slots__ = [] |
224
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1359 def __call__(self, kind, data, pos, namespaces, variables): |
155
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1360 return True |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1361 def __repr__(self): |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1362 return 'true()' |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1363 |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1364 _function_map = {'boolean': BooleanFunction, 'ceiling': CeilingFunction, |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1365 'concat': ConcatFunction, 'contains': ContainsFunction, |
820
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
1366 'matches': MatchesFunction, 'false': FalseFunction, 'floor': |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
1367 FloorFunction, 'local-name': LocalNameFunction, 'name': |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
1368 NameFunction, 'namespace-uri': NamespaceUriFunction, |
155
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1369 'normalize-space': NormalizeSpaceFunction, 'not': NotFunction, |
162
f767cf98e3e3
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1370 'number': NumberFunction, 'round': RoundFunction, |
820
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
1371 'starts-with': StartsWithFunction, 'string-length': |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
1372 StringLengthFunction, 'substring': SubstringFunction, |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
1373 'substring-after': SubstringAfterFunction, 'substring-before': |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
1374 SubstringBeforeFunction, 'translate': TranslateFunction, |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
1375 'true': TrueFunction} |
137 | 1376 |
179
a2e0a7986d19
Implemented support for XPath variables in predicates (#31).
cmlenz
parents:
169
diff
changeset
|
1377 # Literals & Variables |
137 | 1378 |
155
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1379 class Literal(object): |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1380 """Abstract base class for literal nodes.""" |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1381 |
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1382 class StringLiteral(Literal): |
161
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1383 """A string literal node.""" |
137 | 1384 __slots__ = ['text'] |
1385 def __init__(self, text): | |
1386 self.text = text | |
224
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1387 def __call__(self, kind, data, pos, namespaces, variables): |
228
f79b20a50919
Add support for position predicates in XPath expressions.
cmlenz
parents:
224
diff
changeset
|
1388 return self.text |
137 | 1389 def __repr__(self): |
1390 return '"%s"' % self.text | |
1391 | |
155
50d4b08017df
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1392 class NumberLiteral(Literal): |
161
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1393 """A number literal node.""" |
137 | 1394 __slots__ = ['number'] |
1395 def __init__(self, number): | |
1396 self.number = number | |
224
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1397 def __call__(self, kind, data, pos, namespaces, variables): |
228
f79b20a50919
Add support for position predicates in XPath expressions.
cmlenz
parents:
224
diff
changeset
|
1398 return self.number |
137 | 1399 def __repr__(self): |
1400 return str(self.number) | |
1401 | |
179
a2e0a7986d19
Implemented support for XPath variables in predicates (#31).
cmlenz
parents:
169
diff
changeset
|
1402 class VariableReference(Literal): |
a2e0a7986d19
Implemented support for XPath variables in predicates (#31).
cmlenz
parents:
169
diff
changeset
|
1403 """A variable reference node.""" |
a2e0a7986d19
Implemented support for XPath variables in predicates (#31).
cmlenz
parents:
169
diff
changeset
|
1404 __slots__ = ['name'] |
a2e0a7986d19
Implemented support for XPath variables in predicates (#31).
cmlenz
parents:
169
diff
changeset
|
1405 def __init__(self, name): |
a2e0a7986d19
Implemented support for XPath variables in predicates (#31).
cmlenz
parents:
169
diff
changeset
|
1406 self.name = name |
224
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1407 def __call__(self, kind, data, pos, namespaces, variables): |
228
f79b20a50919
Add support for position predicates in XPath expressions.
cmlenz
parents:
224
diff
changeset
|
1408 return variables.get(self.name) |
179
a2e0a7986d19
Implemented support for XPath variables in predicates (#31).
cmlenz
parents:
169
diff
changeset
|
1409 def __repr__(self): |
215 | 1410 return str(self.name) |
179
a2e0a7986d19
Implemented support for XPath variables in predicates (#31).
cmlenz
parents:
169
diff
changeset
|
1411 |
137 | 1412 # Operators |
1413 | |
1414 class AndOperator(object): | |
161
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1415 """The boolean operator `and`.""" |
137 | 1416 __slots__ = ['lval', 'rval'] |
1417 def __init__(self, lval, rval): | |
1418 self.lval = lval | |
1419 self.rval = rval | |
224
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1420 def __call__(self, kind, data, pos, namespaces, variables): |
820
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
1421 lval = as_bool(self.lval(kind, data, pos, namespaces, variables)) |
161
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1422 if not lval: |
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1423 return False |
224
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1424 rval = self.rval(kind, data, pos, namespaces, variables) |
820
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
1425 return as_bool(rval) |
137 | 1426 def __repr__(self): |
161
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1427 return '%s and %s' % (self.lval, self.rval) |
137 | 1428 |
161
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1429 class EqualsOperator(object): |
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1430 """The equality operator `=`.""" |
137 | 1431 __slots__ = ['lval', 'rval'] |
1432 def __init__(self, lval, rval): | |
1433 self.lval = lval | |
1434 self.rval = rval | |
224
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1435 def __call__(self, kind, data, pos, namespaces, variables): |
820
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
1436 lval = as_scalar(self.lval(kind, data, pos, namespaces, variables)) |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
1437 rval = as_scalar(self.rval(kind, data, pos, namespaces, variables)) |
161
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1438 return lval == rval |
137 | 1439 def __repr__(self): |
161
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1440 return '%s=%s' % (self.lval, self.rval) |
137 | 1441 |
161
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1442 class NotEqualsOperator(object): |
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1443 """The equality operator `!=`.""" |
137 | 1444 __slots__ = ['lval', 'rval'] |
1445 def __init__(self, lval, rval): | |
1446 self.lval = lval | |
1447 self.rval = rval | |
224
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1448 def __call__(self, kind, data, pos, namespaces, variables): |
820
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
1449 lval = as_scalar(self.lval(kind, data, pos, namespaces, variables)) |
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
1450 rval = as_scalar(self.rval(kind, data, pos, namespaces, variables)) |
161
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1451 return lval != rval |
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1452 def __repr__(self): |
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1453 return '%s!=%s' % (self.lval, self.rval) |
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1454 |
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1455 class OrOperator(object): |
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1456 """The boolean operator `or`.""" |
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1457 __slots__ = ['lval', 'rval'] |
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1458 def __init__(self, lval, rval): |
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1459 self.lval = lval |
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1460 self.rval = rval |
224
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1461 def __call__(self, kind, data, pos, namespaces, variables): |
820
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
1462 lval = as_bool(self.lval(kind, data, pos, namespaces, variables)) |
161
a25f9fc5787d
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1463 if lval: |
137 | 1464 return True |
224
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1465 rval = self.rval(kind, data, pos, namespaces, variables) |
820
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
1466 return as_bool(rval) |
137 | 1467 def __repr__(self): |
1468 return '%s or %s' % (self.lval, self.rval) | |
1469 | |
162
f767cf98e3e3
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1470 class GreaterThanOperator(object): |
f767cf98e3e3
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1471 """The relational operator `>` (greater than).""" |
f767cf98e3e3
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1472 __slots__ = ['lval', 'rval'] |
f767cf98e3e3
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1473 def __init__(self, lval, rval): |
f767cf98e3e3
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1474 self.lval = lval |
f767cf98e3e3
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1475 self.rval = rval |
224
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1476 def __call__(self, kind, data, pos, namespaces, variables): |
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1477 lval = self.lval(kind, data, pos, namespaces, variables) |
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1478 rval = self.rval(kind, data, pos, namespaces, variables) |
820
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
1479 return as_float(lval) > as_float(rval) |
162
f767cf98e3e3
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1480 def __repr__(self): |
f767cf98e3e3
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1481 return '%s>%s' % (self.lval, self.rval) |
f767cf98e3e3
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1482 |
f767cf98e3e3
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1483 class GreaterThanOrEqualOperator(object): |
f767cf98e3e3
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1484 """The relational operator `>=` (greater than or equal).""" |
f767cf98e3e3
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1485 __slots__ = ['lval', 'rval'] |
f767cf98e3e3
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1486 def __init__(self, lval, rval): |
f767cf98e3e3
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1487 self.lval = lval |
f767cf98e3e3
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1488 self.rval = rval |
224
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1489 def __call__(self, kind, data, pos, namespaces, variables): |
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1490 lval = self.lval(kind, data, pos, namespaces, variables) |
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1491 rval = self.rval(kind, data, pos, namespaces, variables) |
820
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
1492 return as_float(lval) >= as_float(rval) |
162
f767cf98e3e3
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1493 def __repr__(self): |
f767cf98e3e3
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1494 return '%s>=%s' % (self.lval, self.rval) |
f767cf98e3e3
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1495 |
f767cf98e3e3
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1496 class LessThanOperator(object): |
f767cf98e3e3
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1497 """The relational operator `<` (less than).""" |
f767cf98e3e3
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1498 __slots__ = ['lval', 'rval'] |
f767cf98e3e3
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1499 def __init__(self, lval, rval): |
f767cf98e3e3
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1500 self.lval = lval |
f767cf98e3e3
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1501 self.rval = rval |
224
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1502 def __call__(self, kind, data, pos, namespaces, variables): |
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1503 lval = self.lval(kind, data, pos, namespaces, variables) |
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1504 rval = self.rval(kind, data, pos, namespaces, variables) |
820
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
1505 return as_float(lval) < as_float(rval) |
162
f767cf98e3e3
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1506 def __repr__(self): |
f767cf98e3e3
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1507 return '%s<%s' % (self.lval, self.rval) |
f767cf98e3e3
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1508 |
f767cf98e3e3
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1509 class LessThanOrEqualOperator(object): |
f767cf98e3e3
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1510 """The relational operator `<=` (less than or equal).""" |
f767cf98e3e3
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1511 __slots__ = ['lval', 'rval'] |
f767cf98e3e3
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1512 def __init__(self, lval, rval): |
f767cf98e3e3
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1513 self.lval = lval |
f767cf98e3e3
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1514 self.rval = rval |
224
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1515 def __call__(self, kind, data, pos, namespaces, variables): |
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1516 lval = self.lval(kind, data, pos, namespaces, variables) |
e4dad1145f84
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1517 rval = self.rval(kind, data, pos, namespaces, variables) |
820
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
1518 return as_float(lval) <= as_float(rval) |
162
f767cf98e3e3
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1519 def __repr__(self): |
f767cf98e3e3
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1520 return '%s<=%s' % (self.lval, self.rval) |
f767cf98e3e3
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1521 |
f767cf98e3e3
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1522 _operator_map = {'=': EqualsOperator, '!=': NotEqualsOperator, |
f767cf98e3e3
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1523 '>': GreaterThanOperator, '>=': GreaterThanOrEqualOperator, |
f767cf98e3e3
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1524 '<': LessThanOperator, '>=': LessThanOrEqualOperator} |
333
b70563ade748
Fix XPath traversal in match templates. Previously, `div/p` would be treated the same as `div//p`, i.e. it would match all descendants and not just the immediate children.
cmlenz
parents:
330
diff
changeset
|
1525 |
b70563ade748
Fix XPath traversal in match templates. Previously, `div/p` would be treated the same as `div//p`, i.e. it would match all descendants and not just the immediate children.
cmlenz
parents:
330
diff
changeset
|
1526 |
b70563ade748
Fix XPath traversal in match templates. Previously, `div/p` would be treated the same as `div//p`, i.e. it would match all descendants and not just the immediate children.
cmlenz
parents:
330
diff
changeset
|
1527 _DOTSLASHSLASH = (DESCENDANT_OR_SELF, PrincipalTypeTest(None), ()) |
820
1837f39efd6f
Sync (old) experimental inline branch with trunk@1027.
cmlenz
parents:
500
diff
changeset
|
1528 _DOTSLASH = (SELF, PrincipalTypeTest(None), ()) |