annotate markup/path.py @ 216:0a01371cecbc

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