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.

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