1628 lines
40 KiB
C++
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;
|
|
intptr_t 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_i (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_i (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_i (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_i (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_i(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_i(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_i(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_i(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_i (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_i (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_i (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_i (1);
|
|
r_unev = vector_to_list (this, cons (r_unev, nil)); // yyy
|
|
}
|
|
else
|
|
save_i (0);
|
|
|
|
save_i (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_i (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_i (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_i (ev_qq2); // new continuation
|
|
r_unev = r_exp;
|
|
GOTO (ev_qq0);
|
|
case ev_qq2:
|
|
restore (r_unev);
|
|
save (r_val);
|
|
save_i (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_i(r_cont);
|
|
GOTO (r_cont);
|
|
|
|
case ev_qq_finish: // finished. reconvert to
|
|
restore_i(flag); // vector form if necessary.
|
|
if (flag)
|
|
r_val = vector_from_list (this, r_val);
|
|
--r_qq;
|
|
restore_i(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_i (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_i(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_i(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_i(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_i(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_i(ev_withinput2); // continuation
|
|
GOTO (apply_dispatch2);
|
|
|
|
case ev_withinput2:
|
|
without_input ();
|
|
restore_i(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_i(ev_withoutput2); // continuation
|
|
GOTO (apply_dispatch2);
|
|
|
|
case ev_withoutput2:
|
|
without_output ();
|
|
restore_i(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_i(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;
|