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.

176 lines
4.9 KiB

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