annotate genshi/path.py @ 498:700e7b47bf2b trunk

A couple of minor doc refinements.
author cmlenz
date Fri, 01 Jun 2007 12:22:50 +0000
parents 073640758a42
children 317a7f4e3c69
rev   line source
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
1 # -*- coding: utf-8 -*-
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
2 #
66
59eb24184e9c Switch copyright to Edgewall and URLs to markup.edgewall.org.
cmlenz
parents: 61
diff changeset
3 # Copyright (C) 2006 Edgewall Software
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
4 # All rights reserved.
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
5 #
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
6 # This software is licensed as described in the file COPYING, which
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
7 # you should have received as part of this distribution. The terms
230
84168828b074 Renamed Markup to Genshi in repository.
cmlenz
parents: 228
diff changeset
8 # are also available at http://genshi.edgewall.org/wiki/License.
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
9 #
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
10 # This software consists of voluntary contributions made by many
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
11 # individuals. For the exact contribution history, see the revision
230
84168828b074 Renamed Markup to Genshi in repository.
cmlenz
parents: 228
diff changeset
12 # history and logs, available at http://genshi.edgewall.org/log/.
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
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
84168828b074 Renamed Markup to Genshi in repository.
cmlenz
parents: 228
diff changeset
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>
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
18 ... <items count="2">
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>
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
25 ... </items>
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
26 ... </doc>''')
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
27 >>> print doc.select('items/item[@status="closed"]/summary/text()')
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
28 Bar
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
29
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
30 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
31 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
32 """
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
33
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
34 from math import ceil, floor
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
35 import re
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
36
230
84168828b074 Renamed Markup to Genshi in repository.
cmlenz
parents: 228
diff changeset
37 from genshi.core import Stream, Attrs, Namespace, QName
84168828b074 Renamed Markup to Genshi in repository.
cmlenz
parents: 228
diff changeset
38 from genshi.core import START, END, TEXT, COMMENT, PI
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
39
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
40 __all__ = ['Path', 'PathSyntaxError']
425
073640758a42 Try to use proper reStructuredText for docstrings throughout.
cmlenz
parents: 386
diff changeset
41 __docformat__ = 'restructuredtext en'
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
42
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
43
114
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
44 class Axis(object):
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
45 """Defines constants for the various supported XPath axes."""
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
46
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
47 ATTRIBUTE = 'attribute'
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
48 CHILD = 'child'
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
49 DESCENDANT = 'descendant'
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
50 DESCENDANT_OR_SELF = 'descendant-or-self'
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
51 SELF = 'self'
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
52
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
53 def forname(cls, name):
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
54 """Return the axis constant for the given name, or `None` if no such
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
55 axis was defined.
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
56 """
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
57 return getattr(cls, name.upper().replace('-', '_'), None)
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
58 forname = classmethod(forname)
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
59
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
60
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
61 ATTRIBUTE = Axis.ATTRIBUTE
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
62 CHILD = Axis.CHILD
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
63 DESCENDANT = Axis.DESCENDANT
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
64 DESCENDANT_OR_SELF = Axis.DESCENDANT_OR_SELF
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
65 SELF = Axis.SELF
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
66
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
67
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
68 class Path(object):
26
3c1a022be04c * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
69 """Implements basic XPath support on streams.
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
70
26
3c1a022be04c * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
71 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
72 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
73 substream matching that path.
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
74 """
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
75
139
8332287b5508 Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents: 137
diff changeset
76 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
77 """Create the path object from a string.
3c1a022be04c * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
78
425
073640758a42 Try to use proper reStructuredText for docstrings throughout.
cmlenz
parents: 386
diff changeset
79 :param text: the path expression
498
700e7b47bf2b A couple of minor doc refinements.
cmlenz
parents: 425
diff changeset
80 :param filename: the name of the file in which the path expression was
700e7b47bf2b A couple of minor doc refinements.
cmlenz
parents: 425
diff changeset
81 found (used in error messages)
700e7b47bf2b A couple of minor doc refinements.
cmlenz
parents: 425
diff changeset
82 :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
83 """
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
84 self.source = text
139
8332287b5508 Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents: 137
diff changeset
85 self.paths = PathParser(text, filename, lineno).parse()
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
86
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
87 def __repr__(self):
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
88 paths = []
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
89 for path in self.paths:
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
90 steps = []
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
91 for axis, nodetest, predicates in path:
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
92 steps.append('%s::%s' % (axis, nodetest))
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
93 for predicate in predicates:
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
94 steps[-1] += '[%s]' % predicate
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
95 paths.append('/'.join(steps))
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
96 return '<%s "%s">' % (self.__class__.__name__, '|'.join(paths))
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
97
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
98 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
99 """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
100
3c1a022be04c * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
101 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
102
230
84168828b074 Renamed Markup to Genshi in repository.
cmlenz
parents: 228
diff changeset
103 >>> from genshi.input import XML
33
1fdb7054fb42 Add doctests for path module.
cmlenz
parents: 27
diff changeset
104 >>> xml = XML('<root><elem><child>Text</child></elem></root>')
61
448792ab1303 Use a different namespace than Kid uses.
cmlenz
parents: 38
diff changeset
105
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
106 >>> print Path('.//child').select(xml)
33
1fdb7054fb42 Add doctests for path module.
cmlenz
parents: 27
diff changeset
107 <child>Text</child>
1fdb7054fb42 Add doctests for path module.
cmlenz
parents: 27
diff changeset
108
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
109 >>> print Path('.//child/text()').select(xml)
33
1fdb7054fb42 Add doctests for path module.
cmlenz
parents: 27
diff changeset
110 Text
1fdb7054fb42 Add doctests for path module.
cmlenz
parents: 27
diff changeset
111
425
073640758a42 Try to use proper reStructuredText for docstrings throughout.
cmlenz
parents: 386
diff changeset
112 :param stream: the stream to select from
073640758a42 Try to use proper reStructuredText for docstrings throughout.
cmlenz
parents: 386
diff changeset
113 :param namespaces: (optional) a mapping of namespace prefixes to URIs
073640758a42 Try to use proper reStructuredText for docstrings throughout.
cmlenz
parents: 386
diff changeset
114 :param variables: (optional) a mapping of variable names to values
073640758a42 Try to use proper reStructuredText for docstrings throughout.
cmlenz
parents: 386
diff changeset
115 :return: the substream matching the path, or an empty stream
498
700e7b47bf2b A couple of minor doc refinements.
cmlenz
parents: 425
diff changeset
116 :rtype: `Stream`
26
3c1a022be04c * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
117 """
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
118 if namespaces is None:
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
119 namespaces = {}
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
120 if variables is None:
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
121 variables = {}
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
122 stream = iter(stream)
26
3c1a022be04c * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
123 def _generate():
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
124 test = self.test()
305
60111a041e7c Various performance-oriented tweaks.
cmlenz
parents: 282
diff changeset
125 for event in stream:
60111a041e7c Various performance-oriented tweaks.
cmlenz
parents: 282
diff changeset
126 result = test(event, namespaces, variables)
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
127 if result is True:
305
60111a041e7c Various performance-oriented tweaks.
cmlenz
parents: 282
diff changeset
128 yield event
330
3e749eaa3100 XPath tests should never return event tuples, just values or booleans.
cmlenz
parents: 326
diff changeset
129 if event[0] is START:
3e749eaa3100 XPath tests should never return event tuples, just values or booleans.
cmlenz
parents: 326
diff changeset
130 depth = 1
3e749eaa3100 XPath tests should never return event tuples, just values or booleans.
cmlenz
parents: 326
diff changeset
131 while depth > 0:
3e749eaa3100 XPath tests should never return event tuples, just values or booleans.
cmlenz
parents: 326
diff changeset
132 subevent = stream.next()
3e749eaa3100 XPath tests should never return event tuples, just values or booleans.
cmlenz
parents: 326
diff changeset
133 if subevent[0] is START:
3e749eaa3100 XPath tests should never return event tuples, just values or booleans.
cmlenz
parents: 326
diff changeset
134 depth += 1
3e749eaa3100 XPath tests should never return event tuples, just values or booleans.
cmlenz
parents: 326
diff changeset
135 elif subevent[0] is END:
3e749eaa3100 XPath tests should never return event tuples, just values or booleans.
cmlenz
parents: 326
diff changeset
136 depth -= 1
3e749eaa3100 XPath tests should never return event tuples, just values or booleans.
cmlenz
parents: 326
diff changeset
137 yield subevent
3e749eaa3100 XPath tests should never return event tuples, just values or booleans.
cmlenz
parents: 326
diff changeset
138 test(subevent, namespaces, variables,
3e749eaa3100 XPath tests should never return event tuples, just values or booleans.
cmlenz
parents: 326
diff changeset
139 updateonly=True)
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
140 elif result:
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
141 yield result
26
3c1a022be04c * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
142 return Stream(_generate())
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
143
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
144 def test(self, ignore_context=False):
26
3c1a022be04c * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
145 """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
146 a specific stream event.
3c1a022be04c * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
147
425
073640758a42 Try to use proper reStructuredText for docstrings throughout.
cmlenz
parents: 386
diff changeset
148 The function returned expects the positional arguments ``event``,
073640758a42 Try to use proper reStructuredText for docstrings throughout.
cmlenz
parents: 386
diff changeset
149 ``namespaces`` and ``variables``. The first is a stream event, while the
305
60111a041e7c Various performance-oriented tweaks.
cmlenz
parents: 282
diff changeset
150 latter two are a mapping of namespace prefixes to URIs, and a mapping
306
095a754f95a8 Minor optimization for XPath evaluation.
cmlenz
parents: 305
diff changeset
151 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
152 accepts an ``updateonly`` keyword argument that default to ``False``. If
073640758a42 Try to use proper reStructuredText for docstrings throughout.
cmlenz
parents: 386
diff changeset
153 it is set to ``True``, the function only updates its internal state,
306
095a754f95a8 Minor optimization for XPath evaluation.
cmlenz
parents: 305
diff changeset
154 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
155
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
156 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
157 example, a `START` or `TEXT` event.) Otherwise, it returns ``None``.
33
1fdb7054fb42 Add doctests for path module.
cmlenz
parents: 27
diff changeset
158
230
84168828b074 Renamed Markup to Genshi in repository.
cmlenz
parents: 228
diff changeset
159 >>> from genshi.input import XML
33
1fdb7054fb42 Add doctests for path module.
cmlenz
parents: 27
diff changeset
160 >>> xml = XML('<root><elem><child id="1"/></elem><child id="2"/></root>')
1fdb7054fb42 Add doctests for path module.
cmlenz
parents: 27
diff changeset
161 >>> test = Path('child').test()
305
60111a041e7c Various performance-oriented tweaks.
cmlenz
parents: 282
diff changeset
162 >>> for event in xml:
60111a041e7c Various performance-oriented tweaks.
cmlenz
parents: 282
diff changeset
163 ... if test(event, {}, {}):
386
c66370dfc41b Unit test fixes for Python 2.3.
cmlenz
parents: 384
diff changeset
164 ... print event[0], repr(event[1])
c66370dfc41b Unit test fixes for Python 2.3.
cmlenz
parents: 384
diff changeset
165 START (QName(u'child'), Attrs([(QName(u'id'), u'2')]))
498
700e7b47bf2b A couple of minor doc refinements.
cmlenz
parents: 425
diff changeset
166
700e7b47bf2b A couple of minor doc refinements.
cmlenz
parents: 425
diff changeset
167 :param ignore_context: if `True`, the path is interpreted like a pattern
700e7b47bf2b A couple of minor doc refinements.
cmlenz
parents: 425
diff changeset
168 in XSLT, meaning for example that it will match
700e7b47bf2b A couple of minor doc refinements.
cmlenz
parents: 425
diff changeset
169 at any depth
700e7b47bf2b A couple of minor doc refinements.
cmlenz
parents: 425
diff changeset
170 :return: a function that can be used to test individual events in a
700e7b47bf2b A couple of minor doc refinements.
cmlenz
parents: 425
diff changeset
171 stream against the path
700e7b47bf2b A couple of minor doc refinements.
cmlenz
parents: 425
diff changeset
172 :rtype: ``function``
26
3c1a022be04c * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
173 """
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
174 paths = [(p, len(p), [0], [], [0] * len(p)) for p in [
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
175 (ignore_context and [_DOTSLASHSLASH] or []) + p for p in self.paths
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
176 ]]
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
177
306
095a754f95a8 Minor optimization for XPath evaluation.
cmlenz
parents: 305
diff changeset
178 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
179 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
180 retval = None
228
a5b38b459cbb Add support for position predicates in XPath expressions.
cmlenz
parents: 224
diff changeset
181 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
182 # 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
183 if kind is END:
228
a5b38b459cbb Add support for position predicates in XPath expressions.
cmlenz
parents: 224
diff changeset
184 if cursors:
a5b38b459cbb Add support for position predicates in XPath expressions.
cmlenz
parents: 224
diff changeset
185 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
186 continue
223
9c199fcb9f31 Fix typo introduced in [272].
cmlenz
parents: 217
diff changeset
187 elif kind is START:
228
a5b38b459cbb Add support for position predicates in XPath expressions.
cmlenz
parents: 224
diff changeset
188 cursors.append(cursors and cursors[-1] or 0)
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
189
306
095a754f95a8 Minor optimization for XPath evaluation.
cmlenz
parents: 305
diff changeset
190 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
191 continue
228
a5b38b459cbb Add support for position predicates in XPath expressions.
cmlenz
parents: 224
diff changeset
192 cursor = cursors[-1]
a5b38b459cbb Add support for position predicates in XPath expressions.
cmlenz
parents: 224
diff changeset
193 depth = len(cursors)
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
194
228
a5b38b459cbb Add support for position predicates in XPath expressions.
cmlenz
parents: 224
diff changeset
195 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
196 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
197
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 ctxtnode = not ignore_context and kind is START \
228
a5b38b459cbb Add support for position predicates in XPath expressions.
cmlenz
parents: 224
diff changeset
199 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
200 matched = None
111
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
201 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
202 # Fetch the next location step
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
203 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
204
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
205 # 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
206 # 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
207 # 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
208 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
209 break
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
210
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
211 # 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
212 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
213
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
214 # Perform the actual node test
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
215 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
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 # 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
218 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
219
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
220 # 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
221 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
222 for predicate in predicates:
228
a5b38b459cbb Add support for position predicates in XPath expressions.
cmlenz
parents: 224
diff changeset
223 pretval = predicate(kind, data, pos, namespaces,
a5b38b459cbb Add support for position predicates in XPath expressions.
cmlenz
parents: 224
diff changeset
224 variables)
a5b38b459cbb Add support for position predicates in XPath expressions.
cmlenz
parents: 224
diff changeset
225 if type(pretval) is float:
a5b38b459cbb Add support for position predicates in XPath expressions.
cmlenz
parents: 224
diff changeset
226 counter[cursor] += 1
a5b38b459cbb Add support for position predicates in XPath expressions.
cmlenz
parents: 224
diff changeset
227 if counter[cursor] != int(pretval):
a5b38b459cbb Add support for position predicates in XPath expressions.
cmlenz
parents: 224
diff changeset
228 pretval = False
a5b38b459cbb Add support for position predicates in XPath expressions.
cmlenz
parents: 224
diff changeset
229 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
230 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
231 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
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 # 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
234 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
235 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
236 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
237 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
238 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
239 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
240 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
241 cursor += 1
228
a5b38b459cbb Add support for position predicates in XPath expressions.
cmlenz
parents: 224
diff changeset
242 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
243 cutoff[:] = []
228
a5b38b459cbb Add support for position predicates in XPath expressions.
cmlenz
parents: 224
diff changeset
244
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
245 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
246 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
247 axis is DESCENDANT_OR_SELF):
228
a5b38b459cbb Add support for position predicates in XPath expressions.
cmlenz
parents: 224
diff changeset
248 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
249
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
250 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
251 # 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
252 # 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
253 # 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
254 # 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
255 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
256
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 # 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
258 # axis isn't "self"
384
5b6e4335ee21 Fix for infinite loop in XPath test. Closes #82.
cmlenz
parents: 364
diff changeset
259 if not matched or last_step or not (
5b6e4335ee21 Fix for infinite loop in XPath test. Closes #82.
cmlenz
parents: 364
diff changeset
260 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
261 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
262
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
263 if (retval or not matched) and kind is START and \
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
264 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
265 # 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
266 # the current element is closed... so we need to move the
114
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
267 # cursor back to the previous closure and retest that
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
268 # 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
269 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
270 in enumerate(steps[:cursor])
215
94120e6b0ce4 A couple of minor XPath fixes.
cmlenz
parents: 211
diff changeset
271 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
272 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
273 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
274 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
275 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
276 break
228
a5b38b459cbb Add support for position predicates in XPath expressions.
cmlenz
parents: 224
diff changeset
277 cursors[-1] = cursor
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
278
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
279 return retval
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
280
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
281 return _test
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
282
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
283
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
284 class PathSyntaxError(Exception):
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
285 """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
286
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
287 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
288 if filename:
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
289 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
290 Exception.__init__(self, message)
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
291 self.filename = filename
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
292 self.lineno = lineno
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
293 self.offset = offset
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
294
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
295
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
296 class PathParser(object):
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
297 """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
298
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
299 _QUOTES = (("'", "'"), ('"', '"'))
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
300 _TOKENS = ('::', ':', '..', '.', '//', '/', '[', ']', '()', '(', ')', '@',
179
13909179e5e1 Implemented support for XPath variables in predicates (#31).
cmlenz
parents: 169
diff changeset
301 '=', '!=', '!', '|', ',', '>=', '>', '<=', '<', '$')
163
9c023c395e44 Support for XPath number literals including decimal places.
cmlenz
parents: 162
diff changeset
302 _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
303 '|'.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
304 ''.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
305
139
8332287b5508 Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents: 137
diff changeset
306 def __init__(self, text, filename=None, lineno=-1):
8332287b5508 Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents: 137
diff changeset
307 self.filename = filename
8332287b5508 Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents: 137
diff changeset
308 self.lineno = lineno
163
9c023c395e44 Support for XPath number literals including decimal places.
cmlenz
parents: 162
diff changeset
309 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
310 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
311 self._tokenize(text)])
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
312 self.pos = 0
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
313
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
314 # Tokenizer
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
315
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
316 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
317 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
318
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
319 def next_token(self):
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
320 self.pos += 1
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
321 return self.tokens[self.pos]
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
322
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
323 def peek_token(self):
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
324 if not self.at_end:
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
325 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
326 return None
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
327
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
328 # Recursive descent parser
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
329
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
330 def parse(self):
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
331 """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
332 tests.
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
333
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
334 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
335 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
336 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
337
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
338 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
339 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
340 """
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
341 paths = [self._location_path()]
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
342 while self.cur_token == '|':
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
343 self.next_token()
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
344 paths.append(self._location_path())
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
345 if not self.at_end:
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
346 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
347 % 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
348 return paths
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
349
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
350 def _location_path(self):
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
351 steps = []
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
352 while True:
215
94120e6b0ce4 A couple of minor XPath fixes.
cmlenz
parents: 211
diff changeset
353 if self.cur_token.startswith('/'):
94120e6b0ce4 A couple of minor XPath fixes.
cmlenz
parents: 211
diff changeset
354 if self.cur_token == '//':
94120e6b0ce4 A couple of minor XPath fixes.
cmlenz
parents: 211
diff changeset
355 steps.append((DESCENDANT_OR_SELF, NodeTest(), []))
94120e6b0ce4 A couple of minor XPath fixes.
cmlenz
parents: 211
diff changeset
356 elif not steps:
94120e6b0ce4 A couple of minor XPath fixes.
cmlenz
parents: 211
diff changeset
357 raise PathSyntaxError('Absolute location paths not '
94120e6b0ce4 A couple of minor XPath fixes.
cmlenz
parents: 211
diff changeset
358 'supported', self.filename,
94120e6b0ce4 A couple of minor XPath fixes.
cmlenz
parents: 211
diff changeset
359 self.lineno)
111
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
360 self.next_token()
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
361
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
362 axis, nodetest, predicates = self._location_step()
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
363 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
364 axis = CHILD
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
365 steps.append((axis, nodetest, predicates))
111
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
366
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
367 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
368 break
111
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
369
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
370 return steps
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
371
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
372 def _location_step(self):
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
373 if self.cur_token == '@':
114
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
374 axis = ATTRIBUTE
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
375 self.next_token()
111
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
376 elif self.cur_token == '.':
114
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
377 axis = SELF
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
378 elif self.cur_token == '..':
139
8332287b5508 Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents: 137
diff changeset
379 raise PathSyntaxError('Unsupported axis "parent"', self.filename,
8332287b5508 Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents: 137
diff changeset
380 self.lineno)
111
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
381 elif self.peek_token() == '::':
114
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
382 axis = Axis.forname(self.cur_token)
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
383 if axis is None:
139
8332287b5508 Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents: 137
diff changeset
384 raise PathSyntaxError('Unsupport axis "%s"' % axis,
8332287b5508 Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents: 137
diff changeset
385 self.filename, 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 self.next_token()
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
388 else:
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
389 axis = None
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
390 nodetest = self._node_test(axis or CHILD)
111
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
391 predicates = []
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
392 while self.cur_token == '[':
111
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
393 predicates.append(self._predicate())
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
394 return axis, nodetest, predicates
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
395
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
396 def _node_test(self, axis=None):
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
397 test = prefix = None
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
398 next_token = self.peek_token()
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
399 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
400 test = self._node_type()
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
401
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
402 elif next_token == ':': # Namespace prefix
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
403 prefix = self.cur_token
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
404 self.next_token()
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
405 localname = self.next_token()
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
406 if localname == '*':
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
407 test = QualifiedPrincipalTypeTest(axis, prefix)
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
408 else:
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
409 test = QualifiedNameTest(axis, prefix, localname)
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
410
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
411 else: # Name test
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
412 if self.cur_token == '*':
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
413 test = PrincipalTypeTest(axis)
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
414 elif self.cur_token == '.':
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
415 test = NodeTest()
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
416 else:
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
417 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
418
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
419 if not self.at_end:
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
420 self.next_token()
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
421 return test
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
422
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
423 def _node_type(self):
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
424 name = self.cur_token
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
425 self.next_token()
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
426
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
427 args = []
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
428 if self.cur_token != '()':
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
429 # The processing-instruction() function optionally accepts the
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
430 # name of the PI as argument, which must be a literal string
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
431 self.next_token() # (
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
432 if self.cur_token != ')':
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
433 string = self.cur_token
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
434 if (string[0], string[-1]) in self._QUOTES:
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
435 string = string[1:-1]
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
436 args.append(string)
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
437
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
438 cls = _nodetest_map.get(name)
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
439 if not cls:
139
8332287b5508 Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents: 137
diff changeset
440 raise PathSyntaxError('%s() not allowed here' % name, self.filename,
8332287b5508 Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents: 137
diff changeset
441 self.lineno)
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
442 return cls(*args)
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
443
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
444 def _predicate(self):
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
445 assert self.cur_token == '['
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
446 self.next_token()
111
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
447 expr = self._or_expr()
121
062e51ad7b19 Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents: 114
diff changeset
448 if self.cur_token != ']':
062e51ad7b19 Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents: 114
diff changeset
449 raise PathSyntaxError('Expected "]" to close predicate, '
139
8332287b5508 Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents: 137
diff changeset
450 'but found "%s"' % self.cur_token,
8332287b5508 Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents: 137
diff changeset
451 self.filename, self.lineno)
111
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
452 if not self.at_end:
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
453 self.next_token()
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
454 return expr
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
455
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
456 def _or_expr(self):
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
457 expr = self._and_expr()
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
458 while self.cur_token == 'or':
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
459 self.next_token()
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
460 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
461 return expr
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
462
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
463 def _and_expr(self):
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
464 expr = self._equality_expr()
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
465 while self.cur_token == 'and':
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
466 self.next_token()
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
467 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
468 return expr
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 _equality_expr(self):
162
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
471 expr = self._relational_expr()
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
472 while self.cur_token in ('=', '!='):
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
473 op = _operator_map[self.cur_token]
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
474 self.next_token()
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
475 expr = op(expr, self._relational_expr())
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
476 return expr
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
477
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
478 def _relational_expr(self):
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
479 expr = self._primary_expr()
162
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
480 while self.cur_token in ('>', '>=', '<', '>='):
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
481 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
482 self.next_token()
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
483 expr = op(expr, self._primary_expr())
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
484 return expr
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
485
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
486 def _primary_expr(self):
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
487 token = self.cur_token
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
488 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
489 self.next_token()
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
490 return StringLiteral(token[1:-1])
163
9c023c395e44 Support for XPath number literals including decimal places.
cmlenz
parents: 162
diff changeset
491 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
492 self.next_token()
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
493 return NumberLiteral(float(token))
179
13909179e5e1 Implemented support for XPath variables in predicates (#31).
cmlenz
parents: 169
diff changeset
494 elif token == '$':
13909179e5e1 Implemented support for XPath variables in predicates (#31).
cmlenz
parents: 169
diff changeset
495 token = self.next_token()
13909179e5e1 Implemented support for XPath variables in predicates (#31).
cmlenz
parents: 169
diff changeset
496 self.next_token()
13909179e5e1 Implemented support for XPath variables in predicates (#31).
cmlenz
parents: 169
diff changeset
497 return VariableReference(token)
121
062e51ad7b19 Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents: 114
diff changeset
498 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
499 return self._function_call()
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
500 else:
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
501 axis = None
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
502 if token == '@':
114
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
503 axis = ATTRIBUTE
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
504 self.next_token()
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
505 return self._node_test(axis)
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
506
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
507 def _function_call(self):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
508 name = self.cur_token
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
509 if self.next_token() == '()':
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
510 args = []
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
511 else:
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
512 assert self.cur_token == '('
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
513 self.next_token()
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
514 args = [self._or_expr()]
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
515 while self.cur_token == ',':
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
516 self.next_token()
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
517 args.append(self._or_expr())
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
518 if not self.cur_token == ')':
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
519 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
520 '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
521 self.filename, self.lineno)
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
522 self.next_token()
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
523 cls = _function_map.get(name)
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
524 if not cls:
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
525 raise PathSyntaxError('Unsupported function "%s"' % name,
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
526 self.filename, self.lineno)
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
527 return cls(*args)
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
528
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
529
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
530 # Node tests
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
531
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
532 class PrincipalTypeTest(object):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
533 """Node test that matches any event with the given principal type."""
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
534 __slots__ = ['principal_type']
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
535 def __init__(self, principal_type):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
536 self.principal_type = principal_type
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
537 def __call__(self, kind, data, pos, namespaces, variables):
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
538 if kind is START:
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
539 if self.principal_type is ATTRIBUTE:
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
540 return data[1] or None
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
541 else:
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
542 return True
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
543 def __repr__(self):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
544 return '*'
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
545
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
546 class QualifiedPrincipalTypeTest(object):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
547 """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
548 specific namespace."""
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
549 __slots__ = ['principal_type', 'prefix']
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
550 def __init__(self, principal_type, prefix):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
551 self.principal_type = principal_type
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
552 self.prefix = prefix
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
553 def __call__(self, kind, data, pos, namespaces, variables):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
554 namespace = Namespace(namespaces.get(self.prefix))
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
555 if kind is START:
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
556 if self.principal_type is ATTRIBUTE and data[1]:
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
557 return Attrs([(name, value) for name, value in data[1]
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
558 if name in namespace]) or None
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
559 else:
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
560 return data[0] in namespace
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
561 def __repr__(self):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
562 return '%s:*' % self.prefix
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
563
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
564 class LocalNameTest(object):
364
7dabedbb53fb Fix for #77: match templates were matching their own output.
cmlenz
parents: 363
diff changeset
565 """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
566 local name.
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
567 """
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
568 __slots__ = ['principal_type', 'name']
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
569 def __init__(self, principal_type, name):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
570 self.principal_type = principal_type
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
571 self.name = name
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
572 def __call__(self, kind, data, pos, namespaces, variables):
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
573 if kind is START:
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
574 if self.principal_type is ATTRIBUTE and self.name in data[1]:
234
39c424b80edd * Minor simplification of XPath engine.
cmlenz
parents: 230
diff changeset
575 return data[1].get(self.name)
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
576 else:
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
577 return data[0].localname == self.name
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
578 def __repr__(self):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
579 return self.name
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
580
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
581 class QualifiedNameTest(object):
364
7dabedbb53fb Fix for #77: match templates were matching their own output.
cmlenz
parents: 363
diff changeset
582 """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
583 qualified name.
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
584 """
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
585 __slots__ = ['principal_type', 'prefix', 'name']
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
586 def __init__(self, principal_type, prefix, name):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
587 self.principal_type = principal_type
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
588 self.prefix = prefix
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
589 self.name = name
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
590 def __call__(self, kind, data, pos, namespaces, variables):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
591 qname = QName('%s}%s' % (namespaces.get(self.prefix), self.name))
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
592 if kind is START:
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
593 if self.principal_type is ATTRIBUTE and qname in data[1]:
234
39c424b80edd * Minor simplification of XPath engine.
cmlenz
parents: 230
diff changeset
594 return data[1].get(qname)
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
595 else:
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
596 return data[0] == qname
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
597 def __repr__(self):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
598 return '%s:%s' % (self.prefix, self.name)
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
599
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
600 class CommentNodeTest(object):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
601 """Node test that matches any comment events."""
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
602 __slots__ = []
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
603 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
604 return kind is COMMENT
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
605 def __repr__(self):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
606 return 'comment()'
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
607
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
608 class NodeTest(object):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
609 """Node test that matches any node."""
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
610 __slots__ = []
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
611 def __call__(self, kind, data, pos, namespaces, variables):
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
612 if kind is START:
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
613 return True
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
614 return kind, data, pos
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
615 def __repr__(self):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
616 return 'node()'
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
617
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
618 class ProcessingInstructionNodeTest(object):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
619 """Node test that matches any processing instruction event."""
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
620 __slots__ = ['target']
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
621 def __init__(self, target=None):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
622 self.target = target
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
623 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
624 return kind is PI and (not self.target or data[0] == self.target)
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
625 def __repr__(self):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
626 arg = ''
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
627 if self.target:
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
628 arg = '"' + self.target + '"'
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
629 return 'processing-instruction(%s)' % arg
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
630
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
631 class TextNodeTest(object):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
632 """Node test that matches any text event."""
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
633 __slots__ = []
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
634 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
635 return kind is TEXT
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
636 def __repr__(self):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
637 return 'text()'
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
638
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
639 _nodetest_map = {'comment': CommentNodeTest, 'node': NodeTest,
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
640 'processing-instruction': ProcessingInstructionNodeTest,
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
641 'text': TextNodeTest}
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
642
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
643 # Functions
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
644
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
645 class Function(object):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
646 """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
647
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
648 class BooleanFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
649 """The `boolean` function, which converts its argument to a boolean
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
650 value.
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
651 """
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
652 __slots__ = ['expr']
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
653 def __init__(self, expr):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
654 self.expr = expr
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
655 def __call__(self, kind, data, pos, namespaces, variables):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
656 val = self.expr(kind, data, pos, namespaces, variables)
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
657 return bool(val)
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
658 def __repr__(self):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
659 return 'boolean(%r)' % self.expr
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
660
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
661 class CeilingFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
662 """The `ceiling` function, which returns the nearest lower integer number
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
663 for the given number.
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
664 """
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
665 __slots__ = ['number']
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
666 def __init__(self, number):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
667 self.number = number
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
668 def __call__(self, kind, data, pos, namespaces, variables):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
669 number = self.number(kind, data, pos, namespaces, variables)
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
670 return ceil(float(number))
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
671 def __repr__(self):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
672 return 'ceiling(%r)' % self.number
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
673
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
674 class ConcatFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
675 """The `concat` function, which concatenates (joins) the variable number of
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
676 strings it gets as arguments.
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
677 """
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
678 __slots__ = ['exprs']
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
679 def __init__(self, *exprs):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
680 self.exprs = exprs
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
681 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
682 strings = []
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
683 for item in [expr(kind, data, pos, namespaces, variables)
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
684 for expr in self.exprs]:
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
685 strings.append(item)
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
686 return u''.join(strings)
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
687 def __repr__(self):
169
dc6676d3b697 Fix syntax error in `path` module.
cmlenz
parents: 164
diff changeset
688 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
689
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
690 class ContainsFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
691 """The `contains` function, which returns whether a string contains a given
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
692 substring.
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
693 """
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
694 __slots__ = ['string1', 'string2']
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
695 def __init__(self, string1, string2):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
696 self.string1 = string1
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
697 self.string2 = string2
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
698 def __call__(self, kind, data, pos, namespaces, variables):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
699 string1 = self.string1(kind, data, pos, namespaces, variables)
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
700 string2 = 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
701 return string2 in string1
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
702 def __repr__(self):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
703 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
704
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
705 class FalseFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
706 """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
707 __slots__ = []
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
708 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
709 return False
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
710 def __repr__(self):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
711 return 'false()'
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
712
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
713 class FloorFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
714 """The `ceiling` function, which returns the nearest higher integer number
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
715 for the given number.
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
716 """
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
717 __slots__ = ['number']
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
718 def __init__(self, number):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
719 self.number = number
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
720 def __call__(self, kind, data, pos, namespaces, variables):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
721 number = self.number(kind, data, pos, namespaces, variables)
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
722 return floor(float(number))
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
723 def __repr__(self):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
724 return 'floor(%r)' % self.number
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
725
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
726 class LocalNameFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
727 """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
728 element.
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
729 """
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
730 __slots__ = []
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
731 def __call__(self, kind, data, pos, namespaces, variables):
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
732 if kind is START:
234
39c424b80edd * Minor simplification of XPath engine.
cmlenz
parents: 230
diff changeset
733 return data[0].localname
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
734 def __repr__(self):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
735 return 'local-name()'
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
736
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
737 class NameFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
738 """The `name` function, which returns the qualified name of the current
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
739 element.
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
740 """
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
741 __slots__ = []
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
742 def __call__(self, kind, data, pos, namespaces, variables):
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
743 if kind is START:
234
39c424b80edd * Minor simplification of XPath engine.
cmlenz
parents: 230
diff changeset
744 return data[0]
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
745 def __repr__(self):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
746 return 'name()'
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
747
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
748 class NamespaceUriFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
749 """The `namespace-uri` function, which returns the namespace URI of the
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
750 current element.
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
751 """
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
752 __slots__ = []
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
753 def __call__(self, kind, data, pos, namespaces, variables):
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
754 if kind is START:
234
39c424b80edd * Minor simplification of XPath engine.
cmlenz
parents: 230
diff changeset
755 return data[0].namespace
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
756 def __repr__(self):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
757 return 'namespace-uri()'
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
758
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
759 class NotFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
760 """The `not` function, which returns the negated boolean value of its
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
761 argument.
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
762 """
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
763 __slots__ = ['expr']
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
764 def __init__(self, expr):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
765 self.expr = expr
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 return not self.expr(kind, data, pos, namespaces, variables)
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
768 def __repr__(self):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
769 return 'not(%s)' % self.expr
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
770
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
771 class NormalizeSpaceFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
772 """The `normalize-space` function, which removes leading and trailing
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
773 whitespace in the given string, and replaces multiple adjacent whitespace
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
774 characters inside the string with a single space.
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
775 """
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
776 __slots__ = ['expr']
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
777 _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
778 def __init__(self, expr):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
779 self.expr = expr
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
780 def __call__(self, kind, data, pos, namespaces, variables):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
781 string = self.expr(kind, data, pos, namespaces, variables)
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
782 return self._normalize(' ', string.strip())
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
783 def __repr__(self):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
784 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
785
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
786 class NumberFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
787 """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
788 __slots__ = ['expr']
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
789 def __init__(self, expr):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
790 self.expr = expr
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
791 def __call__(self, kind, data, pos, namespaces, variables):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
792 val = self.expr(kind, data, pos, namespaces, variables)
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
793 return float(val)
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
794 def __repr__(self):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
795 return 'number(%r)' % self.expr
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
796
162
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
797 class RoundFunction(Function):
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
798 """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
799 given number.
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
800 """
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
801 __slots__ = ['number']
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
802 def __init__(self, number):
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
803 self.number = number
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
804 def __call__(self, kind, data, pos, namespaces, variables):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
805 number = self.number(kind, data, pos, namespaces, variables)
162
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
806 return round(float(number))
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
807 def __repr__(self):
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
808 return 'round(%r)' % self.number
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
809
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
810 class StartsWithFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
811 """The `starts-with` function that returns whether one string starts with
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
812 a given substring.
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
813 """
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
814 __slots__ = ['string1', 'string2']
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
815 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
816 self.string1 = string1
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
817 self.string2 = string2
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
818 def __call__(self, kind, data, pos, namespaces, variables):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
819 string1 = self.string1(kind, data, pos, namespaces, variables)
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
820 string2 = 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
821 return string1.startswith(string2)
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
822 def __repr__(self):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
823 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
824
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
825 class StringLengthFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
826 """The `string-length` function that returns the length of the given
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
827 string.
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
828 """
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
829 __slots__ = ['expr']
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
830 def __init__(self, expr):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
831 self.expr = expr
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
832 def __call__(self, kind, data, pos, namespaces, variables):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
833 string = self.expr(kind, data, pos, namespaces, variables)
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
834 return len(string)
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
835 def __repr__(self):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
836 return 'string-length(%r)' % self.expr
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
837
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
838 class SubstringFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
839 """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
840 at the given offset, and optionally limited to the given length.
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
841 """
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
842 __slots__ = ['string', 'start', 'length']
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
843 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
844 self.string = string
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
845 self.start = start
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
846 self.length = length
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
847 def __call__(self, kind, data, pos, namespaces, variables):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
848 string = self.string(kind, data, pos, namespaces, variables)
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
849 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
850 length = 0
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
851 if self.length is not None:
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
852 length = self.length(kind, data, pos, namespaces, variables)
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
853 return string[int(start):len(string) - int(length)]
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
854 def __repr__(self):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
855 if self.length is not None:
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
856 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
857 self.length)
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
858 else:
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
859 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
860
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
861 class SubstringAfterFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
862 """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
863 is found after the given substring.
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__ = ['string1', 'string2']
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
866 def __init__(self, string1, string2):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
867 self.string1 = string1
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
868 self.string2 = string2
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 string1 = self.string1(kind, data, pos, namespaces, variables)
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
871 string2 = 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
872 index = string1.find(string2)
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
873 if index >= 0:
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
874 return string1[index + len(string2):]
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
875 return u''
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
876 def __repr__(self):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
877 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
878
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
879 class SubstringBeforeFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
880 """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
881 is found before the given substring.
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
882 """
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
883 __slots__ = ['string1', 'string2']
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
884 def __init__(self, string1, string2):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
885 self.string1 = string1
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
886 self.string2 = string2
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
887 def __call__(self, kind, data, pos, namespaces, variables):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
888 string1 = self.string1(kind, data, pos, namespaces, variables)
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
889 string2 = 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
890 index = string1.find(string2)
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
891 if index >= 0:
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
892 return string1[:index]
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
893 return u''
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
894 def __repr__(self):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
895 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
896
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
897 class TranslateFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
898 """The `translate` function that translates a set of characters in a
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
899 string to target set of characters.
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
900 """
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
901 __slots__ = ['string', 'fromchars', 'tochars']
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
902 def __init__(self, string, fromchars, tochars):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
903 self.string = string
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
904 self.fromchars = fromchars
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
905 self.tochars = tochars
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
906 def __call__(self, kind, data, pos, namespaces, variables):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
907 string = self.string(kind, data, pos, namespaces, variables)
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
908 fromchars = self.fromchars(kind, data, pos, namespaces, variables)
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
909 tochars = 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
910 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
911 [ord(c) for c in tochars]))
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
912 return string.translate(table)
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
913 def __repr__(self):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
914 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
915 self.tochars)
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
916
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
917 class TrueFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
918 """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
919 __slots__ = []
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
920 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
921 return True
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
922 def __repr__(self):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
923 return 'true()'
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
924
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
925 _function_map = {'boolean': BooleanFunction, 'ceiling': CeilingFunction,
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
926 'concat': ConcatFunction, 'contains': ContainsFunction,
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
927 'false': FalseFunction, 'floor': FloorFunction,
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
928 'local-name': LocalNameFunction, 'name': NameFunction,
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
929 'namespace-uri': NamespaceUriFunction,
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
930 'normalize-space': NormalizeSpaceFunction, 'not': NotFunction,
162
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
931 'number': NumberFunction, 'round': RoundFunction,
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
932 'starts-with': StartsWithFunction,
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
933 'string-length': StringLengthFunction,
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
934 'substring': SubstringFunction,
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
935 'substring-after': SubstringAfterFunction,
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
936 'substring-before': SubstringBeforeFunction,
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
937 'translate': TranslateFunction, 'true': TrueFunction}
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
938
179
13909179e5e1 Implemented support for XPath variables in predicates (#31).
cmlenz
parents: 169
diff changeset
939 # Literals & Variables
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
940
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
941 class Literal(object):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
942 """Abstract base class for literal nodes."""
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
943
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
944 class StringLiteral(Literal):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
945 """A string literal node."""
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
946 __slots__ = ['text']
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
947 def __init__(self, text):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
948 self.text = text
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
949 def __call__(self, kind, data, pos, namespaces, variables):
228
a5b38b459cbb Add support for position predicates in XPath expressions.
cmlenz
parents: 224
diff changeset
950 return self.text
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
951 def __repr__(self):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
952 return '"%s"' % self.text
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
953
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
954 class NumberLiteral(Literal):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
955 """A number literal node."""
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
956 __slots__ = ['number']
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
957 def __init__(self, number):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
958 self.number = number
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
959 def __call__(self, kind, data, pos, namespaces, variables):
228
a5b38b459cbb Add support for position predicates in XPath expressions.
cmlenz
parents: 224
diff changeset
960 return self.number
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
961 def __repr__(self):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
962 return str(self.number)
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
963
179
13909179e5e1 Implemented support for XPath variables in predicates (#31).
cmlenz
parents: 169
diff changeset
964 class VariableReference(Literal):
13909179e5e1 Implemented support for XPath variables in predicates (#31).
cmlenz
parents: 169
diff changeset
965 """A variable reference node."""
13909179e5e1 Implemented support for XPath variables in predicates (#31).
cmlenz
parents: 169
diff changeset
966 __slots__ = ['name']
13909179e5e1 Implemented support for XPath variables in predicates (#31).
cmlenz
parents: 169
diff changeset
967 def __init__(self, name):
13909179e5e1 Implemented support for XPath variables in predicates (#31).
cmlenz
parents: 169
diff changeset
968 self.name = name
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
969 def __call__(self, kind, data, pos, namespaces, variables):
228
a5b38b459cbb Add support for position predicates in XPath expressions.
cmlenz
parents: 224
diff changeset
970 return variables.get(self.name)
179
13909179e5e1 Implemented support for XPath variables in predicates (#31).
cmlenz
parents: 169
diff changeset
971 def __repr__(self):
215
94120e6b0ce4 A couple of minor XPath fixes.
cmlenz
parents: 211
diff changeset
972 return str(self.name)
179
13909179e5e1 Implemented support for XPath variables in predicates (#31).
cmlenz
parents: 169
diff changeset
973
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
974 # Operators
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
975
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
976 class AndOperator(object):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
977 """The boolean operator `and`."""
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
978 __slots__ = ['lval', 'rval']
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
979 def __init__(self, lval, rval):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
980 self.lval = lval
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
981 self.rval = rval
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
982 def __call__(self, kind, data, pos, namespaces, variables):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
983 lval = self.lval(kind, data, pos, namespaces, variables)
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
984 if not lval:
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
985 return False
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
986 rval = self.rval(kind, data, pos, namespaces, variables)
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
987 return bool(rval)
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
988 def __repr__(self):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
989 return '%s and %s' % (self.lval, self.rval)
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
990
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
991 class EqualsOperator(object):
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
992 """The equality operator `=`."""
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
993 __slots__ = ['lval', 'rval']
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
994 def __init__(self, lval, rval):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
995 self.lval = lval
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
996 self.rval = rval
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
997 def __call__(self, kind, data, pos, namespaces, variables):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
998 lval = self.lval(kind, data, pos, namespaces, variables)
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
999 rval = self.rval(kind, data, pos, namespaces, variables)
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1000 return lval == rval
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1001 def __repr__(self):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1002 return '%s=%s' % (self.lval, self.rval)
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1003
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1004 class NotEqualsOperator(object):
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1005 """The equality operator `!=`."""
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1006 __slots__ = ['lval', 'rval']
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1007 def __init__(self, lval, rval):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1008 self.lval = lval
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1009 self.rval = rval
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1010 def __call__(self, kind, data, pos, namespaces, variables):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1011 lval = self.lval(kind, data, pos, namespaces, variables)
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1012 rval = self.rval(kind, data, pos, namespaces, variables)
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1013 return lval != rval
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1014 def __repr__(self):
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1015 return '%s!=%s' % (self.lval, self.rval)
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1016
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1017 class OrOperator(object):
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1018 """The boolean operator `or`."""
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1019 __slots__ = ['lval', 'rval']
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1020 def __init__(self, lval, rval):
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1021 self.lval = lval
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1022 self.rval = rval
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1023 def __call__(self, kind, data, pos, namespaces, variables):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1024 lval = self.lval(kind, data, pos, namespaces, variables)
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1025 if lval:
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1026 return True
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1027 rval = self.rval(kind, data, pos, namespaces, variables)
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1028 return bool(rval)
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1029 def __repr__(self):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1030 return '%s or %s' % (self.lval, self.rval)
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1031
162
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1032 class GreaterThanOperator(object):
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1033 """The relational operator `>` (greater than)."""
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1034 __slots__ = ['lval', 'rval']
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1035 def __init__(self, lval, rval):
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1036 self.lval = lval
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1037 self.rval = rval
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1038 def __call__(self, kind, data, pos, namespaces, variables):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1039 lval = self.lval(kind, data, pos, namespaces, variables)
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1040 rval = self.rval(kind, data, pos, namespaces, variables)
162
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1041 return float(lval) > float(rval)
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1042 def __repr__(self):
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1043 return '%s>%s' % (self.lval, self.rval)
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1044
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1045 class GreaterThanOrEqualOperator(object):
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1046 """The relational operator `>=` (greater than or equal)."""
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1047 __slots__ = ['lval', 'rval']
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1048 def __init__(self, lval, rval):
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1049 self.lval = lval
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1050 self.rval = rval
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1051 def __call__(self, kind, data, pos, namespaces, variables):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1052 lval = self.lval(kind, data, pos, namespaces, variables)
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1053 rval = self.rval(kind, data, pos, namespaces, variables)
162
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1054 return float(lval) >= float(rval)
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1055 def __repr__(self):
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1056 return '%s>=%s' % (self.lval, self.rval)
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1057
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1058 class LessThanOperator(object):
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1059 """The relational operator `<` (less than)."""
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1060 __slots__ = ['lval', 'rval']
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1061 def __init__(self, lval, rval):
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1062 self.lval = lval
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1063 self.rval = rval
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1064 def __call__(self, kind, data, pos, namespaces, variables):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1065 lval = self.lval(kind, data, pos, namespaces, variables)
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1066 rval = self.rval(kind, data, pos, namespaces, variables)
162
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1067 return float(lval) < float(rval)
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1068 def __repr__(self):
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1069 return '%s<%s' % (self.lval, self.rval)
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1070
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1071 class LessThanOrEqualOperator(object):
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1072 """The relational operator `<=` (less than or equal)."""
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1073 __slots__ = ['lval', 'rval']
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1074 def __init__(self, lval, rval):
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1075 self.lval = lval
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1076 self.rval = rval
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1077 def __call__(self, kind, data, pos, namespaces, variables):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1078 lval = self.lval(kind, data, pos, namespaces, variables)
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1079 rval = self.rval(kind, data, pos, namespaces, variables)
162
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1080 return float(lval) <= float(rval)
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1081 def __repr__(self):
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1082 return '%s<=%s' % (self.lval, self.rval)
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1083
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1084 _operator_map = {'=': EqualsOperator, '!=': NotEqualsOperator,
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1085 '>': GreaterThanOperator, '>=': GreaterThanOrEqualOperator,
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1086 '<': 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
1087
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
1088
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
1089 _DOTSLASHSLASH = (DESCENDANT_OR_SELF, PrincipalTypeTest(None), ())
Copyright (C) 2012-2017 Edgewall Software