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.

185 lines
5.1 KiB

  1. try:
  2. from future_builtins import filter
  3. except ImportError:
  4. pass
  5. from copy import deepcopy
  6. ###{standalone
  7. class Meta:
  8. def __init__(self):
  9. self.empty = True
  10. class Tree(object):
  11. def __init__(self, data, children, meta=None):
  12. self.data = data
  13. self.children = children
  14. self._meta = meta
  15. @property
  16. def meta(self):
  17. if self._meta is None:
  18. self._meta = Meta()
  19. return self._meta
  20. def __repr__(self):
  21. return 'Tree(%s, %s)' % (self.data, self.children)
  22. def _pretty_label(self):
  23. return self.data
  24. def _pretty(self, level, indent_str):
  25. if len(self.children) == 1 and not isinstance(self.children[0], Tree):
  26. return [ indent_str*level, self._pretty_label(), '\t', '%s' % (self.children[0],), '\n']
  27. l = [ indent_str*level, self._pretty_label(), '\n' ]
  28. for n in self.children:
  29. if isinstance(n, Tree):
  30. l += n._pretty(level+1, indent_str)
  31. else:
  32. l += [ indent_str*(level+1), '%s' % (n,), '\n' ]
  33. return l
  34. def pretty(self, indent_str=' '):
  35. return ''.join(self._pretty(0, indent_str))
  36. def __eq__(self, other):
  37. try:
  38. return self.data == other.data and self.children == other.children
  39. except AttributeError:
  40. return False
  41. def __ne__(self, other):
  42. return not (self == other)
  43. def __hash__(self):
  44. return hash((self.data, tuple(self.children)))
  45. def iter_subtrees(self):
  46. # TODO: Re-write as a more efficient version
  47. visited = set()
  48. q = [self]
  49. l = []
  50. while q:
  51. subtree = q.pop()
  52. l.append( subtree )
  53. if id(subtree) in visited:
  54. continue # already been here from another branch
  55. visited.add(id(subtree))
  56. q += [c for c in subtree.children if isinstance(c, Tree)]
  57. seen = set()
  58. for x in reversed(l):
  59. if id(x) not in seen:
  60. yield x
  61. seen.add(id(x))
  62. def find_pred(self, pred):
  63. "Find all nodes where pred(tree) == True"
  64. return filter(pred, self.iter_subtrees())
  65. def find_data(self, data):
  66. "Find all nodes where tree.data == data"
  67. return self.find_pred(lambda t: t.data == data)
  68. ###}
  69. def expand_kids_by_index(self, *indices):
  70. "Expand (inline) children at the given indices"
  71. for i in sorted(indices, reverse=True): # reverse so that changing tail won't affect indices
  72. kid = self.children[i]
  73. self.children[i:i+1] = kid.children
  74. def scan_values(self, pred):
  75. for c in self.children:
  76. if isinstance(c, Tree):
  77. for t in c.scan_values(pred):
  78. yield t
  79. else:
  80. if pred(c):
  81. yield c
  82. def iter_subtrees_topdown(self):
  83. stack = [self]
  84. while stack:
  85. node = stack.pop()
  86. if not isinstance(node, Tree):
  87. continue
  88. yield node
  89. for n in reversed(node.children):
  90. stack.append(n)
  91. def __deepcopy__(self, memo):
  92. return type(self)(self.data, deepcopy(self.children, memo))
  93. def copy(self):
  94. return type(self)(self.data, self.children)
  95. def set(self, data, children):
  96. self.data = data
  97. self.children = children
  98. # XXX Deprecated! Here for backwards compatibility <0.6.0
  99. @property
  100. def line(self):
  101. return self.meta.line
  102. @property
  103. def column(self):
  104. return self.meta.column
  105. @property
  106. def end_line(self):
  107. return self.meta.end_line
  108. @property
  109. def end_column(self):
  110. return self.meta.end_column
  111. class SlottedTree(Tree):
  112. __slots__ = 'data', 'children', 'rule', '_meta'
  113. def pydot__tree_to_png(tree, filename, rankdir="LR", **kwargs):
  114. """Creates a colorful image that represents the tree (data+children, without meta)
  115. Possible values for `rankdir` are "TB", "LR", "BT", "RL", corresponding to
  116. directed graphs drawn from top to bottom, from left to right, from bottom to
  117. top, and from right to left, respectively.
  118. `kwargs` can be any graph attribute (e. g. `dpi=200`). For a list of
  119. possible attributes, see https://www.graphviz.org/doc/info/attrs.html.
  120. """
  121. import pydot
  122. graph = pydot.Dot(graph_type='digraph', rankdir=rankdir, **kwargs)
  123. i = [0]
  124. def new_leaf(leaf):
  125. node = pydot.Node(i[0], label=repr(leaf))
  126. i[0] += 1
  127. graph.add_node(node)
  128. return node
  129. def _to_pydot(subtree):
  130. color = hash(subtree.data) & 0xffffff
  131. color |= 0x808080
  132. subnodes = [_to_pydot(child) if isinstance(child, Tree) else new_leaf(child)
  133. for child in subtree.children]
  134. node = pydot.Node(i[0], style="filled", fillcolor="#%x"%color, label=subtree.data)
  135. i[0] += 1
  136. graph.add_node(node)
  137. for subnode in subnodes:
  138. graph.add_edge(pydot.Edge(node, subnode))
  139. return node
  140. _to_pydot(tree)
  141. graph.write_png(filename)