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

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; GstValue func;
/* Try to compile generated AST */ /* Try to compile generated AST */
gst_compiler(&c, vm); gst_compiler(&c, vm);
gst_compiler_usemodule(&c, "std"); gst_env_putc(vm, vm->env, "_", last);
gst_compiler_global(&c, "_", last);
func = gst_wrap_function(gst_compiler_compile(&c, ast)); func = gst_wrap_function(gst_compiler_compile(&c, ast));
/* Check for compilation errors */ /* Check for compilation errors */
if (c.error.type != GST_NIL) { if (c.error.type != GST_NIL) {

View File

@ -22,6 +22,8 @@
#include <gst/gst.h> #include <gst/gst.h>
#define GST_LOCAL_FLAG_MUTABLE 1
/* During compilation, FormOptions are passed to ASTs /* During compilation, FormOptions are passed to ASTs
* as configuration options to allow for some optimizations. */ * as configuration options to allow for some optimizations. */
typedef struct FormOptions FormOptions; typedef struct FormOptions FormOptions;
@ -31,7 +33,7 @@ struct FormOptions {
uint16_t target; uint16_t target;
/* If the result of the value being compiled is not going to /* If the result of the value being compiled is not going to
* be used, some forms can simply return a nil slot and save * be used, some forms can simply return a nil slot and save
* copmutation */ * co,putation */
uint16_t resultUnused : 1; uint16_t resultUnused : 1;
/* Allows the sub expression to evaluate into a /* Allows the sub expression to evaluate into a
* temporary slot of it's choice. A temporary Slot * temporary slot of it's choice. A temporary Slot
@ -89,8 +91,6 @@ struct GstScope {
uint16_t *freeHeap; uint16_t *freeHeap;
GstTable *literals; GstTable *literals;
GstArray *literalsArray; GstArray *literalsArray;
GstTable *namedLiterals;
GstTable *nilNamedLiterals; /* Work around tables not containg nil */
GstTable *locals; GstTable *locals;
GstScope *parent; GstScope *parent;
}; };
@ -126,16 +126,24 @@ static void c_error1(GstCompiler *c, GstValue e) {
longjmp(c->onError, 1); 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 /* Push a new scope in the compiler and return
* a pointer to it for configuration. There is * a pointer to it for configuration. There is
* more configuration that needs to be done if * more configuration that needs to be done if
* the new scope is a function declaration. */ * the new scope is a function declaration. */
static GstScope *compiler_push_scope(GstCompiler *c, int sameFunction) { static GstScope *compiler_push_scope(GstCompiler *c, int sameFunction) {
GstScope *scope = gst_alloc(c->vm, sizeof(GstScope)); GstScope *scope = gst_alloc(c->vm, sizeof(GstScope));
scope->locals = gst_table(c->vm, 10); scope->locals = gst_table(c->vm, 4);
scope->freeHeap = gst_alloc(c->vm, 10 * sizeof(uint16_t)); scope->freeHeap = gst_alloc(c->vm, 4 * sizeof(uint16_t));
scope->heapSize = 0; scope->heapSize = 0;
scope->heapCapacity = 10; scope->heapCapacity = 4;
scope->parent = c->tail; scope->parent = c->tail;
scope->frameSize = 0; scope->frameSize = 0;
scope->touchParent = 0; scope->touchParent = 0;
@ -152,14 +160,10 @@ static GstScope *compiler_push_scope(GstCompiler *c, int sameFunction) {
scope->nextLocal = c->tail->nextLocal; scope->nextLocal = c->tail->nextLocal;
scope->literals = c->tail->literals; scope->literals = c->tail->literals;
scope->literalsArray = c->tail->literalsArray; scope->literalsArray = c->tail->literalsArray;
scope->namedLiterals = c->tail->namedLiterals;
scope->nilNamedLiterals = c->tail->nilNamedLiterals;
} else { } else {
scope->nextLocal = 0; scope->nextLocal = 0;
scope->literals = gst_table(c->vm, 10); scope->literals = gst_table(c->vm, 4);
scope->literalsArray = gst_array(c->vm, 10); scope->literalsArray = gst_array(c->vm, 4);
scope->namedLiterals = gst_table(c->vm, 10);
scope->nilNamedLiterals = gst_table(c->vm, 10);
} }
c->tail = scope; c->tail = scope;
return scope; return scope;
@ -194,7 +198,6 @@ static uint16_t compiler_get_local(GstCompiler *c, GstScope *scope) {
} else { } else {
return scope->freeHeap[--scope->heapSize]; return scope->freeHeap[--scope->heapSize];
} }
return 0;
} }
/* Free a slot on the stack for other locals and/or /* 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; 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 */ /* Helper to get a nil slot */
static Slot nil_slot() { Slot ret; ret.isNil = 1; ret.hasReturned = 0; return ret; } 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. */ /* 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; GstValue x;
uint16_t target; uint16_t target;
if (sym.type != GST_STRING) if (sym.type != GST_STRING)
c_error(c, "expected string"); c_error(c, "expected string");
target = compiler_get_local(c, scope); target = compiler_get_local(c, scope);
x.type = GST_INTEGER; x.type = GST_INTEGER;
x.data.integer = target; x.data.integer = target + (flags << 16);
gst_table_put(c->vm, scope->locals, sym, x); gst_table_put(c->vm, scope->locals, sym, x);
return target; 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. */ * 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; GstScope *scope = c->tail;
GstValue check;
uint32_t currentLevel = scope->level; uint32_t currentLevel = scope->level;
while (scope) { while (scope) {
GstValue check = gst_table_get(scope->locals, x); check = gst_table_get(scope->locals, x);
if (check.type != GST_NIL) { if (check.type != GST_NIL) {
*level = currentLevel - scope->level; *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; 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; 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; return 0;
} }
@ -471,7 +516,7 @@ static Slot compile_symbol(GstCompiler *c, FormOptions opts, GstValue sym) {
uint16_t index = 0; uint16_t index = 0;
uint16_t level = 0; uint16_t level = 0;
Slot ret; Slot ret;
int status = symbol_resolve(c, sym, &level, &index, &lit); int status = symbol_resolve(c, sym, &level, &index, NULL, &lit);
if (!status) { if (!status) {
c_error1(c, sym); c_error1(c, sym);
} }
@ -479,6 +524,16 @@ static Slot compile_symbol(GstCompiler *c, FormOptions opts, GstValue sym) {
if (status == 2) { if (status == 2) {
/* We have a named literal */ /* We have a named literal */
return compile_literal(c, opts, lit); 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) { } else if (level > 0) {
/* We have an upvalue from a parent function. Make /* We have an upvalue from a parent function. Make
* sure that the chain of functions up to the upvalue keep * 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; FormOptions subOpts;
uint16_t target = 0; uint16_t target = 0;
uint16_t level = 0; uint16_t level = 0;
uint16_t flags = 0;
Slot slot; Slot slot;
int status; int status;
subOpts.isTail = 0; subOpts.isTail = 0;
subOpts.resultUnused = 0; subOpts.resultUnused = 0;
status = symbol_resolve(c, left, &level, &target, &lit); status = symbol_resolve(c, left, &level, &target, &flags, &lit);
if (status == 2) { if (status == 1) {
c_error(c, "cannot set binding"); if (!(flags & GST_LOCAL_FLAG_MUTABLE))
} else if (status == 1) { c_error(c, "cannot varset immutable binding");
/* Check if we have an up value. Otherwise, it's just a normal /* Check if we have an up value. Otherwise, it's just a normal
* local variable */ * local variable */
if (level != 0) { if (level != 0) {
@ -547,11 +603,21 @@ static Slot compile_assign(GstCompiler *c, FormOptions opts, GstValue left, GstV
subOpts.target = target; subOpts.target = target;
slot = compile_value(c, subOpts, right); 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 { } else {
/* We need to declare a new symbol */ c_error(c, "cannot varset immutable binding");
subOpts.target = compiler_declare_symbol(c, scope, left);
subOpts.canChoose = 0;
slot = compile_value(c, subOpts, right);
} }
if (opts.resultUnused) { if (opts.resultUnused) {
compiler_drop_slot(c, scope, slot); 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 /* Compile series of expressions. This compiles the meat of
* function definitions and the inside of do forms. */ * function definitions and the inside of do forms. */
static Slot compile_block(GstCompiler *c, FormOptions opts, const GstValue *form, uint32_t startIndex) { 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 /* The compiler puts the parameter locals
* in the right place by default - at the beginning * in the right place by default - at the beginning
* of the stack frame. */ * 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 /* Mark where we are on the stack so we can
* return to it later. */ * return to it later. */
@ -832,13 +979,6 @@ static Slot compile_quote(GstCompiler *c, FormOptions opts, const GstValue *form
return ret; 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 */ /* Apply special */
static Slot compile_apply(GstCompiler *c, FormOptions opts, const GstValue *form) { static Slot compile_apply(GstCompiler *c, FormOptions opts, const GstValue *form) {
GstScope *scope = c->tail; GstScope *scope = c->tail;
@ -927,7 +1067,7 @@ static SpecialFormHelper get_special(const GstValue *form) {
/* One character specials. */ /* One character specials. */
if (gst_string_length(name) == 1) { if (gst_string_length(name) == 1) {
switch(name[0]) { switch(name[0]) {
case ':': return compile_var; case ':': return compile_def;
default: default:
break; break;
} }
@ -950,6 +1090,10 @@ static SpecialFormHelper get_special(const GstValue *form) {
if (gst_string_length(name) == 2 && if (gst_string_length(name) == 2 &&
name[1] == 'o') { name[1] == 'o') {
return compile_do; return compile_do;
} else if (gst_string_length(name) == 3 &&
name[1] == 'e' &&
name[2] == 'f') {
return compile_def;
} }
} }
break; break;
@ -990,6 +1134,23 @@ static SpecialFormHelper get_special(const GstValue *form) {
} }
} }
break; 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': case 'w':
{ {
if (gst_string_length(name) == 5 && if (gst_string_length(name) == 5 &&
@ -1057,7 +1218,7 @@ static Slot compile_table(GstCompiler *c, FormOptions opts, GstTable *tab) {
return ret; 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) { static Slot compile_form(GstCompiler *c, FormOptions opts, const GstValue *form) {
GstScope *scope = c->tail; GstScope *scope = c->tail;
GstBuffer *buffer = c->buffer; GstBuffer *buffer = c->buffer;
@ -1136,50 +1297,10 @@ void gst_compiler(GstCompiler *c, Gst *vm) {
c->buffer = gst_buffer(vm, 128); c->buffer = gst_buffer(vm, 128);
c->tail = NULL; c->tail = NULL;
c->error.type = GST_NIL; c->error.type = GST_NIL;
c->env = vm->env;
compiler_push_scope(c, 0); 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 /* Compile interface. Returns a function that evaluates the
* given AST. Returns NULL if there was an error during compilation. */ * given AST. Returns NULL if there was an error during compilation. */
GstFunction *gst_compiler_compile(GstCompiler *c, GstValue form) { 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"); c->error = gst_string_cv(c->vm, "unknown error");
return NULL; return NULL;
} }
/* Create a scope */
opts.isTail = 1; opts.isTail = 1;
compiler_return(c, compile_value(c, opts, form)); compiler_return(c, compile_value(c, opts, form));
def = compiler_gen_funcdef(c, c->buffer->count, 0, 0); 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_thread(vm->thread));
gst_mark_value(vm, gst_wrap_table(vm->modules)); 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->registry));
gst_mark_value(vm, gst_wrap_table(vm->env));
gst_mark_value(vm, vm->ret); gst_mark_value(vm, vm->ret);
if (vm->scratch) if (vm->scratch)
gc_header(vm->scratch)->color = vm->black; gc_header(vm->scratch)->color = vm->black;

View File

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

View File

@ -710,6 +710,28 @@ int gst_stl_funcparent(Gst *vm) {
return GST_RETURN_OK; 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 */ /* IO */
/****/ /****/
@ -1038,13 +1060,11 @@ static int gst_stl_parse(Gst *vm) {
/* Compile a value */ /* Compile a value */
static int gst_stl_compile(Gst *vm) { static int gst_stl_compile(Gst *vm) {
GstFunction *ret; GstFunction *ret;
GstValue std;
GstCompiler c; GstCompiler c;
gst_compiler(&c, vm); gst_compiler(&c, vm);
std = gst_table_get(vm->modules, gst_string_cv(vm, "std")); if (gst_arg(vm, 1).type == GST_TABLE) {
gst_compiler_globals(&c, std); c.env = gst_arg(vm, 1).data.table;
gst_compiler_globals(&c, gst_arg(vm, 1)); }
gst_compiler_nilglobals(&c, gst_arg(vm, 2));
ret = gst_compiler_compile(&c, gst_arg(vm, 0)); ret = gst_compiler_compile(&c, gst_arg(vm, 0));
if (!ret) if (!ret)
gst_c_throw(vm, c.error); gst_c_throw(vm, c.error);
@ -1086,8 +1106,8 @@ static const GstModuleItem std_module[] = {
{"parse-hasvalue", gst_stl_parser_hasvalue}, {"parse-hasvalue", gst_stl_parser_hasvalue},
{"parse-charseq", gst_stl_parser_charseq}, {"parse-charseq", gst_stl_parser_charseq},
{"parse-status", gst_stl_parser_status}, {"parse-status", gst_stl_parser_status},
{"parse-status", gst_stl_parser_status}, {"parse-status", gst_stl_parser_status},
{"parse", gst_stl_parse}, {"parse", gst_stl_parse},
/* Compile */ /* Compile */
{"compile", gst_stl_compile}, {"compile", gst_stl_compile},
/* Other */ /* Other */
@ -1134,11 +1154,14 @@ static const GstModuleItem std_module[] = {
{"funcparent", gst_stl_funcparent}, {"funcparent", gst_stl_funcparent},
{"gcollect", gst_stl_gcollect}, {"gcollect", gst_stl_gcollect},
{"debugp", gst_stl_debugp}, {"debugp", gst_stl_debugp},
{"global-def", gst_stl_def},
{"global-var", gst_stl_var},
{NULL, NULL} {NULL, NULL}
}; };
/* Load all libraries */ /* Load all libraries */
void gst_stl_load(Gst *vm) { void gst_stl_load(Gst *vm) {
GstValue maybeEnv;
/* Load the normal c functions */ /* Load the normal c functions */
gst_module_mutable(vm, "std", std_module); gst_module_mutable(vm, "std", std_module);
/* Wrap stdin and stdout */ /* 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", "stdin", gst_wrap_userdata(inp));
gst_module_put(vm, "std", "stdout", gst_wrap_userdata(outp)); gst_module_put(vm, "std", "stdout", gst_wrap_userdata(outp));
gst_module_put(vm, "std", "stderr", 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); gst_thread_popframe(vm, vm->thread);
return result; 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 */ /* Set up global env */
vm->modules = gst_table(vm, 10); vm->modules = gst_table(vm, 10);
vm->registry = gst_table(vm, 10); vm->registry = gst_table(vm, 10);
vm->env = gst_table(vm, 10);
} }
/* Clear all memory associated with the VM */ /* Clear all memory associated with the VM */

View File

@ -312,6 +312,7 @@ struct Gst {
GstThread *thread; GstThread *thread;
GstTable *modules; GstTable *modules;
GstTable *registry; GstTable *registry;
GstTable *env;
/* Return state */ /* Return state */
const char *crash; const char *crash;
GstValue ret; /* Returned value from gst_start. */ GstValue ret; /* Returned value from gst_start. */
@ -371,6 +372,7 @@ struct GstCompiler {
jmp_buf onError; jmp_buf onError;
GstScope *tail; GstScope *tail;
GstBuffer *buffer; GstBuffer *buffer;
GstTable *env;
}; };
/* Bytecode */ /* Bytecode */
@ -398,7 +400,7 @@ enum GstOpCode {
GST_OP_PAR, /* Push array or tuple */ GST_OP_PAR, /* Push array or tuple */
GST_OP_CAL, /* Call function */ GST_OP_CAL, /* Call function */
GST_OP_TCL, /* Tail call */ 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(GstCompiler *c, Gst *vm);
void gst_compiler_nilglobals(GstCompiler *c, GstValue env); void gst_compiler_mergeenv(GstCompiler *c, GstValue env);
void gst_compiler_globals(GstCompiler *c, GstValue env);
void gst_compiler_global(GstCompiler *c, const char *name, GstValue x); 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); 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 */ /* Misc */
/****/ /****/
#define GST_ENV_NILS 0
#define GST_ENV_METADATA 1
#define GST_ENV_VARS 2
GstReal gst_integer_to_real(GstInteger x); GstReal gst_integer_to_real(GstInteger x);
GstInteger gst_real_to_integer(GstReal x); GstInteger gst_real_to_integer(GstReal x);
GstInteger gst_startrange(GstInteger raw, uint32_t len); GstInteger gst_startrange(GstInteger raw, uint32_t len);
GstInteger gst_endrange(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 #endif // GST_H_defined