comparison markup/path.py @ 228:a5b38b459cbb trunk

Add support for position predicates in XPath expressions.
author cmlenz
date Fri, 08 Sep 2006 10:51:14 +0000
parents 90d62225f411
children
comparison
equal deleted inserted replaced
227:96a7e5011c69 228:a5b38b459cbb
152 >>> for kind, data, pos in xml: 152 >>> for kind, data, pos in xml:
153 ... if test(kind, data, pos, {}, {}): 153 ... if test(kind, data, pos, {}, {}):
154 ... print kind, data 154 ... print kind, data
155 START (u'child', [(u'id', u'2')]) 155 START (u'child', [(u'id', u'2')])
156 """ 156 """
157 paths = [(steps, len(steps), [0], []) for steps in self.paths] 157 paths = [(p, len(p), [0], [], [0] * len(p)) for p in self.paths]
158 158
159 def _test(kind, data, pos, namespaces, variables): 159 def _test(kind, data, pos, namespaces, variables):
160 for steps, size, stack, cutoff in paths: 160 for steps, size, cursors, cutoff, counter in paths:
161
161 # Manage the stack that tells us "where we are" in the stream 162 # Manage the stack that tells us "where we are" in the stream
162 if kind is END: 163 if kind is END:
163 if stack: stack.pop() 164 if cursors:
165 cursors.pop()
164 continue 166 continue
165 elif kind is START: 167 elif kind is START:
166 stack.append(stack and stack[-1] or 0) 168 cursors.append(cursors and cursors[-1] or 0)
167 elif not stack: 169 elif not cursors:
168 continue 170 continue
169 cursor = stack[-1] 171 cursor = cursors[-1]
170 172 depth = len(cursors)
171 if cutoff and len(stack) + int(kind is not START) > cutoff[0]: 173
174 if cutoff and depth + int(kind is not START) > cutoff[0]:
172 continue 175 continue
173 176
174 ctxtnode = not ignore_context and kind is START \ 177 ctxtnode = not ignore_context and kind is START \
175 and len(stack) == 2 178 and depth == 2
176 matched = retval = None 179 matched = retval = None
177 while 1: 180 while 1:
178 # Fetch the next location step 181 # Fetch the next location step
179 axis, nodetest, predicates = steps[cursor] 182 axis, nodetest, predicates = steps[cursor]
180 183
194 if matched: 197 if matched:
195 198
196 # Check all the predicates for this step 199 # Check all the predicates for this step
197 if predicates: 200 if predicates:
198 for predicate in predicates: 201 for predicate in predicates:
199 if not predicate(kind, data, pos, namespaces, 202 pretval = predicate(kind, data, pos, namespaces,
200 variables): 203 variables)
204 if type(pretval) is float:
205 counter[cursor] += 1
206 if counter[cursor] != int(pretval):
207 pretval = False
208 if not pretval:
201 matched = None 209 matched = None
202 break 210 break
203 211
204 # Both the node test and the predicates matched 212 # Both the node test and the predicates matched
205 if matched: 213 if matched:
208 or axis is ATTRIBUTE or axis is SELF: 216 or axis is ATTRIBUTE or axis is SELF:
209 retval = matched 217 retval = matched
210 elif not ctxtnode or axis is SELF \ 218 elif not ctxtnode or axis is SELF \
211 or axis is DESCENDANT_OR_SELF: 219 or axis is DESCENDANT_OR_SELF:
212 cursor += 1 220 cursor += 1
213 stack[-1] = cursor 221 cursors[-1] = cursor
214 cutoff[:] = [] 222 cutoff[:] = []
223
215 elif not ignore_context and kind is START: 224 elif not ignore_context and kind is START:
216 cutoff[:] = [len(stack)] 225 cutoff[:] = [depth]
217 226
218 if last_step and not ignore_context and kind is START: 227 if last_step and not ignore_context and kind is START:
219 if (axis is not DESCENDANT and 228 if (axis is not DESCENDANT and
220 axis is not DESCENDANT_OR_SELF): 229 axis is not DESCENDANT_OR_SELF):
221 cutoff[:] = [len(stack)] 230 cutoff[:] = [depth]
222 231
223 if kind is START and not last_step: 232 if kind is START and not last_step:
224 next_axis = steps[cursor][0] 233 next_axis = steps[cursor][0]
225 if next_axis is ATTRIBUTE: 234 if next_axis is ATTRIBUTE:
226 # If the axis of the next location step is the 235 # If the axis of the next location step is the
249 variables) 258 variables)
250 if not matched: 259 if not matched:
251 cursor -= 1 260 cursor -= 1
252 cutoff[:] = [] 261 cutoff[:] = []
253 break 262 break
254 stack[-1] = cursor 263 cursors[-1] = cursor
255 264
256 if retval: 265 if retval:
257 return retval 266 return retval
258 267
259 return _test 268 return _test
421 430
422 def _predicate(self): 431 def _predicate(self):
423 assert self.cur_token == '[' 432 assert self.cur_token == '['
424 self.next_token() 433 self.next_token()
425 expr = self._or_expr() 434 expr = self._or_expr()
426 if isinstance(expr, NumberLiteral):
427 raise PathSyntaxError('Position predicates not yet supported')
428 if self.cur_token != ']': 435 if self.cur_token != ']':
429 raise PathSyntaxError('Expected "]" to close predicate, ' 436 raise PathSyntaxError('Expected "]" to close predicate, '
430 'but found "%s"' % self.cur_token, 437 'but found "%s"' % self.cur_token,
431 self.filename, self.lineno) 438 self.filename, self.lineno)
432 if not self.at_end: 439 if not self.at_end:
971 """A string literal node.""" 978 """A string literal node."""
972 __slots__ = ['text'] 979 __slots__ = ['text']
973 def __init__(self, text): 980 def __init__(self, text):
974 self.text = text 981 self.text = text
975 def __call__(self, kind, data, pos, namespaces, variables): 982 def __call__(self, kind, data, pos, namespaces, variables):
976 return TEXT, self.text, (None, -1, -1) 983 return self.text
977 def __repr__(self): 984 def __repr__(self):
978 return '"%s"' % self.text 985 return '"%s"' % self.text
979 986
980 class NumberLiteral(Literal): 987 class NumberLiteral(Literal):
981 """A number literal node.""" 988 """A number literal node."""
982 __slots__ = ['number'] 989 __slots__ = ['number']
983 def __init__(self, number): 990 def __init__(self, number):
984 self.number = number 991 self.number = number
985 def __call__(self, kind, data, pos, namespaces, variables): 992 def __call__(self, kind, data, pos, namespaces, variables):
986 return TEXT, self.number, (None, -1, -1) 993 return self.number
987 def __repr__(self): 994 def __repr__(self):
988 return str(self.number) 995 return str(self.number)
989 996
990 class VariableReference(Literal): 997 class VariableReference(Literal):
991 """A variable reference node.""" 998 """A variable reference node."""
992 __slots__ = ['name'] 999 __slots__ = ['name']
993 def __init__(self, name): 1000 def __init__(self, name):
994 self.name = name 1001 self.name = name
995 def __call__(self, kind, data, pos, namespaces, variables): 1002 def __call__(self, kind, data, pos, namespaces, variables):
996 return TEXT, variables.get(self.name), (None, -1, -1) 1003 return variables.get(self.name)
997 def __repr__(self): 1004 def __repr__(self):
998 return str(self.name) 1005 return str(self.name)
999 1006
1000 # Operators 1007 # Operators
1001 1008
Copyright (C) 2012-2017 Edgewall Software