More work on compiler. Still has memory leak?

This commit is contained in:
Calvin Rose 2017-05-05 16:52:05 -04:00
parent 6ca5a76286
commit f817610d4a
5 changed files with 234 additions and 62 deletions

View File

@ -32,7 +32,7 @@
#include <readline/history.h>
/* Compile and run an ast */
int debug_compile_and_run(Gst *vm, GstValue ast, GstValue env) {
int debug_compile_and_run(Gst *vm, GstValue ast, GstValue last) {
GstCompiler c;
GstValue func;
/* Try to compile generated AST */
@ -40,7 +40,8 @@ int debug_compile_and_run(Gst *vm, GstValue ast, GstValue env) {
gst_compiler_usemodule(&c, "std");
gst_compiler_usemodule(&c, "std.io");
gst_compiler_usemodule(&c, "std.parse");
gst_compiler_globals(&c, env);
gst_compiler_usemodule(&c, "std.compile");
gst_compiler_global(&c, "_", last);
func = gst_wrap_function(gst_compiler_compile(&c, ast));
/* Check for compilation errors */
if (c.error) {
@ -105,7 +106,6 @@ int debug_run(Gst *vm, FILE *in) {
int debug_repl(Gst *vm) {
const char *buffer, *reader;
GstParser p;
GstValue *st;
for (;;) {
/* Init parser */
gst_parser(&p, vm);
@ -131,10 +131,7 @@ int debug_repl(Gst *vm) {
printf("Unexpected end of source\n");
continue;
}
/* Add _ to environemt */
st = gst_struct_begin(vm, 1);
gst_struct_put(st, gst_string_cv(vm, "_"), vm->ret);
if (!debug_compile_and_run(vm, gst_parse_consume(&p), gst_wrap_struct(gst_struct_end(vm, st)))) {
if (!debug_compile_and_run(vm, gst_parse_consume(&p), vm->ret)) {
printf("%s\n", gst_to_string(vm, vm->ret));
}
}
@ -147,6 +144,7 @@ int main(int argc, const char **argv) {
gst_init(&vm);
gst_stl_load(&vm);
gst_parse_load(&vm);
gst_compile_load(&vm);
if (argc > 1) {
const char *filename;
FILE *f;

View File

@ -72,6 +72,7 @@ struct SlotTracker {
Slot *slots;
uint32_t count;
uint32_t capacity;
SlotTracker *next;
};
/* A GstScope is a lexical scope in the program. It is
@ -88,6 +89,8 @@ struct GstScope {
uint16_t *freeHeap;
GstTable *literals;
GstArray *literalsArray;
GstTable *namedLiterals;
GstTable *nilNamedLiterals; /* Work around tables not containg nil */
GstTable *locals;
GstScope *parent;
};
@ -142,10 +145,14 @@ static GstScope *compiler_push_scope(GstCompiler *c, int sameFunction) {
scope->nextLocal = c->tail->nextLocal;
scope->literals = c->tail->literals;
scope->literalsArray = c->tail->literalsArray;
scope->namedLiterals = c->tail->namedLiterals;
scope->nilNamedLiterals = c->tail->nilNamedLiterals;
} else {
scope->nextLocal = 0;
scope->literals = gst_table(c->vm, 10);
scope->literalsArray = gst_array(c->vm, 10);
scope->namedLiterals = gst_table(c->vm, 10);
scope->nilNamedLiterals = gst_table(c->vm, 10);
}
c->tail = scope;
return scope;
@ -204,6 +211,9 @@ static void tracker_init(GstCompiler *c, SlotTracker *tracker) {
tracker->slots = gst_alloc(c->vm, 10 * sizeof(Slot));
tracker->count = 0;
tracker->capacity = 10;
/* Push to tracker stack */
tracker->next = (SlotTracker *) c->trackers;
c->trackers = tracker;
}
/* Free up a slot if it is a temporary slot (does not
@ -300,6 +310,8 @@ static void compiler_tracker_free(GstCompiler *c, GstScope *scope, SlotTracker *
for (i = tracker->count - 1; i < tracker->count; --i) {
compiler_drop_slot(c, scope, tracker->slots[i]);
}
/* Pop from tracker stack */
c->trackers = tracker->next;
}
/* Add a new Slot to a slot tracker. */
@ -350,7 +362,7 @@ static uint16_t compiler_declare_symbol(GstCompiler *c, GstScope *scope, GstValu
/* Try to resolve a symbol. If the symbol can be resovled, return true and
* pass back the level and index by reference. */
static int symbol_resolve(GstCompiler *c, GstValue x, uint16_t *level, uint16_t *index) {
static int symbol_resolve(GstCompiler *c, GstValue x, uint16_t *level, uint16_t *index, GstValue *out) {
GstScope *scope = c->tail;
uint32_t currentLevel = scope->level;
while (scope) {
@ -360,7 +372,18 @@ static int symbol_resolve(GstCompiler *c, GstValue x, uint16_t *level, uint16_t
*index = (uint16_t) check.data.integer;
return 1;
}
/* Check for named literals */
check = gst_table_get(scope->namedLiterals, x);
if (check.type != GST_NIL) {
*out = check;
return 2;
}
/* Check for nil named literal */
check = gst_table_get(scope->nilNamedLiterals, x);
if (check.type != GST_NIL) {
*out = gst_wrap_nil();
return 2;
}
scope = scope->parent;
}
return 0;
@ -377,23 +400,6 @@ static int symbol_resolve(GstCompiler *c, GstValue x, uint16_t *level, uint16_t
* as the location on the stack. */
static Slot compile_value(GstCompiler *c, FormOptions opts, GstValue x);
/* Compile a structure that evaluates to a literal value. Useful
* for objects like strings, or anything else that cannot be instatiated
* from bytecode and doesn't do anything in the AST. */
static Slot compile_literal(GstCompiler *c, FormOptions opts, GstValue x) {
GstScope *scope = c->tail;
GstBuffer *buffer = c->buffer;
Slot ret;
uint16_t literalIndex;
if (opts.resultUnused) return nil_slot();
ret = compiler_get_target(c, opts);
literalIndex = compiler_add_literal(c, scope, x);
gst_buffer_push_u16(c->vm, buffer, GST_OP_CST);
gst_buffer_push_u16(c->vm, buffer, ret.index);
gst_buffer_push_u16(c->vm, buffer, literalIndex);
return ret;
}
/* Compile boolean, nil, and number values. */
static Slot compile_nonref_type(GstCompiler *c, FormOptions opts, GstValue x) {
GstBuffer *buffer = c->buffer;
@ -430,17 +436,48 @@ static Slot compile_nonref_type(GstCompiler *c, FormOptions opts, GstValue x) {
return ret;
}
/* Compile a structure that evaluates to a literal value. Useful
* for objects like strings, or anything else that cannot be instantiated
* from bytecode and doesn't do anything in the AST. */
static Slot compile_literal(GstCompiler *c, FormOptions opts, GstValue x) {
GstScope *scope = c->tail;
GstBuffer *buffer = c->buffer;
Slot ret;
uint16_t literalIndex;
if (opts.resultUnused) return nil_slot();
switch (x.type) {
case GST_INTEGER:
case GST_REAL:
case GST_BOOLEAN:
case GST_NIL:
return compile_nonref_type(c, opts, x);
default:
break;
}
ret = compiler_get_target(c, opts);
literalIndex = compiler_add_literal(c, scope, x);
gst_buffer_push_u16(c->vm, buffer, GST_OP_CST);
gst_buffer_push_u16(c->vm, buffer, ret.index);
gst_buffer_push_u16(c->vm, buffer, literalIndex);
return ret;
}
/* Compile a symbol. Resolves any kind of symbol. */
static Slot compile_symbol(GstCompiler *c, FormOptions opts, GstValue sym) {
GstValue lit = gst_wrap_nil();
GstBuffer * buffer = c->buffer;
uint16_t index = 0;
uint16_t level = 0;
Slot ret;
if (opts.resultUnused) return nil_slot();
if (!symbol_resolve(c, sym, &level, &index)) {
int status = symbol_resolve(c, sym, &level, &index, &lit);
if (!status) {
c_error(c, "undefined symbol");
}
if (level > 0) {
if (opts.resultUnused) return nil_slot();
if (status == 2) {
/* We have a named literal */
return compile_literal(c, opts, lit);
} else if (level > 0) {
/* We have an upvalue */
ret = compiler_get_target(c, opts);
gst_buffer_push_u16(c->vm, buffer, GST_OP_UPV);
@ -468,15 +505,20 @@ static Slot compile_symbol(GstCompiler *c, FormOptions opts, GstValue sym) {
/* Compile an assignment operation */
static Slot compile_assign(GstCompiler *c, FormOptions opts, GstValue left, GstValue right) {
GstValue lit = gst_wrap_nil();
GstScope *scope = c->tail;
GstBuffer *buffer = c->buffer;
FormOptions subOpts;
uint16_t target = 0;
uint16_t level = 0;
Slot slot;
int status;
subOpts.isTail = 0;
subOpts.resultUnused = 0;
if (symbol_resolve(c, left, &level, &target)) {
status = symbol_resolve(c, left, &level, &target, &lit);
if (status == 2) {
c_error(c, "cannot set binding");
} else if (status == 1) {
/* Check if we have an up value. Otherwise, it's just a normal
* local variable */
if (level != 0) {
@ -783,7 +825,7 @@ static Slot compile_apply(GstCompiler *c, FormOptions opts, const GstValue *form
Slot slot = compile_value(c, subOpts, form[i]);
compiler_tracker_push(c, &tracker, slot);
}
/* Write last item */
/* Write last item */
{
Slot slot = compile_value(c, subOpts, form[gst_tuple_length(form) - 1]);
slot = compiler_realize_slot(c, slot);
@ -1028,39 +1070,32 @@ static Slot compile_value(GstCompiler *c, FormOptions opts, GstValue x) {
void gst_compiler(GstCompiler *c, Gst *vm) {
c->vm = vm;
c->buffer = gst_buffer(vm, 128);
c->env = gst_array(vm, 10);
c->tail = NULL;
c->error = NULL;
c->trackers = NULL;
compiler_push_scope(c, 0);
}
/* Add a global variable */
void gst_compiler_global(GstCompiler *c, const char *name, GstValue x) {
GstScope *scope = c->tail;
GstValue sym = gst_string_cv(c->vm, name);
compiler_declare_symbol(c, c->tail, sym);
gst_array_push(c->vm, c->env, x);
if (x.type == GST_NIL)
gst_table_put(c->vm, scope->nilNamedLiterals, sym, gst_wrap_boolean(1));
else
gst_table_put(c->vm, scope->namedLiterals, sym, x);
}
/* Add many global variables */
void gst_compiler_globals(GstCompiler *c, GstValue env) {
GstScope *scope = c->tail;
const GstValue *data;
uint32_t len;
uint32_t i;
if (env.type == GST_TABLE) {
GstTable *t = env.data.table;
for (i = 0; i < t->capacity; i += 2) {
if (t->data[i].type == GST_STRING) {
compiler_declare_symbol(c, c->tail, t->data[i]);
gst_array_push(c->vm, c->env, t->data[i + 1]);
}
}
} else if (env.type == GST_STRUCT) {
const GstValue *st = env.data.st;
for (i = 0; i < gst_struct_capacity(st); i += 2) {
if (st[i].type == GST_STRING) {
compiler_declare_symbol(c, c->tail, st[i]);
gst_array_push(c->vm, c->env, st[i + 1]);
}
}
}
if (gst_hashtable_view(env, &data, &len))
for (i = 0; i < len; i += 2)
if (data[i].type == GST_STRING)
gst_table_put(c->vm, scope->namedLiterals, data[i], data[i + 1]);
}
/* Use a module that was loaded into the vm */
@ -1076,6 +1111,7 @@ GstFunction *gst_compiler_compile(GstCompiler *c, GstValue form) {
GstFuncDef *def;
if (setjmp(c->onError)) {
/* Clear all but root scope */
c->trackers = NULL;
if (c->tail)
c->tail->parent = NULL;
if (c->error == NULL)
@ -1088,16 +1124,10 @@ GstFunction *gst_compiler_compile(GstCompiler *c, GstValue form) {
compiler_return(c, compile_value(c, opts, form));
def = compiler_gen_funcdef(c, c->buffer->count, 0);
{
uint32_t envSize = c->env->count;
GstFuncEnv *env = gst_alloc(c->vm, sizeof(GstFuncEnv));
GstFunction *func = gst_alloc(c->vm, sizeof(GstFunction));
if (envSize) {
env->values = gst_alloc(c->vm, sizeof(GstValue) * envSize);
gst_memcpy(env->values, c->env->data, envSize * sizeof(GstValue));
} else {
env->values = NULL;
}
env->stackOffset = envSize;
env->values = NULL;
env->stackOffset = 0;
env->thread = NULL;
func->parent = NULL;
func->def = def;
@ -1105,3 +1135,108 @@ GstFunction *gst_compiler_compile(GstCompiler *c, GstValue form) {
return func;
}
}
/***/
/* Stl */
/***/
/* GC mark mark all memory used by the compiler */
static void gst_compiler_mark(Gst *vm, void *data, uint32_t len) {
SlotTracker *st;
GstScope *scope;
GstCompiler *c = (GstCompiler *) data;
if (len != sizeof(GstCompiler))
return;
/* Mark compiler */
gst_mark_value(vm, gst_wrap_buffer(c->buffer));
/* Mark trackers */
st = (SlotTracker *) c->trackers;
while (st) {
gst_mark_mem(vm, st->slots);
st = st->next;
}
/* Mark scopes */
scope = c->tail;
while (scope) {
gst_mark_mem(vm, scope->freeHeap);
gst_mark_value(vm, gst_wrap_array(scope->literalsArray));
gst_mark_value(vm, gst_wrap_table(scope->locals));
gst_mark_value(vm, gst_wrap_table(scope->literals));
gst_mark_value(vm, gst_wrap_table(scope->namedLiterals));
gst_mark_value(vm, gst_wrap_table(scope->nilNamedLiterals));
scope = scope->parent;
}
}
/* Compiler userdata type */
static const GstUserType gst_stl_compilertype = {
"std.compiler",
NULL,
NULL,
NULL,
&gst_compiler_mark
};
/* Create a compiler userdata */
static int gst_stl_compiler(Gst *vm) {
GstCompiler *c = gst_userdata(vm, sizeof(GstCompiler), &gst_stl_compilertype);
gst_compiler(c, vm);
gst_c_return(vm, gst_wrap_userdata(c));
}
/* Add a binding to the compiler's current scope. */
static int gst_stl_compiler_binding(Gst *vm) {
GstCompiler *c = gst_check_userdata(vm, 0, &gst_stl_compilertype);
GstScope *scope;
GstValue sym;
const uint8_t *data;
uint32_t len;
if (!c)
gst_c_throwc(vm, "expected compiler");
if (!gst_chararray_view(gst_arg(vm, 1), &data, &len))
gst_c_throwc(vm, "expected string/buffer");
scope = c->tail;
sym = gst_wrap_string(gst_string_b(c->vm, data, len));
gst_table_put(c->vm, scope->namedLiterals, sym, gst_arg(vm, 2));
gst_c_return(vm, gst_wrap_userdata(c));
}
/* Compile a value */
static int gst_stl_compiler_compile(Gst *vm) {
GstFunction *ret;
GstCompiler *c = gst_check_userdata(vm, 0, &gst_stl_compilertype);
if (!c)
gst_c_throwc(vm, "expected compiler");
ret = gst_compiler_compile(c, gst_arg(vm, 1));
if (ret == NULL)
gst_c_throwc(vm, c->error);
gst_c_return(vm, gst_wrap_function(ret));
}
/* Use an environment during compilation. Names that are declared more than
* once should use their final declared value. */
static int gst_stl_compiler_bindings(Gst *vm) {
GstValue env;
GstCompiler *c = gst_check_userdata(vm, 0, &gst_stl_compilertype);
if (!c)
gst_c_throwc(vm, "expected compiler");
env = gst_arg(vm, 1);
if (env.type != GST_TABLE && env.type != GST_STRUCT)
gst_c_throwc(vm, "expected table/struct");
gst_compiler_globals(c, env);
gst_c_return(vm, gst_wrap_userdata(c));
}
/* The module stuff */
static const GstModuleItem gst_compile_module[] = {
{"compiler", gst_stl_compiler},
{"compile", gst_stl_compiler_compile},
{"binding!", gst_stl_compiler_binding},
{"bindings!", gst_stl_compiler_bindings},
{NULL, NULL}
};
/* Load compiler library */
void gst_compile_load(Gst *vm) {
gst_module_put(vm, "std.compile", gst_cmodule_struct(vm, gst_compile_module));
}

View File

@ -96,6 +96,37 @@ static const uint8_t *string_description(Gst *vm, const char *title, void *point
return gst_string_b(vm, buf, c - buf);
}
/* Gets the string value of userdata. Allocates memory, so is slower than
* string_description. */
static const uint8_t *string_udata(Gst *vm, const char *title, void *pointer) {
uint32_t strlen = 0;
uint8_t *c, *buf;
uint32_t i;
union {
uint8_t bytes[sizeof(void *)];
void *p;
} pbuf;
while (title[strlen]) ++strlen;
c = buf = gst_alloc(vm, strlen + 5 + 2 * sizeof(void *));
pbuf.p = pointer;
*c++ = '<';
for (i = 0; title[i]; ++i)
*c++ = ((uint8_t *)title) [i];
*c++ = ' ';
*c++ = '0';
*c++ = 'x';
for (i = sizeof(void *); i > 0; --i) {
uint8_t byte = pbuf.bytes[i - 1];
if (!byte) continue;
*c++ = HEX(byte >> 4);
*c++ = HEX(byte & 0xF);
}
*c++ = '>';
return gst_string_b(vm, buf, c - buf);
}
#undef GST_BUFSIZE
/* Returns a string pointer or NULL if could not allocate memory. */
@ -131,7 +162,7 @@ const uint8_t *gst_to_string(Gst *vm, GstValue x) {
case GST_THREAD:
return string_description(vm, "thread", x.data.pointer);
case GST_USERDATA:
return string_description(vm, "userdata", x.data.pointer);
return string_udata(vm, gst_udata_type(x.data.pointer)->name, x.data.pointer);
case GST_FUNCENV:
return string_description(vm, "funcenv", x.data.pointer);
case GST_FUNCDEF:

View File

@ -35,8 +35,8 @@ struct GstCompiler {
const char *error;
jmp_buf onError;
GstScope *tail;
GstArray *env;
GstBuffer *buffer;
void *trackers;
};
/* Initialize the Compiler */
@ -54,4 +54,7 @@ void gst_compiler_usemodule(GstCompiler *c, const char *modulename);
/* Compile a function that evaluates the given form. */
GstFunction *gst_compiler_compile(GstCompiler *c, GstValue form);
/* Load the library */
void gst_compile_load(Gst *vm);
#endif /* end of include guard: COMPILE_H_9VXF71HY */

View File

@ -41,6 +41,11 @@
#define gst_struct_capacity(t) (gst_struct_length(t) * 4)
#define gst_struct_hash(t) (gst_struct_raw(t)[1])
/* Userdata utils */
#define gst_udata_header(u) ((GstUserdataHeader *)(u) - 1)
#define gst_udata_type(u) (gst_udata_header(u)->type)
#define gst_udata_size(u) (gst_udata_header(u)->size)
/* Memcpy for moving memory */
#ifndef gst_memcpy
#include <string.h>
@ -260,7 +265,7 @@ struct GstFunction {
/* Defines a type for userdata */
struct GstUserType {
const char *id;
const char *name;
GstValue (*serialize)(Gst *vm, void *data, uint32_t len);
GstValue (*deserialize)(Gst *vm, GstValue in);
void (*finalize)(Gst *vm, void *data, uint32_t len);