mirror of https://github.com/janet-lang/janet
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:
parent
c3d65cb91d
commit
29a39c47b0
|
@ -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) {
|
||||
|
|
304
core/compile.c
304
core/compile.c
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
*/
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include "cache.h"
|
||||
|
||||
/****/
|
||||
/* Cache */
|
||||
|
|
40
core/stl.c
40
core/stl.c
|
@ -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);
|
||||
}
|
||||
|
|
106
core/util.c
106
core/util.c
|
@ -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);
|
||||
}
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue