1
0
mirror of https://github.com/janet-lang/janet synced 2024-11-28 02:59:54 +00:00

Allow parser to parse files rather than just a repl. I think

there are some memory leak issues (problems with gc).
This commit is contained in:
Calvin Rose 2017-04-17 00:15:18 -04:00
parent f456de5fac
commit f52e290206
11 changed files with 162 additions and 142 deletions

View File

@ -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

View File

@ -6,96 +6,95 @@
#include <gst/stl.h>
#include <gst/disasm.h>
/* 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;
}

View File

@ -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");

View File

@ -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;
}

View File

@ -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;

View File

@ -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, ' ');

View File

@ -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;

View File

@ -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;
}

View File

@ -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 */

View File

@ -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 */

View File

@ -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)))