1
0
mirror of https://github.com/janet-lang/janet synced 2025-01-10 23:50:26 +00:00

Fix some bugs with inner closures.

This commit is contained in:
bakpakin 2018-01-21 14:39:32 -05:00
parent d68eae9592
commit 911b0b15e8
9 changed files with 236 additions and 234 deletions

View File

@ -46,7 +46,6 @@
typedef struct DstInstructionDef DstInstructionDef; typedef struct DstInstructionDef DstInstructionDef;
struct DstInstructionDef { struct DstInstructionDef {
const char *name; const char *name;
DstInstructionType type;
DstOpCode opcode; DstOpCode opcode;
}; };
@ -76,63 +75,63 @@ struct DstAssembler {
* time and is easier to setup statically than a hash table or * time and is easier to setup statically than a hash table or
* prefix tree. */ * prefix tree. */
static const DstInstructionDef dst_ops[] = { static const DstInstructionDef dst_ops[] = {
{"add", DIT_SSS, DOP_ADD}, {"add", DOP_ADD},
{"add-immediate", DIT_SSI, DOP_ADD_IMMEDIATE}, {"add-immediate", DOP_ADD_IMMEDIATE},
{"add-integer", DIT_SSS, DOP_ADD_INTEGER}, {"add-integer", DOP_ADD_INTEGER},
{"add-real", DIT_SSS, DOP_ADD_REAL}, {"add-real", DOP_ADD_REAL},
{"band", DIT_SSS, DOP_BAND}, {"band", DOP_BAND},
{"bnot", DIT_SS, DOP_BNOT}, {"bnot", DOP_BNOT},
{"bor", DIT_SSS, DOP_BOR}, {"bor", DOP_BOR},
{"bxor", DIT_SSS, DOP_BXOR}, {"bxor", DOP_BXOR},
{"call", DIT_SS, DOP_CALL}, {"call", DOP_CALL},
{"closure", DIT_SD, DOP_CLOSURE}, {"closure", DOP_CLOSURE},
{"compare", DIT_SSS, DOP_COMPARE}, {"compare", DOP_COMPARE},
{"divide", DIT_SSS, DOP_DIVIDE}, {"divide", DOP_DIVIDE},
{"divide-immediate", DIT_SSI, DOP_DIVIDE_IMMEDIATE}, {"divide-immediate", DOP_DIVIDE_IMMEDIATE},
{"divide-integer", DIT_SSS, DOP_DIVIDE_INTEGER}, {"divide-integer", DOP_DIVIDE_INTEGER},
{"divide-real", DIT_SSS, DOP_DIVIDE_REAL}, {"divide-real", DOP_DIVIDE_REAL},
{"equals", DIT_SSS, DOP_EQUALS}, {"equals", DOP_EQUALS},
{"error", DIT_S, DOP_ERROR}, {"error", DOP_ERROR},
{"get", DIT_SSS, DOP_GET}, {"get", DOP_GET},
{"get-index", DIT_SSU, DOP_GET_INDEX}, {"get-index", DOP_GET_INDEX},
{"greater-than", DIT_SSS, DOP_GREATER_THAN}, {"greater-than", DOP_GREATER_THAN},
{"jump", DIT_L, DOP_JUMP}, {"jump", DOP_JUMP},
{"jump-if", DIT_SL, DOP_JUMP_IF}, {"jump-if", DOP_JUMP_IF},
{"jump-if-not", DIT_SL, DOP_JUMP_IF_NOT}, {"jump-if-not", DOP_JUMP_IF_NOT},
{"less-than", DIT_SSS, DOP_LESS_THAN}, {"less-than", DOP_LESS_THAN},
{"load-constant", DIT_SC, DOP_LOAD_CONSTANT}, {"load-constant", DOP_LOAD_CONSTANT},
{"load-false", DIT_S, DOP_LOAD_FALSE}, {"load-false", DOP_LOAD_FALSE},
{"load-integer", DIT_SI, DOP_LOAD_INTEGER}, {"load-integer", DOP_LOAD_INTEGER},
{"load-nil", DIT_S, DOP_LOAD_NIL}, {"load-nil", DOP_LOAD_NIL},
{"load-self", DIT_S, DOP_LOAD_SELF}, {"load-self", DOP_LOAD_SELF},
{"load-true", DIT_S, DOP_LOAD_TRUE}, {"load-true", DOP_LOAD_TRUE},
{"load-upvalue", DIT_SES, DOP_LOAD_UPVALUE}, {"load-upvalue", DOP_LOAD_UPVALUE},
{"move-far", DIT_SS, DOP_MOVE_FAR}, {"move-far", DOP_MOVE_FAR},
{"move-near", DIT_SS, DOP_MOVE_NEAR}, {"move-near", DOP_MOVE_NEAR},
{"multiply", DIT_SSS, DOP_MULTIPLY}, {"multiply", DOP_MULTIPLY},
{"multiply-immediate", DIT_SSI, DOP_MULTIPLY_IMMEDIATE}, {"multiply-immediate", DOP_MULTIPLY_IMMEDIATE},
{"multiply-integer", DIT_SSS, DOP_MULTIPLY_INTEGER}, {"multiply-integer", DOP_MULTIPLY_INTEGER},
{"multiply-real", DIT_SSS, DOP_MULTIPLY_REAL}, {"multiply-real", DOP_MULTIPLY_REAL},
{"noop", DIT_0, DOP_NOOP}, {"noop", DOP_NOOP},
{"push", DIT_S, DOP_PUSH}, {"push", DOP_PUSH},
{"push-array", DIT_S, DOP_PUSH_ARRAY}, {"push-array", DOP_PUSH_ARRAY},
{"push2", DIT_SS, DOP_PUSH_2}, {"push2", DOP_PUSH_2},
{"push3", DIT_SSS, DOP_PUSH_3}, {"push3", DOP_PUSH_3},
{"put", DIT_SSS, DOP_PUT}, {"put", DOP_PUT},
{"put-index", DIT_SSU, DOP_PUT_INDEX}, {"put-index", DOP_PUT_INDEX},
{"return", DIT_S, DOP_RETURN}, {"return", DOP_RETURN},
{"return-nil", DIT_0, DOP_RETURN_NIL}, {"return-nil", DOP_RETURN_NIL},
{"set-upvalue", DIT_SES, DOP_SET_UPVALUE}, {"set-upvalue", DOP_SET_UPVALUE},
{"shift-left", DIT_SSS, DOP_SHIFT_LEFT}, {"shift-left", DOP_SHIFT_LEFT},
{"shift-left-immediate", DIT_SSI, DOP_SHIFT_LEFT_IMMEDIATE}, {"shift-left-immediate", DOP_SHIFT_LEFT_IMMEDIATE},
{"shift-right", DIT_SSS, DOP_SHIFT_RIGHT}, {"shift-right", DOP_SHIFT_RIGHT},
{"shift-right-immediate", DIT_SSI, DOP_SHIFT_RIGHT_IMMEDIATE}, {"shift-right-immediate", DOP_SHIFT_RIGHT_IMMEDIATE},
{"shift-right-unsigned", DIT_SSS, DOP_SHIFT_RIGHT_UNSIGNED}, {"shift-right-unsigned", DOP_SHIFT_RIGHT_UNSIGNED},
{"shift-right-unsigned-immediate", DIT_SSS, DOP_SHIFT_RIGHT_UNSIGNED_IMMEDIATE}, {"shift-right-unsigned-immediate", DOP_SHIFT_RIGHT_UNSIGNED_IMMEDIATE},
{"subtract", DIT_SSS, DOP_SUBTRACT}, {"subtract", DOP_SUBTRACT},
{"tailcall", DIT_S, DOP_TAILCALL}, {"tailcall", DOP_TAILCALL},
{"transfer", DIT_SSS, DOP_TRANSFER}, {"transfer", DOP_TRANSFER},
{"typecheck", DIT_ST, DOP_TYPECHECK}, {"typecheck", DOP_TYPECHECK},
}; };
/* Check a dst string against a bunch of test_strings. Return the /* Check a dst string against a bunch of test_strings. Return the
@ -337,7 +336,8 @@ static uint32_t read_instruction(
const DstInstructionDef *idef, const DstInstructionDef *idef,
const Dst *argt) { const Dst *argt) {
uint32_t instr = idef->opcode; uint32_t instr = idef->opcode;
switch (idef->type) { DstInstructionType type = dst_instructions[idef->opcode];
switch (type) {
case DIT_0: case DIT_0:
{ {
if (dst_tuple_length(argt) != 1) if (dst_tuple_length(argt) != 1)
@ -388,7 +388,7 @@ static uint32_t read_instruction(
if (dst_tuple_length(argt) != 3) if (dst_tuple_length(argt) != 3)
dst_asm_error(a, "expected 2 arguments: (op, slot, integer)"); dst_asm_error(a, "expected 2 arguments: (op, slot, integer)");
instr |= doarg(a, DST_OAT_SLOT, 1, 1, 0, argt[1]); instr |= doarg(a, DST_OAT_SLOT, 1, 1, 0, argt[1]);
instr |= doarg(a, DST_OAT_INTEGER, 2, 2, idef->type == DIT_SI, argt[2]); instr |= doarg(a, DST_OAT_INTEGER, 2, 2, type == DIT_SI, argt[2]);
break; break;
} }
case DIT_SD: case DIT_SD:
@ -415,7 +415,7 @@ static uint32_t read_instruction(
dst_asm_error(a, "expected 3 arguments: (op, slot, slot, integer)"); dst_asm_error(a, "expected 3 arguments: (op, slot, slot, integer)");
instr |= doarg(a, DST_OAT_SLOT, 1, 1, 0, argt[1]); instr |= doarg(a, DST_OAT_SLOT, 1, 1, 0, argt[1]);
instr |= doarg(a, DST_OAT_SLOT, 2, 1, 0, argt[2]); instr |= doarg(a, DST_OAT_SLOT, 2, 1, 0, argt[2]);
instr |= doarg(a, DST_OAT_INTEGER, 3, 1, idef->type == DIT_SSI, argt[3]); instr |= doarg(a, DST_OAT_INTEGER, 3, 1, type == DIT_SSI, argt[3]);
break; break;
} }
case DIT_SES: case DIT_SES:
@ -744,7 +744,7 @@ Dst dst_asm_decode_instruction(uint32_t instr) {
} }
name = dst_csymbolv(def->name); name = dst_csymbolv(def->name);
#define oparg(shift, mask) ((instr >> ((shift) << 3)) & (mask)) #define oparg(shift, mask) ((instr >> ((shift) << 3)) & (mask))
switch (def->type) { switch (dst_instructions[def->opcode]) {
case DIT_0: case DIT_0:
return tup1(name); return tup1(name);
case DIT_S: case DIT_S:

View File

@ -141,6 +141,7 @@ void dstc_nameslot(DstCompiler *c, const uint8_t *sym, DstSlot s) {
SymPair sp; SymPair sp;
sp.sym = sym; sp.sym = sym;
sp.slot = s; sp.slot = s;
sp.keep = 0;
sp.slot.flags |= DST_SLOT_NAMED; sp.slot.flags |= DST_SLOT_NAMED;
dst_v_push(scope->syms, sp); dst_v_push(scope->syms, sp);
} }
@ -174,20 +175,33 @@ void dstc_popscope(DstCompiler *c) {
int32_t oldcount = dst_v_count(c->scopes); int32_t oldcount = dst_v_count(c->scopes);
dst_assert(oldcount, "could not pop scope"); dst_assert(oldcount, "could not pop scope");
scope = dst_v_last(c->scopes); scope = dst_v_last(c->scopes);
dst_v_pop(c->scopes);
/* Move free slots to parent scope if not a new function.
* We need to know the total number of slots used when compiling the function. */
if (!(scope.flags & (DST_SCOPE_FUNCTION | DST_SCOPE_UNUSED)) && oldcount > 1) {
int32_t i;
DstScope *newscope = &dst_v_last(c->scopes);
if (newscope->smax < scope.smax)
newscope->smax = scope.smax;
/* Keep upvalue slots */
for (i = 0; i < dst_v_count(scope.syms); i++) {
SymPair pair = scope.syms[i];
if (pair.keep) {
/* The variable should not be lexically accessible */
pair.sym = NULL;
dst_v_push(newscope->syms, pair);
slotalloci(c, pair.slot.index);
}
}
}
/* Free the scope */ /* Free the scope */
dst_v_free(scope.consts); dst_v_free(scope.consts);
dst_v_free(scope.syms); dst_v_free(scope.syms);
dst_v_free(scope.envs); dst_v_free(scope.envs);
dst_v_free(scope.defs); dst_v_free(scope.defs);
dst_v_free(scope.slots); dst_v_free(scope.slots);
dst_v_pop(c->scopes);
/* Move free slots to parent scope if not a new function.
* We need to know the total number of slots used when compiling the function. */
if (!(scope.flags & (DST_SCOPE_FUNCTION | DST_SCOPE_UNUSED)) && oldcount > 1) {
DstScope *newscope = &dst_v_last(c->scopes);
if (newscope->smax < scope.smax)
newscope->smax = scope.smax;
}
} }
/* Leave a scope but keep a slot allocated. */ /* Leave a scope but keep a slot allocated. */
@ -217,6 +231,7 @@ DstSlot dstc_resolve(
DstSlot ret = dstc_cslot(dst_wrap_nil()); DstSlot ret = dstc_cslot(dst_wrap_nil());
DstScope *top = &dst_v_last(c->scopes); DstScope *top = &dst_v_last(c->scopes);
DstScope *scope = top; DstScope *scope = top;
SymPair *pair;
int foundlocal = 1; int foundlocal = 1;
int unused = 0; int unused = 0;
@ -227,8 +242,9 @@ DstSlot dstc_resolve(
unused = 1; unused = 1;
len = dst_v_count(scope->syms); len = dst_v_count(scope->syms);
for (i = 0; i < len; i++) { for (i = 0; i < len; i++) {
if (scope->syms[i].sym == sym) { pair = scope->syms + i;
ret = scope->syms[i].slot; if (pair->sym == sym) {
ret = pair->slot;
goto found; goto found;
} }
} }
@ -272,14 +288,12 @@ DstSlot dstc_resolve(
} }
/* non-local scope needs to expose its environment */ /* non-local scope needs to expose its environment */
if (!foundlocal) { pair->keep = 1;
/* Find function scope */
while (scope >= c->scopes && !(scope->flags & DST_SCOPE_FUNCTION)) scope--; while (scope >= c->scopes && !(scope->flags & DST_SCOPE_FUNCTION)) scope--;
dst_assert(scope >= c->scopes, "invalid scopes"); dst_assert(scope >= c->scopes, "invalid scopes");
scope->flags |= DST_SCOPE_ENV; scope->flags |= DST_SCOPE_ENV;
if (!dst_v_count(scope->envs)) dst_v_push(scope->envs, 0); if (!dst_v_count(scope->envs)) dst_v_push(scope->envs, 0);
scope++; scope++;
}
/* Propogate env up to current scope */ /* Propogate env up to current scope */
int32_t envindex = 0; int32_t envindex = 0;
@ -314,13 +328,7 @@ DstSlot dstc_resolve(
/* Emit a raw instruction with source mapping. */ /* Emit a raw instruction with source mapping. */
void dstc_emit(DstCompiler *c, DstAst *ast, uint32_t instr) { void dstc_emit(DstCompiler *c, DstAst *ast, uint32_t instr) {
dst_v_push(c->buffer, instr); dst_v_push(c->buffer, instr);
if (NULL != ast) { dst_v_push(c->mapbuffer, ast);
dst_v_push(c->mapbuffer, ast->source_start);
dst_v_push(c->mapbuffer, ast->source_end);
} else {
dst_v_push(c->mapbuffer, -1);
dst_v_push(c->mapbuffer, -1);
}
} }
/* Add a constant to the current scope. Return the index of the constant. */ /* Add a constant to the current scope. Return the index of the constant. */
@ -782,6 +790,9 @@ DstSlot dstc_value(DstFopts opts, Dst x) {
ret = dstc_tablector(opts, ast, x, dst_cfun_table); ret = dstc_tablector(opts, ast, x, dst_cfun_table);
break; break;
} }
if (dstc_iserr(&opts)) {
return dstc_cslot(dst_wrap_nil());
}
if (opts.flags & DST_FOPTS_TAIL) { if (opts.flags & DST_FOPTS_TAIL) {
ret = dstc_return(opts.compiler, ast, ret); ret = dstc_return(opts.compiler, ast, ret);
} }
@ -799,6 +810,8 @@ DstFuncDef *dstc_pop_funcdef(DstCompiler *c) {
DstFuncDef *def = dst_funcdef_alloc(); DstFuncDef *def = dst_funcdef_alloc();
def->slotcount = scope.smax + 1; def->slotcount = scope.smax + 1;
dst_assert(scope.flags & DST_SCOPE_FUNCTION, "expected function scope");
/* Copy envs */ /* Copy envs */
def->environments_length = dst_v_count(scope.envs); def->environments_length = dst_v_count(scope.envs);
if (def->environments_length > 1) def->environments = dst_v_flatten(scope.envs); if (def->environments_length > 1) def->environments = dst_v_flatten(scope.envs);
@ -820,11 +833,22 @@ DstFuncDef *dstc_pop_funcdef(DstCompiler *c) {
memcpy(def->bytecode, c->buffer + scope.bytecode_start, s); memcpy(def->bytecode, c->buffer + scope.bytecode_start, s);
dst_v__cnt(c->buffer) = scope.bytecode_start; dst_v__cnt(c->buffer) = scope.bytecode_start;
if (NULL != c->mapbuffer) { if (NULL != c->mapbuffer) {
int32_t i;
size_t s = sizeof(int32_t) * 2 * dst_v_count(c->mapbuffer);
def->sourcemap = malloc(2 * s); def->sourcemap = malloc(2 * s);
if (NULL == def->sourcemap) { if (NULL == def->sourcemap) {
DST_OUT_OF_MEMORY; DST_OUT_OF_MEMORY;
} }
memcpy(def->sourcemap, c->mapbuffer + scope.bytecode_start, 2 * s); for (i = 0; i < dst_v_count(c->mapbuffer); i++) {
DstAst *a = c->mapbuffer[i];
if (a) {
def->sourcemap[2 * i] = a->source_start;
def->sourcemap[2 * i + 1] = a->source_end;
} else {
def->sourcemap[2 * i] = -1;
def->sourcemap[2 * i + 1] = -1;
}
}
dst_v__cnt(c->mapbuffer) = scope.bytecode_start; dst_v__cnt(c->mapbuffer) = scope.bytecode_start;
} }
} }

View File

@ -24,7 +24,6 @@
#define DST_COMPILE_H #define DST_COMPILE_H
#include <dst/dst.h> #include <dst/dst.h>
#include <setjmp.h>
#include <dst/dstcompile.h> #include <dst/dstcompile.h>
#include <dst/dstopcodes.h> #include <dst/dstopcodes.h>
@ -68,6 +67,7 @@ typedef struct DstSM {
/* A symbol and slot pair */ /* A symbol and slot pair */
typedef struct SymPair { typedef struct SymPair {
const uint8_t *sym; const uint8_t *sym;
int keep;
DstSlot slot; DstSlot slot;
} SymPair; } SymPair;
@ -105,7 +105,7 @@ struct DstCompiler {
DstScope *scopes; DstScope *scopes;
uint32_t *buffer; uint32_t *buffer;
int32_t *mapbuffer; DstAst **mapbuffer;
/* Hold the environment */ /* Hold the environment */
DstTable *env; DstTable *env;

View File

@ -80,23 +80,51 @@ static void handleattr(DstCompiler *c, int32_t argn, const Dst *argv, DstTable *
} }
} }
DstSlot dstc_var(DstFopts opts, DstAst *ast, int32_t argn, const Dst *argv) { static DstSlot dohead(DstCompiler *c, DstFopts opts, DstAst *ast, Dst *head, int32_t argn, const Dst *argv) {
DstCompiler *c = opts.compiler;
DstFopts subopts = dstc_fopts_default(c); DstFopts subopts = dstc_fopts_default(c);
Dst head;
DstSlot ret; DstSlot ret;
if (argn < 2) { if (argn < 2) {
dstc_cerror(c, ast, "expected at least 2 arguments"); dstc_cerror(c, ast, "expected at least 2 arguments");
return dstc_cslot(dst_wrap_nil()); return dstc_cslot(dst_wrap_nil());
} }
head = dst_ast_unwrap1(argv[0]); *head = dst_ast_unwrap1(argv[0]);
if (!dst_checktype(head, DST_SYMBOL)) { if (!dst_checktype(*head, DST_SYMBOL)) {
dstc_cerror(c, dst_ast_node(argv[0]), "expected symbol"); dstc_cerror(c, dst_ast_node(argv[0]), "expected symbol");
return dstc_cslot(dst_wrap_nil()); return dstc_cslot(dst_wrap_nil());
} }
subopts.flags = opts.flags & ~DST_FOPTS_TAIL; subopts.flags = opts.flags & ~(DST_FOPTS_TAIL | DST_FOPTS_DROP);
subopts.hint = opts.hint; subopts.hint = opts.hint;
ret = dstc_value(subopts, argv[argn - 1]); ret = dstc_value(subopts, argv[argn - 1]);
return ret;
}
/* Def or var a symbol in a local scope */
static DstSlot namelocal(DstCompiler *c, DstAst *ast, Dst head, int32_t flags, DstSlot ret) {
/* Non root scope, bring to local slot */
if (ret.flags & DST_SLOT_NAMED ||
ret.envindex != 0 ||
ret.index < 0 ||
ret.index > 0xFF) {
/* Slot is not able to be named */
DstSlot localslot;
localslot.index = dstc_lsloti(c);
/* infer type? */
localslot.flags = flags;
localslot.envindex = 0;
localslot.constant = dst_wrap_nil();
dstc_copy(c, ast, localslot, ret);
ret = localslot;
}
dstc_nameslot(c, dst_unwrap_symbol(head), ret);
ret.flags |= DST_SLOT_NAMED;
return ret;
}
DstSlot dstc_var(DstFopts opts, DstAst *ast, int32_t argn, const Dst *argv) {
DstCompiler *c = opts.compiler;
Dst head;
DstSlot ret = dohead(c, opts, ast, &head, argn, argv);
if (dstc_iserr(&opts)) return dstc_cslot(dst_wrap_nil());
if (dst_v_last(c->scopes).flags & DST_SCOPE_TOP) { if (dst_v_last(c->scopes).flags & DST_SCOPE_TOP) {
DstSlot refslot, refarrayslot; DstSlot refslot, refarrayslot;
/* Global var, generate var */ /* Global var, generate var */
@ -120,43 +148,17 @@ DstSlot dstc_var(DstFopts opts, DstAst *ast, int32_t argn, const Dst *argv) {
dstc_postread(c, ret, retindex); dstc_postread(c, ret, retindex);
ret = refslot; ret = refslot;
} else { } else {
/* Non root scope, bring to local slot */ ret = namelocal(c, ast, head, DST_SLOT_NAMED | DST_SLOT_MUTABLE, ret);
if (ret.flags & DST_SLOT_NAMED ||
ret.envindex != 0 ||
ret.index < 0 ||
ret.index > 0xFF) {
/* Slot is not able to be named */
DstSlot localslot;
localslot.index = dstc_lsloti(c);
/* infer type? */
localslot.flags = DST_SLOT_NAMED | DST_SLOT_MUTABLE;
localslot.envindex = 0;
localslot.constant = dst_wrap_nil();
dstc_copy(c, ast, localslot, ret);
ret = localslot;
}
dstc_nameslot(c, dst_unwrap_symbol(head), ret);
} }
return ret; return ret;
} }
DstSlot dstc_def(DstFopts opts, DstAst *ast, int32_t argn, const Dst *argv) { DstSlot dstc_def(DstFopts opts, DstAst *ast, int32_t argn, const Dst *argv) {
DstCompiler *c = opts.compiler; DstCompiler *c = opts.compiler;
DstFopts subopts = dstc_fopts_default(c);
DstSlot ret;
Dst head; Dst head;
if (argn < 2) { opts.flags &= ~DST_FOPTS_HINT;
dstc_cerror(c, ast, "expected at least 2 arguments"); DstSlot ret = dohead(c, opts, ast, &head, argn, argv);
return dstc_cslot(dst_wrap_nil()); if (dstc_iserr(&opts)) return dstc_cslot(dst_wrap_nil());
}
head = dst_ast_unwrap1(argv[0]);
if (!dst_checktype(head, DST_SYMBOL)) {
dstc_cerror(c, dst_ast_node(argv[0]), "expected symbol");
return dstc_cslot(dst_wrap_nil());
}
subopts.flags = opts.flags & ~DST_FOPTS_TAIL;
ret = dstc_value(subopts, argv[argn - 1]);
ret.flags |= DST_SLOT_NAMED;
if (dst_v_last(c->scopes).flags & DST_SCOPE_TOP) { if (dst_v_last(c->scopes).flags & DST_SCOPE_TOP) {
DstTable *tab = dst_table(2); DstTable *tab = dst_table(2);
int32_t tableindex, valsymindex, valueindex; int32_t tableindex, valsymindex, valueindex;
@ -180,8 +182,7 @@ DstSlot dstc_def(DstFopts opts, DstAst *ast, int32_t argn, const Dst *argv) {
dstc_postread(c, valsym, valsymindex); dstc_postread(c, valsym, valsymindex);
dstc_postread(c, ret, valueindex); dstc_postread(c, ret, valueindex);
} else { } else {
/* Non root scope, simple slot alias */ ret = namelocal(c, ast, head, DST_SLOT_NAMED, ret);
dstc_nameslot(c, dst_unwrap_symbol(head), ret);
} }
return ret; return ret;
} }
@ -205,7 +206,6 @@ DstSlot dstc_if(DstFopts opts, DstAst *ast, int32_t argn, const Dst *argv) {
Dst truebody, falsebody; Dst truebody, falsebody;
const int tail = opts.flags & DST_FOPTS_TAIL; const int tail = opts.flags & DST_FOPTS_TAIL;
const int drop = opts.flags & DST_FOPTS_DROP; const int drop = opts.flags & DST_FOPTS_DROP;
(void) argv;
if (argn < 2 || argn > 3) { if (argn < 2 || argn > 3) {
dstc_cerror(c, ast, "expected 2 or 3 arguments to if"); dstc_cerror(c, ast, "expected 2 or 3 arguments to if");
@ -225,7 +225,7 @@ DstSlot dstc_if(DstFopts opts, DstAst *ast, int32_t argn, const Dst *argv) {
/* Check constant condition. */ /* Check constant condition. */
/* TODO: Use type info for more short circuits */ /* TODO: Use type info for more short circuits */
if ((cond.flags & DST_SLOT_CONSTANT) && !(cond.flags & DST_SLOT_REF)) { if (cond.flags & DST_SLOT_CONSTANT) {
if (!dst_truthy(cond.constant)) { if (!dst_truthy(cond.constant)) {
/* Swap the true and false bodies */ /* Swap the true and false bodies */
Dst temp = falsebody; Dst temp = falsebody;
@ -490,6 +490,7 @@ DstSlot dstc_fn(DstFopts opts, DstAst *ast, int32_t argn, const Dst *argv) {
subopts.flags = argi == (argn - 1) ? DST_FOPTS_TAIL : DST_FOPTS_DROP; subopts.flags = argi == (argn - 1) ? DST_FOPTS_TAIL : DST_FOPTS_DROP;
s = dstc_value(subopts, argv[argi]); s = dstc_value(subopts, argv[argi]);
dstc_freeslot(c, s); dstc_freeslot(c, s);
if (dstc_iserr(&opts)) return dstc_cslot(dst_wrap_nil());
} }
/* Build function */ /* Build function */

View File

@ -22,7 +22,9 @@
#include <dst/dsttypes.h> #include <dst/dsttypes.h>
#include <dst/dstopcodes.h> #include <dst/dstopcodes.h>
#include "gc.h"
/* Look up table for instructions */
DstInstructionType dst_instructions[DOP_INSTRUCTION_COUNT] = { DstInstructionType dst_instructions[DOP_INSTRUCTION_COUNT] = {
DIT_0, /* DOP_NOOP, */ DIT_0, /* DOP_NOOP, */
DIT_S, /* DOP_ERROR, */ DIT_S, /* DOP_ERROR, */
@ -86,37 +88,8 @@ DstInstructionType dst_instructions[DOP_INSTRUCTION_COUNT] = {
DIT_SS /* DOP_LENGTH */ DIT_SS /* DOP_LENGTH */
}; };
/* Hold state in stack during the breadth first traversal */
typedef struct Node Node;
struct Node {
DstFuncDef *def;
int32_t index;
};
/* An in memory stack of FuncDefs to verify */
/* Thread local */
static Node *stack = NULL;
static int32_t stackcap = 0;
static int32_t stackcount = 0;
/* Push a Node to the stack */
static void push(DstFuncDef *def, int32_t index) {
Node n;
n.def = def;
n.index = index;
if (stackcount >= stackcap) {
stackcap = 2 * stackcount + 2;
stack = realloc(stack, sizeof(Node) * stackcap);
if (!stack) {
DST_OUT_OF_MEMORY;
}
}
stack[stackcount++] = n;
}
/* Verify some bytecode */ /* Verify some bytecode */
static int32_t dst_verify_one(DstFuncDef *def) { int32_t dst_verify(DstFuncDef *def) {
int vargs = def->flags & DST_FUNCDEF_FLAG_VARARG; int vargs = def->flags & DST_FUNCDEF_FLAG_VARARG;
int32_t i; int32_t i;
int32_t maxslot = def->arity + vargs; int32_t maxslot = def->arity + vargs;
@ -224,23 +197,49 @@ static int32_t dst_verify_one(DstFuncDef *def) {
} }
} }
/* Verify sub funcdefs by pushing next node to stack */
if (def->defs_length) push(def, 0);
return 0; return 0;
} }
/* Verify */ /* Allocate an empty funcdef. This function may have added functionality
int32_t dst_verify(DstFuncDef *def) { * as commonalities between asm and compile arise. */
int32_t status; DstFuncDef *dst_funcdef_alloc() {
stackcount = 0; DstFuncDef *def = dst_gcalloc(DST_MEMORY_FUNCDEF, sizeof(DstFuncDef));
status = dst_verify_one(def); def->environments = NULL;
while (!status && stackcount) { def->constants = NULL;
Node n = stack[--stackcount]; def->bytecode = NULL;
if (n.index < n.def->defs_length) { def->flags = 0;
status = dst_verify_one(n.def->defs[n.index]); def->slotcount = 0;
push(n.def, n.index + 1); def->arity = 0;
def->source = NULL;
def->sourcepath = NULL;
def->sourcemap = NULL;
def->defs = NULL;
def->defs_length = 0;
def->constants_length = 0;
def->bytecode_length = 0;
def->environments_length = 1;
return def;
} }
/* Create a closure from a funcdef and a parent function */
DstFunction *dst_function(DstFuncDef *def, DstFunction *parent) {
int32_t i;
DstFunction *func = dst_gcalloc(DST_MEMORY_FUNCTION, sizeof(DstFunction));
int32_t elen = def->environments_length;
func->def = def;
if (elen) {
func->envs = malloc(sizeof(DstFuncEnv *) * elen);
if (elen > 1 && !parent) return NULL;
if (NULL == func->envs) {
DST_OUT_OF_MEMORY;
} }
return status; func->envs[0] = NULL;
} else {
func->envs = NULL;
}
for (i = 1; i < def->environments_length; ++i) {
int32_t inherit = def->environments[i];
func->envs[i] = parent->envs[inherit];
}
return func;
} }

View File

@ -205,47 +205,3 @@ int dst_hashtable_view(Dst tab, const DstKV **data, int32_t *len, int32_t *cap)
} }
return 0; return 0;
} }
/* Allocate an empty funcdef. This function may have added functionality
* as commonalities between asm and compile arise. */
DstFuncDef *dst_funcdef_alloc() {
DstFuncDef *def = dst_gcalloc(DST_MEMORY_FUNCDEF, sizeof(DstFuncDef));
def->environments = NULL;
def->constants = NULL;
def->bytecode = NULL;
def->flags = 0;
def->slotcount = 0;
def->arity = 0;
def->source = NULL;
def->sourcepath = NULL;
def->sourcemap = NULL;
def->defs = NULL;
def->defs_length = 0;
def->constants_length = 0;
def->bytecode_length = 0;
def->environments_length = 1;
return def;
}
/* Create a closure from a funcdef and a parent function */
DstFunction *dst_function(DstFuncDef *def, DstFunction *parent) {
int32_t i;
DstFunction *func = dst_gcalloc(DST_MEMORY_FUNCTION, sizeof(DstFunction));
int32_t elen = def->environments_length;
func->def = def;
if (elen) {
func->envs = malloc(sizeof(DstFuncEnv *) * elen);
if (elen > 1 && !parent) return NULL;
if (NULL == func->envs) {
DST_OUT_OF_MEMORY;
}
func->envs[0] = NULL;
} else {
func->envs = NULL;
}
for (i = 1; i < def->environments_length; ++i) {
int32_t inherit = def->environments[i];
func->envs[i] = parent->envs[inherit];
}
return func;
}

View File

@ -98,11 +98,9 @@
} while (0) } while (0)
#endif #endif
#ifndef DST_NOASSERT
#define dst_assert(c, m) do { \ #define dst_assert(c, m) do { \
if (!(c)) dst_exit((m)); \ if (!(c)) dst_exit((m)); \
} while (0) } while (0)
#endif
/* What to do when out of memory */ /* What to do when out of memory */
#ifndef DST_OUT_OF_MEMORY #ifndef DST_OUT_OF_MEMORY
@ -115,7 +113,7 @@
/* Prevent some recursive functions from recursing too deeply /* Prevent some recursive functions from recursing too deeply
* ands crashing (the parser). Instead, error out. */ * ands crashing (the parser). Instead, error out. */
#define DST_RECURSION_GUARD 1000 #define DST_RECURSION_GUARD 1024
/* Use nanboxed values - uses 8 bytes per value instead of 12 or 16. */ /* Use nanboxed values - uses 8 bytes per value instead of 12 or 16. */
#define DST_NANBOX #define DST_NANBOX

9
test/scratch.dst Normal file
View File

@ -0,0 +1,9 @@
(def outerfun (fn [x y]
(def c (do
#(+ 1 2 3 4)
(def someval (+ x y))
(def ctemp (if x (fn [] someval) (fn [] y)))
ctemp
))
#(+ 1 2 3 4 5 6 7 8 9 10)
c))

View File

@ -95,18 +95,33 @@
# Fibonacci # Fibonacci
(def fib (do (var fib nil) (varset! fib (fn [n] (if (< n 2) n (+ (fib (- n 1)) (fib (- n 2)))))))) (def fib (do (var fib nil) (varset! fib (fn [n] (if (< n 2) n (+ (fib (- n 1)) (fib (- n 2))))))))
(def fib2 (fn fib2 [n] (if (< n 2) n (+ (fib2 (- n 1)) (fib2 (- n 2))))))
(assert (= (fib 0) 0) "fib(0)") (assert (= (fib 0) (fib2 0) 0) "fib(0)")
(assert (= (fib 1) 1) "fib(1)") (assert (= (fib 1) (fib2 1) 1) "fib(1)")
(assert (= (fib 2) 1) "fib(2)") (assert (= (fib 2) (fib2 2) 1) "fib(2)")
(assert (= (fib 3) 2) "fib(3)") (assert (= (fib 3) (fib2 3) 2) "fib(3)")
(assert (= (fib 4) 3) "fib(4)") (assert (= (fib 4) (fib2 4) 3) "fib(4)")
(assert (= (fib 5) 5) "fib(5)") (assert (= (fib 5) (fib2 5) 5) "fib(5)")
(assert (= (fib 6) 8) "fib(6)") (assert (= (fib 6) (fib2 6) 8) "fib(6)")
(assert (= (fib 7) 13) "fib(7)") (assert (= (fib 7) (fib2 7) 13) "fib(7)")
(assert (= (fib 8) 21) "fib(8)") (assert (= (fib 8) (fib2 8) 21) "fib(8)")
(assert (= (fib 9) 34) "fib(9)") (assert (= (fib 9) (fib2 9) 34) "fib(9)")
(assert (= (fib 10) 55) "fib(10)") (assert (= (fib 10) (fib2 10) 55) "fib(10)")
# Closure in non function scope
(def outerfun (fn [x y]
(def c (do
(def someval (+ 10 y))
(def ctemp (if x (fn [] someval) (fn [] y)))
ctemp
))
(+ 1 2 3 4 5 6 7)
c))
(assert (= ((outerfun 1 2)) 12) "inner closure 1")
(assert (= ((outerfun nil 2)) 2) "inner closure 2")
(assert (= ((outerfun false 3)) 3) "inner closure 3")
(assert (= "hello" :hello) "keyword syntax for strings") (assert (= "hello" :hello) "keyword syntax for strings")
(assert (= '(1 2 3) (quote (1 2 3)) (tuple 1 2 3)) "quote shorthand") (assert (= '(1 2 3) (quote (1 2 3)) (tuple 1 2 3)) "quote shorthand")