mirror of
https://github.com/janet-lang/janet
synced 2024-11-28 11:09:54 +00:00
Getting more work done on assembler, parer, and unit tests.
This commit is contained in:
parent
9a858d5a97
commit
6ca6949c2d
6
Makefile
6
Makefile
@ -78,14 +78,18 @@ $(DST_TARGET): $(DST_CORE_OBJECTS)
|
||||
CCU_FLAGS = $(CFLAGS) -DDST_UNIT_TEST
|
||||
|
||||
DST_UNIT_BINARIES=$(addprefix unittests/,\
|
||||
array_test.out buffer_test.out table_test.out)
|
||||
asm_test.out array_test.out buffer_test.out fiber_test.out parse_test.out \
|
||||
table_test.out)
|
||||
|
||||
%.out: %.c $(DST_CORE_OBJECTS) $(DST_ALL_HEADERS) unittests/unit.h
|
||||
$(CC) $(CCU_FLAGS) $(DST_CORE_OBJECTS) $< -o $@
|
||||
|
||||
unit: $(DST_UNIT_BINARIES)
|
||||
unittests/array_test.out
|
||||
unittests/asm_test.out
|
||||
unittests/buffer_test.out
|
||||
unittests/fiber_test.out
|
||||
unittests/parse_test.out
|
||||
unittests/table_test.out
|
||||
|
||||
###################
|
||||
|
220
core/asm.c
220
core/asm.c
@ -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;
|
||||
}
|
||||
|
66
core/fiber.c
66
core/fiber.c
@ -24,57 +24,27 @@
|
||||
#include "gc.h"
|
||||
|
||||
/* Initialize a new fiber */
|
||||
DstFiber *dst_fiber(DstFunction *func, uint32_t capacity) {
|
||||
DstFiber *dst_fiber(uint32_t capacity) {
|
||||
DstFiber *fiber = dst_alloc(DST_MEMORY_FIBER, sizeof(DstFiber));
|
||||
if (capacity < 2 * DST_FRAME_SIZE)
|
||||
capacity = 2 * DST_FRAME_SIZE;
|
||||
fiber->capacity = capacity;
|
||||
DstValue *data = malloc(sizeof(DstValue) * capacity);
|
||||
if (NULL == data) {
|
||||
DST_OUT_OF_MEMORY;
|
||||
if (capacity) {
|
||||
DstValue *data = malloc(sizeof(DstValue) * capacity);
|
||||
if (NULL == data) {
|
||||
DST_OUT_OF_MEMORY;
|
||||
}
|
||||
fiber->data = data;
|
||||
} else {
|
||||
fiber->data = NULL;
|
||||
}
|
||||
fiber->data = data;
|
||||
return dst_fiber_reset(fiber, func);
|
||||
return dst_fiber_reset(fiber);
|
||||
}
|
||||
|
||||
/* Clear a fiber (reset it) */
|
||||
DstFiber *dst_fiber_reset(DstFiber *fiber, DstFunction *func) {
|
||||
uint32_t i;
|
||||
DstStackFrame *frame;
|
||||
|
||||
if (NULL == func) {
|
||||
/* Cfunction */
|
||||
fiber->frame = DST_FRAME_SIZE;
|
||||
fiber->frametop = DST_FRAME_SIZE;
|
||||
fiber->stacktop = fiber->frametop + DST_FRAME_SIZE;
|
||||
fiber->status = DST_FIBER_PENDING;
|
||||
frame = dst_fiber_frame(fiber);
|
||||
|
||||
/* Initialize the frame */
|
||||
frame->prevframe = 0;
|
||||
frame->pc = NULL;
|
||||
frame->func = NULL;
|
||||
} else {
|
||||
/* Set the frame to the first available index */
|
||||
fiber->frame = DST_FRAME_SIZE;
|
||||
fiber->frametop = DST_FRAME_SIZE + func->def->slotcount;
|
||||
fiber->stacktop = fiber->frametop + DST_FRAME_SIZE;
|
||||
fiber->status = DST_FIBER_PENDING;
|
||||
frame = dst_fiber_frame(fiber);
|
||||
|
||||
/* Initialize the frame */
|
||||
frame->prevframe = 0;
|
||||
frame->pc = func->def->bytecode;
|
||||
frame->func = func;
|
||||
|
||||
/* Nil slots */
|
||||
for (i = DST_FRAME_SIZE; i < fiber->frametop; ++i) {
|
||||
fiber->data[i].type = DST_NIL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Reset parent. Set it manually if needed. */
|
||||
fiber->parent = NULL;
|
||||
DstFiber *dst_fiber_reset(DstFiber *fiber) {
|
||||
fiber->frame = 0;
|
||||
fiber->frametop = 0;
|
||||
fiber->stacktop = DST_FRAME_SIZE;
|
||||
fiber->status = DST_FIBER_DEAD;
|
||||
return fiber;
|
||||
}
|
||||
|
||||
@ -133,7 +103,7 @@ void dst_fiber_pushn(DstFiber *fiber, const DstValue *arr, uint32_t n) {
|
||||
* If there is nothing to pop of of the stack, return nil. */
|
||||
DstValue dst_fiber_popvalue(DstFiber *fiber) {
|
||||
uint32_t newstacktop = fiber->stacktop - 1;
|
||||
if (newstacktop <= fiber->frametop + DST_FRAME_SIZE) {
|
||||
if (newstacktop < fiber->frametop + DST_FRAME_SIZE) {
|
||||
return dst_wrap_nil();
|
||||
}
|
||||
fiber->stacktop = newstacktop;
|
||||
@ -263,6 +233,10 @@ void dst_fiber_cframe_tail(DstFiber *fiber) {
|
||||
uint32_t nextframetop = fiber->frame + size;;
|
||||
uint32_t nextstacktop = nextframetop + DST_FRAME_SIZE;
|
||||
|
||||
if (fiber->frame == 0) {
|
||||
return dst_fiber_cframe(fiber);
|
||||
}
|
||||
|
||||
DstValue *stack = fiber->data + fiber->frame;
|
||||
DstValue *args = fiber->data + fiber->frametop + DST_FRAME_SIZE;
|
||||
|
||||
|
43
core/gc.c
43
core/gc.c
@ -58,24 +58,6 @@ void dst_mark(DstValue x) {
|
||||
}
|
||||
}
|
||||
|
||||
/* Unpin a value. This enables the GC to collect the value's
|
||||
* memory again. */
|
||||
void dst_unpin(DstValue x) {
|
||||
switch (x.type) {
|
||||
default: break;
|
||||
case DST_STRING:
|
||||
case DST_SYMBOL: dst_unpin_string(x.as.string); break;
|
||||
case DST_FUNCTION: dst_unpin_function(x.as.function); break;
|
||||
case DST_ARRAY: dst_unpin_array(x.as.array); break;
|
||||
case DST_TABLE: dst_unpin_table(x.as.table); break;
|
||||
case DST_STRUCT: dst_unpin_struct(x.as.st); break;
|
||||
case DST_TUPLE: dst_unpin_tuple(x.as.tuple); break;
|
||||
case DST_BUFFER: dst_unpin_buffer(x.as.buffer); break;
|
||||
case DST_FIBER: dst_unpin_fiber(x.as.fiber); break;
|
||||
case DST_USERDATA: dst_unpin_userdata(x.as.pointer); break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Pin a value. This prevents a value from being garbage collected.
|
||||
* Needed if the valueis not accesible to the garbage collector, but
|
||||
* still in use by the program. For example, a c function that
|
||||
@ -98,6 +80,24 @@ void dst_pin(DstValue x) {
|
||||
}
|
||||
}
|
||||
|
||||
/* Unpin a value. This enables the GC to collect the value's
|
||||
* memory again. */
|
||||
void dst_unpin(DstValue x) {
|
||||
switch (x.type) {
|
||||
default: break;
|
||||
case DST_STRING:
|
||||
case DST_SYMBOL: dst_unpin_string(x.as.string); break;
|
||||
case DST_FUNCTION: dst_unpin_function(x.as.function); break;
|
||||
case DST_ARRAY: dst_unpin_array(x.as.array); break;
|
||||
case DST_TABLE: dst_unpin_table(x.as.table); break;
|
||||
case DST_STRUCT: dst_unpin_struct(x.as.st); break;
|
||||
case DST_TUPLE: dst_unpin_tuple(x.as.tuple); break;
|
||||
case DST_BUFFER: dst_unpin_buffer(x.as.buffer); break;
|
||||
case DST_FIBER: dst_unpin_fiber(x.as.fiber); break;
|
||||
case DST_USERDATA: dst_unpin_userdata(x.as.pointer); break;
|
||||
}
|
||||
}
|
||||
|
||||
static void dst_mark_string(const uint8_t *str) {
|
||||
dst_gc_mark(dst_string_raw(str));
|
||||
}
|
||||
@ -188,9 +188,10 @@ static void dst_mark_function(DstFunction *func) {
|
||||
return;
|
||||
dst_gc_mark(func);
|
||||
numenvs = func->def->environments_length;
|
||||
for (i = 0; i < numenvs; ++i)
|
||||
if (NULL != func->envs[i])
|
||||
dst_mark_funcenv(func->envs[i]);
|
||||
if (NULL != func->envs)
|
||||
for (i = 0; i < numenvs; ++i)
|
||||
if (NULL != func->envs[i])
|
||||
dst_mark_funcenv(func->envs[i]);
|
||||
dst_mark_funcdef(func->def);
|
||||
}
|
||||
|
||||
|
130
core/parse.c
130
core/parse.c
@ -116,9 +116,8 @@ static int check_str_const(const char *ref, const uint8_t *start, const uint8_t
|
||||
|
||||
/* Quote a value */
|
||||
static DstValue quote(DstValue x) {
|
||||
DstValue sym = dst_wrap_symbol(dst_cstring("quote"));
|
||||
DstValue *t = dst_tuple_begin(2);
|
||||
t[0] = sym;
|
||||
t[0] = dst_cstrings("quote");
|
||||
t[1] = x;
|
||||
return dst_wrap_tuple(dst_tuple_end(t));
|
||||
}
|
||||
@ -153,24 +152,20 @@ static int is_symbol_char(uint8_t c) {
|
||||
static int to_hex(uint8_t c) {
|
||||
if (c >= '0' && c <= '9') {
|
||||
return c - '0';
|
||||
} else if (c >= 'a' && c <= 'f') {
|
||||
return 10 + c - 'a';
|
||||
} else if (c >= 'A' && c <= 'F') {
|
||||
return 10 + c - 'A';
|
||||
} else if (c >= 'a' && c <= 'f') {
|
||||
return 10 + c - 'a';
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
DstFiber *fiber;
|
||||
DstArray stack;
|
||||
const uint8_t *end;
|
||||
const char *errmsg;
|
||||
enum {
|
||||
DST_PARSE_OK,
|
||||
DST_PARSE_ERROR,
|
||||
DST_PARSE_UNEPECTED_EOS
|
||||
} status;
|
||||
DstParseStatus status;
|
||||
} ParseArgs;
|
||||
|
||||
/* Entry point of the recursive descent parser */
|
||||
@ -186,6 +181,9 @@ static const uint8_t *parse_recur(
|
||||
/* Prevent stack overflow */
|
||||
if (recur == 0) goto too_much_recur;
|
||||
|
||||
/* try parsing again */
|
||||
begin:
|
||||
|
||||
/* Trim leading whitespace and count quotes */
|
||||
while (src < end && (is_whitespace(*src) || *src == '\'')) {
|
||||
if (*src == '\'') {
|
||||
@ -201,7 +199,9 @@ static const uint8_t *parse_recur(
|
||||
switch (*src) {
|
||||
|
||||
/* Numbers, symbols, simple literals */
|
||||
default: {
|
||||
default:
|
||||
atom:
|
||||
{
|
||||
double real;
|
||||
int64_t integer;
|
||||
const uint8_t *tokenend = src;
|
||||
@ -230,7 +230,17 @@ static const uint8_t *parse_recur(
|
||||
break;
|
||||
}
|
||||
|
||||
case ':': {
|
||||
case '#':
|
||||
{
|
||||
/* Jump to next newline */
|
||||
while (src < end && *src != '\n')
|
||||
++src;
|
||||
goto begin;
|
||||
}
|
||||
|
||||
/* Check keyword style strings */
|
||||
case ':':
|
||||
{
|
||||
const uint8_t *tokenend = ++src;
|
||||
while (tokenend < end && is_symbol_char(*tokenend))
|
||||
tokenend++;
|
||||
@ -241,9 +251,10 @@ static const uint8_t *parse_recur(
|
||||
}
|
||||
|
||||
/* String literals */
|
||||
case '"': {
|
||||
case '"':
|
||||
{
|
||||
const uint8_t *strend = ++src;
|
||||
const uint8_t *strstart = src;
|
||||
const uint8_t *strstart = strend;
|
||||
uint32_t len = 0;
|
||||
int containsEscape = 0;
|
||||
/* Preprocess string to check for escapes and string end */
|
||||
@ -253,10 +264,11 @@ static const uint8_t *parse_recur(
|
||||
containsEscape = 1;
|
||||
if (strend >= end) goto unexpected_eos;
|
||||
if (*strend == 'h') {
|
||||
strend += 2;
|
||||
strend += 3;
|
||||
if (strend >= end) goto unexpected_eos;
|
||||
} else {
|
||||
strend++;
|
||||
if (strend >= end) goto unexpected_eos;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -266,7 +278,7 @@ static const uint8_t *parse_recur(
|
||||
while (src < strend) {
|
||||
if (*src == '\\') {
|
||||
src++;
|
||||
switch (*++src) {
|
||||
switch (*src++) {
|
||||
case 'n': *write++ = '\n'; break;
|
||||
case 'r': *write++ = '\r'; break;
|
||||
case 't': *write++ = '\t'; break;
|
||||
@ -293,22 +305,31 @@ static const uint8_t *parse_recur(
|
||||
ret = dst_wrap_string(dst_string_end(buf));
|
||||
} else {
|
||||
ret = dst_wrap_string(dst_string(strstart, strend - strstart));
|
||||
src = strend + 1;
|
||||
}
|
||||
src = strend + 1;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Data Structure literals */
|
||||
case '@':
|
||||
if (src[1] != '{')
|
||||
goto atom;
|
||||
case '(':
|
||||
case '[':
|
||||
case '{': {
|
||||
case '{':
|
||||
{
|
||||
uint32_t n = 0, i = 0;
|
||||
uint32_t istable = 0;
|
||||
uint8_t close;
|
||||
switch (*src++) {
|
||||
case '[': close = ']'; break;
|
||||
case '{': close = '}'; break;
|
||||
case '@': close = '}'; src++; istable = 1; break;
|
||||
default: close = ')'; break;
|
||||
}
|
||||
/* Trim trailing whitespace */
|
||||
while (src < end && (is_whitespace(*src)))
|
||||
++src;
|
||||
/* Recursively parse inside literal */
|
||||
while (*src != close) {
|
||||
src = parse_recur(args, src, recur - 1);
|
||||
@ -321,24 +342,42 @@ static const uint8_t *parse_recur(
|
||||
src++;
|
||||
switch (close) {
|
||||
case ')':
|
||||
case ']':
|
||||
{
|
||||
DstValue *tup = dst_tuple_begin(n);
|
||||
for (i = n; i > 0; i--)
|
||||
tup[i - 1] = dst_fiber_popvalue(args->fiber);
|
||||
tup[i - 1] = dst_array_pop(&args->stack);
|
||||
ret = dst_wrap_tuple(dst_tuple_end(tup));
|
||||
break;
|
||||
}
|
||||
case ']':
|
||||
{
|
||||
DstArray *arr = dst_array(n);
|
||||
for (i = n; i > 0; i--)
|
||||
arr->data[i - 1] = dst_array_pop(&args->stack);
|
||||
arr->count = n;
|
||||
ret = dst_wrap_array(arr);
|
||||
break;
|
||||
}
|
||||
case '}':
|
||||
{
|
||||
if (n & 1) goto struct_oddargs;
|
||||
DstValue *st = dst_struct_begin(n >> 1);
|
||||
for (i = n; i > 0; i -= 2) {
|
||||
DstValue val = dst_fiber_popvalue(args->fiber);
|
||||
DstValue key = dst_fiber_popvalue(args->fiber);
|
||||
dst_struct_put(st, key, val);
|
||||
if (istable) {
|
||||
DstTable *t = dst_table(n);
|
||||
for (i = n; i > 0; i -= 2) {
|
||||
DstValue val = dst_array_pop(&args->stack);
|
||||
DstValue key = dst_array_pop(&args->stack);
|
||||
dst_table_put(t, key, val);
|
||||
}
|
||||
ret = dst_wrap_table(t);
|
||||
} else {
|
||||
DstValue *st = dst_struct_begin(n >> 1);
|
||||
for (i = n; i > 0; i -= 2) {
|
||||
DstValue val = dst_array_pop(&args->stack);
|
||||
DstValue key = dst_array_pop(&args->stack);
|
||||
dst_struct_put(st, key, val);
|
||||
}
|
||||
ret = dst_wrap_struct(dst_struct_end(st));
|
||||
}
|
||||
ret = dst_wrap_struct(dst_struct_end(st));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -350,7 +389,7 @@ static const uint8_t *parse_recur(
|
||||
while (qcount--) ret = quote(ret);
|
||||
|
||||
/* Push the result to the stack */
|
||||
dst_fiber_push(args->fiber, ret);
|
||||
dst_array_push(&args->stack, ret);
|
||||
|
||||
/* Return the new source position for further calls */
|
||||
return src;
|
||||
@ -394,42 +433,37 @@ static const uint8_t *parse_recur(
|
||||
}
|
||||
|
||||
/* Parse an array of bytes. Return value in the fiber return value. */
|
||||
int dst_parse(const uint8_t *src, uint32_t len, const uint8_t **newsrc) {
|
||||
DstParseResult dst_parse(const uint8_t *src, uint32_t len) {
|
||||
DstParseResult res;
|
||||
ParseArgs args;
|
||||
DstFiber *fiber = dst_vm_fiber;
|
||||
const uint8_t *newsrc;
|
||||
|
||||
/* Save original stack top */
|
||||
uint32_t oldstacktop = fiber->stacktop;
|
||||
|
||||
args.fiber = fiber;
|
||||
dst_array_init(&args.stack, 10);
|
||||
args.status = DST_PARSE_OK;
|
||||
args.end = src + len;
|
||||
args.errmsg = NULL;
|
||||
|
||||
src = parse_recur(&args, src, DST_RECURSION_GUARD);
|
||||
if (NULL != newsrc) *newsrc = src;
|
||||
newsrc = parse_recur(&args, src, DST_RECURSION_GUARD);
|
||||
res.status = args.status;
|
||||
res.bytes_read = (uint32_t) (newsrc - src);
|
||||
|
||||
/* TODO - source maps */
|
||||
res.map = dst_wrap_nil();
|
||||
|
||||
if (args.errmsg) {
|
||||
fiber->ret = dst_wrap_string(dst_cstring(args.errmsg));
|
||||
res.result.error = dst_cstring(args.errmsg);
|
||||
} else {
|
||||
fiber->ret = dst_fiber_popvalue(fiber);
|
||||
res.result.value = dst_array_pop(&args.stack);
|
||||
}
|
||||
|
||||
/* Reset stacktop */
|
||||
fiber->stacktop = oldstacktop;
|
||||
dst_array_deinit(&args.stack);
|
||||
|
||||
return args.status;
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Parse a c string */
|
||||
int dst_parsec(const char *src, const char **newsrc) {
|
||||
DstParseResult dst_parsec(const char *src) {
|
||||
uint32_t len = 0;
|
||||
const uint8_t *ns = NULL;
|
||||
int status;
|
||||
while (src[len]) ++len;
|
||||
status = dst_parse((const uint8_t *)src, len, &ns);
|
||||
if (newsrc) {
|
||||
*newsrc = (const char *)ns;
|
||||
}
|
||||
return status;
|
||||
return dst_parse((const uint8_t *)src, len);
|
||||
}
|
||||
|
336
core/string.c
336
core/string.c
@ -359,25 +359,176 @@ void dst_short_description_b(DstBuffer *buffer, DstValue x) {
|
||||
}
|
||||
}
|
||||
|
||||
/* Helper structure for stringifying deeply nested structures */
|
||||
typedef struct DstPrinter DstPrinter;
|
||||
struct DstPrinter {
|
||||
DstBuffer buffer;
|
||||
DstTable seen;
|
||||
uint32_t flags;
|
||||
int64_t next;
|
||||
uint32_t indent;
|
||||
uint32_t indent_size;
|
||||
uint32_t token_line_limit;
|
||||
uint32_t depth;
|
||||
};
|
||||
|
||||
#define DST_PRINTFLAG_INDENT 1
|
||||
#define DST_PRINTFLAG_TABLEPAIR 2
|
||||
#define DST_PRINTFLAG_COLORIZE 4
|
||||
#define DST_PRINTFLAG_ALLMAN 8
|
||||
|
||||
/* Go to next line for printer */
|
||||
static void dst_print_indent(DstPrinter *p) {
|
||||
uint32_t i, len;
|
||||
len = p->indent_size * p->indent;
|
||||
for (i = 0; i < len; i++) {
|
||||
dst_buffer_push_u8(&p->buffer, ' ');
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if a value is a print atom (not a printable data structure) */
|
||||
static int is_print_ds(DstValue v) {
|
||||
switch (v.type) {
|
||||
default: return 0;
|
||||
case DST_ARRAY:
|
||||
case DST_STRUCT:
|
||||
case DST_TUPLE:
|
||||
case DST_TABLE: return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* VT100 Colors for types */
|
||||
static const char *dst_type_colors[15] = {
|
||||
"\x1B[35m",
|
||||
"\x1B[33m",
|
||||
"\x1B[33m",
|
||||
"\x1B[35m",
|
||||
"\x1B[32m",
|
||||
"\x1B[36m",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"\x1B[37m",
|
||||
"\x1B[37m",
|
||||
"\x1B[37m",
|
||||
"\x1B[37m",
|
||||
"\x1B[37m"
|
||||
};
|
||||
|
||||
/* Forward declaration */
|
||||
static void dst_description_helper(DstPrinter *p, DstValue x);
|
||||
|
||||
/* Print a hastable view inline */
|
||||
static void dst_print_hashtable_inner(DstPrinter *p, const DstValue *data, uint32_t len, uint32_t cap) {
|
||||
uint32_t i;
|
||||
int doindent = 0;
|
||||
if (p->flags & DST_PRINTFLAG_INDENT) {
|
||||
if (len <= p->token_line_limit) {
|
||||
for (i = 0; i < cap; i += 2) {
|
||||
if (is_print_ds(data[i]) || is_print_ds(data[i + 1])) {
|
||||
doindent = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
doindent = 1;
|
||||
}
|
||||
}
|
||||
if (doindent) {
|
||||
dst_buffer_push_u8(&p->buffer, '\n');
|
||||
p->indent++;
|
||||
for (i = 0; i < cap; i += 2) {
|
||||
if (data[i].type != DST_NIL) {
|
||||
dst_print_indent(p);
|
||||
dst_description_helper(p, data[i]);
|
||||
dst_buffer_push_u8(&p->buffer, ' ');
|
||||
dst_description_helper(p, data[i + 1]);
|
||||
dst_buffer_push_u8(&p->buffer, '\n');
|
||||
}
|
||||
}
|
||||
p->indent--;
|
||||
dst_print_indent(p);
|
||||
} else {
|
||||
int isfirst = 1;
|
||||
for (i = 0; i < cap; i += 2) {
|
||||
if (data[i].type != DST_NIL) {
|
||||
if (isfirst)
|
||||
isfirst = 0;
|
||||
else
|
||||
dst_buffer_push_u8(&p->buffer, ' ');
|
||||
dst_description_helper(p, data[i]);
|
||||
dst_buffer_push_u8(&p->buffer, ' ');
|
||||
dst_description_helper(p, data[i + 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Help print a sequence */
|
||||
static void dst_print_seq_inner(DstPrinter *p, const DstValue *data, uint32_t len) {
|
||||
uint32_t i;
|
||||
int doindent = 0;
|
||||
if (p->flags & DST_PRINTFLAG_INDENT) {
|
||||
if (len <= p->token_line_limit) {
|
||||
for (i = 0; i < len; ++i) {
|
||||
if (is_print_ds(data[i])) {
|
||||
doindent = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
doindent = 1;
|
||||
}
|
||||
}
|
||||
if (doindent) {
|
||||
dst_buffer_push_u8(&p->buffer, '\n');
|
||||
p->indent++;
|
||||
for (i = 0; i < len; ++i) {
|
||||
dst_print_indent(p);
|
||||
dst_description_helper(p, data[i]);
|
||||
dst_buffer_push_u8(&p->buffer, '\n');
|
||||
}
|
||||
p->indent--;
|
||||
dst_print_indent(p);
|
||||
} else {
|
||||
for (i = 0; i < len; ++i) {
|
||||
dst_description_helper(p, data[i]);
|
||||
if (i != len - 1)
|
||||
dst_buffer_push_u8(&p->buffer, ' ');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Static debug print helper */
|
||||
static int64_t dst_description_helper(DstBuffer *b, DstTable *seen, DstValue x, int64_t next, int depth) {
|
||||
DstValue check = dst_table_get(seen, x);
|
||||
static void dst_description_helper(DstPrinter *p, DstValue x) {
|
||||
p->depth--;
|
||||
DstValue check = dst_table_get(&p->seen, x);
|
||||
if (check.type == DST_INTEGER) {
|
||||
dst_buffer_push_cstring(b, "<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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
12
core/table.c
12
core/table.c
@ -170,3 +170,15 @@ DstValue dst_table_next(DstTable *t, DstValue key) {
|
||||
}
|
||||
return dst_wrap_nil();
|
||||
}
|
||||
|
||||
/* Convert table to struct */
|
||||
const DstValue *dst_table_to_struct(DstTable *t) {
|
||||
uint32_t i;
|
||||
const DstValue *st;
|
||||
st = dst_struct_begin(t->count);
|
||||
for (i = 0; i < t->capacity; i++) {
|
||||
if (t->data[i].type != DST_NIL)
|
||||
dst_struct_put(st, t->data[i], t->data[i + 1]);
|
||||
}
|
||||
return dst_struct_end(st);
|
||||
}
|
||||
|
@ -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
151
core/vm.c
@ -27,7 +27,7 @@
|
||||
DstFiber *dst_vm_fiber;
|
||||
|
||||
/* Start running the VM from where it left off. */
|
||||
int dst_continue(DstFiber *fiber) {
|
||||
int dst_continue() {
|
||||
|
||||
/* VM state */
|
||||
DstValue *stack;
|
||||
@ -36,9 +36,9 @@ int dst_continue(DstFiber *fiber) {
|
||||
|
||||
/* Used to extract bits from the opcode that correspond to arguments.
|
||||
* Pulls out unsigned integers */
|
||||
#define oparg(shift, mask) ((*pc >> ((shift) << 3)) & (mask))
|
||||
#define oparg(shift, mask) (((*pc) >> ((shift) << 3)) & (mask))
|
||||
|
||||
#define vm_throw(e) do { fiber->ret = dst_wrap_string(dst_cstring((e))); goto vm_error; } while (0)
|
||||
#define vm_throw(e) do { dst_vm_fiber->ret = dst_wrap_string(dst_cstring((e))); goto vm_error; } while (0)
|
||||
#define vm_assert(cond, e) do {if (!(cond)) vm_throw((e)); } while (0)
|
||||
|
||||
#define vm_binop_integer(op) \
|
||||
@ -69,19 +69,19 @@ int dst_continue(DstFiber *fiber) {
|
||||
vm_assert(op1.type == DST_INTEGER || op1.type == DST_REAL, "expected number");\
|
||||
vm_assert(op2.type == DST_INTEGER || op2.type == DST_REAL, "expected number");\
|
||||
stack[oparg(1, 0xFF)] = op1.type == DST_INTEGER\
|
||||
? op2.type == DST_INTEGER\
|
||||
? (op2.type == DST_INTEGER\
|
||||
? dst_wrap_integer(op1.as.integer op op2.as.integer)\
|
||||
: dst_wrap_real(dst_integer_to_real(op1.as.integer) op op2.as.real)\
|
||||
: op2.type == DST_INTEGER\
|
||||
: dst_wrap_real(dst_integer_to_real(op1.as.integer) op op2.as.real))\
|
||||
: (op2.type == DST_INTEGER\
|
||||
? dst_wrap_real(op1.as.real op dst_integer_to_real(op2.as.integer))\
|
||||
: dst_wrap_real(op1.as.real op op2.as.real);\
|
||||
: dst_wrap_real(op1.as.real op op2.as.real));\
|
||||
pc++;\
|
||||
continue;\
|
||||
}
|
||||
|
||||
#define vm_init_fiber_state() \
|
||||
fiber->status = DST_FIBER_ALIVE;\
|
||||
stack = fiber->data + fiber->frame;\
|
||||
dst_vm_fiber->status = DST_FIBER_ALIVE;\
|
||||
stack = dst_vm_fiber->data + dst_vm_fiber->frame;\
|
||||
pc = dst_stack_frame(stack)->pc;\
|
||||
func = dst_stack_frame(stack)->func;
|
||||
|
||||
@ -105,7 +105,7 @@ int dst_continue(DstFiber *fiber) {
|
||||
continue;
|
||||
|
||||
case DOP_ERROR:
|
||||
fiber->ret = stack[oparg(1, 0xFF)];
|
||||
dst_vm_fiber->ret = stack[oparg(1, 0xFF)];
|
||||
goto vm_error;
|
||||
|
||||
case DOP_TYPECHECK:
|
||||
@ -115,11 +115,11 @@ int dst_continue(DstFiber *fiber) {
|
||||
continue;
|
||||
|
||||
case DOP_RETURN:
|
||||
fiber->ret = stack[oparg(1, 0xFFFFFF)];
|
||||
dst_vm_fiber->ret = stack[oparg(1, 0xFFFFFF)];
|
||||
goto vm_return;
|
||||
|
||||
case DOP_RETURN_NIL:
|
||||
fiber->ret.type = DST_NIL;
|
||||
dst_vm_fiber->ret.type = DST_NIL;
|
||||
goto vm_return;
|
||||
|
||||
case DOP_COERCE_INTEGER:
|
||||
@ -394,8 +394,8 @@ int dst_continue(DstFiber *fiber) {
|
||||
if (fd->flags & DST_FUNCDEF_FLAG_NEEDSENV) {
|
||||
/* Delayed capture of current stack frame */
|
||||
DstFuncEnv *env = dst_alloc(DST_MEMORY_FUNCENV, sizeof(DstFuncEnv));
|
||||
env->offset = fiber->frame;
|
||||
env->as.fiber = fiber;
|
||||
env->offset = dst_vm_fiber->frame;
|
||||
env->as.fiber = dst_vm_fiber;
|
||||
env->length = func->def->slotcount;
|
||||
fn->envs[0] = env;
|
||||
} else {
|
||||
@ -411,19 +411,19 @@ int dst_continue(DstFiber *fiber) {
|
||||
}
|
||||
|
||||
case DOP_PUSH:
|
||||
dst_fiber_push(fiber, stack[oparg(1, 0xFFFFFF)]);
|
||||
dst_fiber_push(dst_vm_fiber, stack[oparg(1, 0xFFFFFF)]);
|
||||
pc++;
|
||||
break;
|
||||
|
||||
case DOP_PUSH_2:
|
||||
dst_fiber_push2(fiber,
|
||||
dst_fiber_push2(dst_vm_fiber,
|
||||
stack[oparg(1, 0xFF)],
|
||||
stack[oparg(2, 0xFFFF)]);
|
||||
pc++;
|
||||
break;;
|
||||
|
||||
case DOP_PUSH_3:
|
||||
dst_fiber_push3(fiber,
|
||||
dst_fiber_push3(dst_vm_fiber,
|
||||
stack[oparg(1, 0xFF)],
|
||||
stack[oparg(2, 0xFF)],
|
||||
stack[oparg(3, 0xFF)]);
|
||||
@ -435,7 +435,7 @@ int dst_continue(DstFiber *fiber) {
|
||||
uint32_t count;
|
||||
const DstValue *array;
|
||||
if (dst_seq_view(stack[oparg(1, 0xFFFFFF)], &array, &count)) {
|
||||
dst_fiber_pushn(fiber, array, count);
|
||||
dst_fiber_pushn(dst_vm_fiber, array, count);
|
||||
} else {
|
||||
vm_throw("expected array or tuple");
|
||||
}
|
||||
@ -448,19 +448,19 @@ int dst_continue(DstFiber *fiber) {
|
||||
DstValue callee = stack[oparg(2, 0xFFFF)];
|
||||
if (callee.type == DST_FUNCTION) {
|
||||
func = callee.as.function;
|
||||
dst_fiber_funcframe(fiber, func);
|
||||
stack = fiber->data + fiber->frame;
|
||||
dst_fiber_funcframe(dst_vm_fiber, func);
|
||||
stack = dst_vm_fiber->data + dst_vm_fiber->frame;
|
||||
pc = func->def->bytecode;
|
||||
break;
|
||||
} else if (callee.type == DST_CFUNCTION) {
|
||||
dst_fiber_cframe(fiber);
|
||||
stack = fiber->data + fiber->frame;
|
||||
fiber->ret.type = DST_NIL;
|
||||
if (callee.as.cfunction(fiber, stack, fiber->frametop - fiber->frame)) {
|
||||
dst_fiber_popframe(fiber);
|
||||
dst_fiber_cframe(dst_vm_fiber);
|
||||
stack = dst_vm_fiber->data + dst_vm_fiber->frame;
|
||||
dst_vm_fiber->ret.type = DST_NIL;
|
||||
if (callee.as.cfunction(stack, dst_vm_fiber->frametop - dst_vm_fiber->frame)) {
|
||||
dst_fiber_popframe(dst_vm_fiber);
|
||||
goto vm_error;
|
||||
} else {
|
||||
dst_fiber_popframe(fiber);
|
||||
dst_fiber_popframe(dst_vm_fiber);
|
||||
goto vm_return;
|
||||
}
|
||||
} else {
|
||||
@ -474,19 +474,19 @@ int dst_continue(DstFiber *fiber) {
|
||||
DstValue callee = stack[oparg(2, 0xFFFF)];
|
||||
if (callee.type == DST_FUNCTION) {
|
||||
func = callee.as.function;
|
||||
dst_fiber_funcframe_tail(fiber, func);
|
||||
stack = fiber->data + fiber->frame;
|
||||
dst_fiber_funcframe_tail(dst_vm_fiber, func);
|
||||
stack = dst_vm_fiber->data + dst_vm_fiber->frame;
|
||||
pc = func->def->bytecode;
|
||||
break;
|
||||
} else if (callee.type == DST_CFUNCTION) {
|
||||
dst_fiber_cframe_tail(fiber);
|
||||
stack = fiber->data + fiber->frame;
|
||||
fiber->ret.type = DST_NIL;
|
||||
if (callee.as.cfunction(fiber, stack, fiber->frametop - fiber->frame)) {
|
||||
dst_fiber_popframe(fiber);
|
||||
dst_fiber_cframe_tail(dst_vm_fiber);
|
||||
stack = dst_vm_fiber->data + dst_vm_fiber->frame;
|
||||
dst_vm_fiber->ret.type = DST_NIL;
|
||||
if (callee.as.cfunction(stack, dst_vm_fiber->frametop - dst_vm_fiber->frame)) {
|
||||
dst_fiber_popframe(dst_vm_fiber);
|
||||
goto vm_error;
|
||||
} else {
|
||||
dst_fiber_popframe(fiber);
|
||||
dst_fiber_popframe(dst_vm_fiber);
|
||||
goto vm_return;
|
||||
}
|
||||
} else {
|
||||
@ -499,14 +499,14 @@ int dst_continue(DstFiber *fiber) {
|
||||
{
|
||||
DstCFunction f = dst_vm_syscalls[oparg(2, 0xFF)];
|
||||
vm_assert(NULL != f, "invalid syscall");
|
||||
dst_fiber_cframe(fiber);
|
||||
stack = fiber->data + fiber->frame;
|
||||
fiber->ret.type = DST_NIL;
|
||||
if (f(fiber, stack, fiber->frametop - fiber->frame)) {
|
||||
dst_fiber_popframe(fiber);
|
||||
dst_fiber_cframe(dst_vm_fiber);
|
||||
stack = dst_vm_fiber->data + dst_vm_fiber->frame;
|
||||
dst_vm_fiber->ret.type = DST_NIL;
|
||||
if (f(stack, dst_vm_fiber->frametop - dst_vm_fiber->frame)) {
|
||||
dst_fiber_popframe(dst_vm_fiber);
|
||||
goto vm_error;
|
||||
} else {
|
||||
dst_fiber_popframe(fiber);
|
||||
dst_fiber_popframe(dst_vm_fiber);
|
||||
goto vm_return;
|
||||
}
|
||||
continue;
|
||||
@ -531,17 +531,17 @@ int dst_continue(DstFiber *fiber) {
|
||||
temp.type == DST_NIL, "expected fiber");
|
||||
nextfiber = temp.type == DST_FIBER
|
||||
? temp.as.fiber
|
||||
: fiber->parent;
|
||||
: dst_vm_fiber->parent;
|
||||
/* Check for root fiber */
|
||||
if (NULL == nextfiber) {
|
||||
frame->pc = pc;
|
||||
fiber->ret = retvalue;
|
||||
dst_vm_fiber->ret = retvalue;
|
||||
return 0;
|
||||
}
|
||||
vm_assert(nextfiber->status == DST_FIBER_PENDING, "can only transfer to pending fiber");
|
||||
frame->pc = pc;
|
||||
fiber->status = DST_FIBER_PENDING;
|
||||
fiber = nextfiber;
|
||||
dst_vm_fiber->status = DST_FIBER_PENDING;
|
||||
dst_vm_fiber = nextfiber;
|
||||
vm_init_fiber_state();
|
||||
stack[oparg(1, 0xFF)] = retvalue;
|
||||
pc++;
|
||||
@ -551,26 +551,26 @@ int dst_continue(DstFiber *fiber) {
|
||||
/* Handle returning from stack frame. Expect return value in fiber->ret */
|
||||
vm_return:
|
||||
{
|
||||
DstValue ret = fiber->ret;
|
||||
dst_fiber_popframe(fiber);
|
||||
while (fiber->frame ||
|
||||
fiber->status == DST_FIBER_DEAD ||
|
||||
fiber->status == DST_FIBER_ERROR) {
|
||||
fiber->status = DST_FIBER_DEAD;
|
||||
if (fiber->parent) {
|
||||
fiber = fiber->parent;
|
||||
if (fiber->status == DST_FIBER_ALIVE) {
|
||||
DstValue ret = dst_vm_fiber->ret;
|
||||
dst_fiber_popframe(dst_vm_fiber);
|
||||
while (!dst_vm_fiber->frame ||
|
||||
dst_vm_fiber->status == DST_FIBER_DEAD ||
|
||||
dst_vm_fiber->status == DST_FIBER_ERROR) {
|
||||
dst_vm_fiber->status = DST_FIBER_DEAD;
|
||||
if (NULL != dst_vm_fiber->parent) {
|
||||
dst_vm_fiber = dst_vm_fiber->parent;
|
||||
if (dst_vm_fiber->status == DST_FIBER_ALIVE) {
|
||||
/* If the parent thread is still alive,
|
||||
we are inside a cfunction */
|
||||
return 0;
|
||||
}
|
||||
stack = fiber->data + fiber->frame;
|
||||
stack = dst_vm_fiber->data + dst_vm_fiber->frame;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
fiber->status = DST_FIBER_ALIVE;
|
||||
stack = fiber->data + fiber->frame;
|
||||
dst_vm_fiber->status = DST_FIBER_ALIVE;
|
||||
stack = dst_vm_fiber->data + dst_vm_fiber->frame;
|
||||
pc = dst_stack_frame(stack)->pc;
|
||||
stack[oparg(1, 0xFF)] = ret;
|
||||
pc++;
|
||||
@ -580,22 +580,22 @@ int dst_continue(DstFiber *fiber) {
|
||||
/* Handle errors from c functions and vm opcodes */
|
||||
vm_error:
|
||||
{
|
||||
DstValue ret = fiber->ret;
|
||||
fiber->status = DST_FIBER_ERROR;
|
||||
while (fiber->frame ||
|
||||
fiber->status == DST_FIBER_DEAD ||
|
||||
fiber->status == DST_FIBER_ERROR) {
|
||||
if (fiber->parent == NULL)
|
||||
DstValue ret = dst_vm_fiber->ret;
|
||||
dst_vm_fiber->status = DST_FIBER_ERROR;
|
||||
while (!dst_vm_fiber->frame ||
|
||||
dst_vm_fiber->status == DST_FIBER_DEAD ||
|
||||
dst_vm_fiber->status == DST_FIBER_ERROR) {
|
||||
if (dst_vm_fiber->parent == NULL)
|
||||
return 1;
|
||||
fiber = fiber->parent;
|
||||
if (fiber->status == DST_FIBER_ALIVE) {
|
||||
dst_vm_fiber = dst_vm_fiber->parent;
|
||||
if (dst_vm_fiber->status == DST_FIBER_ALIVE) {
|
||||
/* If the parent thread is still alive,
|
||||
we are inside a cfunction */
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
fiber->status = DST_FIBER_ALIVE;
|
||||
stack = fiber->data + fiber->frame;
|
||||
dst_vm_fiber->status = DST_FIBER_ALIVE;
|
||||
stack = dst_vm_fiber->data + dst_vm_fiber->frame;
|
||||
pc = dst_stack_frame(stack)->pc;
|
||||
stack[oparg(1, 0xFF)] = ret;
|
||||
pc++;
|
||||
@ -626,20 +626,21 @@ int dst_continue(DstFiber *fiber) {
|
||||
/* Run the vm with a given function. This function is
|
||||
* called to start the vm. */
|
||||
int dst_run(DstValue callee) {
|
||||
int result;
|
||||
if (NULL == dst_vm_fiber) {
|
||||
dst_vm_fiber = dst_fiber(0);
|
||||
} else {
|
||||
dst_fiber_reset(dst_vm_fiber);
|
||||
}
|
||||
if (callee.type == DST_CFUNCTION) {
|
||||
dst_vm_fiber = dst_fiber(NULL, 0);
|
||||
dst_vm_fiber->ret.type = DST_NIL;
|
||||
dst_fiber_cframe(dst_vm_fiber);
|
||||
result = callee.as.cfunction(dst_vm_fiber, dst_vm_fiber->data + dst_vm_fiber->frame, 0);
|
||||
return callee.as.cfunction(dst_vm_fiber->data + dst_vm_fiber->frame, 0);
|
||||
} else if (callee.type == DST_FUNCTION) {
|
||||
dst_vm_fiber = dst_fiber(callee.as.function, 64);
|
||||
result = dst_continue(dst_vm_fiber);
|
||||
} else {
|
||||
dst_vm_fiber->ret = dst_wrap_string(dst_cstring("expected function"));
|
||||
result = 1;
|
||||
dst_fiber_funcframe(dst_vm_fiber, callee.as.function);
|
||||
return dst_continue();
|
||||
}
|
||||
return result;
|
||||
dst_vm_fiber->ret = dst_wrap_string(dst_cstring("expected function"));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Setup functions */
|
||||
|
50
unittests/asm_test.c
Normal file
50
unittests/asm_test.c
Normal 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
88
unittests/fiber_test.c
Normal 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
19
unittests/parse_test.c
Normal 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;
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user