Add support for immutable vectors, byte-strings, and structures.

The interpreter now requires its input to be immutable.
The reader marks values read after '#@' as immutable, e.g. #@#(...).
This commit is contained in:
Jesse D. McDonald 2010-06-27 16:20:51 -05:00
parent 00718b410b
commit 892af308ce
8 changed files with 138 additions and 23 deletions

View File

@ -17,6 +17,8 @@ static void register_template(void);
static void register_lambda(void); static void register_lambda(void);
static void bi_string_to_number(interp_state_t *state); static void bi_string_to_number(interp_state_t *state);
static void bi_freeze(interp_state_t *state);
static void bi_immutable_p(interp_state_t *state);
void builtin_init(void) void builtin_init(void)
{ {
@ -24,8 +26,10 @@ void builtin_init(void)
register_gc_root(&template_type_root, UNDEFINED); register_gc_root(&template_type_root, UNDEFINED);
register_gc_root(&lambda_type_root, UNDEFINED); register_gc_root(&lambda_type_root, UNDEFINED);
register_builtin(BI_UNDEFINED, UNDEFINED); register_builtin(BI_UNDEFINED, UNDEFINED);
register_builtin(BI_STRING_TO_NUMBER, make_builtin_fn(bi_string_to_number)); register_builtin(BI_STRING_TO_NUMBER, make_builtin_fn(bi_string_to_number));
register_builtin(BI_FREEZE, make_builtin_fn(bi_freeze));
register_builtin(BI_IMMUTABLE_P, make_builtin_fn(bi_immutable_p));
#ifdef NAN #ifdef NAN
register_builtin(BI_POS_NAN, make_float(NAN)); register_builtin(BI_POS_NAN, make_float(NAN));
@ -98,6 +102,8 @@ static void register_template(void)
/* Slot 3: Callable object used as proxy when structure is APPLY'd. */ /* Slot 3: Callable object used as proxy when structure is APPLY'd. */
_get_struct(template_type_root.value)->slots[SS(CALLABLE)] = FALSE_VALUE; _get_struct(template_type_root.value)->slots[SS(CALLABLE)] = FALSE_VALUE;
_get_struct(template_type_root.value)->immutable = true;
register_builtin(BI_TEMPLATE, template_type_root.value); register_builtin(BI_TEMPLATE, template_type_root.value);
} }
@ -113,6 +119,8 @@ static void register_lambda(void)
/* Slot 3: Callable object used as proxy when structure is APPLY'd. */ /* Slot 3: Callable object used as proxy when structure is APPLY'd. */
_get_struct(lambda_type_root.value)->slots[SS(CALLABLE)] = FALSE_VALUE; _get_struct(lambda_type_root.value)->slots[SS(CALLABLE)] = FALSE_VALUE;
_get_struct(lambda_type_root.value)->immutable = true;
register_builtin(BI_LAMBDA, lambda_type_root.value); register_builtin(BI_LAMBDA, lambda_type_root.value);
} }
@ -137,4 +145,52 @@ static void bi_string_to_number(interp_state_t *state)
interp_return_values(state, cons(rval, NIL)); interp_return_values(state, cons(rval, NIL));
} }
static void bi_freeze(interp_state_t *state)
{
value_t val = CAR(state->argv.value);
if (is_vector(val))
{
_get_vector(val)->immutable = true;
}
else if (is_byte_string(val))
{
_get_byte_string(val)->immutable = true;
}
else if (is_struct(val))
{
_get_struct(val)->immutable = true;
}
interp_return_values(state, cons(val, NIL));
}
static void bi_immutable_p(interp_state_t *state)
{
value_t val;
bool frozen;
val = CAR(state->argv.value);
if (is_vector(val))
{
frozen = _get_vector(val)->immutable;
}
else if (is_byte_string(val))
{
frozen = _get_byte_string(val)->immutable;
}
else if (is_struct(val))
{
frozen = _get_struct(val)->immutable;
}
else
{
/* These values can't be changed, and thus can be considered frozen: */
frozen = !is_object(val) || is_float(val) || is_builtin_fn(val);
}
interp_return_values(state, cons(boolean_value(frozen), NIL));
}
/* vim:set sw=2 expandtab: */ /* vim:set sw=2 expandtab: */

View File

@ -17,8 +17,10 @@
#define BI_POS_INFINITY "+infinity" #define BI_POS_INFINITY "+infinity"
#define BI_NEG_INFINITY "-infinity" #define BI_NEG_INFINITY "-infinity"
/* Name of builtin function */ /* Names of builtin functions */
#define BI_STRING_TO_NUMBER "string->number" #define BI_STRING_TO_NUMBER "string->number"
#define BI_FREEZE "freeze!"
#define BI_IMMUTABLE_P "immutable?"
#define TEMPLATE_SLOT_GLOBAL_VARS 0 #define TEMPLATE_SLOT_GLOBAL_VARS 0
#define TEMPLATE_SLOT_INSTANCE_VARS 1 #define TEMPLATE_SLOT_INSTANCE_VARS 1

15
gc.c
View File

@ -166,6 +166,7 @@ value_t make_vector(size_t nelem, value_t initial_value)
vec->tag = TYPE_TAG_VECTOR; vec->tag = TYPE_TAG_VECTOR;
vec->size = nelem; vec->size = nelem;
vec->hash = make_hash_value(); vec->hash = make_hash_value();
vec->immutable = false;
for (int i = 0; i < nelem; ++i) for (int i = 0; i < nelem; ++i)
vec->elements[i] = iv_root.value; vec->elements[i] = iv_root.value;
@ -189,6 +190,7 @@ value_t make_byte_string(size_t size, int default_value)
str = (byte_string_t*)gc_alloc(nbytes); str = (byte_string_t*)gc_alloc(nbytes);
str->tag = TYPE_TAG_BYTESTR; str->tag = TYPE_TAG_BYTESTR;
str->size = size; str->size = size;
str->immutable = false;
memset(str->bytes, default_value, size); memset(str->bytes, default_value, size);
@ -242,6 +244,7 @@ value_t make_struct(value_t type)
register_gc_root(&type_root, type); register_gc_root(&type_root, type);
release_assert(struct_is_a(type_root.value, get_structure_type())); release_assert(struct_is_a(type_root.value, get_structure_type()));
release_assert(_get_struct(type_root.value)->immutable);
nslots = get_fixnum(_SLOT_VALUE(STRUCTURE, type_root.value, NSLOTS)); nslots = get_fixnum(_SLOT_VALUE(STRUCTURE, type_root.value, NSLOTS));
s = (struct_t*)gc_alloc(STRUCT_BYTES(nslots)); s = (struct_t*)gc_alloc(STRUCT_BYTES(nslots));
@ -249,6 +252,7 @@ value_t make_struct(value_t type)
s->type = type_root.value; s->type = type_root.value;
s->nslots = nslots; s->nslots = nslots;
s->hash = make_hash_value(); s->hash = make_hash_value();
s->immutable = false;
for (int i = 0; i < nslots; ++i) for (int i = 0; i < nslots; ++i)
s->slots[i] = UNDEFINED; s->slots[i] = UNDEFINED;
@ -281,6 +285,8 @@ static void structure_init(void)
/* Can be LAMBDA, callable structure instance, builtin, or FALSE_VALUE. */ /* Can be LAMBDA, callable structure instance, builtin, or FALSE_VALUE. */
_get_struct(structure_type_root.value)->slots[SS(CALLABLE)] = FALSE_VALUE; _get_struct(structure_type_root.value)->slots[SS(CALLABLE)] = FALSE_VALUE;
#undef SS #undef SS
_get_struct(structure_type_root.value)->immutable = true;
} }
bool struct_is_a(value_t value, value_t type) bool struct_is_a(value_t value, value_t type)
@ -1540,6 +1546,9 @@ static void _fprint_value(FILE *f, value_t v, seen_value_t *seen)
} }
else if (is_vector(v)) else if (is_vector(v))
{ {
if (_get_vector(v)->immutable)
fputs("#@", f);
fputs("#(", f); fputs("#(", f);
for (size_t i = 0; i < _get_vector(v)->size; ++i) for (size_t i = 0; i < _get_vector(v)->size; ++i)
@ -1555,6 +1564,9 @@ static void _fprint_value(FILE *f, value_t v, seen_value_t *seen)
byte_string_t *str = _get_byte_string(v); byte_string_t *str = _get_byte_string(v);
size_t written = 0; size_t written = 0;
if (str->immutable)
fputs("#@", f);
fputc('"', f); fputc('"', f);
for (size_t i = 0; i < str->size; ++i) for (size_t i = 0; i < str->size; ++i)
@ -1585,6 +1597,9 @@ static void _fprint_value(FILE *f, value_t v, seen_value_t *seen)
{ {
value_t meta = _get_struct(v)->type; value_t meta = _get_struct(v)->type;
if (_get_struct(v)->immutable)
fputs("#@", f);
fputs("#S(", f); fputs("#S(", f);
if (meta == get_structure_type()) if (meta == get_structure_type())

4
gc.h
View File

@ -61,6 +61,7 @@ typedef void (builtin_fn_t)(struct interp_state *state);
#define UNDEFINED SPECIAL_VALUE(3) #define UNDEFINED SPECIAL_VALUE(3)
#define GC_GEN0_POISON SPECIAL_VALUE(4) #define GC_GEN0_POISON SPECIAL_VALUE(4)
#define GC_GEN1_POISON SPECIAL_VALUE(5) #define GC_GEN1_POISON SPECIAL_VALUE(5)
#define END_PROGRAM SPECIAL_VALUE(6)
#define TYPE_TAG_BOX TYPE_TAG(0) #define TYPE_TAG_BOX TYPE_TAG(0)
#define TYPE_TAG_VECTOR TYPE_TAG(1) #define TYPE_TAG_VECTOR TYPE_TAG(1)
@ -118,6 +119,7 @@ typedef struct vector
value_t tag; /* TYPE_TAG_VECTOR */ value_t tag; /* TYPE_TAG_VECTOR */
size_t size; size_t size;
value_t hash; value_t hash;
bool immutable;
value_t elements[0]; value_t elements[0];
} vector_t; } vector_t;
@ -125,6 +127,7 @@ typedef struct byte_string
{ {
value_t tag; /* TYPE_TAG_BYTESTR */ value_t tag; /* TYPE_TAG_BYTESTR */
size_t size; size_t size;
bool immutable;
uint8_t bytes[0]; uint8_t bytes[0];
} byte_string_t; } byte_string_t;
@ -135,6 +138,7 @@ typedef struct structure
value_t type; value_t type;
size_t nslots; size_t nslots;
value_t hash; value_t hash;
bool immutable;
value_t slots[0]; value_t slots[0];
} struct_t; } struct_t;

View File

@ -66,8 +66,8 @@ value_t run_interpreter(value_t lambda, value_t argv)
register_state(&state, lambda, argv); register_state(&state, lambda, argv);
/* Keep going until something attempts to tail-call FALSE_VALUE, the original 'k', indicating completion. */ /* Keep going until something attempts to tail-call END_PROGRAM, the original 'k', indicating completion. */
while (state.lambda.value != FALSE_VALUE) while (state.lambda.value != END_PROGRAM)
{ {
/* 'lambda' may be a callable structure; if so, follow the 'callable' proxies and update argv. */ /* 'lambda' may be a callable structure; if so, follow the 'callable' proxies and update argv. */
translate_callable(&state); translate_callable(&state);
@ -95,13 +95,14 @@ value_t run_interpreter(value_t lambda, value_t argv)
} }
else else
{ {
release_assert(get_struct(state.lambda.value)->immutable);
state.nframe = get_fixnum(_LAMBDA_SLOT(state.lambda.value, FRAME_VARS)); state.nframe = get_fixnum(_LAMBDA_SLOT(state.lambda.value, FRAME_VARS));
release_assert((0 <= state.nframe) && (state.nframe <= 120)); release_assert((0 <= state.nframe) && (state.nframe <= 120));
state.globals.value = _LAMBDA_SLOT(state.lambda.value, GLOBAL_VARS); state.globals.value = _LAMBDA_SLOT(state.lambda.value, GLOBAL_VARS);
state.instances.value = _LAMBDA_SLOT(state.lambda.value, INSTANCE_VARS); state.instances.value = _LAMBDA_SLOT(state.lambda.value, INSTANCE_VARS);
release_assert(is_vector(state.globals.value)); release_assert(get_vector(state.globals.value)->immutable);
release_assert(is_vector(state.instances.value)); release_assert(get_vector(state.instances.value)->immutable);
run_byte_code(&state); run_byte_code(&state);
perform_tail_call(&state); perform_tail_call(&state);
@ -169,6 +170,7 @@ 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 vector_set(value_t v, fixnum_t idx, value_t newval)
{ {
vector_t *vec = get_vector(v); vector_t *vec = get_vector(v);
release_assert(!vec->immutable);
release_assert((idx >= 0) && (idx < vec->size)); release_assert((idx >= 0) && (idx < vec->size));
vec->elements[idx] = newval; vec->elements[idx] = newval;
WRITE_BARRIER(v); WRITE_BARRIER(v);
@ -177,6 +179,7 @@ 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 byte_string_set(value_t v, fixnum_t idx, char newval)
{ {
byte_string_t *str = get_byte_string(v); byte_string_t *str = get_byte_string(v);
release_assert(!str->immutable);
release_assert((idx >= 0) && (idx < str->size)); release_assert((idx >= 0) && (idx < str->size));
str->bytes[idx] = newval; str->bytes[idx] = newval;
} }
@ -184,9 +187,8 @@ 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 void struct_set(value_t v, fixnum_t idx, value_t newval)
{ {
struct_t *s = get_struct(v); struct_t *s = get_struct(v);
release_assert(!s->immutable);
release_assert((idx >= 0) && (idx < s->nslots)); release_assert((idx >= 0) && (idx < s->nslots));
s->slots[idx] = newval; s->slots[idx] = newval;
WRITE_BARRIER(v); WRITE_BARRIER(v);
} }
@ -232,6 +234,7 @@ static value_t make_lambda(interp_state_t *state, value_t templ)
ls->slots[LAMBDA_SLOT_FRAME_VARS] = ts->slots[TEMPLATE_SLOT_FRAME_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_BYTE_CODE] = ts->slots[TEMPLATE_SLOT_BYTE_CODE];
ls->slots[LAMBDA_SLOT_TAIL_CALL] = ts->slots[TEMPLATE_SLOT_TAIL_CALL]; ls->slots[LAMBDA_SLOT_TAIL_CALL] = ts->slots[TEMPLATE_SLOT_TAIL_CALL];
ls->immutable = true;
WRITE_BARRIER(lambda_root.value); WRITE_BARRIER(lambda_root.value);
l_inst = _get_vector(ls->slots[LAMBDA_SLOT_INSTANCE_VARS]); l_inst = _get_vector(ls->slots[LAMBDA_SLOT_INSTANCE_VARS]);
@ -241,6 +244,7 @@ static value_t make_lambda(interp_state_t *state, value_t templ)
{ {
l_inst->elements[i] = get_input(state, t_inst->bytes[i]); l_inst->elements[i] = get_input(state, t_inst->bytes[i]);
} }
l_inst->immutable = true;
WRITE_BARRIER(object_value(l_inst)); WRITE_BARRIER(object_value(l_inst));
unregister_gc_root(&templ_root); unregister_gc_root(&templ_root);
@ -270,13 +274,15 @@ static void run_byte_code(interp_state_t *state)
if (bc_root.value != FALSE_VALUE) if (bc_root.value != FALSE_VALUE)
{ {
release_assert((get_byte_string(bc_root.value)->size % 4) == 0); release_assert(get_byte_string(bc_root.value)->immutable);
release_assert((_get_byte_string(bc_root.value)->size % 4) == 0);
for (size_t offset = 0; (offset+3) < _get_byte_string(bc_root.value)->size; offset += 4) for (size_t offset = 0; (offset+3) < _get_byte_string(bc_root.value)->size; offset += 4)
{ {
uint8_t bytes[4]; uint32_t word;
uint8_t *bytes = (uint8_t*)&word;
memcpy(bytes, _get_byte_string(bc_root.value)->bytes + offset, 4); word = *(uint32_t*)(_get_byte_string(bc_root.value)->bytes + offset);
switch (bytes[0]) switch (bytes[0])
{ {
@ -312,7 +318,8 @@ static void perform_tail_call(interp_state_t *state)
gc_root_t new_lambda, new_argv, new_kw_args, new_kw_vals, new_ctx, new_k; gc_root_t new_lambda, new_argv, new_kw_args, new_kw_vals, new_ctx, new_k;
value_t tail_call = _LAMBDA_SLOT(state->lambda.value, TAIL_CALL); value_t tail_call = _LAMBDA_SLOT(state->lambda.value, TAIL_CALL);
release_assert(get_byte_string(tail_call)->size == 6); release_assert(get_byte_string(tail_call)->immutable);
release_assert(_get_byte_string(tail_call)->size == 6);
register_gc_root(&new_lambda, get_input(state, _get_byte_string(tail_call)->bytes[0])); register_gc_root(&new_lambda, get_input(state, _get_byte_string(tail_call)->bytes[0]));
register_gc_root(&new_argv, get_input(state, _get_byte_string(tail_call)->bytes[1])); register_gc_root(&new_argv, get_input(state, _get_byte_string(tail_call)->bytes[1]));
@ -593,14 +600,14 @@ static void register_state(interp_state_t *state, value_t lambda, value_t argv)
register_gc_root(&state->kw_args, NIL); register_gc_root(&state->kw_args, NIL);
register_gc_root(&state->kw_vals, NIL); register_gc_root(&state->kw_vals, NIL);
register_gc_root(&state->ctx, FALSE_VALUE); register_gc_root(&state->ctx, FALSE_VALUE);
register_gc_root(&state->k, FALSE_VALUE); register_gc_root(&state->k, END_PROGRAM);
register_gc_root(&state->globals, UNDEFINED); register_gc_root(&state->globals, UNDEFINED);
register_gc_root(&state->instances, UNDEFINED); register_gc_root(&state->instances, UNDEFINED);
register_gc_root(&state->frame, make_vector(120, UNDEFINED)); register_gc_root(&state->frame, make_vector(120, UNDEFINED));
register_gc_root(&state->in1, FALSE_VALUE); register_gc_root(&state->in1, UNDEFINED);
register_gc_root(&state->in2, FALSE_VALUE); register_gc_root(&state->in2, UNDEFINED);
register_gc_root(&state->in3, FALSE_VALUE); register_gc_root(&state->in3, UNDEFINED);
} }
static void unregister_state(interp_state_t *state) static void unregister_state(interp_state_t *state)

View File

@ -52,7 +52,7 @@
(write-char #\" port)) (write-char #\" port))
(define (write-instance-string inst-vars port) (define (write-instance-string inst-vars port)
(write-char #\" port) (write-string "#@\"" port)
(for ([var (in-list inst-vars)]) (for ([var (in-list inst-vars)])
(write-hex-char (variable->code var) port)) (write-hex-char (variable->code var) port))
(write-char #\" port)) (write-char #\" port))
@ -60,7 +60,7 @@
(define (write-rla-bytecode+tail-call forms port) (define (write-rla-bytecode+tail-call forms port)
(define (write-tail-call tc-form) (define (write-tail-call tc-form)
(req-new-line port) (req-new-line port)
(write-char #\" port) (write-string "#@\"" port)
(for ([var (in-list (cdr tc-form))]) (for ([var (in-list (cdr tc-form))])
(write-hex-char (variable->code var) port)) (write-hex-char (variable->code var) port))
(write-char #\" port) (write-char #\" port)
@ -72,7 +72,7 @@
(let-values ([(line col pos) (port-next-location port)]) (let-values ([(line col pos) (port-next-location port)])
(parameterize ([current-indent col]) (parameterize ([current-indent col])
(write-char #\" port) (write-string "#@\"" port)
(if (eq? (first (first forms)) '#%tail-call) (if (eq? (first (first forms)) '#%tail-call)
(begin (begin
(write-char #\" port) (write-char #\" port)
@ -100,13 +100,13 @@
(define template? (eq? (first value) '#%template)) (define template? (eq? (first value) '#%template))
(let-values ([(line col pos) (port-next-location port)]) (let-values ([(line col pos) (port-next-location port)])
(parameterize ([current-indent col]) (parameterize ([current-indent col])
(write-string "#S(" port) (write-string "#@#S(" port)
(if (eq? (first value) '#%template) (if (eq? (first value) '#%template)
(write-string "#=\"template\"" port) (write-string "#=\"template\"" port)
(write-string "#=\"lambda\"" port)) (write-string "#=\"lambda\"" port))
(parameterize ([current-indent (+ (current-indent-step) (current-indent))]) (parameterize ([current-indent (+ (current-indent-step) (current-indent))])
(req-new-line port) (req-new-line port)
(write-string "#(" port) (write-string "#@#(" port)
(unless (null? (second value)) (unless (null? (second value))
(parameterize ([current-indent (+ (current-indent-step) (current-indent))]) (parameterize ([current-indent (+ (current-indent-step) (current-indent))])
(opt-new-line port) (opt-new-line port)
@ -120,7 +120,7 @@
(if template? (if template?
(write-instance-string (third value) port) (write-instance-string (third value) port)
(begin (begin
(write-string "#(" port) (write-string "#@#(" port)
(unless (null? (third value)) (unless (null? (third value))
(parameterize ([current-indent (+ (current-indent-step) (current-indent))]) (parameterize ([current-indent (+ (current-indent-step) (current-indent))])
(opt-new-line port) (opt-new-line port)

View File

@ -45,6 +45,8 @@ static value_t read_placeholder(reader_state_t *state);
static value_t read_indirect(value_t path); static value_t read_indirect(value_t path);
static value_t freeze(value_t val);
static void next_char(reader_state_t *state); static void next_char(reader_state_t *state);
static void skip_whitespace(reader_state_t *state); static void skip_whitespace(reader_state_t *state);
@ -220,6 +222,9 @@ static value_t read_special(reader_state_t *state)
case 'i': case 'i':
next_char(state); next_char(state);
return read_indirect(read_string(state)); return read_indirect(read_string(state));
case '@':
next_char(state);
return freeze(read_one_value(state));
default: default:
release_assert(NOTREACHED("Invalid character in special value.")); release_assert(NOTREACHED("Invalid character in special value."));
return UNDEFINED; return UNDEFINED;
@ -751,6 +756,29 @@ static value_t read_indirect(value_t path)
return v; return v;
} }
static value_t freeze(value_t val)
{
if (is_vector(val))
{
_get_vector(val)->immutable = true;
}
else if (is_byte_string(val))
{
_get_byte_string(val)->immutable = true;
}
else if (is_struct(val))
{
_get_struct(val)->immutable = true;
}
else
{
/* Error if value cannot be made immutable */
release_assert(!is_object(val) || is_float(val) || is_builtin_fn(val));
}
return val;
}
static bool is_placeholder(reader_state_t *state, value_t value) static bool is_placeholder(reader_state_t *state, value_t value)
{ {
for (value_t item = state->ref_alist.value; !is_nil(item); item = _CDDR(item)) for (value_t item = state->ref_alist.value; !is_nil(item); item = _CDDR(item))

View File

@ -123,6 +123,9 @@
[(memq? current-char '(#\B #\b)) [(memq? current-char '(#\B #\b))
(next-char) (next-char)
(read-fixnum 2)] (read-fixnum 2)]
[(eq? current-char #\@)
(next-char)
(freeze! (read-one-value))]
[else (unexpected-char)])) [else (unexpected-char)]))
(define (read-list) (define (read-list)