001: package gnu.expr;
002:
003: import gnu.bytecode.Type;
004: import gnu.mapping.*;
005: import gnu.text.Printable;
006: import gnu.text.SourceLocator;
007: import gnu.lists.Consumer;
008: import java.io.PrintWriter;
009:
010: /**
011: * Abstract class for syntactic forms that evaluate to a value.
012: * Scheme S-expressions get re-written to these before evaluation.
013: * @author Per Bothner
014: */
015:
016: public abstract class Expression extends Procedure0 implements
017: Printable, SourceLocator {
018: public final Object eval(CallContext ctx) throws Throwable {
019: int start = ctx.startFromContext();
020: try {
021: match0(ctx);
022: return ctx.getFromContext(start);
023: } catch (Throwable ex) {
024: ctx.cleanupFromContext(start);
025: throw ex;
026: }
027: }
028:
029: public final Object eval(Environment env) throws Throwable {
030: CallContext ctx = CallContext.getInstance();
031: Environment save = ctx.getEnvironmentRaw();
032: if (env != save)
033: ctx.setEnvironmentRaw(env);
034: try {
035: return eval(ctx);
036: } finally {
037: if (env != save)
038: ctx.setEnvironmentRaw(save);
039: }
040: }
041:
042: protected abstract boolean mustCompile();
043:
044: public final int match0(CallContext ctx) {
045: ctx.proc = this ;
046: ctx.pc = 0;
047: return 0;
048: }
049:
050: public final Object apply0() throws Throwable {
051: CallContext ctx = CallContext.getInstance();
052: check0(ctx);
053: return ctx.runUntilValue();
054: }
055:
056: /** Evaluate the expression.
057: * This is named apply rather than eval so it is compatible with the
058: * full-tail-call calling convention, and we can stash an Expression in
059: * CallContext's proc field. FIXME - are we making use of this?
060: */
061: public void apply(CallContext ctx) throws Throwable {
062: throw new RuntimeException("internal error - " + getClass()
063: + ".eval called");
064: }
065:
066: public final void print(Consumer out) {
067: if (out instanceof OutPort)
068: print((OutPort) out);
069: else if (out instanceof PrintWriter) {
070: OutPort port = new OutPort((PrintWriter) out);
071: print(port);
072: port.close();
073: } else {
074: CharArrayOutPort port = new CharArrayOutPort();
075: print(port);
076: port.close();
077: port.writeTo(out);
078: }
079: }
080:
081: public abstract void print(OutPort ps);
082:
083: /**
084: * Print line and column number if specified.
085: * This is a helper routineintended for use by print(OutPort).
086: */
087: public void printLineColumn(OutPort out) {
088: int line = getLineNumber();
089: if (line > 0) {
090: out.print("line:");
091: out.print(line);
092: int column = getColumnNumber();
093: if (column > 0) {
094: out.print(':');
095: out.print(column);
096: }
097: out.writeSpaceFill();
098: }
099: }
100:
101: public abstract void compile(Compilation comp, Target target);
102:
103: /** Same as compile, but emit line number beforehard. */
104: public final void compileWithPosition(Compilation comp,
105: Target target) {
106: int line = getLineNumber();
107: if (line > 0) {
108: comp.getCode().putLineNumber(getFileName(), line);
109: compileNotePosition(comp, target, this );
110: } else
111: compile(comp, target);
112: }
113:
114: /** Same as 2-argument compileWithPosition,
115: * but use some other Expression's line number. */
116: public final void compileWithPosition(Compilation comp,
117: Target target, Expression position) {
118: int line = position.getLineNumber();
119: if (line > 0) {
120: comp.getCode().putLineNumber(position.getFileName(), line);
121: compileNotePosition(comp, target, position);
122: } else
123: compile(comp, target);
124: }
125:
126: /** Compile, but take note of line number. */
127: public final void compileNotePosition(Compilation comp,
128: Target target, Expression position) {
129: String saveFilename = comp.getFileName();
130: int saveLine = comp.getLineNumber();
131: int saveColumn = comp.getColumnNumber();
132: comp.setLine(position);
133: compile(comp, target);
134: // This might logically belong in a `finally' clause.
135: // It is intentionally not so, so if there is an internal error causing
136: // an exception, we get the line number where the exception was thrown.
137: comp.setLine(saveFilename, saveLine, saveColumn);
138: }
139:
140: public final void compile(Compilation comp, Type type) {
141: // Should we use Target.pushValue instead? FIXME.
142: compile(comp, StackTarget.getInstance(type));
143: }
144:
145: /** Compile an expression with checking suitable for a known Declaration.
146: * Leaves the result on the stack (i.e. does not assign to the lhs).
147: * It does coerce the value to a suitable type for the lhs, and
148: * throw a hopefully-informative WrongType exception on failure.
149: */
150: public final void compile(Compilation comp, Declaration lhs) {
151: compile(comp, CheckedTarget.getInstance(lhs.getType(), lhs
152: .getName(), WrongType.ARG_VARNAME));
153: }
154:
155: /** Compile all but the first sub-"statement".
156: * A kludge used for constructor methods, since if the first "statement"
157: * is a super-constructor we need to inject initializer expressions. */
158: public static void compileButFirst(Expression exp, Compilation comp) {
159: if (exp instanceof BeginExp) {
160: BeginExp bexp = (BeginExp) exp;
161: int n = bexp.length;
162: if (n == 0)
163: return;
164: Expression[] exps = bexp.exps;
165: compileButFirst(exps[0], comp);
166: for (int i = 1; i < n; i++)
167: exps[i].compileWithPosition(comp, Target.Ignore);
168: }
169: }
170:
171: protected Expression walk(ExpWalker walker) {
172: return walker.walkExpression(this );
173: }
174:
175: protected void walkChildren(ExpWalker walker) {
176: }
177:
178: /** Apply inlining transformations on a given ApplyExp.
179: * Assumes the ApplyExp's function is the this expression,
180: * or can be optimized to this expression.
181: * @param exp an application whose function expression can be simplified
182: * to this expression.
183: * @param walker the context for the current inlining pass
184: * @param decl if non-null, a Declaration bound to this expression
185: * @return an Expression equivalent to te passed-in exp.
186: */
187: public Expression inline(ApplyExp exp, InlineCalls walker,
188: Declaration decl) {
189: return exp;
190: }
191:
192: String filename;
193: int position;
194:
195: public static final Expression[] noExpressions = new Expression[0];
196:
197: /** Helper method to create a `while' statement. */
198: public static Expression makeWhile(Object cond, Object body,
199: Compilation parser) {
200: Expression[] inits = new Expression[1];
201: LetExp let = new LetExp(inits);
202: String fname = "%do%loop";
203: Declaration fdecl = let.addDeclaration(fname);
204: Expression recurse = new ApplyExp(new ReferenceExp(fdecl),
205: noExpressions);
206: LambdaExp lexp = new LambdaExp();
207: parser.push(lexp);
208: lexp.body = new IfExp(parser.parse(cond), new BeginExp(parser
209: .parse(body), recurse), QuoteExp.voidExp);
210: lexp.setName(fname);
211: parser.pop(lexp);
212: inits[0] = lexp;
213: fdecl.noteValue(lexp);
214: let
215: .setBody(new ApplyExp(new ReferenceExp(fdecl),
216: noExpressions));
217: return let;
218: }
219:
220: /** Copies the current location. */
221: public final void setLocation(SourceLocator location) {
222: this .filename = location.getFileName();
223: setLine(location.getLineNumber(), location.getColumnNumber());
224: }
225:
226: public final Expression setLine(Expression old) {
227: setLocation(old);
228: return this ;
229: }
230:
231: public final void setFile(String filename) {
232: this .filename = filename;
233: }
234:
235: public final void setLine(int lineno, int colno) {
236: if (lineno < 0)
237: lineno = 0;
238: if (colno < 0)
239: colno = 0;
240: position = (lineno << 12) + colno;
241: }
242:
243: public final void setLine(int lineno) {
244: setLine(lineno, 0);
245: }
246:
247: public final String getFileName() {
248: return filename;
249: }
250:
251: /** Set line number from current position in <code>Compilation</code>. */
252: public void setLine(Compilation comp) {
253: int line = comp.getLineNumber();
254: if (line > 0) {
255: setFile(comp.getFileName());
256: setLine(line, comp.getColumnNumber());
257: }
258: }
259:
260: public String getPublicId() {
261: return null;
262: }
263:
264: public String getSystemId() {
265: return filename;
266: }
267:
268: /** Get the line number of (the start of) this Expression.
269: * The "first" line is line 1; unknown is -1. */
270: public final int getLineNumber() {
271: int line = position >> 12;
272: return line == 0 ? -1 : line;
273: }
274:
275: public final int getColumnNumber() {
276: int column = position & ((1 << 12) - 1);
277: return column == 0 ? -1 : column;
278: }
279:
280: public boolean isStableSourceLocation() {
281: return true;
282: }
283:
284: /** Return the Type used to represent the values of this Expression. */
285: public Type getType() {
286: return Type.pointer_type;
287: }
288:
289: /** Return value if it is constant, or null if non-constant or unknown. */
290: public Object valueIfConstant() {
291: return null;
292: }
293:
294: protected int flags;
295: protected static final int NEXT_AVAIL_FLAG = 1;
296:
297: public void setFlag(boolean setting, int flag) {
298: if (setting)
299: flags |= flag;
300: else
301: flags &= ~flag;
302: }
303:
304: public void setFlag(int flag) {
305: flags |= flag;
306: }
307:
308: public int getFlags() {
309: return flags;
310: }
311:
312: public boolean getFlag(int flag) {
313: return (flags & flag) != 0;
314: }
315:
316: /** True if evaluating may have side-effects. */
317: public boolean side_effects() {
318: return true;
319: }
320:
321: public String toString() {
322: String tname = getClass().getName();
323: if (tname.startsWith("gnu.expr."))
324: tname = tname.substring(9);
325: return tname + "@" + Integer.toHexString(hashCode());
326: }
327: }
|