package polyglot.ext.jl.ast; import polyglot.ast.*; import polyglot.types.*; import polyglot.visit.*; import polyglot.util.*; import java.util.*; /** * A <code>Switch</code> is an immutable representation of a Java * <code>switch</code> statement. Such a statement has an expression which * is evaluated to determine where to branch to, an a list of labels * and block statements which are conditionally evaluated. One of the * labels, rather than having a constant expression, may be lablled * default. */ public class Switch_c extends Stmt_c implements Switch { protected Expr expr; protected List elements; public Switch_c(Position pos, Expr expr, List elements) { super(pos); this.expr = expr; this.elements = TypedList.copyAndCheck(elements, SwitchElement.class, true); } /** Get the expression to switch on. */ public Expr expr() { return this.expr; } /** Set the expression to switch on. */ public Switch expr(Expr expr) { Switch_c n = (Switch_c) copy(); n.expr = expr; return n; } /** Get the switch elements of the statement. */ public List elements() { return Collections.unmodifiableList(this.elements); } /** Set the switch elements of the statement. */ public Switch elements(List elements) { Switch_c n = (Switch_c) copy(); n.elements = TypedList.copyAndCheck(elements, SwitchElement.class, true); return n; } /** Reconstruct the statement. */ protected Switch_c reconstruct(Expr expr, List elements) { if (expr != this.expr || ! CollectionUtil.equals(elements, this.elements)) { Switch_c n = (Switch_c) copy(); n.expr = expr; n.elements = TypedList.copyAndCheck(elements, SwitchElement.class, true); return n; } return this; } public Context enterScope(Context c) { return c.pushBlock(); } /** Visit the children of the statement. */ public Node visitChildren(NodeVisitor v) { Expr expr = (Expr) visitChild(this.expr, v); List elements = visitList(this.elements, v); return reconstruct(expr, elements); } /** Type check the statement. */ public Node typeCheck(TypeChecker tc) throws SemanticException { TypeSystem ts = tc.typeSystem(); if (! ts.isImplicitCastValid(expr.type(), ts.Int())) { throw new SemanticException("Switch index must be an integer.", position()); } Collection labels = new HashSet(); for (Iterator i = elements.iterator(); i.hasNext();) { SwitchElement s = (SwitchElement) i.next(); if (s instanceof Case) { Case c = (Case) s; Object key; String str; if (c.isDefault()) { key = "default"; str = "default"; } else if (c.expr().isConstant()) { key = new Long(c.value()); str = c.expr().toString() + " (" + c.value() + ")"; } else { continue; } if (labels.contains(key)) { throw new SemanticException("Duplicate case label: " + str + ".", c.position()); } labels.add(key); } } return this; } public Type childExpectedType(Expr child, AscriptionVisitor av) { TypeSystem ts = av.typeSystem(); if (child == expr) { return ts.Int(); } return child.type(); } public String toString() { return "switch (" + expr + ") { ... }"; } /** Write the statement to an output file. */ public void prettyPrint(CodeWriter w, PrettyPrinter tr) { w.write("switch ("); printBlock(expr, w, tr); w.write(") {"); w.allowBreak(4, " "); w.begin(0); boolean lastWasCase = false; boolean first = true; for (Iterator i = elements.iterator(); i.hasNext();) { SwitchElement s = (SwitchElement) i.next(); if (s instanceof Case) { if (lastWasCase) w.newline(0); else if (! first) w.allowBreak(0, " "); printBlock(s, w, tr); lastWasCase = true; } else { w.allowBreak(4," "); print(s, w, tr); lastWasCase = false; } first = false; } w.end(); w.allowBreak(0, " "); w.write("}"); } public Term entry() { return expr.entry(); } public List acceptCFG(CFGBuilder v, List succs) { SwitchElement prev = null; List cases = new LinkedList(); boolean hasDefault = false; for (Iterator i = elements.iterator(); i.hasNext(); ) { SwitchElement s = (SwitchElement) i.next(); if (s instanceof Case) { cases.add(s.entry()); if (((Case) s).expr() == null) { hasDefault = true; } } } // If there is no default case, add an edge to the end of the switch. if (! hasDefault) { cases.add(this); } v.visitCFG(expr, FlowGraph.EDGE_KEY_OTHER, cases); v.push(this).visitCFGList(elements, this); return succs; } }