/* * 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.ast; import java.io.OutputStream; import java.io.Writer; import java.util.*; import polyglot.frontend.Compiler; import polyglot.frontend.ExtensionInfo; import polyglot.types.*; import polyglot.util.*; import polyglot.visit.*; import x10.errors.Errors; /** * 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; protected boolean error; public final int hashCode() { return super.hashCode(); } public final boolean equals(Object o) { return this == o; } public Node_c(Position pos) { assert(pos != null); this.position = pos; this.error = false; } public Node setResolverOverride(Node parent, TypeCheckPreparer v) { return null; } public void setResolver(Node parent, TypeCheckPreparer v) { } 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 boolean error() { return error; } public Node error(boolean flag) { Node_c n = (Node_c) copy(); n.error = flag; 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); } /** * @deprecated Call {@link Node#visitChild(Node, NodeVisitor)} instead. */ 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 <T extends Node> List<T> visitList(List<T> l, NodeVisitor v) { if (l == null) { return null; } List<T> result = l; List<T> vl = new ArrayList<T>(l.size()); for (Iterator<T> i = l.iterator(); i.hasNext(); ) { T n = i.next(); Node m = visitChild(n, v); if (n != m) { result = vl; } if (m instanceof NodeList) { vl.addAll((List<T>)((NodeList) m).nodes()); } else if (m != null) { vl.add((T)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 enterChildScope(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) { return null; } public NodeVisitor buildTypesEnter(TypeBuilder tb) { return tb; } public Node buildTypes(TypeBuilder tb) { return this; } public Node disambiguate(ContextVisitor ar) { return this; } /** Type check the AST. */ public Node typeCheckOverride(Node parent, ContextVisitor tc) { return null; } public NodeVisitor typeCheckEnter(TypeChecker tc) { return tc; } public Node typeCheck(ContextVisitor tc) { return this; } public Node checkConstants(ContextVisitor tc) { return this; } public Node conformanceCheck(ContextVisitor tc) { return this; } public NodeVisitor exceptionCheckEnter(ExceptionChecker ec) { return ec.push(); } public Node exceptionCheck(ExceptionChecker ec) { List<Type> l = this.del().throwTypes(ec.typeSystem()); for (Type t : l) { try { ec.throwsException(t, position()); } catch (SemanticException e) { Errors.issue(ec.job(), e, this); } } return this; } public List<Type> throwTypes(TypeSystem ts) { return Collections.<Type>emptyList(); } /** Dump the AST for debugging. */ public void dump(OutputStream os) { CodeWriter cw = Compiler.createCodeWriter(os); NodeVisitor dumper = new DumpAst(cw); dumper = dumper.begin(); this.visit(dumper); cw.newline(); dumper.finish(); } /** Dump the AST for debugging. */ public void dump(Writer w) { CodeWriter cw = Compiler.createCodeWriter(w); NodeVisitor dumper = new DumpAst(cw); dumper = dumper.begin(); this.visit(dumper); cw.newline(); dumper.finish(); } /** Pretty-print the AST for debugging. */ public void prettyPrint(OutputStream os) { try { CodeWriter cw = Compiler.createCodeWriter(os); this.del().prettyPrint(cw, new PrettyPrinter()); cw.flush(); } catch (java.io.IOException e) { } } /** Pretty-print the AST for debugging. */ public void prettyPrint(Writer w) { try { CodeWriter cw = Compiler.createCodeWriter(w); this.del().prettyPrint(cw, new PrettyPrinter()); cw.flush(); } catch (java.io.IOException e) { } } /** 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 "); if (del() == this) w.write("*"); else w.write(del().toString()); w.write(")"); 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(); } public final Node copy(NodeFactory nf) { throw new InternalCompilerError("Unimplemented operation. This class " + "(" + this.getClass().getName() + ") does " + "not implement copy(NodeFactory). This compiler extension should" + " either implement the method, or not invoke this method."); } public final Node copy(ExtensionInfo extInfo) { return this.del().copy(extInfo.nodeFactory()); } }