Mercurial > genshi > mirror
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 |