ast.py 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. import sys
  2. PYTHON3 = (sys.version_info >= (3, 0))
  3. PYTHON37 = (sys.version_info >= (3, 7))
  4. if PYTHON3:
  5. intern = sys.intern
  6. from collections import UserList
  7. else:
  8. from UserList import UserList
  9. class AST(UserList):
  10. def __init__(self, kind, kids=[]):
  11. self.kind = intern(kind)
  12. UserList.__init__(self, kids)
  13. def __getslice__(self, low, high):
  14. return self.data[low:high]
  15. if PYTHON37:
  16. def __getitem__(self, i):
  17. return self.data[i]
  18. def __eq__(self, o):
  19. if isinstance(o, AST):
  20. return (self.kind == o.kind and
  21. UserList.__eq__(self, o))
  22. else:
  23. return self.kind == o
  24. def __hash__(self):
  25. return hash(self.kind)
  26. def __repr__(self, indent=''):
  27. return self.__repr1__(indent, None)
  28. def __repr1__(self, indent, sibNum=None):
  29. rv = str(self.kind)
  30. if sibNum is not None:
  31. rv = "%d. %s" % (sibNum, rv)
  32. enumerate_children = False
  33. if len(self) > 1:
  34. rv += " (%d)" % (len(self))
  35. enumerate_children = True
  36. rv = indent + rv
  37. indent += ' '
  38. i = 0
  39. for node in self:
  40. if hasattr(node, '__repr1__'):
  41. if enumerate_children:
  42. child = node.__repr1__(indent, i)
  43. else:
  44. child = node.__repr1__(indent, None)
  45. else:
  46. if enumerate_children:
  47. child = indent + "%d. %s" % (i, str(node))
  48. else:
  49. child = indent + str(node)
  50. pass
  51. rv += "\n" + child
  52. i += 1
  53. return rv
  54. class GenericASTTraversalPruningException(BaseException):
  55. pass
  56. class GenericASTTraversal:
  57. '''
  58. GenericASTTraversal is a Visitor pattern according to Design Patterns. For
  59. each node it attempts to invoke the method n_<node type>, falling
  60. back onto the default() method if the n_* can't be found. The preorder
  61. traversal also looks for an exit hook named n_<node type>_exit (no default
  62. routine is called if it's not found). To prematurely halt traversal
  63. of a subtree, call the prune() method -- this only makes sense for a
  64. preorder traversal. Node type is determined via the typestring() method.
  65. '''
  66. def __init__(self, ast):
  67. self.ast = ast
  68. def typestring(self, node):
  69. return node.kind
  70. def prune(self):
  71. raise GenericASTTraversalPruningException
  72. def preorder(self, node=None):
  73. """Walk the tree in roughly 'preorder' (a bit of a lie explained below).
  74. For each node with typestring name *name* if the
  75. node has a method called n_*name*, call that before walking
  76. children. If there is no method define, call a
  77. self.default(node) instead. Subclasses of GenericASTTtraversal
  78. ill probably want to override this method.
  79. If the node has a method called *name*_exit, that is called
  80. after all children have been called.
  81. In typical use a node with children can call "preorder" in any
  82. order it wants which may skip children or order then in ways
  83. other than first to last. In fact, this this happens. So in
  84. this sense this function not strictly preorder.
  85. """
  86. if node is None:
  87. node = self.ast
  88. try:
  89. name = 'n_' + self.typestring(node)
  90. if hasattr(self, name):
  91. func = getattr(self, name)
  92. func(node)
  93. else:
  94. self.default(node)
  95. except GenericASTTraversalPruningException:
  96. return
  97. for kid in node:
  98. self.preorder(kid)
  99. name = name + '_exit'
  100. if hasattr(self, name):
  101. func = getattr(self, name)
  102. func(node)
  103. def postorder(self, node=None):
  104. """Walk the tree in roughly 'postorder' (a bit of a lie
  105. explained below).
  106. For each node with typestring name *name* if the
  107. node has a method called n_*name*, call that before walking
  108. children. If there is no method define, call a
  109. self.default(node) instead. Subclasses of GenericASTTtraversal
  110. ill probably want to override this method.
  111. If the node has a method called *name*_exit, that is called
  112. after all children have been called. So in this sense this
  113. function is a lie.
  114. In typical use a node with children can call "postorder" in
  115. any order it wants which may skip children or order then in
  116. ways other than first to last. In fact, this this happens.
  117. """
  118. if node is None:
  119. node = self.ast
  120. try:
  121. first = iter(node)
  122. except TypeError:
  123. first = None
  124. if first:
  125. for kid in node:
  126. self.postorder(kid)
  127. try:
  128. name = 'n_' + self.typestring(node)
  129. if hasattr(self, name):
  130. func = getattr(self, name)
  131. func(node)
  132. else:
  133. self.default(node)
  134. except GenericASTTraversalPruningException:
  135. return
  136. name = name + '_exit'
  137. if hasattr(self, name):
  138. func = getattr(self, name)
  139. func(node)
  140. def default(self, node):
  141. """Default action to take on an ASTNode. Our defualt is to do nothing.
  142. Subclasses will probably want to define this for other behavior."""
  143. pass