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.

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