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.

206 lines
5.5 KiB

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