annotate genshi/path.py @ 425:5b248708bbed

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