vx-scheme/src/interp.cpp

1628 lines
40 KiB
C++

//----------------------------------------------------------------------
// vx-scheme : Scheme interpreter.
// Copyright (c) 2002,2003,2006 and onwards Colin Smith.
//
// You may distribute under the terms of the Artistic License,
// as specified in the LICENSE file.
//
// interp.cpp : SICP-style R4Rs-compliant Scheme interpreter
#include "vx-scheme.h"
// --------------------------------------------------------------------------
// Populate the symbol table with builtins.
//
INTERN_SYM (s_and, "and");
INTERN_SYM (s_apply, "apply");
INTERN_SYM (s_begin, "begin");
INTERN_SYM (s_callcc, "call-with-current-continuation");
INTERN_SYM (s_callwif, "call-with-input-file");
INTERN_SYM (s_callwof, "call-with-output-file");
INTERN_SYM (s_case, "case");
INTERN_SYM (s_cond, "cond");
INTERN_SYM (s_define, "define");
INTERN_SYM (s_defmacro, "defmacro");
INTERN_SYM (s_delay, "delay");
INTERN_SYM (s_do, "do");
INTERN_SYM (s_else, "else");
INTERN_SYM (s_eval, "eval");
INTERN_SYM (s_force, "force");
INTERN_SYM (s_foreach, "for-each");
INTERN_SYM (s_if, "if");
INTERN_SYM (s_lambda, "lambda");
INTERN_SYM (s_let, "let");
INTERN_SYM (s_letrec, "letrec");
INTERN_SYM (s_letstar, "let*");
INTERN_SYM (s_load, "load");
INTERN_SYM (s_map, "map");
INTERN_SYM (s_or, "or");
INTERN_SYM (s_passto, "=>");
INTERN_SYM (s_set, "set!");
INTERN_SYM (s_time, "time");
INTERN_SYM (s_withinput, "with-input-from-file");
INTERN_SYM (s_withoutput, "with-output-to-file");
// --------------------------------------------------------------------------
// Unsafe Accessors
//
// These are versions of car and cdr that do not check to ensure that
// they are applied to conses. The program can crash if this precondition
// is not meant. They are only safe to use when the implementation can
// guarantee that they are applied to conses (and so we only use them
// on lists that we manage explicitly).
#define CAR(c) ((c)->ca.p)
#define CDR(c) ((c)->cd.p)
void Context::bind (Cell * env, Cell * c, Cell * value)
{
cellvector * vec = car (env)->VectorValue ();
psymbol s = c->SymbolValue ();
if (c->flag (Cell::QUICK) && c->e_skip() == 0)
{
// We have a quick binding, and, as we expect,
// it's within this frame. We can establish
// it without searching.
int b_skip = c->b_skip();
int sz = vec->size ();
if (b_skip == sz)
{
r_nu = cons (c, value);
vec->push (r_nu);
}
else if (b_skip < 0 || b_skip > sz)
error ("internal error: invalid lexical address: ",
c->SymbolValue ()->key);
else
Cell::setcdr (vec->get (b_skip), value);
}
else
{
for (int ix = 0; ix < vec->size (); ++ix)
{
Cell * c = vec->get (ix);
if (car (c)->SymbolValue () == s)
{
Cell::setcdr (c, value);
return;
}
}
// construct new binding element (being carful that
// intermediate material is reachable from the register
// set.
r_nu = make_symbol (s);
r_nu = cons (r_nu, value);
vec->push (r_nu);
}
}
// This is a list of "states" the evaluator can be in. The names
// were chosen to harmonize with those chosen in SICP.
enum {
eval_dispatch,
eval_complete,
ev_application, ev_application2,
ev_args1, ev_args2,
ev_sequence,
ev_sequence_continue,
apply_dispatch, apply_dispatch2,
ev_if,
ev_if_decide,
ev_finish,
ev_define,
ev_define_1,
ev_eval,
ev_eval1,
ev_set,
ev_set_1,
ev_or, ev_or2,
ev_and, ev_and2,
macro_subst,
ev_let,
ev_let_init,
let_accumulate_binding,
ev_letstar,
ev_letstar_init,
ev_letstar_bind,
ev_do,
ev_do_init,
ev_do_bind,
ev_do_test,
ev_after_test,
ev_do_step,
ev_step_1,
ev_step_bind,
ev_step_finish,
ev_do_step_2,
ev_cond,
ev_cond_test,
ev_apply,
ev_apply2,
ev_apply3,
macro_subst2,
ev_cond_passto,
ev_quasiquote, ev_unquote, ev_qq0, ev_qq1, ev_qq2, ev_qq3,
ev_qq_decrease, ev_qqd_1, ev_qq_finish,
ev_apply4,
ev_unq_spl,
ev_unq_spl2,
ev_letrec, ev_letrec1, ev_letrec2,
ev_case, ev_case2,
ev_foreach, ev_foreach1, ev_foreach2, ev_foreach3, ev_foreach4,
ev_force, ev_force2,
ev_map, ev_map1, ev_map2, ev_map3,
ev_withinput, ev_withinput1, ev_withinput2,
ev_withoutput, ev_withoutput1, ev_withoutput2,
ev_load, ev_load2, ev_callwof, ev_callwof2, ev_time, ev_time1,
};
// These are the above states in string form. This is only used
// for debugging, to dump the evaluator's state transitions.
static const char * state_name [] =
{
"eval_dispatch",
"eval_complete",
"ev_application", "ev_application2",
"ev_args1", "ev_args2",
"ev_sequence",
"ev_sequence_continue",
"apply_dispatch", "apply_dispatch2",
"ev_eval", "ev_eval1",
"ev_if",
"ev_if_decide",
"ev_finish",
"ev_define",
"ev_define_1",
"ev_set",
"ev_set_1",
"ev_or", "ev_or2",
"ev_and", "ev_and2",
"macro_subst",
"ev_let",
"ev_let_init",
"let_accumulate_binding",
"ev_letstar",
"ev_letstar_init",
"ev_letstar_bind",
"ev_do",
"ev_do_init",
"ev_do_bind",
"ev_do_test",
"ev_after_test",
"ev_do_step",
"ev_step_1",
"ev_step_bind",
"ev_step_finish",
"ev_do_step_2",
"ev_cond",
"ev_cond_test",
"ev_apply",
"ev_apply2",
"ev_apply3",
"macro_subst2",
"ev_cond_passto",
"ev_quasiquote", "ev_unquote", "ev_qq0", "ev_qq1", "ev_qq2", "ev_qq3",
"ev_qq_decrease", "ev_qqd_1", "ev_qq_finish",
"ev_apply4",
"ev_unq_spl",
"ev_unq_spl2",
"ev_letrec", "ev_letrec1", "ev_letrec2",
"ev_case", "ev_case2",
"ev_foreach", "ev_foreach1", "ev_foreach2", "ev_foreach3", "ev_foreach4",
"ev_force", "ev_force2",
"ev_map", "ev_map1", "ev_map2", "ev_map3",
"ev_withinput", "ev_withinput2", "ev_withoutput", "ev_withoutput2",
"ev_load", "ev_load2", "ev_callwof", "ev_callwof2", "ev_time", "ev_time1",
};
// Here it is: a >1000 line function that consists of a giant switch
// statement, and it's peppered with goto's!
//
// This would be inexcusable, except it has to do its job (evaulation
// Scheme expressions) without using the C stack as a resource. This
// could be thought of as a sort of microcode, using a set of virtual
// machine registers (r_exp, r_unev, etc.: the uses of these registers
// is described in SICP).
//
// I've followed this approach because I wanted to be able to capture
// continuations and collect garbage without having to know any details
// about how the C stack operates. This evaluator is somewhat more
// complicated than the one described in SICP since it supports many
// language features not discussed in that chapter.
//
// For these reasons, eval should not be recursed into by anyone,
// including itself.
Cell* Context::interp_evaluator(Cell * form)
{
psymbol s;
Cell::Type t;
Cell::Procedure lambda;
int flag = 0;
double t1;
bool trace;
psymbol p;
sstring read_sstr;
init_machine ();
state = eval_dispatch;
r_cont = eval_complete;
r_exp = form;
r_qq = 0;
trace = OS::flag (TRACE_EVAL);
#define GOTO(x) do { \
state = x; \
goto TOP; \
} while (0)
#define EVAL_DISPATCH() do { \
if (r_exp == nil) \
{ \
r_val = nil; \
GOTO (r_cont); \
} \
Cell::Type __t = (r_exp)->type(); \
if (__t == Cell::Cons) \
GOTO (ev_application); \
if (__t == Cell::Symbol) \
r_val = get (r_env, r_exp); \
else \
r_val = r_exp; \
GOTO (r_cont); \
} while (0)
#define RETURN_VALUE(v) do { \
r_val = (v); \
restore (r_cont); \
GOTO (r_cont); \
} while (0)
// If the exp is self-evaluating or a variable, handle it
// immediately. Else call eval_dispatch in a context
// where r_env end r_unev are saved/restored.
#define CALL_EVAL(label) \
t = (r_exp)->type(); \
if (t == Cell::Symbol) \
{ \
r_val = get (r_env, r_exp); \
goto label##__2; \
} \
else if (t != Cell::Cons) \
{ \
r_val = r_exp; \
goto label##__2; \
} \
else \
{ \
save (r_env); \
save (r_unev); \
r_cont = label; \
GOTO (eval_dispatch); \
} \
case label: \
restore (r_unev); \
restore (r_env); \
label##__2:
TOP:
if (trace)
print_vm_state ();
switch (state)
{
case eval_dispatch:
if (r_exp == nil)
{
r_val = nil;
GOTO (r_cont);
}
switch (r_exp->type ())
{
case Cell::Symbol:
r_val = get (r_env, r_exp);
GOTO (r_cont);
case Cell::Cons:
GOTO (ev_application);
default: // self-evaluating
r_val = r_exp;
GOTO (r_cont);
}
case eval_complete:
return r_val;
case ev_application:
save (r_cont);
r_unev = cdr (r_exp);
r_exp = car (r_exp);
CALL_EVAL (ev_application2);
r_proc = r_val;
case apply_dispatch:
if (!r_proc->flag (Cell::MACRO))
{
// It's not a special form: evaluate all the arguments
// in unev and collect them into r_argl.
clear (r_argl);
save (r_proc);
case ev_args1:
if (r_unev == nil)
{
restore (r_proc);
GOTO (apply_dispatch2);
}
save (r_argl);
r_exp = car (r_unev);
r_unev = cdr (r_unev);
CALL_EVAL (ev_args2);
restore (r_argl);
l_append (r_argl, r_val);
GOTO (ev_args1);
}
case apply_dispatch2:
switch (r_proc->type ())
{
case Cell::Builtin:
// =================================================
// THE BUILTIN SPECIAL FORMS
// =================================================
s = r_proc->BuiltinValue ();
if (s == s_if) GOTO (ev_if);
else if (s == s_define) GOTO (ev_define);
else if (s == s_begin) GOTO (ev_sequence);
else if (s == s_set) GOTO (ev_set);
else if (s == s_let) GOTO (ev_let);
else if (s == s_letstar) GOTO (ev_letstar);
else if (s == s_letrec) GOTO (ev_letrec);
else if (s == s_do) GOTO (ev_do);
else if (s == s_cond) GOTO (ev_cond);
else if (s == s_case) GOTO (ev_case);
else if (s == s_eval) GOTO (ev_eval);
else if (s == s_foreach) GOTO (ev_foreach);
else if (s == s_load) GOTO (ev_load);
else if (s == s_map) GOTO (ev_map);
else if (s == s_apply) GOTO (ev_apply);
else if (s == s_force) GOTO (ev_force);
else if (s == s_quote) r_val = car (r_unev);
else if (s == s_or) { r_val = &Cell::Bool_F;
GOTO (ev_or); }
else if (s == s_and) { r_val = &Cell::Bool_T;
GOTO (ev_and); }
else if (s == s_delay) r_val = make_promise (r_env,
r_unev);
else if (s == s_quasiquote) { r_unev = car (r_unev);
GOTO (ev_quasiquote); }
else if (s == s_lambda) r_val = make_procedure
(r_env, cdr (r_unev),
car (r_unev));
else if (s == s_defmacro) { r_proc = make_macro (r_env,
cdr (r_unev),
cdar (r_unev));
bind (r_env, caar (r_unev),
r_proc);
r_val = unspecified; }
else if (s == s_time) GOTO (ev_time);
else if (s == s_withinput) GOTO (ev_withinput);
else if (s == s_withoutput) GOTO (ev_withoutput);
else if (s == s_callwof) GOTO (ev_callwof);
else if (s == s_callwif)
{
r_proc = Cell::cadar (&r_argl);
r_tmp = make_iport (Cell::caar (&r_argl)->StringValue ());
r_tmp = cons (r_tmp, nil);
Cell::setcar (&r_argl, r_tmp);
GOTO (apply_dispatch2);
}
else if (s == s_callcc)
{
r_proc = Cell::caar (&r_argl);
r_tmp = make_continuation ();
r_tmp = cons (r_tmp, nil);
Cell::setcar (&r_argl, r_tmp);
GOTO (apply_dispatch2);
}
else
error ("unimplemented builtin ", s->key);
break;
case Cell::Subr:
r_val = r_proc->SubrValue ()->subr (this,
Cell::car (&r_argl));
break;
case Cell::Lambda:
lambda = r_proc->LambdaValue ();
if (r_proc->flag (Cell::MACRO))
{
save (r_env);
r_env = extend (lambda.envt);
bind_arguments (r_env, lambda.arglist, r_unev);
save (macro_subst); // continuation
}
else
{
r_env = extend (lambda.envt);
bind_arguments (r_env, lambda.arglist,
Cell::car (&r_argl));
}
r_unev = lambda.body;
GOTO (ev_sequence);
case Cell::Cont:
r_val = Cell::caar(&r_argl);
load_continuation (r_proc);
break;
case Cell::Cproc:
if (vm_execute) {
(this->*vm_execute) (r_proc, Cell::car (&r_argl));
} else {
error ("VM not loaded: can't dispatch a compiled procedure");
}
break;
default:
r_proc->dump (stdout);
error ("can't dispatch one of those.");
}
restore (r_cont);
GOTO (r_cont);
case ev_eval:
// The eval special form. (Can't let eval recurse, so
// we take care of it here in the VM).
r_exp = Cell::caar (&r_argl);
save (r_env);
r_env = root_envt;
CALL_EVAL (ev_eval1);
restore (r_env);
RETURN_VALUE (r_val);
case ev_time:
// Call the supplied procedure while timing it.
// Return a cons of the elapsed time and the proc's
// value.
r_proc = Cell::caar(&r_argl);
clear(r_argl);
save(make_real(OS::get_time()));
save(ev_time1); // cont
GOTO(apply_dispatch2);
case ev_time1:
t1 = OS::get_time();
restore(r_tmp);
r_tmp = make_real(t1 - r_tmp->RealValue());
RETURN_VALUE (cons (r_tmp, r_val));
case ev_sequence:
r_exp = car (r_unev);
if (r_exp == nil)
RETURN_VALUE (unspecified);
if (cdr (r_unev) == nil)
{
restore (r_cont);
EVAL_DISPATCH ();
}
CALL_EVAL (ev_sequence_continue);
r_unev = cdr (r_unev);
GOTO (ev_sequence);
// ev_if, etc., is a deviation from the presentation in SICP.
// In brief we are not dispatched by syntax analysis, but by
// finding a Builtin in the functor position. We get here via
// apply dispatch. Our job is to compute `r_val', pop the
// continuation and branch there.
//
// Had we followed SICP strictly, then special forms like
// "if" would have no visible definition at all, and could
// never be redefined. Most Scheme interpretations do allow
// for the redefinition of builtin symbols. Hence we use
// builtins as a sort of "flag" that invokes the internal
// sytnax-directed implementation to proceed.
case ev_if:
r_exp = car (r_unev);
r_unev = cdr (r_unev);
CALL_EVAL (ev_if_decide);
restore (r_cont);
if (r_val->istrue ())
{
r_exp = car (r_unev);
EVAL_DISPATCH ();
}
else
{
if ((r_exp = cdr (r_unev)) != nil)
{
r_exp = car (r_exp);
EVAL_DISPATCH ();
}
else
{
r_val = unspecified;
GOTO (r_cont);
}
}
case ev_define:
r_tmp = car (r_unev);
if (r_tmp->type () == Cell::Symbol)
{
save (r_env);
save (r_unev);
r_exp = cadr (r_unev);
r_cont = ev_define_1;
EVAL_DISPATCH ();
}
else
{
r_proc = make_procedure (r_env, cdr (r_unev), cdr (r_tmp));
bind (r_env, car (r_tmp), r_proc);
RETURN_VALUE (unspecified);
}
case ev_define_1:
restore (r_unev);
restore (r_env);
bind (r_env, car (r_unev), r_val);
RETURN_VALUE (unspecified);
case ev_set:
r_exp = cadr (r_unev);
CALL_EVAL (ev_set_1);
set (r_env, car (r_unev), r_val);
RETURN_VALUE (unspecified);
case ev_or:
if (r_unev == nil || r_val->istrue ())
{
restore (r_cont);
GOTO (r_cont);
}
r_exp = car (r_unev);
CALL_EVAL (ev_or2);
r_unev = cdr (r_unev);
GOTO (ev_or);
case ev_and:
if (r_unev == nil || !r_val->istrue ())
{
restore (r_cont);
GOTO (r_cont);
}
r_exp = car (r_unev);
CALL_EVAL (ev_and2);
r_unev = cdr (r_unev);
GOTO (ev_and);
case macro_subst:
// The macro has been expanded. One more trip through
// eval, please.
restore (r_env);
restore (r_cont);
r_exp = r_val;
EVAL_DISPATCH ();
case ev_let:
// (let [name?] ((v e) ...) x...)
// The plan is to accumulate the list of variables (v) in
// r_varl, and the list of initializers (e) in r_argl.
if (car (r_unev)->type () == Cell::Symbol)
{
r_proc = car (r_unev); // named let: stash in r_proc
r_unev = cdr (r_unev);
}
else
r_proc = nil;
clear (r_argl);
clear (r_varl);
if (car (r_unev) == nil)
{
r_unev = cdr (r_unev); // (let () x...)
r_env = extend (r_env);
goto let_noargs;
}
save (r_proc);
save (cdr (r_unev));
r_unev = car (r_unev); // fall through
case ev_let_init:
save (r_argl);
save (r_varl);
r_exp = cadar (r_unev);
CALL_EVAL (let_accumulate_binding);
restore (r_varl);
restore (r_argl);
l_append (r_varl, caar (r_unev));
l_append (r_argl, r_val);
r_unev = cdr (r_unev);
if (r_unev == nil)
{
restore (r_unev);
restore (r_proc);
let_noargs:
if (r_proc != nil)
{
r_env = extend (r_env);
r_tmp = make_procedure (r_env,
r_unev,
Cell::car (&r_varl));
bind (r_env, r_proc, r_tmp);
}
r_env = extend (r_env);
bind_arguments (r_env,
Cell::car (&r_varl),
Cell::car (&r_argl));
GOTO (ev_sequence);
}
GOTO (ev_let_init);
case ev_letstar:
// (let* ((v e) ...) x1 ...)
// We unpack, bind, and extend in a loop, until we're
// ready for the sequence.
if (car (r_unev) == nil)
{
r_env = extend (r_env);
r_unev = cdr (r_unev);
GOTO (ev_sequence);
}
save (cdr (r_unev));
r_unev = car (r_unev);
/* fall thru */
case ev_letstar_init:
save (r_env);
save (r_unev);
r_exp = cadar (r_unev);
r_cont = ev_letstar_bind;
EVAL_DISPATCH ();
case ev_letstar_bind:
restore (r_unev);
restore (r_env);
r_env = extend (r_env);
bind (r_env, caar (r_unev), r_val);
r_unev = cdr (r_unev);
if (r_unev == nil)
{
restore (r_unev);
GOTO (ev_sequence);
}
GOTO (ev_letstar_init);
case ev_letrec:
// we have: (((v1 i1) (v2 i2)...) x1 x2...)
clear (r_varl);
clear (r_argl);
save (cdr (r_unev));
r_env = extend (r_env);
for (r_exp = car (r_unev); r_exp != nil; r_exp = cdr (r_exp))
{
l_append (r_varl, caar (r_exp));
bind (r_env, caar (r_exp), &Cell::Error);
}
save (r_varl);
r_unev = car (r_unev);
case ev_letrec1:
if (r_unev != nil)
{
r_exp = cadar (r_unev);
save (r_argl);
CALL_EVAL (ev_letrec2);
restore (r_argl);
l_append (r_argl, r_val);
r_unev = cdr (r_unev);
GOTO (ev_letrec1);
}
restore (r_varl);
restore (r_unev);
bind_arguments (r_env, Cell::car (&r_varl), Cell::car (&r_argl));
GOTO (ev_sequence);
case ev_do:
// (do ((var init step)...) (test x...) y...)
// Like let, accumulate variables (v) and
// initializers (i) into r_varl and r_argl.
save (r_unev); // (((var init step)...) (test x...) y...)
r_unev = car (r_unev); // ((var init step)...)
clear (r_argl);
clear (r_varl);
/* fall through */
case ev_do_init:
save (r_argl);
save (r_varl);
save (r_env);
save (r_unev);
r_exp = cadar (r_unev);
r_cont = ev_do_bind;
EVAL_DISPATCH ();
case ev_do_bind:
restore (r_unev);
restore (r_env);
restore (r_varl);
restore (r_argl);
l_append (r_varl, caar (r_unev));
l_append (r_argl, r_val);
r_unev = cdr (r_unev);
if (r_unev == nil)
{
// All done with inits. Create environment and start testing.
r_env = extend (r_env);
bind_arguments (r_env,
Cell::car (&r_varl),
Cell::car (&r_argl));
restore (r_unev); // (((var init step)...) (test x...) y...)
GOTO (ev_do_test);
}
GOTO (ev_do_init);
case ev_do_test:
r_exp = caadr (r_unev);
CALL_EVAL (ev_after_test);
if (r_val->istrue ())
{
// test passed: end iteration. Evaulate
// `x' expressions as a sequence.
r_unev = cdadr (r_unev);
if (r_unev == nil) // no consequent expressions?
RETURN_VALUE (unspecified);
GOTO (ev_sequence);
}
// otherwise, evaluate the y expressions for effect, if there
// are any
if (cddr (r_unev) == nil)
GOTO (ev_do_step_2);
save (r_unev);
save (r_env);
r_unev = cddr (r_unev);
save (ev_do_step);
GOTO (ev_sequence);
case ev_do_step:
// then use the step expressions (if any) to rebind the
// variables, and retest.
restore (r_env);
restore (r_unev); // (((var init step)...) (test x...) y...)
case ev_do_step_2:
save (r_unev);
r_unev = car (r_unev); // ((var init step) ...)
clear (r_argl);
clear (r_varl);
/* fall through */
case ev_step_1:
if (r_unev == nil)
{
// all done.
GOTO (ev_step_finish);
}
r_tmp = cddar (r_unev);
if (r_tmp == nil)
{
r_unev = cdr (r_unev);
GOTO (ev_step_1);
}
save (r_argl);
save (r_varl);
r_exp = caddar (r_unev);
CALL_EVAL (ev_step_bind);
restore (r_varl);
restore (r_argl);
l_append (r_varl, caar (r_unev));
l_append (r_argl, r_val);
r_unev = cdr (r_unev);
GOTO (ev_step_1);
case ev_step_finish:
bind_arguments (r_env, Cell::car (&r_varl), Cell::car (&r_argl));
restore (r_unev);
GOTO (ev_do_test);
case ev_cond:
if (r_unev == nil)
RETURN_VALUE (unspecified);
r_exp = caar (r_unev); // t1
if (r_exp->is_symbol (s_else))
r_val = &Cell::Bool_T;
else
{
r_cont = ev_cond_test;
CALL_EVAL (ev_cond_test);
}
if (r_val->istrue ())
{
r_unev = cdar (r_unev);
r_tmp = car (r_unev);
// Check for "=> r_proc" syntax
if (r_tmp->is_symbol (s_passto))
{
// We already have the argument. Now, evaluate
// r_proc, so we can apply it.
save (r_val);
r_unev = cdr (r_unev);
r_exp = car (r_unev);
CALL_EVAL (ev_cond_passto);
r_proc = r_val;
restore (r_val);
Cell::setcar (&r_argl, cons (r_val, nil));
GOTO (apply_dispatch2);
}
GOTO (ev_sequence);
}
r_unev = cdr (r_unev);
GOTO (ev_cond);
case ev_apply:
// we have, e.g., (apply + 1 2 '(3 4))
// and we want (+ 1 2 3 4).
r_proc = Cell::caar(&r_argl); // peel off r_proc
r_tmp = Cell::cdar(&r_argl);
clear (r_argl);
for (; r_tmp != nil; r_tmp = cdr (r_tmp))
if (cdr (r_tmp) == nil) // fold the list
l_appendtail (r_argl, car (r_tmp));
else
l_append (r_argl, car (r_tmp));
GOTO (apply_dispatch2);
case ev_quasiquote:
// If it's a vector, convert it to a list and
// save a flag.
++r_qq;
t = r_unev->type ();
if (t == Cell::Vec)
{
save (1);
r_unev = vector_to_list (this, cons (r_unev, nil)); // yyy
}
else
save (0);
save (ev_qq_finish);
r_val = nil;
case ev_qq0:
t = r_unev->type ();
if (t == Cell::Cons)
{
r_exp = car (r_unev);
if (r_exp->type () == Cell::Symbol)
{
p = r_exp->SymbolValue ();
if (p == s_unquote) // unquote: evaluate sequel.
{
if (r_qq == 1)
{
r_exp = cadr (r_unev);
--r_qq;
CALL_EVAL (ev_unquote);
++r_qq;
}
else
{
r_tmp = make_symbol (s_unquote);
save (r_tmp);
GOTO (ev_qq_decrease);
}
}
else if (p == s_quasiquote) // increase QQ level.
{
r_unev = cdr (r_unev);
save (ev_qq1);
GOTO (ev_quasiquote);
case ev_qq1:
r_tmp = make_symbol (s_quasiquote);
r_val = cons (r_tmp, r_val);
}
else
goto QQCONS;
}
else if (r_exp->type () == Cell::Cons &&
car (r_exp)->is_symbol (s_unquote_splicing))
{
if (r_qq == 1)
{
// unquote_splicing: generate list, and splice it
// in. First evaluate to get the list.
r_exp = cadr (r_exp);
r_unev = cdr (r_unev);
CALL_EVAL (ev_unq_spl);
// r_val holds the list. Install it into r_argl
// (tracking head and tail). Then evaluate
// what follows.
r_tmp = r_val;
while (cdr (r_tmp) != nil)
r_tmp = cdr (r_tmp);
Cell::setcar(&r_argl, r_val);
Cell::setcdr(&r_argl, r_tmp);
save (r_argl);
r_exp = r_unev;
save (ev_unq_spl2);
GOTO (ev_qq0);
}
else
{
r_tmp = make_symbol (s_unquote_splicing);
save (r_tmp);
GOTO (ev_qq_decrease);
}
case ev_unq_spl2:
restore (r_argl);
l_appendtail (r_argl, r_val);
r_val = Cell::car(&r_argl);
}
else if (r_unev == nil)
r_val = nil;
else
{
QQCONS: // "move quasiquotation inward"
save (cdr (r_unev)); // cons (qq (car), qq (cdr))
save (ev_qq2); // new continuation
r_unev = r_exp;
GOTO (ev_qq0);
case ev_qq2:
restore (r_unev);
save (r_val);
save (ev_qq3);
GOTO (ev_qq0);
case ev_qq3:
restore (r_exp);
r_val = cons (r_exp, r_val);
}
}
else
r_val = r_unev; // atoms are self-evaluating
restore (r_cont);
GOTO (r_cont);
case ev_qq_finish: // finished. reconvert to
restore (flag); // vector form if necessary.
if (flag)
r_val = vector_from_list (this, r_val);
--r_qq;
restore (r_cont);
GOTO (r_cont);
case ev_qq_decrease:
// we get here because we saw unquote or unquote_splicing,
// and we want to proceed with a decreased qq level instead
// of evaluating it (because the qq level was too high
// when we encountered the form).
--r_qq;
r_unev = cdr (r_unev);
save (ev_qqd_1);
GOTO (ev_qq0);
case ev_qqd_1:
restore (r_exp); // recover head symbol
++r_qq;
RETURN_VALUE (cons (r_exp, r_val));
case ev_case:
// (key ((d1 d2...) x1 x2...) ((d3 d4...) x3 x4...))
// evaluate key, and shift it away.
r_exp = car (r_unev);
r_unev = cdr (r_unev);
CALL_EVAL (ev_case2);
for (; r_unev != nil; r_unev = cdr (r_unev))
{
r_exp = car (r_unev);
r_tmp = car (r_exp);
if (r_tmp->is_symbol (s_else))
{
r_unev = cdr (r_exp);
GOTO (ev_sequence);
}
for (; r_tmp != nil; r_tmp = cdr (r_tmp))
{
if (r_val->eq (car (r_tmp)))
{
r_unev = cdr (r_exp);
GOTO (ev_sequence);
}
}
}
RETURN_VALUE (unspecified);
case ev_foreach:
r_proc = Cell::caar(&r_argl); // (r_proc list...)
r_unev = Cell::cdar(&r_argl);
case ev_foreach1:
if (car (r_unev) == nil)
RETURN_VALUE (unspecified);
clear (r_argl);
for (r_tmp = r_unev; r_tmp != nil; r_tmp = cdr (r_tmp))
{
l_append (r_argl, caar (r_tmp));
Cell::setcar (r_tmp, cdar (r_tmp));
}
save (r_unev);
save (r_proc);
save (ev_foreach2);
GOTO (apply_dispatch2);
case ev_foreach2:
restore (r_proc);
restore (r_unev);
GOTO (ev_foreach1);
case ev_map:
r_proc = Cell::caar (&r_argl);
// copy r_argl to r_unev (less the first elt., which was the r_proc)
r_unev = Cell::cdar (&r_argl);
clear (r_varl);
case ev_map1:
if (car (r_unev) == nil) // no more arguments
GOTO (ev_map3);
clear (r_argl);
for (r_tmp = r_unev; r_tmp != nil; r_tmp = cdr (r_tmp))
{
l_append (r_argl, caar (r_tmp));
Cell::setcar (r_tmp, cdar (r_tmp));
}
save (r_varl);
save (r_unev);
save (r_proc);
save (ev_map2);
GOTO (apply_dispatch2);
case ev_map2:
restore (r_proc);
restore (r_unev);
restore (r_varl);
l_append (r_varl, r_val);
GOTO (ev_map1);
case ev_map3:
RETURN_VALUE (Cell::car (&r_varl));
case ev_force:
r_exp = Cell::caar (&r_argl);
if (r_exp->flag (Cell::FORCED))
r_val = r_exp->cd.cv->get (0); // return memoized value
else
{
// If we haven't forced the promise yet, then the cdr
// is pointing to a unit vector containing the
// procedure we must evaluate to get the value, which
// we then memoize.
clear (r_argl);
r_proc = r_exp->cd.cv->get (0);
save (r_exp);
save (ev_force2);
GOTO (apply_dispatch2);
case ev_force2:
// Now, it can happen that the procedure we're
// invoking can force its own value. If we find that
// the FORCED flag has magically become set as a
// result of forcing, then we must accept that earlier
// computation (we are "higher" on the evaluation
// stack)...
restore (r_exp);
if (r_exp->flag (Cell::FORCED))
r_val = r_exp->cd.cv->get (0);
else
{
r_exp->cd.cv->set (0, r_val);
r_exp->flag (Cell::FORCED, true);
}
}
restore (r_cont);
GOTO (r_cont);
case ev_withinput:
// (filename proc)
with_input (Cell::caar (&r_argl)->StringValue ());
r_proc = Cell::cadar (&r_argl);
clear (r_argl);
save (ev_withinput2); // continuation
GOTO (apply_dispatch2);
case ev_withinput2:
without_input ();
restore (r_cont);
GOTO (r_cont);
case ev_withoutput:
with_output (Cell::caar (&r_argl)->StringValue ());
r_proc = Cell::cadar (&r_argl);
clear (r_argl);
save (ev_withoutput2); // continuation
GOTO (apply_dispatch2);
case ev_withoutput2:
without_output ();
restore (r_cont);
GOTO (r_cont);
case ev_load:
r_unev = make_iport (Cell::caar (&r_argl)->StringValue ());
save (r_unev); // let r_unev hold input stream
save (r_env);
case ev_load2:
restore (r_env);
restore (r_unev);
r_exp = read (r_unev->IportValue ());
if (r_exp)
{
save (r_unev);
save (r_env);
r_env = root_envt; // read files into global scope
r_cont = ev_load2; // loop
EVAL_DISPATCH ();
}
r_exp = nil;
RETURN_VALUE (r_val);
case ev_callwof:
r_proc = Cell::cadar (&r_argl);
r_unev = make_oport (Cell::caar (&r_argl)->StringValue ());
Cell::setcar (&r_argl, cons (r_unev, nil));
save (r_unev);
save (ev_callwof2); // cont
GOTO (apply_dispatch2);
case ev_callwof2:
restore (r_unev);
fflush (r_unev->OportValue ());
RETURN_VALUE (r_val);
default:
printf ("IC = %x\n", state);
error ("internal: invalid continuation");
}
return unimplemented;
}
void Context::print_vm_state ()
{
printf ("%d %s exp=", m_stack.size (), state_name [state]);
r_exp->write (stdout);
printf (" unev=");
r_unev->write (stdout);
printf (" proc=");
r_proc->write (stdout);
printf (" val=");
r_val->write (stdout);
printf (" argl=");
Cell::car (&r_argl)->write (stdout);
printf (" varl=");
Cell::car (&r_varl)->write (stdout);
printf (" env=");
if (r_env == root_envt)
printf ("#<root>");
else
Cell::car (r_env)->write (stdout);
printf (" cont=%s q%d\n", state_name [r_cont], r_qq);
}
void Context::bind_arguments (Cell * env, Cell * variables, Cell * values)
{
Cell * var;
Cell * val;
if (variables->type () == Cell::Cons)
{
for (var = variables, val = values;
var != nil;
var = cdr (var), val = cdr (val))
{
bind (env, car (var), car (val));
if (cdr (var)->type () == Cell::Symbol)
{
// Implement "dotted tail" procedure call. If
// the cdr of var is another symbol, then this
// was to the right of the "dot"; put all the rest
// of the arguments in there. [SICP 2ed. p. 104, 183n;
// R5RS 4.1.4]
bind (env, cdr (var), cdr (val));
break;
}
}
}
else
{
// "varargs" version: (lambda args body...)
bind (env, variables, values);
}
}
Cell * Context::get (Cell * env, Cell * c)
{
c->typecheck (Cell::Symbol);
Cell * pResult = find (env, c);
if (! pResult || ! CDR (pResult))
error ("unbound variable ", c->SymbolValue ()->key);
Cell * res = CDR (pResult);
if (res->type () == Cell::Magic)
return res->cd.m->get_f (this, res->cd.vp);
else
return res;
}
void Context::set (Cell * env, Cell * var, Cell * value)
{
Cell * target = find (env, var);
Cell * d;
psymbol s = var->SymbolValue ();
if (! target)
error ("unbound variable ", s->key);
if ((d = cdr (target)) && d->type () == Cell::Magic)
d->cd.m->set_f (this, d->cd.vp, value);
else
Cell::setcdr (target, value);
}
Cell * Context::find (Cell * env, Cell * c)
{
int e_count = 0;
int b_count = 0;
psymbol s = c->SymbolValue ();
Cell * e = env;
Cell * val;
if (c->flag (Cell::QUICK))
{
int e_skip = c->e_skip ();
int b_skip = c->b_skip ();
if (e_skip == Cell::GLOBAL_ENV)
{
// Target environment is root envt.
e = root_envt;
e_count = e_skip;
}
else
{
// Skip the indicated number of environments.
for (e_count = 0; (e != nil) && (e_count < e_skip); ++e_count)
e = CDR (e);
}
cellvector * v = CAR (e)->VectorValue ();
if ( b_skip < 0
|| b_skip >= v->size ()
|| e_skip != e_count
)
{
printf ("b=%d, e=%d, ec=%d, vs=%d\n", b_skip, e_skip, e_count,
v->size ());
error ("internal error: invalid lexical address: ", s->key);
}
// Go directly to the binding.
return v->get (b_skip);
}
// Consider each environment in the enclosure chain
// in turn, counting them as we go.
for (e_count = 0; e != nil; ++e_count, e = CDR (e))
{
cellvector * v = CAR (e)->VectorValue ();
// Check the current environment.
for (b_count = 0; b_count < v->size (); ++b_count)
if (CAR (v->get (b_count))->SymbolValue () == s)
{
if (e == root_envt)
{
// Top-level environment. Due to nested defines,
// this turns out to be a special case.
e_count = -1;
}
quicken (c, e_count, b_count);
return v->get (b_count);
}
}
// Can the OS magically supply a value??
if ((val = OS::undef (this, s->truename)))
{
// Yes! The OS has produced a value. We cache
// it in the outermost environment, as if it
// had been established there with (define).
Cell * os_binding = cons (c, val);
car (root_envt)->VectorValue ()->push (os_binding);
return os_binding;
}
// Failure.
return 0;
}
void Context::quicken (Cell * c, int e_count, int b_count)
{
// For global symbols, we have 16 bits of b_skip to work with;
// only 8 if the symbol is not in the global environment.
// XXX: these are magic numbers and should be coordinated with
// the .h file.
if (e_count >= 0)
{
if (e_count > 254 || b_count > 254)
return;
}
else if (b_count > 65534)
return;
c->set_lexaddr (e_count, b_count);
}
Cell * Context::make_procedure (Cell * e, Cell * body, Cell * arglist)
{
Cell * c = alloc (Cell::Lambda);
// XXX cellvector * cv = new cellvector (3);
cellvector* cv = cellvector::alloc(3);
c->cd.cv = cv;
c->flag (Cell::VREF, true);
cv->set (0, e);
cv->set (1, body);
cv->set (2, arglist);
return c;
}
Cell * Context::make_macro (Cell * e, Cell * body, Cell * arglist)
{
Cell * c = make_procedure (e, body, arglist);
c->flag (Cell::MACRO, true);
return c;
}
Cell * Context::make_promise (Cell * e, Cell * body)
{
Cell * c = alloc (Cell::Promise);
cellvector *cv = cellvector::alloc(1);
// Now it may seem odd to allocate a vector of one element to
// store the content of the promise. But, our garbage collector
// only knows how to traverse two kinds of entities: (1) conses,
// consisting of a car and cdr and (2) vectors. Since we're not a
// cons, but contain a reference to either the procedure that will
// compute the promise or that procedure's memoized value, we must
// store that thing in a unit vector.
c->cd.cv = cv;
c->flag (Cell::VREF, true);
gc_protect (c);
cv->set (0, make_procedure (e, body, nil));
gc_unprotect ();
return c;
}
Cell * Context::make_list1 (Cell * e1)
{
return cons (e1, nil);
}
Cell * Context::make_list2 (Cell * e1, Cell * e2)
{
return cons (e1, make_list1 (e2));
}
Cell * Context::make_list3 (Cell * e1, Cell * e2, Cell * e3)
{
return cons (e1, make_list2 (e2, e3));
}
Cell * Context::make_continuation ()
{
Cell * c = alloc (Cell::Cont);
// Allocate a cellvector to hold the continutation (saved
// machine stack).
int msize = m_stack.size ();
cellvector* cv = cellvector::alloc(msize);
c->flag (Cell::VREF, true);
c->cd.cv = cv;
for (int ix = 0; ix < msize; ++ix)
cv->set (ix, m_stack [ix]);
return c;
}
void Context::load_continuation (Cell * cont)
{
cont->typecheck (Cell::Cont);
cellvector * cv = cont->cd.cv;
int msize = cv->size ();
m_stack.clear();
for (int ix = 0; ix < msize; ++ix)
save (cv->get (ix));
}
class InterpreterExt : SchemeExtension
{
public:
InterpreterExt () {
Register (this);
}
virtual void Install (Context * ctx, Cell * envt) {
// Hook in the function pointer to the interpreter's evaluation loop.
ctx->interp_eval = &Context::interp_evaluator;
// Builtin Procedures (treated directly by `eval')
static struct
{
const char * name; // name of procedure or form.
bool macro; // macro arguments are left unevaluated.
} builtin [] = {
{ "and", true },
{ "apply", false },
{ "begin", true },
{ "call-with-current-continuation", false },
{ "call-with-input-file", false },
{ "call-with-output-file", false },
{ "case", true },
{ "cond", true },
{ "define", true },
{ "defmacro", true },
{ "delay", true },
{ "do", true },
{ "eval", false },
{ "for-each", false },
{ "force", false },
{ "if", true },
{ "lambda", true },
{ "let", true },
{ "let*", true },
{ "letrec", true },
{ "load", false },
{ "map", false },
{ "or", true },
{ "quasiquote", true },
{ "quote", true },
{ "set!", true },
{ "time", false },
{ "with-input-from-file", false },
{ "with-output-to-file", false },
};
for (unsigned int ix = 0; ix < sizeof (builtin) / sizeof (*builtin); ++ix)
{
psymbol ps = intern (builtin [ix].name);
Cell * b = ctx->make_builtin (ps);
ctx->set_var (envt, ps, b);
if (builtin [ix].macro)
b->flag (Cell::MACRO, true);
}
}
};
static InterpreterExt interpreter_ext;