Mercurial > genshi > genshi-test
annotate markup/path.py @ 121:22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
author | cmlenz |
---|---|
date | Wed, 02 Aug 2006 12:55:05 +0000 |
parents | 8f53c3ad385c |
children | 441545dbed4a |
rev | line source |
---|---|
1 | 1 # -*- coding: utf-8 -*- |
2 # | |
66
822089ae65ce
Switch copyright to Edgewall and URLs to markup.edgewall.org.
cmlenz
parents:
61
diff
changeset
|
3 # Copyright (C) 2006 Edgewall Software |
1 | 4 # All rights reserved. |
5 # | |
6 # This software is licensed as described in the file COPYING, which | |
7 # you should have received as part of this distribution. The terms | |
66
822089ae65ce
Switch copyright to Edgewall and URLs to markup.edgewall.org.
cmlenz
parents:
61
diff
changeset
|
8 # are also available at http://markup.edgewall.org/wiki/License. |
1 | 9 # |
10 # This software consists of voluntary contributions made by many | |
11 # individuals. For the exact contribution history, see the revision | |
66
822089ae65ce
Switch copyright to Edgewall and URLs to markup.edgewall.org.
cmlenz
parents:
61
diff
changeset
|
12 # history and logs, available at http://markup.edgewall.org/log/. |
1 | 13 |
111
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
14 """Basic support for evaluating XPath expressions against streams. |
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
15 |
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
16 >>> from markup.input import XML |
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
17 >>> doc = XML('''<doc> |
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
18 ... <items count="2"> |
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
19 ... <item status="new"> |
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
20 ... <summary>Foo</summary> |
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
21 ... </item> |
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
22 ... <item status="closed"> |
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
23 ... <summary>Bar</summary> |
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
24 ... </item> |
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
25 ... </items> |
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
26 ... </doc>''') |
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
27 >>> print doc.select('items/item[@status="closed"]/summary/text()') |
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
28 Bar |
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
29 |
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
30 Because the XPath engine operates on markup streams (as opposed to tree |
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
31 structures), it only implements a subset of the full XPath 1.0 language. |
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
32 """ |
1 | 33 |
34 import re | |
35 | |
106
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
36 from markup.core import QName, Stream, START, END, TEXT, COMMENT, PI |
1 | 37 |
106
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
38 __all__ = ['Path', 'PathSyntaxError'] |
1 | 39 |
40 | |
114 | 41 class Axis(object): |
42 """Defines constants for the various supported XPath axes.""" | |
43 | |
44 ATTRIBUTE = 'attribute' | |
45 CHILD = 'child' | |
46 DESCENDANT = 'descendant' | |
47 DESCENDANT_OR_SELF = 'descendant-or-self' | |
48 NAMESPACE = 'namespace' | |
49 SELF = 'self' | |
50 | |
51 def forname(cls, name): | |
52 """Return the axis constant for the given name, or `None` if no such | |
53 axis was defined. | |
54 """ | |
55 return getattr(cls, name.upper().replace('-', '_'), None) | |
56 forname = classmethod(forname) | |
57 | |
58 | |
59 ATTRIBUTE = Axis.ATTRIBUTE | |
60 CHILD = Axis.CHILD | |
61 DESCENDANT = Axis.DESCENDANT | |
62 DESCENDANT_OR_SELF = Axis.DESCENDANT_OR_SELF | |
63 NAMESPACE = Axis.NAMESPACE | |
64 SELF = Axis.SELF | |
65 | |
66 | |
1 | 67 class Path(object): |
26
039fc5b87405
* Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents:
25
diff
changeset
|
68 """Implements basic XPath support on streams. |
1 | 69 |
26
039fc5b87405
* Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents:
25
diff
changeset
|
70 Instances of this class represent a "compiled" XPath expression, and provide |
039fc5b87405
* Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents:
25
diff
changeset
|
71 methods for testing the path against a stream, as well as extracting a |
039fc5b87405
* Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents:
25
diff
changeset
|
72 substream matching that path. |
1 | 73 """ |
74 | |
75 def __init__(self, text): | |
26
039fc5b87405
* Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents:
25
diff
changeset
|
76 """Create the path object from a string. |
039fc5b87405
* Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents:
25
diff
changeset
|
77 |
039fc5b87405
* Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents:
25
diff
changeset
|
78 @param text: the path expression |
039fc5b87405
* Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents:
25
diff
changeset
|
79 """ |
1 | 80 self.source = text |
106
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
81 self.paths = _PathParser(text).parse() |
1 | 82 |
83 def __repr__(self): | |
84 return '<%s "%s">' % (self.__class__.__name__, self.source) | |
85 | |
86 def select(self, stream): | |
26
039fc5b87405
* Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents:
25
diff
changeset
|
87 """Returns a substream of the given stream that matches the path. |
039fc5b87405
* Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents:
25
diff
changeset
|
88 |
039fc5b87405
* Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents:
25
diff
changeset
|
89 If there are no matches, this method returns an empty stream. |
039fc5b87405
* Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents:
25
diff
changeset
|
90 |
33 | 91 >>> from markup.input import XML |
92 >>> xml = XML('<root><elem><child>Text</child></elem></root>') | |
61 | 93 |
33 | 94 >>> print Path('child').select(xml) |
95 <child>Text</child> | |
96 | |
97 >>> print Path('child/text()').select(xml) | |
98 Text | |
99 | |
26
039fc5b87405
* Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents:
25
diff
changeset
|
100 @param stream: the stream to select from |
039fc5b87405
* Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents:
25
diff
changeset
|
101 @return: the substream matching the path, or an empty stream |
039fc5b87405
* Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents:
25
diff
changeset
|
102 """ |
1 | 103 stream = iter(stream) |
26
039fc5b87405
* Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents:
25
diff
changeset
|
104 def _generate(): |
1 | 105 test = self.test() |
106 for kind, data, pos in stream: | |
107 result = test(kind, data, pos) | |
108 if result is True: | |
109 yield kind, data, pos | |
110 depth = 1 | |
111 while depth > 0: | |
73 | 112 subkind, subdata, subpos = stream.next() |
113 if subkind is START: | |
114 depth += 1 | |
115 elif subkind is END: | |
116 depth -= 1 | |
117 yield subkind, subdata, subpos | |
118 test(subkind, subdata, subpos) | |
1 | 119 elif result: |
120 yield result | |
26
039fc5b87405
* Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents:
25
diff
changeset
|
121 return Stream(_generate()) |
1 | 122 |
38
fec9f4897415
Fix for #2 (incorrect context node in path expressions). Still some paths that produce incorrect results, but the common case seems to work now.
cmlenz
parents:
37
diff
changeset
|
123 def test(self, ignore_context=False): |
26
039fc5b87405
* Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents:
25
diff
changeset
|
124 """Returns a function that can be used to track whether the path matches |
039fc5b87405
* Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents:
25
diff
changeset
|
125 a specific stream event. |
039fc5b87405
* Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents:
25
diff
changeset
|
126 |
039fc5b87405
* Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents:
25
diff
changeset
|
127 The function returned expects the positional arguments `kind`, `data`, |
039fc5b87405
* Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents:
25
diff
changeset
|
128 and `pos`, i.e. basically an unpacked stream event. If the path matches |
039fc5b87405
* Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents:
25
diff
changeset
|
129 the event, the function returns the match (for example, a `START` or |
106
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
130 `TEXT` event.) Otherwise, it returns `None`. |
33 | 131 |
132 >>> from markup.input import XML | |
133 >>> xml = XML('<root><elem><child id="1"/></elem><child id="2"/></root>') | |
134 >>> test = Path('child').test() | |
135 >>> for kind, data, pos in xml: | |
136 ... if test(kind, data, pos): | |
137 ... print kind, data | |
138 START (u'child', [(u'id', u'1')]) | |
139 START (u'child', [(u'id', u'2')]) | |
26
039fc5b87405
* Split out the XPath tests into a separate `unittest`-based file.
cmlenz
parents:
25
diff
changeset
|
140 """ |
106
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
141 paths = [(idx, steps, len(steps), [0]) |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
142 for idx, steps in enumerate(self.paths)] |
1 | 143 |
144 def _test(kind, data, pos): | |
106
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
145 for idx, steps, size, stack in paths: |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
146 if not stack: |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
147 continue |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
148 cursor = stack[-1] |
1 | 149 |
106
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
150 if kind is END: |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
151 stack.pop() |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
152 continue |
1 | 153 |
106
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
154 elif kind is START: |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
155 stack.append(cursor) |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
156 |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
157 matched = None |
111
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
158 while 1: |
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
159 axis, node_test, predicates = steps[cursor] |
106
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
160 |
111
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
161 matched = node_test(kind, data, pos) |
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
162 if matched and predicates: |
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
163 for predicate in predicates: |
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
164 if not predicate(kind, data, pos): |
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
165 matched = None |
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
166 break |
106
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
167 |
111
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
168 if matched: |
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
169 if cursor + 1 == size: # the last location step |
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
170 if ignore_context or \ |
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
171 kind is not START or \ |
114 | 172 axis in (ATTRIBUTE, SELF) or \ |
111
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
173 len(stack) > 2: |
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
174 return matched |
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
175 else: |
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
176 cursor += 1 |
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
177 stack[-1] = cursor |
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
178 |
114 | 179 if axis is not SELF: |
111
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
180 break |
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
181 |
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
182 if not matched and kind is START \ |
114 | 183 and axis not in (DESCENDANT, DESCENDANT_OR_SELF): |
106
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
184 # If this step is not a closure, it cannot be matched until |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
185 # the current element is closed... so we need to move the |
114 | 186 # cursor back to the previous closure and retest that |
187 # against the current element | |
111
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
188 backsteps = [step for step in steps[:cursor] |
114 | 189 if step[0] in (DESCENDANT, DESCENDANT_OR_SELF)] |
111
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
190 backsteps.reverse() |
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
191 for axis, node_test, predicates in backsteps: |
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
192 matched = node_test(kind, data, pos) |
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
193 if not matched: |
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
194 cursor -= 1 |
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
195 break |
106
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
196 stack[-1] = cursor |
1 | 197 |
198 return None | |
199 | |
200 return _test | |
201 | |
202 | |
106
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
203 def _node_test_current_element(): |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
204 def _node_test_current_element(kind, *_): |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
205 return kind is START |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
206 return _node_test_current_element |
77
f1aa49c759b2
* Simplify implementation of the individual XPath tests (use closures instead of callable classes)
cmlenz
parents:
73
diff
changeset
|
207 |
106
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
208 def _node_test_any_child_element(): |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
209 def _node_test_any_child_element(kind, *_): |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
210 return kind is START |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
211 return _node_test_any_child_element |
77
f1aa49c759b2
* Simplify implementation of the individual XPath tests (use closures instead of callable classes)
cmlenz
parents:
73
diff
changeset
|
212 |
106
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
213 def _node_test_child_element_by_name(name): |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
214 def _node_test_child_element_by_name(kind, data, _): |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
215 return kind is START and data[0].localname == name |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
216 return _node_test_child_element_by_name |
77
f1aa49c759b2
* Simplify implementation of the individual XPath tests (use closures instead of callable classes)
cmlenz
parents:
73
diff
changeset
|
217 |
106
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
218 def _node_test_any_attribute(): |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
219 def _node_test_any_attribute(kind, data, _): |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
220 if kind is START and data[1]: |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
221 return data[1] |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
222 return _node_test_any_attribute |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
223 |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
224 def _node_test_attribute_by_name(name): |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
225 def _node_test_attribute_by_name(kind, data, pos): |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
226 if kind is START and name in data[1]: |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
227 return TEXT, data[1].get(name), pos |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
228 return _node_test_attribute_by_name |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
229 |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
230 def _function_comment(): |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
231 def _function_comment(kind, data, pos): |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
232 return kind is COMMENT and (kind, data, pos) |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
233 return _function_comment |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
234 |
121
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
235 def _function_local_name(): |
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
236 def _function_local_name(kind, data, pos): |
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
237 if kind is START: |
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
238 return TEXT, data[0].localname, pos |
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
239 return kind, data, pos |
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
240 return _function_local_name |
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
241 |
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
242 def _function_name(): |
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
243 def _function_name(kind, data, pos): |
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
244 if kind is START: |
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
245 return TEXT, data[0], pos |
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
246 return kind, data, pos |
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
247 return _function_name |
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
248 |
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
249 def _function_namespace_uri(): |
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
250 def _function_namespace_uri(kind, data, pos): |
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
251 if kind is START: |
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
252 return TEXT, data[0].namespace, pos |
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
253 return kind, data, pos |
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
254 return _function_namespace_uri |
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
255 |
106
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
256 def _function_node(): |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
257 def _function_node(kind, data, pos): |
111
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
258 if kind is START: |
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
259 return True |
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
260 return kind, data, pos |
106
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
261 return _function_node |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
262 |
121
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
263 def _function_not(expr): |
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
264 def _function_not(kind, data, pos): |
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
265 return not expr(kind, data, pos) |
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
266 return _function_not |
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
267 |
106
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
268 def _function_processing_instruction(name=None): |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
269 def _function_processing_instruction(kind, data, pos): |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
270 if kind is PI and (not name or data[0] == name): |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
271 return (kind, data, pos) |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
272 return _function_processing_instruction |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
273 |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
274 def _function_text(): |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
275 def _function_text(kind, data, pos): |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
276 return kind is TEXT and (kind, data, pos) |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
277 return _function_text |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
278 |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
279 def _literal_string(text): |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
280 def _literal_string(*_): |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
281 return TEXT, text, (None, -1, -1) |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
282 return _literal_string |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
283 |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
284 def _operator_eq(lval, rval): |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
285 def _operator_eq(kind, data, pos): |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
286 lv = lval(kind, data, pos) |
121
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
287 if type(lv) is tuple: |
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
288 lv = lv[1] |
106
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
289 rv = rval(kind, data, pos) |
121
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
290 if type(rv) is tuple: |
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
291 rv = rv[1] |
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
292 return lv == rv |
106
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
293 return _operator_eq |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
294 |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
295 def _operator_neq(lval, rval): |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
296 def _operator_neq(kind, data, pos): |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
297 lv = lval(kind, data, pos) |
121
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
298 if type(lv) is tuple: |
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
299 lv = lv[1] |
106
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
300 rv = rval(kind, data, pos) |
121
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
301 if type(rv) is tuple: |
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
302 rv = rv[1] |
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
303 return lv != rv |
106
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
304 return _operator_neq |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
305 |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
306 def _operator_and(lval, rval): |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
307 def _operator_and(kind, data, pos): |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
308 lv = lval(kind, data, pos) |
121
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
309 if type(lv) is tuple: |
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
310 lv = lv[1] |
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
311 if not lv: |
106
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
312 return False |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
313 rv = rval(kind, data, pos) |
121
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
314 if type(rv) is tuple: |
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
315 rv = rv[1] |
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
316 return bool(rv) |
106
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
317 return _operator_and |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
318 |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
319 def _operator_or(lval, rval): |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
320 def _operator_or(kind, data, pos): |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
321 lv = lval(kind, data, pos) |
121
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
322 if type(lv) is tuple: |
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
323 lv = lv[1] |
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
324 if lv: |
106
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
325 return True |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
326 rv = rval(kind, data, pos) |
121
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
327 if type(rv) is tuple: |
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
328 rv = rv[1] |
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
329 return bool(rv) |
106
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
330 return _operator_or |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
331 |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
332 |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
333 class PathSyntaxError(Exception): |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
334 """Exception raised when an XPath expression is syntactically incorrect.""" |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
335 |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
336 def __init__(self, message, filename=None, lineno=-1, offset=-1): |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
337 if filename: |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
338 message = '%s (%s, line %d)' % (message, filename, lineno) |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
339 Exception.__init__(self, message) |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
340 self.filename = filename |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
341 self.lineno = lineno |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
342 self.offset = offset |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
343 |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
344 |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
345 class _PathParser(object): |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
346 """Tokenizes and parses an XPath expression.""" |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
347 |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
348 _QUOTES = (("'", "'"), ('"', '"')) |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
349 _TOKENS = ('::', ':', '..', '.', '//', '/', '[', ']', '()', '(', ')', '@', |
121
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
350 '=', '!=', '!', '|', ',') |
106
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
351 _tokenize = re.compile('(%s)|([^%s\s]+)|\s+' % ( |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
352 '|'.join([re.escape(t) for t in _TOKENS]), |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
353 ''.join([re.escape(t[0]) for t in _TOKENS]))).findall |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
354 |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
355 def __init__(self, text): |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
356 self.tokens = filter(None, [a or b for a, b in self._tokenize(text)]) |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
357 self.pos = 0 |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
358 |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
359 # Tokenizer |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
360 |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
361 at_end = property(lambda self: self.pos == len(self.tokens) - 1) |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
362 cur_token = property(lambda self: self.tokens[self.pos]) |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
363 |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
364 def next_token(self): |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
365 self.pos += 1 |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
366 return self.tokens[self.pos] |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
367 |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
368 def peek_token(self): |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
369 if not self.at_end: |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
370 return self.tokens[self.pos + 1] |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
371 return None |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
372 |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
373 # Recursive descent parser |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
374 |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
375 def parse(self): |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
376 """Parses the XPath expression and returns a list of location path |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
377 tests. |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
378 |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
379 For union expressions (such as `*|text()`), this function returns one |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
380 test for each operand in the union. For patch expressions that don't |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
381 use the union operator, the function always returns a list of size 1. |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
382 |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
383 Each path test in turn is a sequence of tests that correspond to the |
111
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
384 location steps, each tuples of the form `(axis, testfunc, predicates)` |
106
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
385 """ |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
386 paths = [self._location_path()] |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
387 while self.cur_token == '|': |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
388 self.next_token() |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
389 paths.append(self._location_path()) |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
390 if not self.at_end: |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
391 raise PathSyntaxError('Unexpected token %r after end of expression' |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
392 % self.cur_token) |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
393 return paths |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
394 |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
395 def _location_path(self): |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
396 next_is_closure = True |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
397 steps = [] |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
398 while True: |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
399 if self.cur_token == '//': |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
400 next_is_closure = True |
111
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
401 self.next_token() |
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
402 elif self.cur_token == '/' and not steps: |
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
403 raise PathSyntaxError('Absolute location paths not supported') |
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
404 |
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
405 axis, node_test, predicates = self._location_step() |
114 | 406 if axis is CHILD and next_is_closure: |
407 axis = DESCENDANT_OR_SELF | |
111
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
408 steps.append((axis, node_test, predicates)) |
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
409 next_is_closure = False |
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
410 |
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
411 if self.at_end or not self.cur_token.startswith('/'): |
106
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
412 break |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
413 self.next_token() |
111
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
414 |
106
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
415 return steps |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
416 |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
417 def _location_step(self): |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
418 if self.cur_token == '@': |
114 | 419 axis = ATTRIBUTE |
106
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
420 self.next_token() |
111
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
421 elif self.cur_token == '.': |
114 | 422 axis = SELF |
111
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
423 elif self.peek_token() == '::': |
114 | 424 axis = Axis.forname(self.cur_token) |
425 if axis is None: | |
111
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
426 raise PathSyntaxError('Unsupport axis "%s"' % axis) |
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
427 self.next_token() |
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
428 self.next_token() |
106
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
429 else: |
114 | 430 axis = CHILD |
111
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
431 node_test = self._node_test(axis) |
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
432 predicates = [] |
106
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
433 while self.cur_token == '[': |
111
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
434 predicates.append(self._predicate()) |
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
435 return axis, node_test, predicates |
106
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
436 |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
437 def _node_test(self, axis=None): |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
438 test = None |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
439 if self.peek_token() in ('(', '()'): # Node type test |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
440 test = self._node_type() |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
441 |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
442 else: # Name test |
114 | 443 if axis is ATTRIBUTE: |
106
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
444 if self.cur_token == '*': |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
445 test = _node_test_any_attribute() |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
446 else: |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
447 test = _node_test_attribute_by_name(self.cur_token) |
114 | 448 elif axis is SELF: |
111
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
449 test = _node_test_current_element() |
106
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
450 else: |
111
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
451 if self.cur_token == '*': |
106
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
452 test = _node_test_any_child_element() |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
453 else: |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
454 test = _node_test_child_element_by_name(self.cur_token) |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
455 |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
456 if not self.at_end: |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
457 self.next_token() |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
458 return test |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
459 |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
460 def _node_type(self): |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
461 name = self.cur_token |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
462 self.next_token() |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
463 if name == 'comment': |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
464 return _function_comment() |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
465 elif name == 'node': |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
466 return _function_node() |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
467 elif name == 'processing-instruction': |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
468 args = [] |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
469 if self.cur_token != '()': |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
470 # The processing-instruction() function optionally accepts the |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
471 # name of the PI as argument, which must be a literal string |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
472 self.next_token() # ( |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
473 if self.cur_token != ')': |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
474 string = self.cur_token |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
475 if (string[0], string[-1]) in self._QUOTES: |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
476 string = string[1:-1] |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
477 args.append(string) |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
478 return _function_processing_instruction(*args) |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
479 elif name == 'text': |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
480 return _function_text() |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
481 else: |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
482 raise PathSyntaxError('%s() not allowed here' % name) |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
483 |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
484 def _predicate(self): |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
485 assert self.cur_token == '[' |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
486 self.next_token() |
111
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
487 expr = self._or_expr() |
121
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
488 if self.cur_token != ']': |
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
489 raise PathSyntaxError('Expected "]" to close predicate, ' |
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
490 'but found "%s"' % self.cur_token) |
111
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
491 if not self.at_end: |
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
492 self.next_token() |
8a4d9064f363
Some fixes and more unit tests for the XPath engine.
cmlenz
parents:
106
diff
changeset
|
493 return expr |
106
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
494 |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
495 def _or_expr(self): |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
496 expr = self._and_expr() |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
497 while self.cur_token == 'or': |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
498 self.next_token() |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
499 expr = _operator_or(expr, self._and_expr()) |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
500 return expr |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
501 |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
502 def _and_expr(self): |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
503 expr = self._equality_expr() |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
504 while self.cur_token == 'and': |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
505 self.next_token() |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
506 expr = _operator_and(expr, self._equality_expr()) |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
507 return expr |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
508 |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
509 def _equality_expr(self): |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
510 expr = self._primary_expr() |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
511 while self.cur_token in ('=', '!='): |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
512 op = {'=': _operator_eq, '!=': _operator_neq}[self.cur_token] |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
513 self.next_token() |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
514 expr = op(expr, self._primary_expr()) |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
515 return expr |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
516 |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
517 def _primary_expr(self): |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
518 token = self.cur_token |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
519 if len(token) > 1 and (token[0], token[-1]) in self._QUOTES: |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
520 self.next_token() |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
521 return _literal_string(token[1:-1]) |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
522 elif token[0].isdigit(): |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
523 self.next_token() |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
524 return _literal_number(float(token)) |
121
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
525 elif not self.at_end and self.peek_token().startswith('('): |
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
526 if self.next_token() == '()': |
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
527 args = [] |
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
528 else: |
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
529 self.next_token() |
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
530 args = [self._or_expr()] |
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
531 while self.cur_token not in (',', ')'): |
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
532 args.append(self._or_expr()) |
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
533 self.next_token() |
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
534 if token == 'local-name': |
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
535 return _function_local_name(*args) |
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
536 elif token == 'name': |
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
537 return _function_name(*args) |
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
538 elif token == 'namespace-uri': |
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
539 return _function_namespace_uri(*args) |
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
540 elif token == 'not': |
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
541 return _function_not(*args) |
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
542 else: |
22a7080ed242
Added support for the XPath functions `name()`, `namespace-uri()`, `local-name()`, and `not()`.
cmlenz
parents:
114
diff
changeset
|
543 raise PathSyntaxError('Unsupported function "%s"' % token) |
106
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
544 else: |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
545 axis = None |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
546 if token == '@': |
114 | 547 axis = ATTRIBUTE |
106
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
548 self.next_token() |
61fa4cadb766
Complete rewrite of the XPath parsing, which was a mess before. Closes #19.
cmlenz
parents:
77
diff
changeset
|
549 return self._node_test(axis) |