1
0
mirror of https://github.com/janet-lang/janet synced 2025-07-07 04:22:54 +00:00
This commit is contained in:
bakpakin 2017-11-01 17:53:43 -04:00
parent 3efd400025
commit a2ee028bd5
30 changed files with 2929 additions and 3105 deletions

View File

@ -13,8 +13,8 @@ PREFIX=/usr/local
DST_TARGET=dst DST_TARGET=dst
DST_XXD=xxd DST_XXD=xxd
# Use gdb. On mac use lldb # Use gdb. On mac use lldb
DEBUGGER=gdb DEBUGGER=lldb
DST_INTERNAL_HEADERS=$(addprefix core/, internal.h bootstrap.h) DST_INTERNAL_HEADERS=$(addprefix core/, internal.h bootstrap.h cache.h)
DST_HEADERS=$(addprefix include/dst/, dst.h) DST_HEADERS=$(addprefix include/dst/, dst.h)
############################# #############################
@ -38,9 +38,9 @@ $(DST_XXD): libs/xxd.c
##### The core vm and runtime ##### ##### The core vm and runtime #####
################################### ###################################
DST_CORE_SOURCES=$(addprefix core/,\ DST_CORE_SOURCES=$(addprefix core/,\
util.c wrap.c\ 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\ value.c vm.c ds.c gc.c thread.c serialize.c tuple.c\
string.c bootstrap_parse.c client.c) string.c bootstrap_parse.c client.c cache.c struct.c)
DST_CORE_OBJECTS=$(patsubst %.c,%.o,$(DST_CORE_SOURCES)) DST_CORE_OBJECTS=$(patsubst %.c,%.o,$(DST_CORE_SOURCES))
$(DST_TARGET): $(DST_CORE_OBJECTS) $(DST_LANG_HEADERS) $(DST_TARGET): $(DST_CORE_OBJECTS) $(DST_LANG_HEADERS)

89
core/array.c Normal file
View File

@ -0,0 +1,89 @@
/*
* 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 <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));
DstValue *data = (DstValue *) malloc(sizeof(DstValue) * capacity);
if (NULL == array || NULL == data) {
DST_OUT_OF_MEMORY;
}
array->count = 0;
array->capacity = capacity;
array->data = data;
return array;
}
/* Ensure the array has enough capacity for elements */
void dst_array_ensure(Dst *vm, DstArray *array, uint32_t capacity) {
DstValue *newData;
DstValue *old = array->data;
if (capacity <= array->capacity) return;
newData = realloc(old, capacity * sizeof(DstValue));
if (NULL == newData) {
DST_OUT_OF_MEMORY;
}
array->data = newData;
array->capacity = capacity;
}
/* Set the count of an array. Extend with nil if needed. */
void dst_array_setcount(Dst *vm, DstArray *array, uint32_t count) {
if (count > array->count) {
uint32_t i;
dst_array_ensure(vm, array, count + 1);
for (i = array->count; i < count; ++i)
array->data[i].type = DST_NIL;
}
array->count = count;
}
/* Push a value to the top of the array */
void dst_array_push(Dst *vm, DstArray *array, DstValue x) {
uint32_t newcount = array->count + 1;
if (newcount >= array->capacity) }
dst_array_ensure(vm, array, newcount * 2);
}
array->data[array->count] = x;
array->count = newcount;
}
/* Pop a value from the top of the array */
DstValue dst_array_pop(DstArray *array) {
if (array->count) {
return array->data[--array->count];
} else {
return dst_wrap_nil();
}
}
/* Look at the last value in the array */
DstValue dst_array_peek(DstArray *array) {
if (array->count) {
return array->data[array->count - 1];
} else {
return dst_wrap_nil();
}
}

0
core/asm.c Normal file
View File

132
core/buffer.c Normal file
View File

@ -0,0 +1,132 @@
/*
* 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 <dst/dst.h>
/* Initialize a buffer */
DstBuffer *dst_buffer(Dst *vm, uint32_t capacity) {
DstBuffer *buffer = dst_alloc(vm, DST_MEMORY_BUFFER, sizeof(DstBuffer));
uint8_t *data = malloc(sizeof(uint8_t) * capacity);
if (NULL == data) {
DST_OUT_OF_MEMORY;
}
buffer->count = 0;
buffer->capacity = capacity;
return buffer;
}
/* Ensure that the buffer has enough internal capacity */
void dst_buffer_ensure(Dst *vm, DstBuffer *buffer, uint32_t capacity) {
uint8_t *newData;
uint8_t *old = buffer->data;
if (capacity <= buffer->capacity) return;
newData = realloc(old, capacity * sizeof(uint8_t));
if (NULL == newData) {
DST_OUT_OF_MEMORY;
}
buffer->data = newData;
buffer->capacity = 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) {
uint32_t newCount = buffer->count + n;
if (newCount > buffer->capacity) {
uint32_t newCapacity = newCount * 2;
uint8_t *newData = realloc(buffer->data, newCapacity * sizeof(uint8_t));
if (NULL == newData) {
DST_OUT_OF_MEMORY;
}
buffer->data = newData;
buffer->capacity = newCapacity;
}
}
/* Push multiple bytes into the buffer */
void dst_buffer_push_bytes(Dst *vm, 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_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) {
uint32_t len = 0;
while (cstring[len]) ++len;
dst_buffer_push_bytes(vm, 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) {
uint32_t newSize = buffer->count + 1;
if (newSize > buffer->capacity) {
dst_buffer_ensure(vm, 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) {
uint32_t newSize = buffer->count + 2;
if (newSize > buffer->capacity) {
dst_buffer_ensure(vm, buffer, 2 * newSize);
}
buffer->data[buffer->count] = x & 0xFF;
buffer->data[buffer->count + 1] = (x >> 8) & 0xFF;
buffer->count = newSize;
}
/* Push a 32 bit unsigned integer to the buffer */
void dst_buffer_push_u32(Dst *vm, DstBuffer *buffer, uint32_t x) {
uint32_t newSize = buffer->count + 4;
if (newSize > buffer->capacity) {
dst_buffer_ensure(vm, buffer, 2 * newSize);
}
buffer->data[buffer->count] = x & 0xFF;
buffer->data[buffer->count + 1] = (x >> 8) & 0xFF;
buffer->data[buffer->count + 2] = (x >> 16) & 0xFF;
buffer->data[buffer->count + 3] = (x >> 24) & 0xFF;
buffer->count = newSize;
}
/* Push a 64 bit unsigned integer to the buffer */
void dst_buffer_push_u64(Dst *vm, DstBuffer *buffer, uint64_t x) {
uint32_t newSize = buffer->count + 8;
if (newSize > buffer->capacity) {
dst_buffer_ensure(vm, buffer, 2 * newSize);
}
buffer->data[buffer->count] = x & 0xFF;
buffer->data[buffer->count + 1] = (x >> 8) & 0xFF;
buffer->data[buffer->count + 2] = (x >> 16) & 0xFF;
buffer->data[buffer->count + 3] = (x >> 24) & 0xFF;
buffer->data[buffer->count + 4] = (x >> 32) & 0xFF;
buffer->data[buffer->count + 5] = (x >> 40) & 0xFF;
buffer->data[buffer->count + 6] = (x >> 48) & 0xFF;
buffer->data[buffer->count + 7] = (x >> 56) & 0xFF;
buffer->count = newSize;
}

240
core/cache.c Normal file
View File

@ -0,0 +1,240 @@
/*
* 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 "cache.h"
/* All immutable values are cached in a global hash table. When an immutable
* value is created, this hashtable is checked to see if the value exists. If it
* does, return the cached copy instead. This trades creation time and memory for
* fast equality, which is especially useful for symbols and strings. This may not
* be useful for structs and tuples, in which case it may be removed. However, in cases
* where ther are many copies of the same tuple in the program, this approach may
* save memory. Values are removed from the cache when they are garbage collected.
*/
/* Check if two not necesarrily finalized immutable values
* are equal. Does caching logic */
static int dst_cache_equal(DstValue x, DstValue y) {
uint32_t i, len;
if (x.type != y.type) return 0;
switch (x.type) {
/* Don't bother implementing equality checks for all types. We only care
* about immutable data structures */
default:
return 0;
case DST_STRING:
if (dst_string_hash(x.as.string) != dst_string_hash(y.as.string)) return 0;
if (dst_string_length(x.as.string) != dst_string_length(y.as.string)) return 0;
len = dst_string_length(x.as.string);
for (i = 0; i < len; ++i)
if (x.as.string[i] != y.as.string[i])
return 0;
return 1;
case DST_STRUCT:
if (dst_struct_hash(x.as.st) != dst_struct_hash(y.as.st)) return 0;
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]))
return 0;
return 1;
case DST_TUPLE:
if (dst_tuple_hash(x.as.tuple) != dst_tuple_hash(y.as.tuple)) return 0;
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]))
return 0;
return 1;
}
}
/* Check if a value x is equal to a string. Special version of
* dst_cache_equal */
static int dst_cache_strequal(DstValue x, const uint8_t *str, uint32_t len, uint32_t hash) {
uint32_t i;
if (x.type != DST_STRING) return 0;
if (dst_string_hash(x.as.string) != hash) return 0;
if (dst_string_length(x.as.string) != len) return 0;
for (i = 0; i < len; ++i)
if (x.as.string[i] != str[i])
return 0;
return 1;
}
/* 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) {
uint32_t bounds[4];
uint32_t i, j, index;
uint32_t hash = dst_value_hash(key);
DstValue *firstEmpty = NULL;
index = hash % vm->cache_capacity;
bounds[0] = index;
bounds[1] = 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];
/* Check empty spots */
if (test.type == DST_NIL) {
if (firstEmpty == NULL)
firstEmpty = 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;
continue;
}
if (dst_cache_equal(test, key)) {
/* Replace first deleted */
*success = 1;
if (firstEmpty != NULL) {
*firstEmpty = test;
vm->cache[i].type = DST_BOOLEAN;
return firstEmpty;
}
return vm->cache + i;
}
}
notfound:
*success = 0;
return firstEmpty;
}
/* 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,
const uint8_t *str,
uint32_t len,
uint32_t hash,
int *success) {
uint32_t bounds[4];
uint32_t i, j, index;
DstValue *firstEmpty = NULL;
index = hash % vm->cache_capacity;
bounds[0] = index;
bounds[1] = 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];
/* Check empty spots */
if (test.type == DST_NIL) {
if (firstEmpty == NULL)
firstEmpty = 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;
continue;
}
if (dst_cache_strequal(test, str, len, hash)) {
/* Replace first deleted */
*success = 1;
if (firstEmpty != NULL) {
*firstEmpty = test;
vm->cache[i].type = DST_BOOLEAN;
return firstEmpty;
}
return vm->cache + i;
}
}
notfound:
*success = 0;
return firstEmpty;
}
/* Resize the cache. */
static void dst_cache_resize(Dst *vm, uint32_t newCapacity) {
uint32_t i, oldCapacity;
DstValue *oldCache = 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;
/* 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);
if (status || bucket == NULL) {
/* there was a problem with the algorithm. */
break;
}
*bucket = x;
}
}
/* Free the old cache */
free(oldCache);
}
/* 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) {
int status;
dst_cache_resize(vm, vm->cache_count * 4);
bucket = dst_cache_find(vm, x, &status);
}
/* Add x to the cache */
vm->cache_count++;
*bucket = x;
return x;
}
/* Add a value to the cache */
DstValue dst_cache_add(Dst *vm, DstValue x) {
int status = 0;
DstValue *bucket = dst_cache_find(vm, x, &status);
if (!status) {
return dst_cache_add_bucket(vm, x, bucket);
} else {
return *bucket;
}
}
/* Remove a value from the cache */
void dst_cache_remove(Dst *vm, DstValue x) {
int status = 0;
DstValue *bucket = dst_cache_find(vm, x, &status);
if (status) {
vm->cache_count--;
vm->cache_deleted++;
bucket->type = DST_BOOLEAN;
}
}

View File

@ -20,19 +20,20 @@
* IN THE SOFTWARE. * IN THE SOFTWARE.
*/ */
#ifndef DST_BOOTSTRAP_H_defined #ifndef DST_CACHE_H_defined
#define DST_BOOTSTRAP_H_defined #define DST_CACHE_H_defined
#define PARSE_OK 0 #include <dst/dst.h>
#define PARSE_ERROR 1 #include "internal.h"
#define PARSE_UNEXPECTED_EOS 2
int dst_parseb(Dst *vm, uint32_t dest, const uint8_t *src, const uint8_t **newsrc, uint32_t len); DstValue dst_cache_add(Dst *vm, DstValue x);
DstValue *dst_cache_strfind(Dst *vm,
const uint8_t *str,
uint32_t len,
uint32_t hash,
int *success);
DstValue dst_cache_add_bucket(Dst *vm, DstValue x, DstValue *bucket);
/* Parse a c string */ void dst_cache_remove(Dst *vm, DstValue x);
int dst_parsec(Dst *vm, uint32_t dest, const char *src);
/* Parse a DST char seq (Buffer, String, Symbol) */ #endif
int dst_parse(Dst *vm, uint32_t dest, uint32_t src);
#endif /* DST_BOOTSTRAP_H_defined */

View File

@ -25,57 +25,40 @@
#include <dst/dst.h> #include <dst/dst.h>
#include "bootstrap.h" #include "bootstrap.h"
void teststr(Dst *vm, const char *src) {
/* Simple read line functionality */ uint32_t len = 0;
static char *dst_getline() { const uint8_t *bytes;
char *line = malloc(100); const char *ns = NULL;
char *linep = line; int status = dst_parsec(vm, src, &ns);
size_t lenmax = 100; if (status) {
size_t len = lenmax; printf("Parse failed: ");
int c; bytes = dst_bytes(vm, -1, &len);
if (line == NULL) for (uint32_t i = 0; i < len; i++)
return NULL; putc(bytes[i], stdout);
for (;;) { putc('\n', stdout);
c = fgetc(stdin); printf("%s\n", src);
if (c == EOF) for (const char *scan = src + 1; scan < ns; ++scan)
break; putc(' ', stdout);
if (--len == 0) { putc('^', stdout);
len = lenmax; putc('\n', stdout);
char *linen = realloc(linep, lenmax *= 2); return;
if (linen == NULL) {
free(linep);
return NULL;
} }
line = linen + (line - linep); dst_description(vm);
linep = linen; bytes = dst_bytes(vm, -1, &len);
} for (uint32_t i = 0; i < len; i++)
if ((*line++ = c) == '\n') putc(bytes[i], stdout);
break; putc('\n', stdout);
}
*line = '\0';
return linep;
} }
int main(int argc, const char **argv) { int main() {
Dst *vm = dst_init(); Dst *vm = dst_init();
for (;;) {
char *line = dst_getline(); teststr(vm, "[+ 1 2 3 \"porkpie\" ]");
if (line) { teststr(vm, "(+ 1 2 \t asdajs 1035.89 3)");
uint32_t len = 0; teststr(vm, "[+ 1 2 :bokr]");
int status = dst_parsec(vm, 0, line); teststr(vm, "{+ 1 2 3}");
if (status == PARSE_OK) {
dst_description(vm, 0, 0);
}
const uint8_t *b = dst_bytes(vm, 0, &len);
for (uint32_t i = 0; i < len; ++i) {
putc(b[i]);
}
putc('\n');
} else {
break;
}
}
dst_deinit(vm); dst_deinit(vm);
return 0; return 0;

View File

@ -373,13 +373,13 @@ static uint16_t compiler_add_literal(DstCompiler *c, DstScope *scope, DstValue x
uint16_t literalIndex = 0; uint16_t literalIndex = 0;
if (checkDup.type != DST_NIL) { if (checkDup.type != DST_NIL) {
/* An equal literal is already registered in the current scope */ /* An equal literal is already registered in the current scope */
return (uint16_t) checkDup.data.integer; return (uint16_t) checkDup.as.integer;
} else { } else {
/* Add our literal for tracking */ /* Add our literal for tracking */
DstValue valIndex; DstValue valIndex;
valIndex.type = DST_INTEGER; valIndex.type = DST_INTEGER;
literalIndex = scope->literalsArray->count; literalIndex = scope->literalsArray->count;
valIndex.data.integer = literalIndex; valIndex.as.integer = literalIndex;
dst_table_put(c->vm, scope->literals, x, valIndex); dst_table_put(c->vm, scope->literals, x, valIndex);
dst_array_push(c->vm, scope->literalsArray, x); dst_array_push(c->vm, scope->literalsArray, x);
} }
@ -394,7 +394,7 @@ static uint16_t compiler_declare_symbol(DstCompiler *c, DstScope *scope, DstValu
c_error(c, "expected symbol"); c_error(c, "expected symbol");
target = compiler_get_local(c, scope); target = compiler_get_local(c, scope);
x.type = DST_INTEGER; x.type = DST_INTEGER;
x.data.integer = target + (flags << 16); x.as.integer = target + (flags << 16);
dst_table_put(c->vm, scope->locals, sym, x); dst_table_put(c->vm, scope->locals, sym, x);
return target; return target;
} }
@ -409,8 +409,8 @@ static int symbol_resolve(DstCompiler *c, DstValue x, uint16_t *level, uint16_t
check = dst_table_get(scope->locals, x); check = dst_table_get(scope->locals, x);
if (check.type != DST_NIL) { if (check.type != DST_NIL) {
*level = currentLevel - scope->level; *level = currentLevel - scope->level;
*index = (uint16_t) (check.data.integer & 0xFFFF); *index = (uint16_t) (check.as.integer & 0xFFFF);
if (flags) *flags = check.data.integer >> 16; if (flags) *flags = check.as.integer >> 16;
return 1; return 1;
} }
scope = scope->parent; scope = scope->parent;
@ -422,7 +422,7 @@ static int symbol_resolve(DstCompiler *c, DstValue x, uint16_t *level, uint16_t
DstTable *metas = dst_env_meta(c->vm, c->env); DstTable *metas = dst_env_meta(c->vm, c->env);
DstValue maybeMeta = dst_table_get(metas, x); DstValue maybeMeta = dst_table_get(metas, x);
if (maybeMeta.type == DST_TABLE) { if (maybeMeta.type == DST_TABLE) {
DstValue isMutable = dst_table_get(maybeMeta.data.table, dst_string_cv(c->vm, "mutable")); DstValue isMutable = dst_table_get(maybeMeta.as.table, dst_string_cv(c->vm, "mutable"));
if (dst_truthy(isMutable)) { if (dst_truthy(isMutable)) {
if (flags) *flags = DST_LOCAL_FLAG_MUTABLE; if (flags) *flags = DST_LOCAL_FLAG_MUTABLE;
*out = check; *out = check;
@ -464,25 +464,25 @@ static Slot compile_nonref_type(DstCompiler *c, FormOptions opts, DstValue x) {
dst_buffer_push_u16(c->vm, buffer, DST_OP_NIL); dst_buffer_push_u16(c->vm, buffer, DST_OP_NIL);
dst_buffer_push_u16(c->vm, buffer, ret.index); dst_buffer_push_u16(c->vm, buffer, ret.index);
} else if (x.type == DST_BOOLEAN) { } else if (x.type == DST_BOOLEAN) {
dst_buffer_push_u16(c->vm, buffer, x.data.boolean ? DST_OP_TRU : DST_OP_FLS); dst_buffer_push_u16(c->vm, buffer, x.as.boolean ? DST_OP_TRU : DST_OP_FLS);
dst_buffer_push_u16(c->vm, buffer, ret.index); dst_buffer_push_u16(c->vm, buffer, ret.index);
} else if (x.type == DST_REAL) { } else if (x.type == DST_REAL) {
dst_buffer_push_u16(c->vm, buffer, DST_OP_F64); dst_buffer_push_u16(c->vm, buffer, DST_OP_F64);
dst_buffer_push_u16(c->vm, buffer, ret.index); dst_buffer_push_u16(c->vm, buffer, ret.index);
dst_buffer_push_real(c->vm, buffer, x.data.real); dst_buffer_push_real(c->vm, buffer, x.as.real);
} else if (x.type == DST_INTEGER) { } else if (x.type == DST_INTEGER) {
if (x.data.integer <= 32767 && x.data.integer >= -32768) { if (x.as.integer <= 32767 && x.as.integer >= -32768) {
dst_buffer_push_u16(c->vm, buffer, DST_OP_I16); dst_buffer_push_u16(c->vm, buffer, DST_OP_I16);
dst_buffer_push_u16(c->vm, buffer, ret.index); dst_buffer_push_u16(c->vm, buffer, ret.index);
dst_buffer_push_i16(c->vm, buffer, x.data.integer); dst_buffer_push_i16(c->vm, buffer, x.as.integer);
} else if (x.data.integer <= 2147483647 && x.data.integer >= -2147483648) { } else if (x.as.integer <= 2147483647 && x.as.integer >= -2147483648) {
dst_buffer_push_u16(c->vm, buffer, DST_OP_I32); dst_buffer_push_u16(c->vm, buffer, DST_OP_I32);
dst_buffer_push_u16(c->vm, buffer, ret.index); dst_buffer_push_u16(c->vm, buffer, ret.index);
dst_buffer_push_i32(c->vm, buffer, x.data.integer); dst_buffer_push_i32(c->vm, buffer, x.as.integer);
} else { } else {
dst_buffer_push_u16(c->vm, buffer, DST_OP_I64); dst_buffer_push_u16(c->vm, buffer, DST_OP_I64);
dst_buffer_push_u16(c->vm, buffer, ret.index); dst_buffer_push_u16(c->vm, buffer, ret.index);
dst_buffer_push_i64(c->vm, buffer, x.data.integer); dst_buffer_push_i64(c->vm, buffer, x.as.integer);
} }
} else { } else {
c_error(c, "expected boolean, nil, or number type"); c_error(c, "expected boolean, nil, or number type");
@ -805,14 +805,14 @@ static Slot compile_function(DstCompiler *c, FormOptions opts, const DstValue *f
/* Define the function parameters */ /* Define the function parameters */
if (form[current].type != DST_ARRAY) if (form[current].type != DST_ARRAY)
c_error(c, "expected function arguments array"); c_error(c, "expected function arguments array");
params = form[current++].data.array; params = form[current++].as.array;
arity = params->count; arity = params->count;
for (i = 0; i < params->count; ++i) { for (i = 0; i < params->count; ++i) {
DstValue param = params->data[i]; DstValue param = params->data[i];
if (param.type != DST_SYMBOL) if (param.type != DST_SYMBOL)
c_error(c, "function parameters should be symbols"); c_error(c, "function parameters should be symbols");
/* Check for varargs */ /* Check for varargs */
if (equal_cstr(param.data.string, "&")) { if (equal_cstr(param.as.string, "&")) {
if (i != params->count - 1) { if (i != params->count - 1) {
c_error(c, "& is reserved for vararg argument in function"); c_error(c, "& is reserved for vararg argument in function");
} }
@ -838,7 +838,7 @@ static Slot compile_function(DstCompiler *c, FormOptions opts, const DstValue *f
DstFuncDef *def = compiler_gen_funcdef(c, buffer->count - sizeBefore, arity, varargs); DstFuncDef *def = compiler_gen_funcdef(c, buffer->count - sizeBefore, arity, varargs);
/* Add this FuncDef as a literal in the outer scope */ /* Add this FuncDef as a literal in the outer scope */
newVal.type = DST_FUNCDEF; newVal.type = DST_FUNCDEF;
newVal.data.def = def; newVal.as.def = def;
literalIndex = compiler_add_literal(c, scope, newVal); literalIndex = compiler_add_literal(c, scope, newVal);
dst_buffer_push_u16(c->vm, buffer, DST_OP_CLN); dst_buffer_push_u16(c->vm, buffer, DST_OP_CLN);
dst_buffer_push_u16(c->vm, buffer, ret.index); dst_buffer_push_u16(c->vm, buffer, ret.index);
@ -1070,7 +1070,7 @@ static SpecialFormHelper get_special(const DstValue *form) {
const uint8_t *name; const uint8_t *name;
if (dst_tuple_length(form) < 1 || form[0].type != DST_SYMBOL) if (dst_tuple_length(form) < 1 || form[0].type != DST_SYMBOL)
return NULL; return NULL;
name = form[0].data.string; name = form[0].as.string;
/* If we have a symbol with a zero length name, we have other /* If we have a symbol with a zero length name, we have other
* problems. */ * problems. */
if (dst_string_length(name) == 0) if (dst_string_length(name) == 0)
@ -1292,13 +1292,13 @@ static Slot compile_value(DstCompiler *c, FormOptions opts, DstValue x) {
ret = compile_symbol(c, opts, x); ret = compile_symbol(c, opts, x);
break; break;
case DST_TUPLE: case DST_TUPLE:
ret = compile_form(c, opts, x.data.tuple); ret = compile_form(c, opts, x.as.tuple);
break; break;
case DST_ARRAY: case DST_ARRAY:
ret = compile_array(c, opts, x.data.array); ret = compile_array(c, opts, x.as.array);
break; break;
case DST_TABLE: case DST_TABLE:
ret = compile_table(c, opts, x.data.table); ret = compile_table(c, opts, x.as.table);
break; break;
default: default:
ret = compile_literal(c, opts, x); ret = compile_literal(c, opts, x);

1066
core/ds.c

File diff suppressed because it is too large Load Diff

View File

@ -1,134 +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"
static DstTable *dst_env_keytab(Dst *vm, DstTable *env, const char *keyword) {
DstTable *tab;
DstValue key = dst_string_cv(vm, keyword);
DstValue maybeTab = dst_table_get(env, key);
if (maybeTab.type != DST_TABLE) {
tab = dst_table(vm, 10);
dst_table_put(vm, env, key, dst_wrap_table(tab));
} else {
tab = maybeTab.data.table;
}
return tab;
}
DstTable *dst_env_nils(Dst *vm, DstTable *env) {
return dst_env_keytab(vm, env, "nils");
}
DstTable *dst_env_meta(Dst *vm, DstTable *env) {
return dst_env_keytab(vm, env, "meta");
}
/* Add many global variables and bind to nil */
static void mergenils(Dst *vm, DstTable *destEnv, DstTable *nils) {
const DstValue *data = nils->data;
uint32_t len = nils->capacity;
uint32_t i;
DstTable *destNils = dst_env_nils(vm, destEnv);
for (i = 0; i < len; i += 2) {
if (data[i].type == DST_SYMBOL) {
dst_table_put(vm, destEnv, data[i], dst_wrap_nil());
dst_table_put(vm, destNils, data[i], dst_wrap_boolean(1));
}
}
}
/* Add many global variable metadata */
static void mergemeta(Dst *vm, DstTable *destEnv, DstTable *meta) {
const DstValue *data = meta->data;
uint32_t len = meta->capacity;
uint32_t i;
DstTable *destMeta = dst_env_meta(vm, destEnv);
for (i = 0; i < len; i += 2) {
if (data[i].type == DST_SYMBOL) {
dst_table_put(vm, destMeta, data[i], data[i + 1]);
}
}
}
/* Simple strequal between dst string ans c string, no 0s in b allowed */
static int streq(const char *str, const uint8_t *b) {
uint32_t len = dst_string_length(b);
uint32_t i;
const uint8_t *ustr = (const uint8_t *)str;
for (i = 0; i < len; ++i) {
if (ustr[i] != b[i])
return 0;
}
return 1;
}
/* Add many global variables */
void dst_env_merge(Dst *vm, DstTable *destEnv, DstTable *srcEnv) {
const DstValue *data = srcEnv->data;
uint32_t len = srcEnv->capacity;
uint32_t i;
for (i = 0; i < len; i += 2) {
if (data[i].type == DST_SYMBOL) {
dst_table_put(vm, destEnv, data[i], data[i + 1]);
} else if (data[i].type == DST_STRING) {
const uint8_t *k = data[i].data.string;
if (streq("nils", k)) {
if (data[i + 1].type == DST_TABLE)
mergenils(vm, destEnv, data[i + 1].data.table);
} else if (streq("meta", k)) {
if (data[i + 1].type == DST_TABLE)
mergemeta(vm, destEnv, data[i + 1].data.table);
}
}
}
}
void dst_env_put(Dst *vm, DstTable *env, DstValue key, DstValue value) {
DstTable *meta = dst_env_meta(vm, env);
dst_table_put(vm, meta, key, dst_wrap_nil());
dst_table_put(vm, env, key, value);
if (value.type == DST_NIL) {
dst_table_put(vm, dst_env_nils(vm, env), key, dst_wrap_boolean(1));
}
}
void dst_env_putc(Dst *vm, DstTable *env, const char *key, DstValue value) {
DstValue keyv = dst_string_cvs(vm, key);
dst_env_put(vm, env, keyv, value);
}
void dst_env_putvar(Dst *vm, DstTable *env, DstValue key, DstValue value) {
DstTable *meta = dst_env_meta(vm, env);
DstTable *newmeta = dst_table(vm, 4);
DstArray *ref = dst_array(vm, 1);
ref->count = 1;
ref->data[0] = value;
dst_table_put(vm, env, key, dst_wrap_array(ref));
dst_table_put(vm, newmeta, dst_string_cv(vm, "mutable"), dst_wrap_boolean(1));
dst_table_put(vm, meta, key, dst_wrap_table(newmeta));
}
void dst_env_putvarc(Dst *vm, DstTable *env, const char *key, DstValue value) {
DstValue keyv = dst_string_cvs(vm, key);
dst_env_putvar(vm, env, keyv, value);
}

544
core/func.c Normal file
View File

@ -0,0 +1,544 @@
/*
* 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 "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
}
}
}
/* 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;
}

473
core/gc.c
View File

@ -21,179 +21,251 @@
*/ */
#include "internal.h" #include "internal.h"
#include "cache.h"
#include "wrap.h"
/* The metadata header associated with an allocated block of memory */ /* Helpers for marking the various gc types */
#define gc_header(mem) ((GCMemoryHeader *)(mem) - 1) static void dst_mark_funcenv(DstFuncEnv *env);
static void dst_mark_funcdef(DstFuncEnv *def);
static void dst_mark_function(DstFunction *func);
static void dst_mark_array(DstArray *array);
static void dst_mark_table(DstTable *table);
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_udata(void *udata);
/* Memory header struct. Node of a linked list of memory blocks. */ /* Mark a value */
typedef struct GCMemoryHeader GCMemoryHeader; void dst_mark(DstValue x) {
struct GCMemoryHeader { switch (x.type) {
GCMemoryHeader * next; default: break;
uint32_t color : 1; case DST_STRING:
uint32_t tags : 31; case DST_SYMBOL: dst_mark_string(x.as.string); break;
}; case DST_FUNCTION: dst_mark_function(x.as.function); break;
case DST_ARRAY: dst_mark_array(x.as.array); break;
case DST_TABLE: dst_mark_table(x.as.table); break;
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_USERDATA: dst_mark_udata(x.as.pointer); break;
}
}
/* Mark a chunk of memory as reachable for the gc */ /* Unpin a value */
void dst_mark_mem(Dst *vm, void *mem) { void dst_unpin(DstValue x) {
gc_header(mem)->color = vm->black; switch (x.type) {
default: break;
case DST_STRING:
case DST_SYMBOL: dst_unpin_string(x.as.string); break;
case DST_FUNCTION: dst_unpin_function(x.as.function); break;
case DST_ARRAY: dst_unpin_array(x.as.array); break;
case DST_TABLE: dst_unpin_table(x.as.table); break;
case DST_STRUCT: dst_unpin_struct(x.as.st); break;
case DST_TUPLE: dst_unpin_tuple(x.as.tuple); break;
case DST_BUFFER: dst_unpin_buffer(x.as.buffer); break;
case DST_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;
}
}
/* Pin a value */
void dst_pin(DstValue x) {
switch (x.type) {
default: break;
case DST_STRING:
case DST_SYMBOL: dst_pin_string(x.as.string); break;
case DST_FUNCTION: dst_pin_function(x.as.function); break;
case DST_ARRAY: dst_pin_array(x.as.array); break;
case DST_TABLE: dst_pin_table(x.as.table); break;
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;
}
}
static void dst_mark_string(const uint8_t *str) {
gc_mark(dst_string_raw(str));
}
static void dst_mark_buffer(DstBuffer *buffer) {
gc_mark(buffer);
}
static void dst_mark_udata(void *udata) {
gc_mark(dst_udata_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;
}
}
static void dst_mark_array(DstArray *array) {
if (gc_reachable(array))
return;
gc_mark(array);
dst_mark_many(array->data, array->count);
}
static void dst_mark_table(DstTable *table) {
if (gc_reachable(table))
return;
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)))
return;
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)))
return;
gc_mark(dst_tuple_raw(tuple));
dst_mark_many(tuple, dst_tuple_count(tuple));
} }
/* Helper to mark function environments */ /* Helper to mark function environments */
static void dst_mark_funcenv(Dst *vm, DstFuncEnv *env) { static void dst_mark_funcenv(DstFuncEnv *env) {
if (gc_header(env)->color != vm->black) { if (gc_reachable(env))
gc_header(env)->color = vm->black; return;
if (env->thread) { gc_mark(env);
DstValueUnion x;
x.thread = env->thread;
dst_mark(vm, x, DST_THREAD);
}
if (env->values) { if (env->values) {
uint32_t count = env->stackOffset; uint32_t count = env->stackOffset;
uint32_t i; uint32_t i;
gc_header(env->values)->color = vm->black;
for (i = 0; i < count; ++i) for (i = 0; i < count; ++i)
dst_mark_value(vm, env->values[i]); dst_mark_value(env->values[i]);
}
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))
return;
gc_mark(def);
if (def->literals) {
count = def->literalsLen;
for (i = 0; i < count; ++i) {
DstValue v = def->literals[i];
/* Funcdefs use boolean literals to store other funcdefs */
if (v.type == DST_BOOLEAN) {
dst_mark_funcdef((DstFuncDef *) v.as.pointer);
} else {
dst_mark(v);
}
} }
} }
} }
/* GC helper to mark a FuncDef */ static void dst_mark_function(DstFunction *func) {
static void dst_mark_funcdef(Dst *vm, DstFuncDef *def) { uint32_t i;
if (gc_header(def)->color != vm->black) { uint32_t numenvs;
gc_header(def)->color = vm->black; if (gc_reachable(func))
gc_header(def->byteCode)->color = vm->black; return;
uint32_t count, i; gc_mark(func)
if (def->literals) { numenvs = fun->def->envLen;
count = def->literalsLen; for (i = 0; i < numenvs; ++i)
gc_header(def->literals)->color = vm->black; dst_mark_funcenv(func->envs + i);
for (i = 0; i < count; ++i) dst_mark_funcdef(func->def);
dst_mark_value(vm, def->literals[i]);
}
}
} }
/* Helper to mark a stack frame. Returns the next stackframe. */ /* Helper to mark a stack frame. Returns the next stackframe. */
static DstValue *dst_mark_stackframe(Dst *vm, DstValue *stack) { static DstValue *dst_mark_stackframe(Dst *vm, DstValue *stack) {
uint32_t i; dst_mark(dst_frame_callee(stack));
dst_mark_value(vm, dst_frame_callee(stack));
if (dst_frame_env(stack) != NULL) if (dst_frame_env(stack) != NULL)
dst_mark_funcenv(vm, dst_frame_env(stack)); dst_mark_funcenv(dst_frame_env(stack));
for (i = 0; i < dst_frame_size(stack); ++i) /* Mark all values in the stack frame */
dst_mark_value(vm, stack[i]); dst_mark_many(stack, dst_frame_size(stack));
/* Return the nexct frame */
return stack + dst_frame_size(stack) + DST_FRAME_SIZE; return stack + dst_frame_size(stack) + DST_FRAME_SIZE;
} }
/* Wrapper for marking values */ static void dst_mark_thread(DstThread *thread) {
void dst_mark_value(Dst *vm, DstValue x) {
dst_mark(vm, x.data, x.type);
}
/* Mark allocated memory associated with a value. This is
* the main function for doing the garbage collection mark phase. */
void dst_mark(Dst *vm, DstValueUnion x, DstType type) {
/* Allow for explicit tail recursion */
begin:
switch (type) {
default:
break;
case DST_STRING:
case DST_SYMBOL:
gc_header(dst_string_raw(x.string))->color = vm->black;
break;
case DST_BYTEBUFFER:
gc_header(x.buffer)->color = vm->black;
gc_header(x.buffer->data)->color = vm->black;
break;
case DST_ARRAY:
if (gc_header(x.array)->color != vm->black) {
uint32_t i, count;
count = x.array->count;
gc_header(x.array)->color = vm->black;
gc_header(x.array->data)->color = vm->black;
for (i = 0; i < count; ++i)
dst_mark_value(vm, x.array->data[i]);
}
break;
case DST_TUPLE:
if (gc_header(dst_tuple_raw(x.tuple))->color != vm->black) {
uint32_t i, count;
count = dst_tuple_length(x.tuple);
gc_header(dst_tuple_raw(x.tuple))->color = vm->black;
for (i = 0; i < count; ++i)
dst_mark_value(vm, x.tuple[i]);
}
break;
case DST_STRUCT:
if (gc_header(dst_struct_raw(x.st))->color != vm->black) {
uint32_t i, count;
count = dst_struct_capacity(x.st);
gc_header(dst_struct_raw(x.st))->color = vm->black;
for (i = 0; i < count; ++i)
dst_mark_value(vm, x.st[i]);
}
break;
case DST_THREAD:
if (gc_header(x.thread)->color != vm->black) {
DstThread *thread = x.thread;
DstValue *frame = thread->data + DST_FRAME_SIZE; DstValue *frame = thread->data + DST_FRAME_SIZE;
DstValue *end = thread->data + thread->count; DstValue *end = thread->data + thread->count;
gc_header(thread)->color = vm->black; if (gc_reachable(thread))
gc_header(thread->data)->color = vm->black; return;
gc_mark(thread);
while (frame <= end) while (frame <= end)
frame = dst_mark_stackframe(vm, frame); frame = dst_mark_stackframe(vm, frame);
if (thread->parent) { if (thread->parent)
x.thread = thread->parent; dst_mark_thread(thread->parent);
goto begin; }
}
}
break;
case DST_FUNCTION: /* Deinitialize a block of memory */
if (gc_header(x.function)->color != vm->black) { static void dst_deinit_block(Dst *vm, GCMemoryHeader *block) {
DstFunction *f = x.function; void *mem = ((char *)(block + 1));
gc_header(f)->color = vm->black; DstUserdataHeader *h = (DstUserdataHeader *)mem;
dst_mark_funcdef(vm, f->def); void *smem = mem + 2 * sizeof(uint32_t);
if (f->env) switch (current->tags) {
dst_mark_funcenv(vm, f->env); default:
if (f->parent) { break; /* Do nothing for non gc types */
DstValueUnion pval; case DST_MEMORY_STRING:
pval.function = f->parent; dst_cache_remove(vm, dst_wrap_string(smem));
dst_mark(vm, pval, DST_FUNCTION);
}
}
break; break;
case DST_MEMORY_ARRAY:
case DST_TABLE: free(((DstArray*) mem)->data);
if (gc_header(x.table)->color != vm->black) {
uint32_t i;
gc_header(x.table)->color = vm->black;
gc_header(x.table->data)->color = vm->black;
for (i = 0; i < x.table->capacity; i += 2) {
dst_mark_value(vm, x.table->data[i]);
dst_mark_value(vm, x.table->data[i + 1]);
}
}
break; break;
case DST_MEMORY_TUPLE:
case DST_USERDATA: dst_cache_remove(vm, 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));
break;
case DST_MEMORY_THREAD:
free(((DstThread *) mem)->data);
break;
case DST_MEMORY_BUFFER:
free(((DstBuffer *) mem)->data);
break;
case DST_MEMORY_FUNCTION:
{ {
DstUserdataHeader *h = dst_udata_header(x.pointer); DstFunction *f = (DstFunction *)mem;
gc_header(h)->color = vm->black; if (NULL != f->envs)
free(f->envs);
} }
break; break;
case DST_MEMORY_USERDATA:
case DST_FUNCENV: if (h->type->finalize)
dst_mark_funcenv(vm, x.env); h->type->finalize(vm, (void *)(h + 1), h->size);
break; break;
case DST_MEMORY_FUNCENV:
case DST_FUNCDEF: {
dst_mark_funcdef(vm, x.def); DstFuncEnv *env = (DstFuncEnv *)mem;
if (NULL == env->thread && NULL != env->values)
free(env->values);
}
break;
case DST_MEMORY_FUNCDEF:
{
DstFunDef *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);
}
break; break;
} }
} }
@ -206,124 +278,65 @@ void dst_sweep(Dst *vm) {
GCMemoryHeader *next; GCMemoryHeader *next;
while (current) { while (current) {
next = current->next; next = current->next;
if (current->color != vm->black) { if (current->flags & (DST_MEM_REACHABLE | DST_MEM_DISABLED)) {
previous = current;
} else {
dst_deinit_block(vm, current);
if (previous) { if (previous) {
previous->next = next; previous->next = next;
} else { } else {
vm->blocks = next; vm->blocks = next;
} }
if (current->tags) { free(current);
if (current->tags & DST_MEMTAG_STRING)
dst_cache_remove_string(vm, (char *)(current + 1));
if (current->tags & DST_MEMTAG_STRUCT)
dst_cache_remove_struct(vm, (char *)(current + 1));
if (current->tags & DST_MEMTAG_TUPLE)
dst_cache_remove_tuple(vm, (char *)(current + 1));
if (current->tags & DST_MEMTAG_USER) {
DstUserdataHeader *h = (DstUserdataHeader *)(current + 1);
if (h->type->finalize) {
h->type->finalize(vm, h + 1, h->size);
}
}
}
dst_raw_free(current);
} else {
previous = current;
} }
current->flags &= ~DST_MEM_REACHABLE;
current = next; current = next;
} }
/* Rotate flag */
vm->black = !vm->black;
}
/* Prepare a memory block */
static void *dst_alloc_prepare(Dst *vm, char *rawBlock, uint32_t size) {
GCMemoryHeader *mdata;
if (rawBlock == NULL) {
return NULL;
}
vm->nextCollection += size;
mdata = (GCMemoryHeader *)rawBlock;
mdata->next = vm->blocks;
vm->blocks = mdata;
mdata->color = !vm->black;
mdata->tags = 0;
return rawBlock + sizeof(GCMemoryHeader);
} }
/* Allocate some memory that is tracked for garbage collection */ /* Allocate some memory that is tracked for garbage collection */
void *dst_alloc(Dst *vm, uint32_t size) { void *dst_alloc(Dst *vm, DstMemoryType type, size_t size) {
uint32_t totalSize = size + sizeof(GCMemoryHeader); GCMemoryHeader *mdata;
void *mem = dst_alloc_prepare(vm, dst_raw_alloc(totalSize), totalSize); size_t totalSize = size + sizeof(GCMemoryHeader);
if (!mem) { void *mem = malloc(totalSize);
DST_LOW_MEMORY;
dst_collect(vm); /* Check for bad malloc */
mem = dst_alloc_prepare(vm, dst_raw_alloc(totalSize), totalSize); if (NULL == mem) {
if (!mem) {
DST_OUT_OF_MEMORY; DST_OUT_OF_MEMORY;
} }
}
return mem;
}
/* Allocate some zeroed memory that is tracked for garbage collection */ mdata = (GCMemoryHeader *)rawBlock;
void *dst_zalloc(Dst *vm, uint32_t size) {
uint32_t totalSize = size + sizeof(GCMemoryHeader);
void *mem = dst_alloc_prepare(vm, dst_raw_calloc(1, totalSize), totalSize);
if (!mem) {
DST_LOW_MEMORY;
dst_collect(vm);
mem = dst_alloc_prepare(vm, dst_raw_calloc(1, totalSize), totalSize);
if (!mem) {
DST_OUT_OF_MEMORY;
}
}
return mem;
}
/* Tag some memory to mark it with special properties */ /* Configure block */
void dst_mem_tag(void *mem, uint32_t tags) { mdata->flags = type;
GCMemoryHeader *mh = (GCMemoryHeader *)mem - 1;
mh->tags |= tags; /* Prepend block to heap list */
vm->nextCollection += size;
mdata->next = vm->blocks;
vm->blocks = mdata;
return mem + sizeof(GCMemoryHeader);
} }
/* Run garbage collection */ /* Run garbage collection */
void dst_collect(Dst *vm) { void dst_collect(Dst *vm) {
DstValue x; if (vm->thread)
/* Thread can be null */ dst_mark_thread(vm->thread);
if (vm->thread) { dst_mark_table(vm->modules);
x.type = DST_THREAD; dst_mark_table(vm->registry);
x.data.thread = vm->thread; dst_mark_table(vm->env);
dst_mark_value(vm, x); dst_mark(vm->ret);
}
x.type = DST_TABLE;
x.data.table = vm->modules;
dst_mark_value(vm, x);
x.data.table = vm->registry;
dst_mark_value(vm, x);
x.data.table = vm->env;
dst_mark_value(vm, x);
dst_mark_value(vm, vm->ret);
dst_sweep(vm); dst_sweep(vm);
vm->nextCollection = 0; vm->nextCollection = 0;
} }
/* Run garbage collection if needed */
void dst_maybe_collect(Dst *vm) {
if (vm->nextCollection >= vm->memoryInterval)
dst_collect(vm);
}
/* Free all allocated memory */ /* Free all allocated memory */
void dst_clear_memory(Dst *vm) { void dst_clear_memory(Dst *vm) {
GCMemoryHeader *current = vm->blocks; GCMemoryHeader *current = vm->blocks;
while (current) { while (current) {
dst_deinit_block(vm, current);
GCMemoryHeader *next = current->next; GCMemoryHeader *next = current->next;
dst_raw_free(current); free(current);
current = next; current = next;
} }
vm->blocks = NULL; vm->blocks = NULL;

120
core/gc.h Normal file
View File

@ -0,0 +1,120 @@
/*
* 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

View File

@ -24,7 +24,8 @@
#define DST_INTERNAL_H_defined #define DST_INTERNAL_H_defined
#include <dst/dst.h> #include <dst/dst.h>
#include <setjmp.h> #include <string.h>
#include <stdlib.h>
/* String utils */ /* String utils */
#define dst_string_raw(s) ((uint32_t *)(s) - 2) #define dst_string_raw(s) ((uint32_t *)(s) - 2)
@ -47,41 +48,6 @@
#define dst_udata_type(u) (dst_udata_header(u)->type) #define dst_udata_type(u) (dst_udata_header(u)->type)
#define dst_udata_size(u) (dst_udata_header(u)->size) #define dst_udata_size(u) (dst_udata_header(u)->size)
/* Memcpy for moving memory */
#ifndef dst_memcpy
#include <string.h>
#define dst_memcpy memcpy
#endif
/* Allocation */
#ifndef dst_raw_alloc
#include <stdlib.h>
#define dst_raw_alloc malloc
#endif
/* Zero allocation */
#ifndef dst_raw_calloc
#include <stdlib.h>
#define dst_raw_calloc calloc
#endif
/* Realloc */
#ifndef dst_raw_realloc
#include <stdlib.h>
#define dst_raw_realloc realloc
#endif
/* Free */
#ifndef dst_raw_free
#include <stdlib.h>
#define dst_raw_free free
#endif
/* Null */
#ifndef NULL
#define NULL ((void *)0)
#endif
/* Stack frame manipulation */ /* Stack frame manipulation */
/* Size of stack frame in number of values */ /* Size of stack frame in number of values */
@ -89,7 +55,7 @@
/* Prevent some recursive functions from recursing too deeply /* Prevent some recursive functions from recursing too deeply
* ands crashing. */ * ands crashing. */
#define DST_RECURSION_GUARD 2056 #define DST_RECURSION_GUARD 1000
/* Macros for referencing a stack frame given a stack */ /* Macros for referencing a stack frame given a stack */
#define dst_frame_callee(s) (*(s - 1)) #define dst_frame_callee(s) (*(s - 1))
@ -116,24 +82,14 @@
/* What to do when out of memory */ /* What to do when out of memory */
#ifndef DST_OUT_OF_MEMORY #ifndef DST_OUT_OF_MEMORY
#include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#define DST_OUT_OF_MEMORY do { printf("out of memory\n"); exit(1); } while (0) #define DST_OUT_OF_MEMORY do { printf("out of memory\n"); exit(1); } while (0)
#endif #endif
/* What to do when memory is low */
#ifndef DST_LOW_MEMORY
#include <stdlib.h>
#include <stdio.h>
#define DST_LOW_MEMORY do { printf("low memory\n"); } while (0)
#endif
/* A general dst value type */ /* A general dst value type */
typedef struct DstValue DstValue; typedef struct DstValue DstValue;
/* All of the dst types */ /* All of the dst types */
typedef double DstReal;
typedef int64_t DstInteger;
typedef int DstBoolean; typedef int DstBoolean;
typedef struct DstFunction DstFunction; typedef struct DstFunction DstFunction;
typedef struct DstArray DstArray; typedef struct DstArray DstArray;
@ -146,22 +102,15 @@ typedef struct DstUserdataHeader DstUserdataHeader;
typedef struct DstFuncDef DstFuncDef; typedef struct DstFuncDef DstFuncDef;
typedef struct DstFuncEnv DstFuncEnv; typedef struct DstFuncEnv DstFuncEnv;
typedef union DstValueUnion DstValueUnion; typedef union DstValueUnion DstValueUnion;
typedef struct DstModuleItem DstModuleItem;
typedef struct DstUserType DstUserType; typedef struct DstUserType DstUserType;
typedef struct DstParser DstParser; typedef struct DstParser DstParser;
typedef struct DstParseState DstParseState; typedef struct DstParseState DstParseState;
/* C Api data types */
struct DstModuleItem {
const char *name;
DstCFunction data;
};
/* Union datatype */ /* Union datatype */
union DstValueUnion { union DstValueUnion {
DstBoolean boolean; DstBoolean boolean;
DstReal real; double real;
DstInteger integer; int64_t integer;
DstArray *array; DstArray *array;
DstBuffer *buffer; DstBuffer *buffer;
DstTable *table; DstTable *table;
@ -169,8 +118,6 @@ union DstValueUnion {
const DstValue *tuple; const DstValue *tuple;
DstCFunction cfunction; DstCFunction cfunction;
DstFunction *function; DstFunction *function;
DstFuncEnv *env;
DstFuncDef *def;
const DstValue *st; const DstValue *st;
const uint8_t *string; const uint8_t *string;
/* Indirectly used union members */ /* Indirectly used union members */
@ -186,7 +133,7 @@ union DstValueUnion {
* the type information of the value */ * the type information of the value */
struct DstValue { struct DstValue {
DstType type; DstType type;
DstValueUnion data; DstValueUnion as;
}; };
/* A lightweight green thread in dst. Does not correspond to /* A lightweight green thread in dst. Does not correspond to
@ -228,18 +175,21 @@ struct DstTable {
/* Some function defintion flags */ /* Some function defintion flags */
#define DST_FUNCDEF_FLAG_VARARG 1 #define DST_FUNCDEF_FLAG_VARARG 1
#define DST_FUNCDEF_FLAG_NEEDSPARENT 2
#define DST_FUNCDEF_FLAG_NEEDSENV 4 #define DST_FUNCDEF_FLAG_NEEDSENV 4
/* A function definition. Contains information need to instantiate closures. */ /* A function definition. Contains information needed to instantiate closures. */
struct DstFuncDef { struct DstFuncDef {
uint32_t locals;
uint32_t arity; /* Not including varargs */
uint32_t literalsLen;
uint32_t byteCodeLen;
uint32_t flags; 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. */ DstValue *literals; /* Contains strings, FuncDefs, etc. */
uint16_t *byteCode; uint32_t *byteCode;
}; };
/* A fuction environment */ /* A fuction environment */
@ -252,8 +202,7 @@ struct DstFuncEnv {
/* A function */ /* A function */
struct DstFunction { struct DstFunction {
DstFuncDef *def; DstFuncDef *def;
DstFuncEnv *env; DstFuncEnv *envs;
DstFunction *parent;
}; };
/* Defines a type for userdata */ /* Defines a type for userdata */
@ -321,23 +270,31 @@ enum DstOpCode {
}; };
/****/ /****/
/* Internal buffer functions */ /* Value Stack Manipulation */
/****/ /****/
void dst_value_buffer_ensure(Dst *vm, DstBuffer *buffer, uint32_t capacity); void dst_switchv(Dst *vm, DstValue x);
void dst_buffer_append_bytes(Dst *vm, DstBuffer *buffer, const uint8_t *string, uint32_t length); DstValue dst_popv(Dst *vm);
void dst_buffer_append_cstring(Dst *vm, DstBuffer *buffer, const char *cstring); DstValue dst_peekv(Dst *vm);
void dst_pushv(Dst *vm, DstValue x);
/* Define a push function for pushing a certain type to the buffer */ DstValue dst_getv(Dst *vm, int64_t index);
#define BUFFER_DEFINE(name, type) \ void dst_setv(Dst *vm, int64_t index, DstValue x);
static void dst_buffer_push_##name(Dst *vm, DstBuffer *buffer, type x) { \
union { type t; uint8_t bytes[sizeof(type)]; } u; \
u.t = x; dst_buffer_append(vm, buffer, u.bytes, sizeof(type)); \
}
/****/ /****/
/* Table functions */ /* 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); DstArray *dst_make_array(Dst *vm, uint32_t capacity);
void dst_array_ensure_(Dst *vm, DstArray *array, uint32_t capacity);
/****/ /****/
/* Tuple functions */ /* Tuple functions */
@ -350,8 +307,6 @@ const DstValue *dst_tuple_end(Dst *vm, DstValue *tuple);
/* String/Symbol functions */ /* String/Symbol functions */
/****/ /****/
uint8_t *dst_string_begin(Dst *vm, uint32_t len);
void dst_string_end(Dst *vm, uint32_t dest, uint8_t *str);
const uint8_t *dst_string_b(Dst *vm, const uint8_t *buf, uint32_t len); 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); const uint8_t *dst_string_c(Dst *vm, const char *cstring);
DstValue dst_string_cv(Dst *vm, const char *string); DstValue dst_string_cv(Dst *vm, const char *string);
@ -409,129 +364,42 @@ const char *dst_deserialize_internal(
const char *dst_serialize_internal(Dst *vm, DstBuffer *buffer, DstValue x); const char *dst_serialize_internal(Dst *vm, DstBuffer *buffer, DstValue x);
/****/
/* GC */
/****/
#define DST_MEMTAG_STRING 4
#define DST_MEMTAG_TUPLE 8
#define DST_MEMTAG_STRUCT 16
#define DST_MEMTAG_USER 32
void dst_mark_value(Dst *vm, DstValue x);
void dst_mark(Dst *vm, DstValueUnion x, DstType type);
void dst_sweep(Dst *vm);
void *dst_alloc(Dst *vm, uint32_t size);
void *dst_zalloc(Dst *vm, uint32_t size);
void dst_mem_tag(void *mem, uint32_t tags);
void dst_collect(Dst *vm);
void dst_maybe_collect(Dst *vm);
void dst_clear_memory(Dst *vm);
void dst_mark_mem(Dst *vm, void *mem);
/****/
/* VM */
/****/
DstValue dst_arg(Dst *vm, uint32_t index);
void dst_set_arg(Dst *vm, uint32_t index, DstValue x);
uint32_t dst_args(Dst *vm);
/***/
/* Stl */
/***/
void dst_stl_load(Dst *vm);
/****/
/* C Api */
/****/
void dst_module(Dst *vm, const char *name, const DstModuleItem *mod);
void dst_module_mutable(Dst *vm, const char *name, const DstModuleItem *mod);
void dst_module_put(Dst *vm, const char *packagename, const char *name, DstValue x);
DstValue dst_module_get(Dst *vm, const char *packagename);
void dst_register_put(Dst *vm, const char *packagename, DstValue mod);
DstValue dst_register_get(Dst *vm, const char *name);
int dst_callc(Dst *vm, DstCFunction fn, int numargs, ...);
/* Treat similar types through uniform interfaces for iteration */ /* Treat similar types through uniform interfaces for iteration */
int dst_seq_view(DstValue seq, const DstValue **data, uint32_t *len); 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_chararray_view(DstValue str, const uint8_t **data, uint32_t *len);
int dst_hashtable_view(DstValue tab, const DstValue **data, uint32_t *cap); int dst_hashtable_view(DstValue tab, const DstValue **data, uint32_t *cap);
/****/ /****/
/* Caching for immutable data */ /* Parse functions */
/****/ /****/
void dst_cache_remove_string(Dst *vm, char *strmem); #define PARSE_OK 0
void dst_cache_remove_tuple(Dst *vm, char *tuplemem); #define PARSE_ERROR 1
void dst_cache_remove_struct(Dst *vm, char *structmem); #define PARSE_UNEXPECTED_EOS 2
/****/ int dst_parseb(Dst *vm, const uint8_t *src, const uint8_t **newsrc, uint32_t len);
/* Misc */
/****/
uint32_t dst_index(Dst *vm, int i); /* Parse a c string */
int dst_read_real(const uint8_t *string, const uint8_t *end, double *ret, int forceInt); int dst_parsec(Dst *vm, const char *src, const char **newsrc);
int dst_read_integer(const uint8_t *string, const uint8_t *end, int64_t *ret);
DstReal dst_integer_to_real(DstInteger x); /* Parse a DST char seq (Buffer, String, Symbol) */
DstInteger dst_real_to_integer(DstReal x); int dst_parse(Dst *vm);
uint32_t dst_startrange(DstInteger raw, uint32_t len);
uint32_t dst_endrange(DstInteger raw, uint32_t len);
void dst_env_merge(Dst *vm, DstTable *destEnv, DstTable *srcEnv);
DstTable *dst_env_nils(Dst *vm, DstTable *env);
DstTable *dst_env_meta(Dst *vm, DstTable *env);
void dst_env_put(Dst *vm, DstTable *env, DstValue key, DstValue value);
void dst_env_putc(Dst *vm, DstTable *env, const char *key, DstValue value);
void dst_env_putvar(Dst *vm, DstTable *env, DstValue key, DstValue value);
void dst_env_putvarc(Dst *vm, DstTable *env, const char *key, DstValue value);
/****/ /****/
/* Value functions */ /* Value functions */
/****/ /****/
int dst_value_truthy(DstValue v); int dst_truthy(DstValue v);
int dst_value_equals(DstValue x, DstValue y); int dst_equals(DstValue x, DstValue y);
uint32_t dst_value_hash(DstValue x); uint32_t dst_hash(DstValue x);
int dst_value_compare(DstValue x, DstValue y); int dst_compare(DstValue x, DstValue y);
uint32_t dst_calchash_array(const DstValue *array, uint32_t len);
/* Wrap data in GstValue */ /****/
DstValue dst_wrap_nil(); /* Helper functions */
DstValue dst_wrap_real(DstReal x); /****/
DstValue dst_wrap_integer(DstInteger x); uint32_t dst_startrange(int64_t index, uint32_t modulo);
DstValue dst_wrap_boolean(int x); uint32_t dst_endrange(int64_t index, uint32_t modulo);
DstValue dst_wrap_string(const uint8_t *x); int64_t dst_normalize_index(Dst *vm, int64_t index);
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);
DstValue dst_wrap_funcenv(DstFuncEnv *x);
DstValue dst_wrap_funcdef(DstFuncDef *x);
/* Check data from arguments */
int dst_check_nil(Dst *vm, uint32_t i);
int dst_check_real(Dst *vm, uint32_t i);
int dst_check_integer(Dst *vm, uint32_t i);
int dst_check_boolean(Dst *vm, uint32_t i);
int dst_check_string(Dst *vm, uint32_t i);
int dst_check_symbol(Dst *vm, uint32_t i);
int dst_check_array(Dst *vm, uint32_t i);
int dst_check_tuple(Dst *vm, uint32_t i);
int dst_check_struct(Dst *vm, uint32_t i);
int dst_check_thread(Dst *vm, uint32_t i);
int dst_check_buffer(Dst *vm, uint32_t i);
int dst_check_function(Dst *vm, uint32_t i);
int dst_check_cfunction(Dst *vm, uint32_t i);
int dst_check_table(Dst *vm, uint32_t i);
int dst_check_funcenv(Dst *vm, uint32_t i);
int dst_check_funcdef(Dst *vm, uint32_t i);
void dst_check_userdata(Dst *vm, uint32_t i;
#endif /* DST_INTERNAL_H_defined */ #endif /* DST_INTERNAL_H_defined */

View File

@ -1,101 +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"
static void dst_cmodule_register(Dst *vm, const char *name, const DstModuleItem *mod) {
uint32_t startLength;
DstBuffer *buffer = dst_buffer(vm, 10);
dst_buffer_append_cstring(vm, buffer, name);
dst_buffer_push(vm, buffer, '.');
startLength = buffer->count;
while (mod->name != NULL) {
DstValue key;
buffer->count = startLength;
dst_buffer_append_cstring(vm, buffer, mod->name);
key = dst_wrap_symbol(dst_buffer_to_string(vm, buffer));
dst_table_put(vm, vm->registry, key, dst_wrap_cfunction(mod->data));
dst_table_put(vm, vm->registry, dst_wrap_cfunction(mod->data), key);
mod++;
}
}
static DstValue dst_cmodule_table(Dst *vm, const DstModuleItem *mod) {
DstTable *module = dst_table(vm, 10);
while (mod->name != NULL) {
DstValue key = dst_string_cvs(vm, mod->name);
dst_table_put(vm, module, key, dst_wrap_cfunction(mod->data));
mod++;
}
return dst_wrap_table(module);
}
static DstValue dst_cmodule_struct(Dst *vm, const DstModuleItem *mod) {
uint32_t count = 0;
const DstModuleItem *m = mod;
DstValue *st;
while (m->name != NULL) {
++count;
++m;
}
st = dst_struct_begin(vm, count);
m = mod;
while (m->name != NULL) {
dst_struct_put(st,
dst_string_cvs(vm, m->name),
dst_wrap_cfunction(m->data));
++m;
}
return dst_wrap_struct(dst_struct_end(vm, st));
}
void dst_module(Dst *vm, const char *packagename, const DstModuleItem *mod) {
dst_table_put(vm, vm->modules, dst_string_cvs(vm, packagename), dst_cmodule_struct(vm, mod));
dst_cmodule_register(vm, packagename, mod);
}
void dst_module_mutable(Dst *vm, const char *packagename, const DstModuleItem *mod) {
dst_table_put(vm, vm->modules, dst_string_cvs(vm, packagename), dst_cmodule_table(vm, mod));
dst_cmodule_register(vm, packagename, mod);
}
void dst_module_put(Dst *vm, const char *packagename, const char *name, DstValue v) {
DstValue modtable = dst_table_get(vm->modules, dst_string_cvs(vm, packagename));
if (modtable.type == DST_TABLE) {
DstTable *table = modtable.data.table;
if (v.type == DST_CFUNCTION) {
DstValue key;
DstBuffer *buffer = dst_buffer(vm, 10);
dst_buffer_append_cstring(vm, buffer, packagename);
dst_buffer_push(vm, buffer, '.');
dst_buffer_append_cstring(vm, buffer, name);
key = dst_wrap_string(dst_buffer_to_string(vm, buffer));
dst_table_put(vm, vm->registry, key, v);
dst_table_put(vm, vm->registry, v, key);
}
dst_table_put(vm, table, dst_string_cvs(vm, name), v);
}
}
DstValue dst_module_get(Dst *vm, const char *packagename) {
return dst_table_get(vm->modules, dst_string_cvs(vm, packagename));
}

View File

@ -21,7 +21,88 @@
*/ */
#include "internal.h" #include "internal.h"
#include "bootstrap.h"
/* Get an integer power of 10 */
static double exp10(int power) {
if (power == 0) return 1;
if (power > 0) {
double result = 10;
int currentPower = 1;
while (currentPower * 2 <= power) {
result = result * result;
currentPower *= 2;
}
return result * exp10(power - currentPower);
} else {
return 1 / exp10(-power);
}
}
/* Read an integer */
static int read_integer(const uint8_t *string, const uint8_t *end, int64_t *ret) {
int sign = 1, x = 0;
int64_t accum = 0;
if (*string == '-') {
sign = -1;
++string;
} else if (*string == '+') {
++string;
}
if (string >= end) return 0;
while (string < end) {
x = *string;
if (x < '0' || x > '9') return 0;
x -= '0';
accum = accum * 10 + x;
++string;
}
*ret = accum * sign;
return 1;
}
/* Read a real from a string. Returns if successfuly
* parsed a real from the enitre input string.
* If returned 1, output is int ret.*/
static int read_real(const uint8_t *string, const uint8_t *end, double *ret, int forceInt) {
int sign = 1, x = 0;
double accum = 0, exp = 1, place = 1;
/* Check the sign */
if (*string == '-') {
sign = -1;
++string;
} else if (*string == '+') {
++string;
}
if (string >= end) return 0;
while (string < end) {
if (*string == '.' && !forceInt) {
place = 0.1;
} else if (!forceInt && (*string == 'e' || *string == 'E')) {
/* Read the exponent */
++string;
if (string >= end) return 0;
if (!read_real(string, end, &exp, 1))
return 0;
exp = exp10(exp);
break;
} else {
x = *string;
if (x < '0' || x > '9') return 0;
x -= '0';
if (place < 1) {
accum += x * place;
place *= 0.1;
} else {
accum *= 10;
accum += x;
}
}
++string;
}
*ret = accum * sign * exp;
return 1;
}
/* Checks if a string slice is equal to a string constant */ /* Checks if a string slice is equal to a string constant */
static int check_str_const(const char *ref, const uint8_t *start, const uint8_t *end) { static int check_str_const(const char *ref, const uint8_t *start, const uint8_t *end) {
@ -34,19 +115,25 @@ static int check_str_const(const char *ref, const uint8_t *start, const uint8_t
} }
/* Quote a value */ /* Quote a value */
static DstValue quote(Dst *vm, DstValue x) { static void quote(Dst *vm) {
DstValue *tuple = dst_tuple_begin(vm, 2); dst_cstring(vm, "quote");
tuple[0] = dst_string_cvs(vm, "quote"); dst_hoist(vm, -2);
tuple[1] = x; dst_tuple(vm, 2);
return dst_wrap_tuple(dst_tuple_end(vm, tuple));
} }
/* Check if a character is whitespace */ /* Check if a character is whitespace */
static int is_whitespace(uint8_t c) { static int is_whitespace(uint8_t c) {
return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\0' || c == ','; return c == ' '
|| c == '\t'
|| c == '\n'
|| c == '\r'
|| c == '\0'
|| c == ',';
} }
/* Check if a character is a valid symbol character */ /* Check if a character is a valid symbol character */
/* TODO - wlloe utf8 - shouldn't be difficult, err on side
* of inclusivity */
static int is_symbol_char(uint8_t c) { static int is_symbol_char(uint8_t c) {
if (c >= 'a' && c <= 'z') return 1; if (c >= 'a' && c <= 'z') return 1;
if (c >= 'A' && c <= 'Z') return 1; if (c >= 'A' && c <= 'Z') return 1;
@ -76,12 +163,13 @@ static int to_hex(uint8_t c) {
typedef struct { typedef struct {
Dst *vm; Dst *vm;
const uint8_t *end; const uint8_t *end;
const char **errmsg; const char *errmsg;
int64_t sourcemap;
int status; int status;
} ParseArgs; } ParseArgs;
/* Entry point of recursive descent parser */ /* Entry point of the recursive descent parser */
static const uint8_t *parse( static const uint8_t *parse_recur(
ParseArgs *args, ParseArgs *args,
const uint8_t *src, const uint8_t *src,
uint32_t recur) { uint32_t recur) {
@ -89,7 +177,6 @@ static const uint8_t *parse(
Dst *vm = args->vm; Dst *vm = args->vm;
const uint8_t *end = args->end; const uint8_t *end = args->end;
uint32_t qcount = 0; uint32_t qcount = 0;
uint32_t retindex = dst_args(vm);
/* Prevent stack overflow */ /* Prevent stack overflow */
if (recur == 0) goto too_much_recur; if (recur == 0) goto too_much_recur;
@ -110,29 +197,28 @@ static const uint8_t *parse(
/* Numbers, symbols, simple literals */ /* Numbers, symbols, simple literals */
default: { default: {
DstReal real; double real;
DstInteger integer; int64_t integer;
const uint8_t *tokenend = src; const uint8_t *tokenend = src;
if (!is_symbol_char(*src)) goto unexpected_character; if (!is_symbol_char(*src)) goto unexpected_character;
dst_setsize(vm, retindex + 1);
while (tokenend < end && is_symbol_char(*tokenend)) while (tokenend < end && is_symbol_char(*tokenend))
tokenend++; tokenend++;
if (tokenend >= end) goto unexpected_eos; if (tokenend >= end) goto unexpected_eos;
if (dst_read_integer(src, tokenend, &integer)) { if (read_integer(src, tokenend, &integer)) {
dst_set_integer(vm, retindex, integer); dst_integer(vm, integer);
} else if (dst_read_real(src, tokenend, &real, 0)) { } else if (read_real(src, tokenend, &real, 0)) {
dst_set_real(vm, retindex, real); dst_real(vm, real);
} else if (check_str_const("nil", src, tokenend)) { } else if (check_str_const("nil", src, tokenend)) {
dst_nil(vm, retindex); dst_nil(vm);
} else if (check_str_const("false", src, tokenend)) { } else if (check_str_const("false", src, tokenend)) {
dst_false(vm, retindex); dst_false(vm);
} else if (check_str_const("true", src, tokenend)) { } else if (check_str_const("true", src, tokenend)) {
dst_true(vm, retindex); dst_true(vm);
} else { } else {
if (*src >= '0' && *src <= '9') { if (*src >= '0' && *src <= '9') {
goto sym_nodigits; goto sym_nodigits;
} else { } else {
dst_symbol(vm, retindex, src, tokenend - src); dst_symbol(vm, src, tokenend - src);
} }
} }
src = tokenend; src = tokenend;
@ -141,11 +227,10 @@ static const uint8_t *parse(
case ':': { case ':': {
const uint8_t *tokenend = ++src; const uint8_t *tokenend = ++src;
dst_setsize(vm, retindex + 1);
while (tokenend < end && is_symbol_char(*tokenend)) while (tokenend < end && is_symbol_char(*tokenend))
tokenend++; tokenend++;
if (tokenend >= end) goto unexpected_eos; if (tokenend >= end) goto unexpected_eos;
dst_string(vm, retindex, src, tokenend - src); dst_string(vm, src, tokenend - src);
src = tokenend; src = tokenend;
break; break;
} }
@ -153,28 +238,30 @@ static const uint8_t *parse(
/* String literals */ /* String literals */
case '"': { case '"': {
const uint8_t *strend = ++src; const uint8_t *strend = ++src;
const uint8_t *strstart = src;
uint32_t len = 0; uint32_t len = 0;
int containsEscape = 0; int containsEscape = 0;
/* Preprocess string to check for escapes and string end */ /* Preprocess string to check for escapes and string end */
while (strend < end && *strend != '"') { while (strend < end && *strend != '"') {
len++; len++;
if (*strend++ == '\\') { if (*strend++ == '\\') {
constainsEscape = 1; containsEscape = 1;
if (strend >= end) goto unexpected_eos; if (strend >= end) goto unexpected_eos;
if (*strend == 'h') { if (*strend == 'h') {
strend += 2; strend += 2;
if (strend >= end) goto unexpected_eos; if (strend >= end) goto unexpected_eos;
} else {
strend++;
} }
} }
} }
if (containsEscape) { if (containsEscape) {
uint8_t *buf = dst_string_begin(vm, len); uint8_t *buf = dst_string_begin(vm, len);
uint8_t *write = buf; uint8_t *write = buf;
const uint8_t *scan = src; while (src < strend) {
while (scan < strend) { if (*src == '\\') {
if (*scan == '\\') { src++;
scan++; switch (*++src) {
switch (*++scan) {
case 'n': *write++ = '\n'; break; case 'n': *write++ = '\n'; break;
case 'r': *write++ = '\r'; break; case 'r': *write++ = '\r'; break;
case 't': *write++ = '\t'; break; case 't': *write++ = '\t'; break;
@ -185,69 +272,66 @@ static const uint8_t *parse(
case 'z': *write++ = '\0'; break; case 'z': *write++ = '\0'; break;
case 'e': *write++ = 27; break; case 'e': *write++ = 27; break;
case 'h': { case 'h': {
int d1 = to_hex(scan[0]); int d1 = to_hex(*src++);
int d2 = to_hex(scan[1]); int d2 = to_hex(*src++);
if (d1 < 0 || d2 < 0) goto invalid_hex; if (d1 < 0 || d2 < 0) goto invalid_hex;
*write = 16 * d1 + d2; *write++ = 16 * d1 + d2;
break; break;
} }
default: default:
goto unknown_strescape; goto unknown_strescape;
} }
} else { } else {
*write++ = *scan++; *write++ = *src++;
} }
} }
dst_string_end(vm, retindex, buf); dst_string_end(vm, buf);
} else { } else {
dst_string(vm, retindex, src, strend - src); dst_string(vm, strstart, strend - strstart);
}
src = strend + 1; src = strend + 1;
}
break; break;
} }
/* Data Structure literals */ /* Data Structure literals */
case: '(': case '(':
case: '[': case '[':
case: '{': { case '{': {
uint32_t n = 0;
uint8_t close; uint8_t close;
uint32_t tmpindex;
switch (*src++) { switch (*src++) {
case '(': close = ')'; break;
case '[': close = ']'; break; case '[': close = ']'; break;
case '{': close = '}'; break; case '{': close = '}'; break;
default: close = ')'; break; default: close = ')'; break;
} }
/* Recursively parse inside literal */ /* Recursively parse inside literal */
while (*src != close) { while (*src != close) {
src = parse(args, src, recur - 1); src = parse_recur(args, src, recur - 1);
if (*(args->errmsg) || !src) return src; if (args->errmsg || !src) return src;
n++;
/* Trim trailing whitespace */
while (src < end && (is_whitespace(*src)))
++src;
} }
src++; src++;
tmpindex = dst_args(vm);
dst_push_space(vm, 1);
switch (close) { switch (close) {
case ')': case ')':
dst_tuple_n(vm, tmpindex, retindex, tmpindex - retindex); dst_tuple(vm, n);
break; break;
case ']': case ']':
dst_array_n(vm, tmpindex, retindex, tmpindex - retindex); dst_arrayn(vm, n);
break; break;
case '}': case '}':
if ((tmpindex - retindex) % 2) goto struct_oddargs; if (n & 1) goto struct_oddargs;
dst_struct_n(vm, tmpindex, retindex, tmpindex - retindex); dst_struct(vm, n/2);
break; break;
} }
dst_move(vm, retindex, tmpindex);
dst_setsize(vm, retindex + 1);
break; break;
} }
} }
/* Quote the returned value qcount times */ /* Quote the returned value qcount times */
while (qcount--) { while (qcount--) quote(vm);
dst_set_arg(vm, retindex, quote(vm, dst_arg(vm, retindex)));
}
/* Return the new source position for further calls */ /* Return the new source position for further calls */
return src; return src;
@ -255,77 +339,89 @@ static const uint8_t *parse(
/* Errors below */ /* Errors below */
unexpected_eos: unexpected_eos:
*(args->errmsg) = "unexpected end of source"; args->errmsg = "unexpected end of source";
args->status = PARSE_UNEXPECTED_EOS; args->status = PARSE_UNEXPECTED_EOS;
return NULL; return NULL;
unexpected_character: unexpected_character:
*(args->errmsg) = "unexpected character"; args->errmsg = "unexpected character";
args->status = PARSE_ERROR; args->status = PARSE_ERROR;
return src; return src;
sym_nodigits: sym_nodigits:
*(args->errmsg) = "symbols cannot start with digits"; args->errmsg = "symbols cannot start with digits";
args->status = PARSE_ERROR; args->status = PARSE_ERROR;
return src; return src;
struct_oddargs: struct_oddargs:
*(args->errmsg) = "struct literal needs an even number of arguments"; args->errmsg = "struct literal needs an even number of arguments";
args->status = PARSE_ERROR; args->status = PARSE_ERROR;
return src; return src;
unknown_strescape: unknown_strescape:
*(args->errmsg) = "unknown string escape sequence"; args->errmsg = "unknown string escape sequence";
args->status = PARSE_ERROR; args->status = PARSE_ERROR;
return src; return src;
invalid_hex: invalid_hex:
*(args->errmsg) = "invalid hex escape in string"; args->errmsg = "invalid hex escape in string";
args->status = PARSE_ERROR; args->status = PARSE_ERROR;
return src; return src;
too_much_recur: too_much_recur:
*(args->errmsg) = "recursed too deeply in parsing"; args->errmsg = "recursed too deeply in parsing";
args->status = PARSE_ERROR; args->status = PARSE_ERROR;
return src; return src;
} }
/* Parse an array of bytes */ /* Parse an array of bytes */
int dst_parseb(Dst *vm, uint32_t dest, const uint8_t *src, const uint8_t **newsrc, uint32_t len) { int dst_parseb(Dst *vm, const uint8_t *src, const uint8_t **newsrc, uint32_t len) {
ParseArgs args; ParseArgs args;
uint32_t toploc = dst_args(vm); uint32_t nargs;
const uint8_t *srcrest;
/* Create a source map */
dst_table(vm, 10);
nargs = dst_stacksize(vm);
args.vm = vm; args.vm = vm;
args.status = PARSE_OK; args.status = PARSE_OK;
args.end = src + len; args.end = src + len;
args.errmsg = NULL; args.errmsg = NULL;
args.sourcemap = dst_normalize_index(vm, -1);
src = parse_recur(&args, src, 2048);
if (newsrc) *newsrc = src;
srcrest = parse(&args, src, 2048) || src;
if (newsrc) {
*newsrc = srcrest;
}
if (args.errmsg) { if (args.errmsg) {
/* Error */ /* Unwind the stack */
dst_cstring(vm, dest, args.errmsg); dst_trimstack(vm, nargs);
} else { dst_cstring(vm, args.errmsg);
/* Success */
dst_move(vm, dest, toploc);
} }
dst_setsize(vm, toploc);
return args.status; return args.status;
} }
/* Parse a c string */ /* Parse a c string */
int dst_parsec(Dst *vm, uint32_t dest, const char *src) { int dst_parsec(Dst *vm, const char *src, const char **newsrc) {
uint32_t len = 0; uint32_t len = 0;
const uint8_t *ns = NULL;
int status;
while (src[len]) ++len; while (src[len]) ++len;
return dst_parseb(vm, dest, (const uint8_t *)src, NULL, len); status = dst_parseb(vm, (const uint8_t *)src, &ns, len);
if (newsrc) {
*newsrc = (const char *)ns;
}
return status;
} }
/* Parse a DST char seq (Buffer, String, Symbol) */ /* Parse a DST char seq (Buffer, String, Symbol) */
int dst_parse(Dst *vm, uint32_t dest, uint32_t src) { int dst_parse(Dst *vm) {
int status;
uint32_t len; uint32_t len;
const uint8_t *bytes = dst_bytes(vm, src, &len); const uint8_t *bytes = dst_bytes(vm, -1, &len);
return dst_parseb(vm, dest, bytes, NULL, len); status = dst_parseb(vm, bytes, NULL, len);
dst_swap(vm, -1, -2);
dst_pop(vm);
return status;
} }

View File

@ -24,7 +24,7 @@
#include "internal.h" #include "internal.h"
/** /**
* Data format * Data format v1
* *
* Types: * Types:
* *
@ -32,7 +32,7 @@
* Byte 201: Nil * Byte 201: Nil
* Byte 202: True * Byte 202: True
* Byte 203: False * Byte 203: False
* Byte 204: Number - double format * Byte 204: Number - IEEE 754 double format
* Byte 205: String - [u32 length]*[u8... characters] * Byte 205: String - [u32 length]*[u8... characters]
* Byte 206: Struct - [u32 length]*2*[value... kvs] * Byte 206: Struct - [u32 length]*2*[value... kvs]
* Byte 207: Buffer - [u32 capacity][u32 length]*[u8... characters] * Byte 207: Buffer - [u32 capacity][u32 length]*[u8... characters]
@ -52,703 +52,9 @@
* Byte 217: Ref - [u32 id] * Byte 217: Ref - [u32 id]
* Byte 218: Integer - [i64 value] * Byte 218: Integer - [i64 value]
* Byte 219: Symbol - [u32 length]*[u8... characters] * Byte 219: Symbol - [u32 length]*[u8... characters]
*
* This data format needs both a serialization function and deserialization function
* written in C, as this will load embeded and precompiled programs into the VM, including
* the self hosted parser abd compiler. It is therefor important that it is correct,
* does not leak memory, and is fast.
*/ */
/* Error at buffer end */
static const char UEB[] = "unexpected end of buffer";
/* Read 4 bytes as an unsigned integer */
static uint32_t bytes2u32(const uint8_t *bytes) {
union {
uint8_t bytes[4];
uint32_t u32;
} u;
dst_memcpy(u.bytes, bytes, 4 * sizeof(uint8_t));
return u.u32;
}
/* Read 2 bytes as unsigned short */
static uint16_t bytes2u16(const uint8_t *bytes) {
union {
uint8_t bytes[2];
uint16_t u16;
} u;
dst_memcpy(u.bytes, bytes, 2 * sizeof(uint8_t));
return u.u16;
}
/* Read 8 bytes as a double */
static double bytes2dbl(const uint8_t *bytes) {
union {
uint8_t bytes[8];
double dbl;
} u;
dst_memcpy(u.bytes, bytes, 8 * sizeof(uint8_t));
return u.dbl;
}
/* Read 8 bytes as a integer */
static int64_t bytes2int(const uint8_t *bytes) {
union {
uint8_t bytes[8];
int64_t i;
} u;
dst_memcpy(u.bytes, bytes, 8 * sizeof(uint8_t));
return u.i;
}
/* Read a string and turn it into a dst value. Returns
* an error message if there is an error message during
* deserialization. If successful, the resulting value is
* passed by reference. */
static const char *dst_deserialize_impl(
Dst *vm,
const uint8_t *data,
const uint8_t *end,
const uint8_t **newData,
DstArray *visited,
DstValue *out,
int depth) {
DstValue ret;
ret.type = DST_NIL;
DstValue *buffer;
uint32_t length, i;
const char *err;
/* Handle errors as needed */
#define deser_error(e) return (e)
/* Assertions */
#define deser_assert(c, e) do{if(!(c))deser_error(e);}while(0)
/* Assert enough buffer */
#define deser_datacheck(len) do{if (end < (data + len)) deser_error(UEB);}while(0)
/* Check for enough space to read uint32_t */
#define read_u32(out) do{deser_datacheck(4); (out)=bytes2u32(data); data += 4; }while(0)
#define read_u16(out) do{deser_datacheck(2); (out)=bytes2u16(data); data += 2; }while(0)
#define read_dbl(out) do{deser_datacheck(8); (out)=bytes2dbl(data); data += 8; }while(0)
#define read_i64(out) do{deser_datacheck(8); (out)=bytes2int(data); data += 8; }while(0)
/* Check if we have recursed too deeply */
if (--depth == 0) {
return "deserialize recursed too deeply";
}
/* Check enough buffer left to read one byte */
if (data >= end) deser_error(UEB);
/* Small integer */
if (*data < 201) {
ret.type = DST_INTEGER;
ret.data.integer = *data - 100;
*newData = data + 1;
*out = ret;
return NULL;
}
/* Main switch for types */
switch (*data++) {
default:
return "unable to deserialize";
case 201: /* Nil */
ret.type = DST_NIL;
break;
case 202: /* True */
ret.type = DST_BOOLEAN;
ret.data.boolean = 1;
break;
case 203: /* False */
ret.type = DST_BOOLEAN;
ret.data.boolean = 0;
break;
case 204: /* Long number (double) */
ret.type = DST_REAL;
read_dbl(ret.data.real);
break;
case 205: /* String */
case 219: /* Symbol */
ret.type = data[-1] == 205 ? DST_STRING : DST_SYMBOL;
read_u32(length);
deser_datacheck(length);
ret.data.string = dst_string_b(vm, data, length);
data += length;
dst_array_push(vm, visited, ret);
break;
case 206: /* Struct */
ret.type = DST_STRUCT;
read_u32(length);
buffer = dst_struct_begin(vm, length);
for (i = 0; i < length; ++i) {
DstValue k, v;
if ((err = dst_deserialize_impl(vm, data, end, &data, visited, &k, depth)))
return err;
if ((err = dst_deserialize_impl(vm, data, end, &data, visited, &v, depth)))
return err;
dst_struct_put(buffer, k, v);
}
ret.data.st = dst_struct_end(vm, buffer);
dst_array_push(vm, visited, ret);
break;
case 207: /* Buffer */
{
uint32_t cap;
ret.type = DST_BYTEBUFFER;
read_u32(cap);
read_u32(length);
deser_datacheck(length);
ret.data.buffer = dst_alloc(vm, sizeof(DstBuffer));
ret.data.buffer->data = dst_alloc(vm, cap);
dst_memcpy(ret.data.buffer->data, data, length);
ret.data.buffer->count = length;
ret.data.buffer->capacity = cap;
dst_array_push(vm, visited, ret);
}
break;
case 208: /* Array */
ret.type = DST_ARRAY;
read_u32(length);
buffer = dst_alloc(vm, length * sizeof(DstValue));
ret.data.array = dst_alloc(vm, sizeof(DstArray));
ret.data.array->data = buffer;
ret.data.array->count = length;
ret.data.array->capacity = length;
dst_array_push(vm, visited, ret);
for (i = 0; i < length; ++i)
if ((err = dst_deserialize_impl(vm, data, end, &data, visited, buffer + i, depth)))
return err;
break;
case 209: /* Tuple */
ret.type = DST_TUPLE;
read_u32(length);
buffer = dst_tuple_begin(vm, length);
for (i = 0; i < length; ++i)
if ((err = dst_deserialize_impl(vm, data, end, &data, visited, buffer + i, depth)))
return err;
ret.type = DST_TUPLE;
ret.data.tuple = dst_tuple_end(vm, buffer);
dst_array_push(vm, visited, ret);
break;
case 210: /* Thread */
{
DstThread *t;
DstValue *stack;
uint16_t prevsize = 0;
uint8_t statusbyte;
t = dst_thread(vm, dst_wrap_nil(), 64);
ret = dst_wrap_thread(t);
dst_array_push(vm, visited, ret);
err = dst_deserialize_impl(vm, data, end, &data, visited, &ret, depth);
if (err != NULL) return err;
if (ret.type == DST_NIL) {
t->parent = NULL;
} else if (ret.type == DST_THREAD) {
t->parent = ret.data.thread;
} else {
return "expected thread parent to be thread";
}
deser_assert(data < end, UEB);
statusbyte = *data++;
read_u32(length);
/* Set status */
t->status = statusbyte % 4;
/* Add frames */
for (i = 0; i < length; ++i) {
DstValue callee, env;
uint32_t pcoffset;
uint16_t ret, args, size, j;
/* Read the stack */
err = dst_deserialize_impl(vm, data, end, &data, visited, &callee, depth);
if (err != NULL) return err;
err = dst_deserialize_impl(vm, data, end, &data, visited, &env, depth);
if (err != NULL) return err;
if (env.type != DST_FUNCENV && env.type != DST_NIL)
return "expected funcenv in stackframe";
/* Create a new frame */
if (i > 0)
dst_thread_beginframe(vm, t, dst_wrap_nil(), 0);
read_u32(pcoffset);
read_u32(ret);
read_u32(args);
read_u32(size);
/* Set up the stack */
stack = dst_thread_stack(t);
if (callee.type == DST_FUNCTION) {
dst_frame_pc(stack) = callee.data.function->def->byteCode + pcoffset;
}
dst_frame_ret(stack) = ret;
dst_frame_args(stack) = args;
dst_frame_size(stack) = size;
dst_frame_prevsize(stack) = prevsize;
dst_frame_callee(stack) = callee;
if (env.type == DST_NIL)
dst_frame_env(stack) = NULL;
else
dst_frame_env(stack) = env.data.env;
prevsize = size;
/* Push stack args */
for (j = 0; j < size; ++j) {
DstValue temp;
err = dst_deserialize_impl(vm, data, end, &data, visited, &temp, depth);
dst_thread_push(vm, t, temp);
}
}
}
break;
case 211: /* Table */
read_u32(length);
ret = dst_wrap_table(dst_table(vm, 2 * length));
dst_array_push(vm, visited, ret);
for (i = 0; i < length; ++i) {
DstValue key, value;
err = dst_deserialize_impl(vm, data, end, &data, visited, &key, depth);
if (err != NULL) return err;
err = dst_deserialize_impl(vm, data, end, &data, visited, &value, depth);
if (err != NULL) return err;
dst_table_put(vm, ret.data.table, key, value);
}
break;
case 212: /* Funcdef */
{
DstFuncDef *def;
uint32_t locals, arity, literalsLen, byteCodeLen, flags;
read_u32(locals);
read_u32(arity);
read_u32(flags);
read_u32(literalsLen);
def = dst_alloc(vm, sizeof(DstFuncDef));
ret = dst_wrap_funcdef(def);
dst_array_push(vm, visited, ret);
def->locals = locals;
def->arity = arity;
def->flags = flags;
def->literalsLen = literalsLen;
if (literalsLen > 0) {
def->literals = dst_alloc(vm, literalsLen * sizeof(DstValue));
} else {
def->literals = NULL;
}
for (i = 0; i < literalsLen; ++i) {
err = dst_deserialize_impl(vm, data, end, &data, visited, def->literals + i, depth);
if (err != NULL) return err;
}
read_u32(byteCodeLen);
deser_datacheck(byteCodeLen * sizeof(uint16_t));
def->byteCode = dst_alloc(vm, byteCodeLen * sizeof(uint16_t));
def->byteCodeLen = byteCodeLen;
for (i = 0; i < byteCodeLen; ++i) {
read_u16(def->byteCode[i]);
}
}
break;
case 213: /* Funcenv */
{
DstValue thread;
ret.type = DST_FUNCENV;
ret.data.env = dst_alloc(vm, sizeof(DstFuncEnv));
dst_array_push(vm, visited, ret);
err = dst_deserialize_impl(vm, data, end, &data, visited, &thread, depth);
if (err != NULL) return err;
read_u32(length);
ret.data.env->stackOffset = length;
if (thread.type == DST_THREAD) {
ret.data.env->thread = thread.data.thread;
} else {
ret.data.env->thread = NULL;
ret.data.env->values = dst_alloc(vm, sizeof(DstValue) * length);
for (i = 0; i < length; ++i) {
DstValue item;
err = dst_deserialize_impl(vm, data, end, &data, visited, &item, depth);
if (err != NULL) return err;
ret.data.env->values[i] = item;
}
}
}
break;
case 214: /* Function */
{
DstValue parent, def, env;
ret.type = DST_FUNCTION;
ret.data.function = dst_alloc(vm, sizeof(DstFunction));
dst_array_push(vm, visited, ret);
err = dst_deserialize_impl(vm, data, end, &data, visited, &parent, depth);
if (err != NULL) return err;
err = dst_deserialize_impl(vm, data, end, &data, visited, &env, depth);
if (err != NULL) return err;
err = dst_deserialize_impl(vm, data, end, &data, visited, &def, depth);
if (err != NULL) return err;
if (parent.type == DST_NIL) {
ret.data.function->parent = NULL;
} else if (parent.type == DST_FUNCTION) {
ret.data.function->parent = parent.data.function;
} else {
deser_error("expected function");
}
deser_assert(def.type == DST_FUNCDEF, "expected funcdef");
ret.data.function->def = def.data.def;
if (env.type == DST_NIL) {
ret.data.function->env = NULL;
} else {
deser_assert(env.type == DST_FUNCENV, "expected funcenv");
ret.data.function->env = env.data.env;
}
}
break;
case 215: /* LUdata */
{
/* TODO enable deserialization of userdata through registration
* to names in vm. */
}
break;
case 216: /* C function */
{
DstValue id;
read_u32(length);
deser_datacheck(length);
id = dst_wrap_string(dst_string_b(vm, data, length));
data += length;
ret = dst_table_get(vm->registry, id);
if (ret.type != DST_CFUNCTION) {
deser_error("unabled to deserialize c function");
}
break;
}
break;
case 217: /* Reference */
read_u32(length);
deser_assert(visited->count > length, "invalid reference");
ret = visited->data[length];
break;
case 218: /* Integer */
ret.type = DST_INTEGER;
read_i64(ret.data.integer);
break;
}
*out = ret;
*newData = data;
return NULL;
}
/* Load a value from data */
const char *dst_deserialize_internal(
Dst *vm,
const uint8_t *data,
uint32_t len,
DstValue *out,
const uint8_t **nextData) {
DstValue ret;
const char *err;
DstArray *visited = dst_array(vm, 10);
err = dst_deserialize_impl(vm, data, data + len, nextData, visited, &ret, DST_RECURSION_GUARD);
if (err != NULL) return err;
*out = ret;
return NULL;
}
/* Allow appending other types to buffers */
BUFFER_DEFINE(real, DstReal)
BUFFER_DEFINE(integer, DstInteger)
BUFFER_DEFINE(u32, uint32_t)
BUFFER_DEFINE(u16, uint16_t)
/* Serialize a value and write to a buffer. Returns possible
* error messages. */
static const char *dst_serialize_impl(
Dst *vm,
DstBuffer *buffer,
DstTable *visited,
uint32_t *nextId,
DstValue x,
int depth) {
uint32_t i, count;
const char *err;
DstValue check;
#define write_byte(b) dst_buffer_push(vm, buffer, (b))
#define write_u32(b) dst_buffer_push_u32(vm, buffer, (b))
#define write_u16(b) dst_buffer_push_u16(vm, buffer, (b))
#define write_dbl(b) dst_buffer_push_real(vm, buffer, (b))
#define write_int(b) dst_buffer_push_integer(vm, buffer, (b))
/*printf("Type: %d\n", x.type);*/
/* Check if we have gone too deep */
if (--depth == 0) {
return "serialize recursed too deeply";
}
/* Check non reference types - if successful, return NULL */
switch (x.type) {
case DST_USERDATA:
case DST_NIL:
write_byte(201);
return NULL;
case DST_BOOLEAN:
write_byte(x.data.boolean ? 202 : 203);
return NULL;
case DST_REAL:
write_byte(204);
write_dbl(x.data.real);
return NULL;
case DST_INTEGER:
if (x.data.integer <= 100 && x.data.integer >= -100) {
write_byte(x.data.integer + 100);
} else {
write_byte(218);
write_int(x.data.integer);
}
return NULL;
default:
break;
}
/* Check if already seen - if so, use reference */
check = dst_table_get(visited, x);
if (check.type == DST_INTEGER) {
write_byte(217);
write_u32((uint32_t) check.data.integer);
return NULL;
}
/* Check tuples and structs before other reference types.
* They are immutable, and thus cannot be referenced by other values
* until they are fully constructed. This creates some strange behavior
* if they are treated like other reference types because they cannot
* be added to the visited table before recursing into serializing their
* arguments */
if (x.type == DST_STRUCT || x.type == DST_TUPLE) {
if (x.type == DST_STRUCT) {
const DstValue *data;
write_byte(206);
dst_hashtable_view(x, &data, &count);
write_u32(dst_struct_length(x.data.st));
for (i = 0; i < count; i += 2) {
if (data[i].type != DST_NIL) {
err = dst_serialize_impl(vm, buffer, visited, nextId, data[i], depth);
if (err != NULL) return err;
err = dst_serialize_impl(vm, buffer, visited, nextId, data[i + 1], depth);
if (err != NULL) return err;
}
}
} else {
write_byte(209);
count = dst_tuple_length(x.data.tuple);
write_u32(count);
for (i = 0; i < count; ++i) {
err = dst_serialize_impl(vm, buffer, visited, nextId, x.data.tuple[i], depth);
if (err != NULL) return err;
}
}
/* Record reference after serialization */
dst_table_put(vm, visited, x, dst_wrap_integer(*nextId));
*nextId = *nextId + 1;
return NULL;
}
/* Record reference before serialization */
dst_table_put(vm, visited, x, dst_wrap_integer(*nextId));
*nextId = *nextId + 1;
/* Check reference types */
switch (x.type) {
default:
return "unable to serialize type";
case DST_STRING:
case DST_SYMBOL:
write_byte(x.type == DST_STRING ? 205 : 219);
count = dst_string_length(x.data.string);
write_u32(count);
for (i = 0; i < count; ++i) {
write_byte(x.data.string[i]);
}
break;
case DST_CFUNCTION:
write_byte(216);
{
DstValue id = dst_table_get(vm->registry, x);
count = dst_string_length(id.data.string);
write_u32(count);
for (i = 0; i < count; ++i) {
write_byte(id.data.string[i]);
}
}
break;
case DST_TABLE:
{
const DstValue *data;
write_byte(211);
dst_hashtable_view(x, &data, &count);
write_u32(x.data.table->count);
for (i = 0; i < count; i += 2) {
if (data[i].type != DST_NIL) {
err = dst_serialize_impl(vm, buffer, visited, nextId, data[i], depth);
if (err != NULL) return err;
err = dst_serialize_impl(vm, buffer, visited, nextId, data[i + 1], depth);
if (err != NULL) return err;
}
}
}
break;
case DST_BYTEBUFFER:
write_byte(207);
count = x.data.buffer->count;
write_u32(x.data.buffer->capacity);
write_u32(count);
for (i = 0; i < count; ++i) {
write_byte(x.data.buffer->data[i]);
}
break;
case DST_ARRAY:
write_byte(208);
count = x.data.array->count;
write_u32(count);
for (i = 0; i < count; ++i) {
err = dst_serialize_impl(vm, buffer, visited, nextId, x.data.array->data[i], depth);
if (err != NULL) return err;
}
break;
case DST_THREAD:
{
DstThread *t = x.data.thread;
const DstValue *stack = t->data + DST_FRAME_SIZE;
uint32_t framecount = dst_thread_countframes(t);
uint32_t i;
write_byte(210);
if (t->parent)
err = dst_serialize_impl(vm, buffer, visited, nextId, dst_wrap_thread(t->parent), depth);
else
err = dst_serialize_impl(vm, buffer, visited, nextId, dst_wrap_nil(), depth);
if (err != NULL) return err;
/* Write the status byte */
write_byte(t->status);
/* Write number of stack frames */
write_u32(framecount);
/* Write stack frames */
for (i = 0; i < framecount; ++i) {
uint32_t j, size;
DstValue callee = dst_frame_callee(stack);
DstFuncEnv *env = dst_frame_env(stack);
err = dst_serialize_impl(vm, buffer, visited, nextId, callee, depth);
if (err != NULL) return err;
if (env)
err = dst_serialize_impl(vm, buffer, visited, nextId, dst_wrap_funcenv(env), depth);
else
err = dst_serialize_impl(vm, buffer, visited, nextId, dst_wrap_nil(), depth);
if (err != NULL) return err;
if (callee.type == DST_FUNCTION) {
write_u32(dst_frame_pc(stack) - callee.data.function->def->byteCode);
} else {
write_u32(0);
}
write_u32(dst_frame_ret(stack));
write_u32(dst_frame_args(stack));
size = dst_frame_size(stack);
write_u32(size);
for (j = 0; j < size; ++j) {
err = dst_serialize_impl(vm, buffer, visited, nextId, stack[j], depth);
if (err != NULL) return err;
}
/* Next stack frame */
stack += size + DST_FRAME_SIZE;
}
}
break;
case DST_FUNCDEF: /* Funcdef */
{
DstFuncDef *def = x.data.def;
write_byte(212);
write_u32(def->locals);
write_u32(def->arity);
write_u32(def->flags);
write_u32(def->literalsLen);
for (i = 0; i < def->literalsLen; ++i) {
err = dst_serialize_impl(vm, buffer, visited, nextId, def->literals[i], depth);
if (err != NULL) return err;
}
write_u32(def->byteCodeLen);
for (i = 0; i < def->byteCodeLen; ++i) {
write_u16(def->byteCode[i]);
}
}
break;
case DST_FUNCENV: /* Funcenv */
{
DstFuncEnv *env = x.data.env;
write_byte(213);
if (env->thread) {
err = dst_serialize_impl(vm, buffer, visited, nextId, dst_wrap_thread(env->thread), depth);
if (err != NULL) return err;
write_u32(env->stackOffset);
} else {
write_byte(201); /* Write nil */
write_u32(env->stackOffset);
for (i = 0; i < env->stackOffset; ++i) {
err = dst_serialize_impl(vm, buffer, visited, nextId, env->values[i], depth);
if (err != NULL) return err;
}
}
}
break;
case DST_FUNCTION: /* Function */
{
DstValue pv, ev, dv;
DstFunction *fn = x.data.function;
write_byte(214);
pv = fn->parent ? dst_wrap_function(fn->parent) : dst_wrap_nil();
dv = dst_wrap_funcdef(fn->def);
ev = fn->env ? dst_wrap_funcenv(fn->env) : dst_wrap_nil();
err = dst_serialize_impl(vm, buffer, visited, nextId, pv, depth);
if (err != NULL) return err;
err = dst_serialize_impl(vm, buffer, visited, nextId, ev, depth);
if (err != NULL) return err;
err = dst_serialize_impl(vm, buffer, visited, nextId, dv, depth);
if (err != NULL) return err;
}
break;
}
/* Return success */
return NULL;
}
/* Serialize an object */
const char *dst_serialize_internal(Dst *vm, DstBuffer *buffer, DstValue x) {
uint32_t nextId = 0;
uint32_t oldCount = buffer->count;
const char *err;
DstTable *visited = dst_table(vm, 10);
err = dst_serialize_impl(vm, buffer, visited, &nextId, x, DST_RECURSION_GUARD);
if (err != NULL) {
buffer->count = oldCount;
}
return err;
}

View File

@ -20,7 +20,6 @@
* IN THE SOFTWARE. * IN THE SOFTWARE.
*/ */
#include <dst/dst.h>
#include "internal.h" #include "internal.h"
static const char DST_EXPECTED_INTEGER[] = "expected integer"; static const char DST_EXPECTED_INTEGER[] = "expected integer";
@ -49,28 +48,28 @@ static const char *types[] = {
static DstValue nil() { static DstValue nil() {
DstValue n; DstValue n;
n.type = DST_NIL; n.type = DST_NIL;
n.data.integer = 0; n.as.integer = 0;
return n; return n;
} }
static DstValue integer(DstInteger i) { static DstValue integer(DstInteger i) {
DstValue n; DstValue n;
n.type = DST_INTEGER; n.type = DST_INTEGER;
n.data.integer = i; n.as.integer = i;
return n; return n;
} }
static DstValue real(DstReal r) { static DstValue real(DstReal r) {
DstValue n; DstValue n;
n.type = DST_REAL; n.type = DST_REAL;
n.data.real = r; n.as.real = r;
return n; return n;
} }
static DstValue boolean(int b) { static DstValue boolean(int b) {
DstValue n; DstValue n;
n.type = DST_BOOLEAN; n.type = DST_BOOLEAN;
n.data.boolean = b; n.as.boolean = b;
return n; return n;
} }
@ -82,16 +81,16 @@ static DstValue boolean(int b) {
static DstValue dst_stl_binop_##name(DstValue lhs, DstValue rhs) {\ static DstValue dst_stl_binop_##name(DstValue lhs, DstValue rhs) {\
if (lhs.type == DST_INTEGER)\ if (lhs.type == DST_INTEGER)\
if (rhs.type == DST_INTEGER)\ if (rhs.type == DST_INTEGER)\
return integer(lhs.data.integer op rhs.data.integer);\ return integer(lhs.as.integer op rhs.as.integer);\
else if (rhs.type == DST_REAL)\ else if (rhs.type == DST_REAL)\
return real(lhs.data.integer op rhs.data.real);\ return real(lhs.as.integer op rhs.as.real);\
else\ else\
return nil();\ return nil();\
else if (lhs.type == DST_REAL)\ else if (lhs.type == DST_REAL)\
if (rhs.type == DST_INTEGER)\ if (rhs.type == DST_INTEGER)\
return real(lhs.data.real op rhs.data.integer);\ return real(lhs.as.real op rhs.as.integer);\
else if (rhs.type == DST_REAL)\ else if (rhs.type == DST_REAL)\
return real(lhs.data.real op rhs.data.real);\ return real(lhs.as.real op rhs.as.real);\
else\ else\
return nil();\ return nil();\
else\ else\
@ -127,7 +126,7 @@ int dst_stl_div(Dst *vm) {
lhs = dst_arg(vm, 0); lhs = dst_arg(vm, 0);
for (j = 1; j < count; ++j) { for (j = 1; j < count; ++j) {
rhs = dst_arg(vm, j); rhs = dst_arg(vm, j);
if (lhs.type == DST_INTEGER && rhs.type == DST_INTEGER && rhs.data.integer == 0) if (lhs.type == DST_INTEGER && rhs.type == DST_INTEGER && rhs.as.integer == 0)
dst_c_throwc(vm, "cannot integer divide by 0"); dst_c_throwc(vm, "cannot integer divide by 0");
lhs = dst_stl_binop_div(lhs, rhs); lhs = dst_stl_binop_div(lhs, rhs);
} }
@ -155,7 +154,7 @@ int dst_stl_##name(Dst *vm) {\
if (next.type != DST_INTEGER) {\ if (next.type != DST_INTEGER) {\
dst_c_throwc(vm, "expected integer");\ dst_c_throwc(vm, "expected integer");\
}\ }\
ret.data.integer = ret.data.integer op next.data.integer;\ ret.as.integer = ret.as.integer op next.as.integer;\
}\ }\
dst_c_return(vm, ret);\ dst_c_return(vm, ret);\
} }
@ -174,7 +173,7 @@ int dst_stl_bnot(Dst *vm) {
if (count != 1 || in.type != DST_INTEGER) { if (count != 1 || in.type != DST_INTEGER) {
dst_c_throwc(vm, "expected 1 integer argument"); dst_c_throwc(vm, "expected 1 integer argument");
} }
in.data.integer = ~in.data.integer; in.as.integer = ~in.as.integer;
dst_c_return(vm, in); dst_c_return(vm, in);
} }
@ -183,7 +182,7 @@ int dst_stl_##name(Dst *vm) {\
DstValue ret;\ DstValue ret;\
uint32_t i, count;\ uint32_t i, count;\
count = dst_args(vm);\ count = dst_args(vm);\
ret.data.boolean = 1;\ ret.as.boolean = 1;\
ret.type = DST_BOOLEAN;\ ret.type = DST_BOOLEAN;\
if (count < 2) {\ if (count < 2) {\
dst_c_return(vm, ret);\ dst_c_return(vm, ret);\
@ -192,7 +191,7 @@ int dst_stl_##name(Dst *vm) {\
DstValue lhs = dst_arg(vm, i - 1);\ DstValue lhs = dst_arg(vm, i - 1);\
DstValue rhs = dst_arg(vm, i);\ DstValue rhs = dst_arg(vm, i);\
if (!(check)) {\ if (!(check)) {\
ret.data.boolean = 0;\ ret.as.boolean = 0;\
break;\ break;\
}\ }\
}\ }\
@ -224,13 +223,13 @@ int dst_stl_clear(Dst *vm) {
default: default:
dst_c_throwc(vm, "cannot clear"); dst_c_throwc(vm, "cannot clear");
case DST_ARRAY: case DST_ARRAY:
x.data.array->count = 0; x.as.array->count = 0;
break; break;
case DST_BYTEBUFFER: case DST_BUFFER:
x.data.buffer->count = 0; x.as.buffer->count = 0;
break; break;
case DST_TABLE: case DST_TABLE:
dst_table_clear(x.data.table); dst_table_clear(x.as.table);
break; break;
} }
dst_c_return(vm, x); dst_c_return(vm, x);
@ -255,7 +254,7 @@ int dst_stl_to_int(Dst *vm) {
DstValue x = dst_arg(vm, 0); DstValue x = dst_arg(vm, 0);
if (x.type == DST_INTEGER) dst_c_return(vm, x); if (x.type == DST_INTEGER) dst_c_return(vm, x);
if (x.type == DST_REAL) if (x.type == DST_REAL)
dst_c_return(vm, integer((DstInteger) x.data.real)); dst_c_return(vm, integer((DstInteger) x.as.real));
else else
dst_c_throwc(vm, "expected number"); dst_c_throwc(vm, "expected number");
} }
@ -265,7 +264,7 @@ int dst_stl_to_real(Dst *vm) {
DstValue x = dst_arg(vm, 0); DstValue x = dst_arg(vm, 0);
if (x.type == DST_REAL) dst_c_return(vm, x); if (x.type == DST_REAL) dst_c_return(vm, x);
if (x.type == DST_INTEGER) if (x.type == DST_INTEGER)
dst_c_return(vm, dst_wrap_real((DstReal) x.data.integer)); dst_c_return(vm, dst_wrap_real((DstReal) x.as.integer));
else else
dst_c_throwc(vm, "expected number"); dst_c_throwc(vm, "expected number");
} }
@ -322,12 +321,12 @@ int dst_stl_slice(Dst *vm) {
dst_memcpy(arr->data, data + from, newlength * sizeof(DstValue)); dst_memcpy(arr->data, data + from, newlength * sizeof(DstValue));
dst_c_return(vm, dst_wrap_array(arr)); dst_c_return(vm, dst_wrap_array(arr));
} else if (x.type == DST_STRING) { } else if (x.type == DST_STRING) {
dst_c_return(vm, dst_wrap_string(dst_string_b(vm, x.data.string + from, newlength))); dst_c_return(vm, dst_wrap_string(dst_string_b(vm, x.as.string + from, newlength)));
} else if (x.type == DST_SYMBOL) { } else if (x.type == DST_SYMBOL) {
dst_c_return(vm, dst_wrap_symbol(dst_string_b(vm, x.data.string + from, newlength))); dst_c_return(vm, dst_wrap_symbol(dst_string_b(vm, x.as.string + from, newlength)));
} else { /* buffer */ } else { /* buffer */
DstBuffer *b = dst_buffer(vm, newlength); DstBuffer *b = dst_buffer(vm, newlength);
dst_memcpy(b->data, x.data.buffer->data, newlength); dst_memcpy(b->data, x.as.buffer->data, newlength);
b->count = newlength; b->count = newlength;
dst_c_return(vm, dst_wrap_buffer(b)); dst_c_return(vm, dst_wrap_buffer(b));
} }
@ -368,7 +367,7 @@ int dst_stl_type(Dst *vm) {
case DST_THREAD: case DST_THREAD:
typestr = "thread"; typestr = "thread";
break; break;
case DST_BYTEBUFFER: case DST_BUFFER:
typestr = "buffer"; typestr = "buffer";
break; break;
case DST_FUNCTION: case DST_FUNCTION:
@ -471,7 +470,7 @@ int dst_stl_string(Dst *vm) {
dat = dst_to_string(vm, dst_arg(vm, j)); dat = dst_to_string(vm, dst_arg(vm, j));
slen = dst_string_length(dat); slen = dst_string_length(dat);
newarg.type = DST_STRING; newarg.type = DST_STRING;
newarg.data.string = dat; newarg.as.string = dat;
dst_set_arg(vm, j, newarg); dst_set_arg(vm, j, newarg);
} }
length += slen; length += slen;
@ -505,7 +504,7 @@ int dst_stl_thread(Dst *vm) {
if (callee.type != DST_FUNCTION && callee.type != DST_CFUNCTION) if (callee.type != DST_FUNCTION && callee.type != DST_CFUNCTION)
dst_c_throwc(vm, "expected function in thread constructor"); dst_c_throwc(vm, "expected function in thread constructor");
if (parent.type == DST_THREAD) { if (parent.type == DST_THREAD) {
t->parent = parent.data.thread; t->parent = parent.as.thread;
} else if (parent.type != DST_NIL) { } else if (parent.type != DST_NIL) {
dst_c_throwc(vm, "expected thread/nil as parent"); dst_c_throwc(vm, "expected thread/nil as parent");
} else { } else {
@ -588,7 +587,7 @@ int dst_stl_push(Dst *vm) {
DstValue ds = dst_arg(vm, 0); DstValue ds = dst_arg(vm, 0);
if (ds.type != DST_ARRAY) if (ds.type != DST_ARRAY)
dst_c_throwc(vm, "expected array"); dst_c_throwc(vm, "expected array");
dst_array_push(vm, ds.data.array, dst_arg(vm, 1)); dst_array_push(vm, ds.as.array, dst_arg(vm, 1));
dst_c_return(vm, ds); dst_c_return(vm, ds);
} }
@ -597,7 +596,7 @@ int dst_stl_pop(Dst *vm) {
DstValue ds = dst_arg(vm, 0); DstValue ds = dst_arg(vm, 0);
if (ds.type != DST_ARRAY) if (ds.type != DST_ARRAY)
dst_c_throwc(vm, "expected array"); dst_c_throwc(vm, "expected array");
dst_c_return(vm, dst_array_pop(ds.data.array)); dst_c_return(vm, dst_array_pop(ds.as.array));
} }
/* Peek at end of array */ /* Peek at end of array */
@ -605,7 +604,7 @@ int dst_stl_peek(Dst *vm) {
DstValue ds = dst_arg(vm, 0); DstValue ds = dst_arg(vm, 0);
if (ds.type != DST_ARRAY) if (ds.type != DST_ARRAY)
dst_c_throwc(vm, "expected array"); dst_c_throwc(vm, "expected array");
dst_c_return(vm, dst_array_peek(ds.data.array)); dst_c_return(vm, dst_array_peek(ds.as.array));
} }
/* Ensure array capacity */ /* Ensure array capacity */
@ -616,7 +615,7 @@ int dst_stl_ensure(Dst *vm) {
dst_c_throwc(vm, "expected array"); dst_c_throwc(vm, "expected array");
if (cap.type != DST_INTEGER) if (cap.type != DST_INTEGER)
dst_c_throwc(vm, DST_EXPECTED_INTEGER); dst_c_throwc(vm, DST_EXPECTED_INTEGER);
dst_array_ensure(vm, ds.data.array, (uint32_t) cap.data.integer); dst_array_ensure(vm, ds.as.array, (uint32_t) cap.as.integer);
dst_c_return(vm, ds); dst_c_return(vm, ds);
} }
@ -625,9 +624,9 @@ int dst_stl_next(Dst *vm) {
DstValue ds = dst_arg(vm, 0); DstValue ds = dst_arg(vm, 0);
DstValue key = dst_arg(vm, 1); DstValue key = dst_arg(vm, 1);
if (ds.type == DST_TABLE) { if (ds.type == DST_TABLE) {
dst_c_return(vm, dst_table_next(ds.data.table, key)); dst_c_return(vm, dst_table_next(ds.as.table, key));
} else if (ds.type == DST_STRUCT) { } else if (ds.type == DST_STRUCT) {
dst_c_return(vm, dst_struct_next(ds.data.st, key)); dst_c_return(vm, dst_struct_next(ds.as.st, key));
} else { } else {
dst_c_throwc(vm, "expected table or struct"); dst_c_throwc(vm, "expected table or struct");
} }
@ -666,7 +665,7 @@ int dst_stl_short_description(Dst *vm) {
int dst_stl_exit(Dst *vm) { int dst_stl_exit(Dst *vm) {
int ret; int ret;
DstValue x = dst_arg(vm, 0); DstValue x = dst_arg(vm, 0);
ret = x.type == DST_INTEGER ? x.data.integer : (x.type == DST_REAL ? x.data.real : 0); ret = x.type == DST_INTEGER ? x.as.integer : (x.type == DST_REAL ? x.as.real : 0);
exit(ret); exit(ret);
return DST_RETURN_OK; return DST_RETURN_OK;
} }
@ -676,36 +675,6 @@ int dst_stl_error(Dst *vm) {
dst_c_throw(vm, dst_arg(vm, 0)); dst_c_throw(vm, dst_arg(vm, 0));
} }
/****/
/* Serialization */
/****/
/* Serialize data into buffer */
int dst_stl_serialize(Dst *vm) {
const char *err;
DstValue buffer = dst_arg(vm, 1);
if (buffer.type != DST_BYTEBUFFER)
buffer = dst_wrap_buffer(dst_buffer(vm, 10));
err = dst_serialize(vm, buffer.data.buffer, dst_arg(vm, 0));
if (err != NULL)
dst_c_throwc(vm, err);
dst_c_return(vm, buffer);
}
/* Deserialize data from a buffer */
int dst_stl_deserialize(Dst *vm) {
DstValue ret;
uint32_t len;
const uint8_t *data;
const char *err;
if (!dst_chararray_view(dst_arg(vm, 0), &data, &len))
dst_c_throwc(vm, "expected string/buffer/symbol");
err = dst_deserialize(vm, data, len, &ret, &data);
if (err != NULL)
dst_c_throwc(vm, err);
dst_c_return(vm, ret);
}
/***/ /***/
/* Function reflection */ /* Function reflection */
/***/ /***/
@ -884,7 +853,7 @@ static int dst_stl_gensym(Dst *vm) {
static int dst_stl_compile(Dst *vm) { static int dst_stl_compile(Dst *vm) {
DstTable *env = vm->env; DstTable *env = vm->env;
if (dst_arg(vm, 1).type == DST_TABLE) { if (dst_arg(vm, 1).type == DST_TABLE) {
env = dst_arg(vm, 1).data.table; env = dst_arg(vm, 1).as.table;
} }
dst_c_return(vm, dst_compile(vm, env, dst_arg(vm, 0))); dst_c_return(vm, dst_compile(vm, env, dst_arg(vm, 0)));
} }
@ -900,7 +869,7 @@ static int dst_stl_setenv(Dst *vm) {
if (newEnv.type != DST_TABLE) { if (newEnv.type != DST_TABLE) {
dst_c_throwc(vm, "expected table"); dst_c_throwc(vm, "expected table");
} }
vm->env = newEnv.data.table; vm->env = newEnv.as.table;
return DST_RETURN_OK; return DST_RETURN_OK;
} }
@ -966,8 +935,6 @@ static const DstModuleItem std_module[] = {
{"set!", dst_stl_set}, {"set!", dst_stl_set},
{"next", dst_stl_next}, {"next", dst_stl_next},
{"error", dst_stl_error}, {"error", dst_stl_error},
{"serialize", dst_stl_serialize},
{"deserialize", dst_stl_deserialize},
{"push!", dst_stl_push}, {"push!", dst_stl_push},
{"pop!", dst_stl_pop}, {"pop!", dst_stl_pop},
{"peek", dst_stl_peek}, {"peek", dst_stl_peek},
@ -992,7 +959,7 @@ void dst_stl_load(Dst *vm) {
DstValue maybeEnv = dst_table_get(vm->modules, dst_string_cvs(vm, "std")); DstValue maybeEnv = dst_table_get(vm->modules, dst_string_cvs(vm, "std"));
if (maybeEnv.type == DST_TABLE) { if (maybeEnv.type == DST_TABLE) {
/* Module already created, so merge into main vm. */ /* Module already created, so merge into main vm. */
dst_env_merge(vm, vm->env, maybeEnv.data.table); dst_env_merge(vm, vm->env, maybeEnv.as.table);
} else { } else {
/* Module not yet created */ /* Module not yet created */
/* Load the normal c functions */ /* Load the normal c functions */
@ -1009,6 +976,6 @@ void dst_stl_load(Dst *vm) {
dst_module_put(vm, "std", "stderr", dst_wrap_userdata(outp)); dst_module_put(vm, "std", "stderr", dst_wrap_userdata(outp));
/* Now merge */ /* Now merge */
maybeEnv = dst_table_get(vm->modules, dst_string_cvs(vm, "std")); maybeEnv = dst_table_get(vm->modules, dst_string_cvs(vm, "std"));
dst_env_merge(vm, vm->env, maybeEnv.data.table); dst_env_merge(vm, vm->env, maybeEnv.as.table);
} }
} }

View File

@ -21,22 +21,164 @@
*/ */
#include "internal.h" #include "internal.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"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"_=";
/* Calculate hash for string */
static uint32_t dst_string_calchash(const uint8_t *str, uint32_t len) {
const uint8_t *end = str + len;
uint32_t hash = 5381;
while (str < end)
hash = (hash << 5) + hash + *str++;
return hash;
}
/* 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 *str = (uint8_t *) (data + 2 * sizeof(uint32_t));
dst_string_length(str) = length;
str[length] = 0;
return str;
}
/* 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;
}
/* Load a buffer as a string */
static const uint8_t *dst_string(Dst *vm, 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);
if (status) {
return bucket->data.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);
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;
}
}
/* Helper for creating a unique string. Increment an integer
* represented as an array of integer digits. */
static void inc_counter(uint8_t *digits, int base, int len) {
int i;
uint8_t carry = 1;
for (i = len - 1; i >= 0; --i) {
digits[i] += carry;
carry = 0;
if (digits[i] == base) {
digits[i] = 0;
carry = 1;
}
}
}
/* 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) {
DstValue *bucket;
uint32_t hash;
uint8_t counter[6] = {63, 63, 63, 63, 63, 63};
/* Leave spaces for 6 base 64 digits and two dashes. That means 64^6 possible suffixes, which
* 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));
dst_string_length(str) = newlen;
memcpy(str, buf, len);
str[len] = '-';
str[len + 1] = '-';
str[newlen] = 0;
uint8_t *saltbuf = str + len + 2;
int status = 1;
while (status) {
int i;
inc_counter(counter, 64, 6);
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);
}
dst_string_hash(str) = hash;
return dst_cache_add_bucket(vm, 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) {
uint32_t len = 0;
while (s[len]) ++len;
return dst_string_unique(vm, (const uint8_t *)s, len);
}
/* Load a c string */
const uint8_t *dst_cstring(Dst *vm, const char *str) {
uint32_t len = 0;
while (str[len]) ++len;
return dst_string(vm, (const uint8_t *)str, len);
}
/* Temporary buffer size */ /* Temporary buffer size */
#define DST_BUFSIZE 36 #define DST_BUFSIZE 36
static const uint8_t *real_to_string(Dst *vm, DstReal x) { static uint32_t real_to_string_impl(uint8_t *buf, double x) {
uint8_t buf[DST_BUFSIZE];
int count = snprintf((char *) buf, DST_BUFSIZE, "%.21gF", x); int count = snprintf((char *) buf, DST_BUFSIZE, "%.21gF", x);
return dst_string_b(vm, buf, (uint32_t) count); return (uint32_t) count;
} }
static const uint8_t *integer_to_string(Dst *vm, DstInteger x) { static void real_to_string_b(Dst *vm, DstBuffer *buffer, double x) {
dst_buffer_ensure_(vm, 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) {
uint8_t buf[DST_BUFSIZE]; uint8_t buf[DST_BUFSIZE];
dst_string(vm, buf, real_to_string_impl(buf, x));
}
static uint32_t integer_to_string_impl(uint8_t *buf, int64_t x) {
int neg = 0; int neg = 0;
uint8_t *hi, *low; uint8_t *hi, *low;
uint32_t count = 0; uint32_t count = 0;
if (x == 0) return dst_string_c(vm, "0"); if (x == 0) {
buf[0] = '0';
return 1;
}
if (x < 0) { if (x < 0) {
neg = 1; neg = 1;
x = -x; x = -x;
@ -56,16 +198,24 @@ static const uint8_t *integer_to_string(Dst *vm, DstInteger x) {
*low++ = *hi; *low++ = *hi;
*hi-- = temp; *hi-- = temp;
} }
return dst_string_b(vm, buf, (uint32_t) count); return count;
} }
static const char *HEX_CHARACTERS = "0123456789abcdef"; static void integer_to_string_b(Dst *vm, DstBuffer *buffer, int64_t x) {
#define HEX(i) (((uint8_t *) HEX_CHARACTERS)[(i)]) dst_buffer_extra(vm, buffer, DST_BUFSIZE);
buffer->count += integer_to_string_impl(buffer->data + buffer->count, x);
}
/* Returns a string description for a pointer. Max titlelen is DST_BUFSIZE static void integer_to_string(Dst *vm, int64_t x) {
* - 5 - 2 * sizeof(void *). */
static const uint8_t *string_description(Dst *vm, const char *title, void *pointer) {
uint8_t buf[DST_BUFSIZE]; uint8_t buf[DST_BUFSIZE];
dst_string(vm, buf, integer_to_string_impl(buf, x));
}
#define HEX(i) (((uint8_t *) base64)[(i)])
/* Returns a string description for a pointer. Truncates
* title to 12 characters */
static uint32_t string_description_impl(uint8_t *buf, const char *title, void *pointer) {
uint8_t *c = buf; uint8_t *c = buf;
uint32_t i; uint32_t i;
union { union {
@ -75,7 +225,7 @@ static const uint8_t *string_description(Dst *vm, const char *title, void *point
pbuf.p = pointer; pbuf.p = pointer;
*c++ = '<'; *c++ = '<';
for (i = 0; title[i]; ++i) for (i = 0; title[i] && i < 12; ++i)
*c++ = ((uint8_t *)title) [i]; *c++ = ((uint8_t *)title) [i];
*c++ = ' '; *c++ = ' ';
*c++ = '0'; *c++ = '0';
@ -87,217 +237,219 @@ static const uint8_t *string_description(Dst *vm, const char *title, void *point
*c++ = HEX(byte & 0xF); *c++ = HEX(byte & 0xF);
} }
*c++ = '>'; *c++ = '>';
return dst_string_b(vm, buf, c - buf); return (uint32_t) (c - buf);
} }
/* Gets the string value of userdata. Allocates memory, so is slower than static void string_description_b(Dst *vm, DstBuffer *buffer, const char *title, void *pointer) {
* string_description. */ dst_buffer_ensure_(vm, buffer, buffer->count + DST_BUFSIZE);
static const uint8_t *string_udata(Dst *vm, const char *title, void *pointer) { buffer->count += string_description_impl(buffer->data + buffer->count, title, pointer);
uint32_t strlen = 0;
uint8_t *c, *buf;
uint32_t i;
union {
uint8_t bytes[sizeof(void *)];
void *p;
} pbuf;
while (title[strlen]) ++strlen;
c = buf = dst_alloc(vm, strlen + 5 + 2 * sizeof(void *));
pbuf.p = pointer;
*c++ = '<';
for (i = 0; title[i]; ++i)
*c++ = ((uint8_t *)title) [i];
*c++ = ' ';
*c++ = '0';
*c++ = 'x';
for (i = sizeof(void *); i > 0; --i) {
uint8_t byte = pbuf.bytes[i - 1];
if (!byte) continue;
*c++ = HEX(byte >> 4);
*c++ = HEX(byte & 0xF);
}
*c++ = '>';
return dst_string_b(vm, buf, c - buf);
} }
static const uint8_t *string_description(Dst *vm, const char *title, void *pointer) {
uint8_t buf[DST_BUFSIZE];
return dst_string(vm, buf, string_description_impl(buf, title, pointer));
}
#undef HEX
#undef DST_BUFSIZE #undef DST_BUFSIZE
void dst_escape_string(Dst *vm, DstBuffer *b, const uint8_t *str) { static uint32_t dst_escape_string_length(const uint8_t *str) {
uint32_t len = 2;
uint32_t i; uint32_t i;
dst_buffer_push(vm, b, '"');
for (i = 0; i < dst_string_length(str); ++i) { for (i = 0; i < dst_string_length(str); ++i) {
switch (str[i]) {
case '"':
case '\n':
case '\r':
case '\0':
len += 2;
break;
default:
len += 1;
break;
}
}
return len;
}
static void dst_escape_string_impl(uint8_t *buf, const uint8_t *str) {
uint32_t i, j;
buf[0] = '"';
for (i = 0, j = 1; i < dst_string_length(str); ++i) {
uint8_t c = str[i]; uint8_t c = str[i];
switch (c) { switch (c) {
case '"': case '"':
dst_buffer_push(vm, b, '\\'); buf[j++] = '\\';
dst_buffer_push(vm, b, '"'); buf[j++] = '"';
break; break;
case '\n': case '\n':
dst_buffer_push(vm, b, '\\'); buf[j++] = '\\';
dst_buffer_push(vm, b, 'n'); buf[j++] = 'n';
break; break;
case '\r': case '\r':
dst_buffer_push(vm, b, '\\'); buf[j++] = '\\';
dst_buffer_push(vm, b, 'r'); buf[j++] = 'r';
break; break;
case '\0': case '\0':
dst_buffer_push(vm, b, '\\'); buf[j++] = '\\';
dst_buffer_push(vm, b, '0'); buf[j++] = '0';
break; break;
default: default:
dst_buffer_push(vm, b, c); buf[j++] = c;
break; break;
} }
} }
dst_buffer_push(vm, b, '"'); buf[j++] = '"';
}
static void dst_escape_string_b(Dst *vm, DstBuffer *buffer, const uint8_t *str) {
uint32_t len = dst_escape_string_length(str);
dst_buffer_extra(vm, 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) {
uint32_t len = dst_escape_string_length(str);
uint8_t *buf = dst_string_begin(vm, len);
dst_escape_string_impl(buf, str);
return dst_string_end(vm, buf);
} }
/* Returns a string pointer with the description of the string */ /* Returns a string pointer with the description of the string */
const uint8_t *dst_short_description(Dst *vm, DstValue x) { static const uint8_t *dst_short_description(Dst *vm) {
switch (x.type) { switch (x.type) {
case DST_NIL: case DST_NIL:
return dst_string_c(vm, "nil"); return dst_cstring(vm, "nil");
case DST_BOOLEAN: case DST_BOOLEAN:
if (x.data.boolean) if (x.as.boolean)
return dst_string_c(vm, "true"); return dst_cstring(vm, "true");
else else
return dst_string_c(vm, "false"); return dst_cstring(vm, "false");
case DST_REAL: case DST_REAL:
return real_to_string(vm, x.data.real); return real_to_string(vm, x.as.real);
case DST_INTEGER: case DST_INTEGER:
return integer_to_string(vm, x.data.integer); return integer_to_string(vm, x.as.integer);
case DST_ARRAY:
return string_description(vm, "array", x.data.pointer);
case DST_TUPLE:
return string_description(vm, "tuple", x.data.pointer);
case DST_STRUCT:
return string_description(vm, "struct", x.data.pointer);
case DST_TABLE:
return string_description(vm, "table", x.data.pointer);
case DST_SYMBOL: case DST_SYMBOL:
return x.data.string; return x.as.string;
case DST_STRING: case DST_STRING:
{ return dst_escape_string(vm, x.as.string);
DstBuffer *buf = dst_buffer(vm, dst_string_length(x.data.string) + 4);
dst_escape_string(vm, buf, x.data.string);
return dst_buffer_to_string(vm, buf);
}
case DST_BYTEBUFFER:
return string_description(vm, "buffer", x.data.pointer);
case DST_CFUNCTION:
return string_description(vm, "cfunction", x.data.pointer);
case DST_FUNCTION:
return string_description(vm, "function", x.data.pointer);
case DST_THREAD:
return string_description(vm, "thread", x.data.pointer);
case DST_USERDATA: case DST_USERDATA:
return string_udata(vm, dst_udata_type(x.data.pointer)->name, x.data.pointer); return string_description(vm, dst_udata_type(x.as.pointer)->name, x.as.pointer);
case DST_FUNCENV: default:
return string_description(vm, "funcenv", x.data.pointer); return string_description(vm, types[x.type], x.as.pointer);
case DST_FUNCDEF:
return string_description(vm, "funcdef", x.data.pointer);
} }
} }
static DstValue wrap_integer(DstInteger i) { static void dst_short_description_b(Dst *vm, DstBuffer *buffer, DstValue x) {
DstValue v; switch (x.type) {
v.type = DST_INTEGER; case DST_NIL:
v.data.integer = i; dst_buffer_push_cstring(vm, buffer, "nil");
return v; return;
case DST_BOOLEAN:
if (x.as.boolean)
dst_buffer_push_cstring(vm, buffer, "true");
else
dst_buffer_push_cstring(vm, buffer, "false");
return;
case DST_REAL:
real_to_string_b(vm, buffer, x.as.real);
return;
case DST_INTEGER:
integer_to_string_b(vm, buffer, x.as.integer);
return;
case DST_SYMBOL:
dst_buffer_push_bytes(vm, buffer, x.as.string, dst_string_length(x.as.string));
return;
case DST_STRING:
dst_escape_string_b(vm, buffer, x.as.string);
return;
case DST_USERDATA:
string_description_b(vm, buffer, dst_udata_type(x.as.pointer)->name, x.as.pointer);
return;
default:
string_description_b(vm, buffer, types[x.type], x.as.pointer);
break;
}
} }
/* Static debug print helper */ /* Static debug print helper */
static DstInteger dst_description_helper(Dst *vm, DstBuffer *b, DstTable *seen, DstValue x, DstInteger next, int depth) { static int64_t dst_description_helper(Dst *vm, DstBuffer *b, DstTable *seen, DstValue x, int64_t next, int depth) {
DstValue check = dst_table_get(seen, x); DstValue check = dst_table_get(seen, x);
const uint8_t *str;
/* Prevent a stack overflow */
if (depth++ > DST_RECURSION_GUARD)
return -1;
if (check.type == DST_INTEGER) { if (check.type == DST_INTEGER) {
str = integer_to_string(vm, check.data.integer); dst_buffer_push_cstring(vm, b, "<cycle>");
dst_buffer_append_cstring(vm, b, "<visited ");
dst_buffer_append(vm, b, str, dst_string_length(str));
dst_buffer_append_cstring(vm, b, ">");
} else { } else {
uint8_t open, close; const char *open;
const char *close;
uint32_t len, i; uint32_t len, i;
const DstValue *data; const DstValue *data;
switch (x.type) { switch (x.type) {
default: default:
str = dst_short_description(vm, x); dst_short_description_b(vm, b, x);
dst_buffer_append(vm, b, str, dst_string_length(str));
return next;
case DST_STRING:
dst_escape_string(vm, b, x.data.string);
return next;
case DST_SYMBOL:
dst_buffer_append(vm, b, x.data.string, dst_string_length(x.data.string));
return next;
case DST_NIL:
dst_buffer_append_cstring(vm, b, "nil");
return next;
case DST_BOOLEAN:
dst_buffer_append_cstring(vm, b, x.data.boolean ? "true" : "false");
return next; return next;
case DST_STRUCT: case DST_STRUCT:
open = '<'; close = '>'; open = "{"; close = "}";
break; break;
case DST_TABLE: case DST_TABLE:
open = '{'; close = '}'; open = "{"; close = "}";
break; break;
case DST_TUPLE: case DST_TUPLE:
open = '('; close = ')'; open = "("; close = ")";
break; break;
case DST_ARRAY: case DST_ARRAY:
open = '['; close = ']'; open = "["; close = "]";
break; break;
} }
dst_table_put(vm, seen, x, wrap_integer(next++)); dst_table_put(vm, seen, x, dst_wrap_integer(next++));
dst_buffer_push(vm, b, open); dst_buffer_push_cstring(vm, b, open);
if (dst_hashtable_view(x, &data, &len)) { if (depth == 0) {
dst_buffer_push_cstring(vm, b, "...");
} else if (dst_hashtable_view(x, &data, &len)) {
int isfirst = 1; int isfirst = 1;
for (i = 0; i < len; i += 2) { for (i = 0; i < len; i += 2) {
if (data[i].type != DST_NIL) { if (data[i].type != DST_NIL) {
if (isfirst) if (isfirst)
isfirst = 0; isfirst = 0;
else else
dst_buffer_push(vm, b, ' '); dst_buffer_push_u8(vm, b, ' ');
next = dst_description_helper(vm, b, seen, data[i], next, depth); next = dst_description_helper(vm, b, seen, data[i], next, depth - 1);
if (next == -1) dst_buffer_push_u8(vm, b, ' ');
dst_buffer_append_cstring(vm, b, "..."); next = dst_description_helper(vm, b, seen, data[i + 1], next, depth - 1);
dst_buffer_push(vm, b, ' ');
next = dst_description_helper(vm, b, seen, data[i + 1], next, depth);
if (next == -1)
dst_buffer_append_cstring(vm, b, "...");
} }
} }
} else if (dst_seq_view(x, &data, &len)) { } else if (dst_seq_view(x, &data, &len)) {
for (i = 0; i < len; ++i) { for (i = 0; i < len; ++i) {
next = dst_description_helper(vm, b, seen, data[i], next, depth); next = dst_description_helper(vm, b, seen, data[i], next, depth - 1);
if (next == -1)
return -1;
if (i != len - 1) if (i != len - 1)
dst_buffer_push(vm, b, ' '); dst_buffer_push_u8(vm, b, ' ');
} }
} }
dst_buffer_push(vm, b, close); dst_buffer_push_cstring(vm, b, close);
} }
return next; return next;
} }
/* Debug print. Returns a description of an object as a buffer. */ /* Debug print. Returns a description of an object as a string. */
const uint8_t *dst_description(Dst *vm, DstValue x) { const uint8_t *dst_description(Dst *vm, DstValue x) {
DstBuffer *buf = dst_buffer(vm, 10); DstBuffer *buf = dst_buffer(vm, 10);
dst_description_helper(vm, buf, dst_table(vm, 10), x, 0, 0); DstTable *seen = dst_table(vm, 10);
return dst_buffer_to_string(vm, buf);
/* Only print description up to a depth of 4 */
dst_description_helper(vm, buf, seen, x, 0, 4);
return dst_string(vm, buf->data, buf->count);
} }
/* Convert any value to a dst string */
const uint8_t *dst_to_string(Dst *vm, DstValue x) { const uint8_t *dst_to_string(Dst *vm, DstValue x) {
if (x.type == DST_STRING || x.type == DST_SYMBOL) { DstValue x = dst_getv(vm, -1);
return x.data.string; switch (x.type) {
} else if (x.type == DST_BYTEBUFFER) { default:
return dst_buffer_to_string(vm, x.data.buffer);
} else {
return dst_description(vm, x); return dst_description(vm, 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);
} }
} }

184
core/struct.c Normal file
View File

@ -0,0 +1,184 @@
/*
* 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 "cache.h"
/* Begin creation of a struct */
DstValue *dst_struct_begin(Dst *vm, 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
* sizeof(uint32_t) * 2 + 2 * count * sizeof(DstValue). Adding more space
* 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)
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
* were successfully added. If this number is not equal to the
* original number, we will need to remake the struct using
* only the kv pairs in the struct */
dst_struct_hash(st) = 0;
return st;
}
/* 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 i;
for (i = index; i < cap; i += 2)
if (st[i].type == DST_NIL || dst_equals(st[i], key))
return st + i;
for (i = 0; i < index; i += 2)
if (st[i].type == DST_NIL || dst_equals(st[i], key))
return st + i;
return NULL;
}
/* Put a kv pair into a struct that has not yet been fully constructed.
* Nil keys and values are ignored, extra keys are ignore, and duplicate keys are
* ignored. */
void dst_struct_put(DstValue *st, DstValue key, DstValue value) {
uint32_t cap = dst_struct_capacity(st);
uint32_t hash = dst_hash(key);
uint32_t index = (hash % (cap / 2)) * 2;
uint32_t i, j, dist;
uint32_t bounds[4] = {index, cap, 0, index};
if (key.type == DST_NIL || value.type == DST_NIL) return;
/* Avoid extra items */
if (dst_struct_hash(st) == dst_struct_length(st)) return;
for (dist = 0, j = 0; j < 4; j += 2)
for (i = bounds[j]; i < bounds[j + 1]; i += 2, dist += 2) {
int status;
uint32_t otherhash, otherindex, otherdist;
/* We found an empty slot, so just add key and value */
if (st[i].type == DST_NIL) {
st[i] = key;
st[i + 1] = value;
/* Update the temporary count */
dst_struct_hash(st)++;
return;
}
/* Robinhood hashing - check if colliding kv pair
* is closer to their source than current. We use robinhood
* hashing to ensure that equivalent structs that are contsructed
* with different order have the same internal layout, and therefor
* will compare properly - i.e., {1 2 3 4} should equal {3 4 1 2}. */
otherhash = dst_hash(st[i]);
otherindex = (otherhash % (cap / 2)) * 2;
otherdist = (i + cap - otherindex) % cap;
if (dist < otherdist)
status = -1;
else if (otherdist < dist)
status = 1;
else if (hash < otherhash)
status = -1;
else if (otherhash < hash)
status = 1;
else
status = dst_compare(key, st[i]);
/* If other is closer to their ideal slot */
if (status == 1) {
/* Swap current kv pair with pair in slot */
DstValue t1, t2;
t1 = st[i];
t2 = st[i + 1];
st[i] = key;
st[i + 1] = value;
key = t1;
value = t2;
/* Save dist and hash of new kv pair */
dist = otherdist;
hash = otherhash;
} else {
/* This should not happen - it means
* than a key was added to the struct more than once */
return;
}
}
}
/* Finish building a struct */
static const DstValue *dst_struct_end(Dst *vm, DstValue *st) {
DstValue cached;
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
* succeed. */
uint32_t i, realCount;
DstValue *newst;
realCount = 0;
for (i = 0; i < dst_struct_capacity(st); i += 2) {
realCount += st[i].type != DST_NIL;
}
newst = dst_struct_begin(vm, 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;
}
/* Get an item from a struct */
DstValue dst_struct_get(const DstValue *st, DstValue key) {
const DstValue *bucket = dst_struct_find(st, key);
if (!bucket || bucket[0].type == DST_NIL) {
DstValue ret;
ret.type = DST_NIL;
return ret;
} else {
return bucket[1];
}
}
/* Get the next key in a struct */
DstValue dst_struct_next(const DstValue *st, DstValue key) {
const DstValue *bucket, *end;
end = st + dst_struct_capacity(st);
if (key.type == DST_NIL) {
bucket = st;
} else {
bucket = dst_struct_find(st, key);
if (!bucket || bucket[0].type == DST_NIL)
return dst_wrap_nil();
bucket += 2;
}
for (; bucket < end; bucket += 2) {
if (bucket[0].type != DST_NIL)
return bucket[0];
}
return dst_wrap_nil();
}

163
core/table.c Normal file
View File

@ -0,0 +1,163 @@
/*
* 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 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);
if (capacity < 2) capacity = 2;
if (NULL == data) {
DST_OUT_OF_MEMORY;
}
table->data = data;
table->capacity = capacity;
table->count = 0;
table->deleted = 0;
return table;
}
/* 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) {
uint32_t index = (dst_hash(key) % (t->capacity / 2)) * 2;
uint32_t i, j;
uint32_t start[2], end[2];
start[0] = index; end[0] = t->capacity;
start[1] = 0; end[1] = index;
for (j = 0; j < 2; ++j) {
for (i = start[j]; i < end[j]; i += 2) {
if (t->data[i].type == DST_NIL) {
if (t->data[i + 1].type == DST_NIL) {
/* Empty */
return t->data + i;
}
} else if (dst_equals(t->data[i], key)) {
return t->data + i;
}
}
}
return NULL;
}
/* Resize the dictionary table. */
static void dst_table_rehash(Dst *vm, DstTable *t, uint32_t size) {
DstValue *olddata = t->data;
DstValue *newdata = calloc(sizeof(DstValue), size);
if (NULL == newdata) {
DST_OUT_OF_MEMORY;
}
uint32_t i, oldcapacity;
oldcapacity = t->capacity;
t->data = newdata;
t->capacity = size;
t->deleted = 0;
for (i = 0; i < oldcapacity; i += 2) {
if (olddata[i].type != DST_NIL) {
DstValue *bucket = dst_table_find(t, olddata[i]);
bucket[0] = olddata[i];
bucket[1] = olddata[i + 1];
}
}
free(olddata);
}
/* Get a value out of the object */
DstValue dst_table_get(DstTable *t, DstValue key) {
DstValue *bucket = dst_table_find(t, key);
if (bucket && bucket[0].type != DST_NIL)
return bucket[1];
else
return dst_wrap_nil();
}
/* Remove an entry from the dictionary. Return the value that
* was removed. */
DstValue dst_table_remove(DstTable *t, DstValue key) {
DstValue *bucket = dst_table_find(t, key);
if (bucket && bucket[0].type != DST_NIL) {
DstValue ret = bucket[1];
t->count--;
t->deleted++;
bucket[0].type = DST_NIL;
bucket[1].type = DST_BOOLEAN;
return ret;
} else {
return dst_wrap_nil();
}
}
/* Put a value into the object */
void dst_table_put(Dst *vm, DstTable *t, DstValue key, DstValue value) {
if (key.type == DST_NIL) return;
if (value.type == DST_NIL) {
dst_table_remove(t, key);
} else {
DstValue *bucket = dst_table_find(t, key);
if (bucket && bucket[0].type != DST_NIL) {
bucket[1] = value;
} else {
if (!bucket || 4 * (t->count + t->deleted) >= t->capacity) {
dst_table_rehash(vm, t, 4 * t->count + 6);
}
bucket = dst_table_find(t, key);
if (bucket[1].type == DST_BOOLEAN)
--t->deleted;
bucket[0] = key;
bucket[1] = value;
++t->count;
}
}
}
/* Clear a table */
void dst_table_clear(DstTable *t) {
uint32_t capacity = t->capacity;
uint32_t i;
DstValue *data = t->data;
for (i = 0; i < capacity; i += 2)
data[i].type = DST_NIL;
t->count = 0;
t->deleted = 0;
}
/* Find next key in an object. Returns nil if no next key. */
DstValue dst_table_next(DstTable *t, DstValue key) {
const DstValue *bucket, *end;
end = t->data + t->capacity;
if (key.type == DST_NIL) {
bucket = t->data;
} else {
bucket = dst_table_find(t, key);
if (!bucket || bucket[0].type == DST_NIL)
return dst_wrap_nil();
bucket += 2;
}
for (; bucket < end; bucket += 2) {
if (bucket[0].type != DST_NIL)
return bucket[0];
}
return dst_wrap_nil();
}

View File

@ -21,13 +21,19 @@
*/ */
#include "internal.h" #include "internal.h"
#include "wrap.h"
#include "gc.h"
/* Create a new thread */ /* Initialize a new thread */
DstThread *dst_thread(Dst *vm, DstValue callee, uint32_t capacity) { DstThread *dst_thread(Dst *vm, DstValue callee, uint32_t capacity) {
DstThread *thread = dst_alloc(vm, sizeof(DstThread)); DstThread *thread = dst_alloc(vm, DST_MEMORY_THREAD, sizeof(DstThread));
if (capacity < DST_FRAME_SIZE) capacity = DST_FRAME_SIZE; if (capacity < DST_FRAME_SIZE) capacity = DST_FRAME_SIZE;
thread->data = dst_alloc(vm, sizeof(DstValue) * capacity);
thread->capacity = capacity; 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); return dst_thread_reset(vm, thread, callee);
} }
@ -49,7 +55,7 @@ DstThread *dst_thread_reset(Dst *vm, DstThread *thread, DstValue callee) {
return thread; return thread;
} }
/* Ensure that the thread has enough EXTRA capacity */ /* Ensure that the thread has enough extra capacity */
void dst_thread_ensure_extra(Dst *vm, DstThread *thread, uint32_t extra) { void dst_thread_ensure_extra(Dst *vm, DstThread *thread, uint32_t extra) {
DstValue *newData, *stack; DstValue *newData, *stack;
uint32_t usedCapacity, neededCapacity, newCapacity; uint32_t usedCapacity, neededCapacity, newCapacity;
@ -58,8 +64,12 @@ void dst_thread_ensure_extra(Dst *vm, DstThread *thread, uint32_t extra) {
neededCapacity = usedCapacity + extra; neededCapacity = usedCapacity + extra;
if (thread->capacity >= neededCapacity) return; if (thread->capacity >= neededCapacity) return;
newCapacity = 2 * neededCapacity; newCapacity = 2 * neededCapacity;
newData = dst_alloc(vm, sizeof(DstValue) * newCapacity);
dst_memcpy(newData, thread->data, sizeof(DstValue) * usedCapacity); newData = realloc(thread->data, sizeof(DstValue) * newCapacity);
if (NULL == newData) {
DST_OUT_OF_MEMORY;
}
thread->data = newData; thread->data = newData;
thread->capacity = newCapacity; thread->capacity = newCapacity;
} }
@ -85,7 +95,8 @@ void dst_thread_pushnil(Dst *vm, DstThread *thread, uint32_t n) {
dst_frame_size(stack) += n; dst_frame_size(stack) += n;
} }
/* Package up extra args after and including n into tuple at 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) { void dst_thread_tuplepack(Dst *vm, DstThread *thread, uint32_t n) {
DstValue *stack = thread->data + thread->count; DstValue *stack = thread->data + thread->count;
uint32_t size = dst_frame_size(stack); uint32_t size = dst_frame_size(stack);
@ -94,7 +105,7 @@ void dst_thread_tuplepack(Dst *vm, DstThread *thread, uint32_t n) {
dst_thread_pushnil(vm, thread, n - size + 1); dst_thread_pushnil(vm, thread, n - size + 1);
stack = thread->data + thread->count; stack = thread->data + thread->count;
stack[n].type = DST_TUPLE; stack[n].type = DST_TUPLE;
stack[n].data.tuple = dst_tuple_end(vm, dst_tuple_begin(vm, 0)); stack[n].as.tuple = dst_tuple_end(vm, dst_tuple_begin(vm, 0));
dst_frame_size(stack) = n + 1; dst_frame_size(stack) = n + 1;
} else { } else {
uint32_t i; uint32_t i;
@ -102,7 +113,7 @@ void dst_thread_tuplepack(Dst *vm, DstThread *thread, uint32_t n) {
for (i = n; i < size; ++i) for (i = n; i < size; ++i)
tuple[i - n] = stack[i]; tuple[i - n] = stack[i];
stack[n].type = DST_TUPLE; stack[n].type = DST_TUPLE;
stack[n].data.tuple = dst_tuple_end(vm, tuple); stack[n].as.tuple = dst_tuple_end(vm, tuple);
} }
} }
@ -136,7 +147,7 @@ void dst_thread_endframe(Dst *vm, DstThread *thread) {
DstValue *stack = thread->data + thread->count; DstValue *stack = thread->data + thread->count;
DstValue callee = dst_frame_callee(stack); DstValue callee = dst_frame_callee(stack);
if (callee.type == DST_FUNCTION) { if (callee.type == DST_FUNCTION) {
DstFunction *fn = callee.data.function; DstFunction *fn = callee.as.function;
uint32_t locals = fn->def->locals; uint32_t locals = fn->def->locals;
dst_frame_pc(stack) = fn->def->byteCode; dst_frame_pc(stack) = fn->def->byteCode;
if (fn->def->flags & DST_FUNCDEF_FLAG_VARARG) { if (fn->def->flags & DST_FUNCDEF_FLAG_VARARG) {
@ -162,8 +173,8 @@ DstValue *dst_thread_popframe(Dst *vm, DstThread *thread) {
uint32_t size = dst_frame_size(stack); uint32_t size = dst_frame_size(stack);
env->thread = NULL; env->thread = NULL;
env->stackOffset = size; env->stackOffset = size;
env->values = dst_alloc(vm, sizeof(DstValue) * size); env->values = malloc(sizeof(DstValue) * size);
dst_memcpy(env->values, stack, sizeof(DstValue) * size); memcpy(env->values, stack, sizeof(DstValue) * size);
} }
/* Shrink stack */ /* Shrink stack */

47
core/tuple.c Normal file
View File

@ -0,0 +1,47 @@
/*
* 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 "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 *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) {
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;
}
const DstValue *dst_tuple_n(Dst *vm, DstValue *values, uint32_t n) {
DstValue *t = dst_tuple_begin(vm, n);
memcpy(t, values, sizeof(DstValue) * n);
return dst_tuple_end(vm, t);
}

35
core/userdata.c Normal file
View File

@ -0,0 +1,35 @@
/*
* 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"
/* 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);
DstUserdataHeader *header = (DstUserdataHeader *)data;
void *user = data + sizeof(DstUserdataHeader);
header->size = size;
header->type = utype;
return dst_wrap_userdata(user);
}

View File

@ -22,102 +22,32 @@
#include "internal.h" #include "internal.h"
/****/ int dst_checkerr(Dst *vm) { return !!vm->flags; }
/* Parsing utils */
/****/
/* Get an integer power of 10 */ void dst_return(Dst *vm, DstValue x) {
static double exp10(int power) { vm->ret = x;
if (power == 0) return 1;
if (power > 0) {
double result = 10;
int currentPower = 1;
while (currentPower * 2 <= power) {
result = result * result;
currentPower *= 2;
}
return result * exp10(power - currentPower);
} else {
return 1 / exp10(-power);
}
} }
int dst_read_integer(const uint8_t *string, const uint8_t *end, int64_t *ret) { void dst_throw(Dst *vm) {
int sign = 1, x = 0; vm->flags = 1;
int64_t accum = 0; vm->ret = dst_popv(vm);
if (*string == '-') {
sign = -1;
++string;
} else if (*string == '+') {
++string;
}
if (string >= end) return 0;
while (string < end) {
x = *string;
if (x < '0' || x > '9') return 0;
x -= '0';
accum = accum * 10 + x;
++string;
}
*ret = accum * sign;
return 1;
} }
/* Read a real from a string. Returns if successfuly void dst_cerr(Dst *vm, const char *message) {
* parsed a real from the enitre input string. vm->flags = 1;
* If returned 1, output is int ret.*/ vm->ret = dst_string_cv(vm, message);
int dst_read_real(const uint8_t *string, const uint8_t *end, double *ret, int forceInt) {
int sign = 1, x = 0;
double accum = 0, exp = 1, place = 1;
/* Check the sign */
if (*string == '-') {
sign = -1;
++string;
} else if (*string == '+') {
++string;
}
if (string >= end) return 0;
while (string < end) {
if (*string == '.' && !forceInt) {
place = 0.1;
} else if (!forceInt && (*string == 'e' || *string == 'E')) {
/* Read the exponent */
++string;
if (string >= end) return 0;
if (!dst_read_real(string, end, &exp, 1))
return 0;
exp = exp10(exp);
break;
} else {
x = *string;
if (x < '0' || x > '9') return 0;
x -= '0';
if (place < 1) {
accum += x * place;
place *= 0.1;
} else {
accum *= 10;
accum += x;
}
}
++string;
}
*ret = accum * sign * exp;
return 1;
} }
/* Utilities for manipulating different types with the same semantics */
/* Read both tuples and arrays as c pointers + uint32_t length. Return 1 if the /* Read both tuples and arrays as c pointers + uint32_t length. Return 1 if the
* view can be constructed, 0 if an invalid type. */ * view can be constructed, 0 if an invalid type. */
int dst_seq_view(DstValue seq, const DstValue **data, uint32_t *len) { int dst_seq_view(DstValue seq, const DstValue **data, uint32_t *len) {
if (seq.type == DST_ARRAY) { if (seq.type == DST_ARRAY) {
*data = seq.data.array->data; *data = seq.as.array->data;
*len = seq.data.array->count; *len = seq.as.array->count;
return 1; return 1;
} else if (seq.type == DST_TUPLE) { } else if (seq.type == DST_TUPLE) {
*data = seq.data.st; *data = seq.as.st;
*len = dst_tuple_length(seq.data.st); *len = dst_tuple_length(seq.as.st);
return 1; return 1;
} }
return 0; return 0;
@ -127,12 +57,12 @@ int dst_seq_view(DstValue seq, const DstValue **data, uint32_t *len) {
* Returns 1 if the view can be constructed and 0 if the type is invalid. */ * Returns 1 if the view can be constructed and 0 if the type is invalid. */
int dst_chararray_view(DstValue str, const uint8_t **data, uint32_t *len) { int dst_chararray_view(DstValue str, const uint8_t **data, uint32_t *len) {
if (str.type == DST_STRING || str.type == DST_SYMBOL) { if (str.type == DST_STRING || str.type == DST_SYMBOL) {
*data = str.data.string; *data = str.as.string;
*len = dst_string_length(str.data.string); *len = dst_string_length(str.as.string);
return 1; return 1;
} else if (str.type == DST_BYTEBUFFER) { } else if (str.type == DST_BUFFER) {
*data = str.data.buffer->data; *data = str.as.buffer->data;
*len = str.data.buffer->count; *len = str.as.buffer->count;
return 1; return 1;
} }
return 0; return 0;
@ -143,211 +73,32 @@ int dst_chararray_view(DstValue str, const uint8_t **data, uint32_t *len) {
* 0 if the type is invalid. */ * 0 if the type is invalid. */
int dst_hashtable_view(DstValue tab, const DstValue **data, uint32_t *cap) { int dst_hashtable_view(DstValue tab, const DstValue **data, uint32_t *cap) {
if (tab.type == DST_TABLE) { if (tab.type == DST_TABLE) {
*data = tab.data.table->data; *data = tab.as.table->data;
*cap = tab.data.table->capacity; *cap = tab.as.table->capacity;
return 1; return 1;
} else if (tab.type == DST_STRUCT) { } else if (tab.type == DST_STRUCT) {
*data = tab.data.st; *data = tab.as.st;
*cap = dst_struct_capacity(tab.data.st); *cap = dst_struct_capacity(tab.as.st);
return 1; return 1;
} }
return 0; return 0;
} }
DstReal dst_integer_to_real(DstInteger x) { /* Convert an index used by the capi to an absolute index */
return (DstReal) x; uint32_t dst_startrange(int64_t index, uint32_t modulo) {
if (index < 0) index += modulo;
return ((index >= 0 && index < modulo)) ? ((uint32_t) index) : 0;
} }
DstInteger dst_real_to_integer(DstReal x) { /* Convert an index used by the capi to an absolute index */
return (DstInteger) x; uint32_t dst_endrange(int64_t index, uint32_t modulo) {
return dst_startrange(index, modulo + 1);
} }
uint32_t dst_startrange(DstInteger raw, uint32_t len) { /* Convert a possibly negative index to a positive index on the current stack. */
if (raw >= len) int64_t dst_normalize_index(Dst *vm, int64_t index) {
return -1; if (index < 0) {
if (raw < 0) index += dst_frame_size(dst_thread_stack(vm->thread));
return len + raw;
return raw;
}
uint32_t dst_endrange(DstInteger raw, uint32_t len) {
if (raw > len)
return -1;
if (raw < 0)
return len + raw + 1;
return raw;
}
static DstValue cfunction(DstCFunction fn) {
DstValue n;
n.type = DST_CFUNCTION;
n.data.cfunction = fn;
return n;
}
int dst_callc(Dst *vm, DstCFunction fn, int numargs, ...) {
int result, i;
va_list args;
DstValue *stack;
va_start(args, numargs);
stack = dst_thread_beginframe(vm, vm->thread, cfunction(fn), numargs);
for (i = 0; i < numargs; ++i) {
stack[i] = va_arg(args, DstValue);
} }
va_end(args); return index;
result = fn(vm);
dst_thread_popframe(vm, vm->thread);
return result;
}
/* Stack manipulation functions */
int dst_checkerr(Dst *vm) {
return !!vm->flags;
}
/* Get an argument from the stack */
DstValue dst_arg(Dst *vm, uint32_t index) {
DstValue *stack = dst_thread_stack(vm->thread);
uint32_t frameSize = dst_frame_size(stack);
if (frameSize <= index) {
DstValue ret;
ret.type = DST_NIL;
return ret;
}
return stack[index];
}
/* Put a value on the stack */
void dst_set_arg(Dst* vm, uint32_t index, DstValue x) {
DstValue *stack = dst_thread_stack(vm->thread);
uint32_t frameSize = dst_frame_size(stack);
if (frameSize <= index) return;
stack[index] = x;
}
/* Get the size of the VMStack */
uint32_t dst_args(Dst *vm) {
DstValue *stack = dst_thread_stack(vm->thread);
return dst_frame_size(stack);
}
void dst_addsize(Dst *vm, uint32_t n) {
dst_thread_pushnil(vm, vm->thread, n);
}
void dst_setsize(Dst *vm, uint32_t n) {
DstValue *stack = dst_thread_stack(vm->thread);
uint32_t frameSize = dst_frame_size(stack);
if (frameSize < n) {
dst_thread_ensure_extra(vm, vm->thread, n - frameSize);
}
dst_frame_size(stack) = n;
}
void dst_swap(Dst *vm, uint32_t x, uint32_t y) {
DstValue oldx = dst_arg(vm, x);
DstValue oldy = dst_arg(vm, y);
dst_set_arg(vm, x, oldy);
dst_set_arg(vm, y, oldx);
}
void dst_move(Dst *vm, uint32_t dest, uint32_t src) {
dst_set_arg(vm, dest, dst_arg(vm, src));
}
void dst_nil(Dst *vm, uint32_t dest) {
DstValue n;
n.type = DST_NIL;
dst_set_arg(vm, dest, n);
}
void dst_true(Dst *vm, uint32_t dest) {
dst_set_boolean(vm, dest, 1);
}
void dst_false(Dst *vm, uint32_t dest) {
dst_set_boolean(vm, dest, 0);
}
/* Boolean Functions */
void dst_set_boolean(Dst *vm, uint32_t dest, int val) {
DstValue n;
n.type = DST_BOOLEAN;
n.data.boolean = val;
dst_set_arg(vm, dest, n);
}
int dst_get_boolean(Dst *vm, uint32_t b) {
DstValue x = dst_arg(vm, b);
if (x.type != DST_BOOLEAN) {
return 0;
}
return x.data.boolean;
}
/* Integer functions */
void dst_set_integer(Dst *vm, uint32_t dest, int64_t val) {
DstValue n;
n.type = DST_INTEGER;
n.data.integer = val;
dst_set_arg(vm, dest, n);
}
int64_t dst_get_integer(Dst *vm, uint32_t i) {
DstValue x = dst_arg(vm, i);
if (x.type != DST_INTEGER) {
return 0;
}
return x.data.integer;
}
/* Real functions */
void dst_set_real(Dst *vm, uint32_t dest, double val) {
DstValue n;
n.type = DST_REAL;
n.data.real = val;
dst_set_arg(vm, dest, n);
}
double dst_get_real(Dst *vm, uint32_t r) {
DstValue x = dst_arg(vm, r);
if (x.type != DST_REAL) {
return 0.0;
}
return x.data.real;
}
/* CFunction functions */
void dst_set_cfunction(Dst *vm, uint32_t dest, DstCFunction cfn) {
DstValue n;
n.type = DST_CFUNCTION;
n.data.cfunction = cfn;
dst_set_arg(vm, dest, n);
}
DstCFunction dst_get_cfunction(Dst *vm, uint32_t cfn) {
DstValue x = dst_arg(vm, cfn);
if (x.type != DST_CFUNCTION) {
return NULL;
}
return x.data.cfunction;
}
void dst_return(Dst *vm, uint32_t index) {
vm->ret = dst_arg(vm, index);
}
void dst_throw(Dst *vm, uint32_t index) {
vm->flags = 1;
vm->ret = dst_arg(vm, index);
}
void dst_cerr(Dst *vm, const char *message) {
vm->flags = 1;
vm->ret = dst_string_cv(vm, message);
}
int dst_checktype(Dst *vm, uint32_t n, DstType type) {
return dst_arg(vm, n).type == type;
} }

View File

@ -22,13 +22,17 @@
#include "internal.h" #include "internal.h"
/*
* Define a number of functions that can be used internally on ANY DstValue.
*/
/* Boolean truth definition */ /* Boolean truth definition */
int dst_value_truthy(DstValue v) { int dst_truthy(DstValue v) {
return v.type != DST_NIL && !(v.type == DST_BOOLEAN && !v.data.boolean); return v.type != DST_NIL && !(v.type == DST_BOOLEAN && !v.as.boolean);
} }
/* Check if two values are equal. This is strict equality with no conversion. */ /* Check if two values are equal. This is strict equality with no conversion. */
int dst_value_equals(DstValue x, DstValue y) { int dst_equals(DstValue x, DstValue y) {
int result = 0; int result = 0;
if (x.type != y.type) { if (x.type != y.type) {
result = 0; result = 0;
@ -38,17 +42,17 @@ int dst_value_equals(DstValue x, DstValue y) {
result = 1; result = 1;
break; break;
case DST_BOOLEAN: case DST_BOOLEAN:
result = (x.data.boolean == y.data.boolean); result = (x.as.boolean == y.as.boolean);
break; break;
case DST_REAL: case DST_REAL:
result = (x.data.real == y.data.real); result = (x.as.real == y.as.real);
break; break;
case DST_INTEGER: case DST_INTEGER:
result = (x.data.integer == y.data.integer); result = (x.as.integer == y.as.integer);
break; break;
default: default:
/* compare pointers */ /* compare pointers */
result = (x.data.pointer == y.data.pointer); result = (x.as.pointer == y.as.pointer);
break; break;
} }
} }
@ -56,75 +60,106 @@ int dst_value_equals(DstValue x, DstValue y) {
} }
/* Computes a hash value for a function */ /* Computes a hash value for a function */
uint32_t dst_value_hash(DstValue x) { uint32_t dst_hash(DstValue x) {
uint32_t hash = 0; uint32_t hash = 0;
switch (x.type) { switch (x.type) {
case DST_NIL: case DST_NIL:
hash = 0; hash = 0;
break; break;
case DST_BOOLEAN: case DST_BOOLEAN:
hash = x.data.boolean; hash = x.as.boolean;
break; break;
case DST_STRING: case DST_STRING:
case DST_SYMBOL: case DST_SYMBOL:
hash = dst_string_hash(x.data.string); hash = dst_string_hash(x.as.string);
break; break;
case DST_TUPLE: case DST_TUPLE:
hash = dst_tuple_hash(x.data.tuple); hash = dst_tuple_hash(x.as.tuple);
break; break;
case DST_STRUCT: case DST_STRUCT:
hash = dst_struct_hash(x.data.st); hash = dst_struct_hash(x.as.st);
break; break;
default: default:
if (sizeof(double) == sizeof(void *)) { if (sizeof(double) == sizeof(void *)) {
/* Assuming 8 byte pointer */ /* Assuming 8 byte pointer */
hash = x.data.dwords[0] ^ x.data.dwords[1]; hash = x.as.dwords[0] ^ x.as.dwords[1];
} else { } else {
/* Assuming 4 byte pointer (or smaller) */ /* Assuming 4 byte pointer (or smaller) */
hash = (uint32_t) x.data.pointer; hash = (uint32_t) x.as.pointer;
} }
break; break;
} }
return hash; return hash;
} }
/* Computes hash of an array of values */
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++);
return hash;
}
/* Compare two strings */
int dst_string_compare(const uint8_t *lhs, const uint8_t *rhs) {
uint32_t xlen = dst_string_length(lhs);
uint32_t ylen = dst_string_length(rhs);
uint32_t len = xlen > ylen ? ylen : xlen;
uint32_t i;
for (i = 0; i < len; ++i) {
if (lhs[i] == rhs[i]) {
continue;
} else if (lhs[i] < rhs[i]) {
return -1; /* x is less than y */
} else {
return 1; /* y is less than x */
}
}
if (xlen == ylen) {
return 0;
} else {
return xlen < ylen ? -1 : 1;
}
}
/* Compares x to y. If they are equal retuns 0. If x is less, returns -1. /* Compares x to y. If they are equal retuns 0. If x is less, returns -1.
* If y is less, returns 1. All types are comparable * If y is less, returns 1. All types are comparable
* and should have strict ordering. */ * and should have strict ordering. */
int dst_value_compare(DstValue x, DstValue y) { int dst_compare(DstValue x, DstValue y) {
if (x.type == y.type) { if (x.type == y.type) {
switch (x.type) { switch (x.type) {
case DST_NIL: case DST_NIL:
return 0; return 0;
case DST_BOOLEAN: case DST_BOOLEAN:
if (x.data.boolean == y.data.boolean) { if (x.as.boolean == y.as.boolean) {
return 0; return 0;
} else { } else {
return x.data.boolean ? 1 : -1; return x.as.boolean ? 1 : -1;
} }
case DST_REAL: case DST_REAL:
if (x.data.real == y.data.real) { if (x.as.real == y.as.real) {
return 0; return 0;
} else { } else {
return x.data.real > y.data.real ? 1 : -1; return x.as.real > y.as.real ? 1 : -1;
} }
case DST_INTEGER: case DST_INTEGER:
if (x.data.integer == y.data.integer) { if (x.as.integer == y.as.integer) {
return 0; return 0;
} else { } else {
return x.data.integer > y.data.integer ? 1 : -1; return x.as.integer > y.as.integer ? 1 : -1;
} }
case DST_STRING: case DST_STRING:
return dst_string_compare(x.data.string, y.data.string); return dst_string_compare(x.as.string, y.as.string);
/* Lower indices are most significant */ /* Lower indices are most significant */
case DST_TUPLE: case DST_TUPLE:
{ {
uint32_t i; uint32_t i;
uint32_t xlen = dst_tuple_length(x.data.tuple); uint32_t xlen = dst_tuple_length(x.as.tuple);
uint32_t ylen = dst_tuple_length(y.data.tuple); uint32_t ylen = dst_tuple_length(y.as.tuple);
uint32_t count = xlen < ylen ? xlen : ylen; uint32_t count = xlen < ylen ? xlen : ylen;
for (i = 0; i < count; ++i) { for (i = 0; i < count; ++i) {
int comp = dst_value_compare(x.data.tuple[i], y.data.tuple[i]); int comp = dst_value_compare(x.as.tuple[i], y.as.tuple[i]);
if (comp != 0) return comp; if (comp != 0) return comp;
} }
if (xlen < ylen) if (xlen < ylen)
@ -135,10 +170,10 @@ int dst_value_compare(DstValue x, DstValue y) {
} }
break; break;
default: default:
if (x.data.string == y.data.string) { if (x.as.string == y.as.string) {
return 0; return 0;
} else { } else {
return x.data.string > y.data.string ? 1 : -1; return x.as.string > y.as.string ? 1 : -1;
} }
} }
} else if (x.type < y.type) { } else if (x.type < y.type) {
@ -146,78 +181,3 @@ int dst_value_compare(DstValue x, DstValue y) {
} }
return 1; return 1;
} }
int dst_truthy(Dst *vm, uint32_t x) {
return dst_value_truthy(dst_arg(vm, x));
}
uint32_t dst_hash(Dst *vm, uint32_t x) {
return dst_value_hash(dst_arg(vm, x));
}
int dst_compare(Dst *vm, uint32_t x, uint32_t y) {
return dst_value_compare(dst_arg(vm, x), dst_arg(vm, y));
}
int dst_equals(Dst *vm, uint32_t x, uint32_t y) {
return dst_value_equals(dst_arg(vm, x), dst_arg(vm, y));
}
/* Get the length of an object. Returns errors for invalid types */
uint32_t dst_length(Dst *vm, uint32_t n) {
DstValue x = dst_arg(vm, n);
uint32_t length;
switch (x.type) {
default:
vm->ret = dst_string_cv(vm, "cannot get length");
vm->flags = 1;
return 0;
case DST_STRING:
length = dst_string_length(x.data.string);
break;
case DST_ARRAY:
length = x.data.array->count;
break;
case DST_BYTEBUFFER:
length = x.data.buffer->count;
break;
case DST_TUPLE:
length = dst_tuple_length(x.data.tuple);
break;
case DST_STRUCT:
length = dst_struct_length(x.data.st);
break;
case DST_TABLE:
length = x.data.table->count;
break;
}
return length;
}
/* Get the capacity of an object. Returns errors for invalid types */
uint32_t dst_capacity(Dst *vm, uint32_t n) {
DstValue x = dst_arg(vm, n);
uint32_t cap;
switch (x.type) {
default:
vm->ret = dst_string_cv(vm, "cannot get capacity");
vm->flags = 1;
return 0;
case DST_STRING:
cap = dst_string_length(x.data.string);
break;
case DST_ARRAY:
cap = x.data.array->capacity;
break;
case DST_BYTEBUFFER:
cap = x.data.buffer->capacity;
break;
case DST_TUPLE:
cap = dst_tuple_length(x.data.tuple);
break;
case DST_STRUCT:
cap = dst_struct_length(x.data.st);
break;
case DST_TABLE:
cap = x.data.table->capacity;
break;
}
return cap;
}

View File

@ -21,6 +21,7 @@
*/ */
#include "internal.h" #include "internal.h"
#include "wrap.h"
static const char DST_NO_UPVALUE[] = "no upvalue"; static const char DST_NO_UPVALUE[] = "no upvalue";
static const char DST_EXPECTED_FUNCTION[] = "expected function"; static const char DST_EXPECTED_FUNCTION[] = "expected function";
@ -54,14 +55,14 @@ int dst_continue(Dst *vm) {
case DST_OP_FLS: /* Load False */ case DST_OP_FLS: /* Load False */
temp.type = DST_BOOLEAN; temp.type = DST_BOOLEAN;
temp.data.boolean = 0; temp.as.boolean = 0;
stack[pc[1]] = temp; stack[pc[1]] = temp;
pc += 2; pc += 2;
continue; continue;
case DST_OP_TRU: /* Load True */ case DST_OP_TRU: /* Load True */
temp.type = DST_BOOLEAN; temp.type = DST_BOOLEAN;
temp.data.boolean = 1; temp.as.boolean = 1;
stack[pc[1]] = temp; stack[pc[1]] = temp;
pc += 2; pc += 2;
continue; continue;
@ -74,7 +75,7 @@ int dst_continue(Dst *vm) {
case DST_OP_I16: /* Load Small Integer */ case DST_OP_I16: /* Load Small Integer */
temp.type = DST_INTEGER; temp.type = DST_INTEGER;
temp.data.integer = ((int16_t *)(pc))[2]; temp.as.integer = ((int16_t *)(pc))[2];
stack[pc[1]] = temp; stack[pc[1]] = temp;
pc += 3; pc += 3;
continue; continue;
@ -88,7 +89,7 @@ int dst_continue(Dst *vm) {
uint16_t level = pc[2]; uint16_t level = pc[2];
temp = dst_frame_callee(stack); temp = dst_frame_callee(stack);
dst_assert(vm, temp.type == DST_FUNCTION, DST_EXPECTED_FUNCTION); dst_assert(vm, temp.type == DST_FUNCTION, DST_EXPECTED_FUNCTION);
fn = temp.data.function; fn = temp.as.function;
if (level == 0) if (level == 0)
upv = stack + pc[3]; upv = stack + pc[3];
else { else {
@ -125,29 +126,29 @@ int dst_continue(Dst *vm) {
case DST_OP_CST: /* Load constant value */ case DST_OP_CST: /* Load constant value */
v1 = dst_frame_callee(stack); v1 = dst_frame_callee(stack);
dst_assert(vm, v1.type == DST_FUNCTION, DST_EXPECTED_FUNCTION); dst_assert(vm, v1.type == DST_FUNCTION, DST_EXPECTED_FUNCTION);
if (pc[2] > v1.data.function->def->literalsLen) if (pc[2] > v1.as.function->def->literalsLen)
dst_error(vm, DST_NO_UPVALUE); dst_error(vm, DST_NO_UPVALUE);
stack[pc[1]] = v1.data.function->def->literals[pc[2]]; stack[pc[1]] = v1.as.function->def->literals[pc[2]];
pc += 3; pc += 3;
continue; continue;
case DST_OP_I32: /* Load 32 bit integer */ case DST_OP_I32: /* Load 32 bit integer */
temp.type = DST_INTEGER; temp.type = DST_INTEGER;
temp.data.integer = *((int32_t *)(pc + 2)); temp.as.integer = *((int32_t *)(pc + 2));
stack[pc[1]] = temp; stack[pc[1]] = temp;
pc += 4; pc += 4;
continue; continue;
case DST_OP_I64: /* Load 64 bit integer */ case DST_OP_I64: /* Load 64 bit integer */
temp.type = DST_INTEGER; temp.type = DST_INTEGER;
temp.data.integer = (DstInteger) *((int64_t *)(pc + 2)); temp.as.integer = *((int64_t *)(pc + 2));
stack[pc[1]] = temp; stack[pc[1]] = temp;
pc += 6; pc += 6;
continue; continue;
case DST_OP_F64: /* Load 64 bit float */ case DST_OP_F64: /* Load 64 bit float */
temp.type = DST_REAL; temp.type = DST_REAL;
temp.data.real = (DstReal) *((double *)(pc + 2)); temp.as.real = *((double *)(pc + 2));
stack[pc[1]] = temp; stack[pc[1]] = temp;
pc += 6; pc += 6;
continue; continue;
@ -161,31 +162,32 @@ int dst_continue(Dst *vm) {
{ {
DstFunction *fn; DstFunction *fn;
v1 = dst_frame_callee(stack); v1 = dst_frame_callee(stack);
temp = v1.data.function->def->literals[pc[2]]; temp = v1.as.function->def->literals[pc[2]];
if (temp.type != DST_FUNCDEF) if (temp.type != DST_FUNCDEF)
dst_error(vm, "cannot create closure from non-funcdef"); dst_error(vm, "cannot create closure from non-funcdef");
fn = dst_alloc(vm, sizeof(DstFunction)); fn = dst_mem_resumegc(dst_alloc(vm, sizeof(DstFunction)));
fn->def = temp.data.def; fn->def = temp.as.def;
if (temp.data.def->flags & DST_FUNCDEF_FLAG_NEEDSPARENT) /* Don't always set the parent. We might want to let the gc get it */
fn->parent = v1.data.function; if (temp.as.def->flags & DST_FUNCDEF_FLAG_NEEDSPARENT)
fn->parent = v1.as.function;
else else
fn->parent = NULL; fn->parent = NULL;
if (v1.type != DST_FUNCTION) if (v1.type != DST_FUNCTION)
dst_error(vm, DST_EXPECTED_FUNCTION); dst_error(vm, DST_EXPECTED_FUNCTION);
if (dst_frame_env(stack) == NULL && (fn->def->flags & DST_FUNCDEF_FLAG_NEEDSENV)) { if (dst_frame_env(stack) == NULL && (fn->def->flags & DST_FUNCDEF_FLAG_NEEDSENV)) {
dst_frame_env(stack) = dst_alloc(vm, sizeof(DstFuncEnv)); dst_frame_env(stack) = dst_mem_resumegc(dst_alloc(vm, sizeof(DstFuncEnv)));
dst_frame_env(stack)->thread = vm->thread; dst_frame_env(stack)->thread = vm->thread;
dst_frame_env(stack)->stackOffset = vm->thread->count; dst_frame_env(stack)->stackOffset = vm->thread->count;
dst_frame_env(stack)->values = NULL; dst_frame_env(stack)->values = NULL;
} }
if (pc[2] > v1.data.function->def->literalsLen) if (pc[2] > v1.as.function->def->literalsLen)
dst_error(vm, DST_NO_UPVALUE); dst_error(vm, DST_NO_UPVALUE);
if (fn->def->flags & DST_FUNCDEF_FLAG_NEEDSENV) if (fn->def->flags & DST_FUNCDEF_FLAG_NEEDSENV)
fn->env = dst_frame_env(stack); fn->env = dst_frame_env(stack);
else else
fn->env = NULL; fn->env = NULL;
temp.type = DST_FUNCTION; temp.type = DST_FUNCTION;
temp.data.function = fn; temp.as.function = fn;
stack[pc[1]] = temp; stack[pc[1]] = temp;
pc += 3; pc += 3;
} }
@ -224,11 +226,11 @@ int dst_continue(Dst *vm) {
const DstValue *data; const DstValue *data;
temp = stack[pc[1]]; temp = stack[pc[1]];
if (temp.type == DST_TUPLE) { if (temp.type == DST_TUPLE) {
count = dst_tuple_length(temp.data.tuple); count = dst_tuple_length(temp.as.tuple);
data = temp.data.tuple; data = temp.as.tuple;
} else if (temp.type == DST_ARRAY){ } else if (temp.type == DST_ARRAY){
count = temp.data.array->count; count = temp.as.array->count;
data = temp.data.array->data; data = temp.as.array->data;
} else { } else {
dst_error(vm, "expected array or tuple"); dst_error(vm, "expected array or tuple");
} }
@ -271,7 +273,7 @@ int dst_continue(Dst *vm) {
DstFuncEnv *env = dst_frame_env(stack); DstFuncEnv *env = dst_frame_env(stack);
env->thread = NULL; env->thread = NULL;
env->stackOffset = size; env->stackOffset = size;
env->values = dst_alloc(vm, sizeof(DstValue) * size); env->values = dst_mem_resumegc(dst_alloc(vm, sizeof(DstValue) * size));
dst_memcpy(env->values, stack, sizeof(DstValue) * size); dst_memcpy(env->values, stack, sizeof(DstValue) * size);
} }
if (newStackIndex) if (newStackIndex)
@ -290,11 +292,11 @@ int dst_continue(Dst *vm) {
stack = vm->thread->data + vm->thread->count; stack = vm->thread->data + vm->thread->count;
temp = dst_frame_callee(stack); temp = dst_frame_callee(stack);
if (temp.type == DST_FUNCTION) { if (temp.type == DST_FUNCTION) {
pc = temp.data.function->def->byteCode; pc = temp.as.function->def->byteCode;
} else if (temp.type == DST_CFUNCTION) { } else if (temp.type == DST_CFUNCTION) {
int status; int status;
vm->ret.type = DST_NIL; vm->ret.type = DST_NIL;
status = temp.data.cfunction(vm); status = temp.as.cfunction(vm);
if (status) { if (status) {
goto vm_error; goto vm_error;
} else { } else {
@ -315,7 +317,7 @@ int dst_continue(Dst *vm) {
for (i = 0; i < arrayLen; ++i) for (i = 0; i < arrayLen; ++i)
array->data[i] = stack[pc[3 + i]]; array->data[i] = stack[pc[3 + i]];
temp.type = DST_ARRAY; temp.type = DST_ARRAY;
temp.data.array = array; temp.as.array = array;
stack[pc[1]] = temp; stack[pc[1]] = temp;
pc += 3 + arrayLen; pc += 3 + arrayLen;
} }
@ -326,6 +328,8 @@ int dst_continue(Dst *vm) {
uint32_t i = 3; uint32_t i = 3;
uint32_t kvs = pc[2]; uint32_t kvs = pc[2];
DstTable *t = dst_make_table(vm, 2 * kvs); DstTable *t = dst_make_table(vm, 2 * kvs);
dst_mem_suspendgc(t);
dst_mem_suspendgc(t->data);
kvs = kvs + 3; kvs = kvs + 3;
while (i < kvs) { while (i < kvs) {
v1 = stack[pc[i++]]; v1 = stack[pc[i++]];
@ -333,8 +337,10 @@ int dst_continue(Dst *vm) {
dst_table_put(vm, t, v1, v2); dst_table_put(vm, t, v1, v2);
} }
temp.type = DST_TABLE; temp.type = DST_TABLE;
temp.data.table = t; temp.as.table = t;
stack[pc[1]] = temp; stack[pc[1]] = temp;
dst_mem_resumegc(t);
dst_mem_resumegc(t->data);
pc += kvs; pc += kvs;
} }
break; break;
@ -347,7 +353,7 @@ int dst_continue(Dst *vm) {
for (i = 0; i < len; ++i) for (i = 0; i < len; ++i)
tuple[i] = stack[pc[3 + i]]; tuple[i] = stack[pc[3 + i]];
temp.type = DST_TUPLE; temp.type = DST_TUPLE;
temp.data.tuple = dst_tuple_end(vm, tuple); temp.as.tuple = dst_tuple_end(vm, tuple);
stack[pc[1]] = temp; stack[pc[1]] = temp;
pc += 3 + len; pc += 3 + len;
} }
@ -360,10 +366,10 @@ int dst_continue(Dst *vm) {
dst_error(vm, "expected thread"); dst_error(vm, "expected thread");
if (temp.type == DST_NIL && vm->thread->parent) { if (temp.type == DST_NIL && vm->thread->parent) {
temp.type = DST_THREAD; temp.type = DST_THREAD;
temp.data.thread = vm->thread->parent; temp.as.thread = vm->thread->parent;
} }
if (temp.type == DST_THREAD) { if (temp.type == DST_THREAD) {
if (temp.data.thread->status != DST_THREAD_PENDING) if (temp.as.thread->status != DST_THREAD_PENDING)
dst_error(vm, "can only enter pending thread"); dst_error(vm, "can only enter pending thread");
} }
dst_frame_ret(stack) = pc[1]; dst_frame_ret(stack) = pc[1];
@ -373,9 +379,9 @@ int dst_continue(Dst *vm) {
vm->ret = v1; vm->ret = v1;
return 0; return 0;
} }
temp.data.thread->status = DST_THREAD_ALIVE; temp.as.thread->status = DST_THREAD_ALIVE;
vm->thread = temp.data.thread; vm->thread = temp.as.thread;
stack = dst_thread_stack(temp.data.thread); stack = dst_thread_stack(temp.as.thread);
if (dst_frame_callee(stack).type != DST_FUNCTION) if (dst_frame_callee(stack).type != DST_FUNCTION)
goto vm_return; goto vm_return;
stack[dst_frame_ret(stack)] = v1; stack[dst_frame_ret(stack)] = v1;
@ -455,7 +461,7 @@ int dst_run(Dst *vm, DstValue callee) {
} }
if (callee.type == DST_CFUNCTION) { if (callee.type == DST_CFUNCTION) {
vm->ret.type = DST_NIL; vm->ret.type = DST_NIL;
result = callee.data.cfunction(vm); result = callee.as.cfunction(vm);
} else if (callee.type == DST_FUNCTION) { } else if (callee.type == DST_FUNCTION) {
result = dst_continue(vm); result = dst_continue(vm);
} else { } else {
@ -475,7 +481,10 @@ int dst_run(Dst *vm, DstValue callee) {
/* Setup functions */ /* Setup functions */
Dst *dst_init() { Dst *dst_init() {
Dst *vm = dst_raw_alloc(sizeof(Dst)); Dst *vm = malloc(sizeof(Dst));
if (NULL == vm) {
DST_OUT_OF_MEMORY;
}
vm->ret.type = DST_NIL; vm->ret.type = DST_NIL;
/* Garbage collection */ /* Garbage collection */
vm->blocks = NULL; vm->blocks = NULL;
@ -485,9 +494,8 @@ Dst *dst_init() {
* horrible for performance, but helps ensure * horrible for performance, but helps ensure
* there are no memory bugs during dev */ * there are no memory bugs during dev */
vm->memoryInterval = 0; vm->memoryInterval = 0;
vm->black = 0;
/* Set up the cache */ /* Set up the cache */
vm->cache = dst_raw_calloc(1, 128 * sizeof(DstValue)); vm->cache = calloc(1, 128 * sizeof(DstValue));
vm->cache_capacity = vm->cache == NULL ? 0 : 128; vm->cache_capacity = vm->cache == NULL ? 0 : 128;
vm->cache_count = 0; vm->cache_count = 0;
vm->cache_deleted = 0; vm->cache_deleted = 0;
@ -509,11 +517,11 @@ void dst_deinit(Dst *vm) {
vm->registry = NULL; vm->registry = NULL;
vm->ret.type = DST_NIL; vm->ret.type = DST_NIL;
/* Deinit the cache */ /* Deinit the cache */
dst_raw_free(vm->cache); free(vm->cache);
vm->cache = NULL; vm->cache = NULL;
vm->cache_count = 0; vm->cache_count = 0;
vm->cache_capacity = 0; vm->cache_capacity = 0;
vm->cache_deleted = 0; vm->cache_deleted = 0;
/* Free the vm */ /* Free the vm */
dst_raw_free(vm); free(vm);
} }

View File

@ -20,37 +20,34 @@
* IN THE SOFTWARE. * IN THE SOFTWARE.
*/ */
#include <dst/dst.h> #include "wrap.h"
#include "internal.h"
/* Wrapper functions wrap a data type that is used from C into a /* Wrapper functions wrap a data type that is used from C into a
* gst value, which can then be used in gst. */ * dst value, which can then be used in dst internal functions. Use
* these functions sparingly, as these function will let the programmer
* leak memory, where as the stack based API ensures that all values can
* be collected by the garbage collector. */
DstValue dst_wrap_nil() { DstValue dst_wrap_nil() {
GstValue y; DstValue y;
y.type = GST_NIL; y.type = DST_NIL;
return y; return y;
} }
int dst_check_nil(Gst *vm, uint32_t i) {
DstValue a = dst_arg(vm, i);
return a.type == DST_NIL;
}
#define DST_WRAP_DEFINE(NAME, TYPE, DTYPE, UM)\ #define DST_WRAP_DEFINE(NAME, TYPE, DTYPE, UM)\
DstValue dst_wrap_##NAME(TYPE x) {\ DstValue dst_wrap_##NAME(TYPE x) {\
DstValue y;\ DstValue y;\
y.type = DTYPE;\ y.type = DTYPE;\
y.data.UM = x;\ y.as.UM = x;\
return y;\ return y;\
}\ }\
\ \
int dst_check_##NAME(Dst *vm, uint32_t i) {\ TYPE dst_unwrap_##NAME(Dst *vm, int64_t i) {\
return dst_arg(vm, i).type == DTYPE;\ return dst_getv(vm, i).as.UM;\
}\ }\
DST_WRAP_DEFINE(real, DstReal, DST_REAL, real) DST_WRAP_DEFINE(real, double, DST_REAL, real)
DST_WRAP_DEFINE(integer, DstInteger, DST_INTEGER, integer) DST_WRAP_DEFINE(integer, int64_t, DST_INTEGER, integer)
DST_WRAP_DEFINE(boolean, int, DST_BOOLEAN, boolean) DST_WRAP_DEFINE(boolean, int, DST_BOOLEAN, boolean)
DST_WRAP_DEFINE(string, const uint8_t *, DST_STRING, string) DST_WRAP_DEFINE(string, const uint8_t *, DST_STRING, string)
DST_WRAP_DEFINE(symbol, const uint8_t *, DST_SYMBOL, string) DST_WRAP_DEFINE(symbol, const uint8_t *, DST_SYMBOL, string)
@ -58,27 +55,10 @@ DST_WRAP_DEFINE(array, DstArray *, DST_ARRAY, array)
DST_WRAP_DEFINE(tuple, const DstValue *, DST_TUPLE, tuple) DST_WRAP_DEFINE(tuple, const DstValue *, DST_TUPLE, tuple)
DST_WRAP_DEFINE(struct, const DstValue *, DST_STRUCT, st) DST_WRAP_DEFINE(struct, const DstValue *, DST_STRUCT, st)
DST_WRAP_DEFINE(thread, DstThread *, DST_THREAD, thread) DST_WRAP_DEFINE(thread, DstThread *, DST_THREAD, thread)
DST_WRAP_DEFINE(buffer, DstBuffer *, DST_BYTEBUFFER, buffer) DST_WRAP_DEFINE(buffer, DstBuffer *, DST_BUFFER, buffer)
DST_WRAP_DEFINE(function, DstFunction *, DST_FUNCTION, function) DST_WRAP_DEFINE(function, DstFunction *, DST_FUNCTION, function)
DST_WRAP_DEFINE(cfunction, DstCFunction, DST_CFUNCTION, cfunction) DST_WRAP_DEFINE(cfunction, DstCFunction, DST_CFUNCTION, cfunction)
DST_WRAP_DEFINE(table, DstTable *, DST_TABLE, table) DST_WRAP_DEFINE(table, DstTable *, DST_TABLE, table)
DST_WRAP_DEFINE(funcenv, DstFuncEnv *, DST_FUNCENV, env) DST_WRAP_DEFINE(userdata, void *, DST_USERDATA, pointer)
DST_WRAP_DEFINE(funcdef, DstFuncDef *, DST_FUNCDEF, def)
#undef DST_WRAP_DEFINE #undef DST_WRAP_DEFINE
DstValue dst_wrap_userdata(void *x) {
DstValue ret;
ret.type = DST_USERDATA;
ret.data.pointer = x;
return ret;
}
void *dst_check_userdata(Dst *vm, uint32_t i, const DstUserType *type) {
DstValue x = dst_arg(vm, i);
DstUserdataHeader *h;
if (x.type != DST_USERDATA) return NULL;
h = dst_udata_header(x.data.pointer);
if (h->type != type) return NULL;
return x.data.pointer;
}

61
core/wrap.h Normal file
View File

@ -0,0 +1,61 @@
/*
* 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

68
libs/sample.dsts Normal file
View File

@ -0,0 +1,68 @@
# A .dsts file will contain VM, assembly for a dst function. This includes
# associated constants and what not. The assembler should be able to
# 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"
varargs false
# Name for reference by nested funcdefs
name outerfunc
# Contains the bytecode for this function. This can be assembly
# instructions or integers. Assembly will be converted to integer bytecodes immediately.
bytecode [
(typecheck 0 integer)
(typecheck 1 integer)
(typecheck 2 integer)
:checked-entry
(load-constant 3 bork)
(load-constant 4 bip)
(add-integer-unchecked 5 0 1)
(add-integer-unchecked 5 5 2)
(add-integer-unchecked 5 5 3)
(add-integer-unchecked 5 5 4)
(return 5)
]
# A source map is optional. The sourcemap corresponds with the byte code.
# Each number is a 64 bit integer, the concatenation of two 32 bit integers.
# These integers represent source line, source column for each instruction.
# This format may change.
source-map [
0x0000123400000123
0x0000123400000123
0x0000123400000125
0x0000123400000134
0x0000123400000134
0x0000123400000135
...
]
# Slots can be named as well for convenience.
slots [
x
y
z
]
# Captured outer environments that are referenced
environments [
outer1
outer2
]
# Constants are an array or tuple. For named constants, use a tuple that begins with let
# For a literal tuple, use (quote tuple), or 'tuple. Without names, constants must be indexed
# from their number
constants [
"hello"
(let bork 123)
(let bip 456)
'(1 2 3)
(let atuple (1 2 3))
(567)
# Functions can be recursively defined.
(funcdef funcname {
...
})
# Literal FuncEnvs and Functions may be possible later
]
}