mirror of
https://github.com/janet-lang/janet
synced 2024-11-30 20:09:54 +00:00
More work. Too many changes to be listed.
This commit is contained in:
parent
a2ee028bd5
commit
f6dcb07c8d
3
.gitignore
vendored
3
.gitignore
vendored
@ -5,6 +5,9 @@ dst
|
||||
# Generated files
|
||||
*.gen.h
|
||||
|
||||
# Generate test files
|
||||
*.out
|
||||
|
||||
# Tools
|
||||
xxd
|
||||
|
||||
|
78
Makefile
78
Makefile
@ -1,4 +1,22 @@
|
||||
# DST
|
||||
# Copyright (c) 2017 Calvin Rose
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to
|
||||
# deal in the Software without restriction, including without limitation the
|
||||
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
# sell copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
# IN THE SOFTWARE.
|
||||
|
||||
################################
|
||||
##### Set global variables #####
|
||||
@ -12,22 +30,25 @@ CFLAGS=-std=c99 -Wall -Wextra -I./include -I./libs -g -DDST_VERSION=$(VERSION)
|
||||
PREFIX=/usr/local
|
||||
DST_TARGET=dst
|
||||
DST_XXD=xxd
|
||||
# Use gdb. On mac use lldb
|
||||
# Use gdb. On osx use lldb
|
||||
DEBUGGER=lldb
|
||||
DST_INTERNAL_HEADERS=$(addprefix core/, internal.h bootstrap.h cache.h)
|
||||
DST_HEADERS=$(addprefix include/dst/, dst.h)
|
||||
DST_INTERNAL_HEADERS=$(addprefix core/,cache.h opcodes.h)
|
||||
DST_HEADERS=$(addprefix include/dst/,dst.h)
|
||||
|
||||
#############################
|
||||
##### Generated headers #####
|
||||
#############################
|
||||
|
||||
DST_LANG_SOURCES=$(addprefix libs/, bootstrap.dst)
|
||||
DST_LANG_HEADERS=$(patsubst %.dst,%.gen.h,$(DST_LANG_SOURCES))
|
||||
DST_ALL_HEADERS=$(DST_HEADERS) $(DST_INTERNAL_HEADERS) $(DST_LANG_HEADERS)
|
||||
|
||||
all: $(DST_TARGET)
|
||||
|
||||
#######################
|
||||
##### Build tools #####
|
||||
#######################
|
||||
|
||||
$(DST_XXD): libs/xxd.c
|
||||
$(CC) -o $(DST_XXD) libs/xxd.c
|
||||
|
||||
@ -37,19 +58,39 @@ $(DST_XXD): libs/xxd.c
|
||||
###################################
|
||||
##### The core vm and runtime #####
|
||||
###################################
|
||||
|
||||
DST_CORE_SOURCES=$(addprefix core/,\
|
||||
util.c wrap.c buffer.c array.c table.c userdata.c func.c\
|
||||
value.c vm.c ds.c gc.c thread.c serialize.c tuple.c\
|
||||
string.c bootstrap_parse.c client.c cache.c struct.c)
|
||||
array.c asm.c buffer.c cache.c fiber.c func.c gc.c parse.c string.c\
|
||||
struct.c syscalls.c table.c tuple.c userdata.c util.c\
|
||||
value.c vm.c wrap.c)
|
||||
DST_CORE_OBJECTS=$(patsubst %.c,%.o,$(DST_CORE_SOURCES))
|
||||
|
||||
$(DST_TARGET): $(DST_CORE_OBJECTS) $(DST_LANG_HEADERS)
|
||||
$(DST_TARGET): $(DST_CORE_OBJECTS)
|
||||
$(CC) $(CFLAGS) -o $(DST_TARGET) $(DST_CORE_OBJECTS)
|
||||
|
||||
# Compile all .c to .o
|
||||
%.o: %.c $(DST_HEADERS) $(DST_INTERNAL_HEADERS) $(DST_LANG_HEADERS)
|
||||
%.o: %.c $(DST_ALL_HEADERS)
|
||||
$(CC) $(CFLAGS) -o $@ -c $<
|
||||
|
||||
######################
|
||||
##### Unit Tests #####
|
||||
######################
|
||||
|
||||
CCU_FLAGS = $(CFLAGS) -DDST_UNIT_TEST
|
||||
|
||||
DST_UNIT_BINARIES=$(addprefix unittests/,\
|
||||
array_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
|
||||
|
||||
###################
|
||||
##### Testing #####
|
||||
###################
|
||||
|
||||
run: $(DST_TARGET)
|
||||
@ ./$(DST_TARGET)
|
||||
|
||||
@ -59,19 +100,24 @@ debug: $(DST_TARGET)
|
||||
valgrind: $(DST_TARGET)
|
||||
@ valgrind --leak-check=full -v ./$(DST_TARGET)
|
||||
|
||||
clean:
|
||||
rm $(DST_TARGET) || true
|
||||
rm $(DST_CORE_OBJECTS) || true
|
||||
rm $(DST_LANG_HEADERS) || true
|
||||
rm vgcore.* || true
|
||||
rm $(DST_XXD) || true
|
||||
|
||||
test: $(DST_TARGET)
|
||||
@ ./$(DST_TARGET) dsttests/basic.dst
|
||||
|
||||
valtest: $(DST_TARGET)
|
||||
valgrind --leak-check=full -v ./$(DST_TARGET) dsttests/basic.dst
|
||||
|
||||
#################
|
||||
##### Other #####
|
||||
#################
|
||||
|
||||
clean:
|
||||
rm $(DST_TARGET) || true
|
||||
rm $(DST_CORE_OBJECTS) || true
|
||||
rm $(DST_LANG_HEADERS) || true
|
||||
rm vgcore.* || true
|
||||
rm unittests/*.out || true
|
||||
rm $(DST_XXD) || true
|
||||
|
||||
install: $(DST_TARGET)
|
||||
cp $(DST_TARGET) $(BINDIR)/dst
|
||||
|
||||
|
30
core/array.c
30
core/array.c
@ -20,14 +20,12 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "internal.h"
|
||||
#include <dst/dst.h>
|
||||
|
||||
/* Iniializes an array. Assumes the pointer to the array is already on the stack. */
|
||||
DstArray *dst_array(Dst *vm, uint32_t capacity) {
|
||||
DstArray *array = dst_alloc(vm, DST_MEMORY_ARRAY, sizeof(DstArray));
|
||||
/* Iniializes an array */
|
||||
DstArray *dst_array_init(DstArray *array, uint32_t capacity) {
|
||||
DstValue *data = (DstValue *) malloc(sizeof(DstValue) * capacity);
|
||||
if (NULL == array || NULL == data) {
|
||||
if (NULL == data) {
|
||||
DST_OUT_OF_MEMORY;
|
||||
}
|
||||
array->count = 0;
|
||||
@ -36,8 +34,18 @@ DstArray *dst_array(Dst *vm, uint32_t capacity) {
|
||||
return array;
|
||||
}
|
||||
|
||||
void dst_array_deinit(DstArray *array) {
|
||||
free(array->data);
|
||||
}
|
||||
|
||||
/* Creates a new array */
|
||||
DstArray *dst_array(uint32_t capacity) {
|
||||
DstArray *array = dst_alloc(DST_MEMORY_ARRAY, sizeof(DstArray));
|
||||
return dst_array_init(array, capacity);
|
||||
}
|
||||
|
||||
/* Ensure the array has enough capacity for elements */
|
||||
void dst_array_ensure(Dst *vm, DstArray *array, uint32_t capacity) {
|
||||
void dst_array_ensure(DstArray *array, uint32_t capacity) {
|
||||
DstValue *newData;
|
||||
DstValue *old = array->data;
|
||||
if (capacity <= array->capacity) return;
|
||||
@ -50,10 +58,10 @@ void dst_array_ensure(Dst *vm, DstArray *array, uint32_t capacity) {
|
||||
}
|
||||
|
||||
/* Set the count of an array. Extend with nil if needed. */
|
||||
void dst_array_setcount(Dst *vm, DstArray *array, uint32_t count) {
|
||||
void dst_array_setcount(DstArray *array, uint32_t count) {
|
||||
if (count > array->count) {
|
||||
uint32_t i;
|
||||
dst_array_ensure(vm, array, count + 1);
|
||||
dst_array_ensure(array, count + 1);
|
||||
for (i = array->count; i < count; ++i)
|
||||
array->data[i].type = DST_NIL;
|
||||
}
|
||||
@ -61,10 +69,10 @@ void dst_array_setcount(Dst *vm, DstArray *array, uint32_t count) {
|
||||
}
|
||||
|
||||
/* Push a value to the top of the array */
|
||||
void dst_array_push(Dst *vm, DstArray *array, DstValue x) {
|
||||
void dst_array_push(DstArray *array, DstValue x) {
|
||||
uint32_t newcount = array->count + 1;
|
||||
if (newcount >= array->capacity) }
|
||||
dst_array_ensure(vm, array, newcount * 2);
|
||||
if (newcount >= array->capacity) {
|
||||
dst_array_ensure(array, newcount * 2);
|
||||
}
|
||||
array->data[array->count] = x;
|
||||
array->count = newcount;
|
||||
|
617
core/asm.c
617
core/asm.c
@ -0,0 +1,617 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <setjmp.h>
|
||||
|
||||
#include <dst/dst.h>
|
||||
|
||||
/* Bytecode op argument types */
|
||||
|
||||
/* s - a slot */
|
||||
/* c - a constant */
|
||||
/* i - a small integer */
|
||||
/* t - a type (have a simple type for non unions) */
|
||||
/* l - a label */
|
||||
|
||||
typedef enum DstOpArgType DstOpArgType;
|
||||
enum DstOpArgType {
|
||||
DST_OAT_SLOT,
|
||||
DST_OAT_ENVIRONMENT,
|
||||
DST_OAT_CONSTANT,
|
||||
DST_OAT_INTEGER,
|
||||
DST_OAT_TYPE,
|
||||
DST_OAT_SIMPLETYPE,
|
||||
DST_OAT_LABEL
|
||||
};
|
||||
|
||||
/* Convert a slot to to an integer for bytecode */
|
||||
|
||||
/* Types of instructions */
|
||||
/* _0arg - op.---.--.-- (return-nil, noop, vararg arguments)
|
||||
* _s - op.src.--.-- (push1)
|
||||
* _l - op.XX.XX.XX (jump)
|
||||
* _ss - op.dest.XX.XX (move, swap)
|
||||
* _sl - op.check.XX.XX (jump-if)
|
||||
* _st - op.check.TT.TT (typecheck)
|
||||
* _si - op.dest.XX.XX (load-integer)
|
||||
* _sss - op.dest.op1.op2 (add, subtract, arithmetic, comparison)
|
||||
* _ses - op.dest.up.which (load-upvalue, save-upvalue)
|
||||
* _sc - op.dest.CC.CC (load-constant, closure)
|
||||
*/
|
||||
|
||||
/* Various types of instructions */
|
||||
typedef enum DstInstructionType DstInstructionType;
|
||||
enum DstInstructionType {
|
||||
DIT_0, /* No args */
|
||||
DIT_S, /* One slot */
|
||||
DIT_L, /* One label */
|
||||
DIT_SS, /* Two slots */
|
||||
DIT_SL,
|
||||
DIT_ST,
|
||||
DIT_SI,
|
||||
DIT_SU, /* Unsigned */
|
||||
DIT_SSS,
|
||||
DIT_SES,
|
||||
DIT_SC
|
||||
};
|
||||
|
||||
/* Definition for an instruction in the assembler */
|
||||
typedef struct DstInstructionDef DstInstructionDef;
|
||||
struct DstInstructionDef {
|
||||
const char *name;
|
||||
DstInstructionType type;
|
||||
uint8_t opcode;
|
||||
};
|
||||
|
||||
/* Hold all state needed during assembly */
|
||||
typedef struct DstAssembler DstAssembler;
|
||||
struct DstAssembler {
|
||||
DstAssembler *parent;
|
||||
DstFuncDef *def;
|
||||
jmp_buf on_error;
|
||||
const char *errmessage;
|
||||
|
||||
uint32_t environments_capacity;
|
||||
uint32_t bytecode_count; /* Used for calculating labels */
|
||||
|
||||
DstTable labels; /* symbol -> bytecode index */
|
||||
DstTable constants; /* symbol -> constant index */
|
||||
DstTable slots; /* symbol -> slot index */
|
||||
DstTable envs; /* symbol -> environment index */
|
||||
};
|
||||
|
||||
/* Dst opcode descriptions in lexographic order. This
|
||||
* allows a binary search over the elements to find the
|
||||
* correct opcode given a name. This works in reasonable
|
||||
* 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},
|
||||
{"subtract", DIT_SSS, 0x1F},
|
||||
{"syscall", DIT_SU, 0x21},
|
||||
{"tailcall", DIT_S, 0x22},
|
||||
{"transfer", DIT_SSS, 0x23},
|
||||
{"typecheck", DIT_ST, 0x24},
|
||||
};
|
||||
|
||||
/* Compare a DST string to a native 0 terminated c string. Used in the
|
||||
* binary search for the instruction definition. */
|
||||
static int dst_strcompare(const uint8_t *str, const char *other) {
|
||||
uint32_t len = dst_string_length(str);
|
||||
uint32_t index;
|
||||
for (index = 0; index < len; index++) {
|
||||
uint8_t c = str[index];
|
||||
uint8_t k = ((const uint8_t *)other)[index];
|
||||
if (c < k) return -1;
|
||||
if (c > k) return 1;
|
||||
if (k == '\0') break;
|
||||
}
|
||||
return (other[index] == '\0') ? 0 : -1;
|
||||
}
|
||||
|
||||
/* Find an instruction definition given its name */
|
||||
static const DstInstructionDef *dst_findi(const uint8_t *key) {
|
||||
const DstInstructionDef *low = dst_ops;
|
||||
const DstInstructionDef *hi = dst_ops + (sizeof(dst_ops) / sizeof(DstInstructionDef));
|
||||
while (low < hi) {
|
||||
const DstInstructionDef *mid = low + ((hi - low) / 2);
|
||||
int comp = dst_strcompare(key, mid->name);
|
||||
if (comp < 0) {
|
||||
hi = mid;
|
||||
} else if (comp > 0) {
|
||||
low = mid + 1;
|
||||
} else {
|
||||
return mid;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Check a dst string against a bunch of test_strings. Return the
|
||||
* index of the matching test_string, or -1 if not found. */
|
||||
static int strsearch(const uint8_t *str, const char **test_strings) {
|
||||
uint32_t len = dst_string_length(str);
|
||||
int index;
|
||||
for (index = 0; ; index++) {
|
||||
uint32_t i;
|
||||
const char *testword = test_strings[index];
|
||||
if (NULL == testword)
|
||||
break;
|
||||
for (i = 0; i < len; i++) {
|
||||
if (testword[i] != str[i])
|
||||
goto nextword;
|
||||
}
|
||||
return index;
|
||||
nextword:
|
||||
continue;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Deinitialize an Assembler. Does not deinitialize the parents. */
|
||||
static void dst_asm_deinit(DstAssembler *a) {
|
||||
dst_table_deinit(&a->slots);
|
||||
dst_table_deinit(&a->labels);
|
||||
dst_table_deinit(&a->envs);
|
||||
dst_table_deinit(&a->constants);
|
||||
}
|
||||
|
||||
/* Throw some kind of assembly error */
|
||||
static void dst_asm_error(DstAssembler *a, const char *message) {
|
||||
a->errmessage = message;
|
||||
longjmp(a->on_error, 1);
|
||||
}
|
||||
#define dst_asm_assert(a, c, m) do { if (!(c)) dst_asm_error((a), (m)); } while (0)
|
||||
|
||||
/* 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) {
|
||||
DstTable *c;
|
||||
switch (argtype) {
|
||||
case DST_OAT_SLOT:
|
||||
c = &a->slots;
|
||||
break;
|
||||
case DST_OAT_ENVIRONMENT:
|
||||
c = &a->envs;
|
||||
break;
|
||||
case DST_OAT_CONSTANT:
|
||||
c = &a->constants;
|
||||
break;
|
||||
case DST_OAT_INTEGER:
|
||||
c = NULL;
|
||||
break;
|
||||
case DST_OAT_TYPE:
|
||||
case DST_OAT_SIMPLETYPE:
|
||||
c = NULL;
|
||||
break;
|
||||
case DST_OAT_LABEL:
|
||||
c = &a->labels;
|
||||
break;
|
||||
}
|
||||
switch (x.type) {
|
||||
default:
|
||||
break;
|
||||
case DST_INTEGER:
|
||||
return x.as.integer;
|
||||
case DST_TUPLE:
|
||||
{
|
||||
if (argtype == DST_OAT_TYPE) {
|
||||
int64_t result = 0;
|
||||
uint32_t i = 0;
|
||||
for (i = 0; i < dst_tuple_length(x.as.tuple); i++) {
|
||||
result |= doarg_1(a, DST_OAT_SIMPLETYPE, x.as.tuple[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DST_SYMBOL:
|
||||
{
|
||||
if (NULL != c) {
|
||||
DstValue result = dst_table_get(c, x);
|
||||
if (result.type == DST_INTEGER) {
|
||||
if (argtype == DST_OAT_LABEL)
|
||||
return result.as.integer - a->bytecode_count;
|
||||
return result.as.integer;
|
||||
} else {
|
||||
dst_asm_error(a, "unknown name");
|
||||
}
|
||||
} 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");
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
dst_asm_error(a, "unexpected type parsing instruction argument");
|
||||
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) {
|
||||
/* 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");
|
||||
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);
|
||||
}
|
||||
|
||||
/* Provide parsing methods for the different kinds of arguments */
|
||||
static uint32_t read_instruction(DstAssembler *a, const DstInstructionDef *idef, const DstValue *argt) {
|
||||
uint32_t instr = idef->opcode;
|
||||
switch (idef->type) {
|
||||
case DIT_0:
|
||||
{
|
||||
if (dst_tuple_length(argt) != 1)
|
||||
dst_asm_error(a, "expected 0 arguments: (op)");
|
||||
break;
|
||||
}
|
||||
case DIT_S:
|
||||
{
|
||||
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]);
|
||||
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]);
|
||||
break;
|
||||
}
|
||||
case DIT_SS:
|
||||
{
|
||||
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]);
|
||||
break;
|
||||
}
|
||||
case DIT_SL:
|
||||
{
|
||||
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]);
|
||||
break;
|
||||
}
|
||||
case DIT_ST:
|
||||
{
|
||||
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]);
|
||||
break;
|
||||
}
|
||||
case DIT_SI:
|
||||
case DIT_SU:
|
||||
{
|
||||
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]);
|
||||
break;
|
||||
}
|
||||
case DIT_SSS:
|
||||
{
|
||||
if (dst_tuple_length(argt) != 4)
|
||||
dst_asm_error(a, "expected 3 arguments: (op, slot, slot, slot)");
|
||||
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_SLOT, 3, 1, 0, argt[3]);
|
||||
break;
|
||||
}
|
||||
case DIT_SES:
|
||||
{
|
||||
DstAssembler *b = a;
|
||||
uint32_t env;
|
||||
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]);
|
||||
instr |= env << 16;
|
||||
for (env += 1; env > 0; env--) {
|
||||
b = b->parent;
|
||||
if (NULL == b)
|
||||
dst_asm_error(a, "invalid environment index");
|
||||
}
|
||||
instr |= doarg(b, DST_OAT_SLOT, 3, 1, 0, argt[3]);
|
||||
break;
|
||||
}
|
||||
case DIT_SC:
|
||||
{
|
||||
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]);
|
||||
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
|
||||
* if not found. */
|
||||
static int64_t dst_asm_addenv(DstAssembler *a, DstValue envname) {
|
||||
DstValue check;
|
||||
DstFuncDef *def = a->def;
|
||||
uint32_t oldlen;
|
||||
int64_t res;
|
||||
/* Check for memoized value */
|
||||
check = dst_table_get(&a->envs, envname);
|
||||
if (check.type != DST_NIL) {
|
||||
return check.as.integer;
|
||||
}
|
||||
if (NULL == a->parent) {
|
||||
return -1;
|
||||
}
|
||||
res = dst_asm_addenv(a->parent, envname);
|
||||
if (res < 0)
|
||||
return res;
|
||||
oldlen = def->environments_length;
|
||||
dst_table_put(&a->envs, envname, dst_wrap_integer(def->environments_length));
|
||||
if (oldlen >= a->environments_capacity) {
|
||||
uint32_t newcap = 2 + 2 * oldlen;
|
||||
def->environments = realloc(def->environments, newcap * sizeof(uint32_t));
|
||||
if (NULL == def->environments) {
|
||||
DST_OUT_OF_MEMORY;
|
||||
}
|
||||
a->environments_capacity = newcap;
|
||||
}
|
||||
def->environments[def->environments_length++] = (uint32_t) res;
|
||||
return (int64_t) oldlen;
|
||||
}
|
||||
|
||||
/* Helper to assembly. Return the assembled funcdef */
|
||||
static DstFuncDef *dst_asm1(DstAssembler *parent, DstValue src, const char **errout) {
|
||||
DstAssembler a;
|
||||
DstTable *t = src.as.table;
|
||||
DstFuncDef *def;
|
||||
uint32_t count, i;
|
||||
const DstValue *arr;
|
||||
DstValue x;
|
||||
|
||||
/* 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;
|
||||
def->flags = 0;
|
||||
def->slotcount = 0;
|
||||
def->arity = 0;
|
||||
def->constants_length = 0;
|
||||
def->bytecode_length = 0;
|
||||
def->environments_length = 0;
|
||||
|
||||
/* Initialize Assembler */
|
||||
a.def = def;
|
||||
a.parent = parent;
|
||||
a.errmessage = NULL;
|
||||
a.environments_capacity = 0;
|
||||
a.bytecode_count = 0;
|
||||
dst_table_init(&a.labels, 10);
|
||||
dst_table_init(&a.constants, 10);
|
||||
dst_table_init(&a.slots, 10);
|
||||
dst_table_init(&a.envs, 10);
|
||||
|
||||
/* Set error jump */
|
||||
if (setjmp(a.on_error)) {
|
||||
dst_asm_deinit(&a);
|
||||
if (NULL != a.parent) {
|
||||
longjmp(a.parent->on_error, 1);
|
||||
}
|
||||
*errout = a.errmessage;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dst_asm_assert(&a, src.type == DST_TABLE, "expected table for assembly");
|
||||
|
||||
/* Set function arity */
|
||||
x = dst_table_get(t, 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")));
|
||||
if (dst_seq_view(x, &arr, &count)) {
|
||||
def->slotcount = count;
|
||||
for (i = 0; i < count; i++) {
|
||||
DstValue v = arr[i];
|
||||
if (v.type == DST_TUPLE) {
|
||||
uint32_t j;
|
||||
for (j = 0; j < dst_tuple_length(v.as.tuple); j++) {
|
||||
if (v.as.tuple[j].type != DST_SYMBOL)
|
||||
dst_asm_error(&a, "slot names must be symbols");
|
||||
dst_table_put(&a.slots, v.as.tuple[j], dst_wrap_integer(i));
|
||||
}
|
||||
} else if (v.type == DST_SYMBOL) {
|
||||
dst_table_put(&a.slots, v, dst_wrap_integer(i));
|
||||
} else {
|
||||
dst_asm_error(&a, "slot names must be symbols or tuple of symbols");
|
||||
}
|
||||
}
|
||||
} else if (x.type == DST_INTEGER) {
|
||||
def->slotcount = (uint32_t) x.as.integer;
|
||||
} else {
|
||||
dst_asm_error(&a, "slots must be specified");
|
||||
}
|
||||
|
||||
|
||||
/* Create environment aliases */
|
||||
x = dst_table_get(t, 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");
|
||||
if (dst_asm_addenv(&a, arr[i]) < 0) {
|
||||
dst_asm_error(&a, "environment not found");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Parse constants */
|
||||
x = dst_table_get(t, dst_wrap_symbol(dst_cstring("constants")));
|
||||
if (dst_seq_view(x, &arr, &count)) {
|
||||
def->constants_length = count;
|
||||
def->constants = malloc(sizeof(DstValue) * count);
|
||||
if (NULL == def->constants) {
|
||||
DST_OUT_OF_MEMORY;
|
||||
}
|
||||
for (i = 0; i < count; i++) {
|
||||
DstValue ct = arr[i];
|
||||
if (ct.type == DST_TUPLE &&
|
||||
dst_tuple_length(ct.as.tuple) > 1 &&
|
||||
ct.as.tuple[0].type == DST_SYMBOL) {
|
||||
uint32_t tcount = dst_tuple_length(ct.as.tuple);
|
||||
const uint8_t *macro = ct.as.tuple[0].as.string;
|
||||
if (0 == dst_strcompare(macro, "quote")) {
|
||||
def->constants[i] = ct.as.tuple[1];
|
||||
} else if (tcount == 3 &&
|
||||
ct.as.tuple[1].type == DST_SYMBOL &&
|
||||
0 == dst_strcompare(macro, "def")) {
|
||||
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");
|
||||
}
|
||||
/* Todo - parse nested funcdefs */
|
||||
} else {
|
||||
def->constants[i] = ct;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
def->constants = NULL;
|
||||
def->constants_length = 0;
|
||||
}
|
||||
|
||||
/* Parse bytecode and labels */
|
||||
x = dst_table_get(t, dst_wrap_symbol(dst_cstring("bytecode")));
|
||||
if (dst_seq_view(x, &arr, &count)) {
|
||||
/* Do labels and find length */
|
||||
uint32_t blength = 0;
|
||||
for (i = 0; i < count; ++i) {
|
||||
DstValue instr = arr[i];
|
||||
if (instr.type == DST_STRING) {
|
||||
dst_table_put(&a.labels, instr, dst_wrap_integer(blength));
|
||||
} else if (instr.type == DST_TUPLE) {
|
||||
blength++;
|
||||
} else {
|
||||
dst_asm_error(&a, "expected assembly instruction");
|
||||
}
|
||||
}
|
||||
/* Allocate bytecode array */
|
||||
def->bytecode_length = blength;
|
||||
def->bytecode = malloc(sizeof(uint32_t) * blength);
|
||||
if (NULL == def->bytecode) {
|
||||
DST_OUT_OF_MEMORY;
|
||||
}
|
||||
/* Do bytecode */
|
||||
for (i = 0; i < count; ++i) {
|
||||
DstValue instr = arr[i];
|
||||
if (instr.type == DST_STRING) {
|
||||
continue;
|
||||
} else {
|
||||
uint32_t op;
|
||||
const DstInstructionDef *idef;
|
||||
dst_asm_assert(&a, instr.type == DST_TUPLE, "expected tuple");
|
||||
if (dst_tuple_length(instr.as.tuple) == 0) {
|
||||
op = 0;
|
||||
} else {
|
||||
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");
|
||||
op = read_instruction(&a, idef, instr.as.tuple);
|
||||
}
|
||||
def->bytecode[a.bytecode_count++] = op;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
dst_asm_error(&a, "bytecode expected");
|
||||
}
|
||||
|
||||
/* Finish everything and return funcdef */
|
||||
dst_asm_deinit(&a);
|
||||
def->environments =
|
||||
realloc(def->environments, def->environments_length * sizeof(uint32_t));
|
||||
return def;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
@ -20,12 +20,10 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "internal.h"
|
||||
#include <dst/dst.h>
|
||||
|
||||
/* Initialize a buffer */
|
||||
DstBuffer *dst_buffer(Dst *vm, uint32_t capacity) {
|
||||
DstBuffer *buffer = dst_alloc(vm, DST_MEMORY_BUFFER, sizeof(DstBuffer));
|
||||
DstBuffer *dst_buffer_init(DstBuffer *buffer, uint32_t capacity) {
|
||||
uint8_t *data = malloc(sizeof(uint8_t) * capacity);
|
||||
if (NULL == data) {
|
||||
DST_OUT_OF_MEMORY;
|
||||
@ -35,8 +33,19 @@ DstBuffer *dst_buffer(Dst *vm, uint32_t capacity) {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/* Deinitialize a buffer (free data memory) */
|
||||
void dst_buffer_deinit(DstBuffer *buffer) {
|
||||
free(buffer->data);
|
||||
}
|
||||
|
||||
/* Initialize a buffer */
|
||||
DstBuffer *dst_buffer(uint32_t capacity) {
|
||||
DstBuffer *buffer = dst_alloc(DST_MEMORY_BUFFER, sizeof(DstBuffer));
|
||||
return dst_buffer_init(buffer, capacity);
|
||||
}
|
||||
|
||||
/* Ensure that the buffer has enough internal capacity */
|
||||
void dst_buffer_ensure(Dst *vm, DstBuffer *buffer, uint32_t capacity) {
|
||||
void dst_buffer_ensure(DstBuffer *buffer, uint32_t capacity) {
|
||||
uint8_t *newData;
|
||||
uint8_t *old = buffer->data;
|
||||
if (capacity <= buffer->capacity) return;
|
||||
@ -50,7 +59,7 @@ void dst_buffer_ensure(Dst *vm, DstBuffer *buffer, uint32_t capacity) {
|
||||
|
||||
/* Adds capacity for enough extra bytes to the buffer. Ensures that the
|
||||
* next n bytes pushed to the buffer will not cause a reallocation */
|
||||
void dst_buffer_extra(Dst *vm, DstBuffer *buffer, uint32_t n) {
|
||||
void dst_buffer_extra(DstBuffer *buffer, uint32_t n) {
|
||||
uint32_t newCount = buffer->count + n;
|
||||
if (newCount > buffer->capacity) {
|
||||
uint32_t newCapacity = newCount * 2;
|
||||
@ -64,37 +73,37 @@ void dst_buffer_extra(Dst *vm, DstBuffer *buffer, uint32_t n) {
|
||||
}
|
||||
|
||||
/* Push multiple bytes into the buffer */
|
||||
void dst_buffer_push_bytes(Dst *vm, DstBuffer *buffer, const uint8_t *string, uint32_t length) {
|
||||
void dst_buffer_push_bytes(DstBuffer *buffer, const uint8_t *string, uint32_t length) {
|
||||
uint32_t newSize = buffer->count + length;
|
||||
if (newSize > buffer->capacity) {
|
||||
dst_buffer_ensure(vm, buffer, 2 * newSize);
|
||||
dst_buffer_ensure(buffer, 2 * newSize);
|
||||
}
|
||||
dst_memcpy(buffer->data + buffer->count, string, length);
|
||||
memcpy(buffer->data + buffer->count, string, length);
|
||||
buffer->count = newSize;
|
||||
}
|
||||
|
||||
/* Push a cstring to buffer */
|
||||
void dst_buffer_push_cstring(Dst *vm, DstBuffer *buffer, const char *cstring) {
|
||||
void dst_buffer_push_cstring(DstBuffer *buffer, const char *cstring) {
|
||||
uint32_t len = 0;
|
||||
while (cstring[len]) ++len;
|
||||
dst_buffer_push_bytes(vm, buffer, (const uint8_t *) cstring, len);
|
||||
dst_buffer_push_bytes(buffer, (const uint8_t *) cstring, len);
|
||||
}
|
||||
|
||||
/* Push a single byte to the buffer */
|
||||
void dst_buffer_push_u8(Dst *vm, DstBuffer *buffer, uint8_t byte) {
|
||||
void dst_buffer_push_u8(DstBuffer *buffer, uint8_t byte) {
|
||||
uint32_t newSize = buffer->count + 1;
|
||||
if (newSize > buffer->capacity) {
|
||||
dst_buffer_ensure(vm, buffer, 2 * newSize);
|
||||
dst_buffer_ensure(buffer, 2 * newSize);
|
||||
}
|
||||
buffer->data[buffer->count] = byte;
|
||||
buffer->count = newSize;
|
||||
}
|
||||
|
||||
/* Push a 16 bit unsigned integer to the buffer */
|
||||
void dst_buffer_push_u16(Dst *vm, DstBuffer *buffer, uint16_t x) {
|
||||
void dst_buffer_push_u16(DstBuffer *buffer, uint16_t x) {
|
||||
uint32_t newSize = buffer->count + 2;
|
||||
if (newSize > buffer->capacity) {
|
||||
dst_buffer_ensure(vm, buffer, 2 * newSize);
|
||||
dst_buffer_ensure(buffer, 2 * newSize);
|
||||
}
|
||||
buffer->data[buffer->count] = x & 0xFF;
|
||||
buffer->data[buffer->count + 1] = (x >> 8) & 0xFF;
|
||||
@ -102,10 +111,10 @@ void dst_buffer_push_u16(Dst *vm, DstBuffer *buffer, uint16_t x) {
|
||||
}
|
||||
|
||||
/* Push a 32 bit unsigned integer to the buffer */
|
||||
void dst_buffer_push_u32(Dst *vm, DstBuffer *buffer, uint32_t x) {
|
||||
void dst_buffer_push_u32(DstBuffer *buffer, uint32_t x) {
|
||||
uint32_t newSize = buffer->count + 4;
|
||||
if (newSize > buffer->capacity) {
|
||||
dst_buffer_ensure(vm, buffer, 2 * newSize);
|
||||
dst_buffer_ensure(buffer, 2 * newSize);
|
||||
}
|
||||
buffer->data[buffer->count] = x & 0xFF;
|
||||
buffer->data[buffer->count + 1] = (x >> 8) & 0xFF;
|
||||
@ -115,10 +124,10 @@ void dst_buffer_push_u32(Dst *vm, DstBuffer *buffer, uint32_t x) {
|
||||
}
|
||||
|
||||
/* Push a 64 bit unsigned integer to the buffer */
|
||||
void dst_buffer_push_u64(Dst *vm, DstBuffer *buffer, uint64_t x) {
|
||||
void dst_buffer_push_u64(DstBuffer *buffer, uint64_t x) {
|
||||
uint32_t newSize = buffer->count + 8;
|
||||
if (newSize > buffer->capacity) {
|
||||
dst_buffer_ensure(vm, buffer, 2 * newSize);
|
||||
dst_buffer_ensure(buffer, 2 * newSize);
|
||||
}
|
||||
buffer->data[buffer->count] = x & 0xFF;
|
||||
buffer->data[buffer->count + 1] = (x >> 8) & 0xFF;
|
||||
|
84
core/cache.c
84
core/cache.c
@ -20,7 +20,7 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "internal.h"
|
||||
#include <dst/dst.h>
|
||||
#include "cache.h"
|
||||
|
||||
/* All immutable values are cached in a global hash table. When an immutable
|
||||
@ -32,6 +32,12 @@
|
||||
* save memory. Values are removed from the cache when they are garbage collected.
|
||||
*/
|
||||
|
||||
/* Cache state */
|
||||
DstValue *dst_vm_cache;
|
||||
uint32_t dst_vm_cache_capacity;
|
||||
uint32_t dst_vm_cache_count;
|
||||
uint32_t dst_vm_cache_deleted;
|
||||
|
||||
/* Check if two not necesarrily finalized immutable values
|
||||
* are equal. Does caching logic */
|
||||
static int dst_cache_equal(DstValue x, DstValue y) {
|
||||
@ -55,7 +61,7 @@ static int dst_cache_equal(DstValue x, DstValue y) {
|
||||
if (dst_struct_length(x.as.st) != dst_struct_length(y.as.st)) return 0;
|
||||
len = dst_struct_capacity(x.as.st);
|
||||
for (i = 0; i < len; ++i)
|
||||
if (!dst_value_equals(x.as.st[i], y.as.st[i]))
|
||||
if (!dst_equals(x.as.st[i], y.as.st[i]))
|
||||
return 0;
|
||||
return 1;
|
||||
case DST_TUPLE:
|
||||
@ -63,7 +69,7 @@ static int dst_cache_equal(DstValue x, DstValue y) {
|
||||
if (dst_tuple_length(x.as.tuple) != dst_tuple_length(y.as.tuple)) return 0;
|
||||
len = dst_tuple_length(x.as.tuple);
|
||||
for (i = 0; i < len; ++i)
|
||||
if (!dst_value_equals(x.as.tuple[i], y.as.tuple[i]))
|
||||
if (!dst_equals(x.as.tuple[i], y.as.tuple[i]))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
@ -85,29 +91,29 @@ static int dst_cache_strequal(DstValue x, const uint8_t *str, uint32_t len, uint
|
||||
/* Find an item in the cache and return its location.
|
||||
* If the item is not found, return the location
|
||||
* where one would put it. */
|
||||
static DstValue *dst_cache_find(Dst *vm, DstValue key, int *success) {
|
||||
static DstValue *dst_cache_find(DstValue key, int *success) {
|
||||
uint32_t bounds[4];
|
||||
uint32_t i, j, index;
|
||||
uint32_t hash = dst_value_hash(key);
|
||||
uint32_t hash = dst_hash(key);
|
||||
DstValue *firstEmpty = NULL;
|
||||
index = hash % vm->cache_capacity;
|
||||
index = hash % dst_vm_cache_capacity;
|
||||
bounds[0] = index;
|
||||
bounds[1] = vm->cache_capacity;
|
||||
bounds[1] = dst_vm_cache_capacity;
|
||||
bounds[2] = 0;
|
||||
bounds[3] = index;
|
||||
for (j = 0; j < 4; j += 2)
|
||||
for (i = bounds[j]; i < bounds[j+1]; ++i) {
|
||||
DstValue test = vm->cache[i];
|
||||
DstValue test = dst_vm_cache[i];
|
||||
/* Check empty spots */
|
||||
if (test.type == DST_NIL) {
|
||||
if (firstEmpty == NULL)
|
||||
firstEmpty = vm->cache + i;
|
||||
firstEmpty = dst_vm_cache + i;
|
||||
goto notfound;
|
||||
}
|
||||
/* Check for marked deleted - use booleans as deleted */
|
||||
if (test.type == DST_BOOLEAN) {
|
||||
if (firstEmpty == NULL)
|
||||
firstEmpty = vm->cache + i;
|
||||
firstEmpty = dst_vm_cache + i;
|
||||
continue;
|
||||
}
|
||||
if (dst_cache_equal(test, key)) {
|
||||
@ -115,10 +121,10 @@ static DstValue *dst_cache_find(Dst *vm, DstValue key, int *success) {
|
||||
*success = 1;
|
||||
if (firstEmpty != NULL) {
|
||||
*firstEmpty = test;
|
||||
vm->cache[i].type = DST_BOOLEAN;
|
||||
dst_vm_cache[i].type = DST_BOOLEAN;
|
||||
return firstEmpty;
|
||||
}
|
||||
return vm->cache + i;
|
||||
return dst_vm_cache + i;
|
||||
}
|
||||
}
|
||||
notfound:
|
||||
@ -129,7 +135,7 @@ static DstValue *dst_cache_find(Dst *vm, DstValue key, int *success) {
|
||||
/* Find an item in the cache and return its location.
|
||||
* If the item is not found, return the location
|
||||
* where one would put it. Special case of dst_cache_find */
|
||||
DstValue *dst_cache_strfind(Dst *vm,
|
||||
DstValue *dst_cache_strfind(
|
||||
const uint8_t *str,
|
||||
uint32_t len,
|
||||
uint32_t hash,
|
||||
@ -137,24 +143,24 @@ DstValue *dst_cache_strfind(Dst *vm,
|
||||
uint32_t bounds[4];
|
||||
uint32_t i, j, index;
|
||||
DstValue *firstEmpty = NULL;
|
||||
index = hash % vm->cache_capacity;
|
||||
index = hash % dst_vm_cache_capacity;
|
||||
bounds[0] = index;
|
||||
bounds[1] = vm->cache_capacity;
|
||||
bounds[1] = dst_vm_cache_capacity;
|
||||
bounds[2] = 0;
|
||||
bounds[3] = index;
|
||||
for (j = 0; j < 4; j += 2)
|
||||
for (i = bounds[j]; i < bounds[j+1]; ++i) {
|
||||
DstValue test = vm->cache[i];
|
||||
DstValue test = dst_vm_cache[i];
|
||||
/* Check empty spots */
|
||||
if (test.type == DST_NIL) {
|
||||
if (firstEmpty == NULL)
|
||||
firstEmpty = vm->cache + i;
|
||||
firstEmpty = dst_vm_cache + i;
|
||||
goto notfound;
|
||||
}
|
||||
/* Check for marked deleted - use booleans as deleted */
|
||||
if (test.type == DST_BOOLEAN) {
|
||||
if (firstEmpty == NULL)
|
||||
firstEmpty = vm->cache + i;
|
||||
firstEmpty = dst_vm_cache + i;
|
||||
continue;
|
||||
}
|
||||
if (dst_cache_strequal(test, str, len, hash)) {
|
||||
@ -162,10 +168,10 @@ DstValue *dst_cache_strfind(Dst *vm,
|
||||
*success = 1;
|
||||
if (firstEmpty != NULL) {
|
||||
*firstEmpty = test;
|
||||
vm->cache[i].type = DST_BOOLEAN;
|
||||
dst_vm_cache[i].type = DST_BOOLEAN;
|
||||
return firstEmpty;
|
||||
}
|
||||
return vm->cache + i;
|
||||
return dst_vm_cache + i;
|
||||
}
|
||||
}
|
||||
notfound:
|
||||
@ -174,24 +180,24 @@ DstValue *dst_cache_strfind(Dst *vm,
|
||||
}
|
||||
|
||||
/* Resize the cache. */
|
||||
static void dst_cache_resize(Dst *vm, uint32_t newCapacity) {
|
||||
static void dst_cache_resize(uint32_t newCapacity) {
|
||||
uint32_t i, oldCapacity;
|
||||
DstValue *oldCache = vm->cache;
|
||||
DstValue *oldCache = dst_vm_cache;
|
||||
DstValue *newCache = calloc(1, newCapacity * sizeof(DstValue));
|
||||
if (newCache == NULL) {
|
||||
DST_OUT_OF_MEMORY;
|
||||
}
|
||||
oldCapacity = vm->cache_capacity;
|
||||
vm->cache = newCache;
|
||||
vm->cache_capacity = newCapacity;
|
||||
vm->cache_deleted = 0;
|
||||
oldCapacity = dst_vm_cache_capacity;
|
||||
dst_vm_cache = newCache;
|
||||
dst_vm_cache_capacity = newCapacity;
|
||||
dst_vm_cache_deleted = 0;
|
||||
/* Add all of the old cache entries back */
|
||||
for (i = 0; i < oldCapacity; ++i) {
|
||||
int status;
|
||||
DstValue *bucket;
|
||||
DstValue x = oldCache[i];
|
||||
if (x.type != DST_NIL && x.type != DST_BOOLEAN) {
|
||||
bucket = dst_cache_find(vm, x, &status);
|
||||
bucket = dst_cache_find(x, &status);
|
||||
if (status || bucket == NULL) {
|
||||
/* there was a problem with the algorithm. */
|
||||
break;
|
||||
@ -205,36 +211,36 @@ static void dst_cache_resize(Dst *vm, uint32_t newCapacity) {
|
||||
|
||||
/* Add a value to the cache given we know it is not
|
||||
* already in the cache and we have a bucket. */
|
||||
DstValue dst_cache_add_bucket(Dst *vm, DstValue x, DstValue *bucket) {
|
||||
if ((vm->cache_count + vm->cache_deleted) * 2 > vm->cache_capacity) {
|
||||
DstValue dst_cache_add_bucket(DstValue x, DstValue *bucket) {
|
||||
if ((dst_vm_cache_count + dst_vm_cache_deleted) * 2 > dst_vm_cache_capacity) {
|
||||
int status;
|
||||
dst_cache_resize(vm, vm->cache_count * 4);
|
||||
bucket = dst_cache_find(vm, x, &status);
|
||||
dst_cache_resize(dst_vm_cache_count * 4);
|
||||
bucket = dst_cache_find(x, &status);
|
||||
}
|
||||
/* Add x to the cache */
|
||||
vm->cache_count++;
|
||||
dst_vm_cache_count++;
|
||||
*bucket = x;
|
||||
return x;
|
||||
}
|
||||
|
||||
/* Add a value to the cache */
|
||||
DstValue dst_cache_add(Dst *vm, DstValue x) {
|
||||
DstValue dst_cache_add(DstValue x) {
|
||||
int status = 0;
|
||||
DstValue *bucket = dst_cache_find(vm, x, &status);
|
||||
DstValue *bucket = dst_cache_find(x, &status);
|
||||
if (!status) {
|
||||
return dst_cache_add_bucket(vm, x, bucket);
|
||||
return dst_cache_add_bucket(x, bucket);
|
||||
} else {
|
||||
return *bucket;
|
||||
}
|
||||
}
|
||||
|
||||
/* Remove a value from the cache */
|
||||
void dst_cache_remove(Dst *vm, DstValue x) {
|
||||
void dst_cache_remove(DstValue x) {
|
||||
int status = 0;
|
||||
DstValue *bucket = dst_cache_find(vm, x, &status);
|
||||
DstValue *bucket = dst_cache_find(x, &status);
|
||||
if (status) {
|
||||
vm->cache_count--;
|
||||
vm->cache_deleted++;
|
||||
dst_vm_cache_count--;
|
||||
dst_vm_cache_deleted++;
|
||||
bucket->type = DST_BOOLEAN;
|
||||
}
|
||||
}
|
||||
|
@ -24,16 +24,15 @@
|
||||
#define DST_CACHE_H_defined
|
||||
|
||||
#include <dst/dst.h>
|
||||
#include "internal.h"
|
||||
|
||||
DstValue dst_cache_add(Dst *vm, DstValue x);
|
||||
DstValue *dst_cache_strfind(Dst *vm,
|
||||
DstValue dst_cache_add(DstValue x);
|
||||
DstValue *dst_cache_strfind(
|
||||
const uint8_t *str,
|
||||
uint32_t len,
|
||||
uint32_t hash,
|
||||
int *success);
|
||||
DstValue dst_cache_add_bucket(Dst *vm, DstValue x, DstValue *bucket);
|
||||
DstValue dst_cache_add_bucket(DstValue x, DstValue *bucket);
|
||||
|
||||
void dst_cache_remove(Dst *vm, DstValue x);
|
||||
void dst_cache_remove(DstValue x);
|
||||
|
||||
#endif
|
||||
|
298
core/fiber.c
Normal file
298
core/fiber.c
Normal file
@ -0,0 +1,298 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <dst/dst.h>
|
||||
#include "gc.h"
|
||||
|
||||
/* Initialize a new fiber */
|
||||
DstFiber *dst_fiber(DstFunction *func, 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;
|
||||
}
|
||||
fiber->data = data;
|
||||
return dst_fiber_reset(fiber, func);
|
||||
}
|
||||
|
||||
/* 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;
|
||||
return fiber;
|
||||
}
|
||||
|
||||
/* Ensure that the fiber has enough extra capacity */
|
||||
void dst_fiber_setcapacity(DstFiber *fiber, uint32_t n) {
|
||||
DstValue *newData = realloc(fiber->data, sizeof(DstValue) * n);
|
||||
if (NULL == newData) {
|
||||
DST_OUT_OF_MEMORY;
|
||||
}
|
||||
fiber->data = newData;
|
||||
fiber->capacity = n;
|
||||
}
|
||||
|
||||
/* Push a value on the next stack frame */
|
||||
void dst_fiber_push(DstFiber *fiber, DstValue x) {
|
||||
if (fiber->stacktop >= fiber->capacity) {
|
||||
dst_fiber_setcapacity(fiber, 2 * fiber->stacktop);
|
||||
}
|
||||
fiber->data[fiber->stacktop++] = x;
|
||||
}
|
||||
|
||||
/* Push 2 values on the next stack frame */
|
||||
void dst_fiber_push2(DstFiber *fiber, DstValue x, DstValue y) {
|
||||
uint32_t newtop = fiber->stacktop + 2;
|
||||
if (newtop > fiber->capacity) {
|
||||
dst_fiber_setcapacity(fiber, 2 * newtop);
|
||||
}
|
||||
fiber->data[fiber->stacktop] = x;
|
||||
fiber->data[fiber->stacktop + 1] = y;
|
||||
fiber->stacktop = newtop;
|
||||
}
|
||||
|
||||
/* Push 3 values on the next stack frame */
|
||||
void dst_fiber_push3(DstFiber *fiber, DstValue x, DstValue y, DstValue z) {
|
||||
uint32_t newtop = fiber->stacktop + 3;
|
||||
if (newtop > fiber->capacity) {
|
||||
dst_fiber_setcapacity(fiber, 2 * newtop);
|
||||
}
|
||||
fiber->data[fiber->stacktop] = x;
|
||||
fiber->data[fiber->stacktop + 1] = y;
|
||||
fiber->data[fiber->stacktop + 2] = z;
|
||||
fiber->stacktop = newtop;
|
||||
}
|
||||
|
||||
/* Push an array on the next stack frame */
|
||||
void dst_fiber_pushn(DstFiber *fiber, const DstValue *arr, uint32_t n) {
|
||||
uint32_t newtop = fiber->stacktop + n;
|
||||
if (newtop > fiber->capacity) {
|
||||
dst_fiber_setcapacity(fiber, 2 * newtop);
|
||||
}
|
||||
memcpy(fiber->data + fiber->stacktop, arr, n * sizeof(DstValue));
|
||||
fiber->stacktop = newtop;
|
||||
}
|
||||
|
||||
/* Pop a value off of the stack. Will not destroy a current stack frame.
|
||||
* 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) {
|
||||
return dst_wrap_nil();
|
||||
}
|
||||
fiber->stacktop = newstacktop;
|
||||
return fiber->data[newstacktop];
|
||||
}
|
||||
|
||||
/* Push a stack frame to a fiber */
|
||||
void dst_fiber_funcframe(DstFiber *fiber, DstFunction *func) {
|
||||
DstStackFrame *newframe;
|
||||
|
||||
uint32_t i;
|
||||
uint32_t oldframe = fiber->frame;
|
||||
uint32_t nextframe = fiber->frametop + DST_FRAME_SIZE;
|
||||
uint32_t nextframetop = nextframe + func->def->slotcount;
|
||||
uint32_t nextstacktop = nextframetop + DST_FRAME_SIZE;
|
||||
|
||||
if (fiber->capacity < nextstacktop) {
|
||||
dst_fiber_setcapacity(fiber, 2 * nextstacktop);
|
||||
}
|
||||
|
||||
/* Set the next frame */
|
||||
fiber->frame = nextframe;
|
||||
fiber->frametop = nextframetop;
|
||||
newframe = dst_fiber_frame(fiber);
|
||||
|
||||
/* Set up the new frame */
|
||||
newframe->prevframe = oldframe;
|
||||
newframe->pc = func->def->bytecode;
|
||||
newframe->func = func;
|
||||
|
||||
/* Nil unset locals (Needed for gc correctness) */
|
||||
for (i = fiber->stacktop; i < fiber->frametop; ++i) {
|
||||
fiber->data[i].type = DST_NIL;
|
||||
}
|
||||
|
||||
/* Check varargs */
|
||||
if (func->def->flags & DST_FUNCDEF_FLAG_VARARG) {
|
||||
uint32_t tuplehead = fiber->frame + func->def->arity;
|
||||
if (tuplehead >= fiber->stacktop) {
|
||||
fiber->data[tuplehead] = dst_wrap_tuple(dst_tuple_n(NULL, 0));
|
||||
} else {
|
||||
fiber->data[tuplehead] = dst_wrap_tuple(dst_tuple_n(
|
||||
fiber->data + tuplehead,
|
||||
fiber->stacktop - tuplehead));
|
||||
}
|
||||
}
|
||||
|
||||
/* Set stack top */
|
||||
fiber->stacktop = nextstacktop;
|
||||
}
|
||||
|
||||
/* Create a tail frame for a function */
|
||||
void dst_fiber_funcframe_tail(DstFiber *fiber, DstFunction *func) {
|
||||
uint32_t i;
|
||||
uint32_t nextframetop = fiber->frame + func->def->slotcount;
|
||||
uint32_t nextstacktop = nextframetop + DST_FRAME_SIZE;
|
||||
uint32_t size = (fiber->stacktop - fiber->frametop) - DST_FRAME_SIZE;
|
||||
uint32_t argtop = fiber->frame + size;
|
||||
|
||||
if (fiber->capacity < nextstacktop) {
|
||||
dst_fiber_setcapacity(fiber, 2 * nextstacktop);
|
||||
}
|
||||
|
||||
DstValue *stack = fiber->data + fiber->frame;
|
||||
DstValue *args = fiber->data + fiber->frametop + DST_FRAME_SIZE;
|
||||
|
||||
/* Detatch old function */
|
||||
if (NULL != dst_fiber_frame(fiber)->func)
|
||||
dst_function_detach(dst_fiber_frame(fiber)->func);
|
||||
|
||||
memmove(stack, args, size * sizeof(DstValue));
|
||||
|
||||
/* Set stack stuff */
|
||||
fiber->stacktop = nextstacktop;
|
||||
fiber->frametop = nextframetop;
|
||||
|
||||
/* Nil unset locals (Needed for gc correctness) */
|
||||
for (i = fiber->frame + size; i < fiber->frametop; ++i) {
|
||||
fiber->data[i].type = DST_NIL;
|
||||
}
|
||||
|
||||
/* Check varargs */
|
||||
if (func->def->flags & DST_FUNCDEF_FLAG_VARARG) {
|
||||
uint32_t tuplehead = fiber->frame + func->def->arity;
|
||||
if (tuplehead >= argtop) {
|
||||
fiber->data[tuplehead] = dst_wrap_tuple(dst_tuple_n(NULL, 0));
|
||||
} else {
|
||||
fiber->data[tuplehead] = dst_wrap_tuple(dst_tuple_n(
|
||||
fiber->data + tuplehead,
|
||||
argtop - tuplehead));
|
||||
}
|
||||
}
|
||||
|
||||
/* Set frame stuff */
|
||||
dst_fiber_frame(fiber)->func = func;
|
||||
dst_fiber_frame(fiber)->pc = func->def->bytecode;
|
||||
}
|
||||
|
||||
/* Push a stack frame to a fiber for a c function */
|
||||
void dst_fiber_cframe(DstFiber *fiber) {
|
||||
DstStackFrame *newframe;
|
||||
|
||||
uint32_t oldframe = fiber->frame;
|
||||
uint32_t nextframe = fiber->frametop + DST_FRAME_SIZE;
|
||||
uint32_t nextframetop = fiber->stacktop;
|
||||
uint32_t nextstacktop = nextframetop + DST_FRAME_SIZE;
|
||||
|
||||
if (fiber->capacity < nextstacktop) {
|
||||
dst_fiber_setcapacity(fiber, 2 * nextstacktop);
|
||||
}
|
||||
|
||||
/* Set the next frame */
|
||||
fiber->frame = nextframe;
|
||||
fiber->frametop = nextframetop;
|
||||
fiber->stacktop = nextstacktop;
|
||||
newframe = dst_fiber_frame(fiber);
|
||||
|
||||
/* Set up the new frame */
|
||||
newframe->prevframe = oldframe;
|
||||
newframe->pc = NULL;
|
||||
newframe->func = NULL;
|
||||
}
|
||||
|
||||
/* Create a cframe for a tail call */
|
||||
void dst_fiber_cframe_tail(DstFiber *fiber) {
|
||||
uint32_t size = (fiber->stacktop - fiber->frametop) - DST_FRAME_SIZE;
|
||||
uint32_t nextframetop = fiber->frame + size;;
|
||||
uint32_t nextstacktop = nextframetop + DST_FRAME_SIZE;
|
||||
|
||||
DstValue *stack = fiber->data + fiber->frame;
|
||||
DstValue *args = fiber->data + fiber->frametop + DST_FRAME_SIZE;
|
||||
|
||||
/* Detach old function */
|
||||
if (NULL != dst_fiber_frame(fiber)->func)
|
||||
dst_function_detach(dst_fiber_frame(fiber)->func);
|
||||
|
||||
/* Copy pushed args to frame */
|
||||
memmove(stack, args, size * sizeof(DstValue));
|
||||
|
||||
/* Set the next frame */
|
||||
fiber->frametop = nextframetop;
|
||||
fiber->stacktop = nextstacktop;
|
||||
|
||||
/* Set up the new frame */
|
||||
dst_fiber_frame(fiber)->func = NULL;
|
||||
dst_fiber_frame(fiber)->pc = NULL;
|
||||
}
|
||||
|
||||
/* Pop a stack frame from the fiber. Returns the new stack frame, or
|
||||
* NULL if there are no more frames */
|
||||
void dst_fiber_popframe(DstFiber *fiber) {
|
||||
DstStackFrame *frame = dst_fiber_frame(fiber);
|
||||
|
||||
/* Clean up the frame (detach environments) */
|
||||
if (NULL != frame->func)
|
||||
dst_function_detach(frame->func);
|
||||
|
||||
/* Shrink stack */
|
||||
fiber->stacktop = fiber->frame;
|
||||
fiber->frametop = fiber->frame - DST_FRAME_SIZE;
|
||||
fiber->frame = frame->prevframe;
|
||||
}
|
532
core/func.c
532
core/func.c
@ -20,525 +20,21 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <setjmp.h>
|
||||
#include <dst/dst.h>
|
||||
|
||||
#include "internal.h"
|
||||
#include "wrap.h"
|
||||
#include "gc.h"
|
||||
|
||||
/* Bytecode op argument types */
|
||||
|
||||
/* s - a slot */
|
||||
/* c - a constant */
|
||||
/* i - a small integer */
|
||||
/* t - a type (have a simple type for non unions) */
|
||||
/* l - a label */
|
||||
|
||||
typedef enum DstOpArgType DstOpArgType;
|
||||
enum DstOpArgType {
|
||||
DST_OAT_SLOT,
|
||||
DST_OAT_ENVIRONMENT,
|
||||
DST_OAT_CONSTANT,
|
||||
DST_OAT_INTEGER,
|
||||
DST_OAT_TYPE,
|
||||
DST_OAT_SIMPLETYPE,
|
||||
DST_OAT_LABEL
|
||||
}
|
||||
|
||||
/* Convert a slot to to an integer for bytecode */
|
||||
|
||||
/* Types of instructions */
|
||||
/* _0arg - op.---.--.-- (return-nil, noop, vararg arguments)
|
||||
* _s - op.src.--.-- (push1)
|
||||
* _l - op.XX.XX.XX (jump)
|
||||
* _ss - op.dest.XX.XX (move, swap)
|
||||
* _sl - op.check.XX.XX (jump-if)
|
||||
* _st - op.check.TT.TT (typecheck)
|
||||
* _si - op.dest.XX.XX (load-integer)
|
||||
* _sss - op.dest.op1.op2 (add, subtract, arithmetic, comparison)
|
||||
* _ses - op.dest.up.which (load-upvalue, save-upvalue)
|
||||
* _sc - op.dest.CC.CC (load-constant, closure)
|
||||
*/
|
||||
|
||||
/* Various types of instructions */
|
||||
typedef enum DstInstructionType DstInstructionType;
|
||||
enum DstInstructionType {
|
||||
DIT_0, /* No args */
|
||||
DIT_S, /* One slot */
|
||||
DIT_L, /* One label */
|
||||
DIT_SS, /* Two slots */
|
||||
DIT_SL,
|
||||
DIT_ST,
|
||||
DIT_SI,
|
||||
DIT_SSS,
|
||||
DIT_SES,
|
||||
DIT_SC
|
||||
};
|
||||
|
||||
/* Definition for an instruction in the assembler */
|
||||
typedef struct DstInstructionDef DstInstructionDef;
|
||||
struct DstInstructionDef {
|
||||
const char *name;
|
||||
DstInstructionType type;
|
||||
uint8_t opcode;
|
||||
};
|
||||
|
||||
/* Hold all state needed during assembly */
|
||||
typedef struct DstAssembler DstAssembler;
|
||||
struct DstAssembler {
|
||||
DstAssembler *parent;
|
||||
Dst *vm;
|
||||
DstFuncDef *def;
|
||||
DstValue name;
|
||||
jmp_buf onError;
|
||||
|
||||
DstTable *labels; /* symbol -> bytecode index */
|
||||
DstTable *constants; /* symbol -> constant index */
|
||||
DstTable *slots; /* symbol -> slot index */
|
||||
DstTable *envs; /* symbol -> environment index */
|
||||
uint32_t *bytecode; /* Where to put bytecode */
|
||||
uint32_t bytecode_capacity; /* Set once */
|
||||
uint32_t bytecode_count;
|
||||
}
|
||||
|
||||
/* The DST value types in order. These types can be used as
|
||||
* mnemonics instead of a bit pattern for type checking */
|
||||
static const char *types[] = {
|
||||
"nil",
|
||||
"real",
|
||||
"integer",
|
||||
"boolean",
|
||||
"string",
|
||||
"symbol",
|
||||
"array",
|
||||
"tuple",
|
||||
"table",
|
||||
"struct",
|
||||
"thread",
|
||||
"buffer",
|
||||
"function",
|
||||
"cfunction",
|
||||
"userdata"
|
||||
};
|
||||
|
||||
/* Dst opcode descriptions in lexographic order. This
|
||||
* allows a binary search over the elements to find the
|
||||
* correct opcode given a name. This works in reasonable
|
||||
* time is easier to setup statically than a hash table or
|
||||
* prefix tree. */
|
||||
static const char *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, 0x13},
|
||||
{"push", DIT_VARG, 0x14},
|
||||
{"push1", DIT_S, 0x15},
|
||||
{"push2", DIT_SS, 0x16},
|
||||
{"push3", DIT_SSS, 0x17},
|
||||
{"push-array", DIT_S, 0x18},
|
||||
{"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},
|
||||
{"subtract", DIT_SSS, 0x1F},
|
||||
{"swap", DIT_SS, 0x20},
|
||||
{"syscall", DIT_SI, 0x21},
|
||||
{"tail-call", DIT_S, 0x22},
|
||||
{"transfer", DIT_SSS, 0x23},
|
||||
{"typecheck", DIT_ST, 0x24},
|
||||
};
|
||||
|
||||
/* Compare a DST string to a native 0 terminated c string. Used in the
|
||||
* binary search for the instruction definition. */
|
||||
static int dst_strcompare(const uint8_t *str, const char *other) {
|
||||
uint32_t len = dst_string_length(str);
|
||||
int index;
|
||||
for (index = 0; index < len; index++) {
|
||||
uint8_t c = str[index];
|
||||
uint8_t k = ((const uint8_t *)other)[index];
|
||||
if (c < k) return -1;
|
||||
if (c > k) return 1;
|
||||
if (k == '\0') break;
|
||||
}
|
||||
return (other[index] == '\0') ? 0 : -1;
|
||||
}
|
||||
|
||||
/* Find an instruction definition given its name */
|
||||
static DstInstructionDef *dst_findi(const uint8_t *key) {
|
||||
DstInstructionDef *low = dst_ops;
|
||||
DstInstructionDef *hi = dst_ops + (sizeof(dst_ops) / sizeof(DstInstructionDef));
|
||||
while (low < hi) {
|
||||
DstInstructionDef *mid = low + ((hi - low) / 2);
|
||||
int comp = dst_strcompare(key, mid->name);
|
||||
if (comp < 0) {
|
||||
hi = mid;
|
||||
} else if (comp > 0) {
|
||||
low = mid + 1;
|
||||
} else {
|
||||
return mid;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Check a dst string against a bunch of test_strings. Return the
|
||||
* index of the matching test_string, or -1 if not found. */
|
||||
static int strsearch(const uint8_t *str, const char **test_strings) {
|
||||
uint32_t len = dst_string_length(str);
|
||||
int index;
|
||||
for (index = 0; ; index++) {
|
||||
uint32_t i;
|
||||
const char *testword = test_strings[index];
|
||||
if (NULL == testword)
|
||||
break;
|
||||
for (i = 0; i < len; i++) {
|
||||
if (testword[i] != str[i])
|
||||
goto nextword;
|
||||
}
|
||||
return index;
|
||||
:nextword
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Takes some dst assembly and gets the required capacity
|
||||
* for the output bytecode. Does not do any memory allocation, */
|
||||
static uint32_t estimate_capacity(const DstValue *assembly, uint32_t n) {
|
||||
uint32_t i;
|
||||
uint32_t cap = 0;
|
||||
for (i = 0; i < n; i++) {
|
||||
/* Ignore non tuple types, they are labels */
|
||||
if (assembly[i].type != DST_TUPLE) continue;
|
||||
cap++;
|
||||
}
|
||||
return cap;
|
||||
}
|
||||
|
||||
/* Throw some kind of assembly error */
|
||||
static void dst_asm_error(DstAssembler *a, const char *message) {
|
||||
printf("%s\n", message);
|
||||
exit(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) {
|
||||
DstTable *names;
|
||||
switch (argtype) {
|
||||
case DST_OAT_SLOT:
|
||||
c = a->slots;
|
||||
break;
|
||||
case DST_OAT_ENVIRONMENT:
|
||||
c = e->envs;
|
||||
break;
|
||||
case DST_OAT_CONSTANT:
|
||||
c = a->constants;
|
||||
break;
|
||||
case DST_OAT_INTEGER:
|
||||
c = NULL;
|
||||
break;
|
||||
case DST_OAT_TYPE:
|
||||
case DST_OAT_SIMPLETYPE:
|
||||
c = NULL;
|
||||
break;
|
||||
case DST_OAT_LABEL:
|
||||
c = a->labels;
|
||||
break;
|
||||
}
|
||||
switch (x.type) {
|
||||
default:
|
||||
break;
|
||||
case DST_INTEGER:
|
||||
return x.as.integer;
|
||||
case DST_TUPLE:
|
||||
{
|
||||
if (argtype == DST_OAT_TYPE) {
|
||||
int64_t result = 0;
|
||||
uint32_t i = 0;
|
||||
for (i = 0; i < dst_tuple_length(x.as.tuple); i++) {
|
||||
result |= dst_asm_argument(a, DST_OAT_SIMPLETYPE, x.as.tuple[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DST_SYMBOL:
|
||||
{
|
||||
if (NULL != names) {
|
||||
DstValue result = dst_table_get(names, x);
|
||||
if (result.type == DST_INTEGER) {
|
||||
if (argtype == DST_OAT_LABEL)
|
||||
return result.as.integer - a->bytecode_count;
|
||||
return result.as.integer;
|
||||
} else {
|
||||
dst_asm_error(a, "unknown name");
|
||||
}
|
||||
} else if (argtype == DST_OAT_TYPE || argtype == DST_OAT_SIMPLETYPE) {
|
||||
int index = strsearch(x.as.string, types);
|
||||
if (index != -1) {
|
||||
return (int64_t) index;
|
||||
} else {
|
||||
dst_asm_error(a, "unknown type");
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
dst_asm_error(a, "unexpected type parsing instruction argument");
|
||||
}
|
||||
|
||||
/* 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(int nbytes, int hassign, int64_t arg) {
|
||||
/* 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");
|
||||
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(nbytes, hassign, arg1) << (nth << 3);
|
||||
}
|
||||
|
||||
/* Provide parsing methods for the different kinds of arguments */
|
||||
static uint32_t read_instruction(DstAssembler *a, const DstInstructionDef *idef, const DstValue *argt) {
|
||||
uint32_t instr = idef->opcode;
|
||||
switch (idef->type) {
|
||||
case DIT_0:
|
||||
{
|
||||
if (dst_tuple_lenth(argt) != 1)
|
||||
dst_asm_error(a, "expected 0 arguments: (op)");
|
||||
break;
|
||||
}
|
||||
case DIT_S:
|
||||
{
|
||||
if (dst_tuple_lenth(argt) != 2)
|
||||
dst_asm_error(a, "expected 1 argument: (op, slot)");
|
||||
instr |= doarg(a, DST_OAT_SLOT, 3, 3, 0, argt[1]);
|
||||
break;
|
||||
}
|
||||
case DIT_L:
|
||||
{
|
||||
if (dst_tuple_lenth(argt) != 2)
|
||||
dst_asm_error(a, "expected 1 argument: (op, label)");
|
||||
instr |= doarg(a, DST_OAT_LABEL, 3, 3, 1, argt[1]);
|
||||
break;
|
||||
}
|
||||
case DIT_SS:
|
||||
{
|
||||
if (dst_tuple_lenth(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]);
|
||||
break;
|
||||
}
|
||||
case DIT_SL:
|
||||
{
|
||||
if (dst_tuple_lenth(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]);
|
||||
break;
|
||||
}
|
||||
case DIT_ST:
|
||||
{
|
||||
if (dst_tuple_lenth(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]);
|
||||
break;
|
||||
}
|
||||
case DIT_SI:
|
||||
{
|
||||
if (dst_tuple_lenth(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, 1, argt[2]);
|
||||
break;
|
||||
}
|
||||
case DIT_SSS:
|
||||
{
|
||||
if (dst_tuple_lenth(argt) != 4)
|
||||
dst_asm_error(a, "expected 3 arguments: (op, slot, slot, slot)");
|
||||
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_SLOT, 3, 1, 0, argt[3]);
|
||||
break;
|
||||
}
|
||||
case DIT_SES:
|
||||
{
|
||||
DstAssembler *b = a;
|
||||
uint32_t envn;
|
||||
if (dst_tuple_lenth(argt) != 4)
|
||||
dst_asm_error(a, "expected 3 arguments: (op, slot, environment, envslot)");
|
||||
instr |= doarg(a, DST_OAT_SLOT, 1, 1, 0, argt[1]);
|
||||
envn = doarg(a, DST_OAT_ENVIRONMENT, 0, 1, 0, argt[2]);
|
||||
instr |= envn << 16;
|
||||
for (env += 1; env > 0; env--) {
|
||||
b = b->parent;
|
||||
if (NULL == b)
|
||||
dst_asm_error(a, "invalid environment index");
|
||||
}
|
||||
instr |= doarg(b, DST_OAT_SLOT, 3, 1, 0, argt[3]);
|
||||
break;
|
||||
}
|
||||
case DIT_SC:
|
||||
{
|
||||
if (dst_tuple_lenth(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]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return instr;
|
||||
}
|
||||
|
||||
/* Do assembly. Return 0 if successful, else return an error code. */
|
||||
static void dst_asm1(DstAssembler *a, DstValue src) {
|
||||
DstTable *t = src.as.table;
|
||||
DstFuncDef *def = a->def;
|
||||
uint32_t i;
|
||||
DstValue x;
|
||||
|
||||
if (src.type != DST_TABLE)
|
||||
dst_asm_error(a, "expected table");
|
||||
x = dst_table_get(t, dst_wrap_symbol(dst_cstring(a->vm, "arity")));
|
||||
def->arity = x.type == DST_INTEGER ? x.as.integer : 0;
|
||||
x = dst_table_get(t, dst_wrap_symbol(dst_cstring(a->vm, "stack")));
|
||||
def->locals = x.type == DST_INTEGER ? x.as.integer : 0;
|
||||
|
||||
// Check name
|
||||
x = dst_table_get(t, dst_wrap_symbol(dst_cstring(a->vm, "name")));
|
||||
if (x.type == SYMBOL) {
|
||||
a->name = x;
|
||||
}
|
||||
|
||||
// Create slot aliases
|
||||
x = dst_table_get(t, dst_wrap_symbol(dst_cstring(a->vm, "slots")));
|
||||
if (x.type == DST_ARRAY) {
|
||||
for (i = 0; i < x.as.array->count; i++) {
|
||||
DstValue v = x.as.array->data[i];
|
||||
if (v.type == DST_TUPLE) {
|
||||
uint32_t j;
|
||||
for (j = 0; j < dst_tuple_length(v.as.tuple); j++) {
|
||||
if (v.as.tuple[j].type != SYMBOL)
|
||||
dst_asm_error("slot names must be symbols");
|
||||
dst_table_put(a->vm, a->slots, v.as.tuple[j], dst_wrap_integer(i));
|
||||
}
|
||||
} else if (v.type == DST_SYMBOL) {
|
||||
dst_table_put(a->vm, a->slots, v, dst_wrap_integer(i));
|
||||
} else {
|
||||
dst_asm_error(a, "slot names must be symbols or tuple of symbols");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create environment aliases
|
||||
x = dst_table_get(t, dst_wrap_symbol(dst_cstring(a->vm, "environments")));
|
||||
if (x.type == DST_ARRAY) {
|
||||
for (i = 0; i < x.as.array->count; i++) {
|
||||
DstAssembler *b = a->parent;
|
||||
DstValue v = x.as.array->data[i];
|
||||
if (v.type != DST_SYMBOL) {
|
||||
dst_asm_error(a, "expected a symbol");
|
||||
while (NULL != b) {
|
||||
if (dst_equals(b->name, v)) {
|
||||
break;
|
||||
}
|
||||
b = b->parent;
|
||||
}
|
||||
// Check parent assemblers to find the given environment
|
||||
/* If a frame has a closure environment, detach it from
|
||||
* the stack and have it keep its own values */
|
||||
void dst_function_detach(DstFunction *func) {
|
||||
/* Check for closure environment */
|
||||
if (NULL != func->envs && NULL != func->envs[0]) {
|
||||
DstFuncEnv *env = func->envs[0];
|
||||
size_t s = sizeof(DstValue) * env->length;
|
||||
DstValue *vmem = malloc(s);
|
||||
if (NULL == vmem) {
|
||||
DST_OUT_OF_MEMORY;
|
||||
}
|
||||
memcpy(vmem, env->as.fiber->data + env->offset, s);
|
||||
env->offset = 0;
|
||||
env->as.values = vmem;
|
||||
}
|
||||
}
|
||||
|
||||
/* Detach an environment that was on the stack from the stack, and
|
||||
* ensure that its environment persists */
|
||||
void dst_funcenv_detach_(Dst *vm, DstFuncEnv *env) {
|
||||
DstThread *thread = env->thread;
|
||||
DstValue *stack = thread->data + thread->count;
|
||||
uint32_t size = dst_frame_size(stack);
|
||||
DstValue *values = malloc(sizeof(DstValue * size));
|
||||
if (NULL == values) {
|
||||
DST_OUT_OF_MEMORY;
|
||||
}
|
||||
/* Copy stack into env values (the heap) */
|
||||
memcpy(values, stack, sizeof(DstValue) * size);
|
||||
/* Update env */
|
||||
env->thread = NULL;
|
||||
env->stackOffset = size;
|
||||
env->values = values;
|
||||
}
|
||||
|
||||
/* Deinitialize an environment */
|
||||
void dst_funcenv_deinit(DstFuncEnv *env) {
|
||||
if (NULL == env->thread && NULL != env->values) {
|
||||
free(env->values);
|
||||
}
|
||||
}
|
||||
|
||||
/* Create the FuncEnv for the current stack frame. */
|
||||
DstFuncEnv *dst_funcenv_init_(Dst *vm, DstFuncEnv *env, DstThread *thread, DstValue *stack) {
|
||||
env->thread = thread;
|
||||
env->stackOffset = thread->count;
|
||||
env->values = NULL;
|
||||
dst_frame_env(stack) = env;
|
||||
return env;
|
||||
}
|
||||
|
||||
|
||||
/* Create a funcdef */
|
||||
DstFuncDef *dst_funcdef_init_(Dst *vm, DstFuncDef *def) {
|
||||
uint8_t * byteCode = dst_alloc(c->vm, lastNBytes);
|
||||
def->byteCode = (uint16_t *)byteCode;
|
||||
def->byteCodeLen = lastNBytes / 2;
|
||||
/* Copy the last chunk of bytes in the buffer into the new
|
||||
* memory for the function's byteCOde */
|
||||
dst_memcpy(byteCode, buffer->data + buffer->count - lastNBytes, lastNBytes);
|
||||
/* Remove the byteCode from the end of the buffer */
|
||||
buffer->count -= lastNBytes;
|
||||
/* Create the literals used by this function */
|
||||
if (scope->literalsArray->count) {
|
||||
def->literals = dst_alloc(c->vm, scope->literalsArray->count * sizeof(DstValue));
|
||||
dst_memcpy(def->literals, scope->literalsArray->data,
|
||||
scope->literalsArray->count * sizeof(DstValue));
|
||||
} else {
|
||||
def->literals = NULL;
|
||||
}
|
||||
def->literalsLen = scope->literalsArray->count;
|
||||
/* Delete the sub scope */
|
||||
compiler_pop_scope(c);
|
||||
/* Initialize the new FuncDef */
|
||||
def->locals = scope->frameSize;
|
||||
def->arity = arity;
|
||||
def->flags = (varargs ? DST_FUNCDEF_FLAG_VARARG : 0) |
|
||||
(scope->touchParent ? DST_FUNCDEF_FLAG_NEEDSPARENT : 0) |
|
||||
(scope->touchEnv ? DST_FUNCDEF_FLAG_NEEDSENV : 0);
|
||||
return def;
|
||||
}
|
||||
|
230
core/gc.c
230
core/gc.c
@ -20,13 +20,17 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "internal.h"
|
||||
#include <dst/dst.h>
|
||||
#include "cache.h"
|
||||
#include "wrap.h"
|
||||
|
||||
/* GC State */
|
||||
void *dst_vm_blocks;
|
||||
uint32_t dst_vm_memory_interval;
|
||||
uint32_t dst_vm_next_collection;
|
||||
|
||||
/* Helpers for marking the various gc types */
|
||||
static void dst_mark_funcenv(DstFuncEnv *env);
|
||||
static void dst_mark_funcdef(DstFuncEnv *def);
|
||||
static void dst_mark_funcdef(DstFuncDef *def);
|
||||
static void dst_mark_function(DstFunction *func);
|
||||
static void dst_mark_array(DstArray *array);
|
||||
static void dst_mark_table(DstTable *table);
|
||||
@ -34,7 +38,7 @@ static void dst_mark_struct(const DstValue *st);
|
||||
static void dst_mark_tuple(const DstValue *tuple);
|
||||
static void dst_mark_buffer(DstBuffer *buffer);
|
||||
static void dst_mark_string(const uint8_t *str);
|
||||
static void dst_mark_thread(DstThread *thread);
|
||||
static void dst_mark_fiber(DstFiber *fiber);
|
||||
static void dst_mark_udata(void *udata);
|
||||
|
||||
/* Mark a value */
|
||||
@ -49,13 +53,13 @@ void dst_mark(DstValue x) {
|
||||
case DST_STRUCT: dst_mark_struct(x.as.st); break;
|
||||
case DST_TUPLE: dst_mark_tuple(x.as.tuple); break;
|
||||
case DST_BUFFER: dst_mark_buffer(x.as.buffer); break;
|
||||
case DST_STRING: dst_mark_string(x.as.string); break;
|
||||
case DST_THREAD: dst_mark_thread(x.as.thread); break;
|
||||
case DST_FIBER: dst_mark_fiber(x.as.fiber); break;
|
||||
case DST_USERDATA: dst_mark_udata(x.as.pointer); break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Unpin a value */
|
||||
/* Unpin a value. This enables the GC to collect the value's
|
||||
* memory again. */
|
||||
void dst_unpin(DstValue x) {
|
||||
switch (x.type) {
|
||||
default: break;
|
||||
@ -67,13 +71,17 @@ void dst_unpin(DstValue x) {
|
||||
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_STRING: dst_unpin_string(x.as.string); break;
|
||||
case DST_THREAD: dst_unpin_thread(x.as.thread); break;
|
||||
case DST_USERDATA: dst_unpin_udata(x.as.pointer); break;
|
||||
case DST_FIBER: dst_unpin_fiber(x.as.fiber); break;
|
||||
case DST_USERDATA: dst_unpin_userdata(x.as.pointer); break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Pin a value */
|
||||
/* 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
|
||||
* creates a table, and then runs the garbage collector without
|
||||
* ever saving the table anywhere (except on the c stack, which
|
||||
* the gc cannot inspect). */
|
||||
void dst_pin(DstValue x) {
|
||||
switch (x.type) {
|
||||
default: break;
|
||||
@ -85,86 +93,84 @@ void dst_pin(DstValue x) {
|
||||
case DST_STRUCT: dst_pin_struct(x.as.st); break;
|
||||
case DST_TUPLE: dst_pin_tuple(x.as.tuple); break;
|
||||
case DST_BUFFER: dst_pin_buffer(x.as.buffer); break;
|
||||
case DST_STRING: dst_pin_string(x.as.string); break;
|
||||
case DST_THREAD: dst_pin_thread(x.as.thread); break;
|
||||
case DST_USERDATA: dst_pin_udata(x.as.pointer); break;
|
||||
case DST_FIBER: dst_pin_fiber(x.as.fiber); break;
|
||||
case DST_USERDATA: dst_pin_userdata(x.as.pointer); break;
|
||||
}
|
||||
}
|
||||
|
||||
static void dst_mark_string(const uint8_t *str) {
|
||||
gc_mark(dst_string_raw(str));
|
||||
dst_gc_mark(dst_string_raw(str));
|
||||
}
|
||||
|
||||
static void dst_mark_buffer(DstBuffer *buffer) {
|
||||
gc_mark(buffer);
|
||||
dst_gc_mark(buffer);
|
||||
}
|
||||
|
||||
static void dst_mark_udata(void *udata) {
|
||||
gc_mark(dst_udata_header(udata));
|
||||
dst_gc_mark(dst_userdata_header(udata));
|
||||
}
|
||||
|
||||
/* Mark a bunch of items in memory */
|
||||
static void dst_mark_many(const DstValue *values, uint32_t n) {
|
||||
const DstValue *end = values + n;
|
||||
while (values < end) {
|
||||
dst_mark(*values)
|
||||
++values;
|
||||
dst_mark(*values);
|
||||
values += 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void dst_mark_array(DstArray *array) {
|
||||
if (gc_reachable(array))
|
||||
if (dst_gc_reachable(array))
|
||||
return;
|
||||
gc_mark(array);
|
||||
dst_gc_mark(array);
|
||||
dst_mark_many(array->data, array->count);
|
||||
}
|
||||
|
||||
static void dst_mark_table(DstTable *table) {
|
||||
if (gc_reachable(table))
|
||||
if (dst_gc_reachable(table))
|
||||
return;
|
||||
gc_mark(table);
|
||||
dst_gc_mark(table);
|
||||
dst_mark_many(table->data, table->capacity);
|
||||
}
|
||||
|
||||
static void dst_mark_struct(const DstValue *st) {
|
||||
if (gc_reachable(dst_struct_raw(st)))
|
||||
if (dst_gc_reachable(dst_struct_raw(st)))
|
||||
return;
|
||||
gc_mark(dst_struct_raw(st));
|
||||
dst_gc_mark(dst_struct_raw(st));
|
||||
dst_mark_many(st, dst_struct_capacity(st));
|
||||
}
|
||||
|
||||
static void dst_mark_tuple(const DstValue *tuple) {
|
||||
if (gc_reachable(dst_tuple_raw(tuple)))
|
||||
if (dst_gc_reachable(dst_tuple_raw(tuple)))
|
||||
return;
|
||||
gc_mark(dst_tuple_raw(tuple));
|
||||
dst_mark_many(tuple, dst_tuple_count(tuple));
|
||||
dst_gc_mark(dst_tuple_raw(tuple));
|
||||
dst_mark_many(tuple, dst_tuple_length(tuple));
|
||||
}
|
||||
|
||||
/* Helper to mark function environments */
|
||||
static void dst_mark_funcenv(DstFuncEnv *env) {
|
||||
if (gc_reachable(env))
|
||||
if (dst_gc_reachable(env))
|
||||
return;
|
||||
gc_mark(env);
|
||||
if (env->values) {
|
||||
uint32_t count = env->stackOffset;
|
||||
uint32_t i;
|
||||
for (i = 0; i < count; ++i)
|
||||
dst_mark_value(env->values[i]);
|
||||
dst_gc_mark(env);
|
||||
if (env->offset) {
|
||||
/* On stack */
|
||||
dst_mark_fiber(env->as.fiber);
|
||||
} else {
|
||||
/* Not on stack */
|
||||
dst_mark_many(env->as.values, env->length);
|
||||
}
|
||||
if (env->thread)
|
||||
dst_mark_thread(env->thread);
|
||||
}
|
||||
|
||||
/* GC helper to mark a FuncDef */
|
||||
static void dst_mark_funcdef(DstFuncDef *def) {
|
||||
uint32_t count, i;
|
||||
if (gc_reachable(def))
|
||||
if (dst_gc_reachable(def))
|
||||
return;
|
||||
gc_mark(def);
|
||||
if (def->literals) {
|
||||
count = def->literalsLen;
|
||||
dst_gc_mark(def);
|
||||
if (def->constants) {
|
||||
count = def->constants_length;
|
||||
for (i = 0; i < count; ++i) {
|
||||
DstValue v = def->literals[i];
|
||||
DstValue v = def->constants[i];
|
||||
/* Funcdefs use boolean literals to store other funcdefs */
|
||||
if (v.type == DST_BOOLEAN) {
|
||||
dst_mark_funcdef((DstFuncDef *) v.as.pointer);
|
||||
@ -178,93 +184,91 @@ static void dst_mark_funcdef(DstFuncDef *def) {
|
||||
static void dst_mark_function(DstFunction *func) {
|
||||
uint32_t i;
|
||||
uint32_t numenvs;
|
||||
if (gc_reachable(func))
|
||||
if (dst_gc_reachable(func))
|
||||
return;
|
||||
gc_mark(func)
|
||||
numenvs = fun->def->envLen;
|
||||
dst_gc_mark(func);
|
||||
numenvs = func->def->environments_length;
|
||||
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);
|
||||
}
|
||||
|
||||
/* Helper to mark a stack frame. Returns the next stackframe. */
|
||||
static DstValue *dst_mark_stackframe(Dst *vm, DstValue *stack) {
|
||||
dst_mark(dst_frame_callee(stack));
|
||||
if (dst_frame_env(stack) != NULL)
|
||||
dst_mark_funcenv(dst_frame_env(stack));
|
||||
/* Mark all values in the stack frame */
|
||||
dst_mark_many(stack, dst_frame_size(stack));
|
||||
/* Return the nexct frame */
|
||||
return stack + dst_frame_size(stack) + DST_FRAME_SIZE;
|
||||
}
|
||||
|
||||
static void dst_mark_thread(DstThread *thread) {
|
||||
DstValue *frame = thread->data + DST_FRAME_SIZE;
|
||||
DstValue *end = thread->data + thread->count;
|
||||
if (gc_reachable(thread))
|
||||
static void dst_mark_fiber(DstFiber *fiber) {
|
||||
uint32_t i, j;
|
||||
DstStackFrame *frame;
|
||||
if (dst_gc_reachable(fiber))
|
||||
return;
|
||||
gc_mark(thread);
|
||||
while (frame <= end)
|
||||
frame = dst_mark_stackframe(vm, frame);
|
||||
if (thread->parent)
|
||||
dst_mark_thread(thread->parent);
|
||||
dst_gc_mark(fiber);
|
||||
|
||||
i = fiber->frame;
|
||||
j = fiber->frametop;
|
||||
while (i != 0) {
|
||||
frame = (DstStackFrame *)(fiber->data + i - DST_FRAME_SIZE);
|
||||
if (NULL != frame->func)
|
||||
dst_mark_function(frame->func);
|
||||
/* Mark all values in the stack frame */
|
||||
dst_mark_many(fiber->data + i, j - i);
|
||||
j = i - DST_FRAME_SIZE;
|
||||
i = frame->prevframe;
|
||||
}
|
||||
|
||||
if (NULL != fiber->parent)
|
||||
dst_mark_fiber(fiber->parent);
|
||||
|
||||
dst_mark(fiber->ret);
|
||||
}
|
||||
|
||||
/* Deinitialize a block of memory */
|
||||
static void dst_deinit_block(Dst *vm, GCMemoryHeader *block) {
|
||||
static void dst_deinit_block(DstGCMemoryHeader *block) {
|
||||
void *mem = ((char *)(block + 1));
|
||||
DstUserdataHeader *h = (DstUserdataHeader *)mem;
|
||||
void *smem = mem + 2 * sizeof(uint32_t);
|
||||
switch (current->tags) {
|
||||
switch (block->flags & DST_MEM_TYPEBITS) {
|
||||
default:
|
||||
break; /* Do nothing for non gc types */
|
||||
case DST_MEMORY_STRING:
|
||||
dst_cache_remove(vm, dst_wrap_string(smem));
|
||||
dst_cache_remove(dst_wrap_string(smem));
|
||||
break;
|
||||
case DST_MEMORY_ARRAY:
|
||||
free(((DstArray*) mem)->data);
|
||||
break;
|
||||
case DST_MEMORY_TUPLE:
|
||||
dst_cache_remove(vm, dst_wrap_tuple(smem));
|
||||
dst_cache_remove(dst_wrap_tuple(smem));
|
||||
break;
|
||||
case DST_MEMORY_TABLE:
|
||||
free(((DstTable*) mem)->data);
|
||||
break;
|
||||
case DST_MEMORY_STRUCT:
|
||||
dst_cache_remove(vm, dst_wrap_struct(smem));
|
||||
dst_cache_remove(dst_wrap_struct(smem));
|
||||
break;
|
||||
case DST_MEMORY_THREAD:
|
||||
free(((DstThread *) mem)->data);
|
||||
case DST_MEMORY_FIBER:
|
||||
free(((DstFiber *) mem)->data);
|
||||
break;
|
||||
case DST_MEMORY_BUFFER:
|
||||
free(((DstBuffer *) mem)->data);
|
||||
break;
|
||||
case DST_MEMORY_FUNCTION:
|
||||
{
|
||||
DstFunction *f = (DstFunction *)mem;
|
||||
if (NULL != f->envs)
|
||||
free(f->envs);
|
||||
}
|
||||
free(((DstFunction *)mem)->envs);
|
||||
break;
|
||||
case DST_MEMORY_USERDATA:
|
||||
if (h->type->finalize)
|
||||
h->type->finalize(vm, (void *)(h + 1), h->size);
|
||||
h->type->finalize((void *)(h + 1), h->size);
|
||||
break;
|
||||
case DST_MEMORY_FUNCENV:
|
||||
{
|
||||
DstFuncEnv *env = (DstFuncEnv *)mem;
|
||||
if (NULL == env->thread && NULL != env->values)
|
||||
free(env->values);
|
||||
if (0 == env->offset)
|
||||
free(env->as.values);
|
||||
}
|
||||
break;
|
||||
case DST_MEMORY_FUNCDEF:
|
||||
{
|
||||
DstFunDef *def = (DstFuncDef *)mem;
|
||||
DstFuncDef *def = (DstFuncDef *)mem;
|
||||
/* TODO - get this all with one alloc and one free */
|
||||
free(def->envSizes);
|
||||
free(def->envCaptures);
|
||||
free(def->literals);
|
||||
free(def->byteCode);
|
||||
free(def->environments);
|
||||
free(def->constants);
|
||||
free(def->bytecode);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -272,20 +276,20 @@ static void dst_deinit_block(Dst *vm, GCMemoryHeader *block) {
|
||||
|
||||
/* Iterate over all allocated memory, and free memory that is not
|
||||
* marked as reachable. Flip the gc color flag for next sweep. */
|
||||
void dst_sweep(Dst *vm) {
|
||||
GCMemoryHeader *previous = NULL;
|
||||
GCMemoryHeader *current = vm->blocks;
|
||||
GCMemoryHeader *next;
|
||||
void dst_sweep() {
|
||||
DstGCMemoryHeader *previous = NULL;
|
||||
DstGCMemoryHeader *current = dst_vm_blocks;
|
||||
DstGCMemoryHeader *next;
|
||||
while (current) {
|
||||
next = current->next;
|
||||
if (current->flags & (DST_MEM_REACHABLE | DST_MEM_DISABLED)) {
|
||||
previous = current;
|
||||
} else {
|
||||
dst_deinit_block(vm, current);
|
||||
dst_deinit_block(current);
|
||||
if (previous) {
|
||||
previous->next = next;
|
||||
} else {
|
||||
vm->blocks = next;
|
||||
dst_vm_blocks = next;
|
||||
}
|
||||
free(current);
|
||||
}
|
||||
@ -295,49 +299,45 @@ void dst_sweep(Dst *vm) {
|
||||
}
|
||||
|
||||
/* Allocate some memory that is tracked for garbage collection */
|
||||
void *dst_alloc(Dst *vm, DstMemoryType type, size_t size) {
|
||||
GCMemoryHeader *mdata;
|
||||
size_t totalSize = size + sizeof(GCMemoryHeader);
|
||||
void *mem = malloc(totalSize);
|
||||
void *dst_alloc(DstMemoryType type, size_t size) {
|
||||
DstGCMemoryHeader *mdata;
|
||||
size_t total = size + sizeof(DstGCMemoryHeader);
|
||||
void *mem = malloc(total);
|
||||
|
||||
/* Check for bad malloc */
|
||||
if (NULL == mem) {
|
||||
DST_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
mdata = (GCMemoryHeader *)rawBlock;
|
||||
mdata = (DstGCMemoryHeader *)mem;
|
||||
|
||||
/* Configure block */
|
||||
mdata->flags = type;
|
||||
|
||||
/* Prepend block to heap list */
|
||||
vm->nextCollection += size;
|
||||
mdata->next = vm->blocks;
|
||||
vm->blocks = mdata;
|
||||
dst_vm_next_collection += size;
|
||||
mdata->next = dst_vm_blocks;
|
||||
dst_vm_blocks = mdata;
|
||||
|
||||
return mem + sizeof(GCMemoryHeader);
|
||||
return mem + sizeof(DstGCMemoryHeader);
|
||||
}
|
||||
|
||||
/* Run garbage collection */
|
||||
void dst_collect(Dst *vm) {
|
||||
if (vm->thread)
|
||||
dst_mark_thread(vm->thread);
|
||||
dst_mark_table(vm->modules);
|
||||
dst_mark_table(vm->registry);
|
||||
dst_mark_table(vm->env);
|
||||
dst_mark(vm->ret);
|
||||
dst_sweep(vm);
|
||||
vm->nextCollection = 0;
|
||||
void dst_collect() {
|
||||
if (dst_vm_fiber)
|
||||
dst_mark_fiber(dst_vm_fiber);
|
||||
dst_sweep();
|
||||
dst_vm_next_collection = 0;
|
||||
}
|
||||
|
||||
/* Free all allocated memory */
|
||||
void dst_clear_memory(Dst *vm) {
|
||||
GCMemoryHeader *current = vm->blocks;
|
||||
void dst_clear_memory() {
|
||||
DstGCMemoryHeader *current = dst_vm_blocks;
|
||||
while (current) {
|
||||
dst_deinit_block(vm, current);
|
||||
GCMemoryHeader *next = current->next;
|
||||
dst_deinit_block(current);
|
||||
DstGCMemoryHeader *next = current->next;
|
||||
free(current);
|
||||
current = next;
|
||||
}
|
||||
vm->blocks = NULL;
|
||||
dst_vm_blocks = NULL;
|
||||
}
|
||||
|
120
core/gc.h
120
core/gc.h
@ -1,120 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef DST_GC_H_defined
|
||||
#define DST_GC_H_defined
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
/* The metadata header associated with an allocated block of memory */
|
||||
#define gc_header(mem) ((GCMemoryHeader *)(mem) - 1)
|
||||
|
||||
#define DST_MEM_REACHABLE 0x100
|
||||
#define DST_MEM_DISABLED 0x200
|
||||
|
||||
#define gc_settype(m, t) ((gc_header(m)->flags |= (0xFF & (t))))
|
||||
#define gc_type(m) (gc_header(m)->flags & 0xFF)
|
||||
|
||||
#define gc_mark(m) (gc_header(m)->flags |= DST_MEM_REACHABLE)
|
||||
#define gc_unmark(m) (gc_header(m)->flags &= ~DST_MEM_COLOR)
|
||||
#define gc_reachable(m) (gc_header(m)->flags & DST_MEM_REACHABLE)
|
||||
|
||||
|
||||
/* Memory header struct. Node of a linked list of memory blocks. */
|
||||
typedef struct GCMemoryHeader GCMemoryHeader;
|
||||
struct GCMemoryHeader {
|
||||
GCMemoryHeader *next;
|
||||
uint32_t flags;
|
||||
};
|
||||
|
||||
/* Memory types for the GC. Different from DstType to include funcenv and funcdef. */
|
||||
typedef enum DstMemoryType DstMemoryType;
|
||||
enum DstMemoryType {
|
||||
DST_MEMORY_NONE,
|
||||
DST_MEMORY_STRING,
|
||||
DST_MEMORY_ARRAY,
|
||||
DST_MEMORY_TUPLE,
|
||||
DST_MEMORY_TABLE,
|
||||
DST_MEMORY_STRUCT,
|
||||
DST_MEMORY_THREAD,
|
||||
DST_MEMORY_BUFFER,
|
||||
DST_MEMORY_FUNCTION,
|
||||
DST_MEMORY_USERDATA,
|
||||
DST_MEMORY_FUNCENV,
|
||||
DST_MEMORY_FUNCDEF
|
||||
}
|
||||
|
||||
/* Prevent GC from freeing some memory. */
|
||||
#define dst_disablegc(m) (gc_header(m)->flags |= DST_MEM_DISABLED, (m))
|
||||
|
||||
/* To allocate collectable memory, one must calk dst_alloc, initialize the memory,
|
||||
* and then call when dst_enablegc when it is initailize and reachable by the gc (on the DST stack) */
|
||||
void *dst_alloc(Dst *vm, DstMemoryType type, size_t size);
|
||||
#define dst_enablegc(m) (gc_header(m)->flags &= ~DST_MEM_DISABLED, (m))
|
||||
|
||||
/* When doing C interop, it is often needed to disable GC on a value.
|
||||
* This is needed when a garbage collection could occur in the middle
|
||||
* of a c function. This could happen, for example, if one calls back
|
||||
* into dst inside of a c function. The pin and unpin functions toggle
|
||||
* garbage collection on a value when needed. Note that no dst functions
|
||||
* will call gc when you don't want it to. GC only happens automatically
|
||||
* in the interpreter loop. */
|
||||
void dst_pin(DstValue x);
|
||||
void dst_unpin(DstValue x);
|
||||
|
||||
/* Specific types can also be pinned and unpinned as well. */
|
||||
#define dst_pin_table dst_disablegc
|
||||
#define dst_pin_array dst_disablegc
|
||||
#define dst_pin_buffer dst_disablegc
|
||||
#define dst_pin_function dst_disablegc
|
||||
#define dst_pin_thread dst_disablegc
|
||||
#define dst_pin_string(s) dst_disablegc(dst_string_raw(s))
|
||||
#define dst_pin_symbol(s) dst_disablegc(dst_string_raw(s))
|
||||
#define dst_pin_tuple(s) dst_disablegc(dst_tuple_raw(s))
|
||||
#define dst_pin_struct(s) dst_disablegc(dst_struct_raw(s))
|
||||
#define dst_pin_userdata(s) dst_disablegc(dst_userdata_header(s))
|
||||
|
||||
#define dst_unpin_table dst_enablegc
|
||||
#define dst_unpin_array dst_enablegc
|
||||
#define dst_unpin_buffer dst_enablegc
|
||||
#define dst_unpin_function dst_enablegc
|
||||
#define dst_unpin_thread dst_enablegc
|
||||
#define dst_unpin_string(s) dst_enablegc(dst_string_raw(s))
|
||||
#define dst_unpin_symbol(s) dst_enablegc(dst_string_raw(s))
|
||||
#define dst_unpin_tuple(s) dst_enablegc(dst_tuple_raw(s))
|
||||
#define dst_unpin_struct(s) dst_enablegc(dst_struct_raw(s))
|
||||
#define dst_unpin_userdata(s) dst_enablegc(dst_userdata_header(s))
|
||||
|
||||
void dst_mark(DstValue x);
|
||||
void dst_sweep(Dst *vm);
|
||||
|
||||
/* Collect some memory */
|
||||
void dst_collect(Dst *vm);
|
||||
|
||||
/* Clear all memory. */
|
||||
void dst_clear_memory(Dst *vm);
|
||||
|
||||
/* Run garbage collection if needed */
|
||||
#define dst_maybe_collect(Dst *vm) do {\
|
||||
if (vm->nextCollection >= vm->memoryInterval) dst_collect(vm); } while (0)
|
||||
|
||||
#endif
|
405
core/internal.h
405
core/internal.h
@ -1,405 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef DST_INTERNAL_H_defined
|
||||
#define DST_INTERNAL_H_defined
|
||||
|
||||
#include <dst/dst.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/* String utils */
|
||||
#define dst_string_raw(s) ((uint32_t *)(s) - 2)
|
||||
#define dst_string_length(s) (dst_string_raw(s)[0])
|
||||
#define dst_string_hash(s) (dst_string_raw(s)[1])
|
||||
|
||||
/* Tuple utils */
|
||||
#define dst_tuple_raw(t) ((uint32_t *)(t) - 2)
|
||||
#define dst_tuple_length(t) (dst_tuple_raw(t)[0])
|
||||
#define dst_tuple_hash(t) (dst_tuple_raw(t)[1])
|
||||
|
||||
/* Struct utils */
|
||||
#define dst_struct_raw(t) ((uint32_t *)(t) - 2)
|
||||
#define dst_struct_length(t) (dst_struct_raw(t)[0])
|
||||
#define dst_struct_capacity(t) (dst_struct_length(t) * 4)
|
||||
#define dst_struct_hash(t) (dst_struct_raw(t)[1])
|
||||
|
||||
/* Userdata utils */
|
||||
#define dst_udata_header(u) ((DstUserdataHeader *)(u) - 1)
|
||||
#define dst_udata_type(u) (dst_udata_header(u)->type)
|
||||
#define dst_udata_size(u) (dst_udata_header(u)->size)
|
||||
|
||||
/* Stack frame manipulation */
|
||||
|
||||
/* Size of stack frame in number of values */
|
||||
#define DST_FRAME_SIZE 5
|
||||
|
||||
/* Prevent some recursive functions from recursing too deeply
|
||||
* ands crashing. */
|
||||
#define DST_RECURSION_GUARD 1000
|
||||
|
||||
/* Macros for referencing a stack frame given a stack */
|
||||
#define dst_frame_callee(s) (*(s - 1))
|
||||
#define dst_frame_size(s) ((s - 2)->data.dwords[0])
|
||||
#define dst_frame_prevsize(s) ((s - 2)->data.dwords[1])
|
||||
#define dst_frame_args(s) ((s - 3)->data.dwords[0])
|
||||
#define dst_frame_ret(s) ((s - 3)->data.dwords[1])
|
||||
#define dst_frame_pc(s) ((s - 4)->data.u16p)
|
||||
#define dst_frame_env(s) ((s - 5)->data.env)
|
||||
|
||||
/* C function helpers */
|
||||
|
||||
/* Return in a c function */
|
||||
#define dst_c_return(vm, x) do { (vm)->ret = (x); return DST_RETURN_OK; } while (0)
|
||||
|
||||
/* Throw error from a c function */
|
||||
#define dst_c_throw(vm, e) do { (vm)->ret = (e); return DST_RETURN_ERROR; } while (0)
|
||||
|
||||
/* Throw c string error from a c function */
|
||||
#define dst_c_throwc(vm, e) dst_c_throw((vm), dst_string_cv((vm), (e)))
|
||||
|
||||
/* Assert from a c function */
|
||||
#define dst_c_assert(vm, cond, e) do { if (cond) dst_c_throw((vm), (e)); } while (0)
|
||||
|
||||
/* What to do when out of memory */
|
||||
#ifndef DST_OUT_OF_MEMORY
|
||||
#include <stdio.h>
|
||||
#define DST_OUT_OF_MEMORY do { printf("out of memory\n"); exit(1); } while (0)
|
||||
#endif
|
||||
|
||||
/* A general dst value type */
|
||||
typedef struct DstValue DstValue;
|
||||
|
||||
/* All of the dst types */
|
||||
typedef int DstBoolean;
|
||||
typedef struct DstFunction DstFunction;
|
||||
typedef struct DstArray DstArray;
|
||||
typedef struct DstBuffer DstBuffer;
|
||||
typedef struct DstTable DstTable;
|
||||
typedef struct DstThread DstThread;
|
||||
|
||||
/* Other structs */
|
||||
typedef struct DstUserdataHeader DstUserdataHeader;
|
||||
typedef struct DstFuncDef DstFuncDef;
|
||||
typedef struct DstFuncEnv DstFuncEnv;
|
||||
typedef union DstValueUnion DstValueUnion;
|
||||
typedef struct DstUserType DstUserType;
|
||||
typedef struct DstParser DstParser;
|
||||
typedef struct DstParseState DstParseState;
|
||||
|
||||
/* Union datatype */
|
||||
union DstValueUnion {
|
||||
DstBoolean boolean;
|
||||
double real;
|
||||
int64_t integer;
|
||||
DstArray *array;
|
||||
DstBuffer *buffer;
|
||||
DstTable *table;
|
||||
DstThread *thread;
|
||||
const DstValue *tuple;
|
||||
DstCFunction cfunction;
|
||||
DstFunction *function;
|
||||
const DstValue *st;
|
||||
const uint8_t *string;
|
||||
/* Indirectly used union members */
|
||||
uint16_t *u16p;
|
||||
uint32_t dwords[2];
|
||||
uint16_t words[4];
|
||||
uint8_t bytes[8];
|
||||
void *pointer;
|
||||
const char *cstring;
|
||||
};
|
||||
|
||||
/* The general dst value type. Contains a large union and
|
||||
* the type information of the value */
|
||||
struct DstValue {
|
||||
DstType type;
|
||||
DstValueUnion as;
|
||||
};
|
||||
|
||||
/* A lightweight green thread in dst. Does not correspond to
|
||||
* operating system threads. */
|
||||
struct DstThread {
|
||||
uint32_t count;
|
||||
uint32_t capacity;
|
||||
DstValue *data;
|
||||
DstThread *parent;
|
||||
enum {
|
||||
DST_THREAD_PENDING = 0,
|
||||
DST_THREAD_ALIVE,
|
||||
DST_THREAD_DEAD,
|
||||
DST_THREAD_ERROR
|
||||
} status;
|
||||
};
|
||||
|
||||
/* A dynamic array type. */
|
||||
struct DstArray {
|
||||
uint32_t count;
|
||||
uint32_t capacity;
|
||||
DstValue *data;
|
||||
};
|
||||
|
||||
/* A bytebuffer type. Used as a mutable string or string builder. */
|
||||
struct DstBuffer {
|
||||
uint32_t count;
|
||||
uint32_t capacity;
|
||||
uint8_t *data;
|
||||
};
|
||||
|
||||
/* A mutable associative data type. Backed by a hashtable. */
|
||||
struct DstTable {
|
||||
uint32_t count;
|
||||
uint32_t capacity;
|
||||
uint32_t deleted;
|
||||
DstValue *data;
|
||||
};
|
||||
|
||||
/* Some function defintion flags */
|
||||
#define DST_FUNCDEF_FLAG_VARARG 1
|
||||
#define DST_FUNCDEF_FLAG_NEEDSENV 4
|
||||
|
||||
/* A function definition. Contains information needed to instantiate closures. */
|
||||
struct DstFuncDef {
|
||||
uint32_t flags;
|
||||
uint32_t locals; /* The amount of stack space required for the function */
|
||||
uint32_t arity; /* Not including varargs */
|
||||
uint32_t literalsLen; /* Number of literals */
|
||||
uint32_t byteCodeLen; /* Length of bytcode */
|
||||
uint32_t envLen; /* Number of environments */
|
||||
|
||||
uint32_t *envSizes; /* Minimum sizes of environments (for static analysis) */
|
||||
uint32_t *envCaptures; /* Which environments to capture from parent. Is a bitset with the parent's envLen bits. */
|
||||
DstValue *literals; /* Contains strings, FuncDefs, etc. */
|
||||
uint32_t *byteCode;
|
||||
};
|
||||
|
||||
/* A fuction environment */
|
||||
struct DstFuncEnv {
|
||||
DstThread *thread; /* When nil, index the local values */
|
||||
uint32_t stackOffset; /* Used as environment size when off stack */
|
||||
DstValue *values;
|
||||
};
|
||||
|
||||
/* A function */
|
||||
struct DstFunction {
|
||||
DstFuncDef *def;
|
||||
DstFuncEnv *envs;
|
||||
};
|
||||
|
||||
/* Defines a type for userdata */
|
||||
struct DstUserType {
|
||||
const char *name;
|
||||
int (*serialize)(Dst *vm, void *data, uint32_t len);
|
||||
int (*deserialize)(Dst *vm);
|
||||
void (*finalize)(Dst *vm, void *data, uint32_t len);
|
||||
};
|
||||
|
||||
/* Contains information about userdata */
|
||||
struct DstUserdataHeader {
|
||||
uint32_t size;
|
||||
const DstUserType *type;
|
||||
};
|
||||
|
||||
/* The VM state */
|
||||
struct Dst {
|
||||
/* Garbage collection */
|
||||
void *blocks;
|
||||
uint32_t memoryInterval;
|
||||
uint32_t nextCollection;
|
||||
uint32_t black : 1;
|
||||
/* Immutable value cache */
|
||||
DstValue *cache;
|
||||
uint32_t cache_capacity;
|
||||
uint32_t cache_count;
|
||||
uint32_t cache_deleted;
|
||||
/* GC roots */
|
||||
DstThread *thread;
|
||||
DstTable *modules;
|
||||
DstTable *registry;
|
||||
DstTable *env;
|
||||
/* Return state */
|
||||
DstValue ret;
|
||||
uint32_t flags;
|
||||
};
|
||||
|
||||
/* Bytecode */
|
||||
enum DstOpCode {
|
||||
DST_OP_FLS, /* Load false */
|
||||
DST_OP_TRU, /* Load true */
|
||||
DST_OP_NIL, /* Load nil */
|
||||
DST_OP_UPV, /* Load upvalue */
|
||||
DST_OP_JIF, /* Jump if */
|
||||
DST_OP_JMP, /* Jump */
|
||||
DST_OP_SUV, /* Set upvalue */
|
||||
DST_OP_CST, /* Load constant */
|
||||
DST_OP_I16, /* Load 16 bit signed integer */
|
||||
DST_OP_I32, /* Load 32 bit signed integer */
|
||||
DST_OP_I64, /* Load 64 bit signed integer */
|
||||
DST_OP_F64, /* Load 64 bit IEEE double */
|
||||
DST_OP_MOV, /* Move value */
|
||||
DST_OP_CLN, /* Create a closure */
|
||||
DST_OP_ARR, /* Create array */
|
||||
DST_OP_DIC, /* Create object */
|
||||
DST_OP_TUP, /* Create tuple */
|
||||
DST_OP_RET, /* Return from function */
|
||||
DST_OP_RTN, /* Return nil */
|
||||
DST_OP_PSK, /* Push stack */
|
||||
DST_OP_PAR, /* Push array or tuple */
|
||||
DST_OP_CAL, /* Call function */
|
||||
DST_OP_TCL, /* Tail call */
|
||||
DST_OP_TRN /* Transfer to new thread */
|
||||
};
|
||||
|
||||
/****/
|
||||
/* Value Stack Manipulation */
|
||||
/****/
|
||||
void dst_switchv(Dst *vm, DstValue x);
|
||||
DstValue dst_popv(Dst *vm);
|
||||
DstValue dst_peekv(Dst *vm);
|
||||
void dst_pushv(Dst *vm, DstValue x);
|
||||
DstValue dst_getv(Dst *vm, int64_t index);
|
||||
void dst_setv(Dst *vm, int64_t index, DstValue x);
|
||||
|
||||
/****/
|
||||
/* Buffer functions */
|
||||
/****/
|
||||
void dst_buffer_ensure_(Dst *vm, DstBuffer *buffer, uint32_t capacity);
|
||||
void dst_buffer_push_u8_(Dst *vm, DstBuffer *buffer, uint8_t x);
|
||||
void dst_buffer_push_u16_(Dst *vm, DstBuffer *buffer, uint16_t x);
|
||||
void dst_buffer_push_u32_(Dst *vm, DstBuffer *buffer, uint32_t x);
|
||||
void dst_buffer_push_u64_(Dst *vm, DstBuffer *buffer, uint64_t x);
|
||||
void dst_buffer_push_bytes_(Dst *vm, DstBuffer *buffer, const uint8_t *bytes, uint32_t len);
|
||||
void dst_buffer_push_cstring_(Dst *vm, DstBuffer *buffer, const char *string);
|
||||
|
||||
/****/
|
||||
/* Array functions */
|
||||
/****/
|
||||
DstArray *dst_make_array(Dst *vm, uint32_t capacity);
|
||||
void dst_array_ensure_(Dst *vm, DstArray *array, uint32_t capacity);
|
||||
|
||||
/****/
|
||||
/* Tuple functions */
|
||||
/****/
|
||||
|
||||
DstValue *dst_tuple_begin(Dst *vm, uint32_t length);
|
||||
const DstValue *dst_tuple_end(Dst *vm, DstValue *tuple);
|
||||
|
||||
/****/
|
||||
/* String/Symbol functions */
|
||||
/****/
|
||||
|
||||
const uint8_t *dst_string_b(Dst *vm, const uint8_t *buf, uint32_t len);
|
||||
const uint8_t *dst_string_c(Dst *vm, const char *cstring);
|
||||
DstValue dst_string_cv(Dst *vm, const char *string);
|
||||
DstValue dst_string_cvs(Dst *vm, const char *string);
|
||||
int dst_string_compare(const uint8_t *lhs, const uint8_t *rhs);
|
||||
const uint8_t *dst_string_bu(Dst *vm, const uint8_t *buf, uint32_t len);
|
||||
const uint8_t *dst_string_cu(Dst *vm, const char *s);
|
||||
|
||||
/****/
|
||||
/* Struct functions */
|
||||
/****/
|
||||
|
||||
DstValue *dst_struct_begin(Dst *vm, uint32_t count);
|
||||
void dst_struct_put(DstValue *st, DstValue key, DstValue value);
|
||||
const DstValue *dst_struct_end(Dst *vm, DstValue *st);
|
||||
DstValue dst_struct_get(const DstValue *st, DstValue key);
|
||||
DstValue dst_struct_next(const DstValue *st, DstValue key);
|
||||
|
||||
/****/
|
||||
/* Table functions */
|
||||
/****/
|
||||
|
||||
DstTable *dst_make_table(Dst *vm, uint32_t capacity);
|
||||
DstValue dst_table_get(DstTable *t, DstValue key);
|
||||
DstValue dst_table_remove(DstTable *t, DstValue key);
|
||||
void dst_table_put(Dst *vm, DstTable *t, DstValue key, DstValue value);
|
||||
DstValue dst_table_next(DstTable *o, DstValue key);
|
||||
|
||||
/****/
|
||||
/* Threads */
|
||||
/****/
|
||||
|
||||
#define dst_thread_stack(t) ((t)->data + (t)->count)
|
||||
DstThread *dst_thread(Dst *vm, DstValue callee, uint32_t capacity);
|
||||
DstThread *dst_thread_reset(Dst *vm, DstThread *thread, DstValue callee);
|
||||
void dst_thread_ensure_extra(Dst *vm, DstThread *thread, uint32_t extra);
|
||||
void dst_thread_push(Dst *vm, DstThread *thread, DstValue x);
|
||||
void dst_thread_pushnil(Dst *vm, DstThread *thread, uint32_t n);
|
||||
void dst_thread_tuplepack(Dst *vm, DstThread *thread, uint32_t n);
|
||||
DstValue *dst_thread_beginframe(Dst *vm, DstThread *thread, DstValue callee, uint32_t arity);
|
||||
void dst_thread_endframe(Dst *vm, DstThread *thread);
|
||||
DstValue *dst_thread_popframe(Dst *vm, DstThread *thread);
|
||||
uint32_t dst_thread_countframes(DstThread *thread);
|
||||
|
||||
/****/
|
||||
/* Serialization */
|
||||
/****/
|
||||
|
||||
const char *dst_deserialize_internal(
|
||||
Dst *vm,
|
||||
const uint8_t *data,
|
||||
uint32_t len,
|
||||
DstValue *out,
|
||||
const uint8_t **nextData);
|
||||
|
||||
const char *dst_serialize_internal(Dst *vm, DstBuffer *buffer, DstValue x);
|
||||
|
||||
/* Treat similar types through uniform interfaces for iteration */
|
||||
int dst_seq_view(DstValue seq, const DstValue **data, uint32_t *len);
|
||||
int dst_chararray_view(DstValue str, const uint8_t **data, uint32_t *len);
|
||||
int dst_hashtable_view(DstValue tab, const DstValue **data, uint32_t *cap);
|
||||
|
||||
/****/
|
||||
/* Parse functions */
|
||||
/****/
|
||||
|
||||
#define PARSE_OK 0
|
||||
#define PARSE_ERROR 1
|
||||
#define PARSE_UNEXPECTED_EOS 2
|
||||
|
||||
int dst_parseb(Dst *vm, const uint8_t *src, const uint8_t **newsrc, uint32_t len);
|
||||
|
||||
/* Parse a c string */
|
||||
int dst_parsec(Dst *vm, const char *src, const char **newsrc);
|
||||
|
||||
/* Parse a DST char seq (Buffer, String, Symbol) */
|
||||
int dst_parse(Dst *vm);
|
||||
|
||||
/****/
|
||||
/* Value functions */
|
||||
/****/
|
||||
|
||||
int dst_truthy(DstValue v);
|
||||
int dst_equals(DstValue x, DstValue y);
|
||||
uint32_t dst_hash(DstValue x);
|
||||
int dst_compare(DstValue x, DstValue y);
|
||||
uint32_t dst_calchash_array(const DstValue *array, uint32_t len);
|
||||
|
||||
/****/
|
||||
/* Helper functions */
|
||||
/****/
|
||||
uint32_t dst_startrange(int64_t index, uint32_t modulo);
|
||||
uint32_t dst_endrange(int64_t index, uint32_t modulo);
|
||||
int64_t dst_normalize_index(Dst *vm, int64_t index);
|
||||
|
||||
#endif /* DST_INTERNAL_H_defined */
|
85
core/opcodes.h
Normal file
85
core/opcodes.h
Normal file
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef DST_OPCODES_H_defined
|
||||
#define DST_OPCODES_H_defined
|
||||
|
||||
typedef enum DstOpCode DstOpCode;
|
||||
enum DstOpCode {
|
||||
DOP_NOOP,
|
||||
DOP_ERROR,
|
||||
DOP_TYPECHECK,
|
||||
DOP_RETURN,
|
||||
DOP_RETURN_NIL,
|
||||
DOP_COERCE_INTEGER,
|
||||
DOP_COERCE_REAL,
|
||||
DOP_COERCE_STRING,
|
||||
DOP_ADD_INTEGER,
|
||||
DOP_ADD_IMMEDIATE,
|
||||
DOP_ADD_REAL,
|
||||
DOP_ADD,
|
||||
DOP_SUBTRACT_INTEGER,
|
||||
DOP_SUBTRACT_REAL,
|
||||
DOP_SUBTRACT,
|
||||
DOP_MULTIPLY_INTEGER,
|
||||
DOP_MULTIPLY_IMMEDIATE,
|
||||
DOP_MULTIPLY_REAL,
|
||||
DOP_MULTIPLY,
|
||||
DOP_DIVIDE_INTEGER,
|
||||
DOP_DIVIDE_IMMEDIATE,
|
||||
DOP_DIVIDE_REAL,
|
||||
DOP_DIVIDE,
|
||||
DOP_BAND,
|
||||
DOP_BOR,
|
||||
DOP_BXOR,
|
||||
DOP_BNOT,
|
||||
DOP_SHIFT_LEFT,
|
||||
DOP_SHIFT_LEFT_IMMEDIATE,
|
||||
DOP_SHIFT_RIGHT,
|
||||
DOP_SHIFT_RIGHT_IMMEDIATE,
|
||||
DOP_SHIFT_RIGHT_UNSIGNED,
|
||||
DOP_SHIFT_RIGHT_UNSIGNED_IMMEDIATE,
|
||||
DOP_MOVE,
|
||||
DOP_JUMP,
|
||||
DOP_JUMP_IF,
|
||||
DOP_GREATER_THAN,
|
||||
DOP_EQUALS,
|
||||
DOP_COMPARE,
|
||||
DOP_LOAD_NIL,
|
||||
DOP_LOAD_BOOLEAN,
|
||||
DOP_LOAD_INTEGER,
|
||||
DOP_LOAD_CONSTANT,
|
||||
DOP_LOAD_UPVALUE,
|
||||
DOP_SET_UPVALUE,
|
||||
DOP_CLOSURE,
|
||||
DOP_PUSH,
|
||||
DOP_PUSH_2,
|
||||
DOP_PUSH_3,
|
||||
DOP_PUSH_ARRAY,
|
||||
DOP_CALL,
|
||||
DOP_TAILCALL,
|
||||
DOP_SYSCALL,
|
||||
DOP_LOAD_SYSCALL,
|
||||
DOP_TRANSFER
|
||||
};
|
||||
|
||||
#endif
|
128
core/parse.c
128
core/parse.c
@ -20,7 +20,7 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "internal.h"
|
||||
#include <dst/dst.h>
|
||||
|
||||
/* Get an integer power of 10 */
|
||||
static double exp10(int power) {
|
||||
@ -115,10 +115,12 @@ static int check_str_const(const char *ref, const uint8_t *start, const uint8_t
|
||||
}
|
||||
|
||||
/* Quote a value */
|
||||
static void quote(Dst *vm) {
|
||||
dst_cstring(vm, "quote");
|
||||
dst_hoist(vm, -2);
|
||||
dst_tuple(vm, 2);
|
||||
static DstValue quote(DstValue x) {
|
||||
DstValue sym = dst_wrap_symbol(dst_cstring("quote"));
|
||||
DstValue *t = dst_tuple_begin(2);
|
||||
t[0] = sym;
|
||||
t[1] = x;
|
||||
return dst_wrap_tuple(dst_tuple_end(t));
|
||||
}
|
||||
|
||||
/* Check if a character is whitespace */
|
||||
@ -161,11 +163,14 @@ static int to_hex(uint8_t c) {
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
Dst *vm;
|
||||
DstFiber *fiber;
|
||||
const uint8_t *end;
|
||||
const char *errmsg;
|
||||
int64_t sourcemap;
|
||||
int status;
|
||||
enum {
|
||||
DST_PARSE_OK,
|
||||
DST_PARSE_ERROR,
|
||||
DST_PARSE_UNEPECTED_EOS
|
||||
} status;
|
||||
} ParseArgs;
|
||||
|
||||
/* Entry point of the recursive descent parser */
|
||||
@ -174,9 +179,9 @@ static const uint8_t *parse_recur(
|
||||
const uint8_t *src,
|
||||
uint32_t recur) {
|
||||
|
||||
Dst *vm = args->vm;
|
||||
const uint8_t *end = args->end;
|
||||
uint32_t qcount = 0;
|
||||
DstValue ret;
|
||||
|
||||
/* Prevent stack overflow */
|
||||
if (recur == 0) goto too_much_recur;
|
||||
@ -205,20 +210,20 @@ static const uint8_t *parse_recur(
|
||||
tokenend++;
|
||||
if (tokenend >= end) goto unexpected_eos;
|
||||
if (read_integer(src, tokenend, &integer)) {
|
||||
dst_integer(vm, integer);
|
||||
ret = dst_wrap_integer(integer);
|
||||
} else if (read_real(src, tokenend, &real, 0)) {
|
||||
dst_real(vm, real);
|
||||
ret = dst_wrap_real(real);
|
||||
} else if (check_str_const("nil", src, tokenend)) {
|
||||
dst_nil(vm);
|
||||
ret = dst_wrap_nil();
|
||||
} else if (check_str_const("false", src, tokenend)) {
|
||||
dst_false(vm);
|
||||
ret = dst_wrap_boolean(0);
|
||||
} else if (check_str_const("true", src, tokenend)) {
|
||||
dst_true(vm);
|
||||
ret = dst_wrap_boolean(1);
|
||||
} else {
|
||||
if (*src >= '0' && *src <= '9') {
|
||||
goto sym_nodigits;
|
||||
} else {
|
||||
dst_symbol(vm, src, tokenend - src);
|
||||
ret = dst_wrap_symbol(dst_string(src, tokenend - src));
|
||||
}
|
||||
}
|
||||
src = tokenend;
|
||||
@ -230,7 +235,7 @@ static const uint8_t *parse_recur(
|
||||
while (tokenend < end && is_symbol_char(*tokenend))
|
||||
tokenend++;
|
||||
if (tokenend >= end) goto unexpected_eos;
|
||||
dst_string(vm, src, tokenend - src);
|
||||
ret = dst_wrap_string(dst_string(src, tokenend - src));
|
||||
src = tokenend;
|
||||
break;
|
||||
}
|
||||
@ -256,7 +261,7 @@ static const uint8_t *parse_recur(
|
||||
}
|
||||
}
|
||||
if (containsEscape) {
|
||||
uint8_t *buf = dst_string_begin(vm, len);
|
||||
uint8_t *buf = dst_string_begin(len);
|
||||
uint8_t *write = buf;
|
||||
while (src < strend) {
|
||||
if (*src == '\\') {
|
||||
@ -285,9 +290,9 @@ static const uint8_t *parse_recur(
|
||||
*write++ = *src++;
|
||||
}
|
||||
}
|
||||
dst_string_end(vm, buf);
|
||||
ret = dst_wrap_string(dst_string_end(buf));
|
||||
} else {
|
||||
dst_string(vm, strstart, strend - strstart);
|
||||
ret = dst_wrap_string(dst_string(strstart, strend - strstart));
|
||||
src = strend + 1;
|
||||
}
|
||||
break;
|
||||
@ -297,7 +302,7 @@ static const uint8_t *parse_recur(
|
||||
case '(':
|
||||
case '[':
|
||||
case '{': {
|
||||
uint32_t n = 0;
|
||||
uint32_t n = 0, i = 0;
|
||||
uint8_t close;
|
||||
switch (*src++) {
|
||||
case '[': close = ']'; break;
|
||||
@ -316,22 +321,36 @@ static const uint8_t *parse_recur(
|
||||
src++;
|
||||
switch (close) {
|
||||
case ')':
|
||||
dst_tuple(vm, n);
|
||||
break;
|
||||
case ']':
|
||||
dst_arrayn(vm, n);
|
||||
{
|
||||
DstValue *tup = dst_tuple_begin(n);
|
||||
for (i = n; i > 0; i--)
|
||||
tup[i - 1] = dst_fiber_popvalue(args->fiber);
|
||||
ret = dst_wrap_tuple(dst_tuple_end(tup));
|
||||
break;
|
||||
}
|
||||
case '}':
|
||||
{
|
||||
if (n & 1) goto struct_oddargs;
|
||||
dst_struct(vm, n/2);
|
||||
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);
|
||||
}
|
||||
ret = dst_wrap_struct(dst_struct_end(st));
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Quote the returned value qcount times */
|
||||
while (qcount--) quote(vm);
|
||||
while (qcount--) ret = quote(ret);
|
||||
|
||||
/* Push the result to the stack */
|
||||
dst_fiber_push(args->fiber, ret);
|
||||
|
||||
/* Return the new source position for further calls */
|
||||
return src;
|
||||
@ -340,88 +359,77 @@ static const uint8_t *parse_recur(
|
||||
|
||||
unexpected_eos:
|
||||
args->errmsg = "unexpected end of source";
|
||||
args->status = PARSE_UNEXPECTED_EOS;
|
||||
args->status = DST_PARSE_UNEXPECTED_EOS;
|
||||
return NULL;
|
||||
|
||||
unexpected_character:
|
||||
args->errmsg = "unexpected character";
|
||||
args->status = PARSE_ERROR;
|
||||
args->status = DST_PARSE_ERROR;
|
||||
return src;
|
||||
|
||||
sym_nodigits:
|
||||
args->errmsg = "symbols cannot start with digits";
|
||||
args->status = PARSE_ERROR;
|
||||
args->status = DST_PARSE_ERROR;
|
||||
return src;
|
||||
|
||||
struct_oddargs:
|
||||
args->errmsg = "struct literal needs an even number of arguments";
|
||||
args->status = PARSE_ERROR;
|
||||
args->status = DST_PARSE_ERROR;
|
||||
return src;
|
||||
|
||||
unknown_strescape:
|
||||
args->errmsg = "unknown string escape sequence";
|
||||
args->status = PARSE_ERROR;
|
||||
args->status = DST_PARSE_ERROR;
|
||||
return src;
|
||||
|
||||
invalid_hex:
|
||||
args->errmsg = "invalid hex escape in string";
|
||||
args->status = PARSE_ERROR;
|
||||
args->status = DST_PARSE_ERROR;
|
||||
return src;
|
||||
|
||||
too_much_recur:
|
||||
args->errmsg = "recursed too deeply in parsing";
|
||||
args->status = PARSE_ERROR;
|
||||
args->status = DST_PARSE_ERROR;
|
||||
return src;
|
||||
}
|
||||
|
||||
/* Parse an array of bytes */
|
||||
int dst_parseb(Dst *vm, const uint8_t *src, const uint8_t **newsrc, uint32_t len) {
|
||||
/* 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) {
|
||||
ParseArgs args;
|
||||
uint32_t nargs;
|
||||
DstFiber *fiber = dst_vm_fiber;
|
||||
|
||||
/* Create a source map */
|
||||
dst_table(vm, 10);
|
||||
/* Save original stack top */
|
||||
uint32_t oldstacktop = fiber->stacktop;
|
||||
|
||||
nargs = dst_stacksize(vm);
|
||||
|
||||
args.vm = vm;
|
||||
args.status = PARSE_OK;
|
||||
args.fiber = fiber;
|
||||
args.status = DST_PARSE_OK;
|
||||
args.end = src + len;
|
||||
args.errmsg = NULL;
|
||||
args.sourcemap = dst_normalize_index(vm, -1);
|
||||
|
||||
src = parse_recur(&args, src, 2048);
|
||||
if (newsrc) *newsrc = src;
|
||||
src = parse_recur(&args, src, DST_RECURSION_GUARD);
|
||||
if (NULL != newsrc) *newsrc = src;
|
||||
|
||||
if (args.errmsg) {
|
||||
/* Unwind the stack */
|
||||
dst_trimstack(vm, nargs);
|
||||
dst_cstring(vm, args.errmsg);
|
||||
fiber->ret = dst_wrap_string(dst_cstring(args.errmsg));
|
||||
} else {
|
||||
fiber->ret = dst_fiber_popvalue(fiber);
|
||||
}
|
||||
|
||||
/* Reset stacktop */
|
||||
fiber->stacktop = oldstacktop;
|
||||
|
||||
return args.status;
|
||||
}
|
||||
|
||||
/* Parse a c string */
|
||||
int dst_parsec(Dst *vm, const char *src, const char **newsrc) {
|
||||
int dst_parsec(const char *src, const char **newsrc) {
|
||||
uint32_t len = 0;
|
||||
const uint8_t *ns = NULL;
|
||||
int status;
|
||||
while (src[len]) ++len;
|
||||
status = dst_parseb(vm, (const uint8_t *)src, &ns, len);
|
||||
status = dst_parse((const uint8_t *)src, len, &ns);
|
||||
if (newsrc) {
|
||||
*newsrc = (const char *)ns;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
/* Parse a DST char seq (Buffer, String, Symbol) */
|
||||
int dst_parse(Dst *vm) {
|
||||
int status;
|
||||
uint32_t len;
|
||||
const uint8_t *bytes = dst_bytes(vm, -1, &len);
|
||||
status = dst_parseb(vm, bytes, NULL, len);
|
||||
dst_swap(vm, -1, -2);
|
||||
dst_pop(vm);
|
||||
return status;
|
||||
}
|
||||
|
188
core/string.c
188
core/string.c
@ -20,28 +20,8 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "internal.h"
|
||||
#include <dst/dst.h>
|
||||
#include "cache.h"
|
||||
#include "wrap.h"
|
||||
#include "gc.h"
|
||||
|
||||
static const char *types[] = {
|
||||
"nil",
|
||||
"real",
|
||||
"integer",
|
||||
"boolean",
|
||||
"string",
|
||||
"symbol",
|
||||
"array",
|
||||
"tuple",
|
||||
"table",
|
||||
"struct",
|
||||
"thread",
|
||||
"buffer",
|
||||
"function",
|
||||
"cfunction",
|
||||
"userdata"
|
||||
};
|
||||
|
||||
static const char base64[] =
|
||||
"0123456789"
|
||||
@ -59,8 +39,8 @@ static uint32_t dst_string_calchash(const uint8_t *str, uint32_t len) {
|
||||
}
|
||||
|
||||
/* Begin building a string */
|
||||
uint8_t *dst_string_begin(Dst *vm, uint32_t length) {
|
||||
char *data = dst_alloc(vm, DST_MEMORY_NONE, 2 * sizeof(uint32_t) + length + 1);
|
||||
uint8_t *dst_string_begin(uint32_t length) {
|
||||
char *data = dst_alloc(DST_MEMORY_NONE, 2 * sizeof(uint32_t) + length + 1);
|
||||
uint8_t *str = (uint8_t *) (data + 2 * sizeof(uint32_t));
|
||||
dst_string_length(str) = length;
|
||||
str[length] = 0;
|
||||
@ -68,28 +48,32 @@ uint8_t *dst_string_begin(Dst *vm, uint32_t length) {
|
||||
}
|
||||
|
||||
/* Finish building a string */
|
||||
const uint8_t *dst_string_end(Dst *vm, uint8_t *str) {
|
||||
DstValue check = dst_string_calchash(str, dst_string_length(str));
|
||||
const uint8_t *ret = dst_cache_add(vm, dst_wrap_string(check)).as.string;
|
||||
gc_settype(dst_string_raw(ret), DST_MEMORY_STRING);
|
||||
return ret;
|
||||
const uint8_t *dst_string_end(uint8_t *str) {
|
||||
DstValue check;
|
||||
dst_string_hash(str) = dst_string_calchash(str, dst_string_length(str));
|
||||
check = dst_cache_add(dst_wrap_string(str));
|
||||
/* Don't tag the memory of the string builder directly. If the string is
|
||||
* already cached, we don't want the gc to remove it from cache when the original
|
||||
* string builder is gced (check will contained the cached string) */
|
||||
dst_gc_settype(dst_string_raw(check.as.string), DST_MEMORY_STRING);
|
||||
return check.as.string;
|
||||
}
|
||||
|
||||
/* Load a buffer as a string */
|
||||
static const uint8_t *dst_string(Dst *vm, const uint8_t *buf, uint32_t len) {
|
||||
const uint8_t *dst_string(const uint8_t *buf, uint32_t len) {
|
||||
uint32_t hash = dst_string_calchash(buf, len);
|
||||
int status = 0;
|
||||
DstValue *bucket = dst_cache_strfind(vm, buf, len, hash, &status);
|
||||
DstValue *bucket = dst_cache_strfind(buf, len, hash, &status);
|
||||
if (status) {
|
||||
return bucket->data.string;
|
||||
return bucket->as.string;
|
||||
} else {
|
||||
uint32_t newbufsize = len + 2 * sizeof(uint32_t) + 1;
|
||||
uint8_t *str = (uint8_t *)(dst_alloc(vm, DST_MEMORY_STRING, newbufsize) + 2 * sizeof(uint32_t));
|
||||
dst_memcpy(str, buf, len);
|
||||
uint8_t *str = (uint8_t *)(dst_alloc(DST_MEMORY_STRING, newbufsize) + 2 * sizeof(uint32_t));
|
||||
memcpy(str, buf, len);
|
||||
dst_string_length(str) = len;
|
||||
dst_string_hash(str) = hash;
|
||||
str[len] = 0;
|
||||
return dst_cache_add_bucket(vm, dst_wrap_string(str), bucket).as.string;
|
||||
return dst_cache_add_bucket(dst_wrap_string(str), bucket).as.string;
|
||||
}
|
||||
}
|
||||
|
||||
@ -111,7 +95,7 @@ static void inc_counter(uint8_t *digits, int base, int len) {
|
||||
/* Generate a unique symbol. This is used in the library function gensym. The
|
||||
* symbol string data does not have GC enabled on it yet. You must manuallyb enable
|
||||
* it later. */
|
||||
const uint8_t *dst_string_unique(Dst *vm, const uint8_t *buf, uint32_t len) {
|
||||
const uint8_t *dst_string_unique(const uint8_t *buf, uint32_t len) {
|
||||
DstValue *bucket;
|
||||
uint32_t hash;
|
||||
uint8_t counter[6] = {63, 63, 63, 63, 63, 63};
|
||||
@ -119,7 +103,7 @@ const uint8_t *dst_string_unique(Dst *vm, const uint8_t *buf, uint32_t len) {
|
||||
* is enough for resolving collisions. */
|
||||
uint32_t newlen = len + 8;
|
||||
uint32_t newbufsize = newlen + 2 * sizeof(uint32_t) + 1;
|
||||
uint8_t *str = (uint8_t *)(dst_alloc(vm, DST_MEMORY_STRING, newbufsize) + 2 * sizeof(uint32_t));
|
||||
uint8_t *str = (uint8_t *)(dst_alloc(DST_MEMORY_STRING, newbufsize) + 2 * sizeof(uint32_t));
|
||||
dst_string_length(str) = newlen;
|
||||
memcpy(str, buf, len);
|
||||
str[len] = '-';
|
||||
@ -133,24 +117,24 @@ const uint8_t *dst_string_unique(Dst *vm, const uint8_t *buf, uint32_t len) {
|
||||
for (i = 0; i < 6; ++i)
|
||||
saltbuf[i] = base64[counter[i]];
|
||||
hash = dst_string_calchash(str, newlen);
|
||||
bucket = dst_cache_strfind(vm, str, newlen, hash, &status);
|
||||
bucket = dst_cache_strfind(str, newlen, hash, &status);
|
||||
}
|
||||
dst_string_hash(str) = hash;
|
||||
return dst_cache_add_bucket(vm, dst_wrap_string(str), bucket).as.string;
|
||||
return dst_cache_add_bucket(dst_wrap_string(str), bucket).as.string;
|
||||
}
|
||||
|
||||
/* Generate a unique string from a cstring */
|
||||
const uint8_t *dst_cstring_unique(Dst *vm, const char *s) {
|
||||
const uint8_t *dst_cstring_unique(const char *s) {
|
||||
uint32_t len = 0;
|
||||
while (s[len]) ++len;
|
||||
return dst_string_unique(vm, (const uint8_t *)s, len);
|
||||
return dst_string_unique((const uint8_t *)s, len);
|
||||
}
|
||||
|
||||
/* Load a c string */
|
||||
const uint8_t *dst_cstring(Dst *vm, const char *str) {
|
||||
const uint8_t *dst_cstring(const char *str) {
|
||||
uint32_t len = 0;
|
||||
while (str[len]) ++len;
|
||||
return dst_string(vm, (const uint8_t *)str, len);
|
||||
return dst_string((const uint8_t *)str, len);
|
||||
}
|
||||
|
||||
/* Temporary buffer size */
|
||||
@ -161,14 +145,14 @@ static uint32_t real_to_string_impl(uint8_t *buf, double x) {
|
||||
return (uint32_t) count;
|
||||
}
|
||||
|
||||
static void real_to_string_b(Dst *vm, DstBuffer *buffer, double x) {
|
||||
dst_buffer_ensure_(vm, buffer, buffer->count + DST_BUFSIZE);
|
||||
static void real_to_string_b(DstBuffer *buffer, double x) {
|
||||
dst_buffer_ensure(buffer, buffer->count + DST_BUFSIZE);
|
||||
buffer->count += real_to_string_impl(buffer->data + buffer->count, x);
|
||||
}
|
||||
|
||||
static void real_to_string(Dst *vm, double x) {
|
||||
static const uint8_t *real_to_string(double x) {
|
||||
uint8_t buf[DST_BUFSIZE];
|
||||
dst_string(vm, buf, real_to_string_impl(buf, x));
|
||||
return dst_string(buf, real_to_string_impl(buf, x));
|
||||
}
|
||||
|
||||
static uint32_t integer_to_string_impl(uint8_t *buf, int64_t x) {
|
||||
@ -201,14 +185,14 @@ static uint32_t integer_to_string_impl(uint8_t *buf, int64_t x) {
|
||||
return count;
|
||||
}
|
||||
|
||||
static void integer_to_string_b(Dst *vm, DstBuffer *buffer, int64_t x) {
|
||||
dst_buffer_extra(vm, buffer, DST_BUFSIZE);
|
||||
static void integer_to_string_b(DstBuffer *buffer, int64_t x) {
|
||||
dst_buffer_extra(buffer, DST_BUFSIZE);
|
||||
buffer->count += integer_to_string_impl(buffer->data + buffer->count, x);
|
||||
}
|
||||
|
||||
static void integer_to_string(Dst *vm, int64_t x) {
|
||||
static const uint8_t *integer_to_string(int64_t x) {
|
||||
uint8_t buf[DST_BUFSIZE];
|
||||
dst_string(vm, buf, integer_to_string_impl(buf, x));
|
||||
return dst_string(buf, integer_to_string_impl(buf, x));
|
||||
}
|
||||
|
||||
#define HEX(i) (((uint8_t *) base64)[(i)])
|
||||
@ -240,14 +224,16 @@ static uint32_t string_description_impl(uint8_t *buf, const char *title, void *p
|
||||
return (uint32_t) (c - buf);
|
||||
}
|
||||
|
||||
static void string_description_b(Dst *vm, DstBuffer *buffer, const char *title, void *pointer) {
|
||||
dst_buffer_ensure_(vm, buffer, buffer->count + DST_BUFSIZE);
|
||||
static void string_description_b(DstBuffer *buffer, const char *title, void *pointer) {
|
||||
dst_buffer_ensure(buffer, buffer->count + DST_BUFSIZE);
|
||||
buffer->count += string_description_impl(buffer->data + buffer->count, title, pointer);
|
||||
}
|
||||
|
||||
static const uint8_t *string_description(Dst *vm, const char *title, void *pointer) {
|
||||
/* Describes a pointer with a title (string_description("bork", myp) returns
|
||||
* a string "<bork 0x12345678>") */
|
||||
static const uint8_t *string_description(const char *title, void *pointer) {
|
||||
uint8_t buf[DST_BUFSIZE];
|
||||
return dst_string(vm, buf, string_description_impl(buf, title, pointer));
|
||||
return dst_string(buf, string_description_impl(buf, title, pointer));
|
||||
}
|
||||
|
||||
#undef HEX
|
||||
@ -302,82 +288,82 @@ static void dst_escape_string_impl(uint8_t *buf, const uint8_t *str) {
|
||||
buf[j++] = '"';
|
||||
}
|
||||
|
||||
static void dst_escape_string_b(Dst *vm, DstBuffer *buffer, const uint8_t *str) {
|
||||
void dst_escape_string_b(DstBuffer *buffer, const uint8_t *str) {
|
||||
uint32_t len = dst_escape_string_length(str);
|
||||
dst_buffer_extra(vm, buffer, len);
|
||||
dst_buffer_extra(buffer, len);
|
||||
dst_escape_string_impl(buffer->data + buffer->count, str);
|
||||
buffer->count += len;
|
||||
}
|
||||
|
||||
static const uint8_t *dst_escape_string(Dst *vm, const uint8_t *str) {
|
||||
const uint8_t *dst_escape_string(const uint8_t *str) {
|
||||
uint32_t len = dst_escape_string_length(str);
|
||||
uint8_t *buf = dst_string_begin(vm, len);
|
||||
uint8_t *buf = dst_string_begin(len);
|
||||
dst_escape_string_impl(buf, str);
|
||||
return dst_string_end(vm, buf);
|
||||
return dst_string_end(buf);
|
||||
}
|
||||
|
||||
/* Returns a string pointer with the description of the string */
|
||||
static const uint8_t *dst_short_description(Dst *vm) {
|
||||
const uint8_t *dst_short_description(DstValue x) {
|
||||
switch (x.type) {
|
||||
case DST_NIL:
|
||||
return dst_cstring(vm, "nil");
|
||||
return dst_cstring("nil");
|
||||
case DST_BOOLEAN:
|
||||
if (x.as.boolean)
|
||||
return dst_cstring(vm, "true");
|
||||
return dst_cstring("true");
|
||||
else
|
||||
return dst_cstring(vm, "false");
|
||||
return dst_cstring("false");
|
||||
case DST_REAL:
|
||||
return real_to_string(vm, x.as.real);
|
||||
return real_to_string(x.as.real);
|
||||
case DST_INTEGER:
|
||||
return integer_to_string(vm, x.as.integer);
|
||||
return integer_to_string(x.as.integer);
|
||||
case DST_SYMBOL:
|
||||
return x.as.string;
|
||||
case DST_STRING:
|
||||
return dst_escape_string(vm, x.as.string);
|
||||
return dst_escape_string(x.as.string);
|
||||
case DST_USERDATA:
|
||||
return string_description(vm, dst_udata_type(x.as.pointer)->name, x.as.pointer);
|
||||
return string_description(dst_userdata_type(x.as.pointer)->name, x.as.pointer);
|
||||
default:
|
||||
return string_description(vm, types[x.type], x.as.pointer);
|
||||
return string_description(dst_type_names[x.type], x.as.pointer);
|
||||
}
|
||||
}
|
||||
|
||||
static void dst_short_description_b(Dst *vm, DstBuffer *buffer, DstValue x) {
|
||||
void dst_short_description_b(DstBuffer *buffer, DstValue x) {
|
||||
switch (x.type) {
|
||||
case DST_NIL:
|
||||
dst_buffer_push_cstring(vm, buffer, "nil");
|
||||
dst_buffer_push_cstring(buffer, "nil");
|
||||
return;
|
||||
case DST_BOOLEAN:
|
||||
if (x.as.boolean)
|
||||
dst_buffer_push_cstring(vm, buffer, "true");
|
||||
dst_buffer_push_cstring(buffer, "true");
|
||||
else
|
||||
dst_buffer_push_cstring(vm, buffer, "false");
|
||||
dst_buffer_push_cstring(buffer, "false");
|
||||
return;
|
||||
case DST_REAL:
|
||||
real_to_string_b(vm, buffer, x.as.real);
|
||||
real_to_string_b(buffer, x.as.real);
|
||||
return;
|
||||
case DST_INTEGER:
|
||||
integer_to_string_b(vm, buffer, x.as.integer);
|
||||
integer_to_string_b(buffer, x.as.integer);
|
||||
return;
|
||||
case DST_SYMBOL:
|
||||
dst_buffer_push_bytes(vm, buffer, x.as.string, dst_string_length(x.as.string));
|
||||
dst_buffer_push_bytes(buffer, x.as.string, dst_string_length(x.as.string));
|
||||
return;
|
||||
case DST_STRING:
|
||||
dst_escape_string_b(vm, buffer, x.as.string);
|
||||
dst_escape_string_b(buffer, x.as.string);
|
||||
return;
|
||||
case DST_USERDATA:
|
||||
string_description_b(vm, buffer, dst_udata_type(x.as.pointer)->name, x.as.pointer);
|
||||
string_description_b(buffer, dst_userdata_type(x.as.pointer)->name, x.as.pointer);
|
||||
return;
|
||||
default:
|
||||
string_description_b(vm, buffer, types[x.type], x.as.pointer);
|
||||
string_description_b(buffer, dst_type_names[x.type], x.as.pointer);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Static debug print helper */
|
||||
static int64_t dst_description_helper(Dst *vm, DstBuffer *b, DstTable *seen, DstValue x, int64_t next, int depth) {
|
||||
static int64_t dst_description_helper(DstBuffer *b, DstTable *seen, DstValue x, int64_t next, int depth) {
|
||||
DstValue check = dst_table_get(seen, x);
|
||||
if (check.type == DST_INTEGER) {
|
||||
dst_buffer_push_cstring(vm, b, "<cycle>");
|
||||
dst_buffer_push_cstring(b, "<cycle>");
|
||||
} else {
|
||||
const char *open;
|
||||
const char *close;
|
||||
@ -385,7 +371,7 @@ static int64_t dst_description_helper(Dst *vm, DstBuffer *b, DstTable *seen, Dst
|
||||
const DstValue *data;
|
||||
switch (x.type) {
|
||||
default:
|
||||
dst_short_description_b(vm, b, x);
|
||||
dst_short_description_b(b, x);
|
||||
return next;
|
||||
case DST_STRUCT:
|
||||
open = "{"; close = "}";
|
||||
@ -400,10 +386,10 @@ static int64_t dst_description_helper(Dst *vm, DstBuffer *b, DstTable *seen, Dst
|
||||
open = "["; close = "]";
|
||||
break;
|
||||
}
|
||||
dst_table_put(vm, seen, x, dst_wrap_integer(next++));
|
||||
dst_buffer_push_cstring(vm, b, open);
|
||||
dst_table_put(seen, x, dst_wrap_integer(next++));
|
||||
dst_buffer_push_cstring(b, open);
|
||||
if (depth == 0) {
|
||||
dst_buffer_push_cstring(vm, b, "...");
|
||||
dst_buffer_push_cstring(b, "...");
|
||||
} else if (dst_hashtable_view(x, &data, &len)) {
|
||||
int isfirst = 1;
|
||||
for (i = 0; i < len; i += 2) {
|
||||
@ -411,45 +397,45 @@ static int64_t dst_description_helper(Dst *vm, DstBuffer *b, DstTable *seen, Dst
|
||||
if (isfirst)
|
||||
isfirst = 0;
|
||||
else
|
||||
dst_buffer_push_u8(vm, b, ' ');
|
||||
next = dst_description_helper(vm, b, seen, data[i], next, depth - 1);
|
||||
dst_buffer_push_u8(vm, b, ' ');
|
||||
next = dst_description_helper(vm, b, seen, data[i + 1], next, depth - 1);
|
||||
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)) {
|
||||
for (i = 0; i < len; ++i) {
|
||||
next = dst_description_helper(vm, b, seen, data[i], next, depth - 1);
|
||||
next = dst_description_helper(b, seen, data[i], next, depth - 1);
|
||||
if (i != len - 1)
|
||||
dst_buffer_push_u8(vm, b, ' ');
|
||||
dst_buffer_push_u8(b, ' ');
|
||||
}
|
||||
}
|
||||
dst_buffer_push_cstring(vm, b, close);
|
||||
dst_buffer_push_cstring(b, close);
|
||||
}
|
||||
return next;
|
||||
}
|
||||
|
||||
/* Debug print. Returns a description of an object as a string. */
|
||||
const uint8_t *dst_description(Dst *vm, DstValue x) {
|
||||
DstBuffer *buf = dst_buffer(vm, 10);
|
||||
DstTable *seen = dst_table(vm, 10);
|
||||
const uint8_t *dst_description(DstValue x) {
|
||||
DstBuffer *buf = dst_buffer(10);
|
||||
DstTable *seen = dst_table(10);
|
||||
|
||||
/* Only print description up to a depth of 4 */
|
||||
dst_description_helper(vm, buf, seen, x, 0, 4);
|
||||
dst_description_helper(buf, seen, x, 0, 4);
|
||||
|
||||
return dst_string(vm, buf->data, buf->count);
|
||||
return dst_string(buf->data, buf->count);
|
||||
}
|
||||
|
||||
/* Convert any value to a dst string */
|
||||
const uint8_t *dst_to_string(Dst *vm, DstValue x) {
|
||||
DstValue x = dst_getv(vm, -1);
|
||||
/* Convert any value to a dst string. Similar to description, but
|
||||
* strings, symbols, and buffers will return their content. */
|
||||
const uint8_t *dst_to_string(DstValue x) {
|
||||
switch (x.type) {
|
||||
default:
|
||||
return dst_description(vm, x);
|
||||
return dst_description(x);
|
||||
case DST_STRING:
|
||||
case DST_SYMBOL:
|
||||
return x.as.string;
|
||||
case DST_BUFFER:
|
||||
return dst_string(vm, x.as.buffer->data, x.as.buffer->count);
|
||||
return dst_string(x.as.buffer->data, x.as.buffer->count);
|
||||
}
|
||||
}
|
||||
|
@ -20,11 +20,11 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "internal.h"
|
||||
#include <dst/dst.h>
|
||||
#include "cache.h"
|
||||
|
||||
/* Begin creation of a struct */
|
||||
DstValue *dst_struct_begin(Dst *vm, uint32_t count) {
|
||||
DstValue *dst_struct_begin(uint32_t count) {
|
||||
/* This expression determines size of structs. It must be a pure
|
||||
* function of count, and hold at least enough space for count
|
||||
* key value pairs. The minimum it could be is
|
||||
@ -32,8 +32,8 @@ DstValue *dst_struct_begin(Dst *vm, uint32_t count) {
|
||||
* ensures that structs are less likely to have hash collisions. If more space
|
||||
* is added or s is changed, change the macro dst_struct_capacity in internal.h */
|
||||
size_t s = sizeof(uint32_t) * 2 + 4 * count * sizeof(DstValue);
|
||||
char *data = dst_alloc(vm, DST_MEMORY_STRUCT, s);
|
||||
memset(data, 0, s)
|
||||
char *data = dst_alloc(DST_MEMORY_STRUCT, s);
|
||||
memset(data, 0, s);
|
||||
DstValue *st = (DstValue *) (data + 2 * sizeof(uint32_t));
|
||||
dst_struct_length(st) = count;
|
||||
/* Use the hash storage space as a counter to see how many items
|
||||
@ -47,7 +47,7 @@ DstValue *dst_struct_begin(Dst *vm, 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_value_hash(key) % (cap / 2)) * 2;
|
||||
uint32_t index = (dst_hash(key) % (cap / 2)) * 2;
|
||||
uint32_t i;
|
||||
for (i = index; i < cap; i += 2)
|
||||
if (st[i].type == DST_NIL || dst_equals(st[i], key))
|
||||
@ -122,11 +122,8 @@ void dst_struct_put(DstValue *st, DstValue key, DstValue value) {
|
||||
}
|
||||
|
||||
/* Finish building a struct */
|
||||
static const DstValue *dst_struct_end(Dst *vm, DstValue *st) {
|
||||
DstValue cached;
|
||||
const DstValue *dst_struct_end(DstValue *st) {
|
||||
DstValue check;
|
||||
/* For explicit tail recursion */
|
||||
recur:
|
||||
if (dst_struct_hash(st) != dst_struct_length(st)) {
|
||||
/* Error building struct, probably duplicate values. We need to rebuild
|
||||
* the struct using only the values that went in. The second creation should always
|
||||
@ -137,19 +134,18 @@ static const DstValue *dst_struct_end(Dst *vm, DstValue *st) {
|
||||
for (i = 0; i < dst_struct_capacity(st); i += 2) {
|
||||
realCount += st[i].type != DST_NIL;
|
||||
}
|
||||
newst = dst_struct_begin(vm, realCount);
|
||||
newst = dst_struct_begin(realCount);
|
||||
for (i = 0; i < dst_struct_capacity(st); i += 2) {
|
||||
if (st[i].type != DST_NIL) {
|
||||
dst_struct_put(newst, st[i], st[i + 1]);
|
||||
}
|
||||
}
|
||||
st = newst;
|
||||
goto recur;
|
||||
}
|
||||
dst_struct_hash(st) = dst_calchash_array(st, dst_struct_capacity(st));
|
||||
check.type = DST_STRUCT;
|
||||
check.as.st = (const DstValue *) st;
|
||||
return dst_cache_add(vm, check).as.st;
|
||||
check = dst_cache_add(dst_wrap_struct(st));
|
||||
dst_gc_settype(dst_tuple_raw(check.as.st), DST_MEMORY_STRUCT);
|
||||
return check.as.st;
|
||||
}
|
||||
|
||||
/* Get an item from a struct */
|
||||
|
33
core/syscalls.c
Normal file
33
core/syscalls.c
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#include <dst/dst.h>
|
||||
|
||||
int dst_print(DstFiber *fiber, DstValue *argv, uint32_t argn) {
|
||||
printf("Hello!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
DstCFunction dst_vm_syscalls[256] = {
|
||||
dst_print
|
||||
};
|
27
core/table.c
27
core/table.c
@ -20,15 +20,13 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "internal.h"
|
||||
#include "wrap.h"
|
||||
#include "gc.h"
|
||||
#include <dst/dst.h>
|
||||
|
||||
/* Initialize a table */
|
||||
DstTable *dst_table(Dst *vm, uint32_t capacity) {
|
||||
DstTable *table = dst_alloc(vm, DST_MEMORY_TABLE, sizeof(DstTable));
|
||||
DstValue *data = calloc(sizeof(DstValue), capacity);
|
||||
DstTable *dst_table_init(DstTable *table, uint32_t capacity) {
|
||||
DstValue *data;
|
||||
if (capacity < 2) capacity = 2;
|
||||
data = calloc(sizeof(DstValue), capacity);
|
||||
if (NULL == data) {
|
||||
DST_OUT_OF_MEMORY;
|
||||
}
|
||||
@ -39,6 +37,17 @@ DstTable *dst_table(Dst *vm, uint32_t capacity) {
|
||||
return table;
|
||||
}
|
||||
|
||||
/* Deinitialize a table */
|
||||
void dst_table_deinit(DstTable *table) {
|
||||
free(table->data);
|
||||
}
|
||||
|
||||
/* Create a new table */
|
||||
DstTable *dst_table(uint32_t capacity) {
|
||||
DstTable *table = dst_alloc(DST_MEMORY_TABLE, sizeof(DstTable));
|
||||
return dst_table_init(table, capacity);
|
||||
}
|
||||
|
||||
/* Find the bucket that contains the given key. Will also return
|
||||
* bucket where key should go if not in the table. */
|
||||
static DstValue *dst_table_find(DstTable *t, DstValue key) {
|
||||
@ -63,7 +72,7 @@ static DstValue *dst_table_find(DstTable *t, DstValue key) {
|
||||
}
|
||||
|
||||
/* Resize the dictionary table. */
|
||||
static void dst_table_rehash(Dst *vm, DstTable *t, uint32_t size) {
|
||||
static void dst_table_rehash(DstTable *t, uint32_t size) {
|
||||
DstValue *olddata = t->data;
|
||||
DstValue *newdata = calloc(sizeof(DstValue), size);
|
||||
if (NULL == newdata) {
|
||||
@ -110,7 +119,7 @@ DstValue dst_table_remove(DstTable *t, DstValue key) {
|
||||
}
|
||||
|
||||
/* Put a value into the object */
|
||||
void dst_table_put(Dst *vm, DstTable *t, DstValue key, DstValue value) {
|
||||
void dst_table_put(DstTable *t, DstValue key, DstValue value) {
|
||||
if (key.type == DST_NIL) return;
|
||||
if (value.type == DST_NIL) {
|
||||
dst_table_remove(t, key);
|
||||
@ -120,7 +129,7 @@ void dst_table_put(Dst *vm, DstTable *t, DstValue key, DstValue value) {
|
||||
bucket[1] = value;
|
||||
} else {
|
||||
if (!bucket || 4 * (t->count + t->deleted) >= t->capacity) {
|
||||
dst_table_rehash(vm, t, 4 * t->count + 6);
|
||||
dst_table_rehash(t, 4 * t->count + 6);
|
||||
}
|
||||
bucket = dst_table_find(t, key);
|
||||
if (bucket[1].type == DST_BOOLEAN)
|
||||
|
200
core/thread.c
200
core/thread.c
@ -1,200 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "internal.h"
|
||||
#include "wrap.h"
|
||||
#include "gc.h"
|
||||
|
||||
/* Initialize a new thread */
|
||||
DstThread *dst_thread(Dst *vm, DstValue callee, uint32_t capacity) {
|
||||
DstThread *thread = dst_alloc(vm, DST_MEMORY_THREAD, sizeof(DstThread));
|
||||
if (capacity < DST_FRAME_SIZE) capacity = DST_FRAME_SIZE;
|
||||
thread->capacity = capacity;
|
||||
DstValue *data = malloc(vm, sizeof(DstValue) * capacity);
|
||||
if (NULL == data) {
|
||||
DST_OUT_OF_MEMORY;
|
||||
}
|
||||
thread->data = data;
|
||||
return dst_thread_reset(vm, thread, callee);
|
||||
}
|
||||
|
||||
/* Clear a thread (reset it) */
|
||||
DstThread *dst_thread_reset(Dst *vm, DstThread *thread, DstValue callee) {
|
||||
DstValue *stack;
|
||||
thread->count = DST_FRAME_SIZE;
|
||||
thread->status = DST_THREAD_PENDING;
|
||||
stack = thread->data + DST_FRAME_SIZE;
|
||||
dst_frame_size(stack) = 0;
|
||||
dst_frame_prevsize(stack) = 0;
|
||||
dst_frame_ret(stack) = 0;
|
||||
dst_frame_args(stack) = 0;
|
||||
dst_frame_pc(stack) = NULL;
|
||||
dst_frame_env(stack) = NULL;
|
||||
dst_frame_callee(stack) = callee;
|
||||
dst_thread_endframe(vm, thread);
|
||||
thread->parent = NULL; /* Need to set parent manually */
|
||||
return thread;
|
||||
}
|
||||
|
||||
/* Ensure that the thread has enough extra capacity */
|
||||
void dst_thread_ensure_extra(Dst *vm, DstThread *thread, uint32_t extra) {
|
||||
DstValue *newData, *stack;
|
||||
uint32_t usedCapacity, neededCapacity, newCapacity;
|
||||
stack = thread->data + thread->count;
|
||||
usedCapacity = thread->count + dst_frame_size(stack) + DST_FRAME_SIZE;
|
||||
neededCapacity = usedCapacity + extra;
|
||||
if (thread->capacity >= neededCapacity) return;
|
||||
newCapacity = 2 * neededCapacity;
|
||||
|
||||
newData = realloc(thread->data, sizeof(DstValue) * newCapacity);
|
||||
if (NULL == newData) {
|
||||
DST_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
thread->data = newData;
|
||||
thread->capacity = newCapacity;
|
||||
}
|
||||
|
||||
/* Push a value on the current stack frame*/
|
||||
void dst_thread_push(Dst *vm, DstThread *thread, DstValue x) {
|
||||
DstValue *stack;
|
||||
dst_thread_ensure_extra(vm, thread, 1);
|
||||
stack = thread->data + thread->count;
|
||||
stack[dst_frame_size(stack)++] = x;
|
||||
}
|
||||
|
||||
/* Push n nils onto the stack */
|
||||
void dst_thread_pushnil(Dst *vm, DstThread *thread, uint32_t n) {
|
||||
DstValue *stack, *current, *end;
|
||||
dst_thread_ensure_extra(vm, thread, n);
|
||||
stack = thread->data + thread->count;
|
||||
current = stack + dst_frame_size(stack);
|
||||
end = current + n;
|
||||
for (; current < end; ++current) {
|
||||
current->type = DST_NIL;
|
||||
}
|
||||
dst_frame_size(stack) += n;
|
||||
}
|
||||
|
||||
/* Package up extra args after and including n into tuple at n. Used for
|
||||
* packing up varargs to variadic functions. */
|
||||
void dst_thread_tuplepack(Dst *vm, DstThread *thread, uint32_t n) {
|
||||
DstValue *stack = thread->data + thread->count;
|
||||
uint32_t size = dst_frame_size(stack);
|
||||
if (n >= size) {
|
||||
/* Push one extra nil to ensure space for tuple */
|
||||
dst_thread_pushnil(vm, thread, n - size + 1);
|
||||
stack = thread->data + thread->count;
|
||||
stack[n].type = DST_TUPLE;
|
||||
stack[n].as.tuple = dst_tuple_end(vm, dst_tuple_begin(vm, 0));
|
||||
dst_frame_size(stack) = n + 1;
|
||||
} else {
|
||||
uint32_t i;
|
||||
DstValue *tuple = dst_tuple_begin(vm, size - n);
|
||||
for (i = n; i < size; ++i)
|
||||
tuple[i - n] = stack[i];
|
||||
stack[n].type = DST_TUPLE;
|
||||
stack[n].as.tuple = dst_tuple_end(vm, tuple);
|
||||
}
|
||||
}
|
||||
|
||||
/* Push a stack frame to a thread, with space for arity arguments. Returns the new
|
||||
* stack. */
|
||||
DstValue *dst_thread_beginframe(Dst *vm, DstThread *thread, DstValue callee, uint32_t arity) {
|
||||
uint32_t frameOffset;
|
||||
DstValue *oldStack, *newStack;
|
||||
|
||||
/* Push the frame */
|
||||
dst_thread_ensure_extra(vm, thread, DST_FRAME_SIZE + arity + 4);
|
||||
oldStack = thread->data + thread->count;
|
||||
frameOffset = dst_frame_size(oldStack) + DST_FRAME_SIZE;
|
||||
newStack = oldStack + frameOffset;
|
||||
dst_frame_prevsize(newStack) = dst_frame_size(oldStack);
|
||||
dst_frame_env(newStack) = NULL;
|
||||
dst_frame_size(newStack) = 0;
|
||||
dst_frame_callee(newStack) = callee;
|
||||
thread->count += frameOffset;
|
||||
|
||||
/* Ensure the extra space and initialize to nil */
|
||||
dst_thread_pushnil(vm, thread, arity);
|
||||
|
||||
/* Return ok */
|
||||
return thread->data + thread->count;
|
||||
}
|
||||
|
||||
/* After pushing arguments to a stack frame created with dst_thread_beginframe, call this
|
||||
* to finalize the frame before starting a function call. */
|
||||
void dst_thread_endframe(Dst *vm, DstThread *thread) {
|
||||
DstValue *stack = thread->data + thread->count;
|
||||
DstValue callee = dst_frame_callee(stack);
|
||||
if (callee.type == DST_FUNCTION) {
|
||||
DstFunction *fn = callee.as.function;
|
||||
uint32_t locals = fn->def->locals;
|
||||
dst_frame_pc(stack) = fn->def->byteCode;
|
||||
if (fn->def->flags & DST_FUNCDEF_FLAG_VARARG) {
|
||||
uint32_t arity = fn->def->arity;
|
||||
dst_thread_tuplepack(vm, thread, arity);
|
||||
}
|
||||
if (dst_frame_size(stack) < locals) {
|
||||
dst_thread_pushnil(vm, thread, locals - dst_frame_size(stack));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Pop a stack frame from the thread. Returns the new stack frame, or
|
||||
* NULL if there are no more frames */
|
||||
DstValue *dst_thread_popframe(Dst *vm, DstThread *thread) {
|
||||
DstValue *stack = thread->data + thread->count;
|
||||
uint32_t prevsize = dst_frame_prevsize(stack);
|
||||
DstValue *nextstack = stack - DST_FRAME_SIZE - prevsize;
|
||||
DstFuncEnv *env = dst_frame_env(stack);
|
||||
|
||||
/* Check for closures */
|
||||
if (env != NULL) {
|
||||
uint32_t size = dst_frame_size(stack);
|
||||
env->thread = NULL;
|
||||
env->stackOffset = size;
|
||||
env->values = malloc(sizeof(DstValue) * size);
|
||||
memcpy(env->values, stack, sizeof(DstValue) * size);
|
||||
}
|
||||
|
||||
/* Shrink stack */
|
||||
thread->count -= DST_FRAME_SIZE + prevsize;
|
||||
|
||||
/* Check if the stack is empty, and if so, return null */
|
||||
if (thread->count)
|
||||
return nextstack;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Count the number of stack frames in a thread */
|
||||
uint32_t dst_thread_countframes(DstThread *thread) {
|
||||
uint32_t count = 0;
|
||||
const DstValue *stack = thread->data + DST_FRAME_SIZE;
|
||||
const DstValue *laststack = thread->data + thread->count;
|
||||
while (stack <= laststack) {
|
||||
++count;
|
||||
stack += dst_frame_size(stack) + DST_FRAME_SIZE;
|
||||
}
|
||||
return count;
|
||||
}
|
21
core/tuple.c
21
core/tuple.c
@ -20,28 +20,31 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "internal.h"
|
||||
#include <dst/dst.h>
|
||||
#include "cache.h"
|
||||
#include "wrap.h"
|
||||
|
||||
/* Create a new empty tuple of the given size. This will return memory
|
||||
* which should be filled with DstValues. The memory will not be collected until
|
||||
* dst_tuple_end is called. */
|
||||
DstValue *dst_tuple_begin(Dst *vm, uint32_t length) {
|
||||
char *data = dst_alloc(vm, DST_MEMORY_TUPLE, 2 * sizeof(uint32_t) + length * sizeof(DstValue));
|
||||
DstValue *dst_tuple_begin(uint32_t length) {
|
||||
char *data = dst_alloc(DST_MEMORY_NONE, 2 * sizeof(uint32_t) + length * sizeof(DstValue));
|
||||
DstValue *tuple = (DstValue *)(data + (2 * sizeof(uint32_t)));
|
||||
dst_tuple_length(tuple) = length;
|
||||
return tuple;
|
||||
}
|
||||
|
||||
/* Finish building a tuple */
|
||||
const DstValue *dst_tuple_end(Dst *vm, DstValue *tuple) {
|
||||
const DstValue *dst_tuple_end(DstValue *tuple) {
|
||||
DstValue check;
|
||||
dst_tuple_hash(tuple) = dst_calchash_array(tuple, dst_tuple_length(tuple));
|
||||
return dst_cache_add(vm, dst_wrap_tuple((const DstValue *) tuple)).as.tuple;
|
||||
check = dst_cache_add(dst_wrap_tuple((const DstValue *) tuple));
|
||||
dst_gc_settype(dst_tuple_raw(check.as.tuple), DST_MEMORY_TUPLE);
|
||||
return check.as.tuple;
|
||||
}
|
||||
|
||||
const DstValue *dst_tuple_n(Dst *vm, DstValue *values, uint32_t n) {
|
||||
DstValue *t = dst_tuple_begin(vm, n);
|
||||
/* Build a tuple with n values */
|
||||
const DstValue *dst_tuple_n(DstValue *values, uint32_t n) {
|
||||
DstValue *t = dst_tuple_begin(n);
|
||||
memcpy(t, values, sizeof(DstValue) * n);
|
||||
return dst_tuple_end(vm, t);
|
||||
return dst_tuple_end(t);
|
||||
}
|
||||
|
@ -20,16 +20,14 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "internal.h"
|
||||
#include "wrap.h"
|
||||
#include <dst/dst.h>
|
||||
|
||||
/* Create new userdata */
|
||||
void *dst_userdata(Dst *vm, uint32_t size, const DstUserType *utype) {
|
||||
DstValue ud;
|
||||
char *data = dst_alloc(vm, DST_USERDATA, sizeof(DstUserdataHeader) + size);
|
||||
void *dst_userdata(uint32_t size, const DstUserType *utype) {
|
||||
char *data = dst_alloc(DST_MEMORY_USERDATA, sizeof(DstUserdataHeader) + size);
|
||||
DstUserdataHeader *header = (DstUserdataHeader *)data;
|
||||
void *user = data + sizeof(DstUserdataHeader);
|
||||
header->size = size;
|
||||
header->type = utype;
|
||||
return dst_wrap_userdata(user);
|
||||
return user;
|
||||
}
|
||||
|
55
core/util.c
55
core/util.c
@ -20,23 +20,28 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "internal.h"
|
||||
#include <dst/dst.h>
|
||||
|
||||
int dst_checkerr(Dst *vm) { return !!vm->flags; }
|
||||
|
||||
void dst_return(Dst *vm, DstValue x) {
|
||||
vm->ret = x;
|
||||
}
|
||||
|
||||
void dst_throw(Dst *vm) {
|
||||
vm->flags = 1;
|
||||
vm->ret = dst_popv(vm);
|
||||
}
|
||||
|
||||
void dst_cerr(Dst *vm, const char *message) {
|
||||
vm->flags = 1;
|
||||
vm->ret = dst_string_cv(vm, message);
|
||||
}
|
||||
/* The DST value types in order. These types can be used as
|
||||
* mnemonics instead of a bit pattern for type checking */
|
||||
const char *dst_type_names[15] = {
|
||||
"nil",
|
||||
"real",
|
||||
"integer",
|
||||
"boolean",
|
||||
"string",
|
||||
"symbol",
|
||||
"array",
|
||||
"tuple",
|
||||
"table",
|
||||
"struct",
|
||||
"fiber",
|
||||
"buffer",
|
||||
"function",
|
||||
"cfunction",
|
||||
"userdata"
|
||||
};
|
||||
|
||||
/* Read both tuples and arrays as c pointers + uint32_t length. Return 1 if the
|
||||
* view can be constructed, 0 if an invalid type. */
|
||||
@ -84,6 +89,18 @@ int dst_hashtable_view(DstValue tab, const DstValue **data, uint32_t *cap) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Convert a real to int */
|
||||
int64_t dst_real_to_integer(double real) {
|
||||
/* TODO - consider c undefined behavior */
|
||||
return (int64_t) real;
|
||||
}
|
||||
|
||||
/* Convert an integer to a real */
|
||||
double dst_integer_to_real(int64_t integer) {
|
||||
/* TODO - consider c undefined behavior */
|
||||
return (double) integer;
|
||||
}
|
||||
|
||||
/* Convert an index used by the capi to an absolute index */
|
||||
uint32_t dst_startrange(int64_t index, uint32_t modulo) {
|
||||
if (index < 0) index += modulo;
|
||||
@ -94,11 +111,3 @@ uint32_t dst_startrange(int64_t index, uint32_t modulo) {
|
||||
uint32_t dst_endrange(int64_t index, uint32_t modulo) {
|
||||
return dst_startrange(index, modulo + 1);
|
||||
}
|
||||
|
||||
/* Convert a possibly negative index to a positive index on the current stack. */
|
||||
int64_t dst_normalize_index(Dst *vm, int64_t index) {
|
||||
if (index < 0) {
|
||||
index += dst_frame_size(dst_thread_stack(vm->thread));
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
19
core/value.c
19
core/value.c
@ -20,7 +20,7 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "internal.h"
|
||||
#include <dst/dst.h>
|
||||
|
||||
/*
|
||||
* Define a number of functions that can be used internally on ANY DstValue.
|
||||
@ -82,7 +82,8 @@ uint32_t dst_hash(DstValue x) {
|
||||
default:
|
||||
if (sizeof(double) == sizeof(void *)) {
|
||||
/* Assuming 8 byte pointer */
|
||||
hash = x.as.dwords[0] ^ x.as.dwords[1];
|
||||
uint64_t i = x.as.integer;
|
||||
hash = (i >> 32) ^ (i & 0xFFFFFFFF);
|
||||
} else {
|
||||
/* Assuming 4 byte pointer (or smaller) */
|
||||
hash = (uint32_t) x.as.pointer;
|
||||
@ -97,7 +98,7 @@ uint32_t dst_calchash_array(const DstValue *array, uint32_t len) {
|
||||
const DstValue *end = array + len;
|
||||
uint32_t hash = 5381;
|
||||
while (array < end)
|
||||
hash = (hash << 5) + hash + dst_value_hash(*array++);
|
||||
hash = (hash << 5) + hash + dst_hash(*array++);
|
||||
return hash;
|
||||
}
|
||||
|
||||
@ -138,6 +139,15 @@ int dst_compare(DstValue x, DstValue y) {
|
||||
return x.as.boolean ? 1 : -1;
|
||||
}
|
||||
case DST_REAL:
|
||||
|
||||
/* Check for nans to ensure total order */
|
||||
if (x.as.real != x.as.real)
|
||||
return y.as.real != y.as.real
|
||||
? 0
|
||||
: -1;
|
||||
if (y.as.real != y.as.real)
|
||||
return 1;
|
||||
|
||||
if (x.as.real == y.as.real) {
|
||||
return 0;
|
||||
} else {
|
||||
@ -159,7 +169,7 @@ int dst_compare(DstValue x, DstValue y) {
|
||||
uint32_t ylen = dst_tuple_length(y.as.tuple);
|
||||
uint32_t count = xlen < ylen ? xlen : ylen;
|
||||
for (i = 0; i < count; ++i) {
|
||||
int comp = dst_value_compare(x.as.tuple[i], y.as.tuple[i]);
|
||||
int comp = dst_compare(x.as.tuple[i], y.as.tuple[i]);
|
||||
if (comp != 0) return comp;
|
||||
}
|
||||
if (xlen < ylen)
|
||||
@ -169,6 +179,7 @@ int dst_compare(DstValue x, DstValue y) {
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
/* TODO - how should structs compare by default? For now, just use pointers. */
|
||||
default:
|
||||
if (x.as.string == y.as.string) {
|
||||
return 0;
|
||||
|
10
core/wrap.c
10
core/wrap.c
@ -20,7 +20,7 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "wrap.h"
|
||||
#include <dst/dst.h>
|
||||
|
||||
/* Wrapper functions wrap a data type that is used from C into a
|
||||
* dst value, which can then be used in dst internal functions. Use
|
||||
@ -40,11 +40,7 @@ DstValue dst_wrap_##NAME(TYPE x) {\
|
||||
y.type = DTYPE;\
|
||||
y.as.UM = x;\
|
||||
return y;\
|
||||
}\
|
||||
\
|
||||
TYPE dst_unwrap_##NAME(Dst *vm, int64_t i) {\
|
||||
return dst_getv(vm, i).as.UM;\
|
||||
}\
|
||||
}
|
||||
|
||||
DST_WRAP_DEFINE(real, double, DST_REAL, real)
|
||||
DST_WRAP_DEFINE(integer, int64_t, DST_INTEGER, integer)
|
||||
@ -54,7 +50,7 @@ DST_WRAP_DEFINE(symbol, const uint8_t *, DST_SYMBOL, string)
|
||||
DST_WRAP_DEFINE(array, DstArray *, DST_ARRAY, array)
|
||||
DST_WRAP_DEFINE(tuple, const DstValue *, DST_TUPLE, tuple)
|
||||
DST_WRAP_DEFINE(struct, const DstValue *, DST_STRUCT, st)
|
||||
DST_WRAP_DEFINE(thread, DstThread *, DST_THREAD, thread)
|
||||
DST_WRAP_DEFINE(thread, DstFiber *, DST_FIBER, fiber)
|
||||
DST_WRAP_DEFINE(buffer, DstBuffer *, DST_BUFFER, buffer)
|
||||
DST_WRAP_DEFINE(function, DstFunction *, DST_FUNCTION, function)
|
||||
DST_WRAP_DEFINE(cfunction, DstCFunction, DST_CFUNCTION, cfunction)
|
||||
|
61
core/wrap.h
61
core/wrap.h
@ -1,61 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef DST_WRAP_H_defined
|
||||
#define DST_WRAP_H_defined
|
||||
|
||||
#include <dst/dst.h>
|
||||
|
||||
/* Wrap data in DstValue */
|
||||
DstValue dst_wrap_nil();
|
||||
DstValue dst_wrap_real(double x);
|
||||
DstValue dst_wrap_integer(int64_t x);
|
||||
DstValue dst_wrap_boolean(int x);
|
||||
DstValue dst_wrap_string(const uint8_t *x);
|
||||
DstValue dst_wrap_symbol(const uint8_t *x);
|
||||
DstValue dst_wrap_array(DstArray *x);
|
||||
DstValue dst_wrap_tuple(const DstValue *x);
|
||||
DstValue dst_wrap_struct(const DstValue *x);
|
||||
DstValue dst_wrap_thread(DstThread *x);
|
||||
DstValue dst_wrap_buffer(DstBuffer *x);
|
||||
DstValue dst_wrap_function(DstFunction *x);
|
||||
DstValue dst_wrap_cfunction(DstCFunction x);
|
||||
DstValue dst_wrap_table(DstTable *x);
|
||||
DstValue dst_wrap_userdata(void *x);
|
||||
|
||||
/* Unwrap values */
|
||||
double dst_unwrap_real(Dst *vm, int64_t i);
|
||||
int64_t dst_unwrap_integer(Dst *vm, int64_t i);
|
||||
int dst_unwrap_boolean(Dst *vm, int64_t i);
|
||||
const uint8_t *dst_unwrap_string(Dst *vm, int64_t i);
|
||||
const uint8_t *dst_unwrap_symbol(Dst *vm, int64_t i);
|
||||
DstArray *dst_unwrap_array(Dst *vm, int64_t i);
|
||||
const DstValue *dst_unwrap_tuple(Dst *vm, int64_t i);
|
||||
const DstValue *dst_unwrap_struct(Dst *vm, int64_t i);
|
||||
DstThread *dst_unwrap_thread(Dst *vm, int64_t i);
|
||||
DstBuffer *dst_unwrap_buffer(Dst *vm, int64_t i);
|
||||
DstFunction *dst_unwrap_function(Dst *vm, int64_t i);
|
||||
DstCFunction dst_unwrap_cfunction(Dst *vm, int64_t i);
|
||||
DstTable *dst_unwrap_table(Dst *vm, int64_t i);
|
||||
void *dst_unwrap_userdata(Dst *vm, int64_t i);
|
||||
|
||||
#endif
|
@ -20,7 +20,6 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "internal.h"
|
||||
#include "cache.h"
|
||||
#include <dst/dst.h>
|
||||
|
||||
@ -71,7 +70,7 @@ const char *dst_value_get(DstValue ds, DstValue key, DstValue *out) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void dst_get(Dst *vm) {
|
||||
int dst_get(Dst *vm) {
|
||||
DstValue ds = dst_popv(vm);
|
||||
DstValue key = dst_popv(vm);
|
||||
DstValue ret;
|
@ -3,7 +3,6 @@
|
||||
# simply construct a FuncDef from this file. This file is also parsed
|
||||
# in the same markup as dst itself (extended S expressions)
|
||||
{
|
||||
stack 10
|
||||
arity 3
|
||||
signature (integer integer integer integer)
|
||||
source "source file path"
|
||||
@ -38,6 +37,7 @@
|
||||
0x0000123400000135
|
||||
...
|
||||
]
|
||||
# The number of slots available for the function.
|
||||
# Slots can be named as well for convenience.
|
||||
slots [
|
||||
x
|
||||
|
17
unittests/array_test.c
Normal file
17
unittests/array_test.c
Normal file
@ -0,0 +1,17 @@
|
||||
#include "unit.h"
|
||||
#include <dst/dst.h>
|
||||
|
||||
int main() {
|
||||
int64_t i;
|
||||
DstArray *array = dst_array(10);
|
||||
assert(array->capacity == 10);
|
||||
assert(array->count == 0);
|
||||
for (i = 0; i < 500; ++i)
|
||||
dst_array_push(array, dst_wrap_integer(i));
|
||||
for (i = 0; i < 500; ++i)
|
||||
assert(array->data[i].type == DST_INTEGER && array->data[i].as.integer == i);
|
||||
for (i = 0; i < 200; ++i)
|
||||
dst_array_pop(array);
|
||||
assert(array->count == 300);
|
||||
return 0;
|
||||
}
|
31
unittests/unit.h
Normal file
31
unittests/unit.h
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/* Header for unit testing of dst components */
|
||||
|
||||
#ifndef UNIT_H_U09UBW3V
|
||||
#define UNIT_H_U09UBW3V
|
||||
|
||||
#include <dst/dst.h>
|
||||
#include <assert.h>
|
||||
|
||||
#endif /* end of include guard: UNIT_H_U09UBW3V */
|
Loading…
Reference in New Issue
Block a user