Implement fixed-function reader for getting data into the interpreter.
This commit is contained in:
parent
10fbc269c5
commit
449b0cf478
7
Makefile
7
Makefile
|
|
@ -1,5 +1,5 @@
|
|||
CFLAGS = -std=c99
|
||||
LDFLAGS = -lrt
|
||||
CFLAGS = -std=c99 -Wall
|
||||
LDFLAGS = -lrt -lm
|
||||
|
||||
ifeq ($(DEBUG),yes)
|
||||
CFLAGS += -g
|
||||
|
|
@ -25,9 +25,10 @@ endif
|
|||
clean:
|
||||
-rm -f rosella *.o *.gcda *.gcno
|
||||
|
||||
rosella: rosella.o gc.o builtin.o interp.o
|
||||
rosella: rosella.o gc.o builtin.o interp.o reader.o
|
||||
|
||||
rosella.o: rosella.c gc.h builtin.h interp.h
|
||||
gc.o: gc.c gc.h
|
||||
builtin.o: builtin.c builtin.h gc.h
|
||||
interp.o: interp.c interp.h gc.h builtin.h
|
||||
reader.o: reader.c reader.h gc.h builtin.h
|
||||
|
|
|
|||
233
builtin.c
233
builtin.c
|
|
@ -2,120 +2,28 @@
|
|||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "gc.h"
|
||||
#include "builtin.h"
|
||||
|
||||
static gc_root_t builtin_list;
|
||||
|
||||
/* Returns a byte string w/ bytes from 's' (excl. terminating NUL). */
|
||||
static value_t string_to_value(const char *s);
|
||||
|
||||
/* Return value is a new C string which must be free()'d by caller. */
|
||||
static char *value_to_string(value_t v);
|
||||
|
||||
/* Like strcmp(), but for byte strings. */
|
||||
static int byte_strcmp(value_t s1, value_t s2);
|
||||
static void register_structure(gc_root_t *ms_root);
|
||||
static void register_template(gc_root_t *ms_root);
|
||||
static void register_lambda(gc_root_t *ms_root);
|
||||
|
||||
void builtin_init(void)
|
||||
{
|
||||
gc_root_t ms_root, tmp_root;
|
||||
gc_root_t ms_root;
|
||||
|
||||
register_gc_root(&builtin_list, NIL);
|
||||
register_gc_root(&builtin_list, UNDEFINED);
|
||||
register_gc_root(&ms_root, UNDEFINED);
|
||||
|
||||
#define SS(x) STRUCTURE_SLOT_ ## x
|
||||
register_builtin(BI_UNDEFINED, UNDEFINED);
|
||||
|
||||
/* (Meta-)Structure: Instances of this structure describe structures. */
|
||||
register_gc_root(&ms_root, make_struct(NIL, STRUCTURE_SLOTS));
|
||||
register_builtin(BI_STRUCTURE, ms_root.value);
|
||||
|
||||
/* Metastruct is both a structure and a structure description,
|
||||
* and thus is an instance of itself. */
|
||||
_get_struct(ms_root.value)->type = ms_root.value;
|
||||
/* Slot 1: Name */
|
||||
_get_struct(ms_root.value)->slots[SS(NAME)] = string_to_value("structure");
|
||||
/* Slot 2: Super/parent structure type, or NIL */
|
||||
_get_struct(ms_root.value)->slots[SS(SUPER)] = NIL;
|
||||
/* Slot 3: Vector of slot names; size == total number of slots (excl. type) */
|
||||
_get_struct(ms_root.value)->slots[SS(SLOTS)] = make_vector(STRUCTURE_SLOTS, NIL);
|
||||
{
|
||||
gc_root_t vec_root;
|
||||
register_gc_root(&vec_root, _get_struct(ms_root.value)->slots[SS(SLOTS)]);
|
||||
_get_vector(vec_root.value)->elements[SS(NAME)] = string_to_value("name");
|
||||
_get_vector(vec_root.value)->elements[SS(SUPER)] = string_to_value("super");
|
||||
_get_vector(vec_root.value)->elements[SS(SLOTS)] = string_to_value("slots");
|
||||
_get_vector(vec_root.value)->elements[SS(CALLABLE)] = string_to_value("callable");
|
||||
unregister_gc_root(&vec_root);
|
||||
}
|
||||
/* Slot 4: Callable object used as proxy when structure is APPLY'd. */
|
||||
/* Can be LAMBDA or callable structure instance. */
|
||||
_get_struct(ms_root.value)->slots[SS(CALLABLE)] = NIL;
|
||||
|
||||
#define TS(x) TEMPLATE_SLOT_ ## x
|
||||
|
||||
/* Template: Instances of this structure describe what a LAMBDA
|
||||
* will look like when instanciated with the 'lambda' bytecode. */
|
||||
register_gc_root(&tmp_root, make_struct(ms_root.value, STRUCTURE_SLOTS));
|
||||
register_builtin(BI_TEMPLATE, tmp_root.value);
|
||||
|
||||
/* Slot 1: Name */
|
||||
_get_struct(tmp_root.value)->slots[SS(NAME)] = string_to_value("template");
|
||||
/* Slot 2: Super/parent structure type, or NIL */
|
||||
_get_struct(tmp_root.value)->slots[SS(SUPER)] = NIL;
|
||||
/* Slot 3: Vector of slot names; size == total number of slots */
|
||||
_get_struct(tmp_root.value)->slots[SS(SLOTS)] = make_vector(TEMPLATE_SLOTS, NIL);
|
||||
{
|
||||
gc_root_t vec_root;
|
||||
register_gc_root(&vec_root, _get_struct(tmp_root.value)->slots[SS(SLOTS)]);
|
||||
_get_vector(vec_root.value)->elements[TS(GLOBAL_VARS)] = string_to_value("global-vars");
|
||||
_get_vector(vec_root.value)->elements[TS(INSTANCE_VARS)] = string_to_value("instance-vars");
|
||||
_get_vector(vec_root.value)->elements[TS(FRAME_VARS)] = string_to_value("frame-vars");
|
||||
_get_vector(vec_root.value)->elements[TS(BYTE_CODE)] = string_to_value("byte-code");
|
||||
_get_vector(vec_root.value)->elements[TS(TAIL_CALL)] = string_to_value("tail-call");
|
||||
_get_vector(vec_root.value)->elements[TS(ARG_LIST)] = string_to_value("argument-list");
|
||||
_get_vector(vec_root.value)->elements[TS(CONTINUATION)] = string_to_value("continuation");
|
||||
_get_vector(vec_root.value)->elements[TS(CONTEXT)] = string_to_value("context");
|
||||
unregister_gc_root(&vec_root);
|
||||
}
|
||||
/* Slot 4: Callable object used as proxy when structure is apply'd. */
|
||||
_get_struct(tmp_root.value)->slots[SS(CALLABLE)] = NIL;
|
||||
|
||||
unregister_gc_root(&tmp_root);
|
||||
|
||||
#undef TS
|
||||
#define LS(x) LAMBDA_SLOT_ ## x
|
||||
|
||||
/* Lambda: Instances of this structure are fundamental callable objects. */
|
||||
register_gc_root(&tmp_root, make_struct(ms_root.value, STRUCTURE_SLOTS));
|
||||
register_builtin(BI_LAMBDA, tmp_root.value);
|
||||
|
||||
/* Slot 1: Name */
|
||||
_get_struct(tmp_root.value)->slots[SS(NAME)] = string_to_value("lambda");
|
||||
/* Slot 2: Super/parent structure type, or NIL */
|
||||
_get_struct(tmp_root.value)->slots[SS(SUPER)] = NIL;
|
||||
/* Slot 3: Vector of slot names; size == total number of slots */
|
||||
_get_struct(tmp_root.value)->slots[SS(SLOTS)] = make_vector(LAMBDA_SLOTS, NIL);
|
||||
{
|
||||
gc_root_t vec_root;
|
||||
register_gc_root(&vec_root, _get_struct(tmp_root.value)->slots[SS(SLOTS)]);
|
||||
_get_vector(vec_root.value)->elements[LS(GLOBAL_VARS)] = string_to_value("global-vars");
|
||||
_get_vector(vec_root.value)->elements[LS(INSTANCE_VARS)] = string_to_value("instance-vars");
|
||||
_get_vector(vec_root.value)->elements[LS(FRAME_VARS)] = string_to_value("frame-vars");
|
||||
_get_vector(vec_root.value)->elements[LS(BYTE_CODE)] = string_to_value("byte-code");
|
||||
_get_vector(vec_root.value)->elements[LS(TAIL_CALL)] = string_to_value("tail-call");
|
||||
_get_vector(vec_root.value)->elements[LS(ARG_LIST)] = string_to_value("argument-list");
|
||||
_get_vector(vec_root.value)->elements[LS(CONTINUATION)] = string_to_value("continuation");
|
||||
_get_vector(vec_root.value)->elements[LS(CONTEXT)] = string_to_value("context");
|
||||
unregister_gc_root(&vec_root);
|
||||
}
|
||||
/* Slot 4: Callable object used as proxy when structure is apply'd. */
|
||||
_get_struct(tmp_root.value)->slots[SS(CALLABLE)] = NIL;
|
||||
|
||||
unregister_gc_root(&tmp_root);
|
||||
|
||||
#undef LS
|
||||
#undef SS
|
||||
register_structure(&ms_root);
|
||||
register_template(&ms_root);
|
||||
register_lambda(&ms_root);
|
||||
|
||||
unregister_gc_root(&ms_root);
|
||||
}
|
||||
|
|
@ -146,36 +54,117 @@ value_t lookup_builtin(const char *name)
|
|||
return FALSE_VALUE;
|
||||
}
|
||||
|
||||
static value_t string_to_value(const char *s)
|
||||
#define SS(x) STRUCTURE_SLOT_ ## x
|
||||
|
||||
static void register_structure(gc_root_t *ms_root)
|
||||
{
|
||||
size_t len = strlen(s);
|
||||
value_t v = make_byte_string(len, '\0');
|
||||
memcpy(_get_byte_string(v)->bytes, s, len);
|
||||
return v;
|
||||
/* (Meta-)Structure: Instances of this structure describe structures. */
|
||||
ms_root->value = make_struct(UNDEFINED, STRUCTURE_SLOTS);
|
||||
|
||||
/* Metastruct is both a structure and a structure description,
|
||||
* and thus is an instance of itself. */
|
||||
_get_struct(ms_root->value)->type = ms_root->value;
|
||||
/* Slot 1: Name */
|
||||
_get_struct(ms_root->value)->slots[SS(NAME)] = string_to_value("structure");
|
||||
/* Slot 2: Super/parent structure type, or FALSE_VALUE */
|
||||
_get_struct(ms_root->value)->slots[SS(SUPER)] = FALSE_VALUE;
|
||||
/* Slot 3: Vector of slot names; size == total number of slots (excl. type) */
|
||||
_get_struct(ms_root->value)->slots[SS(SLOTS)] = make_vector(STRUCTURE_SLOTS, UNDEFINED);
|
||||
{
|
||||
gc_root_t vec_root;
|
||||
register_gc_root(&vec_root, _get_struct(ms_root->value)->slots[SS(SLOTS)]);
|
||||
_get_vector(vec_root.value)->elements[SS(NAME)] = string_to_value("name");
|
||||
_get_vector(vec_root.value)->elements[SS(SUPER)] = string_to_value("super");
|
||||
_get_vector(vec_root.value)->elements[SS(SLOTS)] = string_to_value("slots");
|
||||
_get_vector(vec_root.value)->elements[SS(CALLABLE)] = string_to_value("callable");
|
||||
_get_vector(vec_root.value)->elements[SS(MUTABLE)] = string_to_value("mutable");
|
||||
unregister_gc_root(&vec_root);
|
||||
}
|
||||
/* Slot 4: Callable object used as proxy when structure is APPLY'd. */
|
||||
/* Can be LAMBDA, callable structure instance, or FALSE_VALUE. */
|
||||
_get_struct(ms_root->value)->slots[SS(CALLABLE)] = FALSE_VALUE;
|
||||
_get_struct(ms_root->value)->slots[SS(MUTABLE)] = FALSE_VALUE;
|
||||
|
||||
register_builtin(BI_STRUCTURE, ms_root->value);
|
||||
}
|
||||
|
||||
static char *value_to_string(value_t v)
|
||||
static void register_template(gc_root_t *ms_root)
|
||||
{
|
||||
byte_string_t *str = get_byte_string(v);
|
||||
char *s = (char*)malloc(str->size + 1);
|
||||
gc_root_t tmp_root;
|
||||
|
||||
memcpy(s, str->bytes, str->size);
|
||||
s[str->size] = '\0';
|
||||
#define TS(x) TEMPLATE_SLOT_ ## x
|
||||
|
||||
return s;
|
||||
/* Template: Instances of this structure describe what a LAMBDA
|
||||
* will look like when instanciated with the 'lambda' bytecode. */
|
||||
register_gc_root(&tmp_root, make_struct(ms_root->value, STRUCTURE_SLOTS));
|
||||
register_builtin(BI_TEMPLATE, tmp_root.value);
|
||||
|
||||
/* Slot 1: Name */
|
||||
_get_struct(tmp_root.value)->slots[SS(NAME)] = string_to_value("template");
|
||||
/* Slot 2: Super/parent structure type, or FALSE_VALUE */
|
||||
_get_struct(tmp_root.value)->slots[SS(SUPER)] = FALSE_VALUE;
|
||||
/* Slot 3: Vector of slot names; size == total number of slots */
|
||||
_get_struct(tmp_root.value)->slots[SS(SLOTS)] = make_vector(TEMPLATE_SLOTS, UNDEFINED);
|
||||
{
|
||||
gc_root_t vec_root;
|
||||
register_gc_root(&vec_root, _get_struct(tmp_root.value)->slots[SS(SLOTS)]);
|
||||
_get_vector(vec_root.value)->elements[TS(GLOBAL_VARS)] = string_to_value("global-vars");
|
||||
_get_vector(vec_root.value)->elements[TS(INSTANCE_VARS)] = string_to_value("instance-vars");
|
||||
_get_vector(vec_root.value)->elements[TS(FRAME_VARS)] = string_to_value("frame-vars");
|
||||
_get_vector(vec_root.value)->elements[TS(BYTE_CODE)] = string_to_value("byte-code");
|
||||
_get_vector(vec_root.value)->elements[TS(TAIL_CALL)] = string_to_value("tail-call");
|
||||
_get_vector(vec_root.value)->elements[TS(ARG_LIST)] = string_to_value("argument-list");
|
||||
_get_vector(vec_root.value)->elements[TS(CONTINUATION)] = string_to_value("continuation");
|
||||
_get_vector(vec_root.value)->elements[TS(CONTEXT)] = string_to_value("context");
|
||||
unregister_gc_root(&vec_root);
|
||||
}
|
||||
/* Slot 4: Callable object used as proxy when structure is apply'd. */
|
||||
_get_struct(tmp_root.value)->slots[SS(CALLABLE)] = FALSE_VALUE;
|
||||
_get_struct(tmp_root.value)->slots[SS(MUTABLE)] = FALSE_VALUE;
|
||||
|
||||
unregister_gc_root(&tmp_root);
|
||||
|
||||
#undef TS
|
||||
}
|
||||
|
||||
static int byte_strcmp(value_t s1, value_t s2)
|
||||
static void register_lambda(gc_root_t *ms_root)
|
||||
{
|
||||
byte_string_t *str1 = get_byte_string(s1);
|
||||
byte_string_t *str2 = get_byte_string(s2);
|
||||
gc_root_t tmp_root;
|
||||
|
||||
if (str1->size < str2->size)
|
||||
return -1;
|
||||
else if (str1->size > str2->size)
|
||||
return 1;
|
||||
else
|
||||
return memcmp(str1->bytes, str2->bytes, str1->size);
|
||||
#define LS(x) LAMBDA_SLOT_ ## x
|
||||
|
||||
/* Lambda: Instances of this structure are fundamental callable objects. */
|
||||
register_gc_root(&tmp_root, make_struct(ms_root->value, STRUCTURE_SLOTS));
|
||||
register_builtin(BI_LAMBDA, tmp_root.value);
|
||||
|
||||
/* Slot 1: Name */
|
||||
_get_struct(tmp_root.value)->slots[SS(NAME)] = string_to_value("lambda");
|
||||
/* Slot 2: Super/parent structure type, or FALSE_VALUE */
|
||||
_get_struct(tmp_root.value)->slots[SS(SUPER)] = FALSE_VALUE;
|
||||
/* Slot 3: Vector of slot names; size == total number of slots */
|
||||
_get_struct(tmp_root.value)->slots[SS(SLOTS)] = make_vector(LAMBDA_SLOTS, UNDEFINED);
|
||||
{
|
||||
gc_root_t vec_root;
|
||||
register_gc_root(&vec_root, _get_struct(tmp_root.value)->slots[SS(SLOTS)]);
|
||||
_get_vector(vec_root.value)->elements[LS(GLOBAL_VARS)] = string_to_value("global-vars");
|
||||
_get_vector(vec_root.value)->elements[LS(INSTANCE_VARS)] = string_to_value("instance-vars");
|
||||
_get_vector(vec_root.value)->elements[LS(FRAME_VARS)] = string_to_value("frame-vars");
|
||||
_get_vector(vec_root.value)->elements[LS(BYTE_CODE)] = string_to_value("byte-code");
|
||||
_get_vector(vec_root.value)->elements[LS(TAIL_CALL)] = string_to_value("tail-call");
|
||||
_get_vector(vec_root.value)->elements[LS(ARG_LIST)] = string_to_value("argument-list");
|
||||
_get_vector(vec_root.value)->elements[LS(CONTINUATION)] = string_to_value("continuation");
|
||||
_get_vector(vec_root.value)->elements[LS(CONTEXT)] = string_to_value("context");
|
||||
unregister_gc_root(&vec_root);
|
||||
}
|
||||
/* Slot 4: Callable object used as proxy when structure is apply'd. */
|
||||
_get_struct(tmp_root.value)->slots[SS(CALLABLE)] = FALSE_VALUE;
|
||||
_get_struct(tmp_root.value)->slots[SS(MUTABLE)] = FALSE_VALUE;
|
||||
|
||||
unregister_gc_root(&tmp_root);
|
||||
|
||||
#undef LS
|
||||
}
|
||||
|
||||
#undef SS
|
||||
|
||||
/* vim:set sw=2 expandtab: */
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
#include "gc.h"
|
||||
|
||||
/* Names of fundamental builtin values */
|
||||
#define BI_UNDEFINED "undefined"
|
||||
#define BI_STRUCTURE "structure"
|
||||
#define BI_TEMPLATE "template"
|
||||
#define BI_LAMBDA "lambda"
|
||||
|
|
@ -16,7 +17,8 @@
|
|||
#define STRUCTURE_SLOT_SUPER 1
|
||||
#define STRUCTURE_SLOT_SLOTS 2
|
||||
#define STRUCTURE_SLOT_CALLABLE 3
|
||||
#define STRUCTURE_SLOTS 4
|
||||
#define STRUCTURE_SLOT_MUTABLE 4
|
||||
#define STRUCTURE_SLOTS 5
|
||||
|
||||
#define TEMPLATE_SLOT_GLOBAL_VARS 0
|
||||
#define TEMPLATE_SLOT_INSTANCE_VARS 1
|
||||
|
|
|
|||
281
gc.c
281
gc.c
|
|
@ -1,6 +1,7 @@
|
|||
#define _POSIX_C_SOURCE 199309L
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
|
@ -43,7 +44,7 @@ static value_t gc_will_list;
|
|||
static value_t gc_will_active_list;
|
||||
|
||||
static gc_root_t gc_root_list = {
|
||||
.value = NIL,
|
||||
.value = UNDEFINED,
|
||||
.prev = &gc_root_list,
|
||||
.next = &gc_root_list
|
||||
};
|
||||
|
|
@ -72,12 +73,22 @@ void unregister_gc_root(gc_root_t *root)
|
|||
|
||||
/****************************************************************************/
|
||||
|
||||
bool get_boolean(value_t v)
|
||||
{
|
||||
release_assert(is_boolean(v));
|
||||
return (v != FALSE_VALUE);
|
||||
}
|
||||
|
||||
fixnum_t get_fixnum(value_t v)
|
||||
{
|
||||
release_assert(is_fixnum(v));
|
||||
return _get_fixnum(v);
|
||||
}
|
||||
|
||||
object_t *get_object(value_t v)
|
||||
{
|
||||
if (is_object(v))
|
||||
release_assert(is_object(v));
|
||||
return _get_object(v);
|
||||
else
|
||||
abort();
|
||||
}
|
||||
|
||||
/* No one outside this module should care... */
|
||||
|
|
@ -106,10 +117,8 @@ value_t cons(value_t car, value_t cdr)
|
|||
|
||||
pair_t *get_pair(value_t v)
|
||||
{
|
||||
if (is_pair(v))
|
||||
release_assert(is_pair(v));
|
||||
return _get_pair(v);
|
||||
else
|
||||
abort();
|
||||
}
|
||||
|
||||
value_t make_box(value_t initial_value)
|
||||
|
|
@ -130,10 +139,8 @@ value_t make_box(value_t initial_value)
|
|||
|
||||
box_t *get_box(value_t v)
|
||||
{
|
||||
if (is_box(v))
|
||||
release_assert(is_box(v));
|
||||
return _get_box(v);
|
||||
else
|
||||
abort();
|
||||
}
|
||||
|
||||
value_t make_vector(size_t nelem, value_t initial_value)
|
||||
|
|
@ -157,10 +164,8 @@ value_t make_vector(size_t nelem, value_t initial_value)
|
|||
|
||||
vector_t *get_vector(value_t v)
|
||||
{
|
||||
if (is_vector(v))
|
||||
release_assert(is_vector(v));
|
||||
return _get_vector(v);
|
||||
else
|
||||
abort();
|
||||
}
|
||||
|
||||
value_t make_byte_string(size_t size, int default_value)
|
||||
|
|
@ -179,10 +184,40 @@ value_t make_byte_string(size_t size, int default_value)
|
|||
|
||||
byte_string_t *get_byte_string(value_t v)
|
||||
{
|
||||
if (is_byte_string(v))
|
||||
release_assert(is_byte_string(v));
|
||||
return _get_byte_string(v);
|
||||
}
|
||||
|
||||
value_t string_to_value(const char *s)
|
||||
{
|
||||
size_t len = strlen(s);
|
||||
value_t v = make_byte_string(len, '\0');
|
||||
memcpy(_get_byte_string(v)->bytes, s, len);
|
||||
return v;
|
||||
}
|
||||
|
||||
char *value_to_string(value_t v)
|
||||
{
|
||||
byte_string_t *str = get_byte_string(v);
|
||||
char *s = (char*)malloc(str->size + 1);
|
||||
|
||||
memcpy(s, str->bytes, str->size);
|
||||
s[str->size] = '\0';
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
int byte_strcmp(value_t s1, value_t s2)
|
||||
{
|
||||
byte_string_t *str1 = get_byte_string(s1);
|
||||
byte_string_t *str2 = get_byte_string(s2);
|
||||
|
||||
if (str1->size < str2->size)
|
||||
return -1;
|
||||
else if (str1->size > str2->size)
|
||||
return 1;
|
||||
else
|
||||
abort();
|
||||
return memcmp(str1->bytes, str2->bytes, str1->size);
|
||||
}
|
||||
|
||||
value_t make_struct(value_t type, size_t nslots)
|
||||
|
|
@ -198,7 +233,7 @@ value_t make_struct(value_t type, size_t nslots)
|
|||
s->nslots = nslots;
|
||||
|
||||
for (int i = 0; i < nslots; ++i)
|
||||
s->slots[i] = NIL;
|
||||
s->slots[i] = UNDEFINED;
|
||||
|
||||
unregister_gc_root(&type_root);
|
||||
|
||||
|
|
@ -207,10 +242,8 @@ value_t make_struct(value_t type, size_t nslots)
|
|||
|
||||
struct_t *get_struct(value_t v)
|
||||
{
|
||||
if (is_struct(v))
|
||||
release_assert(is_struct(v));
|
||||
return _get_struct(v);
|
||||
else
|
||||
abort();
|
||||
}
|
||||
|
||||
value_t make_weak_box(value_t initial_value)
|
||||
|
|
@ -233,10 +266,8 @@ value_t make_weak_box(value_t initial_value)
|
|||
|
||||
weak_box_t *get_weak_box(value_t v)
|
||||
{
|
||||
if (is_weak_box(v))
|
||||
release_assert(is_weak_box(v));
|
||||
return _get_weak_box(v);
|
||||
else
|
||||
abort();
|
||||
}
|
||||
|
||||
void register_finalizer(value_t value, value_t finalizer)
|
||||
|
|
@ -271,24 +302,48 @@ static inline will_t *_get_will(value_t v)
|
|||
|
||||
static will_t *get_will(value_t v)
|
||||
{
|
||||
if (is_will(v))
|
||||
release_assert(is_will(v));
|
||||
return _get_will(v);
|
||||
else
|
||||
abort();
|
||||
}
|
||||
|
||||
fixnum_t get_fixnum(value_t v)
|
||||
value_t make_float(native_float_t value)
|
||||
{
|
||||
if (is_fixnum(v))
|
||||
return _get_fixnum(v);
|
||||
else
|
||||
abort();
|
||||
float_object_t *obj;
|
||||
|
||||
obj = (float_object_t*)gc_alloc(sizeof(float_object_t));
|
||||
obj->tag = TYPE_TAG_FLOAT;
|
||||
obj->value = value;
|
||||
|
||||
return object_value(obj);
|
||||
}
|
||||
|
||||
native_float_t get_float(value_t v)
|
||||
{
|
||||
release_assert(is_float(v));
|
||||
return _get_float(v);
|
||||
}
|
||||
|
||||
value_t make_builtin_fn(builtin_fn_t *fn)
|
||||
{
|
||||
builtin_fn_object_t *obj;
|
||||
|
||||
obj = (builtin_fn_object_t*)gc_alloc(sizeof(builtin_fn_object_t));
|
||||
obj->tag = TYPE_TAG_BUILTIN;
|
||||
obj->fn = fn;
|
||||
|
||||
return object_value(obj);
|
||||
}
|
||||
|
||||
builtin_fn_t *get_builtin_fn(value_t v)
|
||||
{
|
||||
release_assert(is_builtin_fn(v));
|
||||
return _get_builtin_fn(v);
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
static inline size_t gc_align(size_t nbytes) __attribute__ ((const));
|
||||
static int gc_range_of(void *object) __attribute__ ((const));
|
||||
static int gc_range_of(void *object) __attribute__ ((const,unused));
|
||||
static void transfer_object(value_t *value);
|
||||
static size_t transfer_children(object_t *object);
|
||||
static void _collect_garbage(size_t min_free);
|
||||
|
|
@ -384,6 +439,9 @@ static void transfer_object(value_t *value)
|
|||
|
||||
switch (obj->tag)
|
||||
{
|
||||
case TYPE_TAG_BOX:
|
||||
nbytes = sizeof(box_t);
|
||||
break;
|
||||
case TYPE_TAG_VECTOR:
|
||||
nbytes = VECTOR_BYTES(((const vector_t*)obj)->size);
|
||||
break;
|
||||
|
|
@ -399,8 +457,11 @@ static void transfer_object(value_t *value)
|
|||
case TYPE_TAG_WILL:
|
||||
nbytes = sizeof(will_t);
|
||||
break;
|
||||
case TYPE_TAG_BOX:
|
||||
nbytes = sizeof(box_t);
|
||||
case TYPE_TAG_FLOAT:
|
||||
nbytes = sizeof(float_object_t);
|
||||
break;
|
||||
case TYPE_TAG_BUILTIN:
|
||||
nbytes = sizeof(builtin_fn_object_t);
|
||||
break;
|
||||
default: /* pair */
|
||||
nbytes = sizeof(pair_t);
|
||||
|
|
@ -438,6 +499,12 @@ static size_t transfer_struct(struct_t *s)
|
|||
return STRUCT_BYTES(s->nslots);
|
||||
}
|
||||
|
||||
static size_t transfer_box(box_t *b)
|
||||
{
|
||||
transfer_object(&b->value);
|
||||
return sizeof(box_t);
|
||||
}
|
||||
|
||||
static size_t transfer_pair(pair_t *p)
|
||||
{
|
||||
transfer_object(&p->car);
|
||||
|
|
@ -462,6 +529,8 @@ static size_t transfer_children(object_t *obj)
|
|||
{
|
||||
switch (obj->tag)
|
||||
{
|
||||
case TYPE_TAG_BOX:
|
||||
return transfer_box((box_t*)obj);
|
||||
case TYPE_TAG_VECTOR:
|
||||
return transfer_vector((vector_t*)obj);
|
||||
case TYPE_TAG_BYTESTR:
|
||||
|
|
@ -472,7 +541,10 @@ static size_t transfer_children(object_t *obj)
|
|||
return sizeof(weak_box_t);
|
||||
case TYPE_TAG_WILL:
|
||||
return transfer_will((will_t*)obj);
|
||||
case TYPE_TAG_BOX:
|
||||
case TYPE_TAG_FLOAT:
|
||||
return sizeof(float_object_t);
|
||||
case TYPE_TAG_BUILTIN:
|
||||
return sizeof(builtin_fn_object_t);
|
||||
default: /* pair */
|
||||
return transfer_pair((pair_t*)obj);
|
||||
}
|
||||
|
|
@ -776,4 +848,145 @@ void get_next_finalizer(value_t *value, value_t *finalizer)
|
|||
}
|
||||
}
|
||||
|
||||
void _release_assert(bool expr, const char *str, const char *file, int line)
|
||||
{
|
||||
if (!expr)
|
||||
{
|
||||
fprintf(stderr, "ERROR: Invalid state detected in %s, line %d.\n"
|
||||
"Assertion failed: %s\n",
|
||||
file, line, str);
|
||||
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
void fprint_value(FILE *f, value_t v)
|
||||
{
|
||||
if (v == NIL)
|
||||
{
|
||||
fputs("nil", f);
|
||||
}
|
||||
else if (v == FALSE_VALUE)
|
||||
{
|
||||
fputs("#f", f);
|
||||
}
|
||||
else if (v == TRUE_VALUE)
|
||||
{
|
||||
fputs("#t", f);
|
||||
}
|
||||
else if (v == UNDEFINED)
|
||||
{
|
||||
fputs("#<undefined>", f);
|
||||
}
|
||||
else if (is_fixnum(v))
|
||||
{
|
||||
fprintf(f, "%d", (int)get_fixnum(v));
|
||||
}
|
||||
else if (is_box(v))
|
||||
{
|
||||
fputs("#&", f);
|
||||
fprint_value(f, _get_box(v)->value);
|
||||
}
|
||||
else if (is_pair(v))
|
||||
{
|
||||
fputc('(', f);
|
||||
|
||||
fprint_value(f, _get_pair(v)->car);
|
||||
v = _get_pair(v)->cdr;
|
||||
|
||||
while (is_pair(v))
|
||||
{
|
||||
fputc(' ', f);
|
||||
fprint_value(f, _get_pair(v)->car);
|
||||
v = _get_pair(v)->cdr;
|
||||
}
|
||||
|
||||
if (v != NIL)
|
||||
{
|
||||
fputs(" . ", f);
|
||||
fprint_value(f, v);
|
||||
}
|
||||
|
||||
fputc(')', f);
|
||||
}
|
||||
else if (is_vector(v))
|
||||
{
|
||||
fputs("#(", f);
|
||||
|
||||
for (size_t i = 0; i < _get_vector(v)->size; ++i)
|
||||
{
|
||||
if (i != 0) fputc(' ', f);
|
||||
fprint_value(f, _get_vector(v)->elements[i]);
|
||||
}
|
||||
|
||||
fputc(')', f);
|
||||
}
|
||||
else if (is_byte_string(v))
|
||||
{
|
||||
byte_string_t *str = _get_byte_string(v);
|
||||
|
||||
fputc('"', f);
|
||||
|
||||
for (size_t i = 0; i < str->size; ++i)
|
||||
{
|
||||
int ch = str->bytes[i];
|
||||
|
||||
if (isprint(ch) && (ch != '\\') && (ch != '\"'))
|
||||
fputc(str->bytes[i], f);
|
||||
else
|
||||
fprintf(f, "\\x%.2X", (int)str->bytes[i]);
|
||||
}
|
||||
|
||||
fputc('"', f);
|
||||
}
|
||||
else if (is_struct(v))
|
||||
{
|
||||
struct_t *meta = get_struct(_get_struct(v)->type);
|
||||
byte_string_t *str = get_byte_string(meta->slots[0]);
|
||||
|
||||
fputs("#S(", f);
|
||||
fwrite(str->bytes, str->size, 1, f);
|
||||
|
||||
for (size_t i = 0; i < _get_struct(v)->nslots; ++i)
|
||||
{
|
||||
fputc(' ', f);
|
||||
fprint_value(f, _get_struct(v)->slots[i]);
|
||||
}
|
||||
|
||||
fputc(')', f);
|
||||
}
|
||||
else if (is_weak_box(v))
|
||||
{
|
||||
fputs("#W&", f);
|
||||
fprint_value(f, _get_weak_box(v)->value);
|
||||
}
|
||||
else if (is_float(v))
|
||||
{
|
||||
fprintf(f, "%f", (double)_get_float(v));
|
||||
}
|
||||
else if (is_builtin_fn(v))
|
||||
{
|
||||
fputs("#<builtin>", f);
|
||||
}
|
||||
else
|
||||
{
|
||||
fputs("#<unknown>", f);
|
||||
}
|
||||
}
|
||||
|
||||
void fprint_gc_stats(FILE *f)
|
||||
{
|
||||
const double total_time = gc_stats.total_ns / 1.0e9;
|
||||
const double max_time = gc_stats.max_ns / 1.0e9;
|
||||
|
||||
fprintf(f, "%lld bytes freed in %0.6f sec => %0.3f MB/sec. (%d GCs.)\n",
|
||||
gc_stats.total_freed,
|
||||
total_time,
|
||||
(gc_stats.total_freed / total_time) / (1024*1024),
|
||||
gc_stats.collections);
|
||||
|
||||
fprintf(f, "Max GC time was %0.6f sec, avg. %0.6f sec; peak heap size was %d bytes.\n",
|
||||
max_time, (total_time / gc_stats.collections), gc_stats.high_water);
|
||||
}
|
||||
|
||||
/* vim:set sw=2 expandtab: */
|
||||
|
|
|
|||
130
gc.h
130
gc.h
|
|
@ -13,8 +13,34 @@
|
|||
# define debug(printf_args) ((void)0)
|
||||
#endif
|
||||
|
||||
/* Like assert(), but for things we want to check even in release builds. */
|
||||
/* More informative than a simple "if (!x) abort();" statement. */
|
||||
#define release_assert(expr) ((void)_release_assert((expr), #expr, __FILE__, __LINE__))
|
||||
|
||||
/* Evaluates to false, but with an expression that conveys what went wrong. */
|
||||
#define NOTREACHED(msg) 0
|
||||
|
||||
typedef uintptr_t value_t;
|
||||
typedef intptr_t fixnum_t;
|
||||
typedef double native_float_t;
|
||||
|
||||
#if INTPTR_MAX - 0 == 0
|
||||
/* The INTPTR_ macros are defined, but not given values. */
|
||||
# undef INTPTR_MIN
|
||||
# undef INTPTR_MAX
|
||||
# define INTPTR_MIN INT32_MIN
|
||||
# define INTPTR_MAX INT32_MAX
|
||||
#endif
|
||||
|
||||
#define FIXNUM_MIN (INTPTR_MIN/2)
|
||||
#define FIXNUM_MAX (INTPTR_MAX/2)
|
||||
|
||||
/* Builtins replace the normal run_byte_code() and perform_tail_call() steps.
|
||||
* The argv, k, and ctx inputs can be found in the state fields, and should be
|
||||
* updated as necessary (particularly argv) before the builtin returns. The
|
||||
* 'lambda' field will refer to the builtin itself, and in1-in3 are all free. */
|
||||
struct interp_state;
|
||||
typedef void (builtin_fn_t)(struct interp_state *state);
|
||||
|
||||
/* NIL: 00000000 00000000 00000000 00000000 */
|
||||
/* Object: aaaaaaaa aaaaaaaa aaaaaaaa aaaaaa00 (where aa... >= 1024) */
|
||||
|
|
@ -40,6 +66,8 @@ typedef intptr_t fixnum_t;
|
|||
#define TYPE_TAG_STRUCT TYPE_TAG(3)
|
||||
#define TYPE_TAG_WEAK_BOX TYPE_TAG(4)
|
||||
#define TYPE_TAG_WILL TYPE_TAG(5)
|
||||
#define TYPE_TAG_FLOAT TYPE_TAG(6)
|
||||
#define TYPE_TAG_BUILTIN TYPE_TAG(7)
|
||||
|
||||
#define CAR(x) (get_pair(x)->car)
|
||||
#define CDR(x) (get_pair(x)->cdr)
|
||||
|
|
@ -108,6 +136,18 @@ typedef struct will
|
|||
value_t next;
|
||||
} will_t;
|
||||
|
||||
typedef struct float_object
|
||||
{
|
||||
value_t tag;
|
||||
native_float_t value;
|
||||
} float_object_t;
|
||||
|
||||
typedef struct builtin_fn_object
|
||||
{
|
||||
value_t tag;
|
||||
builtin_fn_t *fn;
|
||||
} builtin_fn_object_t;
|
||||
|
||||
typedef struct gc_root
|
||||
{
|
||||
value_t value;
|
||||
|
|
@ -130,6 +170,11 @@ typedef struct gc_stats
|
|||
|
||||
extern gc_stats_t gc_stats;
|
||||
|
||||
/* Must be #t or #f; for generalized booleans use _get_boolean(). */
|
||||
bool get_boolean(value_t v);
|
||||
|
||||
fixnum_t get_fixnum(value_t v);
|
||||
|
||||
object_t *get_object(value_t v);
|
||||
|
||||
pair_t *get_pair(value_t pair);
|
||||
|
|
@ -144,6 +189,15 @@ vector_t *get_vector(value_t v);
|
|||
value_t make_byte_string(size_t size, int default_value);
|
||||
byte_string_t *get_byte_string(value_t v);
|
||||
|
||||
/* Returns a byte string w/ bytes from 's' (excl. terminating NUL). */
|
||||
value_t string_to_value(const char *s);
|
||||
|
||||
/* Return a new C string which must be free()'d by caller. */
|
||||
char *value_to_string(value_t v);
|
||||
|
||||
/* Like strcmp(), but for byte strings. */
|
||||
int byte_strcmp(value_t s1, value_t s2);
|
||||
|
||||
value_t make_struct(value_t type, size_t nslots);
|
||||
struct_t *get_struct(value_t v);
|
||||
|
||||
|
|
@ -157,7 +211,11 @@ bool are_finalizers_pending(void);
|
|||
/* If *value == #f on return there are no more finalizers. */
|
||||
void get_next_finalizer(value_t *value, value_t *finalizer);
|
||||
|
||||
fixnum_t get_fixnum(value_t v);
|
||||
value_t make_float(native_float_t value);
|
||||
native_float_t get_float(value_t v);
|
||||
|
||||
value_t make_builtin_fn(builtin_fn_t *fn);
|
||||
builtin_fn_t *get_builtin_fn(value_t v);
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
|
|
@ -166,14 +224,9 @@ static inline bool is_nil(value_t v)
|
|||
return v == NIL;
|
||||
}
|
||||
|
||||
static inline bool is_false(value_t v)
|
||||
static inline value_t boolean_value(bool b)
|
||||
{
|
||||
return v == FALSE_VALUE;
|
||||
}
|
||||
|
||||
static inline bool is_true(value_t v)
|
||||
{
|
||||
return v != FALSE_VALUE;
|
||||
return b ? TRUE_VALUE : FALSE_VALUE;
|
||||
}
|
||||
|
||||
static inline bool is_boolean(value_t v)
|
||||
|
|
@ -181,9 +234,24 @@ static inline bool is_boolean(value_t v)
|
|||
return (v == FALSE_VALUE) || (v == TRUE_VALUE);
|
||||
}
|
||||
|
||||
static inline value_t make_boolean(bool b)
|
||||
static inline bool _get_boolean(value_t v)
|
||||
{
|
||||
return b ? TRUE_VALUE : FALSE_VALUE;
|
||||
return v != FALSE_VALUE;
|
||||
}
|
||||
|
||||
static inline value_t fixnum_value(fixnum_t n)
|
||||
{
|
||||
return (value_t)(n << 1) | 1;
|
||||
}
|
||||
|
||||
static inline bool is_fixnum(value_t v)
|
||||
{
|
||||
return (v & 1) != 0;
|
||||
}
|
||||
|
||||
static inline fixnum_t _get_fixnum(value_t n)
|
||||
{
|
||||
return ((fixnum_t)n) >> 1;
|
||||
}
|
||||
|
||||
static inline value_t object_value(void *obj)
|
||||
|
|
@ -247,11 +315,6 @@ static inline vector_t *_get_vector(value_t v)
|
|||
return (vector_t*)_get_object(v);
|
||||
}
|
||||
|
||||
static inline size_t vector_size(value_t v)
|
||||
{
|
||||
return get_vector(v)->size;
|
||||
}
|
||||
|
||||
static inline bool is_byte_string(value_t v)
|
||||
{
|
||||
return is_object(v) && (_get_object(v)->tag == TYPE_TAG_BYTESTR);
|
||||
|
|
@ -262,11 +325,6 @@ static inline byte_string_t *_get_byte_string(value_t v)
|
|||
return (byte_string_t*)_get_object(v);
|
||||
}
|
||||
|
||||
static inline size_t byte_string_size(value_t v)
|
||||
{
|
||||
return get_byte_string(v)->size;
|
||||
}
|
||||
|
||||
static inline bool is_struct(value_t v)
|
||||
{
|
||||
return is_object(v) && (_get_object(v)->tag == TYPE_TAG_STRUCT);
|
||||
|
|
@ -292,19 +350,24 @@ static inline bool is_will(value_t v)
|
|||
return is_object(v) && (_get_object(v)->tag == TYPE_TAG_WILL);
|
||||
}
|
||||
|
||||
static inline bool is_fixnum(value_t v)
|
||||
static inline bool is_float(value_t v)
|
||||
{
|
||||
return (v & 1) != 0;
|
||||
return is_object(v) && (_get_object(v)->tag == TYPE_TAG_FLOAT);
|
||||
}
|
||||
|
||||
static inline value_t make_fixnum(fixnum_t n)
|
||||
static inline native_float_t _get_float(value_t v)
|
||||
{
|
||||
return (value_t)(n << 1) | 1;
|
||||
return ((float_object_t*)_get_object(v))->value;
|
||||
}
|
||||
|
||||
static inline fixnum_t _get_fixnum(value_t n)
|
||||
static inline bool is_builtin_fn(value_t v)
|
||||
{
|
||||
return ((fixnum_t)n) >> 1;
|
||||
return is_object(v) && (_get_object(v)->tag == TYPE_TAG_BUILTIN);
|
||||
}
|
||||
|
||||
static inline builtin_fn_t *_get_builtin_fn(value_t v)
|
||||
{
|
||||
return ((builtin_fn_object_t*)_get_object(v))->fn;
|
||||
}
|
||||
|
||||
void gc_init(size_t min_size, size_t max_size);
|
||||
|
|
@ -314,6 +377,21 @@ void *gc_alloc(size_t nbytes);
|
|||
void collect_garbage(size_t min_free);
|
||||
bool set_gc_enabled(bool enable);
|
||||
|
||||
void fprint_value(FILE *f, value_t v);
|
||||
void fprint_gc_stats(FILE *f);
|
||||
|
||||
static inline void print_value(value_t v)
|
||||
{
|
||||
fprint_value(stdout, v);
|
||||
}
|
||||
|
||||
static inline void print_gc_stats(void)
|
||||
{
|
||||
fprint_gc_stats(stderr);
|
||||
}
|
||||
|
||||
void _release_assert(bool expr, const char *str, const char *file, int line);
|
||||
|
||||
/* To be provided by the main application */
|
||||
void out_of_memory(void) __attribute__ ((noreturn));
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,548 @@
|
|||
#include <ctype.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "gc.h"
|
||||
#include "builtin.h"
|
||||
|
||||
typedef struct reader_state
|
||||
{
|
||||
FILE *file;
|
||||
char ch;
|
||||
gc_root_t ref_alist;
|
||||
gc_root_t weak_list;
|
||||
gc_root_t ref_list;
|
||||
} reader_state_t;
|
||||
|
||||
static value_t read_one_value(reader_state_t *state);
|
||||
static value_t read_special(reader_state_t *state);
|
||||
static value_t read_list(reader_state_t *state);
|
||||
static value_t read_number(reader_state_t *state);
|
||||
static value_t read_string(reader_state_t *state);
|
||||
|
||||
static value_t read_box(reader_state_t *state);
|
||||
static value_t read_vector(reader_state_t *state);
|
||||
static value_t read_struct(reader_state_t *state);
|
||||
static value_t read_weak_box(reader_state_t *state);
|
||||
|
||||
static value_t read_definition(reader_state_t *state);
|
||||
static value_t read_backref(reader_state_t *state);
|
||||
|
||||
static void next_char(reader_state_t *state);
|
||||
|
||||
static value_t make_placeholder(reader_state_t *state, fixnum_t ref);
|
||||
static value_t get_placeholder(reader_state_t *state, fixnum_t ref);
|
||||
static value_t patch_placeholders(reader_state_t *state, value_t v);
|
||||
static void set_placeholder(value_t place, value_t value);
|
||||
static void tree_replace(value_t *in, value_t oldval, value_t newval);
|
||||
|
||||
value_t read_value(FILE *f)
|
||||
{
|
||||
reader_state_t state;
|
||||
value_t result;
|
||||
|
||||
register_gc_root(&state.ref_alist, NIL);
|
||||
register_gc_root(&state.weak_list, NIL);
|
||||
register_gc_root(&state.ref_list, NIL);
|
||||
|
||||
state.file = f;
|
||||
next_char(&state);
|
||||
result = read_one_value(&state);
|
||||
ungetc(state.ch, f);
|
||||
|
||||
result = patch_placeholders(&state, result);
|
||||
|
||||
unregister_gc_root(&state.ref_list);
|
||||
unregister_gc_root(&state.weak_list);
|
||||
unregister_gc_root(&state.ref_alist);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static value_t read_one_value(reader_state_t *state)
|
||||
{
|
||||
while (isspace(state->ch))
|
||||
next_char(state);
|
||||
|
||||
release_assert(state->ch != EOF);
|
||||
|
||||
switch (state->ch)
|
||||
{
|
||||
case '#':
|
||||
return read_special(state);
|
||||
case '(':
|
||||
return read_list(state);
|
||||
case '0' ... '9':
|
||||
return read_number(state);
|
||||
case '\"':
|
||||
return read_string(state);
|
||||
default:
|
||||
release_assert(NOTREACHED("Unexpected character in input."));
|
||||
return UNDEFINED;
|
||||
}
|
||||
}
|
||||
|
||||
bool issymbol(int ch)
|
||||
{
|
||||
switch (ch)
|
||||
{
|
||||
case 'A' ... 'Z':
|
||||
case 'a' ... 'z':
|
||||
case '0' ... '9':
|
||||
case '!': case '$': case '%':
|
||||
case '&': case '*': case '+':
|
||||
case '-': case '/': case '<':
|
||||
case '=': case '>': case '?':
|
||||
case '@': case '\\': case '^':
|
||||
case '_': case '|': case '~':
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static value_t read_special(reader_state_t *state)
|
||||
{
|
||||
next_char(state);
|
||||
release_assert(state->ch != EOF);
|
||||
|
||||
switch (state->ch)
|
||||
{
|
||||
case 'F':
|
||||
case 'f':
|
||||
next_char(state);
|
||||
release_assert(!issymbol(state->ch));
|
||||
return FALSE_VALUE;
|
||||
case 'T':
|
||||
case 't':
|
||||
next_char(state);
|
||||
release_assert(!issymbol(state->ch));
|
||||
return TRUE_VALUE;
|
||||
case '&':
|
||||
return read_box(state);
|
||||
case '(':
|
||||
return read_vector(state);
|
||||
case 'S':
|
||||
case 's':
|
||||
next_char(state);
|
||||
release_assert(state->ch == '(');
|
||||
return read_struct(state);
|
||||
case 'W':
|
||||
case 'w':
|
||||
next_char(state);
|
||||
release_assert(state->ch == '&');
|
||||
return read_weak_box(state);
|
||||
case '0' ... '9':
|
||||
return read_definition(state);
|
||||
case '=':
|
||||
return read_backref(state);
|
||||
default:
|
||||
release_assert(NOTREACHED("Invalid character in special value."));
|
||||
return UNDEFINED;
|
||||
}
|
||||
}
|
||||
|
||||
static void reverse_list(value_t *list, value_t newcdr)
|
||||
{
|
||||
value_t lst = *list;
|
||||
|
||||
while (is_pair(lst))
|
||||
{
|
||||
value_t temp = _get_pair(lst)->cdr;
|
||||
_get_pair(lst)->cdr = newcdr;
|
||||
newcdr = lst;
|
||||
lst = temp;
|
||||
}
|
||||
|
||||
*list = newcdr;
|
||||
}
|
||||
|
||||
static value_t read_list(reader_state_t *state)
|
||||
{
|
||||
gc_root_t list_root;
|
||||
bool done = false;
|
||||
|
||||
register_gc_root(&list_root, NIL);
|
||||
next_char(state);
|
||||
|
||||
while (!done)
|
||||
{
|
||||
while (isspace(state->ch))
|
||||
next_char(state);
|
||||
|
||||
release_assert(state->ch != EOF);
|
||||
|
||||
switch (state->ch)
|
||||
{
|
||||
case '.':
|
||||
{
|
||||
release_assert(!is_nil(list_root.value));
|
||||
next_char(state);
|
||||
value_t temp = read_one_value(state);
|
||||
reverse_list(&list_root.value, temp);
|
||||
|
||||
while (isspace(state->ch))
|
||||
next_char(state);
|
||||
|
||||
release_assert(state->ch == ')');
|
||||
next_char(state);
|
||||
|
||||
done = true;
|
||||
}
|
||||
break;
|
||||
case ')':
|
||||
{
|
||||
reverse_list(&list_root.value, NIL);
|
||||
next_char(state);
|
||||
done = true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
value_t temp;
|
||||
list_root.value = cons(UNDEFINED, list_root.value);
|
||||
temp = read_one_value(state);
|
||||
_CAR(list_root.value) = temp;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
unregister_gc_root(&list_root);
|
||||
|
||||
return list_root.value;
|
||||
}
|
||||
|
||||
static value_t read_number(reader_state_t *state)
|
||||
{
|
||||
fixnum_t num = 0;
|
||||
|
||||
while (isdigit(state->ch))
|
||||
{
|
||||
release_assert(num <= (FIXNUM_MAX/10));
|
||||
num *= 10;
|
||||
num += (state->ch - '0');
|
||||
next_char(state);
|
||||
}
|
||||
|
||||
return fixnum_value(num);
|
||||
}
|
||||
|
||||
static value_t read_string(reader_state_t *state)
|
||||
{
|
||||
char *buffer = (char*)malloc(128);
|
||||
size_t buffer_size = 128;
|
||||
size_t length = 0;
|
||||
value_t value;
|
||||
|
||||
release_assert(buffer != NULL);
|
||||
|
||||
next_char(state);
|
||||
|
||||
while (state->ch != '"')
|
||||
{
|
||||
bool skip_whitespace = false;
|
||||
char ch;
|
||||
|
||||
if ((buffer_size - length) < 2)
|
||||
{
|
||||
release_assert(buffer_size <= INT32_MAX / 3);
|
||||
buffer_size = (3 * buffer_size) / 2;
|
||||
buffer = realloc(buffer, buffer_size);
|
||||
release_assert(buffer != NULL);
|
||||
}
|
||||
|
||||
ch = state->ch;
|
||||
next_char(state);
|
||||
|
||||
if (ch == '\\')
|
||||
{
|
||||
switch (state->ch)
|
||||
{
|
||||
case 'o':
|
||||
next_char(state);
|
||||
release_assert(('0' <= state->ch) && (state->ch <= '7'));
|
||||
/* fall through */
|
||||
case '0' ... '7':
|
||||
ch = 0;
|
||||
|
||||
/* One to three octal digits */
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
ch = 8 * ch + (state->ch - '0');
|
||||
next_char(state);
|
||||
if ((state->ch < '0') || (state->ch > '7'))
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'X':
|
||||
case 'x':
|
||||
ch = 0;
|
||||
|
||||
next_char(state);
|
||||
release_assert(isxdigit(state->ch));
|
||||
|
||||
/* One or two hex digits */
|
||||
for (int i = 0; i < 2; ++i)
|
||||
{
|
||||
int n = isdigit(state->ch)
|
||||
? (state->ch - '0')
|
||||
: (10 + toupper(state->ch) - 'A');
|
||||
|
||||
ch = 16 * ch + n;
|
||||
next_char(state);
|
||||
|
||||
if (!isxdigit(state->ch))
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '\v':
|
||||
case '\n':
|
||||
skip_whitespace = true;
|
||||
break;
|
||||
case '\\': ch = '\\'; next_char(state); break;
|
||||
case '\'': ch = '\''; next_char(state); break;
|
||||
case '\"': ch = '\"'; next_char(state); break;
|
||||
case 'a': ch = '\a'; next_char(state); break;
|
||||
case 'b': ch = '\b'; next_char(state); break;
|
||||
case 'f': ch = '\f'; next_char(state); break;
|
||||
case 'n': ch = '\n'; next_char(state); break;
|
||||
case 'r': ch = '\r'; next_char(state); break;
|
||||
case 't': ch = '\t'; next_char(state); break;
|
||||
case 'v': ch = '\v'; next_char(state); break;
|
||||
default:
|
||||
release_assert(NOTREACHED("Invalid escape sequence in string."));
|
||||
ch = '#';
|
||||
next_char(state);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (skip_whitespace)
|
||||
{
|
||||
do {
|
||||
next_char(state);
|
||||
} while (isspace(state->ch) && (state->ch != '\n'));
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer[length++] = ch;
|
||||
}
|
||||
}
|
||||
|
||||
next_char(state);
|
||||
|
||||
buffer[length] = '\0';
|
||||
value = make_byte_string(length, '\0');
|
||||
memcpy(_get_byte_string(value)->bytes, buffer, length);
|
||||
free(buffer);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static value_t read_box(reader_state_t *state)
|
||||
{
|
||||
gc_root_t root;
|
||||
value_t v;
|
||||
|
||||
next_char(state);
|
||||
|
||||
register_gc_root(&root, make_box(UNDEFINED));
|
||||
|
||||
v = read_one_value(state);
|
||||
_get_box(root.value)->value = v;
|
||||
|
||||
unregister_gc_root(&root);
|
||||
|
||||
return root.value;
|
||||
}
|
||||
|
||||
static value_t read_vector(reader_state_t *state)
|
||||
{
|
||||
gc_root_t list_root;
|
||||
gc_root_t vec_root;
|
||||
size_t length = 0;
|
||||
value_t v;
|
||||
|
||||
register_gc_root(&list_root, read_list(state));
|
||||
|
||||
for (value_t v = list_root.value; !is_nil(v); v = CDR(v))
|
||||
++length;
|
||||
|
||||
register_gc_root(&vec_root, make_vector(length, UNDEFINED));
|
||||
|
||||
v = list_root.value;
|
||||
for (size_t i = 0; i < length; ++i, v = _CDR(v))
|
||||
_get_vector(vec_root.value)->elements[i] = _CAR(v);
|
||||
|
||||
unregister_gc_root(&list_root);
|
||||
unregister_gc_root(&vec_root);
|
||||
|
||||
return vec_root.value;
|
||||
}
|
||||
|
||||
static value_t read_struct(reader_state_t *state)
|
||||
{
|
||||
gc_root_t list_root;
|
||||
gc_root_t struct_root;
|
||||
size_t slots = 0;
|
||||
value_t v;
|
||||
|
||||
register_gc_root(&list_root, read_list(state));
|
||||
|
||||
for (value_t v = CDR(list_root.value); !is_nil(v); v = CDR(v))
|
||||
++slots;
|
||||
|
||||
register_gc_root(&struct_root, make_struct(_CAR(list_root.value), slots));
|
||||
|
||||
v = _CDR(list_root.value);
|
||||
for (size_t i = 0; i < slots; ++i, v = _CDR(v))
|
||||
_get_struct(struct_root.value)->slots[i] = _CAR(v);
|
||||
|
||||
unregister_gc_root(&list_root);
|
||||
unregister_gc_root(&struct_root);
|
||||
|
||||
return struct_root.value;
|
||||
}
|
||||
|
||||
static value_t read_weak_box(reader_state_t *state)
|
||||
{
|
||||
gc_root_t box_root;
|
||||
gc_root_t value_root;
|
||||
|
||||
next_char(state);
|
||||
|
||||
register_gc_root(&box_root, make_weak_box(UNDEFINED));
|
||||
register_gc_root(&value_root, read_one_value(state));
|
||||
|
||||
_get_weak_box(box_root.value)->value = value_root.value;
|
||||
state->weak_list.value = cons(value_root.value, state->weak_list.value);
|
||||
|
||||
unregister_gc_root(&box_root);
|
||||
unregister_gc_root(&value_root);
|
||||
|
||||
return box_root.value;
|
||||
}
|
||||
|
||||
static value_t read_definition(reader_state_t *state)
|
||||
{
|
||||
fixnum_t ref = get_fixnum(read_number(state));
|
||||
gc_root_t place_root;
|
||||
value_t v;
|
||||
|
||||
release_assert(state->ch == '=');
|
||||
next_char(state);
|
||||
|
||||
register_gc_root(&place_root, make_placeholder(state, ref));
|
||||
v = read_one_value(state);
|
||||
set_placeholder(place_root.value, v);
|
||||
unregister_gc_root(&place_root);
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
static value_t read_backref(reader_state_t *state)
|
||||
{
|
||||
next_char(state);
|
||||
release_assert(state->ch != EOF);
|
||||
|
||||
if (state->ch == '"')
|
||||
{
|
||||
char *name = value_to_string(read_string(state));
|
||||
value_t bi = lookup_builtin(name);
|
||||
free(name);
|
||||
return bi;
|
||||
}
|
||||
else
|
||||
{
|
||||
return get_placeholder(state, get_fixnum(read_number(state)));
|
||||
}
|
||||
}
|
||||
|
||||
static value_t make_placeholder(reader_state_t *state, fixnum_t ref)
|
||||
{
|
||||
state->ref_alist.value = cons(UNDEFINED, state->ref_alist.value);
|
||||
state->ref_alist.value = cons(fixnum_value(ref), state->ref_alist.value);
|
||||
return state->ref_alist.value;
|
||||
}
|
||||
|
||||
static value_t get_placeholder(reader_state_t *state, fixnum_t ref)
|
||||
{
|
||||
value_t refval = fixnum_value(ref);
|
||||
value_t item = state->ref_alist.value;
|
||||
|
||||
while (!is_nil(item))
|
||||
{
|
||||
if (_CAR(item) == refval)
|
||||
return item;
|
||||
else
|
||||
item = _CDDR(item);
|
||||
}
|
||||
|
||||
release_assert(NOTREACHED("Back-reference without definition!"));
|
||||
return UNDEFINED;
|
||||
}
|
||||
|
||||
static value_t patch_placeholders(reader_state_t *state, value_t v)
|
||||
{
|
||||
value_t item = state->ref_alist.value;
|
||||
|
||||
while (!is_nil(item))
|
||||
{
|
||||
tree_replace(&v, item, _CADR(item));
|
||||
item = _CDDR(item);
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
static void set_placeholder(value_t place, value_t value)
|
||||
{
|
||||
CAR(CDR(place)) = value;
|
||||
}
|
||||
|
||||
static void tree_replace(value_t *in, value_t oldval, value_t newval)
|
||||
{
|
||||
if (*in == oldval)
|
||||
{
|
||||
*in = newval;
|
||||
}
|
||||
else if (is_box(*in))
|
||||
{
|
||||
tree_replace(&_get_box(*in)->value, oldval, newval);
|
||||
}
|
||||
else if (is_pair(*in))
|
||||
{
|
||||
tree_replace(&_get_pair(*in)->car, oldval, newval);
|
||||
tree_replace(&_get_pair(*in)->cdr, oldval, newval);
|
||||
}
|
||||
else if (is_vector(*in))
|
||||
{
|
||||
for (size_t i = 0; i < _get_vector(*in)->size; ++i)
|
||||
{
|
||||
tree_replace(&_get_vector(*in)->elements[i], oldval, newval);
|
||||
}
|
||||
}
|
||||
else if (is_struct(*in))
|
||||
{
|
||||
tree_replace(&_get_struct(*in)->type, oldval, newval);
|
||||
|
||||
for (size_t i = 0; i < _get_struct(*in)->nslots; ++i)
|
||||
tree_replace(&_get_struct(*in)->slots[i], oldval, newval);
|
||||
}
|
||||
else if (is_weak_box(*in))
|
||||
{
|
||||
tree_replace(&_get_weak_box(*in)->value, oldval, newval);
|
||||
}
|
||||
}
|
||||
|
||||
static void next_char(reader_state_t *state)
|
||||
{
|
||||
state->ch = fgetc(state->file);
|
||||
}
|
||||
|
||||
/* vim:set sw=2 expandtab: */
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
#ifndef READER_H_bc8f9bf546e3914a72851703b38326d2
|
||||
#define READER_H_bc8f9bf546e3914a72851703b38326d2
|
||||
|
||||
#include "gc.h"
|
||||
|
||||
value_t read_value(FILE *f);
|
||||
|
||||
#endif
|
||||
/* vim:set sw=2 expandtab: */
|
||||
165
rosella.c
165
rosella.c
|
|
@ -1,6 +1,5 @@
|
|||
#include <sys/time.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
|
@ -11,12 +10,12 @@
|
|||
#include "gc.h"
|
||||
#include "builtin.h"
|
||||
#include "interp.h"
|
||||
#include "reader.h"
|
||||
|
||||
static void test_builtins(void);
|
||||
static void test_weak_boxes_and_wills(void);
|
||||
static void test_garbage_collection(bool keep_going);
|
||||
static void print_value(value_t v);
|
||||
static void print_gc_stats(void);
|
||||
static void test_reader(void);
|
||||
|
||||
static inline void comma(void) { fputs(", ", stdout); }
|
||||
static inline void nl(void) { putchar('\n'); }
|
||||
|
|
@ -39,6 +38,7 @@ int main(int argc, char **argv)
|
|||
|
||||
test_builtins();
|
||||
test_weak_boxes_and_wills();
|
||||
test_reader();
|
||||
test_garbage_collection(argc > 1);
|
||||
|
||||
return 0;
|
||||
|
|
@ -63,19 +63,19 @@ static void test_weak_boxes_and_wills(void)
|
|||
{
|
||||
gc_root_t box_root, tmp_root;
|
||||
|
||||
register_gc_root(&box_root, NIL);
|
||||
register_gc_root(&tmp_root, NIL);
|
||||
register_gc_root(&box_root, UNDEFINED);
|
||||
register_gc_root(&tmp_root, UNDEFINED);
|
||||
|
||||
tmp_root.value = cons(make_fixnum(1), cons(make_fixnum(2), NIL));
|
||||
tmp_root.value = cons(fixnum_value(1), cons(fixnum_value(2), NIL));
|
||||
box_root.value = make_weak_box(tmp_root.value);
|
||||
|
||||
register_finalizer(tmp_root.value, make_fixnum(10));
|
||||
register_finalizer(tmp_root.value, fixnum_value(10));
|
||||
print_weak_box_results(box_root.value);
|
||||
|
||||
collect_garbage(0);
|
||||
print_weak_box_results(box_root.value);
|
||||
|
||||
tmp_root.value = NIL;
|
||||
tmp_root.value = UNDEFINED;
|
||||
print_weak_box_results(box_root.value);
|
||||
|
||||
collect_garbage(0);
|
||||
|
|
@ -83,6 +83,7 @@ static void test_weak_boxes_and_wills(void)
|
|||
|
||||
collect_garbage(0);
|
||||
print_weak_box_results(box_root.value);
|
||||
nl();
|
||||
|
||||
unregister_gc_root(&box_root);
|
||||
unregister_gc_root(&tmp_root);
|
||||
|
|
@ -100,13 +101,13 @@ static void test_garbage_collection(bool keep_going)
|
|||
int r = rand() & 0xffff;
|
||||
|
||||
if (r == 0)
|
||||
root.value = make_fixnum(rand());
|
||||
root.value = fixnum_value(rand());
|
||||
else
|
||||
{
|
||||
switch (r & 7)
|
||||
{
|
||||
case 0:
|
||||
root.value = cons(make_fixnum(rand()), root.value);
|
||||
root.value = cons(fixnum_value(rand()), root.value);
|
||||
break;
|
||||
case 1:
|
||||
root.value = cons(root.value, make_byte_string(256, '\0'));
|
||||
|
|
@ -115,7 +116,7 @@ static void test_garbage_collection(bool keep_going)
|
|||
root.value = make_box(root.value);
|
||||
break;
|
||||
case 3:
|
||||
root.value = cons(root.value, cons(make_fixnum(-1), NIL));
|
||||
root.value = cons(root.value, cons(fixnum_value(-1), NIL));
|
||||
get_pair(get_pair(root.value)->cdr)->cdr = root.value;
|
||||
break;
|
||||
case 4:
|
||||
|
|
@ -123,8 +124,8 @@ static void test_garbage_collection(bool keep_going)
|
|||
case 6:
|
||||
case 7:
|
||||
{
|
||||
value_t s = make_struct(NIL, 4);
|
||||
_get_struct(s)->slots[r & 3] = root.value;
|
||||
value_t s = make_vector(4, FALSE_VALUE);
|
||||
_get_vector(s)->elements[r & 3] = root.value;
|
||||
root.value = s;
|
||||
}
|
||||
break;
|
||||
|
|
@ -151,140 +152,14 @@ static void test_garbage_collection(bool keep_going)
|
|||
unregister_gc_root(&root);
|
||||
}
|
||||
|
||||
static bool all_print(const char *s, size_t len)
|
||||
static void test_reader(void)
|
||||
{
|
||||
for (size_t i = 0; i < len; ++i)
|
||||
{
|
||||
if (s[i] == '"' || !isprint(s[i]))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
value_t v;
|
||||
|
||||
static void print_value(value_t v)
|
||||
{
|
||||
if (v == NIL)
|
||||
{
|
||||
fputs("nil", stdout);
|
||||
}
|
||||
else if (v == FALSE_VALUE)
|
||||
{
|
||||
fputs("#f", stdout);
|
||||
}
|
||||
else if (v == TRUE_VALUE)
|
||||
{
|
||||
fputs("#t", stdout);
|
||||
}
|
||||
else if (v == UNDEFINED)
|
||||
{
|
||||
fputs("#<undefined>", stdout);
|
||||
}
|
||||
else if (is_fixnum(v))
|
||||
{
|
||||
printf("%d", (int)get_fixnum(v));
|
||||
}
|
||||
else if (is_box(v))
|
||||
{
|
||||
fputs("#&", stdout);
|
||||
print_value(_get_box(v)->value);
|
||||
}
|
||||
else if (is_pair(v))
|
||||
{
|
||||
putchar('(');
|
||||
|
||||
print_value(_get_pair(v)->car);
|
||||
v = _get_pair(v)->cdr;
|
||||
|
||||
while (is_pair(v))
|
||||
{
|
||||
putchar(' ');
|
||||
print_value(_get_pair(v)->car);
|
||||
v = _get_pair(v)->cdr;
|
||||
}
|
||||
|
||||
if (v != NIL)
|
||||
{
|
||||
fputs(" . ", stdout);
|
||||
print_value(v);
|
||||
}
|
||||
|
||||
putchar(')');
|
||||
}
|
||||
else if (is_vector(v))
|
||||
{
|
||||
fputs("#(", stdout);
|
||||
|
||||
for (size_t i = 0; i < _get_vector(v)->size; ++i)
|
||||
{
|
||||
if (i != 0) putchar(' ');
|
||||
print_value(_get_vector(v)->elements[i]);
|
||||
}
|
||||
|
||||
putchar(')');
|
||||
}
|
||||
else if (is_byte_string(v))
|
||||
{
|
||||
byte_string_t *str = _get_byte_string(v);
|
||||
|
||||
if (all_print(str->bytes, str->size))
|
||||
{
|
||||
putchar('"');
|
||||
fwrite(str->bytes, str->size, 1, stdout);
|
||||
putchar('"');
|
||||
}
|
||||
else
|
||||
{
|
||||
fputs("#B(", stdout);
|
||||
|
||||
for (size_t i = 0; i < str->size; ++i)
|
||||
{
|
||||
if (i != 0) putchar(' ');
|
||||
printf("%d", (int)str->bytes[i]);
|
||||
}
|
||||
|
||||
putchar(')');
|
||||
}
|
||||
}
|
||||
else if (is_struct(v))
|
||||
{
|
||||
struct_t *meta = get_struct(_get_struct(v)->type);
|
||||
byte_string_t *str = get_byte_string(meta->slots[0]);
|
||||
|
||||
fputs("#S(", stdout);
|
||||
fwrite(str->bytes, str->size, 1, stdout);
|
||||
|
||||
for (size_t i = 0; i < _get_struct(v)->nslots; ++i)
|
||||
{
|
||||
putchar(' ');
|
||||
print_value(_get_struct(v)->slots[i]);
|
||||
}
|
||||
|
||||
putchar(')');
|
||||
}
|
||||
else if (is_weak_box(v))
|
||||
{
|
||||
fputs("#W&", stdout);
|
||||
print_value(_get_weak_box(v)->value);
|
||||
}
|
||||
else
|
||||
{
|
||||
fputs("#<unknown>", stdout);
|
||||
}
|
||||
}
|
||||
|
||||
static void print_gc_stats(void)
|
||||
{
|
||||
const double total_time = gc_stats.total_ns / 1.0e9;
|
||||
const double max_time = gc_stats.max_ns / 1.0e9;
|
||||
|
||||
fprintf(stderr, "%lld bytes freed in %0.6f sec => %0.3f MB/sec. (%d GCs.)\n",
|
||||
gc_stats.total_freed,
|
||||
total_time,
|
||||
(gc_stats.total_freed / total_time) / (1024*1024),
|
||||
gc_stats.collections);
|
||||
|
||||
fprintf(stderr, "Max GC time was %0.6f sec, avg. %0.6f sec; peak heap size was %d bytes.\n",
|
||||
max_time, (total_time / gc_stats.collections), gc_stats.high_water);
|
||||
do {
|
||||
v = read_value(stdin);
|
||||
print_value(v); nl(); nl();
|
||||
} while (v != NIL);
|
||||
}
|
||||
|
||||
/* vim:set sw=2 expandtab: */
|
||||
|
|
|
|||
Loading…
Reference in New Issue