001: package sisc.interpreter;
002:
003: import java.io.*;
004:
005: import sisc.compiler.Compiler;
006: import sisc.data.*;
007: import sisc.env.*;
008: import sisc.ser.Deserializer;
009: import sisc.ser.Serializer;
010: import sisc.util.Util;
011:
012: /**
013: * The SISC engine.
014: * <p>
015: * Interpreter is the SISC engine. It contains the engine registers,
016: * and the main loop responsible for repeatedly executing the
017: * <tt>nxp</tt> register and maintaining the stack. Interpreter also
018: * localizes all thread-specific information. Interpreters must only
019: * execute in the thread which created them. Furthermore, nested calls
020: * from Java into Scheme must be carried out in fresh interpreter
021: * instances; thus at any point in time a thread contains a stack of
022: * interpreters, the top of which is the interpreter currently in use.
023: * </p>
024: * <p>
025: * Additionally, it is the interface from Java code for evaluating
026: * Scheme code or calling Scheme procedures.
027: * </p>
028: * @see Context
029: */
030: public class Interpreter extends Util {
031:
032: private final static Expression EVAL_APPEVAL = annotatedAppEval("eval");
033: private final static Expression CONTINUATION_APPEVAL = annotatedAppEval("continuation");
034:
035: private static Expression annotatedAppEval(String fn) {
036: return annotatedAppEval(Interpreter.class, fn);
037: }
038:
039: public static class ThrowSchemeException extends Expression {
040:
041: public void eval(Interpreter r) throws ContinuationException,
042: SchemeRuntimeException {
043: r.nxp = null;
044: Values v = (Values) r.acc;
045: throw new SchemeRuntimeException(pair(v.values[0]),
046: proc(v.values[1]),
047: v.values.length > 2 ? proc(v.values[2]) :
048: //If we are at the top of the
049: //stack, use a default fk
050: (r.fk == null ? top_fk : r.fk));
051: }
052:
053: public Value express() {
054: return list(Symbol.get("TSException"));
055: }
056:
057: public void serialize(Serializer s) throws IOException {
058: }
059:
060: public void deserialize(Deserializer s) throws IOException {
061: }
062: }
063:
064: //the compiler is stateless; if that ever changes it would need to
065: //be moved to the dynenv
066: public static Compiler compiler = new Compiler();
067:
068: public ThreadContext tctx;
069: public DynamicEnvironment dynenv;
070:
071: //FLAGS
072: private boolean saveVLR; //prevent recycling of VLR after procedure
073: //invocation
074:
075: //Interpreter specific temporaries
076: public Value[][] IAI = new Value[][] { new Value[1], new Value[2],
077: new Value[3] };
078:
079: //ACCOUNTING REGISTERS
080: private boolean vlk; //vlk, when true, indicates the
081: //frame was captured.
082:
083: //ACTIVITY REGISTERS
084: public Value acc; //Accumulator
085: public Expression nxp; //Next Expression
086: public Value[] vlr, //Value Rib
087: lcl, //Local Variables
088: env; //Lexical Variables
089: private CallFrame stk; //Continuation (Stack)
090: public CallFrame fk; //Failure Continuation
091: public SymbolicEnvironment tpl; //Top-level environment
092:
093: private StackTracer tracer; //for stack tracking
094:
095: //Scheme->Java exception conversion FK
096: static CallFrame top_fk = new CallFrame(new ThrowSchemeException(),
097: null, false, null, null, null, null, null, null);
098: static {
099: top_fk.vlk = true;
100: // This creates a loop in the stack, which will be a problem for
101: // any code checking for null as the bottom of the stack. However,
102: // the only code in SISC which does this is CallFrame.capture(), which
103: // will also break when vlk=true.
104: top_fk.fk = top_fk;
105: }
106:
107: public Interpreter(ThreadContext tctx, DynamicEnvironment dynenv) {
108: fk = top_fk;
109: this .tctx = tctx;
110: this .dynenv = dynenv;
111: tpl = getCtx().toplevel_env;
112: }
113:
114: public AppContext getCtx() {
115: return dynenv.ctx;
116: }
117:
118: public Symbol getSymbol(String v) {
119: return Symbol.get(v, dynenv.caseSensitive);
120: }
121:
122: public Expression compile(Value v) throws ContinuationException {
123: return compile(v, getCtx().toplevel_env);
124: }
125:
126: public Expression compile(Value v, SymbolicEnvironment env)
127: throws ContinuationException {
128: return compiler.compile(this , v, env);
129: }
130:
131: public Value interpret(Expression e) throws SchemeException {
132: SymbolicEnvironment tpl = getCtx().toplevel_env;
133:
134: stk = createFrame(null, null, false, null, null, tpl, top_fk,
135: null, null);
136: tracer = makeStackTracer();
137: nxp = e;
138: this .tpl = tpl;
139: interpret();
140: return acc;
141: }
142:
143: protected void interpret() throws SchemeException {
144: try {
145: do {
146: try {
147: do {
148: while (nxp == null)
149: pop(stk);
150: nxp.eval(this );
151: } while (true);
152: } catch (ContinuationException ce) {
153: pop(ce.k);
154: }
155: } while (true);
156: } catch (NullPointerException done) {
157: if (nxp != null) {
158: try {
159: error(this , null, done.getMessage(), done);
160: } catch (ContinuationException ce) {
161: pop(ce.k);
162: interpret();
163: }
164: }
165: } catch (SchemeRuntimeException rte) {
166: throw rte.promote();
167: }
168: }
169:
170: public final void next(Expression nextExpr)
171: throws ContinuationException {
172: nxp = nextExpr;
173: nextExpr.eval(this );
174: }
175:
176: public final void newVLR(int size) {
177: newVLR(createValues(size));
178: }
179:
180: public final void newVLR(Value[] vlr) {
181: if (vlk) {
182: tracer = copyStackTracer();
183: vlk = false;
184: }
185: this .vlr = vlr;
186: }
187:
188: public final void pop(CallFrame c) {
189: nxp = c.nxp;
190: vlr = c.vlr;
191: lcl = c.lcl;
192: env = c.env;
193: tpl = c.tpl;
194: fk = c.fk;
195: stk = c.parent;
196: vlk = c.vlk;
197: tracer = c.tracer;
198: returnFrame(c);
199: }
200:
201: public final StackTracer makeStackTracer() {
202: int depth = dynenv.getMaxStackTraceDepthAsInt();
203: return (depth == 0 ? null : new StackTracer(depth));
204: }
205:
206: private final StackTracer copyStackTracer() {
207: return (tracer == null ? null : tracer.copy());
208: }
209:
210: private final void makeSafe() {
211: /*
212: The frame which contains the current vlr has been captured
213: by a continuation. As a result, several, possibly
214: concurrent, evaluations may reach it. In order to prevent
215: these evaluations from stepping on eachother's toes
216: (i.e. modify the same vlr), we copy the vlr before writing
217: to it. The copy does not need to be marked as captured since
218: it is private to the current computation.
219: */
220: Value[] newvlr = createValues(vlr.length);
221: System.arraycopy(vlr, 0, newvlr, 0, vlr.length);
222: vlr = newvlr;
223: vlk = false;
224: tracer = copyStackTracer();
225: }
226:
227: public final void setVLR(int pos, Value v) {
228: if (vlk)
229: makeSafe();
230: vlr[pos] = v;
231: }
232:
233: private final CallFrame createEmptyFrame(Expression e, CallFrame p,
234: StackTracer t) {
235: return createFrame(e, null, false, null, null, null, top_fk, p,
236: t);
237: }
238:
239: private final CallFrame createNearlyEmptyFrame(Expression e,
240: CallFrame p, StackTracer t) {
241: return createFrame(e, null, false, null, null, tpl, fk, p, t);
242: }
243:
244: public final void pushExpr(Expression e) {
245: stk = createNearlyEmptyFrame(e, stk, tracer);
246: tracer = makeStackTracer();
247: }
248:
249: public final void setFailureContinuation(Expression e) {
250: fk = createNearlyEmptyFrame(e, stk, copyStackTracer());
251: }
252:
253: private final Procedure createContinuation(CallFrame p) {
254: //In order to produce accurate stack traces for ks we insert a
255: //dummy frame with a copy of the current frame's stack trace.
256: //The CONTINUATION_APPEVAL nxp of the dummy frame is only
257: //there in order to avoid a harmless, but ugly, null nxp.
258: //It is never evaluated.
259: if (tracer == null)
260: return p;
261: else
262: return new ApplyParentFrame(createEmptyFrame(
263: CONTINUATION_APPEVAL, p, tracer.copy()));
264: }
265:
266: public final Procedure captureContinuation() {
267: return createContinuation(stk.capture(this ));
268: }
269:
270: public final Procedure captureEscapingContinuation() {
271: //Even though we're not capturing for long term preservation, we must protect this individual
272: //call frame from being recycled, mostly for error handling.
273: stk.vlk = true;
274: return createContinuation(stk);
275: }
276:
277: public void trace(Expression e) {
278: if (tracer != null) {
279: if (vlk) {
280: if (vlr == null)
281: vlr = ZV; //rare, but can happen
282: makeSafe();
283: }
284: tracer.add(e);
285: }
286: }
287:
288: public void error(Pair error) throws ContinuationException {
289: Procedure k = new ApplyParentFrame(createEmptyFrame(nxp, stk
290: .capture(this ), copyStackTracer()));
291: acc = new Values(new Value[] { error, k });
292: throw new ContinuationException(fk);
293: }
294:
295: /**
296: * Parses and evaluates s-expression(s) from an input port
297: *
298: * @param port input port
299: * @return The value of the last evaluated s-expression
300: * @exception IOException Raised if the port does not
301: * contain a parseable s-expression
302: * @exception SchemeException Raised if the evaluation of
303: * an expression results in an error
304: */
305: public Value evalInput(PushbackReader port) throws IOException,
306: SchemeException {
307: Value rv = VOID;
308: do {
309: try {
310: rv = eval(dynenv.parser.nextExpression(port));
311: } catch (EOFException e) {
312: return rv;
313: }
314: } while (true);
315: }
316:
317: /**
318: * Parses and evaluates s-expression(s)
319: *
320: * @param expr s-expressions(s)
321: * @return The value of the last evaluated s-expression
322: * @exception IOException Raised if the given string does not
323: * contain a parseable s-expression
324: * @exception SchemeException Raised if the evaluation of
325: * an expression results in an error
326: */
327: public Value eval(String expr) throws IOException, SchemeException {
328: return evalInput(new PushbackReader(new BufferedReader(
329: new StringReader(expr))));
330: }
331:
332: /**
333: * Evaluates a Scheme value as code. This is equivalent to
334: * <tt>(eval <i>v</i>)</tt> in Scheme.
335: *
336: * @param v A Scheme Value
337: * @return The resulting value
338: * @exception SchemeException Raised if the evaluation of the
339: * expression results in an error
340: */
341: public Value eval(Value v) throws SchemeException {
342: return eval(v, getCtx().toplevel_env);
343: }
344:
345: /**
346: * Evaluates a Scheme value as code. This is equivalent to
347: * <tt>(eval <i>v</i> <i>e</i>)</tt> in Scheme.
348: *
349: * @param v A Scheme Value
350: * @param env The environment in which to evaluate the value
351: * @return The resulting value
352: * @exception SchemeException Raised if the evaluation of the
353: * expression results in an error
354: */
355: public Value eval(Value v, SymbolicEnvironment env)
356: throws SchemeException {
357: return eval((Procedure) lookup(EVAL, REPORT), new Value[] { v,
358: env.asValue() });
359: }
360:
361: /**
362: * Applies the given procedure to the given values
363: *
364: * @param p A procedure
365: * @param args Arguments to call the procedure with
366: * @return The result returned by the procedure
367: * @exception SchemeException Raised if applying the
368: * procedure results in an error
369: */
370: public Value eval(Procedure p, Value[] args) throws SchemeException {
371: acc = p;
372: vlr = args;
373: return interpret(EVAL_APPEVAL);
374: }
375:
376: /**
377: * Loads zero or more Scheme source files or compiled libraries.
378: *
379: * @param files An array of Strings naming files to load.
380: * @return true on success, false if any source file produced
381: * an error.
382: */
383: public boolean loadSourceFiles(String[] files) {
384:
385: boolean returnStatus = true;
386: Procedure load = (Procedure) lookup(Symbol.get("load"),
387: Util.TOPLEVEL);
388:
389: for (int i = 0; i < files.length; i++) {
390: try {
391: eval(load, new Value[] { new SchemeString(files[i]) });
392: } catch (SchemeException se) {
393: Value vm = se.m;
394: try {
395: eval((Procedure) lookup(Symbol.get("print-error"),
396: Util.TOPLEVEL), new Value[] { vm, se.e });
397: } catch (SchemeException se2) {
398: if (vm instanceof Pair) {
399: System.err.println(Util
400: .simpleErrorToString((Pair) vm));
401: } else {
402: System.err.println(Util.liMessage(Util.SISCB,
403: "errorduringload")
404: + vm);
405: }
406: }
407: returnStatus = false;
408: }
409: }
410:
411: return returnStatus;
412: }
413:
414: public SymbolicEnvironment lookupContextEnv(Symbol s) {
415: return getCtx().lookupContextEnv(s);
416: }
417:
418: public void defineContextEnv(Symbol s, SymbolicEnvironment env) {
419: getCtx().defineContextEnv(s, env);
420: }
421:
422: public SymbolicEnvironment getContextEnv(Symbol s) {
423: SymbolicEnvironment contenv = null;
424: try {
425: contenv = lookupContextEnv(s);
426: } catch (ArrayIndexOutOfBoundsException e) {
427: contenv = new MemorySymEnv();
428: defineContextEnv(s, contenv);
429: }
430: return contenv;
431: }
432:
433: /**
434: * Defines a new binding in a named environment.
435: *
436: * @param s The name of the new binding
437: * @param v The value of the new binding
438: * @param context The name of the environment in which to
439: * create the binding
440: */
441: public void define(Symbol s, Value v, Symbol context) {
442: getContextEnv(context).define(s, v);
443: }
444:
445: /**
446: * Retrieves the value of a binding in a named environment
447: *
448: * @param s The name of the binding
449: * @param context The name of the environment from which
450: * the binding will be retrieved
451: * @return A value or expression
452: */
453: public Expression lookup(Symbol s, Symbol context) {
454: try {
455: return lookupContextEnv(context).lookup(s);
456: } catch (ArrayIndexOutOfBoundsException e) {
457: return null;
458: }
459: }
460:
461: /**
462: * Removes a binding in a named environment
463: *
464: * @param s The name of the binding
465: * @param context The name of the environment from which
466: * the binding will be retrieved
467: */
468: public void undefine(Symbol s, Symbol context) {
469: try {
470: lookupContextEnv(context).undefine(s);
471: } catch (ArrayIndexOutOfBoundsException e) {
472: }
473: }
474:
475: //POOLING
476: //STATIC --------------------
477:
478: protected static final int FRAMEPOOLMAX = 128;
479: protected CallFrame frameFreeList;
480: protected int frameFreeListSize;
481:
482: private final CallFrame createFrame(Expression n, Value[] v,
483: boolean vk, Value[] l, Value[] e, SymbolicEnvironment t,
484: CallFrame f, CallFrame p, StackTracer tr) {
485: CallFrame rv;
486: if (frameFreeList == null) {
487: rv = new CallFrame();
488: } else {
489: rv = frameFreeList;
490: frameFreeList = frameFreeList.parent;
491: frameFreeListSize--;
492: }
493: rv.init(n, v, vk, l, e, t, f, p, tr);
494: return rv;
495: }
496:
497: public final void push(Expression n) {
498: stk = createFrame(n, vlr, vlk, lcl, env, tpl, fk, stk, tracer);
499: tracer = makeStackTracer();
500: }
501:
502: public final void returnFrame(CallFrame f) {
503: if (f.vlk || frameFreeListSize >= FRAMEPOOLMAX)
504: return;
505:
506: //Clear some fields to avoid hanging onto otherwise
507: //garbage collectable data for too long
508: f.clear();
509:
510: f.parent = frameFreeList;
511: frameFreeList = f;
512: frameFreeListSize++;
513: }
514:
515: protected Value dv1[], dv2[], dv3[], dv4[];
516:
517: public final Value[] createValues(int size) {
518: Value[] rv;
519: switch (size) {
520: case 0:
521: return ZV;
522: case 1:
523: if (dv1 != null) {
524: rv = dv1;
525: dv1 = null;
526: return rv;
527: }
528: break;
529: case 2:
530: if (dv2 != null) {
531: rv = dv2;
532: dv2 = null;
533: return rv;
534: }
535: break;
536: case 3:
537: if (dv3 != null) {
538: rv = dv3;
539: dv3 = null;
540: return rv;
541: }
542: break;
543: case 4:
544: if (dv4 != null) {
545: rv = dv4;
546: dv4 = null;
547: return rv;
548: }
549: break;
550: }
551: return new Value[size];
552:
553: }
554:
555: public final void returnVLR() {
556: if (saveVLR) {
557: saveVLR = false;
558: } else {
559: if (!vlk)
560: returnValues(vlr);
561: vlr = null;
562: }
563: }
564:
565: public final void setupTailCall(Expression e, Value vlr0) {
566: saveVLR = true;
567: nxp = e;
568: if (vlk) {
569: newVLR(1);
570: } else {
571: if (vlr.length != 1) {
572: returnValues(vlr);
573: newVLR(1);
574: }
575: }
576: vlr[0] = vlr0;
577: }
578:
579: public final void setupTailCall(Expression e, Value[] newvlr) {
580: saveVLR = true;
581: nxp = e;
582: if (!vlk) {
583: returnValues(vlr);
584: }
585: vlr = newvlr;
586: }
587:
588: public final void returnValues(Value[] v) {
589: switch (v.length) {
590: case 4:
591: v[3] = v[2] = v[1] = v[0] = null;
592: dv4 = v;
593: break;
594: case 3:
595: v[2] = v[1] = v[0] = null;
596: dv3 = v;
597: break;
598: case 2:
599: v[1] = v[0] = null;
600: dv2 = v;
601: break;
602: case 1:
603: v[0] = null;
604: dv1 = v;
605: break;
606: }
607: }
608:
609: /**
610: * Returns a Value[] prepared as a value rib
611: * for a procedure with a fixed argument count.
612: * This may or may not clone the VLR depending on
613: * whether it is safe to not do so.
614: */
615: public Value[] vlrToArgs() {
616: Value[] vals;
617: if (vlk) {
618: vals = createValues(vlr.length);
619: System.arraycopy(vlr, 0, vals, 0, vals.length);
620: } else {
621: vals = vlr;
622: }
623: return vals;
624: }
625:
626: /**
627: * Returns a Value[] prepared as a value rib for a
628: * for procedure expecting rest args in the last
629: * rib position. This may or may not clone the VLR
630: * depending on whether it is safe to not do so.
631: *
632: * @param fcount The number of arguments to prepare
633: * including the rest variable
634: */
635: public Value[] vlrToRestArgs(int fcount) {
636: Value[] vals;
637: int sm1 = fcount - 1;
638: int vl = vlr.length;
639:
640: if (vl < fcount || vlk) {
641: /**
642: * We must copy the vlr if its locked,
643: * otherwise we may side-effect a captured vlr by
644: * creating the rest argument.
645: *
646: * @see Closure.matchArgs
647: */
648: vals = createValues(fcount);
649: System.arraycopy(vlr, 0, vals, 0, sm1);
650:
651: vals[sm1] = valArrayToList(vlr, sm1, vl - sm1);
652: returnVLR(); //NB: this checks vlk first
653: } else {
654: vals = vlr;
655: vals[sm1] = valArrayToList(vlr, sm1, vl - sm1);
656: }
657: return vals;
658: }
659: }
660: /*
661: * The contents of this file are subject to the Mozilla Public
662: * License Version 1.1 (the "License"); you may not use this file
663: * except in compliance with the License. You may obtain a copy of
664: * the License at http://www.mozilla.org/MPL/
665: *
666: * Software distributed under the License is distributed on an "AS
667: * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
668: * implied. See the License for the specific language governing
669: * rights and limitations under the License.
670: *
671: * The Original Code is the Second Interpreter of Scheme Code (SISC).
672: *
673: * The Initial Developer of the Original Code is Scott G. Miller.
674: * Portions created by Scott G. Miller are Copyright (C) 2000-2007
675: * Scott G. Miller. All Rights Reserved.
676: *
677: * Contributor(s):
678: * Matthias Radestock
679: *
680: * Alternatively, the contents of this file may be used under the
681: * terms of the GNU General Public License Version 2 or later (the
682: * "GPL"), in which case the provisions of the GPL are applicable
683: * instead of those above. If you wish to allow use of your
684: * version of this file only under the terms of the GPL and not to
685: * allow others to use your version of this file under the MPL,
686: * indicate your decision by deleting the provisions above and
687: * replace them with the notice and other provisions required by
688: * the GPL. If you do not delete the provisions above, a recipient
689: * may use your version of this file under either the MPL or the
690: * GPL.
691: */
|