1
0
mirror of https://github.com/janet-lang/janet synced 2024-12-01 12:29:54 +00:00

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 CCU_FLAGS = $(CFLAGS) -DDST_UNIT_TEST
DST_UNIT_BINARIES=$(addprefix unittests/,\ 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 %.out: %.c $(DST_CORE_OBJECTS) $(DST_ALL_HEADERS) unittests/unit.h
$(CC) $(CCU_FLAGS) $(DST_CORE_OBJECTS) $< -o $@ $(CC) $(CCU_FLAGS) $(DST_CORE_OBJECTS) $< -o $@
unit: $(DST_UNIT_BINARIES) unit: $(DST_UNIT_BINARIES)
unittests/array_test.out unittests/array_test.out
unittests/asm_test.out
unittests/buffer_test.out unittests/buffer_test.out
unittests/fiber_test.out
unittests/parse_test.out
unittests/table_test.out unittests/table_test.out
################### ###################

View File

@ -23,6 +23,7 @@
#include <setjmp.h> #include <setjmp.h>
#include <dst/dst.h> #include <dst/dst.h>
#include "opcodes.h"
/* Bytecode op argument types */ /* Bytecode op argument types */
@ -70,6 +71,7 @@ enum DstInstructionType {
DIT_SI, DIT_SI,
DIT_SU, /* Unsigned */ DIT_SU, /* Unsigned */
DIT_SSS, DIT_SSS,
DIT_SSI,
DIT_SES, DIT_SES,
DIT_SC DIT_SC
}; };
@ -79,7 +81,7 @@ typedef struct DstInstructionDef DstInstructionDef;
struct DstInstructionDef { struct DstInstructionDef {
const char *name; const char *name;
DstInstructionType type; DstInstructionType type;
uint8_t opcode; DstOpCode opcode;
}; };
/* Hold all state needed during assembly */ /* Hold all state needed during assembly */
@ -88,7 +90,7 @@ struct DstAssembler {
DstAssembler *parent; DstAssembler *parent;
DstFuncDef *def; DstFuncDef *def;
jmp_buf on_error; jmp_buf on_error;
const char *errmessage; const uint8_t *errmessage;
uint32_t environments_capacity; uint32_t environments_capacity;
uint32_t bytecode_count; /* Used for calculating labels */ 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 * time and is easier to setup statically than a hash table or
* prefix tree. */ * prefix tree. */
static const DstInstructionDef dst_ops[] = { static const DstInstructionDef dst_ops[] = {
{"add", DIT_SSS, 0x01}, {"add", DIT_SSS, DOP_ADD},
{"bitand", DIT_SSS, 0x02}, {"add-immediate", DIT_SSI, DOP_ADD_IMMEDIATE},
{"bitor", DIT_SSS, 0x03}, {"add-integer", DIT_SSS, DOP_ADD_INTEGER},
{"bitxor", DIT_SSS, 0x04}, {"add-real", DIT_SSS, DOP_ADD_REAL},
{"call", DIT_SS, 0x05}, {"bitand", DIT_SSS, DOP_BAND},
{"closure", DIT_SC, 0x06}, {"bitnot", DIT_SS, DOP_BNOT},
{"divide", DIT_SSS, 0x07}, {"bitor", DIT_SSS, DOP_BOR},
{"jump", DIT_L, 0x08}, {"bitxor", DIT_SSS, DOP_BXOR},
{"jump-if", DIT_SL, 0x09}, {"call", DIT_SS, DOP_CALL},
{"load-constant", DIT_SC, 0x0A}, {"closure", DIT_SC, DOP_CLOSURE},
{"load-false", DIT_S, 0x0B}, {"coerce-integer", DIT_SS, DOP_COERCE_INTEGER},
{"load-integer", DIT_SI, 0x0C}, {"coerce-real", DIT_SS, DOP_COERCE_REAL},
{"load-nil", DIT_S, 0x0D}, {"coerce-string", DIT_SS, DOP_COERCE_STRING},
{"load-true", DIT_S, 0x0E}, {"compare", DIT_SSS, DOP_COMPARE},
{"load-upvalue", DIT_SES, 0x0F}, {"divide", DIT_SSS, DOP_DIVIDE},
{"move", DIT_SS, 0x10}, {"divide-immediate", DIT_SSI, DOP_DIVIDE_IMMEDIATE},
{"modulo", DIT_SSS, 0x11}, {"divide-integer", DIT_SSS, DOP_DIVIDE_INTEGER},
{"multiply", DIT_SSS, 0x12}, {"divide-real", DIT_SSS, DOP_DIVIDE_REAL},
{"noop", DIT_0, 0x00}, {"equals", DIT_SSS, DOP_EQUALS},
{"push", DIT_S, 0x13}, {"error", DIT_S, DOP_ERROR},
{"push2", DIT_SS, 0x14}, {"greater-than", DIT_SSS, DOP_GREATER_THAN},
{"push3", DIT_SSS, 0x15}, {"jump", DIT_L, DOP_JUMP},
{"push-array", DIT_S, 0x16}, {"jump-if", DIT_SL, DOP_JUMP_IF},
{"return", DIT_S, 0x19}, {"load-boolean", DIT_S, DOP_LOAD_BOOLEAN},
{"return-nil", DIT_0, 0x1A}, {"load-constant", DIT_SC, DOP_LOAD_CONSTANT},
{"save-upvalue", DIT_SES, 0x1B}, {"load-integer", DIT_SI, DOP_LOAD_INTEGER},
{"shift-left", DIT_SSS, 0x1C}, {"load-nil", DIT_S, DOP_LOAD_NIL},
{"shift-right", DIT_SSS, 0x1D}, {"load-syscall", DIT_SU, DOP_LOAD_SYSCALL},
{"shift-right-signed", DIT_SSS, 0x1E}, {"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}, {"subtract", DIT_SSS, 0x1F},
{"syscall", DIT_SU, 0x21}, {"syscall", DIT_SU, DOP_SYSCALL},
{"tailcall", DIT_S, 0x22}, {"tailcall", DIT_S, DOP_TAILCALL},
{"transfer", DIT_SSS, 0x23}, {"transfer", DIT_SSS, DOP_TRANSFER},
{"typecheck", DIT_ST, 0x24}, {"typecheck", DIT_ST, DOP_TYPECHECK},
}; };
/* Compare a DST string to a native 0 terminated c string. Used in the /* 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 */ /* Throw some kind of assembly error */
static void dst_asm_error(DstAssembler *a, const char *message) { static void dst_asm_error(DstAssembler *a, const char *message) {
a->errmessage = message; a->errmessage = dst_cstring(message);
longjmp(a->on_error, 1); longjmp(a->on_error, 1);
} }
#define dst_asm_assert(a, c, m) do { if (!(c)) dst_asm_error((a), (m)); } while (0) #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 /* Parse an argument to an assembly instruction, and return the result as an
* integer. This integer will need to be trimmed and bound checked. */ * integer. This integer will need to be trimmed and bound checked. */
static int64_t doarg_1(DstAssembler *a, DstOpArgType argtype, DstValue x) { 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 - a->bytecode_count;
return result.as.integer; return result.as.integer;
} else { } 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) { } else if (argtype == DST_OAT_TYPE || argtype == DST_OAT_SIMPLETYPE) {
int index = strsearch(x.as.string, dst_type_names); int index = strsearch(x.as.string, dst_type_names);
if (index != -1) { if (index != -1) {
return (int64_t) index; return (int64_t) index;
} else { } else {
dst_asm_error(a, "unknown type"); dst_asm_errorv(a, dst_formatc("unknown type %q", x));
} }
} }
break; 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; return 0;
} }
/* Trim a bytecode operand to 1, 2, or 3 bytes. Error out if /* Parse a single argument to an instruction. Trims it as well as
* the given argument doesn't fit in the required number of bytes. */ * try to convert arguments to bit patterns */
static uint32_t doarg_2(DstAssembler *a, int nbytes, int hassign, int64_t arg) { 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 /* Calculate the min and max values that can be stored given
* nbytes, and whether or not the storage is signed */ * nbytes, and whether or not the storage is signed */
int64_t min = (-hassign) << ((nbytes << 3) - 1); int64_t min = (-hassign) << ((nbytes << 3) - 1);
int64_t max = ~((-1) << ((nbytes << 3) - hassign)); int64_t max = ~((-1) << ((nbytes << 3) - hassign));
if (arg < min) 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) if (arg > max)
dst_asm_error(a, "instruction argument is too large"); dst_asm_errorv(a, dst_formatc("instruction argument %v is too large, must be %d byte%s",
return (uint32_t) (arg & 0xFFFFFFFF); x, nbytes, nbytes > 1 ? "s" : ""));
} return ((uint32_t) (arg & 0xFFFFFFFF)) << (nth << 3);
/* 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);
} }
/* Provide parsing methods for the different kinds of arguments */ /* 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) if (dst_tuple_length(argt) != 2)
dst_asm_error(a, "expected 1 argument: (op, slot)"); 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; break;
} }
case DIT_L: case DIT_L:
{ {
if (dst_tuple_length(argt) != 2) if (dst_tuple_length(argt) != 2)
dst_asm_error(a, "expected 1 argument: (op, label)"); 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; break;
} }
case DIT_SS: case DIT_SS:
@ -328,7 +357,7 @@ static uint32_t read_instruction(DstAssembler *a, const DstInstructionDef *idef,
if (dst_tuple_length(argt) != 3) if (dst_tuple_length(argt) != 3)
dst_asm_error(a, "expected 2 arguments: (op, slot, slot)"); 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, 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; break;
} }
case DIT_SL: case DIT_SL:
@ -336,7 +365,7 @@ static uint32_t read_instruction(DstAssembler *a, const DstInstructionDef *idef,
if (dst_tuple_length(argt) != 3) if (dst_tuple_length(argt) != 3)
dst_asm_error(a, "expected 2 arguments: (op, slot, label)"); 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_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; break;
} }
case DIT_ST: case DIT_ST:
@ -344,7 +373,7 @@ static uint32_t read_instruction(DstAssembler *a, const DstInstructionDef *idef,
if (dst_tuple_length(argt) != 3) if (dst_tuple_length(argt) != 3)
dst_asm_error(a, "expected 2 arguments: (op, slot, type)"); 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_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; break;
} }
case DIT_SI: case DIT_SI:
@ -353,7 +382,7 @@ static uint32_t read_instruction(DstAssembler *a, const DstInstructionDef *idef,
if (dst_tuple_length(argt) != 3) if (dst_tuple_length(argt) != 3)
dst_asm_error(a, "expected 2 arguments: (op, slot, integer)"); dst_asm_error(a, "expected 2 arguments: (op, slot, integer)");
instr |= doarg(a, DST_OAT_SLOT, 1, 1, 0, argt[1]); instr |= doarg(a, DST_OAT_SLOT, 1, 1, 0, argt[1]);
instr |= doarg(a, DST_OAT_INTEGER, 3, 2, idef->type == DIT_SI, argt[2]); instr |= doarg(a, DST_OAT_INTEGER, 2, 2, idef->type == DIT_SI, argt[2]);
break; break;
} }
case DIT_SSS: 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]); instr |= doarg(a, DST_OAT_SLOT, 3, 1, 0, argt[3]);
break; 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: case DIT_SES:
{ {
DstAssembler *b = a; DstAssembler *b = a;
@ -372,7 +410,7 @@ static uint32_t read_instruction(DstAssembler *a, const DstInstructionDef *idef,
if (dst_tuple_length(argt) != 4) if (dst_tuple_length(argt) != 4)
dst_asm_error(a, "expected 3 arguments: (op, slot, environment, envslot)"); dst_asm_error(a, "expected 3 arguments: (op, slot, environment, envslot)");
instr |= doarg(a, DST_OAT_SLOT, 1, 1, 0, argt[1]); 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; instr |= env << 16;
for (env += 1; env > 0; env--) { for (env += 1; env > 0; env--) {
b = b->parent; b = b->parent;
@ -387,14 +425,13 @@ static uint32_t read_instruction(DstAssembler *a, const DstInstructionDef *idef,
if (dst_tuple_length(argt) != 3) if (dst_tuple_length(argt) != 3)
dst_asm_error(a, "expected 2 arguments: (op, slot, constant)"); 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_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; break;
} }
} }
return instr; return instr;
} }
/* Add a closure environment to the assembler. Sub funcdefs may need /* Add a closure environment to the assembler. Sub funcdefs may need
* to reference outer function environments, and may change the outer environment. * to reference outer function environments, and may change the outer environment.
* Returns the index of the environment in the assembler's environments, or -1 * 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; return (int64_t) oldlen;
} }
/* Helper to assembly. Return the assembled funcdef */ /* Helper to assembly. Return the assembly result */
static DstFuncDef *dst_asm1(DstAssembler *parent, DstValue src, const char **errout) { static DstAssembleResult dst_asm1(DstAssembler *parent, DstAssembleOptions opts) {
DstAssembleResult result;
DstAssembler a; DstAssembler a;
DstTable *t = src.as.table; const DstValue *st = opts.source.as.st;
DstFuncDef *def; DstFuncDef *def;
uint32_t count, i; uint32_t count, i;
const DstValue *arr; const DstValue *arr;
@ -440,9 +478,6 @@ static DstFuncDef *dst_asm1(DstAssembler *parent, DstValue src, const char **err
/* Initialize funcdef */ /* Initialize funcdef */
def = dst_alloc(DST_MEMORY_FUNCDEF, sizeof(DstFuncDef)); def = dst_alloc(DST_MEMORY_FUNCDEF, sizeof(DstFuncDef));
if (NULL == def) {
DST_OUT_OF_MEMORY;
}
def->environments = NULL; def->environments = NULL;
def->constants = NULL; def->constants = NULL;
def->bytecode = NULL; def->bytecode = NULL;
@ -451,7 +486,7 @@ static DstFuncDef *dst_asm1(DstAssembler *parent, DstValue src, const char **err
def->arity = 0; def->arity = 0;
def->constants_length = 0; def->constants_length = 0;
def->bytecode_length = 0; def->bytecode_length = 0;
def->environments_length = 0; def->environments_length = 1;
/* Initialize Assembler */ /* Initialize Assembler */
a.def = def; a.def = def;
@ -470,18 +505,19 @@ static DstFuncDef *dst_asm1(DstAssembler *parent, DstValue src, const char **err
if (NULL != a.parent) { if (NULL != a.parent) {
longjmp(a.parent->on_error, 1); longjmp(a.parent->on_error, 1);
} }
*errout = a.errmessage; result.result.error = a.errmessage;
return NULL; 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 */ /* 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; def->arity = x.type == DST_INTEGER ? x.as.integer : 0;
/* Create slot aliases */ /* 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)) { if (dst_seq_view(x, &arr, &count)) {
def->slotcount = count; def->slotcount = count;
for (i = 0; i < count; i++) { for (i = 0; i < count; i++) {
@ -507,7 +543,7 @@ static DstFuncDef *dst_asm1(DstAssembler *parent, DstValue src, const char **err
/* Create environment aliases */ /* 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)) { if (dst_seq_view(x, &arr, &count)) {
for (i = 0; i < count; i++) { for (i = 0; i < count; i++) {
dst_asm_assert(&a, arr[i].type == DST_SYMBOL, "environment must be a symbol"); 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 */ /* 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)) { if (dst_seq_view(x, &arr, &count)) {
def->constants_length = count; def->constants_length = count;
def->constants = malloc(sizeof(DstValue) * 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]; def->constants[i] = ct.as.tuple[2];
dst_table_put(&a.constants, ct.as.tuple[1], dst_wrap_integer(i)); dst_table_put(&a.constants, ct.as.tuple[1], dst_wrap_integer(i));
} else { } 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 */ /* Todo - parse nested funcdefs */
} else { } else {
@ -553,7 +589,7 @@ static DstFuncDef *dst_asm1(DstAssembler *parent, DstValue src, const char **err
} }
/* Parse bytecode and labels */ /* 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)) { if (dst_seq_view(x, &arr, &count)) {
/* Do labels and find length */ /* Do labels and find length */
uint32_t blength = 0; 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, dst_asm_assert(&a, instr.as.tuple[0].type == DST_SYMBOL,
"expected symbol in assembly instruction"); "expected symbol in assembly instruction");
idef = dst_findi(instr.as.tuple[0].as.string); 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); op = read_instruction(&a, idef, instr.as.tuple);
} }
def->bytecode[a.bytecode_count++] = op; 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); dst_asm_deinit(&a);
def->environments = def->environments =
realloc(def->environments, def->environments_length * sizeof(uint32_t)); 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. */ /* Assemble a function */
int dst_asm(DstFuncDef **out, DstValue source) { DstAssembleResult dst_asm(DstAssembleOptions opts) {
const char *err; return dst_asm1(NULL, opts);
DstFuncDef *ret = dst_asm1(NULL, source, &err); }
if (NULL == ret) {
return 1; /* Build a function from the result */
} DstFunction *dst_asm_func(DstAssembleResult result) {
*out = ret; if (result.status != DST_ASSEMBLE_OK) {
return 0; 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" #include "gc.h"
/* Initialize a new fiber */ /* 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)); DstFiber *fiber = dst_alloc(DST_MEMORY_FIBER, sizeof(DstFiber));
if (capacity < 2 * DST_FRAME_SIZE)
capacity = 2 * DST_FRAME_SIZE;
fiber->capacity = capacity; fiber->capacity = capacity;
DstValue *data = malloc(sizeof(DstValue) * capacity); if (capacity) {
if (NULL == data) { DstValue *data = malloc(sizeof(DstValue) * capacity);
DST_OUT_OF_MEMORY; if (NULL == data) {
DST_OUT_OF_MEMORY;
}
fiber->data = data;
} else {
fiber->data = NULL;
} }
fiber->data = data; return dst_fiber_reset(fiber);
return dst_fiber_reset(fiber, func);
} }
/* Clear a fiber (reset it) */ /* Clear a fiber (reset it) */
DstFiber *dst_fiber_reset(DstFiber *fiber, DstFunction *func) { DstFiber *dst_fiber_reset(DstFiber *fiber) {
uint32_t i; fiber->frame = 0;
DstStackFrame *frame; fiber->frametop = 0;
fiber->stacktop = DST_FRAME_SIZE;
if (NULL == func) { fiber->status = DST_FIBER_DEAD;
/* 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;
return fiber; 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. */ * If there is nothing to pop of of the stack, return nil. */
DstValue dst_fiber_popvalue(DstFiber *fiber) { DstValue dst_fiber_popvalue(DstFiber *fiber) {
uint32_t newstacktop = fiber->stacktop - 1; uint32_t newstacktop = fiber->stacktop - 1;
if (newstacktop <= fiber->frametop + DST_FRAME_SIZE) { if (newstacktop < fiber->frametop + DST_FRAME_SIZE) {
return dst_wrap_nil(); return dst_wrap_nil();
} }
fiber->stacktop = newstacktop; fiber->stacktop = newstacktop;
@ -263,6 +233,10 @@ void dst_fiber_cframe_tail(DstFiber *fiber) {
uint32_t nextframetop = fiber->frame + size;; uint32_t nextframetop = fiber->frame + size;;
uint32_t nextstacktop = nextframetop + DST_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 *stack = fiber->data + fiber->frame;
DstValue *args = fiber->data + fiber->frametop + DST_FRAME_SIZE; 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. /* Pin a value. This prevents a value from being garbage collected.
* Needed if the valueis not accesible to the garbage collector, but * Needed if the valueis not accesible to the garbage collector, but
* still in use by the program. For example, a c function that * 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) { static void dst_mark_string(const uint8_t *str) {
dst_gc_mark(dst_string_raw(str)); dst_gc_mark(dst_string_raw(str));
} }
@ -188,9 +188,10 @@ static void dst_mark_function(DstFunction *func) {
return; return;
dst_gc_mark(func); dst_gc_mark(func);
numenvs = func->def->environments_length; numenvs = func->def->environments_length;
for (i = 0; i < numenvs; ++i) if (NULL != func->envs)
if (NULL != func->envs[i]) for (i = 0; i < numenvs; ++i)
dst_mark_funcenv(func->envs[i]); if (NULL != func->envs[i])
dst_mark_funcenv(func->envs[i]);
dst_mark_funcdef(func->def); 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 */ /* Quote a value */
static DstValue quote(DstValue x) { static DstValue quote(DstValue x) {
DstValue sym = dst_wrap_symbol(dst_cstring("quote"));
DstValue *t = dst_tuple_begin(2); DstValue *t = dst_tuple_begin(2);
t[0] = sym; t[0] = dst_cstrings("quote");
t[1] = x; t[1] = x;
return dst_wrap_tuple(dst_tuple_end(t)); 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) { static int to_hex(uint8_t c) {
if (c >= '0' && c <= '9') { if (c >= '0' && c <= '9') {
return c - '0'; return c - '0';
} else if (c >= 'a' && c <= 'f') {
return 10 + c - 'a';
} else if (c >= 'A' && c <= 'F') { } else if (c >= 'A' && c <= 'F') {
return 10 + c - 'A'; return 10 + c - 'A';
} else if (c >= 'a' && c <= 'f') {
return 10 + c - 'a';
} else { } else {
return -1; return -1;
} }
} }
typedef struct { typedef struct {
DstFiber *fiber; DstArray stack;
const uint8_t *end; const uint8_t *end;
const char *errmsg; const char *errmsg;
enum { DstParseStatus status;
DST_PARSE_OK,
DST_PARSE_ERROR,
DST_PARSE_UNEPECTED_EOS
} status;
} ParseArgs; } ParseArgs;
/* Entry point of the recursive descent parser */ /* Entry point of the recursive descent parser */
@ -186,6 +181,9 @@ static const uint8_t *parse_recur(
/* Prevent stack overflow */ /* Prevent stack overflow */
if (recur == 0) goto too_much_recur; if (recur == 0) goto too_much_recur;
/* try parsing again */
begin:
/* Trim leading whitespace and count quotes */ /* Trim leading whitespace and count quotes */
while (src < end && (is_whitespace(*src) || *src == '\'')) { while (src < end && (is_whitespace(*src) || *src == '\'')) {
if (*src == '\'') { if (*src == '\'') {
@ -201,7 +199,9 @@ static const uint8_t *parse_recur(
switch (*src) { switch (*src) {
/* Numbers, symbols, simple literals */ /* Numbers, symbols, simple literals */
default: { default:
atom:
{
double real; double real;
int64_t integer; int64_t integer;
const uint8_t *tokenend = src; const uint8_t *tokenend = src;
@ -230,7 +230,17 @@ static const uint8_t *parse_recur(
break; 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; const uint8_t *tokenend = ++src;
while (tokenend < end && is_symbol_char(*tokenend)) while (tokenend < end && is_symbol_char(*tokenend))
tokenend++; tokenend++;
@ -241,9 +251,10 @@ static const uint8_t *parse_recur(
} }
/* String literals */ /* String literals */
case '"': { case '"':
{
const uint8_t *strend = ++src; const uint8_t *strend = ++src;
const uint8_t *strstart = src; const uint8_t *strstart = strend;
uint32_t len = 0; uint32_t len = 0;
int containsEscape = 0; int containsEscape = 0;
/* Preprocess string to check for escapes and string end */ /* Preprocess string to check for escapes and string end */
@ -253,10 +264,11 @@ static const uint8_t *parse_recur(
containsEscape = 1; containsEscape = 1;
if (strend >= end) goto unexpected_eos; if (strend >= end) goto unexpected_eos;
if (*strend == 'h') { if (*strend == 'h') {
strend += 2; strend += 3;
if (strend >= end) goto unexpected_eos; if (strend >= end) goto unexpected_eos;
} else { } else {
strend++; strend++;
if (strend >= end) goto unexpected_eos;
} }
} }
} }
@ -266,7 +278,7 @@ static const uint8_t *parse_recur(
while (src < strend) { while (src < strend) {
if (*src == '\\') { if (*src == '\\') {
src++; src++;
switch (*++src) { switch (*src++) {
case 'n': *write++ = '\n'; break; case 'n': *write++ = '\n'; break;
case 'r': *write++ = '\r'; break; case 'r': *write++ = '\r'; break;
case 't': *write++ = '\t'; break; case 't': *write++ = '\t'; break;
@ -293,22 +305,31 @@ static const uint8_t *parse_recur(
ret = dst_wrap_string(dst_string_end(buf)); ret = dst_wrap_string(dst_string_end(buf));
} else { } else {
ret = dst_wrap_string(dst_string(strstart, strend - strstart)); ret = dst_wrap_string(dst_string(strstart, strend - strstart));
src = strend + 1;
} }
src = strend + 1;
break; break;
} }
/* Data Structure literals */ /* Data Structure literals */
case '@':
if (src[1] != '{')
goto atom;
case '(': case '(':
case '[': case '[':
case '{': { case '{':
{
uint32_t n = 0, i = 0; uint32_t n = 0, i = 0;
uint32_t istable = 0;
uint8_t close; uint8_t close;
switch (*src++) { switch (*src++) {
case '[': close = ']'; break; case '[': close = ']'; break;
case '{': close = '}'; break; case '{': close = '}'; break;
case '@': close = '}'; src++; istable = 1; break;
default: close = ')'; break; default: close = ')'; break;
} }
/* Trim trailing whitespace */
while (src < end && (is_whitespace(*src)))
++src;
/* Recursively parse inside literal */ /* Recursively parse inside literal */
while (*src != close) { while (*src != close) {
src = parse_recur(args, src, recur - 1); src = parse_recur(args, src, recur - 1);
@ -321,24 +342,42 @@ static const uint8_t *parse_recur(
src++; src++;
switch (close) { switch (close) {
case ')': case ')':
case ']':
{ {
DstValue *tup = dst_tuple_begin(n); DstValue *tup = dst_tuple_begin(n);
for (i = n; i > 0; i--) 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)); ret = dst_wrap_tuple(dst_tuple_end(tup));
break; 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 '}': case '}':
{ {
if (n & 1) goto struct_oddargs; if (n & 1) goto struct_oddargs;
DstValue *st = dst_struct_begin(n >> 1); if (istable) {
for (i = n; i > 0; i -= 2) { DstTable *t = dst_table(n);
DstValue val = dst_fiber_popvalue(args->fiber); for (i = n; i > 0; i -= 2) {
DstValue key = dst_fiber_popvalue(args->fiber); DstValue val = dst_array_pop(&args->stack);
dst_struct_put(st, key, val); 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; break;
} }
} }
@ -350,7 +389,7 @@ static const uint8_t *parse_recur(
while (qcount--) ret = quote(ret); while (qcount--) ret = quote(ret);
/* Push the result to the stack */ /* 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 the new source position for further calls */
return src; return src;
@ -394,42 +433,37 @@ static const uint8_t *parse_recur(
} }
/* Parse an array of bytes. Return value in the fiber return value. */ /* 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; ParseArgs args;
DstFiber *fiber = dst_vm_fiber; const uint8_t *newsrc;
/* Save original stack top */ dst_array_init(&args.stack, 10);
uint32_t oldstacktop = fiber->stacktop;
args.fiber = fiber;
args.status = DST_PARSE_OK; args.status = DST_PARSE_OK;
args.end = src + len; args.end = src + len;
args.errmsg = NULL; args.errmsg = NULL;
src = parse_recur(&args, src, DST_RECURSION_GUARD); newsrc = parse_recur(&args, src, DST_RECURSION_GUARD);
if (NULL != newsrc) *newsrc = src; res.status = args.status;
res.bytes_read = (uint32_t) (newsrc - src);
/* TODO - source maps */
res.map = dst_wrap_nil();
if (args.errmsg) { if (args.errmsg) {
fiber->ret = dst_wrap_string(dst_cstring(args.errmsg)); res.result.error = dst_cstring(args.errmsg);
} else { } else {
fiber->ret = dst_fiber_popvalue(fiber); res.result.value = dst_array_pop(&args.stack);
} }
/* Reset stacktop */ dst_array_deinit(&args.stack);
fiber->stacktop = oldstacktop;
return args.status; return res;
} }
/* Parse a c string */ /* Parse a c string */
int dst_parsec(const char *src, const char **newsrc) { DstParseResult dst_parsec(const char *src) {
uint32_t len = 0; uint32_t len = 0;
const uint8_t *ns = NULL;
int status;
while (src[len]) ++len; while (src[len]) ++len;
status = dst_parse((const uint8_t *)src, len, &ns); return dst_parse((const uint8_t *)src, len);
if (newsrc) {
*newsrc = (const char *)ns;
}
return status;
} }

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 debug print helper */
static int64_t dst_description_helper(DstBuffer *b, DstTable *seen, DstValue x, int64_t next, int depth) { static void dst_description_helper(DstPrinter *p, DstValue x) {
DstValue check = dst_table_get(seen, x); p->depth--;
DstValue check = dst_table_get(&p->seen, x);
if (check.type == DST_INTEGER) { 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 { } else {
const char *open; const char *open;
const char *close; const char *close;
uint32_t len, i; uint32_t len, cap;
const DstValue *data; const DstValue *data;
switch (x.type) { switch (x.type) {
default: default:
dst_short_description_b(b, x); if (p->flags & DST_PRINTFLAG_COLORIZE) {
return next; 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: case DST_STRUCT:
open = "{"; close = "}"; open = "{"; close = "}";
break; break;
case DST_TABLE: case DST_TABLE:
open = "{"; close = "}"; open = "@{"; close = "}";
break; break;
case DST_TUPLE: case DST_TUPLE:
open = "("; close = ")"; open = "("; close = ")";
@ -386,44 +537,50 @@ static int64_t dst_description_helper(DstBuffer *b, DstTable *seen, DstValue x,
open = "["; close = "]"; open = "["; close = "]";
break; break;
} }
dst_table_put(seen, x, dst_wrap_integer(next++)); dst_table_put(&p->seen, x, dst_wrap_integer(p->next++));
dst_buffer_push_cstring(b, open); dst_buffer_push_cstring(&p->buffer, open);
if (depth == 0) { if (p->depth == 0) {
dst_buffer_push_cstring(b, "..."); dst_buffer_push_cstring(&p->buffer, "...");
} else if (dst_hashtable_view(x, &data, &len)) { } else if (dst_hashtable_view(x, &data, &len, &cap)) {
int isfirst = 1; dst_print_hashtable_inner(p, data, len, cap);
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);
}
}
} else if (dst_seq_view(x, &data, &len)) { } else if (dst_seq_view(x, &data, &len)) {
for (i = 0; i < len; ++i) { dst_print_seq_inner(p, data, len);
next = dst_description_helper(b, seen, data[i], next, depth - 1);
if (i != len - 1)
dst_buffer_push_u8(b, ' ');
}
} }
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. */ /* Debug print. Returns a description of an object as a string. */
const uint8_t *dst_description(DstValue x) { const uint8_t *dst_description(DstValue x) {
DstBuffer *buf = dst_buffer(10); DstPrinter printer;
DstTable *seen = dst_table(10); 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 */ /* 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 /* 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) { const uint8_t *dst_to_string(DstValue x) {
switch (x.type) { switch (x.type) {
default: default:
return dst_description(x); return dst_short_description(x);
case DST_STRING: case DST_STRING:
case DST_SYMBOL: case DST_SYMBOL:
return x.as.string; 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 */ /* Find an item in a struct */
static const DstValue *dst_struct_find(const DstValue *st, DstValue key) { static const DstValue *dst_struct_find(const DstValue *st, DstValue key) {
uint32_t cap = dst_struct_capacity(st); 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; uint32_t i;
for (i = index; i < cap; i += 2) for (i = index; i < cap; i += 2)
if (st[i].type == DST_NIL || dst_equals(st[i], key)) 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) { void dst_struct_put(DstValue *st, DstValue key, DstValue value) {
uint32_t cap = dst_struct_capacity(st); uint32_t cap = dst_struct_capacity(st);
uint32_t hash = dst_hash(key); 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 i, j, dist;
uint32_t bounds[4] = {index, cap, 0, index}; uint32_t bounds[4] = {index, cap, 0, index};
if (key.type == DST_NIL || value.type == DST_NIL) return; 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 * 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}. */ * will compare properly - i.e., {1 2 3 4} should equal {3 4 1 2}. */
otherhash = dst_hash(st[i]); otherhash = dst_hash(st[i]);
otherindex = (otherhash % (cap / 2)) * 2; otherindex = (otherhash % cap) & (~1);
otherdist = (i + cap - otherindex) % cap; otherdist = (i + cap - otherindex) % cap;
if (dist < otherdist) if (dist < otherdist)
status = -1; status = -1;
@ -113,7 +113,7 @@ void dst_struct_put(DstValue *st, DstValue key, DstValue value) {
/* Save dist and hash of new kv pair */ /* Save dist and hash of new kv pair */
dist = otherdist; dist = otherdist;
hash = otherhash; hash = otherhash;
} else { } else if (status == 0) {
/* This should not happen - it means /* This should not happen - it means
* than a key was added to the struct more than once */ * than a key was added to the struct more than once */
return; return;
@ -178,3 +178,15 @@ DstValue dst_struct_next(const DstValue *st, DstValue key) {
} }
return dst_wrap_nil(); 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 <dst/dst.h>
#include <stdio.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; uint32_t i;
for (i = 0; i < argn; ++i) { for (i = 0; i < argn; ++i) {
uint32_t j, len; uint32_t j, len;

View File

@ -170,3 +170,15 @@ DstValue dst_table_next(DstTable *t, DstValue key) {
} }
return dst_wrap_nil(); 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 /* Read both structs and tables as the entries of a hashtable with
* identical structure. Returns 1 if the view can be constructed and * identical structure. Returns 1 if the view can be constructed and
* 0 if the type is invalid. */ * 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) { if (tab.type == DST_TABLE) {
*data = tab.as.table->data; *data = tab.as.table->data;
*cap = tab.as.table->capacity; *cap = tab.as.table->capacity;
*len = tab.as.table->count;
return 1; return 1;
} else if (tab.type == DST_STRUCT) { } else if (tab.type == DST_STRUCT) {
*data = tab.as.st; *data = tab.as.st;
*cap = dst_struct_capacity(tab.as.st); *cap = dst_struct_capacity(tab.as.st);
*len = dst_struct_length(tab.as.st);
return 1; return 1;
} }
return 0; return 0;

151
core/vm.c
View File

@ -27,7 +27,7 @@
DstFiber *dst_vm_fiber; DstFiber *dst_vm_fiber;
/* Start running the VM from where it left off. */ /* Start running the VM from where it left off. */
int dst_continue(DstFiber *fiber) { int dst_continue() {
/* VM state */ /* VM state */
DstValue *stack; DstValue *stack;
@ -36,9 +36,9 @@ int dst_continue(DstFiber *fiber) {
/* Used to extract bits from the opcode that correspond to arguments. /* Used to extract bits from the opcode that correspond to arguments.
* Pulls out unsigned integers */ * 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_assert(cond, e) do {if (!(cond)) vm_throw((e)); } while (0)
#define vm_binop_integer(op) \ #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(op1.type == DST_INTEGER || op1.type == DST_REAL, "expected number");\
vm_assert(op2.type == DST_INTEGER || op2.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\ 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_integer(op1.as.integer op op2.as.integer)\
: dst_wrap_real(dst_integer_to_real(op1.as.integer) op op2.as.real)\ : dst_wrap_real(dst_integer_to_real(op1.as.integer) op op2.as.real))\
: op2.type == DST_INTEGER\ : (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 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++;\ pc++;\
continue;\ continue;\
} }
#define vm_init_fiber_state() \ #define vm_init_fiber_state() \
fiber->status = DST_FIBER_ALIVE;\ dst_vm_fiber->status = DST_FIBER_ALIVE;\
stack = fiber->data + fiber->frame;\ stack = dst_vm_fiber->data + dst_vm_fiber->frame;\
pc = dst_stack_frame(stack)->pc;\ pc = dst_stack_frame(stack)->pc;\
func = dst_stack_frame(stack)->func; func = dst_stack_frame(stack)->func;
@ -105,7 +105,7 @@ int dst_continue(DstFiber *fiber) {
continue; continue;
case DOP_ERROR: case DOP_ERROR:
fiber->ret = stack[oparg(1, 0xFF)]; dst_vm_fiber->ret = stack[oparg(1, 0xFF)];
goto vm_error; goto vm_error;
case DOP_TYPECHECK: case DOP_TYPECHECK:
@ -115,11 +115,11 @@ int dst_continue(DstFiber *fiber) {
continue; continue;
case DOP_RETURN: case DOP_RETURN:
fiber->ret = stack[oparg(1, 0xFFFFFF)]; dst_vm_fiber->ret = stack[oparg(1, 0xFFFFFF)];
goto vm_return; goto vm_return;
case DOP_RETURN_NIL: case DOP_RETURN_NIL:
fiber->ret.type = DST_NIL; dst_vm_fiber->ret.type = DST_NIL;
goto vm_return; goto vm_return;
case DOP_COERCE_INTEGER: case DOP_COERCE_INTEGER:
@ -394,8 +394,8 @@ int dst_continue(DstFiber *fiber) {
if (fd->flags & DST_FUNCDEF_FLAG_NEEDSENV) { if (fd->flags & DST_FUNCDEF_FLAG_NEEDSENV) {
/* Delayed capture of current stack frame */ /* Delayed capture of current stack frame */
DstFuncEnv *env = dst_alloc(DST_MEMORY_FUNCENV, sizeof(DstFuncEnv)); DstFuncEnv *env = dst_alloc(DST_MEMORY_FUNCENV, sizeof(DstFuncEnv));
env->offset = fiber->frame; env->offset = dst_vm_fiber->frame;
env->as.fiber = fiber; env->as.fiber = dst_vm_fiber;
env->length = func->def->slotcount; env->length = func->def->slotcount;
fn->envs[0] = env; fn->envs[0] = env;
} else { } else {
@ -411,19 +411,19 @@ int dst_continue(DstFiber *fiber) {
} }
case DOP_PUSH: case DOP_PUSH:
dst_fiber_push(fiber, stack[oparg(1, 0xFFFFFF)]); dst_fiber_push(dst_vm_fiber, stack[oparg(1, 0xFFFFFF)]);
pc++; pc++;
break; break;
case DOP_PUSH_2: case DOP_PUSH_2:
dst_fiber_push2(fiber, dst_fiber_push2(dst_vm_fiber,
stack[oparg(1, 0xFF)], stack[oparg(1, 0xFF)],
stack[oparg(2, 0xFFFF)]); stack[oparg(2, 0xFFFF)]);
pc++; pc++;
break;; break;;
case DOP_PUSH_3: case DOP_PUSH_3:
dst_fiber_push3(fiber, dst_fiber_push3(dst_vm_fiber,
stack[oparg(1, 0xFF)], stack[oparg(1, 0xFF)],
stack[oparg(2, 0xFF)], stack[oparg(2, 0xFF)],
stack[oparg(3, 0xFF)]); stack[oparg(3, 0xFF)]);
@ -435,7 +435,7 @@ int dst_continue(DstFiber *fiber) {
uint32_t count; uint32_t count;
const DstValue *array; const DstValue *array;
if (dst_seq_view(stack[oparg(1, 0xFFFFFF)], &array, &count)) { 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 { } else {
vm_throw("expected array or tuple"); vm_throw("expected array or tuple");
} }
@ -448,19 +448,19 @@ int dst_continue(DstFiber *fiber) {
DstValue callee = stack[oparg(2, 0xFFFF)]; DstValue callee = stack[oparg(2, 0xFFFF)];
if (callee.type == DST_FUNCTION) { if (callee.type == DST_FUNCTION) {
func = callee.as.function; func = callee.as.function;
dst_fiber_funcframe(fiber, func); dst_fiber_funcframe(dst_vm_fiber, func);
stack = fiber->data + fiber->frame; stack = dst_vm_fiber->data + dst_vm_fiber->frame;
pc = func->def->bytecode; pc = func->def->bytecode;
break; break;
} else if (callee.type == DST_CFUNCTION) { } else if (callee.type == DST_CFUNCTION) {
dst_fiber_cframe(fiber); dst_fiber_cframe(dst_vm_fiber);
stack = fiber->data + fiber->frame; stack = dst_vm_fiber->data + dst_vm_fiber->frame;
fiber->ret.type = DST_NIL; dst_vm_fiber->ret.type = DST_NIL;
if (callee.as.cfunction(fiber, stack, fiber->frametop - fiber->frame)) { if (callee.as.cfunction(stack, dst_vm_fiber->frametop - dst_vm_fiber->frame)) {
dst_fiber_popframe(fiber); dst_fiber_popframe(dst_vm_fiber);
goto vm_error; goto vm_error;
} else { } else {
dst_fiber_popframe(fiber); dst_fiber_popframe(dst_vm_fiber);
goto vm_return; goto vm_return;
} }
} else { } else {
@ -474,19 +474,19 @@ int dst_continue(DstFiber *fiber) {
DstValue callee = stack[oparg(2, 0xFFFF)]; DstValue callee = stack[oparg(2, 0xFFFF)];
if (callee.type == DST_FUNCTION) { if (callee.type == DST_FUNCTION) {
func = callee.as.function; func = callee.as.function;
dst_fiber_funcframe_tail(fiber, func); dst_fiber_funcframe_tail(dst_vm_fiber, func);
stack = fiber->data + fiber->frame; stack = dst_vm_fiber->data + dst_vm_fiber->frame;
pc = func->def->bytecode; pc = func->def->bytecode;
break; break;
} else if (callee.type == DST_CFUNCTION) { } else if (callee.type == DST_CFUNCTION) {
dst_fiber_cframe_tail(fiber); dst_fiber_cframe_tail(dst_vm_fiber);
stack = fiber->data + fiber->frame; stack = dst_vm_fiber->data + dst_vm_fiber->frame;
fiber->ret.type = DST_NIL; dst_vm_fiber->ret.type = DST_NIL;
if (callee.as.cfunction(fiber, stack, fiber->frametop - fiber->frame)) { if (callee.as.cfunction(stack, dst_vm_fiber->frametop - dst_vm_fiber->frame)) {
dst_fiber_popframe(fiber); dst_fiber_popframe(dst_vm_fiber);
goto vm_error; goto vm_error;
} else { } else {
dst_fiber_popframe(fiber); dst_fiber_popframe(dst_vm_fiber);
goto vm_return; goto vm_return;
} }
} else { } else {
@ -499,14 +499,14 @@ int dst_continue(DstFiber *fiber) {
{ {
DstCFunction f = dst_vm_syscalls[oparg(2, 0xFF)]; DstCFunction f = dst_vm_syscalls[oparg(2, 0xFF)];
vm_assert(NULL != f, "invalid syscall"); vm_assert(NULL != f, "invalid syscall");
dst_fiber_cframe(fiber); dst_fiber_cframe(dst_vm_fiber);
stack = fiber->data + fiber->frame; stack = dst_vm_fiber->data + dst_vm_fiber->frame;
fiber->ret.type = DST_NIL; dst_vm_fiber->ret.type = DST_NIL;
if (f(fiber, stack, fiber->frametop - fiber->frame)) { if (f(stack, dst_vm_fiber->frametop - dst_vm_fiber->frame)) {
dst_fiber_popframe(fiber); dst_fiber_popframe(dst_vm_fiber);
goto vm_error; goto vm_error;
} else { } else {
dst_fiber_popframe(fiber); dst_fiber_popframe(dst_vm_fiber);
goto vm_return; goto vm_return;
} }
continue; continue;
@ -531,17 +531,17 @@ int dst_continue(DstFiber *fiber) {
temp.type == DST_NIL, "expected fiber"); temp.type == DST_NIL, "expected fiber");
nextfiber = temp.type == DST_FIBER nextfiber = temp.type == DST_FIBER
? temp.as.fiber ? temp.as.fiber
: fiber->parent; : dst_vm_fiber->parent;
/* Check for root fiber */ /* Check for root fiber */
if (NULL == nextfiber) { if (NULL == nextfiber) {
frame->pc = pc; frame->pc = pc;
fiber->ret = retvalue; dst_vm_fiber->ret = retvalue;
return 0; return 0;
} }
vm_assert(nextfiber->status == DST_FIBER_PENDING, "can only transfer to pending fiber"); vm_assert(nextfiber->status == DST_FIBER_PENDING, "can only transfer to pending fiber");
frame->pc = pc; frame->pc = pc;
fiber->status = DST_FIBER_PENDING; dst_vm_fiber->status = DST_FIBER_PENDING;
fiber = nextfiber; dst_vm_fiber = nextfiber;
vm_init_fiber_state(); vm_init_fiber_state();
stack[oparg(1, 0xFF)] = retvalue; stack[oparg(1, 0xFF)] = retvalue;
pc++; pc++;
@ -551,26 +551,26 @@ int dst_continue(DstFiber *fiber) {
/* Handle returning from stack frame. Expect return value in fiber->ret */ /* Handle returning from stack frame. Expect return value in fiber->ret */
vm_return: vm_return:
{ {
DstValue ret = fiber->ret; DstValue ret = dst_vm_fiber->ret;
dst_fiber_popframe(fiber); dst_fiber_popframe(dst_vm_fiber);
while (fiber->frame || while (!dst_vm_fiber->frame ||
fiber->status == DST_FIBER_DEAD || dst_vm_fiber->status == DST_FIBER_DEAD ||
fiber->status == DST_FIBER_ERROR) { dst_vm_fiber->status == DST_FIBER_ERROR) {
fiber->status = DST_FIBER_DEAD; dst_vm_fiber->status = DST_FIBER_DEAD;
if (fiber->parent) { if (NULL != dst_vm_fiber->parent) {
fiber = fiber->parent; dst_vm_fiber = dst_vm_fiber->parent;
if (fiber->status == DST_FIBER_ALIVE) { if (dst_vm_fiber->status == DST_FIBER_ALIVE) {
/* If the parent thread is still alive, /* If the parent thread is still alive,
we are inside a cfunction */ we are inside a cfunction */
return 0; return 0;
} }
stack = fiber->data + fiber->frame; stack = dst_vm_fiber->data + dst_vm_fiber->frame;
} else { } else {
return 0; return 0;
} }
} }
fiber->status = DST_FIBER_ALIVE; dst_vm_fiber->status = DST_FIBER_ALIVE;
stack = fiber->data + fiber->frame; stack = dst_vm_fiber->data + dst_vm_fiber->frame;
pc = dst_stack_frame(stack)->pc; pc = dst_stack_frame(stack)->pc;
stack[oparg(1, 0xFF)] = ret; stack[oparg(1, 0xFF)] = ret;
pc++; pc++;
@ -580,22 +580,22 @@ int dst_continue(DstFiber *fiber) {
/* Handle errors from c functions and vm opcodes */ /* Handle errors from c functions and vm opcodes */
vm_error: vm_error:
{ {
DstValue ret = fiber->ret; DstValue ret = dst_vm_fiber->ret;
fiber->status = DST_FIBER_ERROR; dst_vm_fiber->status = DST_FIBER_ERROR;
while (fiber->frame || while (!dst_vm_fiber->frame ||
fiber->status == DST_FIBER_DEAD || dst_vm_fiber->status == DST_FIBER_DEAD ||
fiber->status == DST_FIBER_ERROR) { dst_vm_fiber->status == DST_FIBER_ERROR) {
if (fiber->parent == NULL) if (dst_vm_fiber->parent == NULL)
return 1; return 1;
fiber = fiber->parent; dst_vm_fiber = dst_vm_fiber->parent;
if (fiber->status == DST_FIBER_ALIVE) { if (dst_vm_fiber->status == DST_FIBER_ALIVE) {
/* If the parent thread is still alive, /* If the parent thread is still alive,
we are inside a cfunction */ we are inside a cfunction */
return 1; return 1;
} }
} }
fiber->status = DST_FIBER_ALIVE; dst_vm_fiber->status = DST_FIBER_ALIVE;
stack = fiber->data + fiber->frame; stack = dst_vm_fiber->data + dst_vm_fiber->frame;
pc = dst_stack_frame(stack)->pc; pc = dst_stack_frame(stack)->pc;
stack[oparg(1, 0xFF)] = ret; stack[oparg(1, 0xFF)] = ret;
pc++; pc++;
@ -626,20 +626,21 @@ int dst_continue(DstFiber *fiber) {
/* Run the vm with a given function. This function is /* Run the vm with a given function. This function is
* called to start the vm. */ * called to start the vm. */
int dst_run(DstValue callee) { 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) { if (callee.type == DST_CFUNCTION) {
dst_vm_fiber = dst_fiber(NULL, 0);
dst_vm_fiber->ret.type = DST_NIL; dst_vm_fiber->ret.type = DST_NIL;
dst_fiber_cframe(dst_vm_fiber); 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) { } else if (callee.type == DST_FUNCTION) {
dst_vm_fiber = dst_fiber(callee.as.function, 64); dst_fiber_funcframe(dst_vm_fiber, callee.as.function);
result = dst_continue(dst_vm_fiber); return dst_continue();
} else {
dst_vm_fiber->ret = dst_wrap_string(dst_cstring("expected function"));
result = 1;
} }
return result; dst_vm_fiber->ret = dst_wrap_string(dst_cstring("expected function"));
return 1;
} }
/* Setup functions */ /* 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) # in the same markup as dst itself (extended S expressions)
{ {
arity 3 arity 3
signature (integer integer integer integer)
source "source file path" source "source file path"
varargs false varargs false
# Name for reference by nested funcdefs # Name for reference by nested funcdefs
@ -12,31 +11,32 @@
# Contains the bytecode for this function. This can be assembly # Contains the bytecode for this function. This can be assembly
# instructions or integers. Assembly will be converted to integer bytecodes immediately. # instructions or integers. Assembly will be converted to integer bytecodes immediately.
bytecode [ bytecode [
(typecheck 0 integer) (load-constant 0 bork)
(typecheck 1 integer) (load-constant 1 bip)
(typecheck 2 integer) (add 0 0 1)
:checked-entry (add-immediate 0 0 127)
(load-constant 3 bork) (push3 0 0 0)
(load-constant 4 bip) (push3 0 0 0)
(add-integer-unchecked 5 0 1) (push3 0 0 0)
(add-integer-unchecked 5 5 2) (syscall 1 0)
(add-integer-unchecked 5 5 3) (return 0)
(add-integer-unchecked 5 5 4)
(return 5)
] ]
# A source map is optional. The sourcemap corresponds with the byte code. # 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. # 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. # This format may change.
source-map [ # map [
0x0000123400000123 # 1
0x0000123400000123 # 2
0x0000123400000125 # 3
0x0000123400000134 # 10
0x0000123400000134 # 15
0x0000123400000135 # 39
... # 90
] # 134
# ...
# ]
#
# The number of slots available for the function. # The number of slots available for the function.
# Slots can be named as well for convenience. # Slots can be named as well for convenience.
slots [ slots [
@ -45,24 +45,25 @@
z z
] ]
# Captured outer environments that are referenced # Captured outer environments that are referenced
environments [ environments [ ]
outer1
outer2
]
# Constants are an array or tuple. For named constants, use a tuple that begins with let # 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 # For a literal tuple, use (quote tuple), or 'tuple. Without names, constants must be indexed
# from their number # from their number
# Literal FuncEnvs and Functions may be possible later
constants [ constants [
"hello" "hello"
(let bork 123) (def bork 123)
(let bip 456) (def bip 456)
'(1 2 3) '(1 2 3)
(let atuple (1 2 3)) (def atuple (1 2 3))
(567) (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); assert(table->count == 2);
dst_table_remove(table, dst_cstringv("a")); dst_table_remove(table, dst_cstringv("a"));
assert(table->count == 1); assert(table->count == 1);
assert(dst_equals(
dst_table_get(table, dst_cstringv("b")),
dst_cstringv("a")
));
return 0; return 0;
} }