/*
* This file is part of the X10 project (http://x10-lang.org).
*
* This file is licensed to You under the Eclipse Public License (EPL);
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.opensource.org/licenses/eclipse-1.0.php
*
* This file was originally derived from the Polyglot extensible compiler framework.
*
* (C) Copyright 2000-2007 Polyglot project group, Cornell University
* (C) Copyright IBM Corporation 2007-2012.
*/
package polyglot.visit;
import polyglot.ast.Node;
import polyglot.ast.NodeFactory;
import polyglot.frontend.Job;
import polyglot.main.Reporter;
import polyglot.types.Context;
import polyglot.types.TypeSystem;
import polyglot.util.InternalCompilerError;
/**
* 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 = ts.emptyContext();
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.shallowCopy();
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().enterChildScope(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) {
if (n != null)
n.addDecls(context);
}
public final NodeVisitor enter(Node n) {
throw new InternalCompilerError("Cannot call enter(Node n) on a ContextVisitor; use enter(Node parent, Node n) instead");
}
public final NodeVisitor enter(Node parent, Node n) {
if (reporter.should_report(Reporter.visit, 5))
reporter.report(5, "enter(" + n + ")");
if (prune) {
return new PruningVisitor();
}
ContextVisitor v = this;
Context c = this.enterScope(parent, n);
if (c != this.context) {
v = (ContextVisitor) this.shallowCopy();
v.context = c;
v.outer = this;
v.error = false;
}
return v.superEnter(parent, n);
}
protected boolean prune;
public final NodeVisitor superEnter(Node parent, Node n) {
return super.enter(parent, n);
}
public final Node leave(Node parent, Node old, Node n, NodeVisitor v) {
// If the traversal was pruned, just return n since leaveCall
// might expect a ContextVisitor, not a PruningVisitor.
if (v instanceof PruningVisitor || prune) {
return n;
}
Node m = super.leave(parent, old, n, v);
this.addDecls(m);
return m;
}
public final Node leave(Node old, Node n, NodeVisitor v) {
throw new InternalCompilerError("ContextVisitor.leave(Node, Node, NodeVisitor) should not be called");
}
}