package polyglot.ext.jl.ast;
import polyglot.ast.*;
import polyglot.types.*;
import polyglot.visit.*;
import polyglot.util.*;
import java.util.*;
/**
* A <code>Node</code> represents an AST node. All AST nodes must implement
* this interface. Nodes should be immutable: methods which set fields
* of the node should copy the node, set the field in the copy, and then
* return the copy.
*/
public abstract class Node_c implements Node
{
protected Position position;
protected JL del;
protected Ext ext;
public Node_c(Position pos) {
this.position = pos;
}
public void init(Node node) {
if (node != this) {
throw new InternalCompilerError("Cannot use a Node as a delegate or extension.");
}
}
public Node node() {
return this;
}
public JL del() {
return del != null ? del : this;
}
public Node del(JL del) {
if (this.del == del) {
return this;
}
JL old = this.del;
this.del = null;
Node_c n = (Node_c) copy();
n.del = del != this ? del : null;
if (n.del != null) {
n.del.init(n);
}
this.del = old;
return n;
}
public Ext ext(int n) {
if (n < 1) throw new InternalCompilerError("n must be >= 1");
if (n == 1) return ext();
return ext(n-1).ext();
}
public Node ext(int n, Ext ext) {
if (n < 1)
throw new InternalCompilerError("n must be >= 1");
if (n == 1)
return ext(ext);
Ext prev = this.ext(n-1);
if (prev == null)
throw new InternalCompilerError("cannot set the nth extension if there is no (n-1)st extension");
return this.ext(n-1, prev.ext(ext));
}
public Ext ext() {
return ext;
}
public Node ext(Ext ext) {
if (this.ext == ext) {
return this;
}
Ext old = this.ext;
this.ext = null;
Node_c n = (Node_c) copy();
n.ext = ext;
if (n.ext != null) {
n.ext.init(n);
}
this.ext = old;
return n;
}
public Object copy() {
try {
Node_c n = (Node_c) super.clone();
if (this.del != null) {
n.del = (JL) this.del.copy();
n.del.init(n);
}
if (this.ext != null) {
n.ext = (Ext) this.ext.copy();
n.ext.init(n);
}
return n;
}
catch (CloneNotSupportedException e) {
throw new InternalCompilerError("Java clone() weirdness.");
}
}
public Position position() {
return this.position;
}
public Node position(Position position) {
Node_c n = (Node_c) copy();
n.position = position;
return n;
}
public Node visitChild(Node n, NodeVisitor v) {
if (n == null) {
return null;
}
return v.visitEdge(this, n);
}
public Node visit(NodeVisitor v) {
return v.visitEdge(null, this);
}
public Node visitEdge(Node parent, NodeVisitor v) {
Node n = v.override(parent, this);
if (n == null) {
NodeVisitor v_ = v.enter(parent, this);
if (v_ == null) {
throw new InternalCompilerError(
"NodeVisitor.enter() returned null.");
}
n = this.del().visitChildren(v_);
if (n == null) {
throw new InternalCompilerError(
"Node_c.visitChildren() returned null.");
}
n = v.leave(parent, this, n, v_);
if (n == null) {
throw new InternalCompilerError(
"NodeVisitor.leave() returned null.");
}
}
return n;
}
/**
* Visit all the elements of a list.
* @param l The list to visit.
* @param v The visitor to use.
* @return A new list with each element from the old list
* replaced by the result of visiting that element.
* If <code>l</code> is a <code>TypedList</code>, the
* new list will also be typed with the same type as
* <code>l</code>. If <code>l</code> is <code>null</code>,
* <code>null</code> is returned.
*/
public List visitList(List l, NodeVisitor v) {
if (l == null) {
return null;
}
List result = l;
List vl = new ArrayList(l.size());
for (Iterator i = l.iterator(); i.hasNext(); ) {
Node n = (Node) i.next();
Node m = visitChild(n, v);
if (n != m) {
result = vl;
}
vl.add(m);
}
return result;
}
public Node visitChildren(NodeVisitor v) {
return this;
}
/**
* Push a new scope upon entering this node, and add any declarations to the
* context that should be in scope when visiting children of this node.
*
* @param c the current <code>Context</code>
* @return the <code>Context</code> to be used for visiting this node.
*/
public Context enterScope(Context c) { return c; }
/**
* Push a new scope for visiting the child node <code>child</code>.
* The default behavior is to delegate the call to the child node, and let
* it add appropriate declarations that should be in scope. However,
* this method gives parent nodes have the ability to modify this behavior.
*
* @param child the child node about to be entered.
* @param c the current <code>Context</code>
* @return the <code>Context</code> to be used for visiting node
* <code>child</code>
*/
public Context enterScope(Node child, Context c) {
return child.del().enterScope(c);
}
/**
* Add any declarations to the context that should be in scope when
* visiting later sibling nodes.
*/
public void addDecls(Context c) { }
// These methods override the methods in Ext_c.
// These are the default implementation of these passes.
public Node buildTypesOverride(TypeBuilder tb) throws SemanticException {
return null;
}
public NodeVisitor buildTypesEnter(TypeBuilder tb) throws SemanticException {
return tb;
}
public Node buildTypes(TypeBuilder tb) throws SemanticException {
return this;
}
/** Remove any remaining ambiguities from the AST. */
public Node disambiguateOverride(AmbiguityRemover ar) throws SemanticException {
return null;
}
public NodeVisitor disambiguateEnter(AmbiguityRemover ar) throws SemanticException {
return ar;
}
public Node disambiguate(AmbiguityRemover ar) throws SemanticException {
return this;
}
/** Add members to a class. */
public Node addMembersOverride(AddMemberVisitor am) throws SemanticException {
return null;
}
public NodeVisitor addMembersEnter(AddMemberVisitor am) throws SemanticException {
return am;
}
public Node addMembers(AddMemberVisitor am) throws SemanticException {
return this;
}
/** Type check the AST. */
public Node typeCheckOverride(TypeChecker tc) throws SemanticException {
return null;
}
public NodeVisitor typeCheckEnter(TypeChecker tc) throws SemanticException {
return tc;
}
public Node typeCheck(TypeChecker tc) throws SemanticException {
return this;
}
public Type childExpectedType(Expr child, AscriptionVisitor av) {
return child.type();
}
/** Check that exceptions are properly propagated throughout the AST. */
public Node exceptionCheckOverride(ExceptionChecker ec) throws SemanticException {
return null;
}
public NodeVisitor exceptionCheckEnter(ExceptionChecker ec) throws SemanticException {
return ec.push();
}
public Node exceptionCheck(ExceptionChecker ec) throws SemanticException {
List l = this.del().throwTypes(ec.typeSystem());
for (Iterator i = l.iterator(); i.hasNext(); ) {
ec.throwsException((Type)i.next(), position());
}
return this;
}
public List throwTypes(TypeSystem ts) {
return Collections.EMPTY_LIST;
}
/** Pretty-print the AST using the given <code>CodeWriter</code>. */
public void prettyPrint(CodeWriter w, PrettyPrinter pp) { }
public void printBlock(Node n, CodeWriter w, PrettyPrinter pp) {
w.begin(0);
print(n, w, pp);
w.end();
}
public void printSubStmt(Stmt stmt, CodeWriter w, PrettyPrinter pp) {
if (stmt instanceof Block) {
w.write(" ");
print(stmt, w, pp);
}
else {
w.allowBreak(4, " ");
printBlock(stmt, w, pp);
}
}
public void print(Node child, CodeWriter w, PrettyPrinter pp) {
pp.print(this, child, w);
}
/** Translate the AST using the given <code>CodeWriter</code>. */
public void translate(CodeWriter w, Translator tr) {
// By default, just rely on the pretty printer.
this.del().prettyPrint(w, tr);
}
public void dump(CodeWriter w) {
w.write(StringUtil.getShortNameComponent(getClass().getName()));
w.allowBreak(4, " ");
w.begin(0);
w.write("(del " + del() + ")");
w.end();
w.allowBreak(4, " ");
w.begin(0);
w.write("(ext ");
if (ext() == null) w.write("null");
else ext().dump(w);
w.write(")");
w.end();
w.allowBreak(4, " ");
w.begin(0);
w.write("(position " + (position != null ? position.toString()
: "UNKNOWN") + ")");
w.end();
}
public String toString() {
// This is really slow and so you are encouraged to override.
// return new StringPrettyPrinter(5).toString(this);
// Not slow anymore.
return getClass().getName();
}
}