113 lines · 3.8 KB · Python
RawDownload
from compiler import ast # XXX should probably rename ASTVisitor to ASTWalker# XXX can it be made even more generic? class ASTVisitor:    """Performs a depth-first walk of the AST     The ASTVisitor will walk the AST, performing either a preorder or    postorder traversal depending on which method is called.     methods:    preorder(tree, visitor)    postorder(tree, visitor)        tree: an instance of ast.Node        visitor: an instance with visitXXX methods     The ASTVisitor is responsible for walking over the tree in the    correct order.  For each node, it checks the visitor argument for    a method named 'visitNodeType' where NodeType is the name of the    node's class, e.g. Class.  If the method exists, it is called    with the node as its sole argument.     The visitor method for a particular node type can control how    child nodes are visited during a preorder walk.  (It can't control    the order during a postorder walk, because it is called _after_    the walk has occurred.)  The ASTVisitor modifies the visitor    argument by adding a visit method to the visitor; this method can    be used to visit a child node of arbitrary type.    """     VERBOSE = 0     def __init__(self):        self.node = None        self._cache = {}     def default(self, node, *args):        for child in node.getChildNodes():            self.dispatch(child, *args)     def dispatch(self, node, *args):        self.node = node        klass = node.__class__        meth = self._cache.get(klass, None)        if meth is None:            className = klass.__name__            meth = getattr(self.visitor, 'visit' + className, self.default)            self._cache[klass] = meth##        if self.VERBOSE > 0:##            className = klass.__name__##            if self.VERBOSE == 1:##                if meth == 0:##                    print "dispatch", className##            else:##                print "dispatch", className, (meth and meth.__name__ or '')        return meth(node, *args)     def preorder(self, tree, visitor, *args):        """Do preorder walk of tree using visitor"""        self.visitor = visitor        visitor.visit = self.dispatch        self.dispatch(tree, *args) # XXX *args make sense? class ExampleASTVisitor(ASTVisitor):    """Prints examples of the nodes that aren't visited     This visitor-driver is only useful for development, when it's    helpful to develop a visitor incrementally, and get feedback on what    you still have to do.    """    examples = {}     def dispatch(self, node, *args):        self.node = node        meth = self._cache.get(node.__class__, None)        className = node.__class__.__name__        if meth is None:            meth = getattr(self.visitor, 'visit' + className, 0)            self._cache[node.__class__] = meth        if self.VERBOSE > 1:            print "dispatch", className, (meth and meth.__name__ or '')        if meth:            meth(node, *args)        elif self.VERBOSE > 0:            klass = node.__class__            if not self.examples.has_key(klass):                self.examples[klass] = klass                print                print self.visitor                print klass                for attr in dir(node):                    if attr[0] != '_':                        print "\t", "%-12.12s" % attr, getattr(node, attr)                print            return self.default(node, *args) # XXX this is an API change _walker = ASTVisitordef walk(tree, visitor, walker=None, verbose=None):    if walker is None:        walker = _walker()    if verbose is not None:        walker.VERBOSE = verbose    walker.preorder(tree, visitor)    return walker.visitor def dumpNode(node):    print node.__class__    for attr in dir(node):        if attr[0] != '_':            print "\t", "%-10.10s" % attr, getattr(node, attr)