001: // Copyright (c) 2001 Per M.A. Bothner.
002: // This is free software; for terms and warranty disclaimer see ./COPYING.
003:
004: package gnu.expr;
005:
006: import gnu.bytecode.*;
007: import gnu.mapping.*;
008:
009: /**
010: * This class represents a conditional.
011: * @author Per Bothner
012: */
013:
014: public class IfExp extends Expression {
015: Expression test;
016: Expression then_clause;
017: Expression else_clause;
018:
019: public IfExp(Expression i, Expression t, Expression e) {
020: test = i;
021: then_clause = t;
022: else_clause = e;
023: }
024:
025: /**
026: Create an if expression.
027: Optimizes the case where i is a constant.
028: */
029: public static Expression make(Expression i, Expression t,
030: Expression e) {
031: Interpreter interpreter = Interpreter.getInterpreter();
032:
033: if (i instanceof QuoteExp)
034: if (interpreter.isTrue(((QuoteExp) i).getValue()))
035: return t;
036: else
037: return e;
038:
039: return new IfExp(i, t, e);
040: }
041:
042: protected final Interpreter getInterpreter() {
043: return Interpreter.defaultInterpreter; // FIXME
044: }
045:
046: public Object eval(Environment env) throws Throwable {
047: Interpreter interpreter = getInterpreter();
048: if (interpreter.isTrue((test.eval(env))))
049: return then_clause.eval(env);
050: else if (else_clause != null)
051: return else_clause.eval(env);
052: else
053: return interpreter.noValue();
054: }
055:
056: public void eval(Environment env, CallContext ctx) throws Throwable {
057: Interpreter interpreter = getInterpreter();
058: if (interpreter.isTrue((test.eval(env))))
059: then_clause.eval(env, ctx);
060: else if (else_clause != null)
061: else_clause.eval(env, ctx);
062: }
063:
064: public void compile(Compilation comp, Target target) {
065: compile(test, then_clause,
066: else_clause == null ? QuoteExp.voidExp : else_clause,
067: comp, target);
068: }
069:
070: public static void compile(Expression test, Expression then_clause,
071: Expression else_clause, Compilation comp, Target target) {
072: Interpreter interpreter = comp.getInterpreter();
073: gnu.bytecode.CodeAttr code = comp.getCode();
074: Label trueLabel, falseLabel;
075: boolean trueInherited, falseInherited;
076: // A constant else_clause results from the expansion of (and ...),
077: // and also if the else_clause if elided, so we optimize this case.
078: if (target instanceof ConditionalTarget
079: && else_clause instanceof QuoteExp) {
080: falseInherited = true;
081: Object value = ((QuoteExp) else_clause).getValue();
082: if (interpreter.isTrue(value))
083: falseLabel = ((ConditionalTarget) target).ifTrue;
084: else
085: falseLabel = ((ConditionalTarget) target).ifFalse;
086: } else if (else_clause instanceof ExitExp
087: && ((ExitExp) else_clause).result instanceof QuoteExp
088: && ((ExitExp) else_clause).block.subTarget instanceof IgnoreTarget) {
089: falseInherited = true;
090: falseLabel = ((ExitExp) else_clause).block.exitLabel;
091: } else {
092: falseInherited = false;
093: falseLabel = new Label(code);
094: }
095: // The expansion of "or" creates an IfExp with test==then_clause.
096: // In that case, we know that the then_clause must be true.
097: // Let's optimize that case.
098: if (test == then_clause && target instanceof ConditionalTarget
099: && then_clause instanceof ReferenceExp) {
100: trueInherited = true;
101: trueLabel = ((ConditionalTarget) target).ifTrue;
102: } else {
103: trueInherited = false;
104: trueLabel = new Label(code);
105: }
106: ConditionalTarget ctarget = new ConditionalTarget(trueLabel,
107: falseLabel, interpreter);
108: if (trueInherited)
109: ctarget.trueBranchComesFirst = false;
110: test.compile(comp, ctarget);
111: code.emitIfThen();
112: if (!trueInherited /* && trueLabel.hasFixups()*/) {
113: trueLabel.define(code);
114: then_clause.compileWithPosition(comp, target);
115: }
116: if (!falseInherited /* && falseLabel.hasFixups()*/) {
117: code.emitElse();
118: falseLabel.define(code);
119: if (else_clause == null)
120: comp.compileConstant(Values.empty, target);
121: else
122: else_clause.compileWithPosition(comp, target);
123: } else
124: code.setUnreachable();
125: code.emitFi();
126: }
127:
128: public Type getType() {
129: Type thenType = then_clause.getType();
130: Type elseType = else_clause.getType();
131:
132: Type res;
133: if (thenType.isVoid())
134: res = thenType;
135: else if (elseType.isVoid())
136: res = elseType;
137: else
138: res = Type.lowestCommonSuperType(thenType, elseType);
139:
140: if (res == null) {
141: throw new Error("Incompatible types in " + this + ": "
142: + thenType + " and " + elseType);
143: }
144:
145: return res;
146: }
147:
148: protected Expression walk(ExpWalker walker) {
149: return walker.walkIfExp(this );
150: }
151:
152: protected void walkChildren(ExpWalker walker) {
153: test = test.walk(walker);
154: if (walker.exitValue == null)
155: then_clause = then_clause.walk(walker);
156: if (walker.exitValue == null)
157: else_clause = else_clause.walk(walker);
158: }
159:
160: public void print(OutPort out) {
161: out.startLogicalBlock("(If ", false, ")");
162: out.setIndentation(-2, false);
163: test.print(out);
164: out.writeSpaceLinear();
165: then_clause.print(out);
166: if (else_clause != null) {
167: out.writeSpaceLinear();
168: else_clause.print(out);
169: }
170: out.endLogicalBlock(")");
171: }
172: }
|