diff --git a/Makefile b/Makefile index 324fa66a..f8d5bd40 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ ###################################################### ##### Set global variables for all gst Makefiles ##### ###################################################### -CFLAGS=-std=c99 -Wall -Wextra -Wpedantic -g -I./include +CFLAGS=-std=c99 -Wall -Wextra -Wpedantic -I./include -g PREFIX=/usr/local GST_TARGET=client/gst GST_CORELIB=core/libgst.a diff --git a/client/main.c b/client/main.c index e33da6ed..1959a4e4 100644 --- a/client/main.c +++ b/client/main.c @@ -6,96 +6,95 @@ #include #include -/* A simple repl for debugging */ -void debug_repl(FILE *in, FILE *out) { +/* Parse a file and execute it */ +int debug_run(Gst *vm, FILE *in) { char buffer[1024] = {0}; - const char * reader = buffer; + const char *reader = buffer; GstValue func; - Gst vm; GstParser p; GstCompiler c; + gst_parser(&p, vm); + + /* Create do struct */ + GstArray *arr = gst_array(vm, 10); + gst_array_push(vm, arr, gst_string_cv(vm, "do")); + + /* Get and parse input until we have a full form */ + while (p.status != GST_PARSER_ERROR) { + if (*reader == '\0') { + if (!fgets(buffer, sizeof(buffer), in)) { + break; + } + reader = buffer; + } + reader += gst_parse_cstring(&p, reader); + if (gst_parse_hasvalue(&p)) + gst_array_push(vm, arr, gst_parse_consume(&p)); + } + + /* Turn array into tuple */ + GstValue *tup = gst_tuple_begin(vm, arr->count); + gst_memcpy(tup, arr->data, arr->count * sizeof(GstValue)); + vm->ret.type = GST_TUPLE; + vm->ret.data.tuple = gst_tuple_end(vm, tup); + + /* Check if file read in correctly */ + if (p.error) { + printf("Parse error: %s\n", p.error); + return 1; + } + + /* Check that parser is complete */ + if (p.status != GST_PARSER_FULL && p.status != GST_PARSER_ROOT) { + printf("Unexpected end of source\n"); + return 1; + } + + /* Try to compile generated AST */ + gst_compiler(&c, vm); + func.type = GST_NIL; + gst_compiler_usemodule(&c, "std"); + func.type = GST_FUNCTION; + func.data.function = gst_compiler_compile(&c, vm->ret); + + /* Check for compilation errors */ + if (c.error) { + printf("Compiler error: %s\n", c.error); + return 1; + } + + /* Execute function */ + if (gst_run(vm, func)) { + if (vm->crash) { + printf("VM crash: %s\n", vm->crash); + } else { + printf("VM error: %s\n", (char *)gst_to_string(vm, vm->ret)); + } + return 1; + } + return 0; +} + +int main(int argc, const char **argv) { + + Gst vm; + int status = 0; gst_init(&vm); gst_stl_load(&vm); - for (;;) { + const char *filename; - /* Reset state */ - gst_parser(&p, &vm); + // if (argc > 1) { + // filename = argv[1]; + //} else { + filename = "libs/stl.gst"; + //} - /* Get and parse input until we have a full form */ - while (p.status == GST_PARSER_PENDING) { - /* Get some input if we are done */ - if (*reader == '\0') { - if (out) - fprintf(out, ">> "); - if (!fgets(buffer, sizeof(buffer), in)) { - return; - } - p.index = 0; - reader = buffer; - } - reader += gst_parse_cstring(&p, reader); - } + FILE *f = fopen(filename, "rb"); + status = debug_run(&vm, f); - /* Check for parsing errors */ - if (p.error) { - unsigned i; - if (out) { - fprintf(out, "\n"); - fprintf(out, "%s\n", buffer); - for (i = 1; i < p.index; ++i) { - fprintf(out, " "); - } - fprintf(out, "^\n"); - fprintf(out, "\nParse error: %s\n", p.error); - } - reader = buffer; /* Flush the input buffer */ - buffer[0] = '\0'; - continue; - } - - /* Try to compile generated AST */ - gst_compiler(&c, &vm); - func.type = GST_NIL; - gst_compiler_usemodule(&c, "std"); - gst_compiler_global(&c, "ans", gst_object_get(vm.rootenv, gst_string_cv(&vm, "ans"))); - func.type = GST_FUNCTION; - func.data.function = gst_compiler_compile(&c, p.value); - - /* Check for compilation errors */ - if (c.error) { - if (out) { - fprintf(out, "Compiler error: %s\n", c.error); - } - reader = buffer; - buffer[0] = 0; - continue; - } - - /* Execute function */ - if (gst_run(&vm, func)) { - if (out) { - if (vm.crash) { - fprintf(out, "VM crash: %s\n", vm.crash); - } else { - fprintf(out, "VM error: "); - fprintf(out, "%s\n", (char *)gst_to_string(&vm, vm.ret)); - } - } - reader = buffer; - buffer[0] = 0; - continue; - } else if (out) { - fprintf(out, "%s\n", (char *)gst_to_string(&vm, vm.ret)); - gst_object_put(&vm, vm.rootenv, gst_string_cv(&vm, "ans"), vm.ret); - } - } + gst_deinit(&vm); -} - -int main() { - printf("GST v0.0 repl\nCopyright 2017 Calvin Rose\n"); - debug_repl(stdin, stdout); - return 0; + return status; } diff --git a/core/compile.c b/core/compile.c index 8b22fd4f..86cb6508 100644 --- a/core/compile.c +++ b/core/compile.c @@ -555,9 +555,6 @@ static Slot compile_function(GstCompiler *c, FormOptions opts, const GstValue *f if (opts.resultUnused) return nil_slot(); ret = compiler_get_target(c, opts); subGstScope = compiler_push_scope(c, 0); - /* Check for function documentation - for now just ignore. */ - if (form[current].type == GST_STRING) - ++current; /* Define the function parameters */ if (form[current].type != GST_ARRAY) c_error(c, "expected function arguments array"); @@ -672,6 +669,7 @@ static Slot compile_try(GstCompiler *c, FormOptions opts, const GstValue *form) Slot body; uint16_t errorIndex; uint32_t countAtTry, countTemp, countAtJump; + countAtJump = 0; /* Check argument count */ if (gst_tuple_length(form) < 3 || gst_tuple_length(form) > 4) c_error(c, "try takes either 2 or 3 arguments"); diff --git a/core/gc.c b/core/gc.c index e106a00c..5bff8632 100644 --- a/core/gc.c +++ b/core/gc.c @@ -145,10 +145,8 @@ void gst_mark(Gst *vm, GstValueUnion x, GstType type) { gc_header(x.object)->color = vm->black; gc_header(x.object->data)->color = vm->black; for (i = 0; i < x.object->capacity; i += 2) { - if (x.object->data[i].type != GST_NIL) { - gst_mark_value(vm, x.object->data[i]); - gst_mark_value(vm, x.object->data[i + 1]); - } + gst_mark_value(vm, x.object->data[i]); + gst_mark_value(vm, x.object->data[i + 1]); } if (x.object->parent != NULL) { GstValueUnion temp; @@ -254,6 +252,9 @@ void gst_collect(Gst *vm) { renv.object = vm->rootenv; gst_mark(vm, renv, GST_OBJECT); gst_mark_value(vm, vm->ret); + if (vm->scratch) { + gc_header(vm->scratch)->color = vm->black; + } gst_sweep(vm); vm->nextCollection = 0; } diff --git a/core/ids.c b/core/ids.c index a027129b..c958ec8c 100644 --- a/core/ids.c +++ b/core/ids.c @@ -254,7 +254,7 @@ const GstValue *gst_struct_end(Gst *vm, GstValue *st) { /* Get an item from a struct */ GstValue gst_struct_get(const GstValue *st, GstValue key) { - GstValue *bucket = gst_struct_find(st, key); + const GstValue *bucket = gst_struct_find(st, key); if (!bucket || bucket[0].type == GST_NIL) { GstValue ret; ret.type = GST_NIL; diff --git a/core/parse.c b/core/parse.c index 70582b61..618037cc 100644 --- a/core/parse.c +++ b/core/parse.c @@ -341,6 +341,8 @@ static int string_state(GstParser *p, uint8_t c) { /* Root state of the parser */ static int root_state(GstParser *p, uint8_t c) { + if (is_whitespace(c)) return 1; + p->status = GST_PARSER_PENDING; if (c == ']' || c == ')' || c == '}') { p_error(p, UNEXPECTED_CLOSING_DELIM); return 1; @@ -357,7 +359,6 @@ static int root_state(GstParser *p, uint8_t c) { p->quoteCount++; return 1; } - if (is_whitespace(c)) return 1; if (is_symbol_char(c)) { parser_push(p, PTYPE_TOKEN, c); return 0; @@ -421,7 +422,7 @@ static void dispatch_char(GstParser *p, uint8_t c) { } } /* Dispatch character to state */ - while (!done && p->status == GST_PARSER_PENDING) { + while (!done && (p->status == GST_PARSER_PENDING || p->status == GST_PARSER_ROOT)) { GstParseState *top = parser_peek(p); switch (top->type) { case PTYPE_ROOT: @@ -447,8 +448,8 @@ static void dispatch_char(GstParser *p, uint8_t c) { */ int gst_parse_cstring(GstParser *p, const char *string) { int bytesRead = 0; - p->status = GST_PARSER_PENDING; - while ((p->status == GST_PARSER_PENDING) && (string[bytesRead] != '\0')) { + while ((p->status == GST_PARSER_PENDING || p->status == GST_PARSER_ROOT) + && (string[bytesRead] != '\0')) { dispatch_char(p, string[bytesRead++]); } return bytesRead; @@ -457,14 +458,26 @@ int gst_parse_cstring(GstParser *p, const char *string) { /* Parse a gst string */ int gst_parse_string(GstParser *p, const uint8_t *string) { uint32_t i; - p->status = GST_PARSER_PENDING; for (i = 0; i < gst_string_length(string); ++i) { - if (p->status != GST_PARSER_PENDING) break; + if (p->status != GST_PARSER_PENDING && p->status != GST_PARSER_ROOT) break; dispatch_char(p, string[i]); } return i; } +/* Check if a parser has a value that needs to be handled. If + * so, the parser will not parse any more input until that value + * is consumed. */ +int gst_parse_hasvalue(GstParser *p) { + return p->status == GST_PARSER_FULL; +} + +/* Gets a value from the parser */ +GstValue gst_parse_consume(GstParser *p) { + p->status = GST_PARSER_ROOT; + return p->value; +} + /* Parser initialization (memory allocation) */ void gst_parser(GstParser *p, Gst *vm) { p->vm = vm; @@ -475,7 +488,7 @@ void gst_parser(GstParser *p, Gst *vm) { p->index = 0; p->quoteCount = 0; p->error = NULL; - p->status = GST_PARSER_PENDING; + p->status = GST_PARSER_ROOT; p->value.type = GST_NIL; p->flags = GST_PARSER_FLAG_EXPECTING_COMMENT; parser_push(p, PTYPE_ROOT, ' '); diff --git a/core/serialize.c b/core/serialize.c index 955747d0..9a522164 100644 --- a/core/serialize.c +++ b/core/serialize.c @@ -88,7 +88,7 @@ static const char *gst_deserialize_impl( #define deser_assert(c, e) do{if(!(c))deser_error(e);}while(0) /* Assert enough buffer */ -#define deser_datacheck(len) do{if (end - data < (len)) deser_error(UEB);}while(0) +#define deser_datacheck(len) do{if (end < (data + len)) deser_error(UEB);}while(0) /* Check for enough space to read uint32_t */ #define read_u32(out) do{deser_datacheck(4); (out)=bytes2u32(data); data += 4; }while(0) @@ -485,6 +485,19 @@ const char *gst_serialize_impl( write_byte(x.data.string[i]); } break; + case GST_STRUCT: + write_byte(206); + count = gst_struct_length(x.data.st); + write_u32(count); + for (i = 0; i < gst_struct_capacity(x.data.st); i += 2) { + if (x.data.st[i].type != GST_NIL) { + err = gst_serialize_impl(vm, buffer, visited, nextId, x.data.st[i]); + if (err != NULL) return err; + err = gst_serialize_impl(vm, buffer, visited, nextId, x.data.st[i + 1]); + if (err != NULL) return err; + } + } + break; case GST_BYTEBUFFER: write_byte(207); count = x.data.buffer->count; diff --git a/core/stl.c b/core/stl.c index f19caa83..fc1dad5d 100644 --- a/core/stl.c +++ b/core/stl.c @@ -363,8 +363,10 @@ int gst_stl_print(Gst *vm) { uint32_t len = gst_string_length(string); for (i = 0; i < len; ++i) fputc(string[i], stdout); - fputc('\n', stdout); + if (j < count - 1) + fputc(' ', stdout); } + fputc('\n', stdout); return GST_RETURN_OK; } diff --git a/core/vm.c b/core/vm.c index a0880b6e..58cf1f17 100644 --- a/core/vm.c +++ b/core/vm.c @@ -19,28 +19,16 @@ static const char GST_EXPECTED_FUNCTION[] = "expected function"; static const char GST_EXPECTED_NUMBER_ROP[] = "expected right operand to be number"; static const char GST_EXPECTED_NUMBER_LOP[] = "expected left operand to be number"; -/* Contextual macro to state in function with VM */ -#define GST_STATE_SYNC() do { \ - thread = *vm->thread; \ - stack = thread.data + thread.count; \ -} while (0) - -/* Write local state back to VM */ -#define GST_STATE_WRITE() do { \ - *vm->thread = thread; \ -} while (0) - /* Start running the VM from where it left off. Continue running * until the stack size is smaller than minStackSize. */ static int gst_continue_size(Gst *vm, uint32_t stackBase) { /* VM state */ - GstThread thread; GstValue *stack; GstValue temp, v1, v2; uint16_t *pc; /* Intialize local state */ - GST_STATE_SYNC(); + stack = vm->thread->data + vm->thread->count; pc = gst_frame_pc(stack); /* Main interpreter loop */ @@ -158,9 +146,8 @@ static int gst_continue_size(Gst *vm, uint32_t stackBase) { gst_error(vm, GST_EXPECTED_FUNCTION); if (gst_frame_env(stack) == NULL) { gst_frame_env(stack) = gst_alloc(vm, sizeof(GstFuncEnv)); - *vm->thread = thread; gst_frame_env(stack)->thread = vm->thread; - gst_frame_env(stack)->stackOffset = thread.count; + gst_frame_env(stack)->stackOffset = vm->thread->count; gst_frame_env(stack)->values = NULL; } if (pc[2] > v1.data.function->def->literalsLen) @@ -179,10 +166,6 @@ static int gst_continue_size(Gst *vm, uint32_t stackBase) { } break; - case GST_OP_ERR: /* Throw error */ - vm->ret = stack[pc[1]]; - goto vm_error; - case GST_OP_TRY: /* Begin try block */ gst_frame_errloc(stack) = pc[1]; gst_frame_errjmp(stack) = pc + *(uint32_t *)(pc + 2); @@ -195,10 +178,9 @@ static int gst_continue_size(Gst *vm, uint32_t stackBase) { continue; case GST_OP_RTN: /* Return nil */ - stack = gst_thread_popframe(vm, &thread); - if (thread.count < stackBase) { + stack = gst_thread_popframe(vm, vm->thread); + if (vm->thread->count < stackBase) { vm->ret.type = GST_NIL; - GST_STATE_WRITE(); return GST_RETURN_OK; } pc = gst_frame_pc(stack); @@ -207,10 +189,9 @@ static int gst_continue_size(Gst *vm, uint32_t stackBase) { case GST_OP_RET: /* Return */ temp = stack[pc[1]]; - stack = gst_thread_popframe(vm, &thread); - if (thread.count < stackBase) { + stack = gst_thread_popframe(vm, vm->thread); + if (vm->thread->count < stackBase) { vm->ret = temp; - GST_STATE_WRITE(); return GST_RETURN_OK; } pc = gst_frame_pc(stack); @@ -230,17 +211,17 @@ static int gst_continue_size(Gst *vm, uint32_t stackBase) { /* Push new frame */ if (temp.type != GST_FUNCTION && temp.type != GST_CFUNCTION) gst_error(vm, GST_EXPECTED_FUNCTION); - stack = gst_thread_beginframe(vm, &thread, temp, arity); + stack = gst_thread_beginframe(vm, vm->thread, temp, arity); oldStack = stack - GST_FRAME_SIZE - gst_frame_prevsize(stack); /* Write arguments */ size = gst_frame_size(stack); for (i = 0; i < arity; ++i) stack[i + size - arity] = oldStack[pc[offset + i]]; /* Finish new frame */ - gst_thread_endframe(vm, &thread); + gst_thread_endframe(vm, vm->thread); /* Check tail call - if so, replace frame. */ if (isTCall) { - stack = gst_thread_tail(vm, &thread); + stack = gst_thread_tail(vm, vm->thread); } else { gst_frame_ret(oldStack) = ret; } @@ -254,14 +235,11 @@ static int gst_continue_size(Gst *vm, uint32_t stackBase) { } else { int status; gst_frame_pc(stack) = pc; - GST_STATE_WRITE(); vm->ret.type = GST_NIL; status = temp.data.cfunction(vm); - GST_STATE_SYNC(); - stack = gst_thread_popframe(vm, &thread); + stack = gst_thread_popframe(vm, vm->thread); if (status == GST_RETURN_OK) { - if (thread.count < stackBase) { - GST_STATE_WRITE(); + if (vm->thread->count < stackBase) { return status; } else { stack[gst_frame_ret(stack)] = vm->ret; @@ -281,6 +259,10 @@ static int gst_continue_size(Gst *vm, uint32_t stackBase) { * These opcodes are nto strictlyre required and can * be reimplemented with stanard library functions */ + case GST_OP_ERR: /* Throw error */ + vm->ret = stack[pc[1]]; + goto vm_error; + #define OP_BINARY_MATH(op) \ v1 = stack[pc[2]]; \ v2 = stack[pc[3]]; \ @@ -401,15 +383,13 @@ static int gst_continue_size(Gst *vm, uint32_t stackBase) { case GST_OP_YLD: /* Yield from function */ temp = stack[pc[1]]; - if (thread.parent == NULL) { + if (vm->thread->parent == NULL) { vm->ret = temp; return GST_RETURN_OK; } gst_frame_pc(stack) = pc + 2; - GST_STATE_WRITE(); - vm->thread = thread.parent; - thread = *vm->thread; - stack = thread.data + thread.count; + vm->thread = vm->thread->parent; + stack = vm->thread->data + vm->thread->count; pc = gst_frame_pc(stack); break; @@ -418,8 +398,8 @@ static int gst_continue_size(Gst *vm, uint32_t stackBase) { if (stack == NULL) return GST_RETURN_ERROR; while (gst_frame_errjmp(stack) == NULL) { - stack = gst_thread_popframe(vm, &thread); - if (thread.count < stackBase) + stack = gst_thread_popframe(vm, vm->thread); + if (vm->thread->count < stackBase) return GST_RETURN_ERROR; } pc = gst_frame_errjmp(stack); @@ -428,9 +408,9 @@ static int gst_continue_size(Gst *vm, uint32_t stackBase) { } /* end switch */ - /* TODO: Move collection only to places that allocate memory */ - /* This, however, is good for testing to ensure no memory leaks */ - *vm->thread = thread; + /* Check for collection every cycle. If the instruction definitely does + * not allocate memory, it can use continue instead of break to + * skip this check */ gst_maybe_collect(vm); } /* end for */ diff --git a/include/gst/parse.h b/include/gst/parse.h index 57abb46d..865844da 100644 --- a/include/gst/parse.h +++ b/include/gst/parse.h @@ -20,7 +20,8 @@ struct GstParser { enum { GST_PARSER_PENDING = 0, GST_PARSER_FULL, - GST_PARSER_ERROR + GST_PARSER_ERROR, + GST_PARSER_ROOT } status; }; @@ -37,4 +38,12 @@ int gst_parse_cstring(GstParser *p, const char *string); /* Parse a gst string. Returns number of bytes read */ int gst_parse_string(GstParser *p, const uint8_t *string); +/* Check if a parser has a value that needs to be handled. If + * so, the parser will not parse any more input until that value + * is consumed. */ +int gst_parse_hasvalue(GstParser *p); + +/* Gets a value from the parser */ +GstValue gst_parse_consume(GstParser *p); + #endif /* end of include guard: PARSE_H_ONYWMADW */ diff --git a/libs/stl.gst b/libs/stl.gst index 8b137891..101ea886 100644 --- a/libs/stl.gst +++ b/libs/stl.gst @@ -1 +1,6 @@ +(: f (fn [x] (strcat (tostring x) "-abumba"))) +(: i 100) +(while (> i 0) (print i) (: i (- i 1))) + +(print (strcat (tostring (+ 1 2 3)) 'a 'b (f 'c)))