annotate genshi/path.py @ 820:1837f39efd6f experimental-inline

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