package org.rubypeople.rdt.internal.ti; import org.jruby.ast.BlockNode; import org.jruby.ast.CallNode; import org.jruby.ast.ClassNode; import org.jruby.ast.DefnNode; import org.jruby.ast.DefsNode; import org.jruby.ast.IterNode; import org.jruby.ast.LocalAsgnNode; import org.jruby.ast.ModuleNode; import org.jruby.ast.NewlineNode; import org.jruby.ast.Node; import org.jruby.parser.StaticScope; /** * Visits an AST while retaining memory of current scope. * * Nomenclature: * scopingNode: [ModuleNode, ClassNode, DefnNode, DefsNode, IterNode] * scopingNode.getBodyNode() returns a ScopeNode (JRuby AST type) * @author Jason * */ // XXX Pretty broken in concept at the moment - what is really needed is a modification of // InOrderVisitor with push/pop added in and assoc'ing the closest Scope to each Node along // the way with some metadata hash. //todo: How hard is this? public class ScopingVisitor { protected Scope globalScope; protected Scope currentScope; /** * Create a new ScopingVisitor, and have it traverse the specified root node * @param root */ public ScopingVisitor(Node root) { globalScope = new Scope(root, null); currentScope = globalScope; if ( isScopingNode(root) ) { processNode(root); } else if ( root instanceof BlockNode ) { BlockNode blockRoot = (BlockNode)root; for (Object node : blockRoot.childNodes() ) { if (node instanceof NewlineNode) { NewlineNode newlineNode = (NewlineNode) node; processNode(newlineNode.getNextNode()); } } } // processNode(root); } /** * Recursive method for processing an AST. Handles pushing/popping scopes appropriately. * Calls visitScopingNode or visitNode for scoping/nonscoping nodes; these are to be * filled out by subclasses. * * @param node Node to visit recursively. */ private void processNode(Node node) { if (node == null) { return ; } if ( isScopingNode( node ) ) { // Push scope currentScope = new Scope(node, currentScope); // Visit node for great power and riches - also for prepopulated localNames. visitScopingNode(node); // Visit children for( Object child : node.childNodes() ) { processNode((Node)child); } // Pop scope currentScope = currentScope.getParentScope(); } else { // Not a scoping node; visit it. visitNode(node); } } /** * Visits a scoping node's bodyNode. * It extracts the local variables from node.getBodyNode().getLocalNames(). * * Naming is little confusing maybe, since node.getBodyNode() is the actual ScopeNode. * @param node */ //todo: does this functionality belong here in ScopingVisitor, or in a subclass? protected void visitScopingNode(Node node) { StaticScope bodyNode = null; if ( node instanceof ModuleNode ) bodyNode = ((ModuleNode)node).getScope(); if ( node instanceof ClassNode ) bodyNode = ((ClassNode)node).getScope(); if ( node instanceof DefnNode ) bodyNode = ((DefnNode)node).getScope(); if ( node instanceof DefsNode ) bodyNode = ((DefsNode)node).getScope(); if ( node instanceof IterNode ) bodyNode = ((IterNode)node).getScope(); // Extract localNames Variable.insertLocalsFromScopeNode(bodyNode, currentScope); } /** * Visits the node. Logs and delegates to more specific visit*Node methods. */ protected void visitNode(Node node) { System.out.print("ScopedVisitor :: "); if ( node != null ) { String pos = ""; String cls = ""; if ( node.getPosition() != null ) pos = Integer.toString(node.getPosition().getStartLine()); if ( node.getClass() != null ) cls = node.getClass().getName(); System.out.print("Visiting " + node.getClass().getSimpleName() + "\tat line " + pos + " of class " + cls ); System.out.println("[ Spanning " + node.getPosition().getStartOffset() + "-" + node.getPosition().getEndOffset()+"]"); } // Visit specific node types if ( node instanceof CallNode ) visitCallNode( (CallNode)node ); if ( node instanceof LocalAsgnNode ) visitLocalAsgnNode( (LocalAsgnNode)node ); // : // : // : } protected void visitCallNode( CallNode node ) {} protected void visitLocalAsgnNode( LocalAsgnNode node ) {} private boolean isScopingNode(Node node) { return ( node instanceof ModuleNode ) || ( node instanceof ClassNode ) || ( node instanceof DefnNode ) || ( node instanceof DefsNode ) || ( node instanceof IterNode ); } }