0001 from compiler import ast 0002 0003 # XXX should probably rename ASTVisitor to ASTWalker 0004 # XXX can it be made even more generic? 0005 0006 class ASTVisitor: 0007 """Performs a depth-first walk of the AST 0008 0009 The ASTVisitor will walk the AST, performing either a preorder or 0010 postorder traversal depending on which method is called. 0011 0012 methods: 0013 preorder(tree, visitor) 0014 postorder(tree, visitor) 0015 tree: an instance of ast.Node 0016 visitor: an instance with visitXXX methods 0017 0018 The ASTVisitor is responsible for walking over the tree in the 0019 correct order. For each node, it checks the visitor argument for 0020 a method named 'visitNodeType' where NodeType is the name of the 0021 node's class, e.g. Class. If the method exists, it is called 0022 with the node as its sole argument. 0023 0024 The visitor method for a particular node type can control how 0025 child nodes are visited during a preorder walk. (It can't control 0026 the order during a postorder walk, because it is called _after_ 0027 the walk has occurred.) The ASTVisitor modifies the visitor 0028 argument by adding a visit method to the visitor; this method can 0029 be used to visit a child node of arbitrary type. 0030 """ 0031 0032 VERBOSE = 0 0033 0034 def __init__(self): 0035 self.node = None 0036 self._cache = {} 0037 0038 def default(self, node, *args): 0039 for child in node.getChildNodes(): 0040 self.dispatch(child, *args) 0041 0042 def dispatch(self, node, *args): 0043 self.node = node 0044 klass = node.__class__ 0045 meth = self._cache.get(klass, None) 0046 if meth is None: 0047 className = klass.__name__ 0048 meth = getattr(self.visitor, 'visit' + className, self.default) 0049 self._cache[klass] = meth 0050 ## if self.VERBOSE > 0: 0051 ## className = klass.__name__ 0052 ## if self.VERBOSE == 1: 0053 ## if meth == 0: 0054 ## print "dispatch", className 0055 ## else: 0056 ## print "dispatch", className, (meth and meth.__name__ or '') 0057 return meth(node, *args) 0058 0059 def preorder(self, tree, visitor, *args): 0060 """Do preorder walk of tree using visitor""" 0061 self.visitor = visitor 0062 visitor.visit = self.dispatch 0063 self.dispatch(tree, *args) # XXX *args make sense? 0064 0065 class ExampleASTVisitor(ASTVisitor): 0066 """Prints examples of the nodes that aren't visited 0067 0068 This visitor-driver is only useful for development, when it's 0069 helpful to develop a visitor incrementally, and get feedback on what 0070 you still have to do. 0071 """ 0072 examples = {} 0073 0074 def dispatch(self, node, *args): 0075 self.node = node 0076 meth = self._cache.get(node.__class__, None) 0077 className = node.__class__.__name__ 0078 if meth is None: 0079 meth = getattr(self.visitor, 'visit' + className, 0) 0080 self._cache[node.__class__] = meth 0081 if self.VERBOSE > 1: 0082 print "dispatch", className, (meth and meth.__name__ or '') 0083 if meth: 0084 meth(node, *args) 0085 elif self.VERBOSE > 0: 0086 klass = node.__class__ 0087 if not self.examples.has_key(klass): 0088 self.examples[klass] = klass 0089 print 0090 print self.visitor 0091 print klass 0092 for attr in dir(node): 0093 if attr[0] != '_': 0094 print "\t", "%-12.12s" % attr, getattr(node, attr) 0095 print 0096 return self.default(node, *args) 0097 0098 # XXX this is an API change 0099 0100 _walker = ASTVisitor 0101 def walk(tree, visitor, walker=None, verbose=None): 0102 if walker is None: 0103 walker = _walker() 0104 if verbose is not None: 0105 walker.VERBOSE = verbose 0106 walker.preorder(tree, visitor) 0107 return walker.visitor 0108 0109 def dumpNode(node): 0110 print node.__class__ 0111 for attr in dir(node): 0112 if attr[0] != '_': 0113 print "\t", "%-10.10s" % attr, getattr(node, attr) 0114
Generated by PyXR 0.9.4