Getting more work done on assembler, parer, and unit tests.

This commit is contained in:
bakpakin 2017-11-20 21:39:44 -05:00
parent 9a858d5a97
commit 6ca6949c2d
16 changed files with 863 additions and 357 deletions

View File

@ -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
###################

View File

@ -23,6 +23,7 @@
#include <setjmp.h>
#include <dst/dst.h>
#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;
}

View File

@ -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;

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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, "<cycle>");
dst_buffer_push_cstring(&p->buffer, "<cycle ");
integer_to_string_b(&p->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);
}
}

View File

@ -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;
}

View File

@ -24,7 +24,7 @@
#include <dst/dst.h>
#include <stdio.h>
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;

View File

@ -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);
}

View File

@ -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;

151
core/vm.c
View File

@ -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 */

50
unittests/asm_test.c Normal file
View File

@ -0,0 +1,50 @@
#include "unit.h"
#include <dst/dst.h>
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;
}

88
unittests/fiber_test.c Normal file
View File

@ -0,0 +1,88 @@
#include "unit.h"
#include <dst/dst.h>
#include <stdio.h>
/* 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("<anonymous>");
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;
}

19
unittests/parse_test.c Normal file
View File

@ -0,0 +1,19 @@
#include "unit.h"
#include <dst/dst.h>
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;
}

View File

@ -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
}
}

View File

@ -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;
}