This repo contains code to mirror other repos. It also contains the code that is getting mirrored.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

235 lines
6.0 KiB

  1. try:
  2. from future_builtins import filter
  3. except ImportError:
  4. pass
  5. from copy import deepcopy
  6. from .utils import inline_args
  7. ###{standalone
  8. class Tree(object):
  9. def __init__(self, data, children, rule=None):
  10. self.data = data
  11. self.children = list(children)
  12. self.rule = rule
  13. def __repr__(self):
  14. return 'Tree(%s, %s)' % (self.data, self.children)
  15. def _pretty_label(self):
  16. return self.data
  17. def _pretty(self, level, indent_str):
  18. if len(self.children) == 1 and not isinstance(self.children[0], Tree):
  19. return [ indent_str*level, self._pretty_label(), '\t', '%s' % (self.children[0],), '\n']
  20. l = [ indent_str*level, self._pretty_label(), '\n' ]
  21. for n in self.children:
  22. if isinstance(n, Tree):
  23. l += n._pretty(level+1, indent_str)
  24. else:
  25. l += [ indent_str*(level+1), '%s' % (n,), '\n' ]
  26. return l
  27. def pretty(self, indent_str=' '):
  28. return ''.join(self._pretty(0, indent_str))
  29. ###}
  30. def expand_kids_by_index(self, *indices):
  31. for i in sorted(indices, reverse=True): # reverse so that changing tail won't affect indices
  32. kid = self.children[i]
  33. self.children[i:i+1] = kid.children
  34. def __eq__(self, other):
  35. try:
  36. return self.data == other.data and self.children == other.children
  37. except AttributeError:
  38. return False
  39. def __ne__(self, other):
  40. return not (self == other)
  41. def __hash__(self):
  42. return hash((self.data, tuple(self.children)))
  43. def find_pred(self, pred):
  44. return filter(pred, self.iter_subtrees())
  45. def find_data(self, data):
  46. return self.find_pred(lambda t: t.data == data)
  47. def scan_values(self, pred):
  48. for c in self.children:
  49. if isinstance(c, Tree):
  50. for t in c.scan_values(pred):
  51. yield t
  52. else:
  53. if pred(c):
  54. yield c
  55. def iter_subtrees(self):
  56. # TODO: Re-write as a more efficient version
  57. visited = set()
  58. q = [self]
  59. l = []
  60. while q:
  61. subtree = q.pop()
  62. l.append( subtree )
  63. if id(subtree) in visited:
  64. continue # already been here from another branch
  65. visited.add(id(subtree))
  66. q += [c for c in subtree.children if isinstance(c, Tree)]
  67. seen = set()
  68. for x in reversed(l):
  69. if id(x) not in seen:
  70. yield x
  71. seen.add(id(x))
  72. def __deepcopy__(self, memo):
  73. return type(self)(self.data, deepcopy(self.children, memo))
  74. def copy(self):
  75. return type(self)(self.data, self.children)
  76. def set(self, data, children):
  77. self.data = data
  78. self.children = children
  79. ###{standalone
  80. class Transformer(object):
  81. def _get_func(self, name):
  82. return getattr(self, name)
  83. def transform(self, tree):
  84. items = []
  85. for c in tree.children:
  86. try:
  87. items.append(self.transform(c) if isinstance(c, Tree) else c)
  88. except Discard:
  89. pass
  90. try:
  91. f = self._get_func(tree.data)
  92. except AttributeError:
  93. return self.__default__(tree.data, items)
  94. else:
  95. return f(items)
  96. def __default__(self, data, children):
  97. return Tree(data, children)
  98. def __mul__(self, other):
  99. return TransformerChain(self, other)
  100. class Discard(Exception):
  101. pass
  102. class TransformerChain(object):
  103. def __init__(self, *transformers):
  104. self.transformers = transformers
  105. def transform(self, tree):
  106. for t in self.transformers:
  107. tree = t.transform(tree)
  108. return tree
  109. def __mul__(self, other):
  110. return TransformerChain(*self.transformers + (other,))
  111. class InlineTransformer(Transformer):
  112. def _get_func(self, name): # use super()._get_func
  113. return inline_args(getattr(self, name)).__get__(self)
  114. class Visitor(object):
  115. def visit(self, tree):
  116. for child in tree.children:
  117. if isinstance(child, Tree):
  118. self.visit(child)
  119. f = getattr(self, tree.data, self.__default__)
  120. f(tree)
  121. return tree
  122. def __default__(self, tree):
  123. pass
  124. class Visitor_NoRecurse(Visitor):
  125. def visit(self, tree):
  126. subtrees = list(tree.iter_subtrees())
  127. for subtree in (subtrees):
  128. getattr(self, subtree.data, self.__default__)(subtree)
  129. return tree
  130. class Transformer_NoRecurse(Transformer):
  131. def transform(self, tree):
  132. subtrees = list(tree.iter_subtrees())
  133. def _t(t):
  134. # Assumes t is already transformed
  135. try:
  136. f = self._get_func(t.data)
  137. except AttributeError:
  138. return self.__default__(t)
  139. else:
  140. return f(t)
  141. for subtree in subtrees:
  142. children = []
  143. for c in subtree.children:
  144. try:
  145. children.append(_t(c) if isinstance(c, Tree) else c)
  146. except Discard:
  147. pass
  148. subtree.children = children
  149. return _t(tree)
  150. def __default__(self, t):
  151. return t
  152. ###}
  153. def pydot__tree_to_png(tree, filename):
  154. import pydot
  155. graph = pydot.Dot(graph_type='digraph', rankdir="LR")
  156. i = [0]
  157. def new_leaf(leaf):
  158. node = pydot.Node(i[0], label=repr(leaf))
  159. i[0] += 1
  160. graph.add_node(node)
  161. return node
  162. def _to_pydot(subtree):
  163. color = hash(subtree.data) & 0xffffff
  164. color |= 0x808080
  165. subnodes = [_to_pydot(child) if isinstance(child, Tree) else new_leaf(child)
  166. for child in subtree.children]
  167. node = pydot.Node(i[0], style="filled", fillcolor="#%x"%color, label=subtree.data)
  168. i[0] += 1
  169. graph.add_node(node)
  170. for subnode in subnodes:
  171. graph.add_edge(pydot.Edge(node, subnode))
  172. return node
  173. _to_pydot(tree)
  174. graph.write_png(filename)