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.

169 lines
4.6 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 __deepcopy__(self, memo):
  82. return type(self)(self.data, deepcopy(self.children, memo))
  83. def copy(self):
  84. return type(self)(self.data, self.children)
  85. def set(self, data, children):
  86. self.data = data
  87. self.children = children
  88. # XXX Deprecated! Here for backwards compatibility <0.6.0
  89. @property
  90. def line(self):
  91. return self.meta.line
  92. @property
  93. def column(self):
  94. return self.meta.column
  95. @property
  96. def end_line(self):
  97. return self.meta.end_line
  98. @property
  99. def end_column(self):
  100. return self.meta.end_column
  101. class SlottedTree(Tree):
  102. __slots__ = 'data', 'children', 'rule', '_meta'
  103. def pydot__tree_to_png(tree, filename, rankdir="LR"):
  104. """Creates a colorful image that represents the tree (data+children, without meta)
  105. Possible values for `rankdir` are "TB", "LR", "BT", "RL", corresponding to
  106. directed graphs drawn from top to bottom, from left to right, from bottom to
  107. top, and from right to left, respectively. See:
  108. https://www.graphviz.org/doc/info/attrs.html#k:rankdir
  109. """
  110. import pydot
  111. graph = pydot.Dot(graph_type='digraph', rankdir=rankdir)
  112. i = [0]
  113. def new_leaf(leaf):
  114. node = pydot.Node(i[0], label=repr(leaf))
  115. i[0] += 1
  116. graph.add_node(node)
  117. return node
  118. def _to_pydot(subtree):
  119. color = hash(subtree.data) & 0xffffff
  120. color |= 0x808080
  121. subnodes = [_to_pydot(child) if isinstance(child, Tree) else new_leaf(child)
  122. for child in subtree.children]
  123. node = pydot.Node(i[0], style="filled", fillcolor="#%x"%color, label=subtree.data)
  124. i[0] += 1
  125. graph.add_node(node)
  126. for subnode in subnodes:
  127. graph.add_edge(pydot.Edge(node, subnode))
  128. return node
  129. _to_pydot(tree)
  130. graph.write_png(filename)