Commit | Line | Data |
---|---|---|
920dae64 AT |
1 | from compiler import ast |
2 | ||
3 | # XXX should probably rename ASTVisitor to ASTWalker | |
4 | # XXX can it be made even more generic? | |
5 | ||
6 | class ASTVisitor: | |
7 | """Performs a depth-first walk of the AST | |
8 | ||
9 | The ASTVisitor will walk the AST, performing either a preorder or | |
10 | postorder traversal depending on which method is called. | |
11 | ||
12 | methods: | |
13 | preorder(tree, visitor) | |
14 | postorder(tree, visitor) | |
15 | tree: an instance of ast.Node | |
16 | visitor: an instance with visitXXX methods | |
17 | ||
18 | The ASTVisitor is responsible for walking over the tree in the | |
19 | correct order. For each node, it checks the visitor argument for | |
20 | a method named 'visitNodeType' where NodeType is the name of the | |
21 | node's class, e.g. Class. If the method exists, it is called | |
22 | with the node as its sole argument. | |
23 | ||
24 | The visitor method for a particular node type can control how | |
25 | child nodes are visited during a preorder walk. (It can't control | |
26 | the order during a postorder walk, because it is called _after_ | |
27 | the walk has occurred.) The ASTVisitor modifies the visitor | |
28 | argument by adding a visit method to the visitor; this method can | |
29 | be used to visit a child node of arbitrary type. | |
30 | """ | |
31 | ||
32 | VERBOSE = 0 | |
33 | ||
34 | def __init__(self): | |
35 | self.node = None | |
36 | self._cache = {} | |
37 | ||
38 | def default(self, node, *args): | |
39 | for child in node.getChildNodes(): | |
40 | self.dispatch(child, *args) | |
41 | ||
42 | def dispatch(self, node, *args): | |
43 | self.node = node | |
44 | klass = node.__class__ | |
45 | meth = self._cache.get(klass, None) | |
46 | if meth is None: | |
47 | className = klass.__name__ | |
48 | meth = getattr(self.visitor, 'visit' + className, self.default) | |
49 | self._cache[klass] = meth | |
50 | ## if self.VERBOSE > 0: | |
51 | ## className = klass.__name__ | |
52 | ## if self.VERBOSE == 1: | |
53 | ## if meth == 0: | |
54 | ## print "dispatch", className | |
55 | ## else: | |
56 | ## print "dispatch", className, (meth and meth.__name__ or '') | |
57 | return meth(node, *args) | |
58 | ||
59 | def preorder(self, tree, visitor, *args): | |
60 | """Do preorder walk of tree using visitor""" | |
61 | self.visitor = visitor | |
62 | visitor.visit = self.dispatch | |
63 | self.dispatch(tree, *args) # XXX *args make sense? | |
64 | ||
65 | class ExampleASTVisitor(ASTVisitor): | |
66 | """Prints examples of the nodes that aren't visited | |
67 | ||
68 | This visitor-driver is only useful for development, when it's | |
69 | helpful to develop a visitor incrementally, and get feedback on what | |
70 | you still have to do. | |
71 | """ | |
72 | examples = {} | |
73 | ||
74 | def dispatch(self, node, *args): | |
75 | self.node = node | |
76 | meth = self._cache.get(node.__class__, None) | |
77 | className = node.__class__.__name__ | |
78 | if meth is None: | |
79 | meth = getattr(self.visitor, 'visit' + className, 0) | |
80 | self._cache[node.__class__] = meth | |
81 | if self.VERBOSE > 1: | |
82 | print "dispatch", className, (meth and meth.__name__ or '') | |
83 | if meth: | |
84 | meth(node, *args) | |
85 | elif self.VERBOSE > 0: | |
86 | klass = node.__class__ | |
87 | if not self.examples.has_key(klass): | |
88 | self.examples[klass] = klass | |
89 | ||
90 | print self.visitor | |
91 | print klass | |
92 | for attr in dir(node): | |
93 | if attr[0] != '_': | |
94 | print "\t", "%-12.12s" % attr, getattr(node, attr) | |
95 | ||
96 | return self.default(node, *args) | |
97 | ||
98 | # XXX this is an API change | |
99 | ||
100 | _walker = ASTVisitor | |
101 | def walk(tree, visitor, walker=None, verbose=None): | |
102 | if walker is None: | |
103 | walker = _walker() | |
104 | if verbose is not None: | |
105 | walker.VERBOSE = verbose | |
106 | walker.preorder(tree, visitor) | |
107 | return walker.visitor | |
108 | ||
109 | def dumpNode(node): | |
110 | print node.__class__ | |
111 | for attr in dir(node): | |
112 | if attr[0] != '_': | |
113 | print "\t", "%-10.10s" % attr, getattr(node, attr) |