Index: third_party/cython/src/Cython/Compiler/TreePath.py |
diff --git a/third_party/cython/src/Cython/Compiler/TreePath.py b/third_party/cython/src/Cython/Compiler/TreePath.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..ee996e821bb0a3fbe2568ab730ef89b4ba349058 |
--- /dev/null |
+++ b/third_party/cython/src/Cython/Compiler/TreePath.py |
@@ -0,0 +1,288 @@ |
+""" |
+A simple XPath-like language for tree traversal. |
+ |
+This works by creating a filter chain of generator functions. Each |
+function selects a part of the expression, e.g. a child node, a |
+specific descendant or a node that holds an attribute. |
+""" |
+ |
+import re |
+import sys |
+ |
+path_tokenizer = re.compile( |
+ "(" |
+ "'[^']*'|\"[^\"]*\"|" |
+ "//?|" |
+ "\(\)|" |
+ "==?|" |
+ "[/.*\[\]\(\)@])|" |
+ "([^/\[\]\(\)@=\s]+)|" |
+ "\s+" |
+ ).findall |
+ |
+def iterchildren(node, attr_name): |
+ # returns an iterable of all child nodes of that name |
+ child = getattr(node, attr_name) |
+ if child is not None: |
+ if type(child) is list: |
+ return child |
+ else: |
+ return [child] |
+ else: |
+ return () |
+ |
+def _get_first_or_none(it): |
+ try: |
+ try: |
+ _next = it.next |
+ except AttributeError: |
+ return next(it) |
+ else: |
+ return _next() |
+ except StopIteration: |
+ return None |
+ |
+def type_name(node): |
+ return node.__class__.__name__.split('.')[-1] |
+ |
+def parse_func(next, token): |
+ name = token[1] |
+ token = next() |
+ if token[0] != '(': |
+ raise ValueError("Expected '(' after function name '%s'" % name) |
+ predicate = handle_predicate(next, token) |
+ return name, predicate |
+ |
+def handle_func_not(next, token): |
+ """ |
+ not(...) |
+ """ |
+ name, predicate = parse_func(next, token) |
+ |
+ def select(result): |
+ for node in result: |
+ if _get_first_or_none(predicate([node])) is None: |
+ yield node |
+ return select |
+ |
+def handle_name(next, token): |
+ """ |
+ /NodeName/ |
+ or |
+ func(...) |
+ """ |
+ name = token[1] |
+ if name in functions: |
+ return functions[name](next, token) |
+ def select(result): |
+ for node in result: |
+ for attr_name in node.child_attrs: |
+ for child in iterchildren(node, attr_name): |
+ if type_name(child) == name: |
+ yield child |
+ return select |
+ |
+def handle_star(next, token): |
+ """ |
+ /*/ |
+ """ |
+ def select(result): |
+ for node in result: |
+ for name in node.child_attrs: |
+ for child in iterchildren(node, name): |
+ yield child |
+ return select |
+ |
+def handle_dot(next, token): |
+ """ |
+ /./ |
+ """ |
+ def select(result): |
+ return result |
+ return select |
+ |
+def handle_descendants(next, token): |
+ """ |
+ //... |
+ """ |
+ token = next() |
+ if token[0] == "*": |
+ def iter_recursive(node): |
+ for name in node.child_attrs: |
+ for child in iterchildren(node, name): |
+ yield child |
+ for c in iter_recursive(child): |
+ yield c |
+ elif not token[0]: |
+ node_name = token[1] |
+ def iter_recursive(node): |
+ for name in node.child_attrs: |
+ for child in iterchildren(node, name): |
+ if type_name(child) == node_name: |
+ yield child |
+ for c in iter_recursive(child): |
+ yield c |
+ else: |
+ raise ValueError("Expected node name after '//'") |
+ |
+ def select(result): |
+ for node in result: |
+ for child in iter_recursive(node): |
+ yield child |
+ |
+ return select |
+ |
+def handle_attribute(next, token): |
+ token = next() |
+ if token[0]: |
+ raise ValueError("Expected attribute name") |
+ name = token[1] |
+ value = None |
+ try: |
+ token = next() |
+ except StopIteration: |
+ pass |
+ else: |
+ if token[0] == '=': |
+ value = parse_path_value(next) |
+ if sys.version_info >= (2,6) or (sys.version_info >= (2,4) and '.' not in name): |
+ import operator |
+ readattr = operator.attrgetter(name) |
+ else: |
+ name_path = name.split('.') |
+ def readattr(node): |
+ attr_value = node |
+ for attr in name_path: |
+ attr_value = getattr(attr_value, attr) |
+ return attr_value |
+ if value is None: |
+ def select(result): |
+ for node in result: |
+ try: |
+ attr_value = readattr(node) |
+ except AttributeError: |
+ continue |
+ if attr_value is not None: |
+ yield attr_value |
+ else: |
+ def select(result): |
+ for node in result: |
+ try: |
+ attr_value = readattr(node) |
+ except AttributeError: |
+ continue |
+ if attr_value == value: |
+ yield attr_value |
+ return select |
+ |
+def parse_path_value(next): |
+ token = next() |
+ value = token[0] |
+ if value: |
+ if value[:1] == "'" or value[:1] == '"': |
+ return value[1:-1] |
+ try: |
+ return int(value) |
+ except ValueError: |
+ pass |
+ else: |
+ name = token[1].lower() |
+ if name == 'true': |
+ return True |
+ elif name == 'false': |
+ return False |
+ raise ValueError("Invalid attribute predicate: '%s'" % value) |
+ |
+def handle_predicate(next, token): |
+ token = next() |
+ selector = [] |
+ while token[0] != ']': |
+ selector.append( operations[token[0]](next, token) ) |
+ try: |
+ token = next() |
+ except StopIteration: |
+ break |
+ else: |
+ if token[0] == "/": |
+ token = next() |
+ |
+ if not token[0] and token[1] == 'and': |
+ return logical_and(selector, handle_predicate(next, token)) |
+ |
+ def select(result): |
+ for node in result: |
+ subresult = iter((node,)) |
+ for select in selector: |
+ subresult = select(subresult) |
+ predicate_result = _get_first_or_none(subresult) |
+ if predicate_result is not None: |
+ yield node |
+ return select |
+ |
+def logical_and(lhs_selects, rhs_select): |
+ def select(result): |
+ for node in result: |
+ subresult = iter((node,)) |
+ for select in lhs_selects: |
+ subresult = select(subresult) |
+ predicate_result = _get_first_or_none(subresult) |
+ subresult = iter((node,)) |
+ if predicate_result is not None: |
+ for result_node in rhs_select(subresult): |
+ yield node |
+ return select |
+ |
+ |
+operations = { |
+ "@": handle_attribute, |
+ "": handle_name, |
+ "*": handle_star, |
+ ".": handle_dot, |
+ "//": handle_descendants, |
+ "[": handle_predicate, |
+ } |
+ |
+functions = { |
+ 'not' : handle_func_not |
+ } |
+ |
+def _build_path_iterator(path): |
+ # parse pattern |
+ stream = iter([ (special,text) |
+ for (special,text) in path_tokenizer(path) |
+ if special or text ]) |
+ try: |
+ _next = stream.next |
+ except AttributeError: |
+ # Python 3 |
+ def _next(): |
+ return next(stream) |
+ token = _next() |
+ selector = [] |
+ while 1: |
+ try: |
+ selector.append(operations[token[0]](_next, token)) |
+ except StopIteration: |
+ raise ValueError("invalid path") |
+ try: |
+ token = _next() |
+ if token[0] == "/": |
+ token = _next() |
+ except StopIteration: |
+ break |
+ return selector |
+ |
+# main module API |
+ |
+def iterfind(node, path): |
+ selector_chain = _build_path_iterator(path) |
+ result = iter((node,)) |
+ for select in selector_chain: |
+ result = select(result) |
+ return result |
+ |
+def find_first(node, path): |
+ return _get_first_or_none(iterfind(node, path)) |
+ |
+def find_all(node, path): |
+ return list(iterfind(node, path)) |