mirror of
				https://github.com/python/cpython.git
				synced 2025-11-03 23:21:29 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			113 lines
		
	
	
	
		
			3.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			113 lines
		
	
	
	
		
			3.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
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 incremently, 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 = ASTVisitor
 | 
						|
def 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)
 |