1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/compiler/visitor.py Tue May 01 22:04:53 2012 +0200
1.3 @@ -0,0 +1,113 @@
1.4 +from compiler import ast
1.5 +
1.6 +# XXX should probably rename ASTVisitor to ASTWalker
1.7 +# XXX can it be made even more generic?
1.8 +
1.9 +class ASTVisitor:
1.10 + """Performs a depth-first walk of the AST
1.11 +
1.12 + The ASTVisitor will walk the AST, performing either a preorder or
1.13 + postorder traversal depending on which method is called.
1.14 +
1.15 + methods:
1.16 + preorder(tree, visitor)
1.17 + postorder(tree, visitor)
1.18 + tree: an instance of ast.Node
1.19 + visitor: an instance with visitXXX methods
1.20 +
1.21 + The ASTVisitor is responsible for walking over the tree in the
1.22 + correct order. For each node, it checks the visitor argument for
1.23 + a method named 'visitNodeType' where NodeType is the name of the
1.24 + node's class, e.g. Class. If the method exists, it is called
1.25 + with the node as its sole argument.
1.26 +
1.27 + The visitor method for a particular node type can control how
1.28 + child nodes are visited during a preorder walk. (It can't control
1.29 + the order during a postorder walk, because it is called _after_
1.30 + the walk has occurred.) The ASTVisitor modifies the visitor
1.31 + argument by adding a visit method to the visitor; this method can
1.32 + be used to visit a child node of arbitrary type.
1.33 + """
1.34 +
1.35 + VERBOSE = 0
1.36 +
1.37 + def __init__(self):
1.38 + self.node = None
1.39 + self._cache = {}
1.40 +
1.41 + def default(self, node, *args):
1.42 + for child in node.getChildNodes():
1.43 + self.dispatch(child, *args)
1.44 +
1.45 + def dispatch(self, node, *args):
1.46 + self.node = node
1.47 + klass = node.__class__
1.48 + meth = self._cache.get(klass, None)
1.49 + if meth is None:
1.50 + className = klass.__name__
1.51 + meth = getattr(self.visitor, 'visit' + className, self.default)
1.52 + self._cache[klass] = meth
1.53 +## if self.VERBOSE > 0:
1.54 +## className = klass.__name__
1.55 +## if self.VERBOSE == 1:
1.56 +## if meth == 0:
1.57 +## print "dispatch", className
1.58 +## else:
1.59 +## print "dispatch", className, (meth and meth.__name__ or '')
1.60 + return meth(node, *args)
1.61 +
1.62 + def preorder(self, tree, visitor, *args):
1.63 + """Do preorder walk of tree using visitor"""
1.64 + self.visitor = visitor
1.65 + visitor.visit = self.dispatch
1.66 + self.dispatch(tree, *args) # XXX *args make sense?
1.67 +
1.68 +class ExampleASTVisitor(ASTVisitor):
1.69 + """Prints examples of the nodes that aren't visited
1.70 +
1.71 + This visitor-driver is only useful for development, when it's
1.72 + helpful to develop a visitor incrementally, and get feedback on what
1.73 + you still have to do.
1.74 + """
1.75 + examples = {}
1.76 +
1.77 + def dispatch(self, node, *args):
1.78 + self.node = node
1.79 + meth = self._cache.get(node.__class__, None)
1.80 + className = node.__class__.__name__
1.81 + if meth is None:
1.82 + meth = getattr(self.visitor, 'visit' + className, 0)
1.83 + self._cache[node.__class__] = meth
1.84 + if self.VERBOSE > 1:
1.85 + print "dispatch", className, (meth and meth.__name__ or '')
1.86 + if meth:
1.87 + meth(node, *args)
1.88 + elif self.VERBOSE > 0:
1.89 + klass = node.__class__
1.90 + if not self.examples.has_key(klass):
1.91 + self.examples[klass] = klass
1.92 + print
1.93 + print self.visitor
1.94 + print klass
1.95 + for attr in dir(node):
1.96 + if attr[0] != '_':
1.97 + print "\t", "%-12.12s" % attr, getattr(node, attr)
1.98 + print
1.99 + return self.default(node, *args)
1.100 +
1.101 +# XXX this is an API change
1.102 +
1.103 +_walker = ASTVisitor
1.104 +def walk(tree, visitor, walker=None, verbose=None):
1.105 + if walker is None:
1.106 + walker = _walker()
1.107 + if verbose is not None:
1.108 + walker.VERBOSE = verbose
1.109 + walker.preorder(tree, visitor)
1.110 + return walker.visitor
1.111 +
1.112 +def dumpNode(node):
1.113 + print node.__class__
1.114 + for attr in dir(node):
1.115 + if attr[0] != '_':
1.116 + print "\t", "%-10.10s" % attr, getattr(node, attr)