556 lines
14 KiB
C
556 lines
14 KiB
C
#include <inttypes.h>
|
||
#include <stdbool.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
|
||
#include "builtin.h"
|
||
#include "gc.h"
|
||
|
||
/* Ex: _SLOT_VALUE(STRUCTURE, v, NAME) */
|
||
#define _SLOT_VALUE(t,v,s) (_get_struct(v)->slots[t ## _SLOT_ ## s])
|
||
#define _LAMBDA_SLOT(v,s) _SLOT_VALUE(LAMBDA, v, s)
|
||
|
||
/* Shorthand for frequently-used fields */
|
||
#define ST1 (state->in1.value)
|
||
#define ST2 (state->in2.value)
|
||
#define ST3 (state->in3.value)
|
||
|
||
typedef struct interp_state
|
||
{
|
||
gc_root_t lambda;
|
||
gc_root_t frame;
|
||
gc_root_t argv;
|
||
gc_root_t k;
|
||
gc_root_t ctx;
|
||
gc_root_t in1;
|
||
gc_root_t in2;
|
||
gc_root_t in3;
|
||
} interp_state_t;
|
||
|
||
/* Quick references to main builtins */
|
||
static gc_root_t structure_type_root;
|
||
static gc_root_t template_type_root;
|
||
static gc_root_t lambda_type_root;
|
||
|
||
/*
|
||
* Local helper routines
|
||
*/
|
||
|
||
static bool struct_is_a(value_t s, value_t type);
|
||
|
||
static value_t vector_ref(value_t v, fixnum_t idx);
|
||
static char byte_string_ref(value_t v, fixnum_t idx);
|
||
static value_t struct_ref(value_t v, fixnum_t idx);
|
||
|
||
static void vector_set(value_t v, fixnum_t idx, value_t newval);
|
||
static void byte_string_set(value_t v, fixnum_t idx, char newval);
|
||
static void struct_set(value_t v, fixnum_t idx, value_t newval);
|
||
|
||
static value_t make_lambda(interp_state_t *state, value_t templ);
|
||
|
||
static void translate_callable(interp_state_t *state);
|
||
static void run_byte_code(interp_state_t *state);
|
||
static void perform_tail_call(interp_state_t *state);
|
||
|
||
static value_t eval_expression(interp_state_t *state, uint8_t code, uint8_t in1, uint8_t in2);
|
||
static value_t eval_unary_expression(interp_state_t *state, uint8_t subcode, uint8_t in);
|
||
|
||
static void run_statement(interp_state_t *state, uint8_t code, uint8_t in1, uint8_t in2, uint32_t in3);
|
||
|
||
static value_t get_input(const interp_state_t *state, fixnum_t var);
|
||
static void set_output(const interp_state_t *state, fixnum_t var, value_t val);
|
||
|
||
static void register_state(interp_state_t *state, value_t lambda, value_t argv);
|
||
static void unregister_state(interp_state_t *state);
|
||
|
||
/**********************************************************/
|
||
|
||
void interpreter_init(void)
|
||
{
|
||
register_gc_root(&structure_type_root, lookup_builtin(BI_STRUCTURE));
|
||
register_gc_root(&template_type_root, lookup_builtin(BI_TEMPLATE));
|
||
register_gc_root(&lambda_type_root, lookup_builtin(BI_LAMBDA));
|
||
}
|
||
|
||
value_t run_interpreter(value_t lambda, value_t argv)
|
||
{
|
||
static bool run_finalizers = true;
|
||
interp_state_t state;
|
||
|
||
register_state(&state, lambda, argv);
|
||
|
||
/* Keep going until something attempt to tail-call NIL, the original 'k', indicating completion. */
|
||
while (!is_nil(state.lambda.value))
|
||
{
|
||
/* 'lambda' may be a callable structure; if so, follow the 'callable' proxies and update argv. */
|
||
translate_callable(&state);
|
||
|
||
/*
|
||
* Now 'lambda' really is a lambda structure instance.
|
||
*/
|
||
|
||
/* Allocate frame variables */
|
||
state.frame.value = make_vector(get_fixnum(_LAMBDA_SLOT(state.lambda.value, FRAME_VARS)), NIL);
|
||
|
||
run_byte_code(&state);
|
||
perform_tail_call(&state);
|
||
|
||
if (run_finalizers)
|
||
{
|
||
value_t v, f;
|
||
get_next_finalizer(&v, &f);
|
||
|
||
if (is_object(v))
|
||
{
|
||
gc_root_t f_root;
|
||
|
||
register_gc_root(&f_root, f);
|
||
run_finalizers = false;
|
||
|
||
/* Note that recursion is limited to a single level by the static variable. */
|
||
run_interpreter(f_root.value, cons(v, NIL));
|
||
|
||
run_finalizers = true;
|
||
unregister_gc_root(&f_root);
|
||
}
|
||
}
|
||
}
|
||
|
||
unregister_state(&state);
|
||
|
||
/* The arguments passed to NIL continuation are the final return value. */
|
||
return state.argv.value;
|
||
}
|
||
|
||
/* TODO: Permit derivatives of 'structure'. */
|
||
static bool struct_is_a(value_t s, value_t type)
|
||
{
|
||
/* Detect unbounded loops w/ cyclic 'parent' links. */
|
||
int ttl = 256;
|
||
|
||
if (!is_struct(s))
|
||
return false;
|
||
|
||
for (value_t t = _get_struct(s)->type; t != type; t = _SLOT_VALUE(STRUCTURE, t, SUPER), --ttl)
|
||
{
|
||
if (is_nil(t))
|
||
return false;
|
||
|
||
if (get_struct(t)->type != structure_type_root.value)
|
||
abort();
|
||
|
||
if (ttl <= 0)
|
||
abort();
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
static value_t vector_ref(value_t v, fixnum_t idx)
|
||
{
|
||
vector_t *vec = get_vector(v);
|
||
|
||
if (idx < 0 || idx >= vec->size)
|
||
abort();
|
||
|
||
return vec->elements[idx];
|
||
}
|
||
|
||
static char byte_string_ref(value_t v, fixnum_t idx)
|
||
{
|
||
byte_string_t *str = get_byte_string(v);
|
||
|
||
if (idx < 0 || idx >= str->size)
|
||
abort();
|
||
|
||
return str->bytes[idx];
|
||
}
|
||
|
||
static value_t struct_ref(value_t v, fixnum_t idx)
|
||
{
|
||
struct_t *s = get_struct(v);
|
||
|
||
if (idx < 0 || idx >= s->nslots)
|
||
abort();
|
||
|
||
return s->slots[idx];
|
||
}
|
||
|
||
static void vector_set(value_t v, fixnum_t idx, value_t newval)
|
||
{
|
||
vector_t *vec = get_vector(v);
|
||
|
||
if (idx < 0 || idx >= vec->size)
|
||
abort();
|
||
|
||
vec->elements[idx] = newval;
|
||
}
|
||
|
||
static void byte_string_set(value_t v, fixnum_t idx, char newval)
|
||
{
|
||
byte_string_t *str = get_byte_string(v);
|
||
|
||
if (idx < 0 || idx >= str->size)
|
||
abort();
|
||
|
||
str->bytes[idx] = newval;
|
||
}
|
||
|
||
static void struct_set(value_t v, fixnum_t idx, value_t newval)
|
||
{
|
||
struct_t *s = get_struct(v);
|
||
|
||
if (idx < 0 || idx >= s->nslots)
|
||
abort();
|
||
|
||
s->slots[idx] = newval;
|
||
}
|
||
|
||
static value_t make_lambda(interp_state_t *state, value_t templ)
|
||
{
|
||
gc_root_t templ_root, lambda_root;
|
||
value_t lval;
|
||
struct_t *ls;
|
||
struct_t *ts;
|
||
vector_t *l_inst;
|
||
byte_string_t *t_inst;
|
||
|
||
register_gc_root(&templ_root, templ);
|
||
register_gc_root(&lambda_root, make_struct(lambda_type_root.value, LAMBDA_SLOTS));
|
||
|
||
/* Need to do this first, since it can call the garbage collector. */
|
||
_get_struct(lambda_root.value)->slots[LAMBDA_SLOT_INSTANCE_VARS] =
|
||
make_vector(get_vector(get_struct(templ_root.value)
|
||
->slots[TEMPLATE_SLOT_INSTANCE_VARS])
|
||
->size,
|
||
NIL);
|
||
|
||
ls = _get_struct(lambda_root.value);
|
||
ts = _get_struct(templ_root.value);
|
||
l_inst = _get_vector(ls->slots[LAMBDA_SLOT_INSTANCE_VARS]);
|
||
t_inst = get_byte_string(ts->slots[TEMPLATE_SLOT_INSTANCE_VARS]);
|
||
|
||
/* All but the instance variables are just shallow-copied. */
|
||
ls->slots[LAMBDA_SLOT_GLOBAL_VARS] = ts->slots[TEMPLATE_SLOT_GLOBAL_VARS];
|
||
ls->slots[LAMBDA_SLOT_FRAME_VARS] = ts->slots[TEMPLATE_SLOT_FRAME_VARS];
|
||
ls->slots[LAMBDA_SLOT_BYTE_CODE] = ts->slots[TEMPLATE_SLOT_BYTE_CODE];
|
||
ls->slots[LAMBDA_SLOT_TAIL_CALL] = ts->slots[TEMPLATE_SLOT_TAIL_CALL];
|
||
ls->slots[LAMBDA_SLOT_ARG_LIST] = ts->slots[TEMPLATE_SLOT_ARG_LIST];
|
||
ls->slots[LAMBDA_SLOT_CONTINUATION] = ts->slots[TEMPLATE_SLOT_CONTINUATION];
|
||
ls->slots[LAMBDA_SLOT_CONTEXT] = ts->slots[TEMPLATE_SLOT_CONTEXT];
|
||
|
||
for (size_t i = 0; i < t_inst->size; ++i)
|
||
{
|
||
l_inst->elements[i] = get_input(state, t_inst->bytes[i]);
|
||
}
|
||
|
||
unregister_gc_root(&templ_root);
|
||
unregister_gc_root(&lambda_root);
|
||
|
||
return lambda_root.value;
|
||
}
|
||
|
||
static void translate_callable(interp_state_t *state)
|
||
{
|
||
while (!struct_is_a(state->lambda.value, lambda_type_root.value))
|
||
{
|
||
if (!struct_is_a(get_struct(state->lambda.value)->type, structure_type_root.value))
|
||
abort();
|
||
|
||
/* Prepend structure instance to argument list, per proxy protocol. */
|
||
state->argv.value = cons(state->lambda.value, state->argv.value);
|
||
|
||
/* Follow link to next callable. */
|
||
state->lambda.value = _SLOT_VALUE(STRUCTURE, _get_struct(state->lambda.value)->type, CALLABLE);
|
||
}
|
||
}
|
||
|
||
static void run_byte_code(interp_state_t *state)
|
||
{
|
||
gc_root_t bc_root;
|
||
|
||
register_gc_root(&bc_root, _LAMBDA_SLOT(state->lambda.value, BYTE_CODE));
|
||
|
||
for (size_t offset = 0; (offset+3) < _get_byte_string(bc_root.value)->size; offset += 4)
|
||
{
|
||
uint8_t bytes[4];
|
||
|
||
memcpy(bytes, _get_byte_string(bc_root.value)->bytes + offset, 4);
|
||
|
||
switch (bytes[0])
|
||
{
|
||
case 0 ... 63: /* expression */
|
||
set_output(state, bytes[1], eval_expression(state, bytes[0], bytes[2], bytes[3]));
|
||
break;
|
||
case 64 ... 127: /* statement */
|
||
run_statement(state, bytes[0], bytes[1], bytes[2], bytes[3]);
|
||
break;
|
||
case 128 ... 255: /* conditional */
|
||
set_output(state, bytes[0],
|
||
get_input(state,
|
||
is_true(get_input(state, bytes[1])) ? bytes[2] : bytes[3]));
|
||
break;
|
||
}
|
||
}
|
||
|
||
unregister_gc_root(&bc_root);
|
||
}
|
||
|
||
static void perform_tail_call(interp_state_t *state)
|
||
{
|
||
value_t new_lambda, new_argv, new_ctx, new_k;
|
||
|
||
new_lambda = get_input(state, get_fixnum(_LAMBDA_SLOT(state->lambda.value, TAIL_CALL)));
|
||
new_argv = get_input(state, get_fixnum(_LAMBDA_SLOT(state->lambda.value, ARG_LIST)));
|
||
new_k = get_input(state, get_fixnum(_LAMBDA_SLOT(state->lambda.value, CONTINUATION)));
|
||
new_ctx = get_input(state, get_fixnum(_LAMBDA_SLOT(state->lambda.value, CONTEXT)));
|
||
|
||
state->lambda.value = new_lambda;
|
||
state->argv.value = new_argv;
|
||
state->k.value = new_k;
|
||
state->ctx.value = new_ctx;
|
||
}
|
||
|
||
static value_t eval_expression(interp_state_t *state, uint8_t code, uint8_t in1, uint8_t in2)
|
||
{
|
||
if (code == 0)
|
||
{
|
||
return eval_unary_expression(state, in1, in2);
|
||
}
|
||
|
||
ST1 = get_input(state, in1);
|
||
ST2 = get_input(state, in2);
|
||
|
||
switch (code)
|
||
{
|
||
case 0x01:
|
||
return cons(ST1, ST2);
|
||
case 0x02:
|
||
return make_vector(get_fixnum(ST1), ST2);
|
||
case 0x03:
|
||
return make_byte_string(get_fixnum(ST1), (char)get_fixnum(ST2));
|
||
case 0x04:
|
||
return vector_ref(ST1, get_fixnum(ST2));
|
||
case 0x05:
|
||
return make_fixnum(byte_string_ref(ST1, get_fixnum(ST2)));
|
||
case 0x06:
|
||
return struct_ref(ST1, get_fixnum(ST2));
|
||
case 0x07:
|
||
return ST1 == ST2;
|
||
case 0x08:
|
||
return make_fixnum(get_fixnum(ST1) + get_fixnum(ST2));
|
||
case 0x09:
|
||
return make_fixnum(get_fixnum(ST1) - get_fixnum(ST2));
|
||
case 0x0a:
|
||
return make_fixnum(get_fixnum(ST1) * get_fixnum(ST2));
|
||
case 0x0b:
|
||
return make_fixnum(get_fixnum(ST1) / get_fixnum(ST2));
|
||
case 0x0c:
|
||
return make_fixnum(get_fixnum(ST1) % get_fixnum(ST2));
|
||
case 0x0d:
|
||
return make_fixnum(get_fixnum(ST1) < get_fixnum(ST2));
|
||
case 0x0e:
|
||
return make_fixnum(get_fixnum(ST1) >= get_fixnum(ST2));
|
||
case 0x0f:
|
||
return make_fixnum(get_fixnum(ST1) & get_fixnum(ST2));
|
||
case 0x10:
|
||
return make_fixnum(get_fixnum(ST1) | get_fixnum(ST2));
|
||
case 0x11:
|
||
return make_fixnum(get_fixnum(ST1) ^ get_fixnum(ST2));
|
||
case 0x12:
|
||
return make_fixnum(get_fixnum(ST1) << get_fixnum(ST2));
|
||
case 0x13:
|
||
return make_fixnum(get_fixnum(ST1) >> get_fixnum(ST2));
|
||
case 0x14:
|
||
return make_fixnum((unsigned long)get_fixnum(ST1) >> get_fixnum(ST2));
|
||
case 0x15 ... 0x24:
|
||
return UNDEFINED;
|
||
default:
|
||
abort();
|
||
}
|
||
|
||
return UNDEFINED;
|
||
}
|
||
|
||
static value_t eval_unary_expression(interp_state_t *state, uint8_t subcode, uint8_t in)
|
||
{
|
||
if (subcode == 0)
|
||
{
|
||
abort();
|
||
}
|
||
|
||
ST1 = get_input(state, in);
|
||
|
||
switch (subcode)
|
||
{
|
||
case 0x01:
|
||
return ST1;
|
||
case 0x02:
|
||
return get_pair(ST1)->car;
|
||
case 0x03:
|
||
return get_pair(ST1)->cdr;
|
||
case 0x04:
|
||
return get_box(ST1)->value;
|
||
case 0x05:
|
||
return make_boolean(is_false(ST1));
|
||
case 0x06:
|
||
return make_boolean(is_nil(ST1));
|
||
case 0x07:
|
||
return make_boolean(is_pair(ST1));
|
||
case 0x08:
|
||
return make_boolean(is_box(ST1));
|
||
case 0x09:
|
||
return make_boolean(is_vector(ST1));
|
||
case 0x0a:
|
||
return make_boolean(is_byte_string(ST1));
|
||
case 0x0b:
|
||
return make_boolean(is_struct(ST1));
|
||
case 0x0c:
|
||
return make_boolean(is_fixnum(ST1));
|
||
case 0x0d:
|
||
//return make_boolean(is_float(ST1));
|
||
return FALSE_VALUE;
|
||
case 0x0e:
|
||
return make_box(ST1);
|
||
case 0x0f:
|
||
if (!struct_is_a(ST1, structure_type_root.value))
|
||
abort();
|
||
return make_struct(ST1, get_vector(_get_struct(ST1)->slots[STRUCTURE_SLOT_SLOTS])->size);
|
||
case 0x10:
|
||
//return make_float((float_t)get_fixnum(ST1));
|
||
return UNDEFINED;
|
||
case 0x11:
|
||
return make_lambda(state, ST1);
|
||
case 0x12:
|
||
return make_fixnum(~get_fixnum(ST1));
|
||
case 0x13:
|
||
return make_fixnum(-get_fixnum(ST1));
|
||
case 0x14:
|
||
//return make_float(-from_float(ST1));
|
||
return UNDEFINED;
|
||
case 0x20 ... 0x31:
|
||
case 0x40 ... 0x4e:
|
||
return UNDEFINED;
|
||
default:
|
||
abort();
|
||
}
|
||
}
|
||
|
||
static void run_statement(interp_state_t *state, uint8_t code, uint8_t in1, uint8_t in2, uint32_t in3)
|
||
{
|
||
ST1 = get_input(state, in1);
|
||
ST2 = get_input(state, in2);
|
||
|
||
if (code >= 0x60)
|
||
{
|
||
ST3 = get_input(state, in3);
|
||
}
|
||
|
||
switch (code)
|
||
{
|
||
case 0x40:
|
||
get_box(ST1)->value = ST2;
|
||
break;
|
||
case 0x41:
|
||
get_pair(ST1)->car = ST2;
|
||
break;
|
||
case 0x42:
|
||
get_pair(ST1)->cdr = ST2;
|
||
break;
|
||
case 0x60:
|
||
vector_set(ST1, get_fixnum(ST2), ST3);
|
||
break;
|
||
case 0x61:
|
||
byte_string_set(ST1, get_fixnum(ST2), (char)get_fixnum(ST3));
|
||
break;
|
||
case 0x62:
|
||
struct_set(ST1, get_fixnum(ST2), ST3);
|
||
}
|
||
}
|
||
|
||
static value_t get_input(const interp_state_t *state, fixnum_t var)
|
||
{
|
||
switch (var)
|
||
{
|
||
case 0:
|
||
return NIL;
|
||
case 1 ... 63:
|
||
{
|
||
vector_t *vec = get_vector(_LAMBDA_SLOT(state->lambda.value, GLOBAL_VARS));
|
||
var -= 1;
|
||
|
||
if (var >= vec->size)
|
||
abort();
|
||
|
||
return vec->elements[var];
|
||
}
|
||
case 64 ... 127:
|
||
{
|
||
vector_t *vec = get_vector(_LAMBDA_SLOT(state->lambda.value, INSTANCE_VARS));
|
||
var -= 64;
|
||
|
||
if (var >= vec->size)
|
||
abort();
|
||
|
||
return vec->elements[var];
|
||
}
|
||
case 128 ... 247:
|
||
{
|
||
/* Frame is allocated by interpreter, so we know it's a vector already. */
|
||
vector_t *vec = _get_vector(state->frame.value);
|
||
var -= 128;
|
||
|
||
if (var >= vec->size)
|
||
abort();
|
||
|
||
return vec->elements[var];
|
||
}
|
||
/* 248 ... 252 are reserved */
|
||
case 253:
|
||
return state->argv.value;
|
||
case 254:
|
||
return state->k.value;
|
||
case 255:
|
||
return state->ctx.value;
|
||
default:
|
||
abort();
|
||
}
|
||
}
|
||
|
||
static void set_output(const interp_state_t *state, fixnum_t var, value_t val)
|
||
{
|
||
vector_t *vec = _get_vector(state->frame.value);
|
||
|
||
/* Only frame variables can be output targets for bytecode instructions. */
|
||
if (var < 128 || var >= (128 + vec->size))
|
||
abort();
|
||
|
||
vec->elements[var-128] = val;
|
||
}
|
||
|
||
static void register_state(interp_state_t *state, value_t lambda, value_t argv)
|
||
{
|
||
register_gc_root(&state->lambda, lambda);
|
||
register_gc_root(&state->frame, NIL);
|
||
register_gc_root(&state->argv, argv);
|
||
register_gc_root(&state->k, NIL);
|
||
register_gc_root(&state->ctx, NIL);
|
||
register_gc_root(&state->in1, NIL);
|
||
register_gc_root(&state->in2, NIL);
|
||
register_gc_root(&state->in3, NIL);
|
||
}
|
||
|
||
static void unregister_state(interp_state_t *state)
|
||
{
|
||
unregister_gc_root(&state->lambda);
|
||
unregister_gc_root(&state->frame);
|
||
unregister_gc_root(&state->argv);
|
||
unregister_gc_root(&state->k);
|
||
unregister_gc_root(&state->ctx);
|
||
unregister_gc_root(&state->in1);
|
||
unregister_gc_root(&state->in2);
|
||
unregister_gc_root(&state->in3);
|
||
}
|
||
|
||
/* vim:set sw=2 expandtab: */
|