Mercurial > genshi > mirror
annotate genshi/path.py @ 724:919809e55d16 experimental-match-fastpaths
update to trunk to track r847, fixing python 2.4 compatibility issues in speedup (and fixing copyrights, apparently :))
author | aflett |
---|---|
date | Mon, 21 Apr 2008 19:36:53 +0000 |
parents | 3d7288f373bd |
children |
rev | line source |
---|---|
1 | 1 # -*- coding: utf-8 -*- |
2 # | |
724
919809e55d16
update to trunk to track r847, fixing python 2.4 compatibility issues in speedup (and fixing copyrights, apparently :))
aflett
parents:
687
diff
changeset
|
3 # Copyright (C) 2006-2008 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
2368c3becc52
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
14 """Basic support for evaluating XPath expressions against streams. |
2368c3becc52
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
15 |
230 | 16 >>> from genshi.input import XML |
111
2368c3becc52
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
17 >>> doc = XML('''<doc> |
516 | 18 ... <items count="4"> |
111
2368c3becc52
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
19 ... <item status="new"> |
2368c3becc52
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
20 ... <summary>Foo</summary> |
2368c3becc52
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
21 ... </item> |
2368c3becc52
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
22 ... <item status="closed"> |
2368c3becc52
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
23 ... <summary>Bar</summary> |
2368c3becc52
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
24 ... </item> |
516 | 25 ... <item status="closed" resolution="invalid"> |
26 ... <summary>Baz</summary> | |
27 ... </item> | |
28 ... <item status="closed" resolution="fixed"> | |
29 ... <summary>Waz</summary> | |
30 ... </item> | |
111
2368c3becc52
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
31 ... </items> |
2368c3becc52
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
32 ... </doc>''') |
516 | 33 >>> print doc.select('items/item[@status="closed" and ' |
34 ... '(@resolution="invalid" or not(@resolution))]/summary/text()') | |
35 BarBaz | |
111
2368c3becc52
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
36 |
2368c3becc52
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 |
2368c3becc52
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. |
2368c3becc52
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
39 """ |
1 | 40 |
155
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
41 from math import ceil, floor |
593 | 42 import operator |
1 | 43 import re |
44 | |
230 | 45 from genshi.core import Stream, Attrs, Namespace, QName |
638
1f73e534e715
Fix for descendant-or-self XPath patterns when namespaces are involved.
cmlenz
parents:
605
diff
changeset
|
46 from genshi.core import START, END, TEXT, START_NS, END_NS, COMMENT, PI, \ |
1f73e534e715
Fix for descendant-or-self XPath patterns when namespaces are involved.
cmlenz
parents:
605
diff
changeset
|
47 START_CDATA, END_CDATA |
1 | 48 |
106
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
49 __all__ = ['Path', 'PathSyntaxError'] |
425
073640758a42
Try to use proper reStructuredText for docstrings throughout.
cmlenz
parents:
386
diff
changeset
|
50 __docformat__ = 'restructuredtext en' |
1 | 51 |
52 | |
114 | 53 class Axis(object): |
54 """Defines constants for the various supported XPath axes.""" | |
55 | |
56 ATTRIBUTE = 'attribute' | |
57 CHILD = 'child' | |
58 DESCENDANT = 'descendant' | |
59 DESCENDANT_OR_SELF = 'descendant-or-self' | |
60 SELF = 'self' | |
61 | |
62 def forname(cls, name): | |
63 """Return the axis constant for the given name, or `None` if no such | |
64 axis was defined. | |
65 """ | |
66 return getattr(cls, name.upper().replace('-', '_'), None) | |
67 forname = classmethod(forname) | |
68 | |
69 | |
70 ATTRIBUTE = Axis.ATTRIBUTE | |
71 CHILD = Axis.CHILD | |
72 DESCENDANT = Axis.DESCENDANT | |
73 DESCENDANT_OR_SELF = Axis.DESCENDANT_OR_SELF | |
74 SELF = Axis.SELF | |
75 | |
76 | |
1 | 77 class Path(object): |
26
3c1a022be04c
* Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents:
25
diff
changeset
|
78 """Implements basic XPath support on streams. |
1 | 79 |
26
3c1a022be04c
* Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents:
25
diff
changeset
|
80 Instances of this class represent a "compiled" XPath expression, and provide |
3c1a022be04c
* Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents:
25
diff
changeset
|
81 methods for testing the path against a stream, as well as extracting a |
3c1a022be04c
* Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents:
25
diff
changeset
|
82 substream matching that path. |
1 | 83 """ |
84 | |
139
8332287b5508
Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents:
137
diff
changeset
|
85 def __init__(self, text, filename=None, lineno=-1): |
26
3c1a022be04c
* Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents:
25
diff
changeset
|
86 """Create the path object from a string. |
3c1a022be04c
* Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents:
25
diff
changeset
|
87 |
425
073640758a42
Try to use proper reStructuredText for docstrings throughout.
cmlenz
parents:
386
diff
changeset
|
88 :param text: the path expression |
498 | 89 :param filename: the name of the file in which the path expression was |
90 found (used in error messages) | |
91 :param lineno: the line on which the expression was found | |
26
3c1a022be04c
* Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents:
25
diff
changeset
|
92 """ |
1 | 93 self.source = text |
139
8332287b5508
Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents:
137
diff
changeset
|
94 self.paths = PathParser(text, filename, lineno).parse() |
1 | 95 |
96 def __repr__(self): | |
137 | 97 paths = [] |
98 for path in self.paths: | |
99 steps = [] | |
100 for axis, nodetest, predicates in path: | |
101 steps.append('%s::%s' % (axis, nodetest)) | |
102 for predicate in predicates: | |
224
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
103 steps[-1] += '[%s]' % predicate |
137 | 104 paths.append('/'.join(steps)) |
105 return '<%s "%s">' % (self.__class__.__name__, '|'.join(paths)) | |
1 | 106 |
224
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
107 def select(self, stream, namespaces=None, variables=None): |
26
3c1a022be04c
* Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents:
25
diff
changeset
|
108 """Returns a substream of the given stream that matches the path. |
3c1a022be04c
* Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents:
25
diff
changeset
|
109 |
3c1a022be04c
* Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents:
25
diff
changeset
|
110 If there are no matches, this method returns an empty stream. |
3c1a022be04c
* Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents:
25
diff
changeset
|
111 |
230 | 112 >>> from genshi.input import XML |
33 | 113 >>> xml = XML('<root><elem><child>Text</child></elem></root>') |
61 | 114 |
216
636fe6766b4d
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
|
115 >>> print Path('.//child').select(xml) |
33 | 116 <child>Text</child> |
117 | |
216
636fe6766b4d
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
|
118 >>> print Path('.//child/text()').select(xml) |
33 | 119 Text |
120 | |
425
073640758a42
Try to use proper reStructuredText for docstrings throughout.
cmlenz
parents:
386
diff
changeset
|
121 :param stream: the stream to select from |
073640758a42
Try to use proper reStructuredText for docstrings throughout.
cmlenz
parents:
386
diff
changeset
|
122 :param namespaces: (optional) a mapping of namespace prefixes to URIs |
073640758a42
Try to use proper reStructuredText for docstrings throughout.
cmlenz
parents:
386
diff
changeset
|
123 :param variables: (optional) a mapping of variable names to values |
073640758a42
Try to use proper reStructuredText for docstrings throughout.
cmlenz
parents:
386
diff
changeset
|
124 :return: the substream matching the path, or an empty stream |
498 | 125 :rtype: `Stream` |
26
3c1a022be04c
* Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents:
25
diff
changeset
|
126 """ |
224
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
127 if namespaces is None: |
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
128 namespaces = {} |
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
129 if variables is None: |
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
130 variables = {} |
1 | 131 stream = iter(stream) |
26
3c1a022be04c
* Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents:
25
diff
changeset
|
132 def _generate(): |
1 | 133 test = self.test() |
305 | 134 for event in stream: |
135 result = test(event, namespaces, variables) | |
1 | 136 if result is True: |
305 | 137 yield event |
330
3e749eaa3100
XPath tests should never return event tuples, just values or booleans.
cmlenz
parents:
326
diff
changeset
|
138 if event[0] is START: |
3e749eaa3100
XPath tests should never return event tuples, just values or booleans.
cmlenz
parents:
326
diff
changeset
|
139 depth = 1 |
3e749eaa3100
XPath tests should never return event tuples, just values or booleans.
cmlenz
parents:
326
diff
changeset
|
140 while depth > 0: |
3e749eaa3100
XPath tests should never return event tuples, just values or booleans.
cmlenz
parents:
326
diff
changeset
|
141 subevent = stream.next() |
3e749eaa3100
XPath tests should never return event tuples, just values or booleans.
cmlenz
parents:
326
diff
changeset
|
142 if subevent[0] is START: |
3e749eaa3100
XPath tests should never return event tuples, just values or booleans.
cmlenz
parents:
326
diff
changeset
|
143 depth += 1 |
3e749eaa3100
XPath tests should never return event tuples, just values or booleans.
cmlenz
parents:
326
diff
changeset
|
144 elif subevent[0] is END: |
3e749eaa3100
XPath tests should never return event tuples, just values or booleans.
cmlenz
parents:
326
diff
changeset
|
145 depth -= 1 |
3e749eaa3100
XPath tests should never return event tuples, just values or booleans.
cmlenz
parents:
326
diff
changeset
|
146 yield subevent |
3e749eaa3100
XPath tests should never return event tuples, just values or booleans.
cmlenz
parents:
326
diff
changeset
|
147 test(subevent, namespaces, variables, |
3e749eaa3100
XPath tests should never return event tuples, just values or booleans.
cmlenz
parents:
326
diff
changeset
|
148 updateonly=True) |
1 | 149 elif result: |
150 yield result | |
605
d0345c64da65
Text templates now default to rendering as plain text; it is no longer necessary to explicitly specify the "text" method to the `render()` or `serialize()` method of the generated markup stream. See tickets #62 and #118.
cmlenz
parents:
593
diff
changeset
|
151 return Stream(_generate(), |
d0345c64da65
Text templates now default to rendering as plain text; it is no longer necessary to explicitly specify the "text" method to the `render()` or `serialize()` method of the generated markup stream. See tickets #62 and #118.
cmlenz
parents:
593
diff
changeset
|
152 serializer=getattr(stream, 'serializer', None)) |
1 | 153 |
38
ee669cb9cccc
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
|
154 def test(self, ignore_context=False): |
26
3c1a022be04c
* Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents:
25
diff
changeset
|
155 """Returns a function that can be used to track whether the path matches |
3c1a022be04c
* Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents:
25
diff
changeset
|
156 a specific stream event. |
3c1a022be04c
* Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents:
25
diff
changeset
|
157 |
425
073640758a42
Try to use proper reStructuredText for docstrings throughout.
cmlenz
parents:
386
diff
changeset
|
158 The function returned expects the positional arguments ``event``, |
073640758a42
Try to use proper reStructuredText for docstrings throughout.
cmlenz
parents:
386
diff
changeset
|
159 ``namespaces`` and ``variables``. The first is a stream event, while the |
305 | 160 latter two are a mapping of namespace prefixes to URIs, and a mapping |
306 | 161 of variable names to values, respectively. In addition, the function |
425
073640758a42
Try to use proper reStructuredText for docstrings throughout.
cmlenz
parents:
386
diff
changeset
|
162 accepts an ``updateonly`` keyword argument that default to ``False``. If |
073640758a42
Try to use proper reStructuredText for docstrings throughout.
cmlenz
parents:
386
diff
changeset
|
163 it is set to ``True``, the function only updates its internal state, |
306 | 164 but does not perform any tests or return a result. |
224
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
165 |
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
166 If the path matches the event, the function returns the match (for |
425
073640758a42
Try to use proper reStructuredText for docstrings throughout.
cmlenz
parents:
386
diff
changeset
|
167 example, a `START` or `TEXT` event.) Otherwise, it returns ``None``. |
33 | 168 |
230 | 169 >>> from genshi.input import XML |
33 | 170 >>> xml = XML('<root><elem><child id="1"/></elem><child id="2"/></root>') |
171 >>> test = Path('child').test() | |
305 | 172 >>> for event in xml: |
173 ... if test(event, {}, {}): | |
386 | 174 ... print event[0], repr(event[1]) |
175 START (QName(u'child'), Attrs([(QName(u'id'), u'2')])) | |
498 | 176 |
177 :param ignore_context: if `True`, the path is interpreted like a pattern | |
178 in XSLT, meaning for example that it will match | |
179 at any depth | |
180 :return: a function that can be used to test individual events in a | |
181 stream against the path | |
182 :rtype: ``function`` | |
26
3c1a022be04c
* Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents:
25
diff
changeset
|
183 """ |
687
3d7288f373bd
land first cut at fast-path matching - needs some cleanup
aflett
parents:
638
diff
changeset
|
184 |
3d7288f373bd
land first cut at fast-path matching - needs some cleanup
aflett
parents:
638
diff
changeset
|
185 # paths is path state that is maintained across calls to _test() |
3d7288f373bd
land first cut at fast-path matching - needs some cleanup
aflett
parents:
638
diff
changeset
|
186 # paths is a list of tuples, one for each segment in the path |
3d7288f373bd
land first cut at fast-path matching - needs some cleanup
aflett
parents:
638
diff
changeset
|
187 paths = [] |
3d7288f373bd
land first cut at fast-path matching - needs some cleanup
aflett
parents:
638
diff
changeset
|
188 for p in self.paths: |
3d7288f373bd
land first cut at fast-path matching - needs some cleanup
aflett
parents:
638
diff
changeset
|
189 if ignore_context: |
3d7288f373bd
land first cut at fast-path matching - needs some cleanup
aflett
parents:
638
diff
changeset
|
190 p = [_DOTSLASHSLASH] + p |
3d7288f373bd
land first cut at fast-path matching - needs some cleanup
aflett
parents:
638
diff
changeset
|
191 path = (p, len(p), [0], [], [0] * len(p)) |
3d7288f373bd
land first cut at fast-path matching - needs some cleanup
aflett
parents:
638
diff
changeset
|
192 paths.append(path) |
1 | 193 |
306 | 194 def _test(event, namespaces, variables, updateonly=False): |
330
3e749eaa3100
XPath tests should never return event tuples, just values or booleans.
cmlenz
parents:
326
diff
changeset
|
195 kind, data, pos = event[:3] |
259
fe8dbe9066c1
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
|
196 retval = None |
228
a5b38b459cbb
Add support for position predicates in XPath expressions.
cmlenz
parents:
224
diff
changeset
|
197 for steps, size, cursors, cutoff, counter in paths: |
216
636fe6766b4d
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
|
198 # Manage the stack that tells us "where we are" in the stream |
211
e5151983df0d
Fix another regression introduced in [258]: some kinds of cascaded match templates were broken, for example in the TurboGears example app.
cmlenz
parents:
179
diff
changeset
|
199 if kind is END: |
228
a5b38b459cbb
Add support for position predicates in XPath expressions.
cmlenz
parents:
224
diff
changeset
|
200 if cursors: |
a5b38b459cbb
Add support for position predicates in XPath expressions.
cmlenz
parents:
224
diff
changeset
|
201 cursors.pop() |
211
e5151983df0d
Fix another regression introduced in [258]: some kinds of cascaded match templates were broken, for example in the TurboGears example app.
cmlenz
parents:
179
diff
changeset
|
202 continue |
223 | 203 elif kind is START: |
228
a5b38b459cbb
Add support for position predicates in XPath expressions.
cmlenz
parents:
224
diff
changeset
|
204 cursors.append(cursors and cursors[-1] or 0) |
638
1f73e534e715
Fix for descendant-or-self XPath patterns when namespaces are involved.
cmlenz
parents:
605
diff
changeset
|
205 elif kind is START_NS or kind is END_NS \ |
1f73e534e715
Fix for descendant-or-self XPath patterns when namespaces are involved.
cmlenz
parents:
605
diff
changeset
|
206 or kind is START_CDATA or kind is END_CDATA: |
1f73e534e715
Fix for descendant-or-self XPath patterns when namespaces are involved.
cmlenz
parents:
605
diff
changeset
|
207 continue |
259
fe8dbe9066c1
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
|
208 |
306 | 209 if updateonly or retval or not cursors: |
106
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
210 continue |
228
a5b38b459cbb
Add support for position predicates in XPath expressions.
cmlenz
parents:
224
diff
changeset
|
211 cursor = cursors[-1] |
a5b38b459cbb
Add support for position predicates in XPath expressions.
cmlenz
parents:
224
diff
changeset
|
212 depth = len(cursors) |
1 | 213 |
228
a5b38b459cbb
Add support for position predicates in XPath expressions.
cmlenz
parents:
224
diff
changeset
|
214 if cutoff and depth + int(kind is not START) > cutoff[0]: |
216
636fe6766b4d
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
|
215 continue |
636fe6766b4d
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
|
216 |
636fe6766b4d
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
|
217 ctxtnode = not ignore_context and kind is START \ |
228
a5b38b459cbb
Add support for position predicates in XPath expressions.
cmlenz
parents:
224
diff
changeset
|
218 and depth == 2 |
259
fe8dbe9066c1
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
|
219 matched = None |
111
2368c3becc52
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
220 while 1: |
216
636fe6766b4d
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
|
221 # Fetch the next location step |
137 | 222 axis, nodetest, predicates = steps[cursor] |
106
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
223 |
216
636fe6766b4d
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
|
224 # If this is the start event for the context node, and the |
636fe6766b4d
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
|
225 # axis of the location step doesn't include the current |
636fe6766b4d
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
|
226 # element, skip the test |
636fe6766b4d
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
|
227 if ctxtnode and (axis is CHILD or axis is DESCENDANT): |
111
2368c3becc52
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
228 break |
2368c3becc52
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
229 |
216
636fe6766b4d
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
|
230 # Is this the last step of the location path? |
636fe6766b4d
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
|
231 last_step = cursor + 1 == size |
636fe6766b4d
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
|
232 |
636fe6766b4d
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
|
233 # Perform the actual node test |
224
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
234 matched = nodetest(kind, data, pos, namespaces, variables) |
216
636fe6766b4d
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
|
235 |
636fe6766b4d
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
|
236 # The node test matched |
636fe6766b4d
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
|
237 if matched: |
636fe6766b4d
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
|
238 |
636fe6766b4d
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
|
239 # Check all the predicates for this step |
636fe6766b4d
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
|
240 if predicates: |
636fe6766b4d
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
|
241 for predicate in predicates: |
228
a5b38b459cbb
Add support for position predicates in XPath expressions.
cmlenz
parents:
224
diff
changeset
|
242 pretval = predicate(kind, data, pos, namespaces, |
a5b38b459cbb
Add support for position predicates in XPath expressions.
cmlenz
parents:
224
diff
changeset
|
243 variables) |
518
c1f2a859fd75
Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents:
516
diff
changeset
|
244 if type(pretval) is float: # FIXME <- need to |
c1f2a859fd75
Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents:
516
diff
changeset
|
245 # check this for |
c1f2a859fd75
Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents:
516
diff
changeset
|
246 # other types that |
c1f2a859fd75
Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents:
516
diff
changeset
|
247 # can be coerced to |
c1f2a859fd75
Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents:
516
diff
changeset
|
248 # float |
228
a5b38b459cbb
Add support for position predicates in XPath expressions.
cmlenz
parents:
224
diff
changeset
|
249 counter[cursor] += 1 |
a5b38b459cbb
Add support for position predicates in XPath expressions.
cmlenz
parents:
224
diff
changeset
|
250 if counter[cursor] != int(pretval): |
a5b38b459cbb
Add support for position predicates in XPath expressions.
cmlenz
parents:
224
diff
changeset
|
251 pretval = False |
a5b38b459cbb
Add support for position predicates in XPath expressions.
cmlenz
parents:
224
diff
changeset
|
252 if not pretval: |
216
636fe6766b4d
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
|
253 matched = None |
636fe6766b4d
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
|
254 break |
636fe6766b4d
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
|
255 |
636fe6766b4d
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
|
256 # Both the node test and the predicates matched |
636fe6766b4d
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
|
257 if matched: |
636fe6766b4d
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
|
258 if last_step: |
217
f150cff4da18
Fix `py:match` directive which would screw up in some scenarios due to incorrect handling of the substream. Closes #49.
cmlenz
parents:
216
diff
changeset
|
259 if not ctxtnode or kind is not START \ |
f150cff4da18
Fix `py:match` directive which would screw up in some scenarios due to incorrect handling of the substream. Closes #49.
cmlenz
parents:
216
diff
changeset
|
260 or axis is ATTRIBUTE or axis is SELF: |
216
636fe6766b4d
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
|
261 retval = matched |
636fe6766b4d
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
|
262 elif not ctxtnode or axis is SELF \ |
636fe6766b4d
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
|
263 or axis is DESCENDANT_OR_SELF: |
636fe6766b4d
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
|
264 cursor += 1 |
228
a5b38b459cbb
Add support for position predicates in XPath expressions.
cmlenz
parents:
224
diff
changeset
|
265 cursors[-1] = cursor |
216
636fe6766b4d
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
|
266 cutoff[:] = [] |
228
a5b38b459cbb
Add support for position predicates in XPath expressions.
cmlenz
parents:
224
diff
changeset
|
267 |
333
e5ba8c5a30fe
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
|
268 if kind is START: |
e5ba8c5a30fe
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
|
269 if last_step and not (axis is DESCENDANT or |
e5ba8c5a30fe
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
|
270 axis is DESCENDANT_OR_SELF): |
228
a5b38b459cbb
Add support for position predicates in XPath expressions.
cmlenz
parents:
224
diff
changeset
|
271 cutoff[:] = [depth] |
216
636fe6766b4d
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
|
272 |
333
e5ba8c5a30fe
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
|
273 elif steps[cursor][0] is ATTRIBUTE: |
216
636fe6766b4d
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
|
274 # If the axis of the next location step is the |
363
37e4b4bb0b53
Parse template includes at parse time to avoid some runtime overhead.
cmlenz
parents:
333
diff
changeset
|
275 # attribute axis, we need to move on to processing |
37e4b4bb0b53
Parse template includes at parse time to avoid some runtime overhead.
cmlenz
parents:
333
diff
changeset
|
276 # that step without waiting for the next markup |
37e4b4bb0b53
Parse template includes at parse time to avoid some runtime overhead.
cmlenz
parents:
333
diff
changeset
|
277 # event |
216
636fe6766b4d
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
|
278 continue |
636fe6766b4d
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
|
279 |
636fe6766b4d
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
|
280 # We're done with this step if it's the last step or the |
636fe6766b4d
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
|
281 # axis isn't "self" |
384 | 282 if not matched or last_step or not ( |
283 axis is SELF or axis is DESCENDANT_OR_SELF): | |
216
636fe6766b4d
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
|
284 break |
638
1f73e534e715
Fix for descendant-or-self XPath patterns when namespaces are involved.
cmlenz
parents:
605
diff
changeset
|
285 if ctxtnode and axis is DESCENDANT_OR_SELF: |
1f73e534e715
Fix for descendant-or-self XPath patterns when namespaces are involved.
cmlenz
parents:
605
diff
changeset
|
286 ctxtnode = False |
216
636fe6766b4d
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
|
287 |
687
3d7288f373bd
land first cut at fast-path matching - needs some cleanup
aflett
parents:
638
diff
changeset
|
288 if (retval or not matched) and \ |
3d7288f373bd
land first cut at fast-path matching - needs some cleanup
aflett
parents:
638
diff
changeset
|
289 kind is START and \ |
333
e5ba8c5a30fe
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
|
290 not (axis is DESCENDANT or axis is DESCENDANT_OR_SELF): |
106
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
291 # If this step is not a closure, it cannot be matched until |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
292 # the current element is closed... so we need to move the |
114 | 293 # cursor back to the previous closure and retest that |
294 # against the current element | |
333
e5ba8c5a30fe
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
|
295 backsteps = [(i, k, d, p) for i, (k, d, p) |
e5ba8c5a30fe
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
|
296 in enumerate(steps[:cursor]) |
215 | 297 if k is DESCENDANT or k is DESCENDANT_OR_SELF] |
111
2368c3becc52
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
298 backsteps.reverse() |
333
e5ba8c5a30fe
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
|
299 for cursor, axis, nodetest, predicates in backsteps: |
e5ba8c5a30fe
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
|
300 if nodetest(kind, data, pos, namespaces, variables): |
e5ba8c5a30fe
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
|
301 cutoff[:] = [] |
e5ba8c5a30fe
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
|
302 break |
228
a5b38b459cbb
Add support for position predicates in XPath expressions.
cmlenz
parents:
224
diff
changeset
|
303 cursors[-1] = cursor |
1 | 304 |
259
fe8dbe9066c1
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
|
305 return retval |
1 | 306 |
307 return _test | |
308 | |
309 | |
106
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
310 class PathSyntaxError(Exception): |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
311 """Exception raised when an XPath expression is syntactically incorrect.""" |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
312 |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
313 def __init__(self, message, filename=None, lineno=-1, offset=-1): |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
314 if filename: |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
315 message = '%s (%s, line %d)' % (message, filename, lineno) |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
316 Exception.__init__(self, message) |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
317 self.filename = filename |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
318 self.lineno = lineno |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
319 self.offset = offset |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
320 |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
321 |
137 | 322 class PathParser(object): |
106
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
323 """Tokenizes and parses an XPath expression.""" |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
324 |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
325 _QUOTES = (("'", "'"), ('"', '"')) |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
326 _TOKENS = ('::', ':', '..', '.', '//', '/', '[', ']', '()', '(', ')', '@', |
179
13909179e5e1
Implemented support for XPath variables in predicates (#31).
cmlenz
parents:
169
diff
changeset
|
327 '=', '!=', '!', '|', ',', '>=', '>', '<=', '<', '$') |
163
9c023c395e44
Support for XPath number literals including decimal places.
cmlenz
parents:
162
diff
changeset
|
328 _tokenize = re.compile('("[^"]*")|(\'[^\']*\')|((?:\d+)?\.\d+)|(%s)|([^%s\s]+)|\s+' % ( |
106
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
329 '|'.join([re.escape(t) for t in _TOKENS]), |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
330 ''.join([re.escape(t[0]) for t in _TOKENS]))).findall |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
331 |
139
8332287b5508
Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents:
137
diff
changeset
|
332 def __init__(self, text, filename=None, lineno=-1): |
8332287b5508
Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents:
137
diff
changeset
|
333 self.filename = filename |
8332287b5508
Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents:
137
diff
changeset
|
334 self.lineno = lineno |
163
9c023c395e44
Support for XPath number literals including decimal places.
cmlenz
parents:
162
diff
changeset
|
335 self.tokens = filter(None, [dqstr or sqstr or number or token or name |
9c023c395e44
Support for XPath number literals including decimal places.
cmlenz
parents:
162
diff
changeset
|
336 for dqstr, sqstr, number, token, name in |
155
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
337 self._tokenize(text)]) |
106
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
338 self.pos = 0 |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
339 |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
340 # Tokenizer |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
341 |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
342 at_end = property(lambda self: self.pos == len(self.tokens) - 1) |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
343 cur_token = property(lambda self: self.tokens[self.pos]) |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
344 |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
345 def next_token(self): |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
346 self.pos += 1 |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
347 return self.tokens[self.pos] |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
348 |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
349 def peek_token(self): |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
350 if not self.at_end: |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
351 return self.tokens[self.pos + 1] |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
352 return None |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
353 |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
354 # Recursive descent parser |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
355 |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
356 def parse(self): |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
357 """Parses the XPath expression and returns a list of location path |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
358 tests. |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
359 |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
360 For union expressions (such as `*|text()`), this function returns one |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
361 test for each operand in the union. For patch expressions that don't |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
362 use the union operator, the function always returns a list of size 1. |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
363 |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
364 Each path test in turn is a sequence of tests that correspond to the |
111
2368c3becc52
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
365 location steps, each tuples of the form `(axis, testfunc, predicates)` |
106
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
366 """ |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
367 paths = [self._location_path()] |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
368 while self.cur_token == '|': |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
369 self.next_token() |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
370 paths.append(self._location_path()) |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
371 if not self.at_end: |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
372 raise PathSyntaxError('Unexpected token %r after end of expression' |
139
8332287b5508
Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents:
137
diff
changeset
|
373 % self.cur_token, self.filename, self.lineno) |
106
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
374 return paths |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
375 |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
376 def _location_path(self): |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
377 steps = [] |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
378 while True: |
215 | 379 if self.cur_token.startswith('/'): |
380 if self.cur_token == '//': | |
381 steps.append((DESCENDANT_OR_SELF, NodeTest(), [])) | |
382 elif not steps: | |
383 raise PathSyntaxError('Absolute location paths not ' | |
384 'supported', self.filename, | |
385 self.lineno) | |
111
2368c3becc52
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
386 self.next_token() |
2368c3becc52
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
387 |
137 | 388 axis, nodetest, predicates = self._location_step() |
389 if not axis: | |
145
47bbd9d2a5af
* Fix error in expression evaluation when the expression evaluates to an iterable that does not produce event tuples.
cmlenz
parents:
139
diff
changeset
|
390 axis = CHILD |
137 | 391 steps.append((axis, nodetest, predicates)) |
111
2368c3becc52
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
392 |
2368c3becc52
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
393 if self.at_end or not self.cur_token.startswith('/'): |
106
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
394 break |
111
2368c3becc52
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
395 |
106
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
396 return steps |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
397 |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
398 def _location_step(self): |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
399 if self.cur_token == '@': |
114 | 400 axis = ATTRIBUTE |
106
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
401 self.next_token() |
111
2368c3becc52
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
402 elif self.cur_token == '.': |
114 | 403 axis = SELF |
137 | 404 elif self.cur_token == '..': |
139
8332287b5508
Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents:
137
diff
changeset
|
405 raise PathSyntaxError('Unsupported axis "parent"', self.filename, |
8332287b5508
Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents:
137
diff
changeset
|
406 self.lineno) |
111
2368c3becc52
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
407 elif self.peek_token() == '::': |
114 | 408 axis = Axis.forname(self.cur_token) |
409 if axis is None: | |
139
8332287b5508
Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents:
137
diff
changeset
|
410 raise PathSyntaxError('Unsupport axis "%s"' % axis, |
8332287b5508
Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents:
137
diff
changeset
|
411 self.filename, self.lineno) |
111
2368c3becc52
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
412 self.next_token() |
2368c3becc52
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
413 self.next_token() |
106
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
414 else: |
137 | 415 axis = None |
416 nodetest = self._node_test(axis or CHILD) | |
111
2368c3becc52
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
417 predicates = [] |
106
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
418 while self.cur_token == '[': |
111
2368c3becc52
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
419 predicates.append(self._predicate()) |
137 | 420 return axis, nodetest, predicates |
106
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
421 |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
422 def _node_test(self, axis=None): |
224
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
423 test = prefix = None |
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
424 next_token = self.peek_token() |
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
425 if next_token in ('(', '()'): # Node type test |
106
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
426 test = self._node_type() |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
427 |
224
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
428 elif next_token == ':': # Namespace prefix |
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
429 prefix = self.cur_token |
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
430 self.next_token() |
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
431 localname = self.next_token() |
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
432 if localname == '*': |
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
433 test = QualifiedPrincipalTypeTest(axis, prefix) |
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
434 else: |
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
435 test = QualifiedNameTest(axis, prefix, localname) |
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
436 |
106
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
437 else: # Name test |
137 | 438 if self.cur_token == '*': |
439 test = PrincipalTypeTest(axis) | |
440 elif self.cur_token == '.': | |
441 test = NodeTest() | |
106
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
442 else: |
137 | 443 test = LocalNameTest(axis, self.cur_token) |
106
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
444 |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
445 if not self.at_end: |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
446 self.next_token() |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
447 return test |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
448 |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
449 def _node_type(self): |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
450 name = self.cur_token |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
451 self.next_token() |
137 | 452 |
453 args = [] | |
454 if self.cur_token != '()': | |
455 # The processing-instruction() function optionally accepts the | |
456 # name of the PI as argument, which must be a literal string | |
457 self.next_token() # ( | |
458 if self.cur_token != ')': | |
459 string = self.cur_token | |
460 if (string[0], string[-1]) in self._QUOTES: | |
461 string = string[1:-1] | |
462 args.append(string) | |
463 | |
464 cls = _nodetest_map.get(name) | |
465 if not cls: | |
139
8332287b5508
Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents:
137
diff
changeset
|
466 raise PathSyntaxError('%s() not allowed here' % name, self.filename, |
8332287b5508
Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents:
137
diff
changeset
|
467 self.lineno) |
137 | 468 return cls(*args) |
106
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
469 |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
470 def _predicate(self): |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
471 assert self.cur_token == '[' |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
472 self.next_token() |
111
2368c3becc52
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
473 expr = self._or_expr() |
121
062e51ad7b19
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
474 if self.cur_token != ']': |
062e51ad7b19
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
475 raise PathSyntaxError('Expected "]" to close predicate, ' |
139
8332287b5508
Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents:
137
diff
changeset
|
476 'but found "%s"' % self.cur_token, |
8332287b5508
Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents:
137
diff
changeset
|
477 self.filename, self.lineno) |
111
2368c3becc52
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
478 if not self.at_end: |
2368c3becc52
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
479 self.next_token() |
2368c3becc52
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
480 return expr |
106
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
481 |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
482 def _or_expr(self): |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
483 expr = self._and_expr() |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
484 while self.cur_token == 'or': |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
485 self.next_token() |
137 | 486 expr = OrOperator(expr, self._and_expr()) |
106
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
487 return expr |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
488 |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
489 def _and_expr(self): |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
490 expr = self._equality_expr() |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
491 while self.cur_token == 'and': |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
492 self.next_token() |
137 | 493 expr = AndOperator(expr, self._equality_expr()) |
106
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
494 return expr |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
495 |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
496 def _equality_expr(self): |
162
456039594db9
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
497 expr = self._relational_expr() |
456039594db9
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
498 while self.cur_token in ('=', '!='): |
456039594db9
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
499 op = _operator_map[self.cur_token] |
456039594db9
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
500 self.next_token() |
456039594db9
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
501 expr = op(expr, self._relational_expr()) |
456039594db9
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
502 return expr |
456039594db9
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
503 |
456039594db9
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
504 def _relational_expr(self): |
516 | 505 expr = self._sub_expr() |
162
456039594db9
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
506 while self.cur_token in ('>', '>=', '<', '>='): |
456039594db9
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
507 op = _operator_map[self.cur_token] |
106
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
508 self.next_token() |
516 | 509 expr = op(expr, self._sub_expr()) |
510 return expr | |
511 | |
512 def _sub_expr(self): | |
513 token = self.cur_token | |
514 if token != '(': | |
515 return self._primary_expr() | |
516 self.next_token() | |
517 expr = self._or_expr() | |
518 if self.cur_token != ')': | |
519 raise PathSyntaxError('Expected ")" to close sub-expression, ' | |
520 'but found "%s"' % self.cur_token, | |
521 self.filename, self.lineno) | |
522 self.next_token() | |
106
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
523 return expr |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
524 |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
525 def _primary_expr(self): |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
526 token = self.cur_token |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
527 if len(token) > 1 and (token[0], token[-1]) in self._QUOTES: |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
528 self.next_token() |
137 | 529 return StringLiteral(token[1:-1]) |
163
9c023c395e44
Support for XPath number literals including decimal places.
cmlenz
parents:
162
diff
changeset
|
530 elif token[0].isdigit() or token[0] == '.': |
106
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
531 self.next_token() |
518
c1f2a859fd75
Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents:
516
diff
changeset
|
532 return NumberLiteral(as_float(token)) |
179
13909179e5e1
Implemented support for XPath variables in predicates (#31).
cmlenz
parents:
169
diff
changeset
|
533 elif token == '$': |
13909179e5e1
Implemented support for XPath variables in predicates (#31).
cmlenz
parents:
169
diff
changeset
|
534 token = self.next_token() |
13909179e5e1
Implemented support for XPath variables in predicates (#31).
cmlenz
parents:
169
diff
changeset
|
535 self.next_token() |
13909179e5e1
Implemented support for XPath variables in predicates (#31).
cmlenz
parents:
169
diff
changeset
|
536 return VariableReference(token) |
121
062e51ad7b19
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
537 elif not self.at_end and self.peek_token().startswith('('): |
155
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
538 return self._function_call() |
106
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
539 else: |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
540 axis = None |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
541 if token == '@': |
114 | 542 axis = ATTRIBUTE |
106
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
543 self.next_token() |
f9473bdc93b2
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
544 return self._node_test(axis) |
137 | 545 |
155
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
546 def _function_call(self): |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
547 name = self.cur_token |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
548 if self.next_token() == '()': |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
549 args = [] |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
550 else: |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
551 assert self.cur_token == '(' |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
552 self.next_token() |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
553 args = [self._or_expr()] |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
554 while self.cur_token == ',': |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
555 self.next_token() |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
556 args.append(self._or_expr()) |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
557 if not self.cur_token == ')': |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
558 raise PathSyntaxError('Expected ")" to close function argument ' |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
559 'list, but found "%s"' % self.cur_token, |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
560 self.filename, self.lineno) |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
561 self.next_token() |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
562 cls = _function_map.get(name) |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
563 if not cls: |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
564 raise PathSyntaxError('Unsupported function "%s"' % name, |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
565 self.filename, self.lineno) |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
566 return cls(*args) |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
567 |
137 | 568 |
518
c1f2a859fd75
Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents:
516
diff
changeset
|
569 # Type coercion |
c1f2a859fd75
Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents:
516
diff
changeset
|
570 |
c1f2a859fd75
Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents:
516
diff
changeset
|
571 def as_scalar(value): |
c1f2a859fd75
Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents:
516
diff
changeset
|
572 """Convert value to a scalar. If a single element Attrs() object is passed |
c1f2a859fd75
Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents:
516
diff
changeset
|
573 the value of the single attribute will be returned.""" |
c1f2a859fd75
Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents:
516
diff
changeset
|
574 if isinstance(value, Attrs): |
c1f2a859fd75
Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents:
516
diff
changeset
|
575 assert len(value) == 1 |
c1f2a859fd75
Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents:
516
diff
changeset
|
576 return value[0][1] |
c1f2a859fd75
Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents:
516
diff
changeset
|
577 else: |
c1f2a859fd75
Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents:
516
diff
changeset
|
578 return value |
c1f2a859fd75
Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents:
516
diff
changeset
|
579 |
c1f2a859fd75
Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents:
516
diff
changeset
|
580 def as_float(value): |
c1f2a859fd75
Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents:
516
diff
changeset
|
581 # FIXME - if value is a bool it will be coerced to 0.0 and consequently |
c1f2a859fd75
Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents:
516
diff
changeset
|
582 # compared as a float. This is probably not ideal. |
c1f2a859fd75
Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents:
516
diff
changeset
|
583 return float(as_scalar(value)) |
c1f2a859fd75
Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents:
516
diff
changeset
|
584 |
c1f2a859fd75
Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents:
516
diff
changeset
|
585 def as_long(value): |
c1f2a859fd75
Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents:
516
diff
changeset
|
586 return long(as_scalar(value)) |
c1f2a859fd75
Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents:
516
diff
changeset
|
587 |
c1f2a859fd75
Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents:
516
diff
changeset
|
588 def as_string(value): |
c1f2a859fd75
Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents:
516
diff
changeset
|
589 value = as_scalar(value) |
c1f2a859fd75
Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents:
516
diff
changeset
|
590 if value is False: |
c1f2a859fd75
Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents:
516
diff
changeset
|
591 return u'' |
c1f2a859fd75
Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents:
516
diff
changeset
|
592 return unicode(value) |
c1f2a859fd75
Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents:
516
diff
changeset
|
593 |
c1f2a859fd75
Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents:
516
diff
changeset
|
594 def as_bool(value): |
c1f2a859fd75
Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents:
516
diff
changeset
|
595 return bool(as_scalar(value)) |
c1f2a859fd75
Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents:
516
diff
changeset
|
596 |
c1f2a859fd75
Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents:
516
diff
changeset
|
597 |
137 | 598 # Node tests |
599 | |
600 class PrincipalTypeTest(object): | |
161
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
601 """Node test that matches any event with the given principal type.""" |
137 | 602 __slots__ = ['principal_type'] |
603 def __init__(self, principal_type): | |
604 self.principal_type = principal_type | |
224
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
605 def __call__(self, kind, data, pos, namespaces, variables): |
137 | 606 if kind is START: |
607 if self.principal_type is ATTRIBUTE: | |
608 return data[1] or None | |
609 else: | |
610 return True | |
611 def __repr__(self): | |
612 return '*' | |
613 | |
224
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
614 class QualifiedPrincipalTypeTest(object): |
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
615 """Node test that matches any event with the given principal type in a |
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
616 specific namespace.""" |
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
617 __slots__ = ['principal_type', 'prefix'] |
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
618 def __init__(self, principal_type, prefix): |
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
619 self.principal_type = principal_type |
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
620 self.prefix = prefix |
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
621 def __call__(self, kind, data, pos, namespaces, variables): |
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
622 namespace = Namespace(namespaces.get(self.prefix)) |
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
623 if kind is START: |
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
624 if self.principal_type is ATTRIBUTE and data[1]: |
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
625 return Attrs([(name, value) for name, value in data[1] |
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
626 if name in namespace]) or None |
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
627 else: |
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
628 return data[0] in namespace |
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
629 def __repr__(self): |
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
630 return '%s:*' % self.prefix |
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
631 |
137 | 632 class LocalNameTest(object): |
364
7dabedbb53fb
Fix for #77: match templates were matching their own output.
cmlenz
parents:
363
diff
changeset
|
633 """Node test that matches any event with the given principal type and |
161
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
634 local name. |
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
635 """ |
137 | 636 __slots__ = ['principal_type', 'name'] |
637 def __init__(self, principal_type, name): | |
638 self.principal_type = principal_type | |
639 self.name = name | |
224
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
640 def __call__(self, kind, data, pos, namespaces, variables): |
137 | 641 if kind is START: |
642 if self.principal_type is ATTRIBUTE and self.name in data[1]: | |
518
c1f2a859fd75
Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents:
516
diff
changeset
|
643 return Attrs([(self.name, data[1].get(self.name))]) |
137 | 644 else: |
645 return data[0].localname == self.name | |
646 def __repr__(self): | |
647 return self.name | |
648 | |
224
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
649 class QualifiedNameTest(object): |
364
7dabedbb53fb
Fix for #77: match templates were matching their own output.
cmlenz
parents:
363
diff
changeset
|
650 """Node test that matches any event with the given principal type and |
224
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
651 qualified name. |
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
652 """ |
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
653 __slots__ = ['principal_type', 'prefix', 'name'] |
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
654 def __init__(self, principal_type, prefix, name): |
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
655 self.principal_type = principal_type |
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
656 self.prefix = prefix |
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
657 self.name = name |
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
658 def __call__(self, kind, data, pos, namespaces, variables): |
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
659 qname = QName('%s}%s' % (namespaces.get(self.prefix), self.name)) |
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
660 if kind is START: |
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
661 if self.principal_type is ATTRIBUTE and qname in data[1]: |
518
c1f2a859fd75
Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents:
516
diff
changeset
|
662 return Attrs([(self.name, data[1].get(self.name))]) |
224
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
663 else: |
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
664 return data[0] == qname |
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
665 def __repr__(self): |
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
666 return '%s:%s' % (self.prefix, self.name) |
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
667 |
137 | 668 class CommentNodeTest(object): |
161
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
669 """Node test that matches any comment events.""" |
137 | 670 __slots__ = [] |
224
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
671 def __call__(self, kind, data, pos, namespaces, variables): |
330
3e749eaa3100
XPath tests should never return event tuples, just values or booleans.
cmlenz
parents:
326
diff
changeset
|
672 return kind is COMMENT |
137 | 673 def __repr__(self): |
674 return 'comment()' | |
675 | |
676 class NodeTest(object): | |
161
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
677 """Node test that matches any node.""" |
137 | 678 __slots__ = [] |
224
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
679 def __call__(self, kind, data, pos, namespaces, variables): |
137 | 680 if kind is START: |
681 return True | |
682 return kind, data, pos | |
683 def __repr__(self): | |
684 return 'node()' | |
685 | |
686 class ProcessingInstructionNodeTest(object): | |
161
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
687 """Node test that matches any processing instruction event.""" |
137 | 688 __slots__ = ['target'] |
689 def __init__(self, target=None): | |
690 self.target = target | |
224
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
691 def __call__(self, kind, data, pos, namespaces, variables): |
330
3e749eaa3100
XPath tests should never return event tuples, just values or booleans.
cmlenz
parents:
326
diff
changeset
|
692 return kind is PI and (not self.target or data[0] == self.target) |
137 | 693 def __repr__(self): |
694 arg = '' | |
695 if self.target: | |
696 arg = '"' + self.target + '"' | |
697 return 'processing-instruction(%s)' % arg | |
698 | |
699 class TextNodeTest(object): | |
161
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
700 """Node test that matches any text event.""" |
137 | 701 __slots__ = [] |
224
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
702 def __call__(self, kind, data, pos, namespaces, variables): |
330
3e749eaa3100
XPath tests should never return event tuples, just values or booleans.
cmlenz
parents:
326
diff
changeset
|
703 return kind is TEXT |
137 | 704 def __repr__(self): |
705 return 'text()' | |
706 | |
707 _nodetest_map = {'comment': CommentNodeTest, 'node': NodeTest, | |
708 'processing-instruction': ProcessingInstructionNodeTest, | |
709 'text': TextNodeTest} | |
710 | |
711 # Functions | |
712 | |
155
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
713 class Function(object): |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
714 """Base class for function nodes in XPath expressions.""" |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
715 |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
716 class BooleanFunction(Function): |
161
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
717 """The `boolean` function, which converts its argument to a boolean |
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
718 value. |
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
719 """ |
155
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
720 __slots__ = ['expr'] |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
721 def __init__(self, expr): |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
722 self.expr = expr |
224
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
723 def __call__(self, kind, data, pos, namespaces, variables): |
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
724 val = self.expr(kind, data, pos, namespaces, variables) |
518
c1f2a859fd75
Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents:
516
diff
changeset
|
725 return as_bool(val) |
155
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
726 def __repr__(self): |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
727 return 'boolean(%r)' % self.expr |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
728 |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
729 class CeilingFunction(Function): |
161
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
730 """The `ceiling` function, which returns the nearest lower integer number |
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
731 for the given number. |
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
732 """ |
155
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
733 __slots__ = ['number'] |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
734 def __init__(self, number): |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
735 self.number = number |
224
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
736 def __call__(self, kind, data, pos, namespaces, variables): |
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
737 number = self.number(kind, data, pos, namespaces, variables) |
518
c1f2a859fd75
Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents:
516
diff
changeset
|
738 return ceil(as_float(number)) |
155
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
739 def __repr__(self): |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
740 return 'ceiling(%r)' % self.number |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
741 |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
742 class ConcatFunction(Function): |
161
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
743 """The `concat` function, which concatenates (joins) the variable number of |
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
744 strings it gets as arguments. |
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
745 """ |
155
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
746 __slots__ = ['exprs'] |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
747 def __init__(self, *exprs): |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
748 self.exprs = exprs |
224
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
749 def __call__(self, kind, data, pos, namespaces, variables): |
155
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
750 strings = [] |
224
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
751 for item in [expr(kind, data, pos, namespaces, variables) |
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
752 for expr in self.exprs]: |
518
c1f2a859fd75
Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents:
516
diff
changeset
|
753 strings.append(as_string(item)) |
155
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
754 return u''.join(strings) |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
755 def __repr__(self): |
169 | 756 return 'concat(%s)' % ', '.join([repr(expr) for expr in self.exprs]) |
155
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
757 |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
758 class ContainsFunction(Function): |
161
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
759 """The `contains` function, which returns whether a string contains a given |
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
760 substring. |
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
761 """ |
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
762 __slots__ = ['string1', 'string2'] |
155
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
763 def __init__(self, string1, string2): |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
764 self.string1 = string1 |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
765 self.string2 = string2 |
224
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
766 def __call__(self, kind, data, pos, namespaces, variables): |
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
767 string1 = self.string1(kind, data, pos, namespaces, variables) |
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
768 string2 = self.string2(kind, data, pos, namespaces, variables) |
518
c1f2a859fd75
Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents:
516
diff
changeset
|
769 return as_string(string2) in as_string(string1) |
155
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
770 def __repr__(self): |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
771 return 'contains(%r, %r)' % (self.string1, self.string2) |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
772 |
534
57b5d5138f1a
Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents:
518
diff
changeset
|
773 class MatchesFunction(Function): |
57b5d5138f1a
Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents:
518
diff
changeset
|
774 """The `matches` function, which returns whether a string matches a regular |
57b5d5138f1a
Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents:
518
diff
changeset
|
775 expression. |
57b5d5138f1a
Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents:
518
diff
changeset
|
776 """ |
57b5d5138f1a
Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents:
518
diff
changeset
|
777 __slots__ = ['string1', 'string2'] |
57b5d5138f1a
Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents:
518
diff
changeset
|
778 flag_mapping = {'s': re.S, 'm': re.M, 'i': re.I, 'x': re.X} |
57b5d5138f1a
Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents:
518
diff
changeset
|
779 |
57b5d5138f1a
Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents:
518
diff
changeset
|
780 def __init__(self, string1, string2, flags=''): |
57b5d5138f1a
Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents:
518
diff
changeset
|
781 self.string1 = string1 |
57b5d5138f1a
Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents:
518
diff
changeset
|
782 self.string2 = string2 |
57b5d5138f1a
Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents:
518
diff
changeset
|
783 self.flags = self._map_flags(flags) |
57b5d5138f1a
Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents:
518
diff
changeset
|
784 def __call__(self, kind, data, pos, namespaces, variables): |
57b5d5138f1a
Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents:
518
diff
changeset
|
785 string1 = as_string(self.string1(kind, data, pos, namespaces, variables)) |
57b5d5138f1a
Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents:
518
diff
changeset
|
786 string2 = as_string(self.string2(kind, data, pos, namespaces, variables)) |
57b5d5138f1a
Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents:
518
diff
changeset
|
787 return re.search(string2, string1, self.flags) |
57b5d5138f1a
Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents:
518
diff
changeset
|
788 def _map_flags(self, flags): |
593 | 789 return reduce(operator.or_, |
534
57b5d5138f1a
Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents:
518
diff
changeset
|
790 [self.flag_map[flag] for flag in flags], re.U) |
57b5d5138f1a
Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents:
518
diff
changeset
|
791 def __repr__(self): |
57b5d5138f1a
Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents:
518
diff
changeset
|
792 return 'contains(%r, %r)' % (self.string1, self.string2) |
57b5d5138f1a
Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents:
518
diff
changeset
|
793 |
155
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
794 class FalseFunction(Function): |
161
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
795 """The `false` function, which always returns the boolean `false` value.""" |
155
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
796 __slots__ = [] |
224
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
797 def __call__(self, kind, data, pos, namespaces, variables): |
155
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
798 return False |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
799 def __repr__(self): |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
800 return 'false()' |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
801 |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
802 class FloorFunction(Function): |
161
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
803 """The `ceiling` function, which returns the nearest higher integer number |
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
804 for the given number. |
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
805 """ |
155
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
806 __slots__ = ['number'] |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
807 def __init__(self, number): |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
808 self.number = number |
224
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
809 def __call__(self, kind, data, pos, namespaces, variables): |
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
810 number = self.number(kind, data, pos, namespaces, variables) |
518
c1f2a859fd75
Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents:
516
diff
changeset
|
811 return floor(as_float(number)) |
155
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
812 def __repr__(self): |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
813 return 'floor(%r)' % self.number |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
814 |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
815 class LocalNameFunction(Function): |
161
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
816 """The `local-name` function, which returns the local name of the current |
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
817 element. |
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
818 """ |
137 | 819 __slots__ = [] |
224
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
820 def __call__(self, kind, data, pos, namespaces, variables): |
137 | 821 if kind is START: |
234 | 822 return data[0].localname |
137 | 823 def __repr__(self): |
824 return 'local-name()' | |
825 | |
155
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
826 class NameFunction(Function): |
161
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
827 """The `name` function, which returns the qualified name of the current |
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
828 element. |
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
829 """ |
137 | 830 __slots__ = [] |
224
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
831 def __call__(self, kind, data, pos, namespaces, variables): |
137 | 832 if kind is START: |
234 | 833 return data[0] |
137 | 834 def __repr__(self): |
835 return 'name()' | |
836 | |
155
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
837 class NamespaceUriFunction(Function): |
161
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
838 """The `namespace-uri` function, which returns the namespace URI of the |
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
839 current element. |
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
840 """ |
137 | 841 __slots__ = [] |
224
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
842 def __call__(self, kind, data, pos, namespaces, variables): |
137 | 843 if kind is START: |
234 | 844 return data[0].namespace |
137 | 845 def __repr__(self): |
846 return 'namespace-uri()' | |
847 | |
155
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
848 class NotFunction(Function): |
161
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
849 """The `not` function, which returns the negated boolean value of its |
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
850 argument. |
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
851 """ |
137 | 852 __slots__ = ['expr'] |
853 def __init__(self, expr): | |
854 self.expr = expr | |
224
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
855 def __call__(self, kind, data, pos, namespaces, variables): |
518
c1f2a859fd75
Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents:
516
diff
changeset
|
856 return not as_bool(self.expr(kind, data, pos, namespaces, variables)) |
137 | 857 def __repr__(self): |
858 return 'not(%s)' % self.expr | |
859 | |
155
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
860 class NormalizeSpaceFunction(Function): |
161
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
861 """The `normalize-space` function, which removes leading and trailing |
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
862 whitespace in the given string, and replaces multiple adjacent whitespace |
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
863 characters inside the string with a single space. |
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
864 """ |
155
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
865 __slots__ = ['expr'] |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
866 _normalize = re.compile(r'\s{2,}').sub |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
867 def __init__(self, expr): |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
868 self.expr = expr |
224
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
869 def __call__(self, kind, data, pos, namespaces, variables): |
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
870 string = self.expr(kind, data, pos, namespaces, variables) |
518
c1f2a859fd75
Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents:
516
diff
changeset
|
871 return self._normalize(' ', as_string(string).strip()) |
155
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
872 def __repr__(self): |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
873 return 'normalize-space(%s)' % repr(self.expr) |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
874 |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
875 class NumberFunction(Function): |
161
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
876 """The `number` function that converts its argument to a number.""" |
155
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
877 __slots__ = ['expr'] |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
878 def __init__(self, expr): |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
879 self.expr = expr |
224
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
880 def __call__(self, kind, data, pos, namespaces, variables): |
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
881 val = self.expr(kind, data, pos, namespaces, variables) |
518
c1f2a859fd75
Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents:
516
diff
changeset
|
882 return as_float(val) |
155
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
883 def __repr__(self): |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
884 return 'number(%r)' % self.expr |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
885 |
162
456039594db9
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
886 class RoundFunction(Function): |
456039594db9
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
887 """The `round` function, which returns the nearest integer number for the |
456039594db9
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
888 given number. |
456039594db9
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
889 """ |
456039594db9
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
890 __slots__ = ['number'] |
456039594db9
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
891 def __init__(self, number): |
456039594db9
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
892 self.number = number |
224
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
893 def __call__(self, kind, data, pos, namespaces, variables): |
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
894 number = self.number(kind, data, pos, namespaces, variables) |
518
c1f2a859fd75
Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents:
516
diff
changeset
|
895 return round(as_float(number)) |
162
456039594db9
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
896 def __repr__(self): |
456039594db9
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
897 return 'round(%r)' % self.number |
456039594db9
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
898 |
155
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
899 class StartsWithFunction(Function): |
161
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
900 """The `starts-with` function that returns whether one string starts with |
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
901 a given substring. |
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
902 """ |
155
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
903 __slots__ = ['string1', 'string2'] |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
904 def __init__(self, string1, string2): |
282
24b3cbbc1b1b
Fix `starts-with()` XPath function so that it actually compares the two strings. Closes #61.
cmlenz
parents:
259
diff
changeset
|
905 self.string1 = string1 |
155
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
906 self.string2 = string2 |
224
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
907 def __call__(self, kind, data, pos, namespaces, variables): |
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
908 string1 = self.string1(kind, data, pos, namespaces, variables) |
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
909 string2 = self.string2(kind, data, pos, namespaces, variables) |
518
c1f2a859fd75
Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents:
516
diff
changeset
|
910 return as_string(string1).startswith(as_string(string2)) |
155
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
911 def __repr__(self): |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
912 return 'starts-with(%r, %r)' % (self.string1, self.string2) |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
913 |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
914 class StringLengthFunction(Function): |
161
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
915 """The `string-length` function that returns the length of the given |
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
916 string. |
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
917 """ |
155
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
918 __slots__ = ['expr'] |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
919 def __init__(self, expr): |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
920 self.expr = expr |
224
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
921 def __call__(self, kind, data, pos, namespaces, variables): |
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
922 string = self.expr(kind, data, pos, namespaces, variables) |
518
c1f2a859fd75
Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents:
516
diff
changeset
|
923 return len(as_string(string)) |
155
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
924 def __repr__(self): |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
925 return 'string-length(%r)' % self.expr |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
926 |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
927 class SubstringFunction(Function): |
161
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
928 """The `substring` function that returns the part of a string that starts |
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
929 at the given offset, and optionally limited to the given length. |
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
930 """ |
155
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
931 __slots__ = ['string', 'start', 'length'] |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
932 def __init__(self, string, start, length=None): |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
933 self.string = string |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
934 self.start = start |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
935 self.length = length |
224
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
936 def __call__(self, kind, data, pos, namespaces, variables): |
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
937 string = self.string(kind, data, pos, namespaces, variables) |
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
938 start = self.start(kind, data, pos, namespaces, variables) |
155
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
939 length = 0 |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
940 if self.length is not None: |
224
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
941 length = self.length(kind, data, pos, namespaces, variables) |
518
c1f2a859fd75
Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents:
516
diff
changeset
|
942 return string[as_long(start):len(as_string(string)) - as_long(length)] |
155
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
943 def __repr__(self): |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
944 if self.length is not None: |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
945 return 'substring(%r, %r, %r)' % (self.string, self.start, |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
946 self.length) |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
947 else: |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
948 return 'substring(%r, %r)' % (self.string, self.start) |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
949 |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
950 class SubstringAfterFunction(Function): |
161
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
951 """The `substring-after` function that returns the part of a string that |
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
952 is found after the given substring. |
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
953 """ |
155
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
954 __slots__ = ['string1', 'string2'] |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
955 def __init__(self, string1, string2): |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
956 self.string1 = string1 |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
957 self.string2 = string2 |
224
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
958 def __call__(self, kind, data, pos, namespaces, variables): |
518
c1f2a859fd75
Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents:
516
diff
changeset
|
959 string1 = as_string(self.string1(kind, data, pos, namespaces, variables)) |
c1f2a859fd75
Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents:
516
diff
changeset
|
960 string2 = as_string(self.string2(kind, data, pos, namespaces, variables)) |
155
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
961 index = string1.find(string2) |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
962 if index >= 0: |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
963 return string1[index + len(string2):] |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
964 return u'' |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
965 def __repr__(self): |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
966 return 'substring-after(%r, %r)' % (self.string1, self.string2) |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
967 |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
968 class SubstringBeforeFunction(Function): |
161
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
969 """The `substring-before` function that returns the part of a string that |
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
970 is found before the given substring. |
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
971 """ |
155
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
972 __slots__ = ['string1', 'string2'] |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
973 def __init__(self, string1, string2): |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
974 self.string1 = string1 |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
975 self.string2 = string2 |
224
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
976 def __call__(self, kind, data, pos, namespaces, variables): |
518
c1f2a859fd75
Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents:
516
diff
changeset
|
977 string1 = as_string(self.string1(kind, data, pos, namespaces, variables)) |
c1f2a859fd75
Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents:
516
diff
changeset
|
978 string2 = as_string(self.string2(kind, data, pos, namespaces, variables)) |
155
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
979 index = string1.find(string2) |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
980 if index >= 0: |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
981 return string1[:index] |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
982 return u'' |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
983 def __repr__(self): |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
984 return 'substring-after(%r, %r)' % (self.string1, self.string2) |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
985 |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
986 class TranslateFunction(Function): |
161
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
987 """The `translate` function that translates a set of characters in a |
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
988 string to target set of characters. |
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
989 """ |
155
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
990 __slots__ = ['string', 'fromchars', 'tochars'] |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
991 def __init__(self, string, fromchars, tochars): |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
992 self.string = string |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
993 self.fromchars = fromchars |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
994 self.tochars = tochars |
224
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
995 def __call__(self, kind, data, pos, namespaces, variables): |
518
c1f2a859fd75
Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents:
516
diff
changeset
|
996 string = as_string(self.string(kind, data, pos, namespaces, variables)) |
c1f2a859fd75
Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents:
516
diff
changeset
|
997 fromchars = as_string(self.fromchars(kind, data, pos, namespaces, variables)) |
c1f2a859fd75
Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents:
516
diff
changeset
|
998 tochars = as_string(self.tochars(kind, data, pos, namespaces, variables)) |
155
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
999 table = dict(zip([ord(c) for c in fromchars], |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1000 [ord(c) for c in tochars])) |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1001 return string.translate(table) |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1002 def __repr__(self): |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1003 return 'translate(%r, %r, %r)' % (self.string, self.fromchars, |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1004 self.tochars) |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1005 |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1006 class TrueFunction(Function): |
161
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1007 """The `true` function, which always returns the boolean `true` value.""" |
155
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1008 __slots__ = [] |
224
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1009 def __call__(self, kind, data, pos, namespaces, variables): |
155
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1010 return True |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1011 def __repr__(self): |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1012 return 'true()' |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1013 |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1014 _function_map = {'boolean': BooleanFunction, 'ceiling': CeilingFunction, |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1015 'concat': ConcatFunction, 'contains': ContainsFunction, |
534
57b5d5138f1a
Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents:
518
diff
changeset
|
1016 'matches': MatchesFunction, 'false': FalseFunction, 'floor': |
57b5d5138f1a
Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents:
518
diff
changeset
|
1017 FloorFunction, 'local-name': LocalNameFunction, 'name': |
57b5d5138f1a
Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents:
518
diff
changeset
|
1018 NameFunction, 'namespace-uri': NamespaceUriFunction, |
155
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1019 'normalize-space': NormalizeSpaceFunction, 'not': NotFunction, |
162
456039594db9
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1020 'number': NumberFunction, 'round': RoundFunction, |
534
57b5d5138f1a
Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents:
518
diff
changeset
|
1021 'starts-with': StartsWithFunction, 'string-length': |
57b5d5138f1a
Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents:
518
diff
changeset
|
1022 StringLengthFunction, 'substring': SubstringFunction, |
57b5d5138f1a
Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents:
518
diff
changeset
|
1023 'substring-after': SubstringAfterFunction, 'substring-before': |
57b5d5138f1a
Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents:
518
diff
changeset
|
1024 SubstringBeforeFunction, 'translate': TranslateFunction, |
57b5d5138f1a
Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents:
518
diff
changeset
|
1025 'true': TrueFunction} |
137 | 1026 |
179
13909179e5e1
Implemented support for XPath variables in predicates (#31).
cmlenz
parents:
169
diff
changeset
|
1027 # Literals & Variables |
137 | 1028 |
155
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1029 class Literal(object): |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1030 """Abstract base class for literal nodes.""" |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1031 |
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1032 class StringLiteral(Literal): |
161
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1033 """A string literal node.""" |
137 | 1034 __slots__ = ['text'] |
1035 def __init__(self, text): | |
1036 self.text = text | |
224
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1037 def __call__(self, kind, data, pos, namespaces, variables): |
228
a5b38b459cbb
Add support for position predicates in XPath expressions.
cmlenz
parents:
224
diff
changeset
|
1038 return self.text |
137 | 1039 def __repr__(self): |
1040 return '"%s"' % self.text | |
1041 | |
155
9a5aedda1099
* String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents:
145
diff
changeset
|
1042 class NumberLiteral(Literal): |
161
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1043 """A number literal node.""" |
137 | 1044 __slots__ = ['number'] |
1045 def __init__(self, number): | |
1046 self.number = number | |
224
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1047 def __call__(self, kind, data, pos, namespaces, variables): |
228
a5b38b459cbb
Add support for position predicates in XPath expressions.
cmlenz
parents:
224
diff
changeset
|
1048 return self.number |
137 | 1049 def __repr__(self): |
1050 return str(self.number) | |
1051 | |
179
13909179e5e1
Implemented support for XPath variables in predicates (#31).
cmlenz
parents:
169
diff
changeset
|
1052 class VariableReference(Literal): |
13909179e5e1
Implemented support for XPath variables in predicates (#31).
cmlenz
parents:
169
diff
changeset
|
1053 """A variable reference node.""" |
13909179e5e1
Implemented support for XPath variables in predicates (#31).
cmlenz
parents:
169
diff
changeset
|
1054 __slots__ = ['name'] |
13909179e5e1
Implemented support for XPath variables in predicates (#31).
cmlenz
parents:
169
diff
changeset
|
1055 def __init__(self, name): |
13909179e5e1
Implemented support for XPath variables in predicates (#31).
cmlenz
parents:
169
diff
changeset
|
1056 self.name = name |
224
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1057 def __call__(self, kind, data, pos, namespaces, variables): |
228
a5b38b459cbb
Add support for position predicates in XPath expressions.
cmlenz
parents:
224
diff
changeset
|
1058 return variables.get(self.name) |
179
13909179e5e1
Implemented support for XPath variables in predicates (#31).
cmlenz
parents:
169
diff
changeset
|
1059 def __repr__(self): |
215 | 1060 return str(self.name) |
179
13909179e5e1
Implemented support for XPath variables in predicates (#31).
cmlenz
parents:
169
diff
changeset
|
1061 |
137 | 1062 # Operators |
1063 | |
1064 class AndOperator(object): | |
161
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1065 """The boolean operator `and`.""" |
137 | 1066 __slots__ = ['lval', 'rval'] |
1067 def __init__(self, lval, rval): | |
1068 self.lval = lval | |
1069 self.rval = rval | |
224
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1070 def __call__(self, kind, data, pos, namespaces, variables): |
518
c1f2a859fd75
Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents:
516
diff
changeset
|
1071 lval = as_bool(self.lval(kind, data, pos, namespaces, variables)) |
161
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1072 if not lval: |
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1073 return False |
224
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1074 rval = self.rval(kind, data, pos, namespaces, variables) |
518
c1f2a859fd75
Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents:
516
diff
changeset
|
1075 return as_bool(rval) |
137 | 1076 def __repr__(self): |
161
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1077 return '%s and %s' % (self.lval, self.rval) |
137 | 1078 |
161
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1079 class EqualsOperator(object): |
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1080 """The equality operator `=`.""" |
137 | 1081 __slots__ = ['lval', 'rval'] |
1082 def __init__(self, lval, rval): | |
1083 self.lval = lval | |
1084 self.rval = rval | |
224
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1085 def __call__(self, kind, data, pos, namespaces, variables): |
518
c1f2a859fd75
Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents:
516
diff
changeset
|
1086 lval = as_scalar(self.lval(kind, data, pos, namespaces, variables)) |
c1f2a859fd75
Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents:
516
diff
changeset
|
1087 rval = as_scalar(self.rval(kind, data, pos, namespaces, variables)) |
161
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1088 return lval == rval |
137 | 1089 def __repr__(self): |
161
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1090 return '%s=%s' % (self.lval, self.rval) |
137 | 1091 |
161
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1092 class NotEqualsOperator(object): |
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1093 """The equality operator `!=`.""" |
137 | 1094 __slots__ = ['lval', 'rval'] |
1095 def __init__(self, lval, rval): | |
1096 self.lval = lval | |
1097 self.rval = rval | |
224
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1098 def __call__(self, kind, data, pos, namespaces, variables): |
518
c1f2a859fd75
Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents:
516
diff
changeset
|
1099 lval = as_scalar(self.lval(kind, data, pos, namespaces, variables)) |
c1f2a859fd75
Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents:
516
diff
changeset
|
1100 rval = as_scalar(self.rval(kind, data, pos, namespaces, variables)) |
161
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1101 return lval != rval |
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1102 def __repr__(self): |
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1103 return '%s!=%s' % (self.lval, self.rval) |
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1104 |
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1105 class OrOperator(object): |
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1106 """The boolean operator `or`.""" |
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1107 __slots__ = ['lval', 'rval'] |
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1108 def __init__(self, lval, rval): |
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1109 self.lval = lval |
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1110 self.rval = rval |
224
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1111 def __call__(self, kind, data, pos, namespaces, variables): |
518
c1f2a859fd75
Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents:
516
diff
changeset
|
1112 lval = as_bool(self.lval(kind, data, pos, namespaces, variables)) |
161
7b1f07496bf7
Various docstring additions and other cosmetic changes.
cmlenz
parents:
156
diff
changeset
|
1113 if lval: |
137 | 1114 return True |
224
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1115 rval = self.rval(kind, data, pos, namespaces, variables) |
518
c1f2a859fd75
Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents:
516
diff
changeset
|
1116 return as_bool(rval) |
137 | 1117 def __repr__(self): |
1118 return '%s or %s' % (self.lval, self.rval) | |
1119 | |
162
456039594db9
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1120 class GreaterThanOperator(object): |
456039594db9
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1121 """The relational operator `>` (greater than).""" |
456039594db9
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1122 __slots__ = ['lval', 'rval'] |
456039594db9
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1123 def __init__(self, lval, rval): |
456039594db9
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1124 self.lval = lval |
456039594db9
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1125 self.rval = rval |
224
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1126 def __call__(self, kind, data, pos, namespaces, variables): |
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1127 lval = self.lval(kind, data, pos, namespaces, variables) |
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1128 rval = self.rval(kind, data, pos, namespaces, variables) |
518
c1f2a859fd75
Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents:
516
diff
changeset
|
1129 return as_float(lval) > as_float(rval) |
162
456039594db9
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1130 def __repr__(self): |
456039594db9
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1131 return '%s>%s' % (self.lval, self.rval) |
456039594db9
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1132 |
456039594db9
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1133 class GreaterThanOrEqualOperator(object): |
456039594db9
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1134 """The relational operator `>=` (greater than or equal).""" |
456039594db9
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1135 __slots__ = ['lval', 'rval'] |
456039594db9
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1136 def __init__(self, lval, rval): |
456039594db9
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1137 self.lval = lval |
456039594db9
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1138 self.rval = rval |
224
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1139 def __call__(self, kind, data, pos, namespaces, variables): |
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1140 lval = self.lval(kind, data, pos, namespaces, variables) |
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1141 rval = self.rval(kind, data, pos, namespaces, variables) |
518
c1f2a859fd75
Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents:
516
diff
changeset
|
1142 return as_float(lval) >= as_float(rval) |
162
456039594db9
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1143 def __repr__(self): |
456039594db9
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1144 return '%s>=%s' % (self.lval, self.rval) |
456039594db9
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1145 |
456039594db9
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1146 class LessThanOperator(object): |
456039594db9
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1147 """The relational operator `<` (less than).""" |
456039594db9
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1148 __slots__ = ['lval', 'rval'] |
456039594db9
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1149 def __init__(self, lval, rval): |
456039594db9
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1150 self.lval = lval |
456039594db9
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1151 self.rval = rval |
224
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1152 def __call__(self, kind, data, pos, namespaces, variables): |
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1153 lval = self.lval(kind, data, pos, namespaces, variables) |
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1154 rval = self.rval(kind, data, pos, namespaces, variables) |
518
c1f2a859fd75
Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents:
516
diff
changeset
|
1155 return as_float(lval) < as_float(rval) |
162
456039594db9
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1156 def __repr__(self): |
456039594db9
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1157 return '%s<%s' % (self.lval, self.rval) |
456039594db9
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1158 |
456039594db9
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1159 class LessThanOrEqualOperator(object): |
456039594db9
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1160 """The relational operator `<=` (less than or equal).""" |
456039594db9
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1161 __slots__ = ['lval', 'rval'] |
456039594db9
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1162 def __init__(self, lval, rval): |
456039594db9
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1163 self.lval = lval |
456039594db9
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1164 self.rval = rval |
224
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1165 def __call__(self, kind, data, pos, namespaces, variables): |
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1166 lval = self.lval(kind, data, pos, namespaces, variables) |
90d62225f411
Implement support for namespace prefixes in XPath expressions.
cmlenz
parents:
223
diff
changeset
|
1167 rval = self.rval(kind, data, pos, namespaces, variables) |
518
c1f2a859fd75
Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents:
516
diff
changeset
|
1168 return as_float(lval) <= as_float(rval) |
162
456039594db9
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1169 def __repr__(self): |
456039594db9
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1170 return '%s<=%s' % (self.lval, self.rval) |
456039594db9
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1171 |
456039594db9
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1172 _operator_map = {'=': EqualsOperator, '!=': NotEqualsOperator, |
456039594db9
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1173 '>': GreaterThanOperator, '>=': GreaterThanOrEqualOperator, |
456039594db9
Implement the XPath relational operators and the `round()` function.
cmlenz
parents:
161
diff
changeset
|
1174 '<': LessThanOperator, '>=': LessThanOrEqualOperator} |
333
e5ba8c5a30fe
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
|
1175 |
e5ba8c5a30fe
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
|
1176 |
e5ba8c5a30fe
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
|
1177 _DOTSLASHSLASH = (DESCENDANT_OR_SELF, PrincipalTypeTest(None), ()) |