mirror of
https://github.com/janet-lang/janet
synced 2024-12-28 17:30:31 +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:
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
Block a user