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:
parent
d68eae9592
commit
911b0b15e8
@ -46,7 +46,6 @@
|
||||
typedef struct DstInstructionDef DstInstructionDef;
|
||||
struct DstInstructionDef {
|
||||
const char *name;
|
||||
DstInstructionType type;
|
||||
DstOpCode opcode;
|
||||
};
|
||||
|
||||
@ -76,63 +75,63 @@ struct DstAssembler {
|
||||
* time and is easier to setup statically than a hash table or
|
||||
* prefix tree. */
|
||||
static const DstInstructionDef dst_ops[] = {
|
||||
{"add", DIT_SSS, DOP_ADD},
|
||||
{"add-immediate", DIT_SSI, DOP_ADD_IMMEDIATE},
|
||||
{"add-integer", DIT_SSS, DOP_ADD_INTEGER},
|
||||
{"add-real", DIT_SSS, DOP_ADD_REAL},
|
||||
{"band", DIT_SSS, DOP_BAND},
|
||||
{"bnot", DIT_SS, DOP_BNOT},
|
||||
{"bor", DIT_SSS, DOP_BOR},
|
||||
{"bxor", DIT_SSS, DOP_BXOR},
|
||||
{"call", DIT_SS, DOP_CALL},
|
||||
{"closure", DIT_SD, DOP_CLOSURE},
|
||||
{"compare", DIT_SSS, DOP_COMPARE},
|
||||
{"divide", DIT_SSS, DOP_DIVIDE},
|
||||
{"divide-immediate", DIT_SSI, DOP_DIVIDE_IMMEDIATE},
|
||||
{"divide-integer", DIT_SSS, DOP_DIVIDE_INTEGER},
|
||||
{"divide-real", DIT_SSS, DOP_DIVIDE_REAL},
|
||||
{"equals", DIT_SSS, DOP_EQUALS},
|
||||
{"error", DIT_S, DOP_ERROR},
|
||||
{"get", DIT_SSS, DOP_GET},
|
||||
{"get-index", DIT_SSU, DOP_GET_INDEX},
|
||||
{"greater-than", DIT_SSS, DOP_GREATER_THAN},
|
||||
{"jump", DIT_L, DOP_JUMP},
|
||||
{"jump-if", DIT_SL, DOP_JUMP_IF},
|
||||
{"jump-if-not", DIT_SL, DOP_JUMP_IF_NOT},
|
||||
{"less-than", DIT_SSS, DOP_LESS_THAN},
|
||||
{"load-constant", DIT_SC, DOP_LOAD_CONSTANT},
|
||||
{"load-false", DIT_S, DOP_LOAD_FALSE},
|
||||
{"load-integer", DIT_SI, DOP_LOAD_INTEGER},
|
||||
{"load-nil", DIT_S, DOP_LOAD_NIL},
|
||||
{"load-self", DIT_S, DOP_LOAD_SELF},
|
||||
{"load-true", DIT_S, DOP_LOAD_TRUE},
|
||||
{"load-upvalue", DIT_SES, DOP_LOAD_UPVALUE},
|
||||
{"move-far", DIT_SS, DOP_MOVE_FAR},
|
||||
{"move-near", DIT_SS, DOP_MOVE_NEAR},
|
||||
{"multiply", DIT_SSS, DOP_MULTIPLY},
|
||||
{"multiply-immediate", DIT_SSI, DOP_MULTIPLY_IMMEDIATE},
|
||||
{"multiply-integer", DIT_SSS, DOP_MULTIPLY_INTEGER},
|
||||
{"multiply-real", DIT_SSS, DOP_MULTIPLY_REAL},
|
||||
{"noop", DIT_0, DOP_NOOP},
|
||||
{"push", DIT_S, DOP_PUSH},
|
||||
{"push-array", DIT_S, DOP_PUSH_ARRAY},
|
||||
{"push2", DIT_SS, DOP_PUSH_2},
|
||||
{"push3", DIT_SSS, DOP_PUSH_3},
|
||||
{"put", DIT_SSS, DOP_PUT},
|
||||
{"put-index", DIT_SSU, DOP_PUT_INDEX},
|
||||
{"return", DIT_S, DOP_RETURN},
|
||||
{"return-nil", DIT_0, DOP_RETURN_NIL},
|
||||
{"set-upvalue", DIT_SES, DOP_SET_UPVALUE},
|
||||
{"shift-left", DIT_SSS, DOP_SHIFT_LEFT},
|
||||
{"shift-left-immediate", DIT_SSI, DOP_SHIFT_LEFT_IMMEDIATE},
|
||||
{"shift-right", DIT_SSS, DOP_SHIFT_RIGHT},
|
||||
{"shift-right-immediate", DIT_SSI, DOP_SHIFT_RIGHT_IMMEDIATE},
|
||||
{"shift-right-unsigned", DIT_SSS, DOP_SHIFT_RIGHT_UNSIGNED},
|
||||
{"shift-right-unsigned-immediate", DIT_SSS, DOP_SHIFT_RIGHT_UNSIGNED_IMMEDIATE},
|
||||
{"subtract", DIT_SSS, DOP_SUBTRACT},
|
||||
{"tailcall", DIT_S, DOP_TAILCALL},
|
||||
{"transfer", DIT_SSS, DOP_TRANSFER},
|
||||
{"typecheck", DIT_ST, DOP_TYPECHECK},
|
||||
{"add", DOP_ADD},
|
||||
{"add-immediate", DOP_ADD_IMMEDIATE},
|
||||
{"add-integer", DOP_ADD_INTEGER},
|
||||
{"add-real", DOP_ADD_REAL},
|
||||
{"band", DOP_BAND},
|
||||
{"bnot", DOP_BNOT},
|
||||
{"bor", DOP_BOR},
|
||||
{"bxor", DOP_BXOR},
|
||||
{"call", DOP_CALL},
|
||||
{"closure", DOP_CLOSURE},
|
||||
{"compare", DOP_COMPARE},
|
||||
{"divide", DOP_DIVIDE},
|
||||
{"divide-immediate", DOP_DIVIDE_IMMEDIATE},
|
||||
{"divide-integer", DOP_DIVIDE_INTEGER},
|
||||
{"divide-real", DOP_DIVIDE_REAL},
|
||||
{"equals", DOP_EQUALS},
|
||||
{"error", DOP_ERROR},
|
||||
{"get", DOP_GET},
|
||||
{"get-index", DOP_GET_INDEX},
|
||||
{"greater-than", DOP_GREATER_THAN},
|
||||
{"jump", DOP_JUMP},
|
||||
{"jump-if", DOP_JUMP_IF},
|
||||
{"jump-if-not", DOP_JUMP_IF_NOT},
|
||||
{"less-than", DOP_LESS_THAN},
|
||||
{"load-constant", DOP_LOAD_CONSTANT},
|
||||
{"load-false", DOP_LOAD_FALSE},
|
||||
{"load-integer", DOP_LOAD_INTEGER},
|
||||
{"load-nil", DOP_LOAD_NIL},
|
||||
{"load-self", DOP_LOAD_SELF},
|
||||
{"load-true", DOP_LOAD_TRUE},
|
||||
{"load-upvalue", DOP_LOAD_UPVALUE},
|
||||
{"move-far", DOP_MOVE_FAR},
|
||||
{"move-near", DOP_MOVE_NEAR},
|
||||
{"multiply", DOP_MULTIPLY},
|
||||
{"multiply-immediate", DOP_MULTIPLY_IMMEDIATE},
|
||||
{"multiply-integer", DOP_MULTIPLY_INTEGER},
|
||||
{"multiply-real", DOP_MULTIPLY_REAL},
|
||||
{"noop", DOP_NOOP},
|
||||
{"push", DOP_PUSH},
|
||||
{"push-array", DOP_PUSH_ARRAY},
|
||||
{"push2", DOP_PUSH_2},
|
||||
{"push3", DOP_PUSH_3},
|
||||
{"put", DOP_PUT},
|
||||
{"put-index", DOP_PUT_INDEX},
|
||||
{"return", DOP_RETURN},
|
||||
{"return-nil", DOP_RETURN_NIL},
|
||||
{"set-upvalue", DOP_SET_UPVALUE},
|
||||
{"shift-left", DOP_SHIFT_LEFT},
|
||||
{"shift-left-immediate", DOP_SHIFT_LEFT_IMMEDIATE},
|
||||
{"shift-right", DOP_SHIFT_RIGHT},
|
||||
{"shift-right-immediate", DOP_SHIFT_RIGHT_IMMEDIATE},
|
||||
{"shift-right-unsigned", DOP_SHIFT_RIGHT_UNSIGNED},
|
||||
{"shift-right-unsigned-immediate", DOP_SHIFT_RIGHT_UNSIGNED_IMMEDIATE},
|
||||
{"subtract", DOP_SUBTRACT},
|
||||
{"tailcall", DOP_TAILCALL},
|
||||
{"transfer", DOP_TRANSFER},
|
||||
{"typecheck", DOP_TYPECHECK},
|
||||
};
|
||||
|
||||
/* 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 Dst *argt) {
|
||||
uint32_t instr = idef->opcode;
|
||||
switch (idef->type) {
|
||||
DstInstructionType type = dst_instructions[idef->opcode];
|
||||
switch (type) {
|
||||
case DIT_0:
|
||||
{
|
||||
if (dst_tuple_length(argt) != 1)
|
||||
@ -388,7 +388,7 @@ static uint32_t read_instruction(
|
||||
if (dst_tuple_length(argt) != 3)
|
||||
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_INTEGER, 2, 2, idef->type == DIT_SI, argt[2]);
|
||||
instr |= doarg(a, DST_OAT_INTEGER, 2, 2, type == DIT_SI, argt[2]);
|
||||
break;
|
||||
}
|
||||
case DIT_SD:
|
||||
@ -415,7 +415,7 @@ static uint32_t read_instruction(
|
||||
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, 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;
|
||||
}
|
||||
case DIT_SES:
|
||||
@ -744,7 +744,7 @@ Dst dst_asm_decode_instruction(uint32_t instr) {
|
||||
}
|
||||
name = dst_csymbolv(def->name);
|
||||
#define oparg(shift, mask) ((instr >> ((shift) << 3)) & (mask))
|
||||
switch (def->type) {
|
||||
switch (dst_instructions[def->opcode]) {
|
||||
case DIT_0:
|
||||
return tup1(name);
|
||||
case DIT_S:
|
||||
|
@ -141,6 +141,7 @@ void dstc_nameslot(DstCompiler *c, const uint8_t *sym, DstSlot s) {
|
||||
SymPair sp;
|
||||
sp.sym = sym;
|
||||
sp.slot = s;
|
||||
sp.keep = 0;
|
||||
sp.slot.flags |= DST_SLOT_NAMED;
|
||||
dst_v_push(scope->syms, sp);
|
||||
}
|
||||
@ -174,20 +175,33 @@ void dstc_popscope(DstCompiler *c) {
|
||||
int32_t oldcount = dst_v_count(c->scopes);
|
||||
dst_assert(oldcount, "could not pop scope");
|
||||
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 */
|
||||
dst_v_free(scope.consts);
|
||||
dst_v_free(scope.syms);
|
||||
dst_v_free(scope.envs);
|
||||
dst_v_free(scope.defs);
|
||||
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. */
|
||||
@ -217,6 +231,7 @@ DstSlot dstc_resolve(
|
||||
DstSlot ret = dstc_cslot(dst_wrap_nil());
|
||||
DstScope *top = &dst_v_last(c->scopes);
|
||||
DstScope *scope = top;
|
||||
SymPair *pair;
|
||||
int foundlocal = 1;
|
||||
int unused = 0;
|
||||
|
||||
@ -227,8 +242,9 @@ DstSlot dstc_resolve(
|
||||
unused = 1;
|
||||
len = dst_v_count(scope->syms);
|
||||
for (i = 0; i < len; i++) {
|
||||
if (scope->syms[i].sym == sym) {
|
||||
ret = scope->syms[i].slot;
|
||||
pair = scope->syms + i;
|
||||
if (pair->sym == sym) {
|
||||
ret = pair->slot;
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
@ -272,14 +288,12 @@ DstSlot dstc_resolve(
|
||||
}
|
||||
|
||||
/* non-local scope needs to expose its environment */
|
||||
if (!foundlocal) {
|
||||
/* Find function scope */
|
||||
while (scope >= c->scopes && !(scope->flags & DST_SCOPE_FUNCTION)) scope--;
|
||||
dst_assert(scope >= c->scopes, "invalid scopes");
|
||||
scope->flags |= DST_SCOPE_ENV;
|
||||
if (!dst_v_count(scope->envs)) dst_v_push(scope->envs, 0);
|
||||
scope++;
|
||||
}
|
||||
pair->keep = 1;
|
||||
while (scope >= c->scopes && !(scope->flags & DST_SCOPE_FUNCTION)) scope--;
|
||||
dst_assert(scope >= c->scopes, "invalid scopes");
|
||||
scope->flags |= DST_SCOPE_ENV;
|
||||
if (!dst_v_count(scope->envs)) dst_v_push(scope->envs, 0);
|
||||
scope++;
|
||||
|
||||
/* Propogate env up to current scope */
|
||||
int32_t envindex = 0;
|
||||
@ -314,13 +328,7 @@ DstSlot dstc_resolve(
|
||||
/* Emit a raw instruction with source mapping. */
|
||||
void dstc_emit(DstCompiler *c, DstAst *ast, uint32_t instr) {
|
||||
dst_v_push(c->buffer, instr);
|
||||
if (NULL != 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);
|
||||
}
|
||||
dst_v_push(c->mapbuffer, ast);
|
||||
}
|
||||
|
||||
/* 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);
|
||||
break;
|
||||
}
|
||||
if (dstc_iserr(&opts)) {
|
||||
return dstc_cslot(dst_wrap_nil());
|
||||
}
|
||||
if (opts.flags & DST_FOPTS_TAIL) {
|
||||
ret = dstc_return(opts.compiler, ast, ret);
|
||||
}
|
||||
@ -799,6 +810,8 @@ DstFuncDef *dstc_pop_funcdef(DstCompiler *c) {
|
||||
DstFuncDef *def = dst_funcdef_alloc();
|
||||
def->slotcount = scope.smax + 1;
|
||||
|
||||
dst_assert(scope.flags & DST_SCOPE_FUNCTION, "expected function scope");
|
||||
|
||||
/* Copy envs */
|
||||
def->environments_length = dst_v_count(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);
|
||||
dst_v__cnt(c->buffer) = scope.bytecode_start;
|
||||
if (NULL != c->mapbuffer) {
|
||||
int32_t i;
|
||||
size_t s = sizeof(int32_t) * 2 * dst_v_count(c->mapbuffer);
|
||||
def->sourcemap = malloc(2 * s);
|
||||
if (NULL == def->sourcemap) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,6 @@
|
||||
#define DST_COMPILE_H
|
||||
|
||||
#include <dst/dst.h>
|
||||
#include <setjmp.h>
|
||||
#include <dst/dstcompile.h>
|
||||
#include <dst/dstopcodes.h>
|
||||
|
||||
@ -68,6 +67,7 @@ typedef struct DstSM {
|
||||
/* A symbol and slot pair */
|
||||
typedef struct SymPair {
|
||||
const uint8_t *sym;
|
||||
int keep;
|
||||
DstSlot slot;
|
||||
} SymPair;
|
||||
|
||||
@ -105,7 +105,7 @@ struct DstCompiler {
|
||||
DstScope *scopes;
|
||||
|
||||
uint32_t *buffer;
|
||||
int32_t *mapbuffer;
|
||||
DstAst **mapbuffer;
|
||||
|
||||
/* Hold the environment */
|
||||
DstTable *env;
|
||||
|
@ -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) {
|
||||
DstCompiler *c = opts.compiler;
|
||||
static DstSlot dohead(DstCompiler *c, DstFopts opts, DstAst *ast, Dst *head, int32_t argn, const Dst *argv) {
|
||||
DstFopts subopts = dstc_fopts_default(c);
|
||||
Dst head;
|
||||
DstSlot ret;
|
||||
if (argn < 2) {
|
||||
dstc_cerror(c, ast, "expected at least 2 arguments");
|
||||
return dstc_cslot(dst_wrap_nil());
|
||||
}
|
||||
head = dst_ast_unwrap1(argv[0]);
|
||||
if (!dst_checktype(head, DST_SYMBOL)) {
|
||||
*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;
|
||||
subopts.flags = opts.flags & ~(DST_FOPTS_TAIL | DST_FOPTS_DROP);
|
||||
subopts.hint = opts.hint;
|
||||
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) {
|
||||
DstSlot refslot, refarrayslot;
|
||||
/* 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);
|
||||
ret = refslot;
|
||||
} else {
|
||||
/* 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 = 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);
|
||||
ret = namelocal(c, ast, head, DST_SLOT_NAMED | DST_SLOT_MUTABLE, ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
DstSlot dstc_def(DstFopts opts, DstAst *ast, int32_t argn, const Dst *argv) {
|
||||
DstCompiler *c = opts.compiler;
|
||||
DstFopts subopts = dstc_fopts_default(c);
|
||||
DstSlot ret;
|
||||
Dst head;
|
||||
if (argn < 2) {
|
||||
dstc_cerror(c, ast, "expected at least 2 arguments");
|
||||
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;
|
||||
opts.flags &= ~DST_FOPTS_HINT;
|
||||
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) {
|
||||
DstTable *tab = dst_table(2);
|
||||
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, ret, valueindex);
|
||||
} else {
|
||||
/* Non root scope, simple slot alias */
|
||||
dstc_nameslot(c, dst_unwrap_symbol(head), ret);
|
||||
ret = namelocal(c, ast, head, DST_SLOT_NAMED, ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@ -205,7 +206,6 @@ DstSlot dstc_if(DstFopts opts, DstAst *ast, int32_t argn, const Dst *argv) {
|
||||
Dst truebody, falsebody;
|
||||
const int tail = opts.flags & DST_FOPTS_TAIL;
|
||||
const int drop = opts.flags & DST_FOPTS_DROP;
|
||||
(void) argv;
|
||||
|
||||
if (argn < 2 || argn > 3) {
|
||||
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. */
|
||||
/* 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)) {
|
||||
/* Swap the true and false bodies */
|
||||
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;
|
||||
s = dstc_value(subopts, argv[argi]);
|
||||
dstc_freeslot(c, s);
|
||||
if (dstc_iserr(&opts)) return dstc_cslot(dst_wrap_nil());
|
||||
}
|
||||
|
||||
/* Build function */
|
||||
|
@ -22,7 +22,9 @@
|
||||
|
||||
#include <dst/dsttypes.h>
|
||||
#include <dst/dstopcodes.h>
|
||||
#include "gc.h"
|
||||
|
||||
/* Look up table for instructions */
|
||||
DstInstructionType dst_instructions[DOP_INSTRUCTION_COUNT] = {
|
||||
DIT_0, /* DOP_NOOP, */
|
||||
DIT_S, /* DOP_ERROR, */
|
||||
@ -86,37 +88,8 @@ DstInstructionType dst_instructions[DOP_INSTRUCTION_COUNT] = {
|
||||
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 */
|
||||
static int32_t dst_verify_one(DstFuncDef *def) {
|
||||
int32_t dst_verify(DstFuncDef *def) {
|
||||
int vargs = def->flags & DST_FUNCDEF_FLAG_VARARG;
|
||||
int32_t i;
|
||||
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;
|
||||
}
|
||||
|
||||
/* Verify */
|
||||
int32_t dst_verify(DstFuncDef *def) {
|
||||
int32_t status;
|
||||
stackcount = 0;
|
||||
status = dst_verify_one(def);
|
||||
while (!status && stackcount) {
|
||||
Node n = stack[--stackcount];
|
||||
if (n.index < n.def->defs_length) {
|
||||
status = dst_verify_one(n.def->defs[n.index]);
|
||||
push(n.def, n.index + 1);
|
||||
}
|
||||
}
|
||||
return status;
|
||||
/* 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;
|
||||
}
|
||||
|
@ -205,47 +205,3 @@ int dst_hashtable_view(Dst tab, const DstKV **data, int32_t *len, int32_t *cap)
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
@ -98,11 +98,9 @@
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#ifndef DST_NOASSERT
|
||||
#define dst_assert(c, m) do { \
|
||||
if (!(c)) dst_exit((m)); \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
/* What to do when out of memory */
|
||||
#ifndef DST_OUT_OF_MEMORY
|
||||
@ -115,7 +113,7 @@
|
||||
|
||||
/* Prevent some recursive functions from recursing too deeply
|
||||
* 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. */
|
||||
#define DST_NANBOX
|
||||
|
9
test/scratch.dst
Normal file
9
test/scratch.dst
Normal 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))
|
@ -95,18 +95,33 @@
|
||||
|
||||
# Fibonacci
|
||||
(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 1) 1) "fib(1)")
|
||||
(assert (= (fib 2) 1) "fib(2)")
|
||||
(assert (= (fib 3) 2) "fib(3)")
|
||||
(assert (= (fib 4) 3) "fib(4)")
|
||||
(assert (= (fib 5) 5) "fib(5)")
|
||||
(assert (= (fib 6) 8) "fib(6)")
|
||||
(assert (= (fib 7) 13) "fib(7)")
|
||||
(assert (= (fib 8) 21) "fib(8)")
|
||||
(assert (= (fib 9) 34) "fib(9)")
|
||||
(assert (= (fib 10) 55) "fib(10)")
|
||||
(assert (= (fib 0) (fib2 0) 0) "fib(0)")
|
||||
(assert (= (fib 1) (fib2 1) 1) "fib(1)")
|
||||
(assert (= (fib 2) (fib2 2) 1) "fib(2)")
|
||||
(assert (= (fib 3) (fib2 3) 2) "fib(3)")
|
||||
(assert (= (fib 4) (fib2 4) 3) "fib(4)")
|
||||
(assert (= (fib 5) (fib2 5) 5) "fib(5)")
|
||||
(assert (= (fib 6) (fib2 6) 8) "fib(6)")
|
||||
(assert (= (fib 7) (fib2 7) 13) "fib(7)")
|
||||
(assert (= (fib 8) (fib2 8) 21) "fib(8)")
|
||||
(assert (= (fib 9) (fib2 9) 34) "fib(9)")
|
||||
(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 (= '(1 2 3) (quote (1 2 3)) (tuple 1 2 3)) "quote shorthand")
|
||||
|
Loading…
Reference in New Issue
Block a user