From 6ca6949c2d3bb3993f7c7badbe5ee13a6b30f51b Mon Sep 17 00:00:00 2001 From: bakpakin Date: Mon, 20 Nov 2017 21:39:44 -0500 Subject: [PATCH] Getting more work done on assembler, parer, and unit tests. --- Makefile | 6 +- core/asm.c | 220 ++++++++++++--------- core/fiber.c | 66 ++----- core/gc.c | 43 ++-- core/parse.c | 130 +++++++----- core/string.c | 336 ++++++++++++++++++++++++++++---- core/struct.c | 20 +- core/syscalls.c | 2 +- core/table.c | 12 ++ core/util.c | 4 +- core/vm.c | 151 +++++++------- unittests/asm_test.c | 50 +++++ unittests/fiber_test.c | 88 +++++++++ unittests/parse_test.c | 19 ++ {libs => unittests}/sample.dsts | 69 +++---- unittests/table_test.c | 4 + 16 files changed, 863 insertions(+), 357 deletions(-) create mode 100644 unittests/asm_test.c create mode 100644 unittests/fiber_test.c create mode 100644 unittests/parse_test.c rename {libs => unittests}/sample.dsts (58%) diff --git a/Makefile b/Makefile index 8db496dc..9ec37d84 100644 --- a/Makefile +++ b/Makefile @@ -78,14 +78,18 @@ $(DST_TARGET): $(DST_CORE_OBJECTS) CCU_FLAGS = $(CFLAGS) -DDST_UNIT_TEST DST_UNIT_BINARIES=$(addprefix unittests/,\ - array_test.out buffer_test.out table_test.out) + asm_test.out array_test.out buffer_test.out fiber_test.out parse_test.out \ + table_test.out) %.out: %.c $(DST_CORE_OBJECTS) $(DST_ALL_HEADERS) unittests/unit.h $(CC) $(CCU_FLAGS) $(DST_CORE_OBJECTS) $< -o $@ unit: $(DST_UNIT_BINARIES) unittests/array_test.out + unittests/asm_test.out unittests/buffer_test.out + unittests/fiber_test.out + unittests/parse_test.out unittests/table_test.out ################### diff --git a/core/asm.c b/core/asm.c index f80abc34..f3aaeb3e 100644 --- a/core/asm.c +++ b/core/asm.c @@ -23,6 +23,7 @@ #include #include +#include "opcodes.h" /* Bytecode op argument types */ @@ -70,6 +71,7 @@ enum DstInstructionType { DIT_SI, DIT_SU, /* Unsigned */ DIT_SSS, + DIT_SSI, DIT_SES, DIT_SC }; @@ -79,7 +81,7 @@ typedef struct DstInstructionDef DstInstructionDef; struct DstInstructionDef { const char *name; DstInstructionType type; - uint8_t opcode; + DstOpCode opcode; }; /* Hold all state needed during assembly */ @@ -88,7 +90,7 @@ struct DstAssembler { DstAssembler *parent; DstFuncDef *def; jmp_buf on_error; - const char *errmessage; + const uint8_t *errmessage; uint32_t environments_capacity; uint32_t bytecode_count; /* Used for calculating labels */ @@ -105,40 +107,59 @@ struct DstAssembler { * time and is easier to setup statically than a hash table or * prefix tree. */ static const DstInstructionDef dst_ops[] = { - {"add", DIT_SSS, 0x01}, - {"bitand", DIT_SSS, 0x02}, - {"bitor", DIT_SSS, 0x03}, - {"bitxor", DIT_SSS, 0x04}, - {"call", DIT_SS, 0x05}, - {"closure", DIT_SC, 0x06}, - {"divide", DIT_SSS, 0x07}, - {"jump", DIT_L, 0x08}, - {"jump-if", DIT_SL, 0x09}, - {"load-constant", DIT_SC, 0x0A}, - {"load-false", DIT_S, 0x0B}, - {"load-integer", DIT_SI, 0x0C}, - {"load-nil", DIT_S, 0x0D}, - {"load-true", DIT_S, 0x0E}, - {"load-upvalue", DIT_SES, 0x0F}, - {"move", DIT_SS, 0x10}, - {"modulo", DIT_SSS, 0x11}, - {"multiply", DIT_SSS, 0x12}, - {"noop", DIT_0, 0x00}, - {"push", DIT_S, 0x13}, - {"push2", DIT_SS, 0x14}, - {"push3", DIT_SSS, 0x15}, - {"push-array", DIT_S, 0x16}, - {"return", DIT_S, 0x19}, - {"return-nil", DIT_0, 0x1A}, - {"save-upvalue", DIT_SES, 0x1B}, - {"shift-left", DIT_SSS, 0x1C}, - {"shift-right", DIT_SSS, 0x1D}, - {"shift-right-signed", DIT_SSS, 0x1E}, + {"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}, + {"bitand", DIT_SSS, DOP_BAND}, + {"bitnot", DIT_SS, DOP_BNOT}, + {"bitor", DIT_SSS, DOP_BOR}, + {"bitxor", DIT_SSS, DOP_BXOR}, + {"call", DIT_SS, DOP_CALL}, + {"closure", DIT_SC, DOP_CLOSURE}, + {"coerce-integer", DIT_SS, DOP_COERCE_INTEGER}, + {"coerce-real", DIT_SS, DOP_COERCE_REAL}, + {"coerce-string", DIT_SS, DOP_COERCE_STRING}, + {"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}, + {"greater-than", DIT_SSS, DOP_GREATER_THAN}, + {"jump", DIT_L, DOP_JUMP}, + {"jump-if", DIT_SL, DOP_JUMP_IF}, + {"load-boolean", DIT_S, DOP_LOAD_BOOLEAN}, + {"load-constant", DIT_SC, DOP_LOAD_CONSTANT}, + {"load-integer", DIT_SI, DOP_LOAD_INTEGER}, + {"load-nil", DIT_S, DOP_LOAD_NIL}, + {"load-syscall", DIT_SU, DOP_LOAD_SYSCALL}, + {"load-upvalue", DIT_SES, DOP_LOAD_UPVALUE}, + {"move", DIT_SS, DOP_MOVE}, + {"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}, + {"push2", DIT_SS, DOP_PUSH_2}, + {"push3", DIT_SSS, DOP_PUSH_3}, + {"push-array", DIT_S, DOP_PUSH_ARRAY}, + {"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, 0x1F}, - {"syscall", DIT_SU, 0x21}, - {"tailcall", DIT_S, 0x22}, - {"transfer", DIT_SSS, 0x23}, - {"typecheck", DIT_ST, 0x24}, + {"syscall", DIT_SU, DOP_SYSCALL}, + {"tailcall", DIT_S, DOP_TAILCALL}, + {"transfer", DIT_SSS, DOP_TRANSFER}, + {"typecheck", DIT_ST, DOP_TYPECHECK}, }; /* Compare a DST string to a native 0 terminated c string. Used in the @@ -205,11 +226,17 @@ static void dst_asm_deinit(DstAssembler *a) { /* Throw some kind of assembly error */ static void dst_asm_error(DstAssembler *a, const char *message) { - a->errmessage = message; + a->errmessage = dst_cstring(message); longjmp(a->on_error, 1); } #define dst_asm_assert(a, c, m) do { if (!(c)) dst_asm_error((a), (m)); } while (0) +/* Throw some kind of assembly error */ +static void dst_asm_errorv(DstAssembler *a, const uint8_t *m) { + a->errmessage = m; + longjmp(a->on_error, 1); +} + /* Parse an argument to an assembly instruction, and return the result as an * integer. This integer will need to be trimmed and bound checked. */ static int64_t doarg_1(DstAssembler *a, DstOpArgType argtype, DstValue x) { @@ -261,42 +288,44 @@ static int64_t doarg_1(DstAssembler *a, DstOpArgType argtype, DstValue x) { return result.as.integer - a->bytecode_count; return result.as.integer; } else { - dst_asm_error(a, "unknown name"); + dst_asm_errorv(a, dst_formatc("unknown name %q", x)); } } else if (argtype == DST_OAT_TYPE || argtype == DST_OAT_SIMPLETYPE) { int index = strsearch(x.as.string, dst_type_names); if (index != -1) { return (int64_t) index; } else { - dst_asm_error(a, "unknown type"); + dst_asm_errorv(a, dst_formatc("unknown type %q", x)); } } break; } } - dst_asm_error(a, "unexpected type parsing instruction argument"); + dst_asm_errorv(a, dst_formatc("unexpected type %t parsing instruction argument", x.type)); return 0; } -/* Trim a bytecode operand to 1, 2, or 3 bytes. Error out if - * the given argument doesn't fit in the required number of bytes. */ -static uint32_t doarg_2(DstAssembler *a, int nbytes, int hassign, int64_t arg) { +/* Parse a single argument to an instruction. Trims it as well as + * try to convert arguments to bit patterns */ +static uint32_t doarg( + DstAssembler *a, + DstOpArgType argtype, + int nth, + int nbytes, + int hassign, + DstValue x) { + int64_t arg = doarg_1(a, argtype, x); /* Calculate the min and max values that can be stored given * nbytes, and whether or not the storage is signed */ int64_t min = (-hassign) << ((nbytes << 3) - 1); int64_t max = ~((-1) << ((nbytes << 3) - hassign)); if (arg < min) - dst_asm_error(a, "instruction argument is too small"); + dst_asm_errorv(a, dst_formatc("instruction argument %v is too small, must be %d byte%s", + x, nbytes, nbytes > 1 ? "s" : "")); if (arg > max) - dst_asm_error(a, "instruction argument is too large"); - return (uint32_t) (arg & 0xFFFFFFFF); -} - -/* Parse a single argument to an instruction. Trims it as well as - * try to convert arguments to bit patterns */ -static uint32_t doarg(DstAssembler *a, DstOpArgType argtype, int nth, int nbytes, int hassign, DstValue x) { - int64_t arg1 = doarg_1(a, argtype, x); - return doarg_2(a, nbytes, hassign, arg1) << (nth << 3); + dst_asm_errorv(a, dst_formatc("instruction argument %v is too large, must be %d byte%s", + x, nbytes, nbytes > 1 ? "s" : "")); + return ((uint32_t) (arg & 0xFFFFFFFF)) << (nth << 3); } /* Provide parsing methods for the different kinds of arguments */ @@ -313,14 +342,14 @@ static uint32_t read_instruction(DstAssembler *a, const DstInstructionDef *idef, { if (dst_tuple_length(argt) != 2) dst_asm_error(a, "expected 1 argument: (op, slot)"); - instr |= doarg(a, DST_OAT_SLOT, 3, 3, 0, argt[1]); + instr |= doarg(a, DST_OAT_SLOT, 1, 3, 0, argt[1]); break; } case DIT_L: { if (dst_tuple_length(argt) != 2) dst_asm_error(a, "expected 1 argument: (op, label)"); - instr |= doarg(a, DST_OAT_LABEL, 3, 3, 1, argt[1]); + instr |= doarg(a, DST_OAT_LABEL, 1, 3, 1, argt[1]); break; } case DIT_SS: @@ -328,7 +357,7 @@ static uint32_t read_instruction(DstAssembler *a, const DstInstructionDef *idef, if (dst_tuple_length(argt) != 3) dst_asm_error(a, "expected 2 arguments: (op, slot, slot)"); instr |= doarg(a, DST_OAT_SLOT, 1, 1, 0, argt[1]); - instr |= doarg(a, DST_OAT_SLOT, 3, 2, 0, argt[2]); + instr |= doarg(a, DST_OAT_SLOT, 2, 2, 0, argt[2]); break; } case DIT_SL: @@ -336,7 +365,7 @@ static uint32_t read_instruction(DstAssembler *a, const DstInstructionDef *idef, if (dst_tuple_length(argt) != 3) dst_asm_error(a, "expected 2 arguments: (op, slot, label)"); instr |= doarg(a, DST_OAT_SLOT, 1, 1, 0, argt[1]); - instr |= doarg(a, DST_OAT_LABEL, 3, 2, 1, argt[2]); + instr |= doarg(a, DST_OAT_LABEL, 2, 2, 1, argt[2]); break; } case DIT_ST: @@ -344,7 +373,7 @@ static uint32_t read_instruction(DstAssembler *a, const DstInstructionDef *idef, if (dst_tuple_length(argt) != 3) dst_asm_error(a, "expected 2 arguments: (op, slot, type)"); instr |= doarg(a, DST_OAT_SLOT, 1, 1, 0, argt[1]); - instr |= doarg(a, DST_OAT_TYPE, 3, 2, 0, argt[2]); + instr |= doarg(a, DST_OAT_TYPE, 2, 2, 0, argt[2]); break; } case DIT_SI: @@ -353,7 +382,7 @@ static uint32_t read_instruction(DstAssembler *a, const DstInstructionDef *idef, 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, 3, 2, idef->type == DIT_SI, argt[2]); + instr |= doarg(a, DST_OAT_INTEGER, 2, 2, idef->type == DIT_SI, argt[2]); break; } case DIT_SSS: @@ -365,6 +394,15 @@ static uint32_t read_instruction(DstAssembler *a, const DstInstructionDef *idef, instr |= doarg(a, DST_OAT_SLOT, 3, 1, 0, argt[3]); break; } + case DIT_SSI: + { + if (dst_tuple_length(argt) != 4) + 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, 1, argt[3]); + break; + } case DIT_SES: { DstAssembler *b = a; @@ -372,7 +410,7 @@ static uint32_t read_instruction(DstAssembler *a, const DstInstructionDef *idef, if (dst_tuple_length(argt) != 4) dst_asm_error(a, "expected 3 arguments: (op, slot, environment, envslot)"); instr |= doarg(a, DST_OAT_SLOT, 1, 1, 0, argt[1]); - env = doarg(a, DST_OAT_ENVIRONMENT, 0, 1, 0, argt[2]); + env = doarg(a, DST_OAT_ENVIRONMENT, 2, 1, 0, argt[2]); instr |= env << 16; for (env += 1; env > 0; env--) { b = b->parent; @@ -387,14 +425,13 @@ static uint32_t read_instruction(DstAssembler *a, const DstInstructionDef *idef, if (dst_tuple_length(argt) != 3) dst_asm_error(a, "expected 2 arguments: (op, slot, constant)"); instr |= doarg(a, DST_OAT_SLOT, 1, 1, 0, argt[1]); - instr |= doarg(a, DST_OAT_CONSTANT, 3, 2, 0, argt[2]); + instr |= doarg(a, DST_OAT_CONSTANT, 2, 2, 0, argt[2]); break; } } return instr; } - /* Add a closure environment to the assembler. Sub funcdefs may need * to reference outer function environments, and may change the outer environment. * Returns the index of the environment in the assembler's environments, or -1 @@ -429,10 +466,11 @@ static int64_t dst_asm_addenv(DstAssembler *a, DstValue envname) { return (int64_t) oldlen; } -/* Helper to assembly. Return the assembled funcdef */ -static DstFuncDef *dst_asm1(DstAssembler *parent, DstValue src, const char **errout) { +/* Helper to assembly. Return the assembly result */ +static DstAssembleResult dst_asm1(DstAssembler *parent, DstAssembleOptions opts) { + DstAssembleResult result; DstAssembler a; - DstTable *t = src.as.table; + const DstValue *st = opts.source.as.st; DstFuncDef *def; uint32_t count, i; const DstValue *arr; @@ -440,9 +478,6 @@ static DstFuncDef *dst_asm1(DstAssembler *parent, DstValue src, const char **err /* Initialize funcdef */ def = dst_alloc(DST_MEMORY_FUNCDEF, sizeof(DstFuncDef)); - if (NULL == def) { - DST_OUT_OF_MEMORY; - } def->environments = NULL; def->constants = NULL; def->bytecode = NULL; @@ -451,7 +486,7 @@ static DstFuncDef *dst_asm1(DstAssembler *parent, DstValue src, const char **err def->arity = 0; def->constants_length = 0; def->bytecode_length = 0; - def->environments_length = 0; + def->environments_length = 1; /* Initialize Assembler */ a.def = def; @@ -470,18 +505,19 @@ static DstFuncDef *dst_asm1(DstAssembler *parent, DstValue src, const char **err if (NULL != a.parent) { longjmp(a.parent->on_error, 1); } - *errout = a.errmessage; - return NULL; + result.result.error = a.errmessage; + result.status = DST_ASSEMBLE_ERROR; + return result; } - dst_asm_assert(&a, src.type == DST_TABLE, "expected table for assembly"); + dst_asm_assert(&a, opts.source.type == DST_STRUCT, "expected struct for assembly source"); /* Set function arity */ - x = dst_table_get(t, dst_wrap_symbol(dst_cstring("arity"))); + x = dst_struct_get(st, dst_wrap_symbol(dst_cstring("arity"))); def->arity = x.type == DST_INTEGER ? x.as.integer : 0; /* Create slot aliases */ - x = dst_table_get(t, dst_wrap_symbol(dst_cstring("slots"))); + x = dst_struct_get(st, dst_wrap_symbol(dst_cstring("slots"))); if (dst_seq_view(x, &arr, &count)) { def->slotcount = count; for (i = 0; i < count; i++) { @@ -507,7 +543,7 @@ static DstFuncDef *dst_asm1(DstAssembler *parent, DstValue src, const char **err /* Create environment aliases */ - x = dst_table_get(t, dst_wrap_symbol(dst_cstring("environments"))); + x = dst_struct_get(st, dst_wrap_symbol(dst_cstring("environments"))); if (dst_seq_view(x, &arr, &count)) { for (i = 0; i < count; i++) { dst_asm_assert(&a, arr[i].type == DST_SYMBOL, "environment must be a symbol"); @@ -518,7 +554,7 @@ static DstFuncDef *dst_asm1(DstAssembler *parent, DstValue src, const char **err } /* Parse constants */ - x = dst_table_get(t, dst_wrap_symbol(dst_cstring("constants"))); + x = dst_struct_get(st, dst_wrap_symbol(dst_cstring("constants"))); if (dst_seq_view(x, &arr, &count)) { def->constants_length = count; def->constants = malloc(sizeof(DstValue) * count); @@ -540,7 +576,7 @@ static DstFuncDef *dst_asm1(DstAssembler *parent, DstValue src, const char **err def->constants[i] = ct.as.tuple[2]; dst_table_put(&a.constants, ct.as.tuple[1], dst_wrap_integer(i)); } else { - dst_asm_error(&a, "could not parse constant"); + dst_asm_errorv(&a, dst_formatc("could not parse constant \"%v\"", ct)); } /* Todo - parse nested funcdefs */ } else { @@ -553,7 +589,7 @@ static DstFuncDef *dst_asm1(DstAssembler *parent, DstValue src, const char **err } /* Parse bytecode and labels */ - x = dst_table_get(t, dst_wrap_symbol(dst_cstring("bytecode"))); + x = dst_struct_get(st, dst_wrap_symbol(dst_cstring("bytecode"))); if (dst_seq_view(x, &arr, &count)) { /* Do labels and find length */ uint32_t blength = 0; @@ -588,7 +624,8 @@ static DstFuncDef *dst_asm1(DstAssembler *parent, DstValue src, const char **err dst_asm_assert(&a, instr.as.tuple[0].type == DST_SYMBOL, "expected symbol in assembly instruction"); idef = dst_findi(instr.as.tuple[0].as.string); - dst_asm_assert(&a, NULL != idef, "unknown instruction"); + if (NULL == idef) + dst_asm_errorv(&a, dst_formatc("unknown instruction %v", instr)); op = read_instruction(&a, idef, instr.as.tuple); } def->bytecode[a.bytecode_count++] = op; @@ -602,16 +639,23 @@ static DstFuncDef *dst_asm1(DstAssembler *parent, DstValue src, const char **err dst_asm_deinit(&a); def->environments = realloc(def->environments, def->environments_length * sizeof(uint32_t)); - return def; + result.result.def = def; + result.status = DST_ASSEMBLE_OK; + return result; } -/* Assembled a function definition. */ -int dst_asm(DstFuncDef **out, DstValue source) { - const char *err; - DstFuncDef *ret = dst_asm1(NULL, source, &err); - if (NULL == ret) { - return 1; - } - *out = ret; - return 0; +/* Assemble a function */ +DstAssembleResult dst_asm(DstAssembleOptions opts) { + return dst_asm1(NULL, opts); +} + +/* Build a function from the result */ +DstFunction *dst_asm_func(DstAssembleResult result) { + if (result.status != DST_ASSEMBLE_OK) { + return NULL; + } + DstFunction *func = dst_alloc(DST_MEMORY_FUNCTION, sizeof(DstFunction)); + func->def = result.result.def; + func->envs = NULL; + return func; } diff --git a/core/fiber.c b/core/fiber.c index 598da716..8148df02 100644 --- a/core/fiber.c +++ b/core/fiber.c @@ -24,57 +24,27 @@ #include "gc.h" /* Initialize a new fiber */ -DstFiber *dst_fiber(DstFunction *func, uint32_t capacity) { +DstFiber *dst_fiber(uint32_t capacity) { DstFiber *fiber = dst_alloc(DST_MEMORY_FIBER, sizeof(DstFiber)); - if (capacity < 2 * DST_FRAME_SIZE) - capacity = 2 * DST_FRAME_SIZE; fiber->capacity = capacity; - DstValue *data = malloc(sizeof(DstValue) * capacity); - if (NULL == data) { - DST_OUT_OF_MEMORY; + if (capacity) { + DstValue *data = malloc(sizeof(DstValue) * capacity); + if (NULL == data) { + DST_OUT_OF_MEMORY; + } + fiber->data = data; + } else { + fiber->data = NULL; } - fiber->data = data; - return dst_fiber_reset(fiber, func); + return dst_fiber_reset(fiber); } /* Clear a fiber (reset it) */ -DstFiber *dst_fiber_reset(DstFiber *fiber, DstFunction *func) { - uint32_t i; - DstStackFrame *frame; - - if (NULL == func) { - /* Cfunction */ - fiber->frame = DST_FRAME_SIZE; - fiber->frametop = DST_FRAME_SIZE; - fiber->stacktop = fiber->frametop + DST_FRAME_SIZE; - fiber->status = DST_FIBER_PENDING; - frame = dst_fiber_frame(fiber); - - /* Initialize the frame */ - frame->prevframe = 0; - frame->pc = NULL; - frame->func = NULL; - } else { - /* Set the frame to the first available index */ - fiber->frame = DST_FRAME_SIZE; - fiber->frametop = DST_FRAME_SIZE + func->def->slotcount; - fiber->stacktop = fiber->frametop + DST_FRAME_SIZE; - fiber->status = DST_FIBER_PENDING; - frame = dst_fiber_frame(fiber); - - /* Initialize the frame */ - frame->prevframe = 0; - frame->pc = func->def->bytecode; - frame->func = func; - - /* Nil slots */ - for (i = DST_FRAME_SIZE; i < fiber->frametop; ++i) { - fiber->data[i].type = DST_NIL; - } - } - - /* Reset parent. Set it manually if needed. */ - fiber->parent = NULL; +DstFiber *dst_fiber_reset(DstFiber *fiber) { + fiber->frame = 0; + fiber->frametop = 0; + fiber->stacktop = DST_FRAME_SIZE; + fiber->status = DST_FIBER_DEAD; return fiber; } @@ -133,7 +103,7 @@ void dst_fiber_pushn(DstFiber *fiber, const DstValue *arr, uint32_t n) { * If there is nothing to pop of of the stack, return nil. */ DstValue dst_fiber_popvalue(DstFiber *fiber) { uint32_t newstacktop = fiber->stacktop - 1; - if (newstacktop <= fiber->frametop + DST_FRAME_SIZE) { + if (newstacktop < fiber->frametop + DST_FRAME_SIZE) { return dst_wrap_nil(); } fiber->stacktop = newstacktop; @@ -263,6 +233,10 @@ void dst_fiber_cframe_tail(DstFiber *fiber) { uint32_t nextframetop = fiber->frame + size;; uint32_t nextstacktop = nextframetop + DST_FRAME_SIZE; + if (fiber->frame == 0) { + return dst_fiber_cframe(fiber); + } + DstValue *stack = fiber->data + fiber->frame; DstValue *args = fiber->data + fiber->frametop + DST_FRAME_SIZE; diff --git a/core/gc.c b/core/gc.c index 7e226ec1..e669af0a 100644 --- a/core/gc.c +++ b/core/gc.c @@ -58,24 +58,6 @@ void dst_mark(DstValue x) { } } -/* Unpin a value. This enables the GC to collect the value's - * memory again. */ -void dst_unpin(DstValue x) { - switch (x.type) { - default: break; - case DST_STRING: - case DST_SYMBOL: dst_unpin_string(x.as.string); break; - case DST_FUNCTION: dst_unpin_function(x.as.function); break; - case DST_ARRAY: dst_unpin_array(x.as.array); break; - case DST_TABLE: dst_unpin_table(x.as.table); break; - case DST_STRUCT: dst_unpin_struct(x.as.st); break; - case DST_TUPLE: dst_unpin_tuple(x.as.tuple); break; - case DST_BUFFER: dst_unpin_buffer(x.as.buffer); break; - case DST_FIBER: dst_unpin_fiber(x.as.fiber); break; - case DST_USERDATA: dst_unpin_userdata(x.as.pointer); break; - } -} - /* Pin a value. This prevents a value from being garbage collected. * Needed if the valueis not accesible to the garbage collector, but * still in use by the program. For example, a c function that @@ -98,6 +80,24 @@ void dst_pin(DstValue x) { } } +/* Unpin a value. This enables the GC to collect the value's + * memory again. */ +void dst_unpin(DstValue x) { + switch (x.type) { + default: break; + case DST_STRING: + case DST_SYMBOL: dst_unpin_string(x.as.string); break; + case DST_FUNCTION: dst_unpin_function(x.as.function); break; + case DST_ARRAY: dst_unpin_array(x.as.array); break; + case DST_TABLE: dst_unpin_table(x.as.table); break; + case DST_STRUCT: dst_unpin_struct(x.as.st); break; + case DST_TUPLE: dst_unpin_tuple(x.as.tuple); break; + case DST_BUFFER: dst_unpin_buffer(x.as.buffer); break; + case DST_FIBER: dst_unpin_fiber(x.as.fiber); break; + case DST_USERDATA: dst_unpin_userdata(x.as.pointer); break; + } +} + static void dst_mark_string(const uint8_t *str) { dst_gc_mark(dst_string_raw(str)); } @@ -188,9 +188,10 @@ static void dst_mark_function(DstFunction *func) { return; dst_gc_mark(func); numenvs = func->def->environments_length; - for (i = 0; i < numenvs; ++i) - if (NULL != func->envs[i]) - dst_mark_funcenv(func->envs[i]); + if (NULL != func->envs) + for (i = 0; i < numenvs; ++i) + if (NULL != func->envs[i]) + dst_mark_funcenv(func->envs[i]); dst_mark_funcdef(func->def); } diff --git a/core/parse.c b/core/parse.c index 0642b1d4..ad0af931 100644 --- a/core/parse.c +++ b/core/parse.c @@ -116,9 +116,8 @@ static int check_str_const(const char *ref, const uint8_t *start, const uint8_t /* Quote a value */ static DstValue quote(DstValue x) { - DstValue sym = dst_wrap_symbol(dst_cstring("quote")); DstValue *t = dst_tuple_begin(2); - t[0] = sym; + t[0] = dst_cstrings("quote"); t[1] = x; return dst_wrap_tuple(dst_tuple_end(t)); } @@ -153,24 +152,20 @@ static int is_symbol_char(uint8_t c) { static int to_hex(uint8_t c) { if (c >= '0' && c <= '9') { return c - '0'; - } else if (c >= 'a' && c <= 'f') { - return 10 + c - 'a'; } else if (c >= 'A' && c <= 'F') { return 10 + c - 'A'; + } else if (c >= 'a' && c <= 'f') { + return 10 + c - 'a'; } else { return -1; } } typedef struct { - DstFiber *fiber; + DstArray stack; const uint8_t *end; const char *errmsg; - enum { - DST_PARSE_OK, - DST_PARSE_ERROR, - DST_PARSE_UNEPECTED_EOS - } status; + DstParseStatus status; } ParseArgs; /* Entry point of the recursive descent parser */ @@ -186,6 +181,9 @@ static const uint8_t *parse_recur( /* Prevent stack overflow */ if (recur == 0) goto too_much_recur; + /* try parsing again */ + begin: + /* Trim leading whitespace and count quotes */ while (src < end && (is_whitespace(*src) || *src == '\'')) { if (*src == '\'') { @@ -201,7 +199,9 @@ static const uint8_t *parse_recur( switch (*src) { /* Numbers, symbols, simple literals */ - default: { + default: + atom: + { double real; int64_t integer; const uint8_t *tokenend = src; @@ -230,7 +230,17 @@ static const uint8_t *parse_recur( break; } - case ':': { + case '#': + { + /* Jump to next newline */ + while (src < end && *src != '\n') + ++src; + goto begin; + } + + /* Check keyword style strings */ + case ':': + { const uint8_t *tokenend = ++src; while (tokenend < end && is_symbol_char(*tokenend)) tokenend++; @@ -241,9 +251,10 @@ static const uint8_t *parse_recur( } /* String literals */ - case '"': { + case '"': + { const uint8_t *strend = ++src; - const uint8_t *strstart = src; + const uint8_t *strstart = strend; uint32_t len = 0; int containsEscape = 0; /* Preprocess string to check for escapes and string end */ @@ -253,10 +264,11 @@ static const uint8_t *parse_recur( containsEscape = 1; if (strend >= end) goto unexpected_eos; if (*strend == 'h') { - strend += 2; + strend += 3; if (strend >= end) goto unexpected_eos; } else { strend++; + if (strend >= end) goto unexpected_eos; } } } @@ -266,7 +278,7 @@ static const uint8_t *parse_recur( while (src < strend) { if (*src == '\\') { src++; - switch (*++src) { + switch (*src++) { case 'n': *write++ = '\n'; break; case 'r': *write++ = '\r'; break; case 't': *write++ = '\t'; break; @@ -293,22 +305,31 @@ static const uint8_t *parse_recur( ret = dst_wrap_string(dst_string_end(buf)); } else { ret = dst_wrap_string(dst_string(strstart, strend - strstart)); - src = strend + 1; } + src = strend + 1; break; } /* Data Structure literals */ + case '@': + if (src[1] != '{') + goto atom; case '(': case '[': - case '{': { + case '{': + { uint32_t n = 0, i = 0; + uint32_t istable = 0; uint8_t close; switch (*src++) { case '[': close = ']'; break; case '{': close = '}'; break; + case '@': close = '}'; src++; istable = 1; break; default: close = ')'; break; } + /* Trim trailing whitespace */ + while (src < end && (is_whitespace(*src))) + ++src; /* Recursively parse inside literal */ while (*src != close) { src = parse_recur(args, src, recur - 1); @@ -321,24 +342,42 @@ static const uint8_t *parse_recur( src++; switch (close) { case ')': - case ']': { DstValue *tup = dst_tuple_begin(n); for (i = n; i > 0; i--) - tup[i - 1] = dst_fiber_popvalue(args->fiber); + tup[i - 1] = dst_array_pop(&args->stack); ret = dst_wrap_tuple(dst_tuple_end(tup)); break; } + case ']': + { + DstArray *arr = dst_array(n); + for (i = n; i > 0; i--) + arr->data[i - 1] = dst_array_pop(&args->stack); + arr->count = n; + ret = dst_wrap_array(arr); + break; + } case '}': { if (n & 1) goto struct_oddargs; - DstValue *st = dst_struct_begin(n >> 1); - for (i = n; i > 0; i -= 2) { - DstValue val = dst_fiber_popvalue(args->fiber); - DstValue key = dst_fiber_popvalue(args->fiber); - dst_struct_put(st, key, val); + if (istable) { + DstTable *t = dst_table(n); + for (i = n; i > 0; i -= 2) { + DstValue val = dst_array_pop(&args->stack); + DstValue key = dst_array_pop(&args->stack); + dst_table_put(t, key, val); + } + ret = dst_wrap_table(t); + } else { + DstValue *st = dst_struct_begin(n >> 1); + for (i = n; i > 0; i -= 2) { + DstValue val = dst_array_pop(&args->stack); + DstValue key = dst_array_pop(&args->stack); + dst_struct_put(st, key, val); + } + ret = dst_wrap_struct(dst_struct_end(st)); } - ret = dst_wrap_struct(dst_struct_end(st)); break; } } @@ -350,7 +389,7 @@ static const uint8_t *parse_recur( while (qcount--) ret = quote(ret); /* Push the result to the stack */ - dst_fiber_push(args->fiber, ret); + dst_array_push(&args->stack, ret); /* Return the new source position for further calls */ return src; @@ -394,42 +433,37 @@ static const uint8_t *parse_recur( } /* Parse an array of bytes. Return value in the fiber return value. */ -int dst_parse(const uint8_t *src, uint32_t len, const uint8_t **newsrc) { +DstParseResult dst_parse(const uint8_t *src, uint32_t len) { + DstParseResult res; ParseArgs args; - DstFiber *fiber = dst_vm_fiber; + const uint8_t *newsrc; - /* Save original stack top */ - uint32_t oldstacktop = fiber->stacktop; - - args.fiber = fiber; + dst_array_init(&args.stack, 10); args.status = DST_PARSE_OK; args.end = src + len; args.errmsg = NULL; - src = parse_recur(&args, src, DST_RECURSION_GUARD); - if (NULL != newsrc) *newsrc = src; + newsrc = parse_recur(&args, src, DST_RECURSION_GUARD); + res.status = args.status; + res.bytes_read = (uint32_t) (newsrc - src); + + /* TODO - source maps */ + res.map = dst_wrap_nil(); if (args.errmsg) { - fiber->ret = dst_wrap_string(dst_cstring(args.errmsg)); + res.result.error = dst_cstring(args.errmsg); } else { - fiber->ret = dst_fiber_popvalue(fiber); + res.result.value = dst_array_pop(&args.stack); } - /* Reset stacktop */ - fiber->stacktop = oldstacktop; + dst_array_deinit(&args.stack); - return args.status; + return res; } /* Parse a c string */ -int dst_parsec(const char *src, const char **newsrc) { +DstParseResult dst_parsec(const char *src) { uint32_t len = 0; - const uint8_t *ns = NULL; - int status; while (src[len]) ++len; - status = dst_parse((const uint8_t *)src, len, &ns); - if (newsrc) { - *newsrc = (const char *)ns; - } - return status; + return dst_parse((const uint8_t *)src, len); } diff --git a/core/string.c b/core/string.c index a28e340d..707e5f28 100644 --- a/core/string.c +++ b/core/string.c @@ -359,25 +359,176 @@ void dst_short_description_b(DstBuffer *buffer, DstValue x) { } } +/* Helper structure for stringifying deeply nested structures */ +typedef struct DstPrinter DstPrinter; +struct DstPrinter { + DstBuffer buffer; + DstTable seen; + uint32_t flags; + int64_t next; + uint32_t indent; + uint32_t indent_size; + uint32_t token_line_limit; + uint32_t depth; +}; + +#define DST_PRINTFLAG_INDENT 1 +#define DST_PRINTFLAG_TABLEPAIR 2 +#define DST_PRINTFLAG_COLORIZE 4 +#define DST_PRINTFLAG_ALLMAN 8 + +/* Go to next line for printer */ +static void dst_print_indent(DstPrinter *p) { + uint32_t i, len; + len = p->indent_size * p->indent; + for (i = 0; i < len; i++) { + dst_buffer_push_u8(&p->buffer, ' '); + } +} + +/* Check if a value is a print atom (not a printable data structure) */ +static int is_print_ds(DstValue v) { + switch (v.type) { + default: return 0; + case DST_ARRAY: + case DST_STRUCT: + case DST_TUPLE: + case DST_TABLE: return 1; + } +} + +/* VT100 Colors for types */ +static const char *dst_type_colors[15] = { + "\x1B[35m", + "\x1B[33m", + "\x1B[33m", + "\x1B[35m", + "\x1B[32m", + "\x1B[36m", + "", + "", + "", + "", + "\x1B[37m", + "\x1B[37m", + "\x1B[37m", + "\x1B[37m", + "\x1B[37m" +}; + +/* Forward declaration */ +static void dst_description_helper(DstPrinter *p, DstValue x); + +/* Print a hastable view inline */ +static void dst_print_hashtable_inner(DstPrinter *p, const DstValue *data, uint32_t len, uint32_t cap) { + uint32_t i; + int doindent = 0; + if (p->flags & DST_PRINTFLAG_INDENT) { + if (len <= p->token_line_limit) { + for (i = 0; i < cap; i += 2) { + if (is_print_ds(data[i]) || is_print_ds(data[i + 1])) { + doindent = 1; + break; + } + } + } else { + doindent = 1; + } + } + if (doindent) { + dst_buffer_push_u8(&p->buffer, '\n'); + p->indent++; + for (i = 0; i < cap; i += 2) { + if (data[i].type != DST_NIL) { + dst_print_indent(p); + dst_description_helper(p, data[i]); + dst_buffer_push_u8(&p->buffer, ' '); + dst_description_helper(p, data[i + 1]); + dst_buffer_push_u8(&p->buffer, '\n'); + } + } + p->indent--; + dst_print_indent(p); + } else { + int isfirst = 1; + for (i = 0; i < cap; i += 2) { + if (data[i].type != DST_NIL) { + if (isfirst) + isfirst = 0; + else + dst_buffer_push_u8(&p->buffer, ' '); + dst_description_helper(p, data[i]); + dst_buffer_push_u8(&p->buffer, ' '); + dst_description_helper(p, data[i + 1]); + } + } + } +} + +/* Help print a sequence */ +static void dst_print_seq_inner(DstPrinter *p, const DstValue *data, uint32_t len) { + uint32_t i; + int doindent = 0; + if (p->flags & DST_PRINTFLAG_INDENT) { + if (len <= p->token_line_limit) { + for (i = 0; i < len; ++i) { + if (is_print_ds(data[i])) { + doindent = 1; + break; + } + } + } else { + doindent = 1; + } + } + if (doindent) { + dst_buffer_push_u8(&p->buffer, '\n'); + p->indent++; + for (i = 0; i < len; ++i) { + dst_print_indent(p); + dst_description_helper(p, data[i]); + dst_buffer_push_u8(&p->buffer, '\n'); + } + p->indent--; + dst_print_indent(p); + } else { + for (i = 0; i < len; ++i) { + dst_description_helper(p, data[i]); + if (i != len - 1) + dst_buffer_push_u8(&p->buffer, ' '); + } + } +} + /* Static debug print helper */ -static int64_t dst_description_helper(DstBuffer *b, DstTable *seen, DstValue x, int64_t next, int depth) { - DstValue check = dst_table_get(seen, x); +static void dst_description_helper(DstPrinter *p, DstValue x) { + p->depth--; + DstValue check = dst_table_get(&p->seen, x); if (check.type == DST_INTEGER) { - dst_buffer_push_cstring(b, ""); + dst_buffer_push_cstring(&p->buffer, "buffer, check.as.integer); + dst_buffer_push_cstring(&p->buffer, ">"); } else { const char *open; const char *close; - uint32_t len, i; + uint32_t len, cap; const DstValue *data; switch (x.type) { default: - dst_short_description_b(b, x); - return next; + if (p->flags & DST_PRINTFLAG_COLORIZE) { + dst_buffer_push_cstring(&p->buffer, dst_type_colors[x.type]); + dst_short_description_b(&p->buffer, x); + dst_buffer_push_cstring(&p->buffer, "\x1B[0m"); + } else { + dst_short_description_b(&p->buffer, x); + } + p->depth++; + return; case DST_STRUCT: open = "{"; close = "}"; break; case DST_TABLE: - open = "{"; close = "}"; + open = "@{"; close = "}"; break; case DST_TUPLE: open = "("; close = ")"; @@ -386,44 +537,50 @@ static int64_t dst_description_helper(DstBuffer *b, DstTable *seen, DstValue x, open = "["; close = "]"; break; } - dst_table_put(seen, x, dst_wrap_integer(next++)); - dst_buffer_push_cstring(b, open); - if (depth == 0) { - dst_buffer_push_cstring(b, "..."); - } else if (dst_hashtable_view(x, &data, &len)) { - int isfirst = 1; - for (i = 0; i < len; i += 2) { - if (data[i].type != DST_NIL) { - if (isfirst) - isfirst = 0; - else - dst_buffer_push_u8(b, ' '); - next = dst_description_helper(b, seen, data[i], next, depth - 1); - dst_buffer_push_u8(b, ' '); - next = dst_description_helper(b, seen, data[i + 1], next, depth - 1); - } - } + dst_table_put(&p->seen, x, dst_wrap_integer(p->next++)); + dst_buffer_push_cstring(&p->buffer, open); + if (p->depth == 0) { + dst_buffer_push_cstring(&p->buffer, "..."); + } else if (dst_hashtable_view(x, &data, &len, &cap)) { + dst_print_hashtable_inner(p, data, len, cap); } else if (dst_seq_view(x, &data, &len)) { - for (i = 0; i < len; ++i) { - next = dst_description_helper(b, seen, data[i], next, depth - 1); - if (i != len - 1) - dst_buffer_push_u8(b, ' '); - } + dst_print_seq_inner(p, data, len); } - dst_buffer_push_cstring(b, close); + dst_buffer_push_cstring(&p->buffer, close); } - return next; + /* Remove from seen as we know that printing completes, we + * can print in multiple times and we know we are not recursing */ + dst_table_remove(&p->seen, x); + p->depth++; +} + +/* Init printer to defaults */ +static void dst_printer_defaults(DstPrinter *p) { + p->next = 0; + p->flags = DST_PRINTFLAG_INDENT; + p->depth = 4; + p->indent = 0; + p->indent_size = 2; + p->token_line_limit = 5; } /* Debug print. Returns a description of an object as a string. */ const uint8_t *dst_description(DstValue x) { - DstBuffer *buf = dst_buffer(10); - DstTable *seen = dst_table(10); + DstPrinter printer; + const uint8_t *ret; + + dst_printer_defaults(&printer); + dst_buffer_init(&printer.buffer, 0); + dst_table_init(&printer.seen, 10); /* Only print description up to a depth of 4 */ - dst_description_helper(buf, seen, x, 0, 4); + dst_description_helper(&printer, x); + ret = dst_string(printer.buffer.data, printer.buffer.count); - return dst_string(buf->data, buf->count); + dst_buffer_deinit(&printer.buffer); + dst_table_deinit(&printer.seen); + + return ret; } /* Convert any value to a dst string. Similar to description, but @@ -431,11 +588,114 @@ const uint8_t *dst_description(DstValue x) { const uint8_t *dst_to_string(DstValue x) { switch (x.type) { default: - return dst_description(x); + return dst_short_description(x); case DST_STRING: case DST_SYMBOL: return x.as.string; - case DST_BUFFER: - return dst_string(x.as.buffer->data, x.as.buffer->count); + } +} + +/* Helper function for formatting strings. Useful for generating error messages and the like. + * Similiar to printf, but specialized for operating with dst. */ +const uint8_t *dst_formatc(const char *format, ...) { + va_list args; + uint32_t len = 0; + uint32_t i; + const uint8_t *ret; + DstPrinter printer; + DstBuffer *bufp = &printer.buffer; + + /* Calculate length */ + while (format[len]) len++; + + /* Initialize buffer */ + dst_buffer_init(bufp, len); + + /* Start args */ + va_start(args, format); + + /* Iterate length */ + for (i = 0; i < len; i++) { + uint8_t c = format[i]; + switch (c) { + default: + dst_buffer_push_u8(bufp, c); + break; + case '%': + { + if (i + 1 >= len) + break; + switch (format[++i]) { + default: + dst_buffer_push_u8(bufp, format[i]); + break; + case 'f': + real_to_string_b(bufp, va_arg(args, double)); + break; + case 'd': + integer_to_string_b(bufp, va_arg(args, int64_t)); + break; + case 'S': + { + const uint8_t *str = va_arg(args, const uint8_t *); + dst_buffer_push_bytes(bufp, str, dst_string_length(str)); + break; + } + case 's': + dst_buffer_push_cstring(bufp, va_arg(args, const char *)); + break; + case 'c': + dst_buffer_push_u8(bufp, va_arg(args, int64_t)); + break; + case 'p': + { + dst_printer_defaults(&printer); + dst_table_init(&printer.seen, 10); + /* Only print description up to a depth of 4 */ + dst_description_helper(&printer, va_arg(args, DstValue)); + dst_table_deinit(&printer.seen); + break; + } + case 'q': + { + const uint8_t *str = dst_to_string(va_arg(args, DstValue)); + dst_escape_string_b(bufp, str); + break; + } + case 't': + { + dst_buffer_push_cstring(bufp, dst_type_names[va_arg(args, DstType)]); + break; + } + case 'V': + { + const uint8_t *str = dst_short_description(va_arg(args, DstValue)); + dst_buffer_push_bytes(bufp, str, dst_string_length(str)); + break; + } + case 'v': + { + const uint8_t *str = dst_description(va_arg(args, DstValue)); + dst_buffer_push_bytes(bufp, str, dst_string_length(str)); + break; + } + } + } + } + } + + va_end(args); + + ret = dst_string(printer.buffer.data, printer.buffer.count); + dst_buffer_deinit(&printer.buffer); + return ret; +} + +/* Print string to stdout */ +void dst_puts(const uint8_t *str) { + uint32_t i; + uint32_t len = dst_string_length(str); + for (i = 0; i < len; i++) { + putc(str[i], stdout); } } diff --git a/core/struct.c b/core/struct.c index c3b9be3c..d7c602b3 100644 --- a/core/struct.c +++ b/core/struct.c @@ -47,7 +47,7 @@ DstValue *dst_struct_begin(uint32_t count) { /* Find an item in a struct */ static const DstValue *dst_struct_find(const DstValue *st, DstValue key) { uint32_t cap = dst_struct_capacity(st); - uint32_t index = (dst_hash(key) % (cap / 2)) * 2; + uint32_t index = (dst_hash(key) % cap) & (~1); uint32_t i; for (i = index; i < cap; i += 2) if (st[i].type == DST_NIL || dst_equals(st[i], key)) @@ -64,7 +64,7 @@ static const DstValue *dst_struct_find(const DstValue *st, DstValue key) { void dst_struct_put(DstValue *st, DstValue key, DstValue value) { uint32_t cap = dst_struct_capacity(st); uint32_t hash = dst_hash(key); - uint32_t index = (hash % (cap / 2)) * 2; + uint32_t index = (hash % cap) & (~1); uint32_t i, j, dist; uint32_t bounds[4] = {index, cap, 0, index}; if (key.type == DST_NIL || value.type == DST_NIL) return; @@ -88,7 +88,7 @@ void dst_struct_put(DstValue *st, DstValue key, DstValue value) { * with different order have the same internal layout, and therefor * will compare properly - i.e., {1 2 3 4} should equal {3 4 1 2}. */ otherhash = dst_hash(st[i]); - otherindex = (otherhash % (cap / 2)) * 2; + otherindex = (otherhash % cap) & (~1); otherdist = (i + cap - otherindex) % cap; if (dist < otherdist) status = -1; @@ -113,7 +113,7 @@ void dst_struct_put(DstValue *st, DstValue key, DstValue value) { /* Save dist and hash of new kv pair */ dist = otherdist; hash = otherhash; - } else { + } else if (status == 0) { /* This should not happen - it means * than a key was added to the struct more than once */ return; @@ -178,3 +178,15 @@ DstValue dst_struct_next(const DstValue *st, DstValue key) { } return dst_wrap_nil(); } + +/* Convert struct to table */ +DstTable *dst_struct_to_table(const DstValue *st) { + DstTable *table = dst_table(dst_struct_capacity(st)); + uint32_t i; + for (i = 0; i < dst_struct_capacity(st); i += 2) { + if (st[i].type != DST_NIL) { + dst_table_put(table, st[i], st[i + 1]); + } + } + return table; +} diff --git a/core/syscalls.c b/core/syscalls.c index 0247839a..60d05ded 100644 --- a/core/syscalls.c +++ b/core/syscalls.c @@ -24,7 +24,7 @@ #include #include -int dst_print(DstFiber *fiber, DstValue *argv, uint32_t argn) { +int dst_print(DstValue *argv, uint32_t argn) { uint32_t i; for (i = 0; i < argn; ++i) { uint32_t j, len; diff --git a/core/table.c b/core/table.c index 7932eb08..c375577a 100644 --- a/core/table.c +++ b/core/table.c @@ -170,3 +170,15 @@ DstValue dst_table_next(DstTable *t, DstValue key) { } return dst_wrap_nil(); } + +/* Convert table to struct */ +const DstValue *dst_table_to_struct(DstTable *t) { + uint32_t i; + const DstValue *st; + st = dst_struct_begin(t->count); + for (i = 0; i < t->capacity; i++) { + if (t->data[i].type != DST_NIL) + dst_struct_put(st, t->data[i], t->data[i + 1]); + } + return dst_struct_end(st); +} diff --git a/core/util.c b/core/util.c index 83723dc6..2c4bf823 100644 --- a/core/util.c +++ b/core/util.c @@ -76,14 +76,16 @@ int dst_chararray_view(DstValue str, const uint8_t **data, uint32_t *len) { /* Read both structs and tables as the entries of a hashtable with * identical structure. Returns 1 if the view can be constructed and * 0 if the type is invalid. */ -int dst_hashtable_view(DstValue tab, const DstValue **data, uint32_t *cap) { +int dst_hashtable_view(DstValue tab, const DstValue **data, uint32_t *len, uint32_t *cap) { if (tab.type == DST_TABLE) { *data = tab.as.table->data; *cap = tab.as.table->capacity; + *len = tab.as.table->count; return 1; } else if (tab.type == DST_STRUCT) { *data = tab.as.st; *cap = dst_struct_capacity(tab.as.st); + *len = dst_struct_length(tab.as.st); return 1; } return 0; diff --git a/core/vm.c b/core/vm.c index b38d63ff..b53caaaa 100644 --- a/core/vm.c +++ b/core/vm.c @@ -27,7 +27,7 @@ DstFiber *dst_vm_fiber; /* Start running the VM from where it left off. */ -int dst_continue(DstFiber *fiber) { +int dst_continue() { /* VM state */ DstValue *stack; @@ -36,9 +36,9 @@ int dst_continue(DstFiber *fiber) { /* Used to extract bits from the opcode that correspond to arguments. * Pulls out unsigned integers */ -#define oparg(shift, mask) ((*pc >> ((shift) << 3)) & (mask)) +#define oparg(shift, mask) (((*pc) >> ((shift) << 3)) & (mask)) -#define vm_throw(e) do { fiber->ret = dst_wrap_string(dst_cstring((e))); goto vm_error; } while (0) +#define vm_throw(e) do { dst_vm_fiber->ret = dst_wrap_string(dst_cstring((e))); goto vm_error; } while (0) #define vm_assert(cond, e) do {if (!(cond)) vm_throw((e)); } while (0) #define vm_binop_integer(op) \ @@ -69,19 +69,19 @@ int dst_continue(DstFiber *fiber) { vm_assert(op1.type == DST_INTEGER || op1.type == DST_REAL, "expected number");\ vm_assert(op2.type == DST_INTEGER || op2.type == DST_REAL, "expected number");\ stack[oparg(1, 0xFF)] = op1.type == DST_INTEGER\ - ? op2.type == DST_INTEGER\ + ? (op2.type == DST_INTEGER\ ? dst_wrap_integer(op1.as.integer op op2.as.integer)\ - : dst_wrap_real(dst_integer_to_real(op1.as.integer) op op2.as.real)\ - : op2.type == DST_INTEGER\ + : dst_wrap_real(dst_integer_to_real(op1.as.integer) op op2.as.real))\ + : (op2.type == DST_INTEGER\ ? dst_wrap_real(op1.as.real op dst_integer_to_real(op2.as.integer))\ - : dst_wrap_real(op1.as.real op op2.as.real);\ + : dst_wrap_real(op1.as.real op op2.as.real));\ pc++;\ continue;\ } #define vm_init_fiber_state() \ - fiber->status = DST_FIBER_ALIVE;\ - stack = fiber->data + fiber->frame;\ + dst_vm_fiber->status = DST_FIBER_ALIVE;\ + stack = dst_vm_fiber->data + dst_vm_fiber->frame;\ pc = dst_stack_frame(stack)->pc;\ func = dst_stack_frame(stack)->func; @@ -105,7 +105,7 @@ int dst_continue(DstFiber *fiber) { continue; case DOP_ERROR: - fiber->ret = stack[oparg(1, 0xFF)]; + dst_vm_fiber->ret = stack[oparg(1, 0xFF)]; goto vm_error; case DOP_TYPECHECK: @@ -115,11 +115,11 @@ int dst_continue(DstFiber *fiber) { continue; case DOP_RETURN: - fiber->ret = stack[oparg(1, 0xFFFFFF)]; + dst_vm_fiber->ret = stack[oparg(1, 0xFFFFFF)]; goto vm_return; case DOP_RETURN_NIL: - fiber->ret.type = DST_NIL; + dst_vm_fiber->ret.type = DST_NIL; goto vm_return; case DOP_COERCE_INTEGER: @@ -394,8 +394,8 @@ int dst_continue(DstFiber *fiber) { if (fd->flags & DST_FUNCDEF_FLAG_NEEDSENV) { /* Delayed capture of current stack frame */ DstFuncEnv *env = dst_alloc(DST_MEMORY_FUNCENV, sizeof(DstFuncEnv)); - env->offset = fiber->frame; - env->as.fiber = fiber; + env->offset = dst_vm_fiber->frame; + env->as.fiber = dst_vm_fiber; env->length = func->def->slotcount; fn->envs[0] = env; } else { @@ -411,19 +411,19 @@ int dst_continue(DstFiber *fiber) { } case DOP_PUSH: - dst_fiber_push(fiber, stack[oparg(1, 0xFFFFFF)]); + dst_fiber_push(dst_vm_fiber, stack[oparg(1, 0xFFFFFF)]); pc++; break; case DOP_PUSH_2: - dst_fiber_push2(fiber, + dst_fiber_push2(dst_vm_fiber, stack[oparg(1, 0xFF)], stack[oparg(2, 0xFFFF)]); pc++; break;; case DOP_PUSH_3: - dst_fiber_push3(fiber, + dst_fiber_push3(dst_vm_fiber, stack[oparg(1, 0xFF)], stack[oparg(2, 0xFF)], stack[oparg(3, 0xFF)]); @@ -435,7 +435,7 @@ int dst_continue(DstFiber *fiber) { uint32_t count; const DstValue *array; if (dst_seq_view(stack[oparg(1, 0xFFFFFF)], &array, &count)) { - dst_fiber_pushn(fiber, array, count); + dst_fiber_pushn(dst_vm_fiber, array, count); } else { vm_throw("expected array or tuple"); } @@ -448,19 +448,19 @@ int dst_continue(DstFiber *fiber) { DstValue callee = stack[oparg(2, 0xFFFF)]; if (callee.type == DST_FUNCTION) { func = callee.as.function; - dst_fiber_funcframe(fiber, func); - stack = fiber->data + fiber->frame; + dst_fiber_funcframe(dst_vm_fiber, func); + stack = dst_vm_fiber->data + dst_vm_fiber->frame; pc = func->def->bytecode; break; } else if (callee.type == DST_CFUNCTION) { - dst_fiber_cframe(fiber); - stack = fiber->data + fiber->frame; - fiber->ret.type = DST_NIL; - if (callee.as.cfunction(fiber, stack, fiber->frametop - fiber->frame)) { - dst_fiber_popframe(fiber); + dst_fiber_cframe(dst_vm_fiber); + stack = dst_vm_fiber->data + dst_vm_fiber->frame; + dst_vm_fiber->ret.type = DST_NIL; + if (callee.as.cfunction(stack, dst_vm_fiber->frametop - dst_vm_fiber->frame)) { + dst_fiber_popframe(dst_vm_fiber); goto vm_error; } else { - dst_fiber_popframe(fiber); + dst_fiber_popframe(dst_vm_fiber); goto vm_return; } } else { @@ -474,19 +474,19 @@ int dst_continue(DstFiber *fiber) { DstValue callee = stack[oparg(2, 0xFFFF)]; if (callee.type == DST_FUNCTION) { func = callee.as.function; - dst_fiber_funcframe_tail(fiber, func); - stack = fiber->data + fiber->frame; + dst_fiber_funcframe_tail(dst_vm_fiber, func); + stack = dst_vm_fiber->data + dst_vm_fiber->frame; pc = func->def->bytecode; break; } else if (callee.type == DST_CFUNCTION) { - dst_fiber_cframe_tail(fiber); - stack = fiber->data + fiber->frame; - fiber->ret.type = DST_NIL; - if (callee.as.cfunction(fiber, stack, fiber->frametop - fiber->frame)) { - dst_fiber_popframe(fiber); + dst_fiber_cframe_tail(dst_vm_fiber); + stack = dst_vm_fiber->data + dst_vm_fiber->frame; + dst_vm_fiber->ret.type = DST_NIL; + if (callee.as.cfunction(stack, dst_vm_fiber->frametop - dst_vm_fiber->frame)) { + dst_fiber_popframe(dst_vm_fiber); goto vm_error; } else { - dst_fiber_popframe(fiber); + dst_fiber_popframe(dst_vm_fiber); goto vm_return; } } else { @@ -499,14 +499,14 @@ int dst_continue(DstFiber *fiber) { { DstCFunction f = dst_vm_syscalls[oparg(2, 0xFF)]; vm_assert(NULL != f, "invalid syscall"); - dst_fiber_cframe(fiber); - stack = fiber->data + fiber->frame; - fiber->ret.type = DST_NIL; - if (f(fiber, stack, fiber->frametop - fiber->frame)) { - dst_fiber_popframe(fiber); + dst_fiber_cframe(dst_vm_fiber); + stack = dst_vm_fiber->data + dst_vm_fiber->frame; + dst_vm_fiber->ret.type = DST_NIL; + if (f(stack, dst_vm_fiber->frametop - dst_vm_fiber->frame)) { + dst_fiber_popframe(dst_vm_fiber); goto vm_error; } else { - dst_fiber_popframe(fiber); + dst_fiber_popframe(dst_vm_fiber); goto vm_return; } continue; @@ -531,17 +531,17 @@ int dst_continue(DstFiber *fiber) { temp.type == DST_NIL, "expected fiber"); nextfiber = temp.type == DST_FIBER ? temp.as.fiber - : fiber->parent; + : dst_vm_fiber->parent; /* Check for root fiber */ if (NULL == nextfiber) { frame->pc = pc; - fiber->ret = retvalue; + dst_vm_fiber->ret = retvalue; return 0; } vm_assert(nextfiber->status == DST_FIBER_PENDING, "can only transfer to pending fiber"); frame->pc = pc; - fiber->status = DST_FIBER_PENDING; - fiber = nextfiber; + dst_vm_fiber->status = DST_FIBER_PENDING; + dst_vm_fiber = nextfiber; vm_init_fiber_state(); stack[oparg(1, 0xFF)] = retvalue; pc++; @@ -551,26 +551,26 @@ int dst_continue(DstFiber *fiber) { /* Handle returning from stack frame. Expect return value in fiber->ret */ vm_return: { - DstValue ret = fiber->ret; - dst_fiber_popframe(fiber); - while (fiber->frame || - fiber->status == DST_FIBER_DEAD || - fiber->status == DST_FIBER_ERROR) { - fiber->status = DST_FIBER_DEAD; - if (fiber->parent) { - fiber = fiber->parent; - if (fiber->status == DST_FIBER_ALIVE) { + DstValue ret = dst_vm_fiber->ret; + dst_fiber_popframe(dst_vm_fiber); + while (!dst_vm_fiber->frame || + dst_vm_fiber->status == DST_FIBER_DEAD || + dst_vm_fiber->status == DST_FIBER_ERROR) { + dst_vm_fiber->status = DST_FIBER_DEAD; + if (NULL != dst_vm_fiber->parent) { + dst_vm_fiber = dst_vm_fiber->parent; + if (dst_vm_fiber->status == DST_FIBER_ALIVE) { /* If the parent thread is still alive, we are inside a cfunction */ return 0; } - stack = fiber->data + fiber->frame; + stack = dst_vm_fiber->data + dst_vm_fiber->frame; } else { return 0; } } - fiber->status = DST_FIBER_ALIVE; - stack = fiber->data + fiber->frame; + dst_vm_fiber->status = DST_FIBER_ALIVE; + stack = dst_vm_fiber->data + dst_vm_fiber->frame; pc = dst_stack_frame(stack)->pc; stack[oparg(1, 0xFF)] = ret; pc++; @@ -580,22 +580,22 @@ int dst_continue(DstFiber *fiber) { /* Handle errors from c functions and vm opcodes */ vm_error: { - DstValue ret = fiber->ret; - fiber->status = DST_FIBER_ERROR; - while (fiber->frame || - fiber->status == DST_FIBER_DEAD || - fiber->status == DST_FIBER_ERROR) { - if (fiber->parent == NULL) + DstValue ret = dst_vm_fiber->ret; + dst_vm_fiber->status = DST_FIBER_ERROR; + while (!dst_vm_fiber->frame || + dst_vm_fiber->status == DST_FIBER_DEAD || + dst_vm_fiber->status == DST_FIBER_ERROR) { + if (dst_vm_fiber->parent == NULL) return 1; - fiber = fiber->parent; - if (fiber->status == DST_FIBER_ALIVE) { + dst_vm_fiber = dst_vm_fiber->parent; + if (dst_vm_fiber->status == DST_FIBER_ALIVE) { /* If the parent thread is still alive, we are inside a cfunction */ return 1; } } - fiber->status = DST_FIBER_ALIVE; - stack = fiber->data + fiber->frame; + dst_vm_fiber->status = DST_FIBER_ALIVE; + stack = dst_vm_fiber->data + dst_vm_fiber->frame; pc = dst_stack_frame(stack)->pc; stack[oparg(1, 0xFF)] = ret; pc++; @@ -626,20 +626,21 @@ int dst_continue(DstFiber *fiber) { /* Run the vm with a given function. This function is * called to start the vm. */ int dst_run(DstValue callee) { - int result; + if (NULL == dst_vm_fiber) { + dst_vm_fiber = dst_fiber(0); + } else { + dst_fiber_reset(dst_vm_fiber); + } if (callee.type == DST_CFUNCTION) { - dst_vm_fiber = dst_fiber(NULL, 0); dst_vm_fiber->ret.type = DST_NIL; dst_fiber_cframe(dst_vm_fiber); - result = callee.as.cfunction(dst_vm_fiber, dst_vm_fiber->data + dst_vm_fiber->frame, 0); + return callee.as.cfunction(dst_vm_fiber->data + dst_vm_fiber->frame, 0); } else if (callee.type == DST_FUNCTION) { - dst_vm_fiber = dst_fiber(callee.as.function, 64); - result = dst_continue(dst_vm_fiber); - } else { - dst_vm_fiber->ret = dst_wrap_string(dst_cstring("expected function")); - result = 1; + dst_fiber_funcframe(dst_vm_fiber, callee.as.function); + return dst_continue(); } - return result; + dst_vm_fiber->ret = dst_wrap_string(dst_cstring("expected function")); + return 1; } /* Setup functions */ diff --git a/unittests/asm_test.c b/unittests/asm_test.c new file mode 100644 index 00000000..ed8d9bd1 --- /dev/null +++ b/unittests/asm_test.c @@ -0,0 +1,50 @@ +#include "unit.h" +#include + +int main() { + DstParseResult pres; + DstAssembleOptions opts; + DstAssembleResult ares; + DstFunction *func; + + FILE *f = fopen("./unittests/sample.dsts", "rb"); + fseek(f, 0, SEEK_END); + long fsize = ftell(f); + fseek(f, 0, SEEK_SET); //same as rewind(f); + + char *string = malloc(fsize + 1); + fread(string, fsize, 1, f); + fclose(f); + + string[fsize] = 0; + + dst_init(); + + pres = dst_parsec(string); + free(string); + + if (pres.status == DST_PARSE_ERROR) { + dst_puts(dst_formatc("parse error at %d: %s\n", pres.bytes_read, pres.result.error)); + return 1; + } + assert(pres.status == DST_PARSE_OK); + dst_puts(dst_formatc("\nparse result: %v\n\n", pres.result.value)); + + opts.flags = 0; + opts.source = pres.result.value; + opts.parsemap = dst_wrap_nil(); + + ares = dst_asm(opts); + if (ares.status == DST_ASSEMBLE_ERROR) { + dst_puts(dst_formatc("assembly error: %s\n", ares.result.error)); + return 1; + } + assert(ares.status == DST_ASSEMBLE_OK); + + func = dst_asm_func(ares); + + dst_run(dst_wrap_function(func)); + dst_puts(dst_formatc("result: %v\n", dst_vm_fiber->ret)); + + return 0; +} diff --git a/unittests/fiber_test.c b/unittests/fiber_test.c new file mode 100644 index 00000000..62913b39 --- /dev/null +++ b/unittests/fiber_test.c @@ -0,0 +1,88 @@ +#include "unit.h" +#include +#include + +/* Create dud funcdef and function */ +static DstFunction *dud_func(uint32_t slotcount, uint32_t arity, int varargs) { + DstFuncDef *def = dst_alloc(DST_MEMORY_FUNCDEF, sizeof(DstFuncDef)); + def->environments_length = 0; + def->constants_length = 0; + def->bytecode_length = 0; + def->environments = NULL; + def->constants = NULL; + def->bytecode = NULL; + def->flags = varargs ? DST_FUNCDEF_FLAG_VARARG : 0; + def->arity = arity; + def->slotcount = slotcount; + DstFunction *f = dst_alloc(DST_MEMORY_FUNCTION, sizeof(DstFunction)); + f->envs = NULL; + f->def = def; + return f; +} + +/* Print debug information for a fiber */ +static void debug_print_fiber(DstFiber *fiber, int showslots) { + uint32_t frameindex = fiber->frame; + uint32_t frametopindex = fiber->frametop; + const char *statusname = + fiber->status == DST_FIBER_ALIVE ? "alive" : + fiber->status == DST_FIBER_PENDING ? "pending" : + fiber->status == DST_FIBER_ERROR ? "error" : + "dead"; + + printf("fiber at %p\n", fiber); + printf(" frame = %d\n", fiber->frame); + printf(" frametop = %d\n", fiber->frametop); + printf(" stacktop = %d\n", fiber->stacktop); + printf(" capacity = %d\n", fiber->capacity); + printf(" status = %s\n -----\n", statusname); + + while (frameindex > 0) { + DstValue *stack = fiber->data + frameindex; + DstStackFrame *frame = dst_stack_frame(stack); + uint32_t slots = frametopindex - frameindex; + const uint8_t *str; + /* Print the stack frame */ + if (frame->func != NULL) + str = dst_to_string(dst_wrap_function(frame->func)); + else + str = dst_cstring(""); + printf(" at %.*s (slots: %d)\n", + dst_string_length(str), + (const char *)str, + slots); + /* Optionally print all values in the stack */ + if (showslots) { + for (uint32_t j = 0; j < slots; j++) { + const uint8_t *vstr = dst_to_string(stack[j]); + printf(" [%d]: %.*s\n", + j, + dst_string_length(vstr), + (const char *)vstr); + } + } + frametopindex = frameindex - DST_FRAME_SIZE; + frameindex = frame->prevframe; + } + +} + +int main() { + dst_init(); + + DstFunction *f1 = dud_func(5, 0, 1); + DstFiber *fiber1 = dst_fiber(10); + for (int i = 0; i < 2; i++) { + dst_fiber_funcframe(fiber1, f1); + } + for (int i = 0; i < 13; i++) { + dst_fiber_push(fiber1, dst_wrap_integer(i)); + } + for (int i = 0; i < 10; i++) { + dst_fiber_popvalue(fiber1); + } + dst_fiber_funcframe_tail(fiber1, dud_func(20, 0, 0)); + debug_print_fiber(fiber1, 1); + + return 0; +} diff --git a/unittests/parse_test.c b/unittests/parse_test.c new file mode 100644 index 00000000..40393cd7 --- /dev/null +++ b/unittests/parse_test.c @@ -0,0 +1,19 @@ +#include "unit.h" +#include + +int main() { + DstParseResult pres; + const uint8_t *str; + + dst_init(); + + pres = dst_parsec("'(+ 1 () [] 3 5 :hello \"hi\\h41\")"); + + assert(pres.status == DST_PARSE_OK); + assert(pres.result.value.type == DST_TUPLE); + + str = dst_to_string(pres.result.value); + printf("%.*s\n", dst_string_length(str), (const char *) str); + + return 0; +} diff --git a/libs/sample.dsts b/unittests/sample.dsts similarity index 58% rename from libs/sample.dsts rename to unittests/sample.dsts index 354f9ce4..31ce835b 100644 --- a/libs/sample.dsts +++ b/unittests/sample.dsts @@ -4,7 +4,6 @@ # in the same markup as dst itself (extended S expressions) { arity 3 - signature (integer integer integer integer) source "source file path" varargs false # Name for reference by nested funcdefs @@ -12,31 +11,32 @@ # Contains the bytecode for this function. This can be assembly # instructions or integers. Assembly will be converted to integer bytecodes immediately. bytecode [ - (typecheck 0 integer) - (typecheck 1 integer) - (typecheck 2 integer) - :checked-entry - (load-constant 3 bork) - (load-constant 4 bip) - (add-integer-unchecked 5 0 1) - (add-integer-unchecked 5 5 2) - (add-integer-unchecked 5 5 3) - (add-integer-unchecked 5 5 4) - (return 5) + (load-constant 0 bork) + (load-constant 1 bip) + (add 0 0 1) + (add-immediate 0 0 127) + (push3 0 0 0) + (push3 0 0 0) + (push3 0 0 0) + (syscall 1 0) + (return 0) ] # A source map is optional. The sourcemap corresponds with the byte code. # Each number is a 64 bit integer, the concatenation of two 32 bit integers. -# These integers represent source line, source column for each instruction. +# These integers represent source character index for each instruction. # This format may change. - source-map [ - 0x0000123400000123 - 0x0000123400000123 - 0x0000123400000125 - 0x0000123400000134 - 0x0000123400000134 - 0x0000123400000135 - ... - ] +# map [ +# 1 +# 2 +# 3 +# 10 +# 15 +# 39 +# 90 +# 134 +# ... +# ] +# # The number of slots available for the function. # Slots can be named as well for convenience. slots [ @@ -45,24 +45,25 @@ z ] # Captured outer environments that are referenced - environments [ - outer1 - outer2 - ] + environments [ ] # Constants are an array or tuple. For named constants, use a tuple that begins with let # For a literal tuple, use (quote tuple), or 'tuple. Without names, constants must be indexed # from their number +# Literal FuncEnvs and Functions may be possible later constants [ "hello" - (let bork 123) - (let bip 456) + (def bork 123) + (def bip 456) '(1 2 3) - (let atuple (1 2 3)) + (def atuple (1 2 3)) (567) -# Functions can be recursively defined. - (funcdef funcname { - ... - }) -# Literal FuncEnvs and Functions may be possible later ] +# Arbitrary meta data can be added to the source + my-meta-2 @{ + 1 2 3 4 + } + my-meta { + 1 2 3 4 5 6 7 8 9 0 11 12 13 14 + } } + diff --git a/unittests/table_test.c b/unittests/table_test.c index bd9fee54..8acccba4 100644 --- a/unittests/table_test.c +++ b/unittests/table_test.c @@ -11,5 +11,9 @@ int main() { assert(table->count == 2); dst_table_remove(table, dst_cstringv("a")); assert(table->count == 1); + assert(dst_equals( + dst_table_get(table, dst_cstringv("b")), + dst_cstringv("a") + )); return 0; }