| 
									
										
										
										
											2000-10-25 18:02:02 +00:00
										 |  |  | import sys | 
					
						
							| 
									
										
										
										
											2000-03-06 18:54:30 +00:00
										 |  |  | from compiler import ast | 
					
						
							| 
									
										
										
										
											2000-03-06 18:49:31 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 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 particular child node.  If the visitor method | 
					
						
							|  |  |  |     returns a true value, the ASTVisitor will not traverse the child | 
					
						
							|  |  |  |     nodes. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     XXX The interface for controlling the preorder walk needs to be | 
					
						
							|  |  |  |     re-considered.  The current interface is convenient for visitors | 
					
						
							|  |  |  |     that mostly let the ASTVisitor do everything.  For something like | 
					
						
							|  |  |  |     a code generator, where you want to walk to occur in a specific | 
					
						
							|  |  |  |     order, it's a pain to add "return 1" to the end of each method. | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     VERBOSE = 0 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self): | 
					
						
							|  |  |  |         self.node = None | 
					
						
							| 
									
										
										
										
											2000-08-12 20:32:46 +00:00
										 |  |  |         self._cache = {} | 
					
						
							| 
									
										
										
										
											2000-03-06 18:49:31 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-03-16 20:04:16 +00:00
										 |  |  |     def default(self, node, *args): | 
					
						
							| 
									
										
										
										
											2000-03-06 18:49:31 +00:00
										 |  |  |         for child in node.getChildren(): | 
					
						
							|  |  |  |             if isinstance(child, ast.Node): | 
					
						
							| 
									
										
										
										
											2000-03-16 20:04:16 +00:00
										 |  |  |                 apply(self._preorder, (child,) + args) | 
					
						
							| 
									
										
										
										
											2000-03-06 18:49:31 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def dispatch(self, node, *args): | 
					
						
							|  |  |  |         self.node = node | 
					
						
							| 
									
										
										
										
											2000-10-25 18:02:02 +00:00
										 |  |  |         klass = node.__class__ | 
					
						
							|  |  |  |         meth = self._cache.get(klass, None) | 
					
						
							| 
									
										
										
										
											2000-08-12 20:32:46 +00:00
										 |  |  |         if meth is None: | 
					
						
							| 
									
										
										
										
											2000-10-25 18:02:02 +00:00
										 |  |  |             className = klass.__name__ | 
					
						
							| 
									
										
										
										
											2000-08-12 20:32:46 +00:00
										 |  |  |             meth = getattr(self.visitor, 'visit' + className, self.default) | 
					
						
							| 
									
										
										
										
											2000-10-25 18:02:02 +00:00
										 |  |  |             self._cache[klass] = meth | 
					
						
							| 
									
										
										
										
											2000-03-06 18:49:31 +00:00
										 |  |  |         if self.VERBOSE > 0: | 
					
						
							| 
									
										
										
										
											2000-10-25 18:02:02 +00:00
										 |  |  |             className = klass.__name__ | 
					
						
							| 
									
										
										
										
											2000-03-06 18:49:31 +00:00
										 |  |  |             if self.VERBOSE == 1: | 
					
						
							|  |  |  |                 if meth == 0: | 
					
						
							|  |  |  |                     print "dispatch", className | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 print "dispatch", className, (meth and meth.__name__ or '') | 
					
						
							| 
									
										
										
										
											2001-04-11 16:26:05 +00:00
										 |  |  |         return meth(node, *args) | 
					
						
							| 
									
										
										
										
											2000-03-06 18:49:31 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-04-11 16:26:05 +00:00
										 |  |  |     def preorder(self, tree, visitor, *args): | 
					
						
							| 
									
										
										
										
											2000-10-25 18:02:02 +00:00
										 |  |  |         """Do preorder walk of tree using visitor""" | 
					
						
							|  |  |  |         self.visitor = visitor | 
					
						
							|  |  |  |         visitor.visit = self._preorder | 
					
						
							| 
									
										
										
										
											2001-04-11 16:26:05 +00:00
										 |  |  |         self._preorder(tree, *args) # XXX *args make sense? | 
					
						
							| 
									
										
										
										
											2000-10-25 18:02:02 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     _preorder = dispatch | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2000-03-06 18:49:31 +00:00
										 |  |  | 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 | 
					
						
							| 
									
										
										
										
											2000-08-12 20:32:46 +00:00
										 |  |  |         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 | 
					
						
							| 
									
										
										
										
											2000-03-16 20:04:16 +00:00
										 |  |  |         if self.VERBOSE > 1: | 
					
						
							|  |  |  |             print "dispatch", className, (meth and meth.__name__ or '') | 
					
						
							| 
									
										
										
										
											2000-03-06 18:49:31 +00:00
										 |  |  |         if meth: | 
					
						
							|  |  |  |             return apply(meth, (node,) + args) | 
					
						
							|  |  |  |         elif self.VERBOSE > 0: | 
					
						
							|  |  |  |             klass = node.__class__ | 
					
						
							| 
									
										
										
										
											2000-03-16 20:04:16 +00:00
										 |  |  |             if not self.examples.has_key(klass): | 
					
						
							| 
									
										
										
										
											2000-08-12 20:32:46 +00:00
										 |  |  |                 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 apply(self.default, (node,) + args) | 
					
						
							| 
									
										
										
										
											2000-03-06 18:49:31 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | _walker = ASTVisitor | 
					
						
							|  |  |  | def walk(tree, visitor, verbose=None): | 
					
						
							|  |  |  |     w = _walker() | 
					
						
							|  |  |  |     if verbose is not None: | 
					
						
							|  |  |  |         w.VERBOSE = verbose | 
					
						
							|  |  |  |     w.preorder(tree, visitor) | 
					
						
							|  |  |  |     return w.visitor | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def dumpNode(node): | 
					
						
							|  |  |  |     print node.__class__ | 
					
						
							|  |  |  |     for attr in dir(node): | 
					
						
							|  |  |  |         if attr[0] != '_': | 
					
						
							|  |  |  |             print "\t", "%-10.10s" % attr, getattr(node, attr) |