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