annotate markup/path.py @ 179:a2e0a7986d19

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