package polyglot.visit; import polyglot.ast.*; import polyglot.types.*; import polyglot.util.*; import polyglot.frontend.Job; import polyglot.main.Report; import java.util.*; /** * A visitor which maintains a context throughout the visitor's pass. This is * the base class of the disambiguation and type checking visitors. * * TODO: update this documentation. * For a node <code>n</code> methods are called in this order: * <pre> * v.enter(n) * v.enterScope(n); * c' = n.enterScope(c) * v' = copy(v) with c' for c * n' = n.visitChildren(v') * v.leave(n, n', v') * v.addDecls(n') * n.addDecls(c) * </pre> */ public class ContextVisitor extends ErrorHandlingVisitor { protected ContextVisitor outer; /** The current context of this visitor. */ protected Context context; public ContextVisitor(Job job, TypeSystem ts, NodeFactory nf) { super(job, ts, nf); this.outer = null; this.context = null; } public NodeVisitor begin() { context = job.context(); if (context == null) { context = ts.createContext(); } outer = null; return super.begin(); } /** Returns the context for this visitor. * * @return Returns the context that is currently in use by this visitor. * @see polyglot.types.Context */ public Context context() { return context; } /** Returns a new ContextVisitor that is a copy of the current visitor, * except with an updated context. * * @param c The new context that is to be used. * @return Returns a copy of this visitor with the new context * <code>c</code>. */ public ContextVisitor context(Context c) { ContextVisitor v = (ContextVisitor) this.copy(); v.context = c; return v; } /** * Returns a new context based on the current context, the Node current * being visited (<code>parent</code>), and the Node that is being * entered (<code>n</code>). This new context is to be used * for visiting <code>n</code>. * * @return The new context after entering Node <code>n</code>. */ protected Context enterScope(Node parent, Node n) { if (parent != null) { return parent.del().enterScope(n, context); } // no parent node yet. return n.del().enterScope(context); } /** * Imperatively update the context with declarations to be added after * visiting the node. */ protected void addDecls(Node n) { n.addDecls(context); } public NodeVisitor enter(Node parent, Node n) { if (Report.should_report(Report.visit, 5)) Report.report(5, "enter(" + n + ")"); ContextVisitor v = this; Context c = this.enterScope(parent, n); if (c != this.context) { v = (ContextVisitor) this.copy(); v.context = c; v.outer = this; v.error = false; } return v.superEnter(parent, n); } public NodeVisitor superEnter(Node parent, Node n) { return super.enter(parent, n); } public Node leave(Node parent, Node old, Node n, NodeVisitor v) { Node m = super.leave(parent, old, n, v); this.addDecls(m); return m; } }