annotate genshi/path.py @ 958:6fc92535c888 experimental-performance-improvement-exploration

Be more careful about what is passed into streams as events and remove many uses of _ensure as a result. An ATTRS event is added for handling Attributes returned by gensh.path.select().
author hodgestar
date Tue, 13 Mar 2012 03:03:02 +0000
parents 16d55698006a
children
rev   line source
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
1 # -*- coding: utf-8 -*-
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
2 #
854
4d9bef447df9 More work on reducing the size of the diff produced by 2to3.
cmlenz
parents: 853
diff changeset
3 # Copyright (C) 2006-2009 Edgewall Software
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
4 # All rights reserved.
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
5 #
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
6 # This software is licensed as described in the file COPYING, which
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
7 # you should have received as part of this distribution. The terms
230
84168828b074 Renamed Markup to Genshi in repository.
cmlenz
parents: 228
diff changeset
8 # are also available at http://genshi.edgewall.org/wiki/License.
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
9 #
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
10 # This software consists of voluntary contributions made by many
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
11 # individuals. For the exact contribution history, see the revision
230
84168828b074 Renamed Markup to Genshi in repository.
cmlenz
parents: 228
diff changeset
12 # history and logs, available at http://genshi.edgewall.org/log/.
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
13
111
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
14 """Basic support for evaluating XPath expressions against streams.
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
15
230
84168828b074 Renamed Markup to Genshi in repository.
cmlenz
parents: 228
diff changeset
16 >>> from genshi.input import XML
111
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
17 >>> doc = XML('''<doc>
516
317a7f4e3c69 Implemented XPath sub-expressions.
athomas
parents: 498
diff changeset
18 ... <items count="4">
111
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
19 ... <item status="new">
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
20 ... <summary>Foo</summary>
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
21 ... </item>
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
22 ... <item status="closed">
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
23 ... <summary>Bar</summary>
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
24 ... </item>
516
317a7f4e3c69 Implemented XPath sub-expressions.
athomas
parents: 498
diff changeset
25 ... <item status="closed" resolution="invalid">
317a7f4e3c69 Implemented XPath sub-expressions.
athomas
parents: 498
diff changeset
26 ... <summary>Baz</summary>
317a7f4e3c69 Implemented XPath sub-expressions.
athomas
parents: 498
diff changeset
27 ... </item>
317a7f4e3c69 Implemented XPath sub-expressions.
athomas
parents: 498
diff changeset
28 ... <item status="closed" resolution="fixed">
317a7f4e3c69 Implemented XPath sub-expressions.
athomas
parents: 498
diff changeset
29 ... <summary>Waz</summary>
317a7f4e3c69 Implemented XPath sub-expressions.
athomas
parents: 498
diff changeset
30 ... </item>
111
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
31 ... </items>
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
32 ... </doc>''')
853
f33ecf3c319e Convert a bunch of print statements to py3k compatible syntax.
cmlenz
parents: 852
diff changeset
33 >>> print(doc.select('items/item[@status="closed" and '
f33ecf3c319e Convert a bunch of print statements to py3k compatible syntax.
cmlenz
parents: 852
diff changeset
34 ... '(@resolution="invalid" or not(@resolution))]/summary/text()'))
516
317a7f4e3c69 Implemented XPath sub-expressions.
athomas
parents: 498
diff changeset
35 BarBaz
111
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
36
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
37 Because the XPath engine operates on markup streams (as opposed to tree
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
38 structures), it only implements a subset of the full XPath 1.0 language.
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
39 """
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
40
818
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
41 from collections import deque
792
25b8ffa7fe65 Apply patch to silence a -3 warning about `reduce` removal. Closes #279.
cmlenz
parents: 719
diff changeset
42 try:
856
21308bd343b8 Add a couple of fallback imports for Python 3.0.
cmlenz
parents: 854
diff changeset
43 reduce # builtin in Python < 3
21308bd343b8 Add a couple of fallback imports for Python 3.0.
cmlenz
parents: 854
diff changeset
44 except NameError:
792
25b8ffa7fe65 Apply patch to silence a -3 warning about `reduce` removal. Closes #279.
cmlenz
parents: 719
diff changeset
45 from functools import reduce
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
46 from math import ceil, floor
593
0af1b7982ba5 Minor, cosmetic tweaks.
cmlenz
parents: 534
diff changeset
47 import operator
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
48 import re
818
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
49 from itertools import chain
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
50
230
84168828b074 Renamed Markup to Genshi in repository.
cmlenz
parents: 228
diff changeset
51 from genshi.core import Stream, Attrs, Namespace, QName
638
1f73e534e715 Fix for descendant-or-self XPath patterns when namespaces are involved.
cmlenz
parents: 605
diff changeset
52 from genshi.core import START, END, TEXT, START_NS, END_NS, COMMENT, PI, \
1f73e534e715 Fix for descendant-or-self XPath patterns when namespaces are involved.
cmlenz
parents: 605
diff changeset
53 START_CDATA, END_CDATA
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
54
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
55 __all__ = ['Path', 'PathSyntaxError']
425
073640758a42 Try to use proper reStructuredText for docstrings throughout.
cmlenz
parents: 386
diff changeset
56 __docformat__ = 'restructuredtext en'
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
57
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
58
114
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
59 class Axis(object):
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
60 """Defines constants for the various supported XPath axes."""
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
61
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
62 ATTRIBUTE = 'attribute'
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
63 CHILD = 'child'
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
64 DESCENDANT = 'descendant'
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
65 DESCENDANT_OR_SELF = 'descendant-or-self'
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
66 SELF = 'self'
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
67
822
70fddd2262f5 Get rid of some Python 2.3 legacy that's no longer needed now that 2.4 is the baseline.
cmlenz
parents: 818
diff changeset
68 @classmethod
114
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
69 def forname(cls, name):
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
70 """Return the axis constant for the given name, or `None` if no such
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
71 axis was defined.
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
72 """
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
73 return getattr(cls, name.upper().replace('-', '_'), None)
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
74
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
75
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
76 ATTRIBUTE = Axis.ATTRIBUTE
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
77 CHILD = Axis.CHILD
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
78 DESCENDANT = Axis.DESCENDANT
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
79 DESCENDANT_OR_SELF = Axis.DESCENDANT_OR_SELF
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
80 SELF = Axis.SELF
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
81
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
82
818
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
83 class GenericStrategy(object):
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
84
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
85 @classmethod
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
86 def supports(cls, path):
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
87 return True
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
88
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
89 def __init__(self, path):
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
90 self.path = path
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
91
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
92 def test(self, ignore_context):
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
93 p = self.path
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
94 if ignore_context:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
95 if p[0][0] is ATTRIBUTE:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
96 steps = [_DOTSLASHSLASH] + p
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
97 else:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
98 steps = [(DESCENDANT_OR_SELF, p[0][1], p[0][2])] + p[1:]
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
99 elif p[0][0] is CHILD or p[0][0] is ATTRIBUTE \
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
100 or p[0][0] is DESCENDANT:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
101 steps = [_DOTSLASH] + p
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
102 else:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
103 steps = p
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
104
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
105 # for node it contains all positions of xpath expression
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
106 # where its child should start checking for matches
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
107 # with list of corresponding context counters
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
108 # there can be many of them, because position that is from
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
109 # descendant-like axis can be achieved from different nodes
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
110 # for example <a><a><b/></a></a> should match both //a//b[1]
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
111 # and //a//b[2]
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
112 # positions always form increasing sequence (invariant)
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
113 stack = [[(0, [[]])]]
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
114
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
115 def _test(event, namespaces, variables, updateonly=False):
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
116 kind, data, pos = event[:3]
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
117 retval = None
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
118
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
119 # Manage the stack that tells us "where we are" in the stream
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
120 if kind is END:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
121 if stack:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
122 stack.pop()
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
123 return None
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
124 if kind is START_NS or kind is END_NS \
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
125 or kind is START_CDATA or kind is END_CDATA:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
126 # should we make namespaces work?
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
127 return None
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
128
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
129 pos_queue = deque([(pos, cou, []) for pos, cou in stack[-1]])
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
130 next_pos = []
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
131
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
132 # length of real part of path - we omit attribute axis
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
133 real_len = len(steps) - ((steps[-1][0] == ATTRIBUTE) or 1 and 0)
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
134 last_checked = -1
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
135
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
136 # places where we have to check for match, are these
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
137 # provided by parent
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
138 while pos_queue:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
139 x, pcou, mcou = pos_queue.popleft()
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
140 axis, nodetest, predicates = steps[x]
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
141
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
142 # we need to push descendant-like positions from parent
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
143 # further
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
144 if (axis is DESCENDANT or axis is DESCENDANT_OR_SELF) and pcou:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
145 if next_pos and next_pos[-1][0] == x:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
146 next_pos[-1][1].extend(pcou)
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
147 else:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
148 next_pos.append((x, pcou))
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
149
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
150 # nodetest first
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
151 if not nodetest(kind, data, pos, namespaces, variables):
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
152 continue
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
153
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
154 # counters packs that were already bad
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
155 missed = set()
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
156 counters_len = len(pcou) + len(mcou)
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
157
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
158 # number of counters - we have to create one
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
159 # for every context position based predicate
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
160 cnum = 0
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
161
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
162 # tells if we have match with position x
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
163 matched = True
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
164
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
165 if predicates:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
166 for predicate in predicates:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
167 pretval = predicate(kind, data, pos,
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
168 namespaces,
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
169 variables)
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
170 if type(pretval) is float: # FIXME <- need to check
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
171 # this for other types that
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
172 # can be coerced to float
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
173
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
174 # each counter pack needs to be checked
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
175 for i, cou in enumerate(chain(pcou, mcou)):
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
176 # it was bad before
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
177 if i in missed:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
178 continue
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
179
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
180 if len(cou) < cnum + 1:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
181 cou.append(0)
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
182 cou[cnum] += 1
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
183
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
184 # it is bad now
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
185 if cou[cnum] != int(pretval):
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
186 missed.add(i)
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
187
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
188 # none of counters pack was good
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
189 if len(missed) == counters_len:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
190 pretval = False
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
191 cnum += 1
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
192
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
193 if not pretval:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
194 matched = False
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
195 break
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
196
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
197 if not matched:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
198 continue
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
199
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
200 # counter for next position with current node as context node
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
201 child_counter = []
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
202
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
203 if x + 1 == real_len:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
204 # we reached end of expression, because x + 1
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
205 # is equal to the length of expression
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
206 matched = True
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
207 axis, nodetest, predicates = steps[-1]
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
208 if axis is ATTRIBUTE:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
209 matched = nodetest(kind, data, pos, namespaces,
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
210 variables)
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
211 if matched:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
212 retval = matched
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
213 else:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
214 next_axis = steps[x + 1][0]
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
215
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
216 # if next axis allows matching self we have
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
217 # to add next position to our queue
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
218 if next_axis is DESCENDANT_OR_SELF or next_axis is SELF:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
219 if not pos_queue or pos_queue[0][0] > x + 1:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
220 pos_queue.appendleft((x + 1, [], [child_counter]))
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
221 else:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
222 pos_queue[0][2].append(child_counter)
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
223
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
224 # if axis is not self we have to add it to child's list
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
225 if next_axis is not SELF:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
226 next_pos.append((x + 1, [child_counter]))
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
227
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
228 if kind is START:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
229 stack.append(next_pos)
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
230
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
231 return retval
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
232
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
233 return _test
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
234
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
235
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
236 class SimplePathStrategy(object):
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
237 """Strategy for path with only local names, attributes and text nodes."""
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
238
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
239 @classmethod
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
240 def supports(cls, path):
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
241 if path[0][0] is ATTRIBUTE:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
242 return False
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
243 allowed_tests = (LocalNameTest, CommentNodeTest, TextNodeTest)
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
244 for _, nodetest, predicates in path:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
245 if predicates:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
246 return False
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
247 if not isinstance(nodetest, allowed_tests):
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
248 return False
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
249 return True
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
250
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
251 def __init__(self, path):
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
252 # fragments is list of tuples (fragment, pi, attr, self_beginning)
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
253 # fragment is list of nodetests for fragment of path with only
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
254 # child:: axes between
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
255 # pi is KMP partial match table for this fragment
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
256 # attr is attribute nodetest if fragment ends with @ and None otherwise
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
257 # self_beginning is True if axis for first fragment element
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
258 # was self (first fragment) or descendant-or-self (farther fragment)
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
259 self.fragments = []
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
260
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
261 self_beginning = False
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
262 fragment = []
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
263
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
264 def nodes_equal(node1, node2):
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
265 """Tests if two node tests are equal"""
860
16d55698006a A bit of cleanup of the `Markup` Python implementation.
cmlenz
parents: 857
diff changeset
266 if type(node1) is not type(node2):
818
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
267 return False
860
16d55698006a A bit of cleanup of the `Markup` Python implementation.
cmlenz
parents: 857
diff changeset
268 if type(node1) == LocalNameTest:
818
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
269 return node1.name == node2.name
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
270 return True
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
271
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
272 def calculate_pi(f):
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
273 """KMP prefix calculation for table"""
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
274 # the indexes in prefix table are shifted by one
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
275 # in comparision with common implementations
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
276 # pi[i] = NORMAL_PI[i + 1]
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
277 if len(f) == 0:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
278 return []
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
279 pi = [0]
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
280 s = 0
854
4d9bef447df9 More work on reducing the size of the diff produced by 2to3.
cmlenz
parents: 853
diff changeset
281 for i in range(1, len(f)):
818
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
282 while s > 0 and not nodes_equal(f[s], f[i]):
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
283 s = pi[s-1]
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
284 if nodes_equal(f[s], f[i]):
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
285 s += 1
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
286 pi.append(s)
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
287 return pi
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
288
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
289 for axis in path:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
290 if axis[0] is SELF:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
291 if len(fragment) != 0:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
292 # if element is not first in fragment it has to be
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
293 # the same as previous one
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
294 # for example child::a/self::b is always wrong
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
295 if axis[1] != fragment[-1][1]:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
296 self.fragments = None
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
297 return
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
298 else:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
299 self_beginning = True
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
300 fragment.append(axis[1])
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
301 elif axis[0] is CHILD:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
302 fragment.append(axis[1])
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
303 elif axis[0] is ATTRIBUTE:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
304 pi = calculate_pi(fragment)
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
305 self.fragments.append((fragment, pi, axis[1], self_beginning))
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
306 # attribute has always to be at the end, so we can jump out
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
307 return
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
308 else:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
309 pi = calculate_pi(fragment)
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
310 self.fragments.append((fragment, pi, None, self_beginning))
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
311 fragment = [axis[1]]
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
312 if axis[0] is DESCENDANT:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
313 self_beginning = False
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
314 else: # DESCENDANT_OR_SELF
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
315 self_beginning = True
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
316 pi = calculate_pi(fragment)
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
317 self.fragments.append((fragment, pi, None, self_beginning))
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
318
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
319 def test(self, ignore_context):
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
320 # stack of triples (fid, p, ic)
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
321 # fid is index of current fragment
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
322 # p is position in this fragment
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
323 # ic is if we ignore context in this fragment
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
324 stack = []
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
325 stack_push = stack.append
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
326 stack_pop = stack.pop
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
327 frags = self.fragments
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
328 frags_len = len(frags)
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
329
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
330 def _test(event, namespaces, variables, updateonly=False):
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
331 # expression found impossible during init
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
332 if frags is None:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
333 return None
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
334
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
335 kind, data, pos = event[:3]
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
336
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
337 # skip events we don't care about
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
338 if kind is END:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
339 if stack:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
340 stack_pop()
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
341 return None
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
342 if kind is START_NS or kind is END_NS \
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
343 or kind is START_CDATA or kind is END_CDATA:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
344 return None
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
345
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
346 if not stack:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
347 # root node, nothing on stack, special case
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
348 fid = 0
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
349 # skip empty fragments (there can be actually only one)
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
350 while not frags[fid][0]:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
351 fid += 1
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
352 p = 0
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
353 # empty fragment means descendant node at beginning
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
354 ic = ignore_context or (fid > 0)
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
355
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
356 # expression can match first node, if first axis is self::,
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
357 # descendant-or-self:: or if ignore_context is True and
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
358 # axis is not descendant::
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
359 if not frags[fid][3] and (not ignore_context or fid > 0):
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
360 # axis is not self-beggining, we have to skip this node
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
361 stack_push((fid, p, ic))
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
362 return None
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
363 else:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
364 # take position of parent
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
365 fid, p, ic = stack[-1]
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
366
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
367 if fid is not None and not ic:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
368 # fragment not ignoring context - we can't jump back
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
369 frag, pi, attrib, _ = frags[fid]
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
370 frag_len = len(frag)
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
371
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
372 if p == frag_len:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
373 # that probably means empty first fragment
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
374 pass
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
375 elif frag[p](kind, data, pos, namespaces, variables):
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
376 # match, so we can go further
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
377 p += 1
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
378 else:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
379 # not matched, so there will be no match in subtree
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
380 fid, p = None, None
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
381
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
382 if p == frag_len and fid + 1 != frags_len:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
383 # we made it to end of fragment, we can go to following
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
384 fid += 1
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
385 p = 0
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
386 ic = True
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
387
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
388 if fid is None:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
389 # there was no match in fragment not ignoring context
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
390 if kind is START:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
391 stack_push((fid, p, ic))
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
392 return None
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
393
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
394 if ic:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
395 # we are in fragment ignoring context
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
396 while True:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
397 frag, pi, attrib, _ = frags[fid]
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
398 frag_len = len(frag)
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
399
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
400 # KMP new "character"
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
401 while p > 0 and (p >= frag_len or not \
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
402 frag[p](kind, data, pos, namespaces, variables)):
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
403 p = pi[p-1]
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
404 if frag[p](kind, data, pos, namespaces, variables):
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
405 p += 1
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
406
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
407 if p == frag_len:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
408 # end of fragment reached
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
409 if fid + 1 == frags_len:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
410 # that was last fragment
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
411 break
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
412 else:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
413 fid += 1
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
414 p = 0
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
415 ic = True
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
416 if not frags[fid][3]:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
417 # next fragment not self-beginning
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
418 break
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
419 else:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
420 break
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
421
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
422 if kind is START:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
423 # we have to put new position on stack, for children
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
424
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
425 if not ic and fid + 1 == frags_len and p == frag_len:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
426 # it is end of the only, not context ignoring fragment
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
427 # so there will be no matches in subtree
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
428 stack_push((None, None, ic))
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
429 else:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
430 stack_push((fid, p, ic))
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
431
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
432 # have we reached the end of the last fragment?
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
433 if fid + 1 == frags_len and p == frag_len:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
434 if attrib: # attribute ended path, return value
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
435 return attrib(kind, data, pos, namespaces, variables)
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
436 return True
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
437
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
438 return None
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
439
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
440 return _test
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
441
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
442
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
443 class SingleStepStrategy(object):
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
444
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
445 @classmethod
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
446 def supports(cls, path):
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
447 return len(path) == 1
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
448
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
449 def __init__(self, path):
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
450 self.path = path
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
451
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
452 def test(self, ignore_context):
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
453 steps = self.path
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
454 if steps[0][0] is ATTRIBUTE:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
455 steps = [_DOTSLASH] + steps
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
456 select_attr = steps[-1][0] is ATTRIBUTE and steps[-1][1] or None
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
457
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
458 # for every position in expression stores counters' list
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
459 # it is used for position based predicates
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
460 counters = []
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
461 depth = [0]
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
462
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
463 def _test(event, namespaces, variables, updateonly=False):
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
464 kind, data, pos = event[:3]
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
465
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
466 # Manage the stack that tells us "where we are" in the stream
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
467 if kind is END:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
468 if not ignore_context:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
469 depth[0] -= 1
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
470 return None
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
471 elif kind is START_NS or kind is END_NS \
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
472 or kind is START_CDATA or kind is END_CDATA:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
473 # should we make namespaces work?
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
474 return None
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
475
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
476 if not ignore_context:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
477 outside = (steps[0][0] is SELF and depth[0] != 0) \
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
478 or (steps[0][0] is CHILD and depth[0] != 1) \
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
479 or (steps[0][0] is DESCENDANT and depth[0] < 1)
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
480 if kind is START:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
481 depth[0] += 1
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
482 if outside:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
483 return None
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
484
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
485 axis, nodetest, predicates = steps[0]
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
486 if not nodetest(kind, data, pos, namespaces, variables):
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
487 return None
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
488
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
489 if predicates:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
490 cnum = 0
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
491 for predicate in predicates:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
492 pretval = predicate(kind, data, pos, namespaces, variables)
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
493 if type(pretval) is float: # FIXME <- need to check this
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
494 # for other types that can be
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
495 # coerced to float
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
496 if len(counters) < cnum + 1:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
497 counters.append(0)
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
498 counters[cnum] += 1
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
499 if counters[cnum] != int(pretval):
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
500 pretval = False
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
501 cnum += 1
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
502 if not pretval:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
503 return None
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
504
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
505 if select_attr:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
506 return select_attr(kind, data, pos, namespaces, variables)
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
507
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
508 return True
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
509
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
510 return _test
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
511
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
512
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
513 class Path(object):
26
3c1a022be04c * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
514 """Implements basic XPath support on streams.
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
515
818
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
516 Instances of this class represent a "compiled" XPath expression, and
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
517 provide methods for testing the path against a stream, as well as
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
518 extracting a substream matching that path.
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
519 """
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
520
818
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
521 STRATEGIES = (SingleStepStrategy, SimplePathStrategy, GenericStrategy)
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
522
139
8332287b5508 Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents: 137
diff changeset
523 def __init__(self, text, filename=None, lineno=-1):
26
3c1a022be04c * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
524 """Create the path object from a string.
3c1a022be04c * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
525
425
073640758a42 Try to use proper reStructuredText for docstrings throughout.
cmlenz
parents: 386
diff changeset
526 :param text: the path expression
498
700e7b47bf2b A couple of minor doc refinements.
cmlenz
parents: 425
diff changeset
527 :param filename: the name of the file in which the path expression was
700e7b47bf2b A couple of minor doc refinements.
cmlenz
parents: 425
diff changeset
528 found (used in error messages)
700e7b47bf2b A couple of minor doc refinements.
cmlenz
parents: 425
diff changeset
529 :param lineno: the line on which the expression was found
26
3c1a022be04c * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
530 """
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
531 self.source = text
139
8332287b5508 Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents: 137
diff changeset
532 self.paths = PathParser(text, filename, lineno).parse()
818
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
533 self.strategies = []
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
534 for path in self.paths:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
535 for strategy_class in self.STRATEGIES:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
536 if strategy_class.supports(path):
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
537 self.strategies.append(strategy_class(path))
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
538 break
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
539 else:
854
4d9bef447df9 More work on reducing the size of the diff produced by 2to3.
cmlenz
parents: 853
diff changeset
540 raise NotImplemented('No strategy found for path')
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
541
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
542 def __repr__(self):
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
543 paths = []
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
544 for path in self.paths:
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
545 steps = []
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
546 for axis, nodetest, predicates in path:
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
547 steps.append('%s::%s' % (axis, nodetest))
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
548 for predicate in predicates:
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
549 steps[-1] += '[%s]' % predicate
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
550 paths.append('/'.join(steps))
860
16d55698006a A bit of cleanup of the `Markup` Python implementation.
cmlenz
parents: 857
diff changeset
551 return '<%s "%s">' % (type(self).__name__, '|'.join(paths))
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
552
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
553 def select(self, stream, namespaces=None, variables=None):
26
3c1a022be04c * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
554 """Returns a substream of the given stream that matches the path.
3c1a022be04c * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
555
3c1a022be04c * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
556 If there are no matches, this method returns an empty stream.
3c1a022be04c * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
557
230
84168828b074 Renamed Markup to Genshi in repository.
cmlenz
parents: 228
diff changeset
558 >>> from genshi.input import XML
33
1fdb7054fb42 Add doctests for path module.
cmlenz
parents: 27
diff changeset
559 >>> xml = XML('<root><elem><child>Text</child></elem></root>')
61
448792ab1303 Use a different namespace than Kid uses.
cmlenz
parents: 38
diff changeset
560
853
f33ecf3c319e Convert a bunch of print statements to py3k compatible syntax.
cmlenz
parents: 852
diff changeset
561 >>> print(Path('.//child').select(xml))
33
1fdb7054fb42 Add doctests for path module.
cmlenz
parents: 27
diff changeset
562 <child>Text</child>
1fdb7054fb42 Add doctests for path module.
cmlenz
parents: 27
diff changeset
563
853
f33ecf3c319e Convert a bunch of print statements to py3k compatible syntax.
cmlenz
parents: 852
diff changeset
564 >>> print(Path('.//child/text()').select(xml))
33
1fdb7054fb42 Add doctests for path module.
cmlenz
parents: 27
diff changeset
565 Text
1fdb7054fb42 Add doctests for path module.
cmlenz
parents: 27
diff changeset
566
425
073640758a42 Try to use proper reStructuredText for docstrings throughout.
cmlenz
parents: 386
diff changeset
567 :param stream: the stream to select from
073640758a42 Try to use proper reStructuredText for docstrings throughout.
cmlenz
parents: 386
diff changeset
568 :param namespaces: (optional) a mapping of namespace prefixes to URIs
073640758a42 Try to use proper reStructuredText for docstrings throughout.
cmlenz
parents: 386
diff changeset
569 :param variables: (optional) a mapping of variable names to values
073640758a42 Try to use proper reStructuredText for docstrings throughout.
cmlenz
parents: 386
diff changeset
570 :return: the substream matching the path, or an empty stream
498
700e7b47bf2b A couple of minor doc refinements.
cmlenz
parents: 425
diff changeset
571 :rtype: `Stream`
26
3c1a022be04c * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
572 """
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
573 if namespaces is None:
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
574 namespaces = {}
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
575 if variables is None:
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
576 variables = {}
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
577 stream = iter(stream)
818
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
578 def _generate(stream=stream, ns=namespaces, vs=variables):
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
579 next = stream.next
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
580 test = self.test()
305
60111a041e7c Various performance-oriented tweaks.
cmlenz
parents: 282
diff changeset
581 for event in stream:
818
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
582 result = test(event, ns, vs)
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
583 if result is True:
305
60111a041e7c Various performance-oriented tweaks.
cmlenz
parents: 282
diff changeset
584 yield event
330
3e749eaa3100 XPath tests should never return event tuples, just values or booleans.
cmlenz
parents: 326
diff changeset
585 if event[0] is START:
3e749eaa3100 XPath tests should never return event tuples, just values or booleans.
cmlenz
parents: 326
diff changeset
586 depth = 1
3e749eaa3100 XPath tests should never return event tuples, just values or booleans.
cmlenz
parents: 326
diff changeset
587 while depth > 0:
818
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
588 subevent = next()
330
3e749eaa3100 XPath tests should never return event tuples, just values or booleans.
cmlenz
parents: 326
diff changeset
589 if subevent[0] is START:
3e749eaa3100 XPath tests should never return event tuples, just values or booleans.
cmlenz
parents: 326
diff changeset
590 depth += 1
3e749eaa3100 XPath tests should never return event tuples, just values or booleans.
cmlenz
parents: 326
diff changeset
591 elif subevent[0] is END:
3e749eaa3100 XPath tests should never return event tuples, just values or booleans.
cmlenz
parents: 326
diff changeset
592 depth -= 1
3e749eaa3100 XPath tests should never return event tuples, just values or booleans.
cmlenz
parents: 326
diff changeset
593 yield subevent
818
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
594 test(subevent, ns, vs, updateonly=True)
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
595 elif result:
958
6fc92535c888 Be more careful about what is passed into streams as events and remove many uses of _ensure as a result. An ATTRS event is added for handling Attributes returned by gensh.path.select().
hodgestar
parents: 860
diff changeset
596 # check for Attrs and wrap them in an ATTRS event
6fc92535c888 Be more careful about what is passed into streams as events and remove many uses of _ensure as a result. An ATTRS event is added for handling Attributes returned by gensh.path.select().
hodgestar
parents: 860
diff changeset
597 # if found
6fc92535c888 Be more careful about what is passed into streams as events and remove many uses of _ensure as a result. An ATTRS event is added for handling Attributes returned by gensh.path.select().
hodgestar
parents: 860
diff changeset
598 if hasattr(result, 'toevent'):
6fc92535c888 Be more careful about what is passed into streams as events and remove many uses of _ensure as a result. An ATTRS event is added for handling Attributes returned by gensh.path.select().
hodgestar
parents: 860
diff changeset
599 result = result.toevent()
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
600 yield result
605
d0345c64da65 Text templates now default to rendering as plain text; it is no longer necessary to explicitly specify the "text" method to the `render()` or `serialize()` method of the generated markup stream. See tickets #62 and #118.
cmlenz
parents: 593
diff changeset
601 return Stream(_generate(),
d0345c64da65 Text templates now default to rendering as plain text; it is no longer necessary to explicitly specify the "text" method to the `render()` or `serialize()` method of the generated markup stream. See tickets #62 and #118.
cmlenz
parents: 593
diff changeset
602 serializer=getattr(stream, 'serializer', None))
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
603
38
ee669cb9cccc Fix for #2 (incorrect context node in path expressions). Still some paths that produce incorrect results, but the common case seems to work now.
cmlenz
parents: 37
diff changeset
604 def test(self, ignore_context=False):
26
3c1a022be04c * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
605 """Returns a function that can be used to track whether the path matches
3c1a022be04c * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
606 a specific stream event.
3c1a022be04c * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
607
425
073640758a42 Try to use proper reStructuredText for docstrings throughout.
cmlenz
parents: 386
diff changeset
608 The function returned expects the positional arguments ``event``,
073640758a42 Try to use proper reStructuredText for docstrings throughout.
cmlenz
parents: 386
diff changeset
609 ``namespaces`` and ``variables``. The first is a stream event, while the
305
60111a041e7c Various performance-oriented tweaks.
cmlenz
parents: 282
diff changeset
610 latter two are a mapping of namespace prefixes to URIs, and a mapping
306
095a754f95a8 Minor optimization for XPath evaluation.
cmlenz
parents: 305
diff changeset
611 of variable names to values, respectively. In addition, the function
425
073640758a42 Try to use proper reStructuredText for docstrings throughout.
cmlenz
parents: 386
diff changeset
612 accepts an ``updateonly`` keyword argument that default to ``False``. If
073640758a42 Try to use proper reStructuredText for docstrings throughout.
cmlenz
parents: 386
diff changeset
613 it is set to ``True``, the function only updates its internal state,
306
095a754f95a8 Minor optimization for XPath evaluation.
cmlenz
parents: 305
diff changeset
614 but does not perform any tests or return a result.
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
615
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
616 If the path matches the event, the function returns the match (for
425
073640758a42 Try to use proper reStructuredText for docstrings throughout.
cmlenz
parents: 386
diff changeset
617 example, a `START` or `TEXT` event.) Otherwise, it returns ``None``.
33
1fdb7054fb42 Add doctests for path module.
cmlenz
parents: 27
diff changeset
618
230
84168828b074 Renamed Markup to Genshi in repository.
cmlenz
parents: 228
diff changeset
619 >>> from genshi.input import XML
33
1fdb7054fb42 Add doctests for path module.
cmlenz
parents: 27
diff changeset
620 >>> xml = XML('<root><elem><child id="1"/></elem><child id="2"/></root>')
1fdb7054fb42 Add doctests for path module.
cmlenz
parents: 27
diff changeset
621 >>> test = Path('child').test()
818
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
622 >>> namespaces, variables = {}, {}
305
60111a041e7c Various performance-oriented tweaks.
cmlenz
parents: 282
diff changeset
623 >>> for event in xml:
818
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
624 ... if test(event, namespaces, variables):
853
f33ecf3c319e Convert a bunch of print statements to py3k compatible syntax.
cmlenz
parents: 852
diff changeset
625 ... print('%s %r' % (event[0], event[1]))
857
129e54866a98 Avoid unicode literals in `repr`s of `QName` and `Namespace` when not necessary.
cmlenz
parents: 856
diff changeset
626 START (QName('child'), Attrs([(QName('id'), u'2')]))
498
700e7b47bf2b A couple of minor doc refinements.
cmlenz
parents: 425
diff changeset
627
700e7b47bf2b A couple of minor doc refinements.
cmlenz
parents: 425
diff changeset
628 :param ignore_context: if `True`, the path is interpreted like a pattern
700e7b47bf2b A couple of minor doc refinements.
cmlenz
parents: 425
diff changeset
629 in XSLT, meaning for example that it will match
700e7b47bf2b A couple of minor doc refinements.
cmlenz
parents: 425
diff changeset
630 at any depth
700e7b47bf2b A couple of minor doc refinements.
cmlenz
parents: 425
diff changeset
631 :return: a function that can be used to test individual events in a
700e7b47bf2b A couple of minor doc refinements.
cmlenz
parents: 425
diff changeset
632 stream against the path
700e7b47bf2b A couple of minor doc refinements.
cmlenz
parents: 425
diff changeset
633 :rtype: ``function``
26
3c1a022be04c * Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents: 25
diff changeset
634 """
818
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
635 tests = [s.test(ignore_context) for s in self.strategies]
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
636 if len(tests) == 1:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
637 return tests[0]
216
636fe6766b4d Many fixes to XPath evaluation. Among other things, this should get rid of the bug that attributes were getting ?pulled up? by `py:match` directives using `py:attrs="select('@*')"` (see #50).
cmlenz
parents: 215
diff changeset
638
818
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
639 def _multi(event, namespaces, variables, updateonly=False):
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
640 retval = None
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
641 for test in tests:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
642 val = test(event, namespaces, variables, updateonly=updateonly)
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
643 if retval is None:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
644 retval = val
259
fe8dbe9066c1 Fix bug in evaluating XPath expressions using the union operator `|`, which caused any path but the first to get out of sync with the event stream, and the whole thing returning too few results.
cmlenz
parents: 250
diff changeset
645 return retval
818
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
646 return _multi
1
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
647
5479aae32f5a Initial import.
cmlenz
parents:
diff changeset
648
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
649 class PathSyntaxError(Exception):
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
650 """Exception raised when an XPath expression is syntactically incorrect."""
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
651
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
652 def __init__(self, message, filename=None, lineno=-1, offset=-1):
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
653 if filename:
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
654 message = '%s (%s, line %d)' % (message, filename, lineno)
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
655 Exception.__init__(self, message)
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
656 self.filename = filename
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
657 self.lineno = lineno
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
658 self.offset = offset
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
659
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
660
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
661 class PathParser(object):
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
662 """Tokenizes and parses an XPath expression."""
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
663
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
664 _QUOTES = (("'", "'"), ('"', '"'))
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
665 _TOKENS = ('::', ':', '..', '.', '//', '/', '[', ']', '()', '(', ')', '@',
179
13909179e5e1 Implemented support for XPath variables in predicates (#31).
cmlenz
parents: 169
diff changeset
666 '=', '!=', '!', '|', ',', '>=', '>', '<=', '<', '$')
163
9c023c395e44 Support for XPath number literals including decimal places.
cmlenz
parents: 162
diff changeset
667 _tokenize = re.compile('("[^"]*")|(\'[^\']*\')|((?:\d+)?\.\d+)|(%s)|([^%s\s]+)|\s+' % (
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
668 '|'.join([re.escape(t) for t in _TOKENS]),
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
669 ''.join([re.escape(t[0]) for t in _TOKENS]))).findall
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
670
139
8332287b5508 Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents: 137
diff changeset
671 def __init__(self, text, filename=None, lineno=-1):
8332287b5508 Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents: 137
diff changeset
672 self.filename = filename
8332287b5508 Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents: 137
diff changeset
673 self.lineno = lineno
854
4d9bef447df9 More work on reducing the size of the diff produced by 2to3.
cmlenz
parents: 853
diff changeset
674 self.tokens = [t for t in [dqstr or sqstr or number or token or name
4d9bef447df9 More work on reducing the size of the diff produced by 2to3.
cmlenz
parents: 853
diff changeset
675 for dqstr, sqstr, number, token, name in
4d9bef447df9 More work on reducing the size of the diff produced by 2to3.
cmlenz
parents: 853
diff changeset
676 self._tokenize(text)] if t]
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
677 self.pos = 0
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
678
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
679 # Tokenizer
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
680
822
70fddd2262f5 Get rid of some Python 2.3 legacy that's no longer needed now that 2.4 is the baseline.
cmlenz
parents: 818
diff changeset
681 @property
70fddd2262f5 Get rid of some Python 2.3 legacy that's no longer needed now that 2.4 is the baseline.
cmlenz
parents: 818
diff changeset
682 def at_end(self):
70fddd2262f5 Get rid of some Python 2.3 legacy that's no longer needed now that 2.4 is the baseline.
cmlenz
parents: 818
diff changeset
683 return self.pos == len(self.tokens) - 1
70fddd2262f5 Get rid of some Python 2.3 legacy that's no longer needed now that 2.4 is the baseline.
cmlenz
parents: 818
diff changeset
684
70fddd2262f5 Get rid of some Python 2.3 legacy that's no longer needed now that 2.4 is the baseline.
cmlenz
parents: 818
diff changeset
685 @property
70fddd2262f5 Get rid of some Python 2.3 legacy that's no longer needed now that 2.4 is the baseline.
cmlenz
parents: 818
diff changeset
686 def cur_token(self):
70fddd2262f5 Get rid of some Python 2.3 legacy that's no longer needed now that 2.4 is the baseline.
cmlenz
parents: 818
diff changeset
687 return self.tokens[self.pos]
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
688
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
689 def next_token(self):
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
690 self.pos += 1
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
691 return self.tokens[self.pos]
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
692
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
693 def peek_token(self):
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
694 if not self.at_end:
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
695 return self.tokens[self.pos + 1]
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
696 return None
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
697
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
698 # Recursive descent parser
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
699
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
700 def parse(self):
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
701 """Parses the XPath expression and returns a list of location path
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
702 tests.
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
703
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
704 For union expressions (such as `*|text()`), this function returns one
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
705 test for each operand in the union. For patch expressions that don't
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
706 use the union operator, the function always returns a list of size 1.
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
707
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
708 Each path test in turn is a sequence of tests that correspond to the
111
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
709 location steps, each tuples of the form `(axis, testfunc, predicates)`
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
710 """
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
711 paths = [self._location_path()]
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
712 while self.cur_token == '|':
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
713 self.next_token()
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
714 paths.append(self._location_path())
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
715 if not self.at_end:
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
716 raise PathSyntaxError('Unexpected token %r after end of expression'
139
8332287b5508 Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents: 137
diff changeset
717 % self.cur_token, self.filename, self.lineno)
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
718 return paths
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
719
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
720 def _location_path(self):
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
721 steps = []
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
722 while True:
215
94120e6b0ce4 A couple of minor XPath fixes.
cmlenz
parents: 211
diff changeset
723 if self.cur_token.startswith('/'):
818
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
724 if not steps:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
725 if self.cur_token == '//':
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
726 # hack to make //* match every node - also root
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
727 self.next_token()
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
728 axis, nodetest, predicates = self._location_step()
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
729 steps.append((DESCENDANT_OR_SELF, nodetest,
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
730 predicates))
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
731 if self.at_end or not self.cur_token.startswith('/'):
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
732 break
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
733 continue
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
734 else:
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
735 raise PathSyntaxError('Absolute location paths not '
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
736 'supported', self.filename,
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
737 self.lineno)
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
738 elif self.cur_token == '//':
215
94120e6b0ce4 A couple of minor XPath fixes.
cmlenz
parents: 211
diff changeset
739 steps.append((DESCENDANT_OR_SELF, NodeTest(), []))
111
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
740 self.next_token()
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
741
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
742 axis, nodetest, predicates = self._location_step()
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
743 if not axis:
145
47bbd9d2a5af * Fix error in expression evaluation when the expression evaluates to an iterable that does not produce event tuples.
cmlenz
parents: 139
diff changeset
744 axis = CHILD
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
745 steps.append((axis, nodetest, predicates))
111
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
746 if self.at_end or not self.cur_token.startswith('/'):
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
747 break
111
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
748
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
749 return steps
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
750
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
751 def _location_step(self):
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
752 if self.cur_token == '@':
114
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
753 axis = ATTRIBUTE
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
754 self.next_token()
111
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
755 elif self.cur_token == '.':
114
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
756 axis = SELF
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
757 elif self.cur_token == '..':
139
8332287b5508 Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents: 137
diff changeset
758 raise PathSyntaxError('Unsupported axis "parent"', self.filename,
8332287b5508 Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents: 137
diff changeset
759 self.lineno)
111
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
760 elif self.peek_token() == '::':
114
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
761 axis = Axis.forname(self.cur_token)
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
762 if axis is None:
139
8332287b5508 Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents: 137
diff changeset
763 raise PathSyntaxError('Unsupport axis "%s"' % axis,
8332287b5508 Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents: 137
diff changeset
764 self.filename, self.lineno)
111
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
765 self.next_token()
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
766 self.next_token()
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
767 else:
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
768 axis = None
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
769 nodetest = self._node_test(axis or CHILD)
111
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
770 predicates = []
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
771 while self.cur_token == '[':
111
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
772 predicates.append(self._predicate())
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
773 return axis, nodetest, predicates
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
774
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
775 def _node_test(self, axis=None):
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
776 test = prefix = None
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
777 next_token = self.peek_token()
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
778 if next_token in ('(', '()'): # Node type test
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
779 test = self._node_type()
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
780
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
781 elif next_token == ':': # Namespace prefix
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
782 prefix = self.cur_token
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
783 self.next_token()
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
784 localname = self.next_token()
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
785 if localname == '*':
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
786 test = QualifiedPrincipalTypeTest(axis, prefix)
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
787 else:
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
788 test = QualifiedNameTest(axis, prefix, localname)
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
789
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
790 else: # Name test
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
791 if self.cur_token == '*':
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
792 test = PrincipalTypeTest(axis)
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
793 elif self.cur_token == '.':
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
794 test = NodeTest()
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
795 else:
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
796 test = LocalNameTest(axis, self.cur_token)
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
797
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
798 if not self.at_end:
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
799 self.next_token()
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
800 return test
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
801
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
802 def _node_type(self):
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
803 name = self.cur_token
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
804 self.next_token()
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
805
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
806 args = []
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
807 if self.cur_token != '()':
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
808 # The processing-instruction() function optionally accepts the
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
809 # name of the PI as argument, which must be a literal string
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
810 self.next_token() # (
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
811 if self.cur_token != ')':
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
812 string = self.cur_token
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
813 if (string[0], string[-1]) in self._QUOTES:
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
814 string = string[1:-1]
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
815 args.append(string)
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
816
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
817 cls = _nodetest_map.get(name)
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
818 if not cls:
139
8332287b5508 Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents: 137
diff changeset
819 raise PathSyntaxError('%s() not allowed here' % name, self.filename,
8332287b5508 Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents: 137
diff changeset
820 self.lineno)
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
821 return cls(*args)
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
822
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
823 def _predicate(self):
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
824 assert self.cur_token == '['
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
825 self.next_token()
111
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
826 expr = self._or_expr()
121
062e51ad7b19 Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents: 114
diff changeset
827 if self.cur_token != ']':
062e51ad7b19 Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents: 114
diff changeset
828 raise PathSyntaxError('Expected "]" to close predicate, '
139
8332287b5508 Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents: 137
diff changeset
829 'but found "%s"' % self.cur_token,
8332287b5508 Implement position reporting for XPath syntax errors. Closes #20.
cmlenz
parents: 137
diff changeset
830 self.filename, self.lineno)
111
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
831 if not self.at_end:
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
832 self.next_token()
2368c3becc52 Some fixes and more unit tests for the XPath engine.
cmlenz
parents: 106
diff changeset
833 return expr
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
834
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
835 def _or_expr(self):
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
836 expr = self._and_expr()
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
837 while self.cur_token == 'or':
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
838 self.next_token()
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
839 expr = OrOperator(expr, self._and_expr())
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
840 return expr
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
841
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
842 def _and_expr(self):
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
843 expr = self._equality_expr()
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
844 while self.cur_token == 'and':
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
845 self.next_token()
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
846 expr = AndOperator(expr, self._equality_expr())
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
847 return expr
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
848
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
849 def _equality_expr(self):
162
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
850 expr = self._relational_expr()
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
851 while self.cur_token in ('=', '!='):
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
852 op = _operator_map[self.cur_token]
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
853 self.next_token()
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
854 expr = op(expr, self._relational_expr())
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
855 return expr
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
856
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
857 def _relational_expr(self):
516
317a7f4e3c69 Implemented XPath sub-expressions.
athomas
parents: 498
diff changeset
858 expr = self._sub_expr()
162
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
859 while self.cur_token in ('>', '>=', '<', '>='):
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
860 op = _operator_map[self.cur_token]
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
861 self.next_token()
516
317a7f4e3c69 Implemented XPath sub-expressions.
athomas
parents: 498
diff changeset
862 expr = op(expr, self._sub_expr())
317a7f4e3c69 Implemented XPath sub-expressions.
athomas
parents: 498
diff changeset
863 return expr
317a7f4e3c69 Implemented XPath sub-expressions.
athomas
parents: 498
diff changeset
864
317a7f4e3c69 Implemented XPath sub-expressions.
athomas
parents: 498
diff changeset
865 def _sub_expr(self):
317a7f4e3c69 Implemented XPath sub-expressions.
athomas
parents: 498
diff changeset
866 token = self.cur_token
317a7f4e3c69 Implemented XPath sub-expressions.
athomas
parents: 498
diff changeset
867 if token != '(':
317a7f4e3c69 Implemented XPath sub-expressions.
athomas
parents: 498
diff changeset
868 return self._primary_expr()
317a7f4e3c69 Implemented XPath sub-expressions.
athomas
parents: 498
diff changeset
869 self.next_token()
317a7f4e3c69 Implemented XPath sub-expressions.
athomas
parents: 498
diff changeset
870 expr = self._or_expr()
317a7f4e3c69 Implemented XPath sub-expressions.
athomas
parents: 498
diff changeset
871 if self.cur_token != ')':
317a7f4e3c69 Implemented XPath sub-expressions.
athomas
parents: 498
diff changeset
872 raise PathSyntaxError('Expected ")" to close sub-expression, '
317a7f4e3c69 Implemented XPath sub-expressions.
athomas
parents: 498
diff changeset
873 'but found "%s"' % self.cur_token,
317a7f4e3c69 Implemented XPath sub-expressions.
athomas
parents: 498
diff changeset
874 self.filename, self.lineno)
317a7f4e3c69 Implemented XPath sub-expressions.
athomas
parents: 498
diff changeset
875 self.next_token()
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
876 return expr
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
877
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
878 def _primary_expr(self):
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
879 token = self.cur_token
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
880 if len(token) > 1 and (token[0], token[-1]) in self._QUOTES:
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
881 self.next_token()
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
882 return StringLiteral(token[1:-1])
163
9c023c395e44 Support for XPath number literals including decimal places.
cmlenz
parents: 162
diff changeset
883 elif token[0].isdigit() or token[0] == '.':
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
884 self.next_token()
518
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
885 return NumberLiteral(as_float(token))
179
13909179e5e1 Implemented support for XPath variables in predicates (#31).
cmlenz
parents: 169
diff changeset
886 elif token == '$':
13909179e5e1 Implemented support for XPath variables in predicates (#31).
cmlenz
parents: 169
diff changeset
887 token = self.next_token()
13909179e5e1 Implemented support for XPath variables in predicates (#31).
cmlenz
parents: 169
diff changeset
888 self.next_token()
13909179e5e1 Implemented support for XPath variables in predicates (#31).
cmlenz
parents: 169
diff changeset
889 return VariableReference(token)
121
062e51ad7b19 Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents: 114
diff changeset
890 elif not self.at_end and self.peek_token().startswith('('):
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
891 return self._function_call()
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
892 else:
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
893 axis = None
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
894 if token == '@':
114
4c4e81d12649 Use constants for axes in XPath engine.
cmlenz
parents: 111
diff changeset
895 axis = ATTRIBUTE
106
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
896 self.next_token()
f9473bdc93b2 Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents: 77
diff changeset
897 return self._node_test(axis)
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
898
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
899 def _function_call(self):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
900 name = self.cur_token
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
901 if self.next_token() == '()':
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
902 args = []
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
903 else:
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
904 assert self.cur_token == '('
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
905 self.next_token()
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
906 args = [self._or_expr()]
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
907 while self.cur_token == ',':
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
908 self.next_token()
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
909 args.append(self._or_expr())
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
910 if not self.cur_token == ')':
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
911 raise PathSyntaxError('Expected ")" to close function argument '
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
912 'list, but found "%s"' % self.cur_token,
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
913 self.filename, self.lineno)
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
914 self.next_token()
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
915 cls = _function_map.get(name)
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
916 if not cls:
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
917 raise PathSyntaxError('Unsupported function "%s"' % name,
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
918 self.filename, self.lineno)
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
919 return cls(*args)
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
920
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
921
518
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
922 # Type coercion
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
923
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
924 def as_scalar(value):
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
925 """Convert value to a scalar. If a single element Attrs() object is passed
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
926 the value of the single attribute will be returned."""
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
927 if isinstance(value, Attrs):
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
928 assert len(value) == 1
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
929 return value[0][1]
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
930 else:
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
931 return value
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
932
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
933 def as_float(value):
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
934 # FIXME - if value is a bool it will be coerced to 0.0 and consequently
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
935 # compared as a float. This is probably not ideal.
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
936 return float(as_scalar(value))
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
937
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
938 def as_long(value):
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
939 return long(as_scalar(value))
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
940
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
941 def as_string(value):
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
942 value = as_scalar(value)
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
943 if value is False:
852
07f4339fecb0 Remove usage of unicode literals in a couple of places where they were not strictly necessary.
cmlenz
parents: 822
diff changeset
944 return ''
518
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
945 return unicode(value)
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
946
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
947 def as_bool(value):
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
948 return bool(as_scalar(value))
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
949
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
950
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
951 # Node tests
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
952
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
953 class PrincipalTypeTest(object):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
954 """Node test that matches any event with the given principal type."""
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
955 __slots__ = ['principal_type']
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
956 def __init__(self, principal_type):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
957 self.principal_type = principal_type
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
958 def __call__(self, kind, data, pos, namespaces, variables):
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
959 if kind is START:
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
960 if self.principal_type is ATTRIBUTE:
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
961 return data[1] or None
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
962 else:
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
963 return True
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
964 def __repr__(self):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
965 return '*'
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
966
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
967 class QualifiedPrincipalTypeTest(object):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
968 """Node test that matches any event with the given principal type in a
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
969 specific namespace."""
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
970 __slots__ = ['principal_type', 'prefix']
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
971 def __init__(self, principal_type, prefix):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
972 self.principal_type = principal_type
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
973 self.prefix = prefix
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
974 def __call__(self, kind, data, pos, namespaces, variables):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
975 namespace = Namespace(namespaces.get(self.prefix))
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
976 if kind is START:
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
977 if self.principal_type is ATTRIBUTE and data[1]:
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
978 return Attrs([(name, value) for name, value in data[1]
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
979 if name in namespace]) or None
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
980 else:
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
981 return data[0] in namespace
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
982 def __repr__(self):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
983 return '%s:*' % self.prefix
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
984
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
985 class LocalNameTest(object):
364
7dabedbb53fb Fix for #77: match templates were matching their own output.
cmlenz
parents: 363
diff changeset
986 """Node test that matches any event with the given principal type and
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
987 local name.
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
988 """
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
989 __slots__ = ['principal_type', 'name']
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
990 def __init__(self, principal_type, name):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
991 self.principal_type = principal_type
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
992 self.name = name
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
993 def __call__(self, kind, data, pos, namespaces, variables):
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
994 if kind is START:
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
995 if self.principal_type is ATTRIBUTE and self.name in data[1]:
518
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
996 return Attrs([(self.name, data[1].get(self.name))])
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
997 else:
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
998 return data[0].localname == self.name
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
999 def __repr__(self):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1000 return self.name
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1001
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1002 class QualifiedNameTest(object):
364
7dabedbb53fb Fix for #77: match templates were matching their own output.
cmlenz
parents: 363
diff changeset
1003 """Node test that matches any event with the given principal type and
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1004 qualified name.
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1005 """
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1006 __slots__ = ['principal_type', 'prefix', 'name']
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1007 def __init__(self, principal_type, prefix, name):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1008 self.principal_type = principal_type
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1009 self.prefix = prefix
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1010 self.name = name
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1011 def __call__(self, kind, data, pos, namespaces, variables):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1012 qname = QName('%s}%s' % (namespaces.get(self.prefix), self.name))
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1013 if kind is START:
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1014 if self.principal_type is ATTRIBUTE and qname in data[1]:
518
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
1015 return Attrs([(self.name, data[1].get(self.name))])
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1016 else:
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1017 return data[0] == qname
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1018 def __repr__(self):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1019 return '%s:%s' % (self.prefix, self.name)
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1020
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1021 class CommentNodeTest(object):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1022 """Node test that matches any comment events."""
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1023 __slots__ = []
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1024 def __call__(self, kind, data, pos, namespaces, variables):
330
3e749eaa3100 XPath tests should never return event tuples, just values or booleans.
cmlenz
parents: 326
diff changeset
1025 return kind is COMMENT
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1026 def __repr__(self):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1027 return 'comment()'
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1028
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1029 class NodeTest(object):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1030 """Node test that matches any node."""
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1031 __slots__ = []
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1032 def __call__(self, kind, data, pos, namespaces, variables):
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1033 if kind is START:
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1034 return True
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1035 return kind, data, pos
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1036 def __repr__(self):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1037 return 'node()'
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1038
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1039 class ProcessingInstructionNodeTest(object):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1040 """Node test that matches any processing instruction event."""
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1041 __slots__ = ['target']
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1042 def __init__(self, target=None):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1043 self.target = target
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1044 def __call__(self, kind, data, pos, namespaces, variables):
330
3e749eaa3100 XPath tests should never return event tuples, just values or booleans.
cmlenz
parents: 326
diff changeset
1045 return kind is PI and (not self.target or data[0] == self.target)
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1046 def __repr__(self):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1047 arg = ''
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1048 if self.target:
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1049 arg = '"' + self.target + '"'
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1050 return 'processing-instruction(%s)' % arg
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1051
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1052 class TextNodeTest(object):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1053 """Node test that matches any text event."""
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1054 __slots__ = []
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1055 def __call__(self, kind, data, pos, namespaces, variables):
330
3e749eaa3100 XPath tests should never return event tuples, just values or booleans.
cmlenz
parents: 326
diff changeset
1056 return kind is TEXT
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1057 def __repr__(self):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1058 return 'text()'
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1059
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1060 _nodetest_map = {'comment': CommentNodeTest, 'node': NodeTest,
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1061 'processing-instruction': ProcessingInstructionNodeTest,
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1062 'text': TextNodeTest}
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1063
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1064 # Functions
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1065
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1066 class Function(object):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1067 """Base class for function nodes in XPath expressions."""
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1068
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1069 class BooleanFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1070 """The `boolean` function, which converts its argument to a boolean
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1071 value.
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1072 """
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1073 __slots__ = ['expr']
818
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
1074 _return_type = bool
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1075 def __init__(self, expr):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1076 self.expr = expr
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1077 def __call__(self, kind, data, pos, namespaces, variables):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1078 val = self.expr(kind, data, pos, namespaces, variables)
518
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
1079 return as_bool(val)
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1080 def __repr__(self):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1081 return 'boolean(%r)' % self.expr
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1082
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1083 class CeilingFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1084 """The `ceiling` function, which returns the nearest lower integer number
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1085 for the given number.
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1086 """
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1087 __slots__ = ['number']
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1088 def __init__(self, number):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1089 self.number = number
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1090 def __call__(self, kind, data, pos, namespaces, variables):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1091 number = self.number(kind, data, pos, namespaces, variables)
518
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
1092 return ceil(as_float(number))
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1093 def __repr__(self):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1094 return 'ceiling(%r)' % self.number
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1095
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1096 class ConcatFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1097 """The `concat` function, which concatenates (joins) the variable number of
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1098 strings it gets as arguments.
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1099 """
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1100 __slots__ = ['exprs']
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1101 def __init__(self, *exprs):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1102 self.exprs = exprs
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1103 def __call__(self, kind, data, pos, namespaces, variables):
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1104 strings = []
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1105 for item in [expr(kind, data, pos, namespaces, variables)
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1106 for expr in self.exprs]:
518
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
1107 strings.append(as_string(item))
852
07f4339fecb0 Remove usage of unicode literals in a couple of places where they were not strictly necessary.
cmlenz
parents: 822
diff changeset
1108 return ''.join(strings)
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1109 def __repr__(self):
169
dc6676d3b697 Fix syntax error in `path` module.
cmlenz
parents: 164
diff changeset
1110 return 'concat(%s)' % ', '.join([repr(expr) for expr in self.exprs])
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1111
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1112 class ContainsFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1113 """The `contains` function, which returns whether a string contains a given
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1114 substring.
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1115 """
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1116 __slots__ = ['string1', 'string2']
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1117 def __init__(self, string1, string2):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1118 self.string1 = string1
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1119 self.string2 = string2
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1120 def __call__(self, kind, data, pos, namespaces, variables):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1121 string1 = self.string1(kind, data, pos, namespaces, variables)
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1122 string2 = self.string2(kind, data, pos, namespaces, variables)
518
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
1123 return as_string(string2) in as_string(string1)
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1124 def __repr__(self):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1125 return 'contains(%r, %r)' % (self.string1, self.string2)
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1126
534
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
1127 class MatchesFunction(Function):
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
1128 """The `matches` function, which returns whether a string matches a regular
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
1129 expression.
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
1130 """
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
1131 __slots__ = ['string1', 'string2']
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
1132 flag_mapping = {'s': re.S, 'm': re.M, 'i': re.I, 'x': re.X}
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
1133
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
1134 def __init__(self, string1, string2, flags=''):
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
1135 self.string1 = string1
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
1136 self.string2 = string2
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
1137 self.flags = self._map_flags(flags)
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
1138 def __call__(self, kind, data, pos, namespaces, variables):
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
1139 string1 = as_string(self.string1(kind, data, pos, namespaces, variables))
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
1140 string2 = as_string(self.string2(kind, data, pos, namespaces, variables))
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
1141 return re.search(string2, string1, self.flags)
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
1142 def _map_flags(self, flags):
593
0af1b7982ba5 Minor, cosmetic tweaks.
cmlenz
parents: 534
diff changeset
1143 return reduce(operator.or_,
534
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
1144 [self.flag_map[flag] for flag in flags], re.U)
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
1145 def __repr__(self):
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
1146 return 'contains(%r, %r)' % (self.string1, self.string2)
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
1147
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1148 class FalseFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1149 """The `false` function, which always returns the boolean `false` value."""
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1150 __slots__ = []
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1151 def __call__(self, kind, data, pos, namespaces, variables):
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1152 return False
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1153 def __repr__(self):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1154 return 'false()'
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1156 class FloorFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1157 """The `ceiling` function, which returns the nearest higher integer number
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1158 for the given number.
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1159 """
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1160 __slots__ = ['number']
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1161 def __init__(self, number):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1162 self.number = number
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1163 def __call__(self, kind, data, pos, namespaces, variables):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1164 number = self.number(kind, data, pos, namespaces, variables)
518
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
1165 return floor(as_float(number))
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1166 def __repr__(self):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1167 return 'floor(%r)' % self.number
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1168
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1169 class LocalNameFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1170 """The `local-name` function, which returns the local name of the current
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1171 element.
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1172 """
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1173 __slots__ = []
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1174 def __call__(self, kind, data, pos, namespaces, variables):
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1175 if kind is START:
234
39c424b80edd * Minor simplification of XPath engine.
cmlenz
parents: 230
diff changeset
1176 return data[0].localname
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1177 def __repr__(self):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1178 return 'local-name()'
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1179
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1180 class NameFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1181 """The `name` function, which returns the qualified name of the current
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1182 element.
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1183 """
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1184 __slots__ = []
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1185 def __call__(self, kind, data, pos, namespaces, variables):
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1186 if kind is START:
234
39c424b80edd * Minor simplification of XPath engine.
cmlenz
parents: 230
diff changeset
1187 return data[0]
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1188 def __repr__(self):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1189 return 'name()'
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1190
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1191 class NamespaceUriFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1192 """The `namespace-uri` function, which returns the namespace URI of the
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1193 current element.
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1194 """
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1195 __slots__ = []
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1196 def __call__(self, kind, data, pos, namespaces, variables):
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1197 if kind is START:
234
39c424b80edd * Minor simplification of XPath engine.
cmlenz
parents: 230
diff changeset
1198 return data[0].namespace
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1199 def __repr__(self):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1200 return 'namespace-uri()'
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1201
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1202 class NotFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1203 """The `not` function, which returns the negated boolean value of its
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1204 argument.
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1205 """
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1206 __slots__ = ['expr']
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1207 def __init__(self, expr):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1208 self.expr = expr
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1209 def __call__(self, kind, data, pos, namespaces, variables):
518
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
1210 return not as_bool(self.expr(kind, data, pos, namespaces, variables))
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1211 def __repr__(self):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1212 return 'not(%s)' % self.expr
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1213
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1214 class NormalizeSpaceFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1215 """The `normalize-space` function, which removes leading and trailing
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1216 whitespace in the given string, and replaces multiple adjacent whitespace
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1217 characters inside the string with a single space.
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1218 """
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1219 __slots__ = ['expr']
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1220 _normalize = re.compile(r'\s{2,}').sub
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1221 def __init__(self, expr):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1222 self.expr = expr
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1223 def __call__(self, kind, data, pos, namespaces, variables):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1224 string = self.expr(kind, data, pos, namespaces, variables)
518
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
1225 return self._normalize(' ', as_string(string).strip())
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1226 def __repr__(self):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1227 return 'normalize-space(%s)' % repr(self.expr)
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1228
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1229 class NumberFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1230 """The `number` function that converts its argument to a number."""
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1231 __slots__ = ['expr']
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1232 def __init__(self, expr):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1233 self.expr = expr
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1234 def __call__(self, kind, data, pos, namespaces, variables):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1235 val = self.expr(kind, data, pos, namespaces, variables)
518
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
1236 return as_float(val)
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1237 def __repr__(self):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1238 return 'number(%r)' % self.expr
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1239
162
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1240 class RoundFunction(Function):
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1241 """The `round` function, which returns the nearest integer number for the
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1242 given number.
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1243 """
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1244 __slots__ = ['number']
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1245 def __init__(self, number):
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1246 self.number = number
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1247 def __call__(self, kind, data, pos, namespaces, variables):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1248 number = self.number(kind, data, pos, namespaces, variables)
518
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
1249 return round(as_float(number))
162
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1250 def __repr__(self):
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1251 return 'round(%r)' % self.number
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1252
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1253 class StartsWithFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1254 """The `starts-with` function that returns whether one string starts with
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1255 a given substring.
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1256 """
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1257 __slots__ = ['string1', 'string2']
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1258 def __init__(self, string1, string2):
282
24b3cbbc1b1b Fix `starts-with()` XPath function so that it actually compares the two strings. Closes #61.
cmlenz
parents: 259
diff changeset
1259 self.string1 = string1
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1260 self.string2 = string2
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1261 def __call__(self, kind, data, pos, namespaces, variables):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1262 string1 = self.string1(kind, data, pos, namespaces, variables)
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1263 string2 = self.string2(kind, data, pos, namespaces, variables)
518
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
1264 return as_string(string1).startswith(as_string(string2))
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1265 def __repr__(self):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1266 return 'starts-with(%r, %r)' % (self.string1, self.string2)
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1267
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1268 class StringLengthFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1269 """The `string-length` function that returns the length of the given
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1270 string.
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1271 """
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1272 __slots__ = ['expr']
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1273 def __init__(self, expr):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1274 self.expr = expr
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1275 def __call__(self, kind, data, pos, namespaces, variables):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1276 string = self.expr(kind, data, pos, namespaces, variables)
518
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
1277 return len(as_string(string))
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1278 def __repr__(self):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1279 return 'string-length(%r)' % self.expr
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1280
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1281 class SubstringFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1282 """The `substring` function that returns the part of a string that starts
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1283 at the given offset, and optionally limited to the given length.
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1284 """
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1285 __slots__ = ['string', 'start', 'length']
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1286 def __init__(self, string, start, length=None):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1287 self.string = string
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1288 self.start = start
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1289 self.length = length
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1290 def __call__(self, kind, data, pos, namespaces, variables):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1291 string = self.string(kind, data, pos, namespaces, variables)
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1292 start = self.start(kind, data, pos, namespaces, variables)
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1293 length = 0
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1294 if self.length is not None:
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1295 length = self.length(kind, data, pos, namespaces, variables)
518
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
1296 return string[as_long(start):len(as_string(string)) - as_long(length)]
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1297 def __repr__(self):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1298 if self.length is not None:
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1299 return 'substring(%r, %r, %r)' % (self.string, self.start,
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1300 self.length)
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1301 else:
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1302 return 'substring(%r, %r)' % (self.string, self.start)
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1303
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1304 class SubstringAfterFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1305 """The `substring-after` function that returns the part of a string that
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1306 is found after the given substring.
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1307 """
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1308 __slots__ = ['string1', 'string2']
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1309 def __init__(self, string1, string2):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1310 self.string1 = string1
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1311 self.string2 = string2
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1312 def __call__(self, kind, data, pos, namespaces, variables):
518
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
1313 string1 = as_string(self.string1(kind, data, pos, namespaces, variables))
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
1314 string2 = as_string(self.string2(kind, data, pos, namespaces, variables))
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1315 index = string1.find(string2)
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1316 if index >= 0:
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1317 return string1[index + len(string2):]
852
07f4339fecb0 Remove usage of unicode literals in a couple of places where they were not strictly necessary.
cmlenz
parents: 822
diff changeset
1318 return ''
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1319 def __repr__(self):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1320 return 'substring-after(%r, %r)' % (self.string1, self.string2)
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1321
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1322 class SubstringBeforeFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1323 """The `substring-before` function that returns the part of a string that
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1324 is found before the given substring.
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1325 """
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1326 __slots__ = ['string1', 'string2']
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1327 def __init__(self, string1, string2):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1328 self.string1 = string1
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1329 self.string2 = string2
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1330 def __call__(self, kind, data, pos, namespaces, variables):
518
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
1331 string1 = as_string(self.string1(kind, data, pos, namespaces, variables))
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
1332 string2 = as_string(self.string2(kind, data, pos, namespaces, variables))
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1333 index = string1.find(string2)
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1334 if index >= 0:
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1335 return string1[:index]
852
07f4339fecb0 Remove usage of unicode literals in a couple of places where they were not strictly necessary.
cmlenz
parents: 822
diff changeset
1336 return ''
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1337 def __repr__(self):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1338 return 'substring-after(%r, %r)' % (self.string1, self.string2)
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1339
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1340 class TranslateFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1341 """The `translate` function that translates a set of characters in a
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1342 string to target set of characters.
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1343 """
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1344 __slots__ = ['string', 'fromchars', 'tochars']
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1345 def __init__(self, string, fromchars, tochars):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1346 self.string = string
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1347 self.fromchars = fromchars
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1348 self.tochars = tochars
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1349 def __call__(self, kind, data, pos, namespaces, variables):
518
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
1350 string = as_string(self.string(kind, data, pos, namespaces, variables))
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
1351 fromchars = as_string(self.fromchars(kind, data, pos, namespaces, variables))
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
1352 tochars = as_string(self.tochars(kind, data, pos, namespaces, variables))
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1353 table = dict(zip([ord(c) for c in fromchars],
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1354 [ord(c) for c in tochars]))
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1355 return string.translate(table)
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1356 def __repr__(self):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1357 return 'translate(%r, %r, %r)' % (self.string, self.fromchars,
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1358 self.tochars)
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1359
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1360 class TrueFunction(Function):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1361 """The `true` function, which always returns the boolean `true` value."""
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1362 __slots__ = []
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1363 def __call__(self, kind, data, pos, namespaces, variables):
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1364 return True
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1365 def __repr__(self):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1366 return 'true()'
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1367
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1368 _function_map = {'boolean': BooleanFunction, 'ceiling': CeilingFunction,
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1369 'concat': ConcatFunction, 'contains': ContainsFunction,
534
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
1370 'matches': MatchesFunction, 'false': FalseFunction, 'floor':
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
1371 FloorFunction, 'local-name': LocalNameFunction, 'name':
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
1372 NameFunction, 'namespace-uri': NamespaceUriFunction,
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1373 'normalize-space': NormalizeSpaceFunction, 'not': NotFunction,
162
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1374 'number': NumberFunction, 'round': RoundFunction,
534
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
1375 'starts-with': StartsWithFunction, 'string-length':
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
1376 StringLengthFunction, 'substring': SubstringFunction,
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
1377 'substring-after': SubstringAfterFunction, 'substring-before':
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
1378 SubstringBeforeFunction, 'translate': TranslateFunction,
57b5d5138f1a Add XPath `matches()` function which, of course, supports the Python regular
athomas
parents: 518
diff changeset
1379 'true': TrueFunction}
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1380
179
13909179e5e1 Implemented support for XPath variables in predicates (#31).
cmlenz
parents: 169
diff changeset
1381 # Literals & Variables
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1382
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1383 class Literal(object):
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1384 """Abstract base class for literal nodes."""
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1385
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1386 class StringLiteral(Literal):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1387 """A string literal node."""
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1388 __slots__ = ['text']
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1389 def __init__(self, text):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1390 self.text = text
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1391 def __call__(self, kind, data, pos, namespaces, variables):
228
a5b38b459cbb Add support for position predicates in XPath expressions.
cmlenz
parents: 224
diff changeset
1392 return self.text
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1393 def __repr__(self):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1394 return '"%s"' % self.text
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1395
155
9a5aedda1099 * String literals in XPath expressions that contains spaces are now tokenizes correctly.
cmlenz
parents: 145
diff changeset
1396 class NumberLiteral(Literal):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1397 """A number literal node."""
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1398 __slots__ = ['number']
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1399 def __init__(self, number):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1400 self.number = number
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1401 def __call__(self, kind, data, pos, namespaces, variables):
228
a5b38b459cbb Add support for position predicates in XPath expressions.
cmlenz
parents: 224
diff changeset
1402 return self.number
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1403 def __repr__(self):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1404 return str(self.number)
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1405
179
13909179e5e1 Implemented support for XPath variables in predicates (#31).
cmlenz
parents: 169
diff changeset
1406 class VariableReference(Literal):
13909179e5e1 Implemented support for XPath variables in predicates (#31).
cmlenz
parents: 169
diff changeset
1407 """A variable reference node."""
13909179e5e1 Implemented support for XPath variables in predicates (#31).
cmlenz
parents: 169
diff changeset
1408 __slots__ = ['name']
13909179e5e1 Implemented support for XPath variables in predicates (#31).
cmlenz
parents: 169
diff changeset
1409 def __init__(self, name):
13909179e5e1 Implemented support for XPath variables in predicates (#31).
cmlenz
parents: 169
diff changeset
1410 self.name = name
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1411 def __call__(self, kind, data, pos, namespaces, variables):
228
a5b38b459cbb Add support for position predicates in XPath expressions.
cmlenz
parents: 224
diff changeset
1412 return variables.get(self.name)
179
13909179e5e1 Implemented support for XPath variables in predicates (#31).
cmlenz
parents: 169
diff changeset
1413 def __repr__(self):
215
94120e6b0ce4 A couple of minor XPath fixes.
cmlenz
parents: 211
diff changeset
1414 return str(self.name)
179
13909179e5e1 Implemented support for XPath variables in predicates (#31).
cmlenz
parents: 169
diff changeset
1415
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1416 # Operators
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1417
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1418 class AndOperator(object):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1419 """The boolean operator `and`."""
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1420 __slots__ = ['lval', 'rval']
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1421 def __init__(self, lval, rval):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1422 self.lval = lval
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1423 self.rval = rval
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1424 def __call__(self, kind, data, pos, namespaces, variables):
518
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
1425 lval = as_bool(self.lval(kind, data, pos, namespaces, variables))
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1426 if not lval:
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1427 return False
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1428 rval = self.rval(kind, data, pos, namespaces, variables)
518
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
1429 return as_bool(rval)
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1430 def __repr__(self):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1431 return '%s and %s' % (self.lval, self.rval)
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1432
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1433 class EqualsOperator(object):
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1434 """The equality operator `=`."""
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1435 __slots__ = ['lval', 'rval']
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1436 def __init__(self, lval, rval):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1437 self.lval = lval
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1438 self.rval = rval
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1439 def __call__(self, kind, data, pos, namespaces, variables):
518
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
1440 lval = as_scalar(self.lval(kind, data, pos, namespaces, variables))
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
1441 rval = as_scalar(self.rval(kind, data, pos, namespaces, variables))
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1442 return lval == rval
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1443 def __repr__(self):
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1444 return '%s=%s' % (self.lval, self.rval)
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1445
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1446 class NotEqualsOperator(object):
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1447 """The equality operator `!=`."""
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1448 __slots__ = ['lval', 'rval']
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1449 def __init__(self, lval, rval):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1450 self.lval = lval
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1451 self.rval = rval
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1452 def __call__(self, kind, data, pos, namespaces, variables):
518
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
1453 lval = as_scalar(self.lval(kind, data, pos, namespaces, variables))
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
1454 rval = as_scalar(self.rval(kind, data, pos, namespaces, variables))
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1455 return lval != rval
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1456 def __repr__(self):
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1457 return '%s!=%s' % (self.lval, self.rval)
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1458
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1459 class OrOperator(object):
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1460 """The boolean operator `or`."""
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1461 __slots__ = ['lval', 'rval']
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1462 def __init__(self, lval, rval):
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1463 self.lval = lval
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1464 self.rval = rval
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1465 def __call__(self, kind, data, pos, namespaces, variables):
518
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
1466 lval = as_bool(self.lval(kind, data, pos, namespaces, variables))
161
7b1f07496bf7 Various docstring additions and other cosmetic changes.
cmlenz
parents: 156
diff changeset
1467 if lval:
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1468 return True
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1469 rval = self.rval(kind, data, pos, namespaces, variables)
518
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
1470 return as_bool(rval)
137
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1471 def __repr__(self):
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1472 return '%s or %s' % (self.lval, self.rval)
ac0bc4a6aeba Further cleanup of XPath engine.
cmlenz
parents: 122
diff changeset
1473
162
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1474 class GreaterThanOperator(object):
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1475 """The relational operator `>` (greater than)."""
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1476 __slots__ = ['lval', 'rval']
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1477 def __init__(self, lval, rval):
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1478 self.lval = lval
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1479 self.rval = rval
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1480 def __call__(self, kind, data, pos, namespaces, variables):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1481 lval = self.lval(kind, data, pos, namespaces, variables)
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1482 rval = self.rval(kind, data, pos, namespaces, variables)
518
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
1483 return as_float(lval) > as_float(rval)
162
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1484 def __repr__(self):
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1485 return '%s>%s' % (self.lval, self.rval)
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1486
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1487 class GreaterThanOrEqualOperator(object):
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1488 """The relational operator `>=` (greater than or equal)."""
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1489 __slots__ = ['lval', 'rval']
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1490 def __init__(self, lval, rval):
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1491 self.lval = lval
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1492 self.rval = rval
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1493 def __call__(self, kind, data, pos, namespaces, variables):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1494 lval = self.lval(kind, data, pos, namespaces, variables)
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1495 rval = self.rval(kind, data, pos, namespaces, variables)
518
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
1496 return as_float(lval) >= as_float(rval)
162
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1497 def __repr__(self):
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1498 return '%s>=%s' % (self.lval, self.rval)
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1499
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1500 class LessThanOperator(object):
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1501 """The relational operator `<` (less than)."""
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1502 __slots__ = ['lval', 'rval']
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1503 def __init__(self, lval, rval):
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1504 self.lval = lval
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1505 self.rval = rval
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1506 def __call__(self, kind, data, pos, namespaces, variables):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1507 lval = self.lval(kind, data, pos, namespaces, variables)
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1508 rval = self.rval(kind, data, pos, namespaces, variables)
518
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
1509 return as_float(lval) < as_float(rval)
162
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1510 def __repr__(self):
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1511 return '%s<%s' % (self.lval, self.rval)
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1512
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1513 class LessThanOrEqualOperator(object):
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1514 """The relational operator `<=` (less than or equal)."""
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1515 __slots__ = ['lval', 'rval']
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1516 def __init__(self, lval, rval):
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1517 self.lval = lval
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1518 self.rval = rval
224
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1519 def __call__(self, kind, data, pos, namespaces, variables):
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1520 lval = self.lval(kind, data, pos, namespaces, variables)
90d62225f411 Implement support for namespace prefixes in XPath expressions.
cmlenz
parents: 223
diff changeset
1521 rval = self.rval(kind, data, pos, namespaces, variables)
518
c1f2a859fd75 Attributes selected with an XPath are now returned as an `Attrs()` object in
athomas
parents: 516
diff changeset
1522 return as_float(lval) <= as_float(rval)
162
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1523 def __repr__(self):
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1524 return '%s<=%s' % (self.lval, self.rval)
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1525
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1526 _operator_map = {'=': EqualsOperator, '!=': NotEqualsOperator,
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1527 '>': GreaterThanOperator, '>=': GreaterThanOrEqualOperator,
456039594db9 Implement the XPath relational operators and the `round()` function.
cmlenz
parents: 161
diff changeset
1528 '<': LessThanOperator, '>=': LessThanOrEqualOperator}
333
e5ba8c5a30fe Fix XPath traversal in match templates. Previously, `div/p` would be treated the same as `div//p`, i.e. it would match all descendants and not just the immediate children.
cmlenz
parents: 330
diff changeset
1529
e5ba8c5a30fe Fix XPath traversal in match templates. Previously, `div/p` would be treated the same as `div//p`, i.e. it would match all descendants and not just the immediate children.
cmlenz
parents: 330
diff changeset
1530
e5ba8c5a30fe Fix XPath traversal in match templates. Previously, `div/p` would be treated the same as `div//p`, i.e. it would match all descendants and not just the immediate children.
cmlenz
parents: 330
diff changeset
1531 _DOTSLASHSLASH = (DESCENDANT_OR_SELF, PrincipalTypeTest(None), ())
818
2886abff9502 Merged soc2008-xpath branch back into trunk.
cmlenz
parents: 792
diff changeset
1532 _DOTSLASH = (SELF, PrincipalTypeTest(None), ())
Copyright (C) 2012-2017 Edgewall Software