diff --git a/Makefile b/Makefile index 1453ba0..1b53fb9 100644 --- a/Makefile +++ b/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 diff --git a/builtin.c b/builtin.c index 9444f37..02b9ab2 100644 --- a/builtin.c +++ b/builtin.c @@ -2,120 +2,28 @@ #include #include #include -#include #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: */ diff --git a/builtin.h b/builtin.h index daea672..55e821d 100644 --- a/builtin.h +++ b/builtin.h @@ -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 diff --git a/gc.c b/gc.c index 9c84bf4..5edfa7b 100644 --- a/gc.c +++ b/gc.c @@ -1,6 +1,7 @@ #define _POSIX_C_SOURCE 199309L #include +#include #include #include #include @@ -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)) - return _get_object(v); - else - abort(); + release_assert(is_object(v)); + return _get_object(v); } /* 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)) - return _get_pair(v); - else - abort(); + release_assert(is_pair(v)); + return _get_pair(v); } 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)) - return _get_box(v); - else - abort(); + release_assert(is_box(v)); + return _get_box(v); } 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)) - return _get_vector(v); - else - abort(); + release_assert(is_vector(v)); + return _get_vector(v); } 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)) - return _get_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)) - return _get_struct(v); - else - abort(); + release_assert(is_struct(v)); + return _get_struct(v); } 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)) - return _get_weak_box(v); - else - abort(); + release_assert(is_weak_box(v)); + return _get_weak_box(v); } 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)) - return _get_will(v); - else - abort(); + release_assert(is_will(v)); + return _get_will(v); } -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("#", 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("#", f); + } + else + { + fputs("#", 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: */ diff --git a/gc.h b/gc.h index acaa6e8..36c7a78 100644 --- a/gc.h +++ b/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)); diff --git a/reader.c b/reader.c new file mode 100644 index 0000000..bc5b4dc --- /dev/null +++ b/reader.c @@ -0,0 +1,548 @@ +#include +#include +#include +#include +#include + +#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: */ diff --git a/reader.h b/reader.h new file mode 100644 index 0000000..3acf57e --- /dev/null +++ b/reader.h @@ -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: */ diff --git a/rosella.c b/rosella.c index f455805..49c4688 100644 --- a/rosella.c +++ b/rosella.c @@ -1,6 +1,5 @@ #include -#include #include #include #include @@ -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("#", 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("#", 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: */