Add var and def. Make them behave the same

but have different implementations in top
level scope in order to enable incremental compilation and repl.
This commit is contained in:
Calvin Rose 2017-06-25 16:36:20 -04:00
parent c3d65cb91d
commit 29a39c47b0
8 changed files with 371 additions and 105 deletions

View File

@ -34,8 +34,7 @@ int debug_compile_and_run(Gst *vm, GstValue ast, GstValue last) {
GstValue func;
/* Try to compile generated AST */
gst_compiler(&c, vm);
gst_compiler_usemodule(&c, "std");
gst_compiler_global(&c, "_", last);
gst_env_putc(vm, vm->env, "_", last);
func = gst_wrap_function(gst_compiler_compile(&c, ast));
/* Check for compilation errors */
if (c.error.type != GST_NIL) {

View File

@ -22,6 +22,8 @@
#include <gst/gst.h>
#define GST_LOCAL_FLAG_MUTABLE 1
/* During compilation, FormOptions are passed to ASTs
* as configuration options to allow for some optimizations. */
typedef struct FormOptions FormOptions;
@ -31,7 +33,7 @@ struct FormOptions {
uint16_t target;
/* If the result of the value being compiled is not going to
* be used, some forms can simply return a nil slot and save
* copmutation */
* co,putation */
uint16_t resultUnused : 1;
/* Allows the sub expression to evaluate into a
* temporary slot of it's choice. A temporary Slot
@ -89,8 +91,6 @@ struct GstScope {
uint16_t *freeHeap;
GstTable *literals;
GstArray *literalsArray;
GstTable *namedLiterals;
GstTable *nilNamedLiterals; /* Work around tables not containg nil */
GstTable *locals;
GstScope *parent;
};
@ -126,16 +126,24 @@ static void c_error1(GstCompiler *c, GstValue e) {
longjmp(c->onError, 1);
}
/* Quote something */
static GstValue quote(Gst *vm, GstValue x) {
GstValue *q = gst_tuple_begin(vm, 2);
q[0] = gst_string_cv(vm, "quote");
q[1] = x; /* lit contains the var container of the environment */
return gst_wrap_tuple(gst_tuple_end(vm, q));
}
/* Push a new scope in the compiler and return
* a pointer to it for configuration. There is
* more configuration that needs to be done if
* the new scope is a function declaration. */
static GstScope *compiler_push_scope(GstCompiler *c, int sameFunction) {
GstScope *scope = gst_alloc(c->vm, sizeof(GstScope));
scope->locals = gst_table(c->vm, 10);
scope->freeHeap = gst_alloc(c->vm, 10 * sizeof(uint16_t));
scope->locals = gst_table(c->vm, 4);
scope->freeHeap = gst_alloc(c->vm, 4 * sizeof(uint16_t));
scope->heapSize = 0;
scope->heapCapacity = 10;
scope->heapCapacity = 4;
scope->parent = c->tail;
scope->frameSize = 0;
scope->touchParent = 0;
@ -152,14 +160,10 @@ 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);
scope->literals = gst_table(c->vm, 4);
scope->literalsArray = gst_array(c->vm, 4);
}
c->tail = scope;
return scope;
@ -194,7 +198,6 @@ static uint16_t compiler_get_local(GstCompiler *c, GstScope *scope) {
} else {
return scope->freeHeap[--scope->heapSize];
}
return 0;
}
/* Free a slot on the stack for other locals and/or
@ -286,6 +289,33 @@ static Slot compiler_realize_slot(GstCompiler *c, Slot slot) {
return slot;
}
/* Coerce a slot to match form options. Can write to buffer. */
static Slot compiler_coerce_slot(GstCompiler *c, FormOptions opts, Slot slot) {
GstScope *scope = c->tail;
if (opts.resultUnused) {
compiler_drop_slot(c, scope, slot);
slot.isNil = 1;
return slot;
} else {
slot = compiler_realize_slot(c, slot);
}
if (opts.canChoose) {
} else {
if (slot.index != opts.target) {
/* We need to move the variable. This
* would occur in a simple assignment like a = b. */
GstBuffer *buffer = c->buffer;
gst_buffer_push_u16(c->vm, buffer, GST_OP_MOV);
gst_buffer_push_u16(c->vm, buffer, opts.target);
gst_buffer_push_u16(c->vm, buffer, slot.index);
slot.index = opts.target;
slot.isTemp = 0; /* We don't own the slot anymore */
}
}
return slot;
}
/* Helper to get a nil slot */
static Slot nil_slot() { Slot ret; ret.isNil = 1; ret.hasReturned = 0; return ret; }
@ -350,44 +380,59 @@ static uint16_t compiler_add_literal(GstCompiler *c, GstScope *scope, GstValue x
}
/* Declare a symbol in a given scope. */
static uint16_t compiler_declare_symbol(GstCompiler *c, GstScope *scope, GstValue sym) {
static uint16_t compiler_declare_symbol(GstCompiler *c, GstScope *scope, GstValue sym, uint16_t flags) {
GstValue x;
uint16_t target;
if (sym.type != GST_STRING)
c_error(c, "expected string");
target = compiler_get_local(c, scope);
x.type = GST_INTEGER;
x.data.integer = target;
x.data.integer = target + (flags << 16);
gst_table_put(c->vm, scope->locals, sym, x);
return target;
}
/* Try to resolve a symbol. If the symbol can be resovled, return true and
/* Try to resolve a symbol. If the symbol can be resolved, 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, GstValue *out) {
static int symbol_resolve(GstCompiler *c, GstValue x, uint16_t *level, uint16_t *index, uint16_t *flags, GstValue *out) {
GstScope *scope = c->tail;
GstValue check;
uint32_t currentLevel = scope->level;
while (scope) {
GstValue check = gst_table_get(scope->locals, x);
check = gst_table_get(scope->locals, x);
if (check.type != GST_NIL) {
*level = currentLevel - scope->level;
*index = (uint16_t) check.data.integer;
*index = (uint16_t) (check.data.integer & 0xFFFF);
if (flags) *flags = check.data.integer >> 16;
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;
}
/* Check for named literals */
check = gst_table_get(c->env, x);
if (check.type != GST_NIL) {
/* Check metadata for var (mutable) */
GstTable *metas = gst_env_meta(c->vm, c->env);
GstValue maybeMeta = gst_table_get(metas, x);
if (maybeMeta.type == GST_TABLE) {
GstValue isMutable = gst_table_get(maybeMeta.data.table, gst_string_cv(c->vm, "mutable"));
if (gst_truthy(isMutable)) {
if (flags) *flags = GST_LOCAL_FLAG_MUTABLE;
*out = check;
return 3;
}
}
if (flags) *flags = 0;
*out = check;
return 2;
}
/* Check for nil named literal */
check = gst_table_get(gst_env_nils(c->vm, c->env), x);
if (check.type != GST_NIL) {
if (flags) *flags = 0;
*out = gst_wrap_nil();
return 2;
}
return 0;
}
@ -471,7 +516,7 @@ static Slot compile_symbol(GstCompiler *c, FormOptions opts, GstValue sym) {
uint16_t index = 0;
uint16_t level = 0;
Slot ret;
int status = symbol_resolve(c, sym, &level, &index, &lit);
int status = symbol_resolve(c, sym, &level, &index, NULL, &lit);
if (!status) {
c_error1(c, sym);
}
@ -479,6 +524,16 @@ static Slot compile_symbol(GstCompiler *c, FormOptions opts, GstValue sym) {
if (status == 2) {
/* We have a named literal */
return compile_literal(c, opts, lit);
} else if (status == 3) {
/* We have a global variable */
const GstValue *tup;
Gst *vm= c->vm;
GstValue *t = gst_tuple_begin(vm, 3);
t[0] = gst_string_cv(vm, "get"); /* Todo - replace with ref ro actual cfunc */
t[1] = quote(vm, lit);
t[2] = quote(vm, sym);
tup = gst_tuple_end(vm, t);
return compile_value(c, opts, gst_wrap_tuple(tup));
} else if (level > 0) {
/* We have an upvalue from a parent function. Make
* sure that the chain of functions up to the upvalue keep
@ -522,14 +577,15 @@ static Slot compile_assign(GstCompiler *c, FormOptions opts, GstValue left, GstV
FormOptions subOpts;
uint16_t target = 0;
uint16_t level = 0;
uint16_t flags = 0;
Slot slot;
int status;
subOpts.isTail = 0;
subOpts.resultUnused = 0;
status = symbol_resolve(c, left, &level, &target, &lit);
if (status == 2) {
c_error(c, "cannot set binding");
} else if (status == 1) {
status = symbol_resolve(c, left, &level, &target, &flags, &lit);
if (status == 1) {
if (!(flags & GST_LOCAL_FLAG_MUTABLE))
c_error(c, "cannot varset immutable binding");
/* Check if we have an up value. Otherwise, it's just a normal
* local variable */
if (level != 0) {
@ -547,11 +603,21 @@ static Slot compile_assign(GstCompiler *c, FormOptions opts, GstValue left, GstV
subOpts.target = target;
slot = compile_value(c, subOpts, right);
}
} else if (status == 3) {
/* Global var */
const GstValue *tup;
Gst *vm= c->vm;
GstValue *t = gst_tuple_begin(vm, 4);
t[0] = gst_string_cv(vm, "set!"); /* Todo - replace with ref ro actual cfunc */
t[1] = quote(vm, lit);
t[2] = quote(vm, left);
t[3] = right;
tup = gst_tuple_end(vm, t);
subOpts.resultUnused = 1;
compile_value(c, subOpts, gst_wrap_tuple(tup));
return compile_value(c, opts, left);
} else {
/* We need to declare a new symbol */
subOpts.target = compiler_declare_symbol(c, scope, left);
subOpts.canChoose = 0;
slot = compile_value(c, subOpts, right);
c_error(c, "cannot varset immutable binding");
}
if (opts.resultUnused) {
compiler_drop_slot(c, scope, slot);
@ -561,6 +627,87 @@ static Slot compile_assign(GstCompiler *c, FormOptions opts, GstValue left, GstV
}
}
/* Set a var */
static Slot compile_varset(GstCompiler *c, FormOptions opts, const GstValue *form) {
if (gst_tuple_length(form) != 3)
c_error(c, "expected 2 arguments to varset");
if (GST_STRING != form[1].type)
c_error(c, "expected symbol as first argument");
return compile_assign(c, opts, form[1], form[2]);
}
/* Global var */
static Slot compile_global_var(GstCompiler *c, FormOptions opts, const GstValue *form) {
const GstValue *tup;
Gst *vm= c->vm;
GstValue *t = gst_tuple_begin(vm, 3);
GstValue *q = gst_tuple_begin(vm, 2);
q[0] = gst_string_cv(vm, "quote");
q[1] = form[1];
t[0] = gst_string_cv(vm, "global-var"); /* Todo - replace with ref ro actual cfunc */
t[1] = gst_wrap_tuple(gst_tuple_end(vm, q));
t[2] = form[2];
tup = gst_tuple_end(vm, t);
return compile_value(c, opts, gst_wrap_tuple(tup));
}
/* Global define */
static Slot compile_global_def(GstCompiler *c, FormOptions opts, const GstValue *form) {
const GstValue *tup;
Gst *vm= c->vm;
GstValue *t = gst_tuple_begin(vm, 3);
GstValue *q = gst_tuple_begin(vm, 2);
q[0] = gst_string_cv(vm, "quote");
q[1] = form[1];
t[0] = gst_string_cv(vm, "global-def"); /* Todo - replace with ref ro actual cfunc */
t[1] = gst_wrap_tuple(gst_tuple_end(vm, q));
t[2] = form[2];
tup = gst_tuple_end(vm, t);
return compile_value(c, opts, gst_wrap_tuple(tup));
}
/* Compile def */
static Slot compile_def(GstCompiler *c, FormOptions opts, const GstValue *form) {
GstScope *scope = c->tail;
if (gst_tuple_length(form) != 3)
c_error(c, "expected 2 arguments to def");
if (GST_STRING != form[1].type)
c_error(c, "expected symbol as first argument");
if (scope->parent) {
FormOptions subOpts;
Slot slot;
subOpts.isTail = opts.isTail;
subOpts.resultUnused = 0;
subOpts.canChoose = 0;
subOpts.target = compiler_declare_symbol(c, scope, form[1], 0);
slot = compile_value(c, subOpts, form[2]);
return compiler_coerce_slot(c, opts, slot);
} else {
return compile_global_def(c, opts, form);
}
}
/* Compile var */
static Slot compile_var(GstCompiler *c, FormOptions opts, const GstValue *form) {
GstScope *scope = c->tail;
if (gst_tuple_length(form) != 3)
c_error(c, "expected 2 arguments to var");
if (GST_STRING != form[1].type)
c_error(c, "expected symbol as first argument");
if (scope->parent) {
FormOptions subOpts;
Slot slot;
subOpts.isTail = opts.isTail;
subOpts.resultUnused = 0;
subOpts.canChoose = 0;
subOpts.target = compiler_declare_symbol(c, scope, form[1], GST_LOCAL_FLAG_MUTABLE);
slot = compile_value(c, subOpts, form[2]);
return compiler_coerce_slot(c, opts, slot);
} else {
return compile_global_var(c, opts, form);
}
}
/* Compile series of expressions. This compiles the meat of
* function definitions and the inside of do forms. */
static Slot compile_block(GstCompiler *c, FormOptions opts, const GstValue *form, uint32_t startIndex) {
@ -664,7 +811,7 @@ static Slot compile_function(GstCompiler *c, FormOptions opts, const GstValue *f
/* The compiler puts the parameter locals
* in the right place by default - at the beginning
* of the stack frame. */
compiler_declare_symbol(c, subGstScope, param);
compiler_declare_symbol(c, subGstScope, param, 0);
}
/* Mark where we are on the stack so we can
* return to it later. */
@ -832,13 +979,6 @@ static Slot compile_quote(GstCompiler *c, FormOptions opts, const GstValue *form
return ret;
}
/* Assignment special */
static Slot compile_var(GstCompiler *c, FormOptions opts, const GstValue *form) {
if (gst_tuple_length(form) != 3)
c_error(c, "assignment expects 2 arguments");
return compile_assign(c, opts, form[1], form[2]);
}
/* Apply special */
static Slot compile_apply(GstCompiler *c, FormOptions opts, const GstValue *form) {
GstScope *scope = c->tail;
@ -927,7 +1067,7 @@ static SpecialFormHelper get_special(const GstValue *form) {
/* One character specials. */
if (gst_string_length(name) == 1) {
switch(name[0]) {
case ':': return compile_var;
case ':': return compile_def;
default:
break;
}
@ -950,6 +1090,10 @@ static SpecialFormHelper get_special(const GstValue *form) {
if (gst_string_length(name) == 2 &&
name[1] == 'o') {
return compile_do;
} else if (gst_string_length(name) == 3 &&
name[1] == 'e' &&
name[2] == 'f') {
return compile_def;
}
}
break;
@ -990,6 +1134,23 @@ static SpecialFormHelper get_special(const GstValue *form) {
}
}
break;
case 'v':
{
if (gst_string_length(name) == 3 &&
name[1] == 'a' &&
name[2] == 'r') {
return compile_var;
}
if (gst_string_length(name) == 6 &&
name[1] == 'a' &&
name[2] == 'r' &&
name[3] == 's' &&
name[4] == 'e' &&
name[5] == 't') {
return compile_varset;
}
}
break;
case 'w':
{
if (gst_string_length(name) == 5 &&
@ -1057,7 +1218,7 @@ static Slot compile_table(GstCompiler *c, FormOptions opts, GstTable *tab) {
return ret;
}
/* Compile a form. Checks for special forms and macros. */
/* Compile a form. Checks for special forms. */
static Slot compile_form(GstCompiler *c, FormOptions opts, const GstValue *form) {
GstScope *scope = c->tail;
GstBuffer *buffer = c->buffer;
@ -1136,50 +1297,10 @@ void gst_compiler(GstCompiler *c, Gst *vm) {
c->buffer = gst_buffer(vm, 128);
c->tail = NULL;
c->error.type = GST_NIL;
c->env = vm->env;
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);
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 (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]);
}
/* Add many global variables and bind to nil */
void gst_compiler_nilglobals(GstCompiler *c, GstValue env) {
GstScope *scope = c->tail;
const GstValue *data;
uint32_t len;
uint32_t i;
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], gst_wrap_nil());
}
/* Use a module that was loaded into the vm */
void gst_compiler_usemodule(GstCompiler *c, const char *modulename) {
GstValue mod = gst_table_get(c->vm->modules, gst_string_cv(c->vm, modulename));
gst_compiler_globals(c, mod);
}
/* Compile interface. Returns a function that evaluates the
* given AST. Returns NULL if there was an error during compilation. */
GstFunction *gst_compiler_compile(GstCompiler *c, GstValue form) {
@ -1193,7 +1314,6 @@ GstFunction *gst_compiler_compile(GstCompiler *c, GstValue form) {
c->error = gst_string_cv(c->vm, "unknown error");
return NULL;
}
/* Create a scope */
opts.isTail = 1;
compiler_return(c, compile_value(c, opts, form));
def = compiler_gen_funcdef(c, c->buffer->count, 0, 0);

View File

@ -276,6 +276,7 @@ void gst_collect(Gst *vm) {
gst_mark_value(vm, gst_wrap_thread(vm->thread));
gst_mark_value(vm, gst_wrap_table(vm->modules));
gst_mark_value(vm, gst_wrap_table(vm->registry));
gst_mark_value(vm, gst_wrap_table(vm->env));
gst_mark_value(vm, vm->ret);
if (vm->scratch)
gc_header(vm->scratch)->color = vm->black;

View File

@ -21,6 +21,7 @@
*/
#include <gst/gst.h>
#include "cache.h"
/****/
/* Cache */

View File

@ -710,6 +710,28 @@ int gst_stl_funcparent(Gst *vm) {
return GST_RETURN_OK;
}
int gst_stl_def(Gst *vm) {
if (gst_count_args(vm) != 2) {
gst_c_throwc(vm, "expected 2 arguments to global-def");
}
if (GST_STRING != gst_arg(vm, 0).type) {
gst_c_throwc(vm, "expected string as first argument");
}
gst_env_put(vm, vm->env, gst_arg(vm, 0), gst_arg(vm, 1));
gst_c_return(vm, gst_arg(vm, 1));
}
int gst_stl_var(Gst *vm) {
if (gst_count_args(vm) != 2) {
gst_c_throwc(vm, "expected 2 arguments to global-var");
}
if (GST_STRING != gst_arg(vm, 0).type) {
gst_c_throwc(vm, "expected string as first argument");
}
gst_env_putvar(vm, vm->env, gst_arg(vm, 0), gst_arg(vm, 1));
gst_c_return(vm, gst_arg(vm, 1));
}
/****/
/* IO */
/****/
@ -1038,13 +1060,11 @@ static int gst_stl_parse(Gst *vm) {
/* Compile a value */
static int gst_stl_compile(Gst *vm) {
GstFunction *ret;
GstValue std;
GstCompiler c;
gst_compiler(&c, vm);
std = gst_table_get(vm->modules, gst_string_cv(vm, "std"));
gst_compiler_globals(&c, std);
gst_compiler_globals(&c, gst_arg(vm, 1));
gst_compiler_nilglobals(&c, gst_arg(vm, 2));
if (gst_arg(vm, 1).type == GST_TABLE) {
c.env = gst_arg(vm, 1).data.table;
}
ret = gst_compiler_compile(&c, gst_arg(vm, 0));
if (!ret)
gst_c_throw(vm, c.error);
@ -1086,8 +1106,8 @@ static const GstModuleItem std_module[] = {
{"parse-hasvalue", gst_stl_parser_hasvalue},
{"parse-charseq", gst_stl_parser_charseq},
{"parse-status", gst_stl_parser_status},
{"parse-status", gst_stl_parser_status},
{"parse", gst_stl_parse},
{"parse-status", gst_stl_parser_status},
{"parse", gst_stl_parse},
/* Compile */
{"compile", gst_stl_compile},
/* Other */
@ -1134,11 +1154,14 @@ static const GstModuleItem std_module[] = {
{"funcparent", gst_stl_funcparent},
{"gcollect", gst_stl_gcollect},
{"debugp", gst_stl_debugp},
{"global-def", gst_stl_def},
{"global-var", gst_stl_var},
{NULL, NULL}
};
/* Load all libraries */
void gst_stl_load(Gst *vm) {
GstValue maybeEnv;
/* Load the normal c functions */
gst_module_mutable(vm, "std", std_module);
/* Wrap stdin and stdout */
@ -1151,4 +1174,7 @@ void gst_stl_load(Gst *vm) {
gst_module_put(vm, "std", "stdin", gst_wrap_userdata(inp));
gst_module_put(vm, "std", "stdout", gst_wrap_userdata(outp));
gst_module_put(vm, "std", "stderr", gst_wrap_userdata(outp));
maybeEnv = gst_table_get(vm->modules, gst_string_cv(vm, "std"));
if (maybeEnv.type == GST_TABLE)
gst_env_merge(vm, vm->env, maybeEnv.data.table);
}

View File

@ -252,3 +252,109 @@ int gst_callc(Gst *vm, GstCFunction fn, int numargs, ...) {
gst_thread_popframe(vm, vm->thread);
return result;
}
static GstTable *gst_env_inttab(Gst *vm, GstTable *env, GstInteger i) {
GstTable *tab;
GstValue key = gst_wrap_integer(i);
GstValue maybeTab = gst_table_get(env, key);
if (maybeTab.type != GST_TABLE) {
tab = gst_table(vm, 10);
gst_table_put(vm, env, key, gst_wrap_table(tab));
} else {
tab = maybeTab.data.table;
}
return tab;
}
GstTable *gst_env_nils(Gst *vm, GstTable *env) {
return gst_env_inttab(vm, env, GST_ENV_NILS);
}
GstTable *gst_env_meta(Gst *vm, GstTable *env) {
return gst_env_inttab(vm, env, GST_ENV_METADATA);
}
GstTable *gst_env_vars(Gst *vm, GstTable *env) {
return gst_env_inttab(vm, env, GST_ENV_VARS);
}
/* Add many global variables and bind to nil */
static void mergenils(Gst *vm, GstTable *destEnv, GstTable *nils) {
const GstValue *data = nils->data;
uint32_t len = nils->capacity;
uint32_t i;
GstTable *destNils = gst_env_nils(vm, destEnv);
for (i = 0; i < len; i += 2) {
if (data[i].type == GST_STRING) {
gst_table_put(vm, destEnv, data[i], gst_wrap_nil());
gst_table_put(vm, destNils, data[i], gst_wrap_boolean(1));
}
}
}
/* Add many global variable metadata */
static void mergemeta(Gst *vm, GstTable *destEnv, GstTable *meta) {
const GstValue *data = meta->data;
uint32_t len = meta->capacity;
uint32_t i;
GstTable *destMeta = gst_env_meta(vm, destEnv);
for (i = 0; i < len; i += 2) {
if (data[i].type == GST_STRING) {
gst_table_put(vm, destMeta, data[i], data[i + 1]);
}
}
}
/* Add many global variables */
void gst_env_merge(Gst *vm, GstTable *destEnv, GstTable *srcEnv) {
const GstValue *data = srcEnv->data;
uint32_t len = srcEnv->capacity;
uint32_t i;
for (i = 0; i < len; i += 2) {
if (data[i].type == GST_STRING) {
gst_table_put(vm, destEnv, data[i], data[i + 1]);
} else if (data[i].type == GST_INTEGER) {
switch (data[i].data.integer) {
case GST_ENV_NILS:
if (data[i + 1].type == GST_TABLE)
mergenils(vm, destEnv, data[i + 1].data.table);
break;
case GST_ENV_METADATA:
if (data[i + 1].type == GST_TABLE)
mergemeta(vm, destEnv, data[i + 1].data.table);
break;
default:
break;
}
}
}
}
void gst_env_put(Gst *vm, GstTable *env, GstValue key, GstValue value) {
GstTable *meta = gst_env_meta(vm, env);
gst_table_put(vm, meta, key, gst_wrap_nil());
gst_table_put(vm, env, key, value);
if (value.type == GST_NIL) {
gst_table_put(vm, gst_env_nils(vm, env), key, gst_wrap_boolean(1));
}
}
void gst_env_putc(Gst *vm, GstTable *env, const char *key, GstValue value) {
GstValue keyv = gst_string_cv(vm, key);
gst_env_put(vm, env, keyv, value);
}
void gst_env_putvar(Gst *vm, GstTable *env, GstValue key, GstValue value) {
GstTable *meta = gst_env_meta(vm, env);
GstTable *newmeta = gst_table(vm, 4);
GstTable *vars = gst_env_vars(vm, env);
gst_table_put(vm, vars, key, value);
gst_table_put(vm, env, key, gst_wrap_table(vars));
gst_table_put(vm, newmeta, gst_string_cv(vm, "mutable"), gst_wrap_boolean(1));
gst_table_put(vm, meta, key, gst_wrap_table(newmeta));
}
void gst_env_putvarc(Gst *vm, GstTable *env, const char *key, GstValue value) {
GstValue keyv = gst_string_cv(vm, key);
gst_env_putvar(vm, env, keyv, value);
}

View File

@ -481,6 +481,7 @@ void gst_init(Gst *vm) {
/* Set up global env */
vm->modules = gst_table(vm, 10);
vm->registry = gst_table(vm, 10);
vm->env = gst_table(vm, 10);
}
/* Clear all memory associated with the VM */

View File

@ -312,6 +312,7 @@ struct Gst {
GstThread *thread;
GstTable *modules;
GstTable *registry;
GstTable *env;
/* Return state */
const char *crash;
GstValue ret; /* Returned value from gst_start. */
@ -371,6 +372,7 @@ struct GstCompiler {
jmp_buf onError;
GstScope *tail;
GstBuffer *buffer;
GstTable *env;
};
/* Bytecode */
@ -398,7 +400,7 @@ enum GstOpCode {
GST_OP_PAR, /* Push array or tuple */
GST_OP_CAL, /* Call function */
GST_OP_TCL, /* Tail call */
GST_OP_TRN, /* Transfer to new thread */
GST_OP_TRN /* Transfer to new thread */
};
/****/
@ -533,10 +535,8 @@ GstValue gst_parse_consume(GstParser *p);
/***/
void gst_compiler(GstCompiler *c, Gst *vm);
void gst_compiler_nilglobals(GstCompiler *c, GstValue env);
void gst_compiler_globals(GstCompiler *c, GstValue env);
void gst_compiler_mergeenv(GstCompiler *c, GstValue env);
void gst_compiler_global(GstCompiler *c, const char *name, GstValue x);
void gst_compiler_usemodule(GstCompiler *c, const char *modulename);
GstFunction *gst_compiler_compile(GstCompiler *c, GstValue form);
/****/
@ -634,9 +634,21 @@ int gst_hashtable_view(GstValue tab, const GstValue **data, uint32_t *cap);
/* Misc */
/****/
#define GST_ENV_NILS 0
#define GST_ENV_METADATA 1
#define GST_ENV_VARS 2
GstReal gst_integer_to_real(GstInteger x);
GstInteger gst_real_to_integer(GstReal x);
GstInteger gst_startrange(GstInteger raw, uint32_t len);
GstInteger gst_endrange(GstInteger raw, uint32_t len);
void gst_env_merge(Gst *vm, GstTable *destEnv, GstTable *srcEnv);
GstTable *gst_env_nils(Gst *vm, GstTable *env);
GstTable *gst_env_meta(Gst *vm, GstTable *env);
GstTable *gst_env_vars(Gst *vm, GstTable *env);
void gst_env_put(Gst *vm, GstTable *env, GstValue key, GstValue value);
void gst_env_putc(Gst *vm, GstTable *env, const char *key, GstValue value);
void gst_env_putvar(Gst *vm, GstTable *env, GstValue key, GstValue value);
void gst_env_putvarc(Gst *vm, GstTable *env, const char *key, GstValue value);
#endif // GST_H_defined