From f273aa8b1ba9ffdb2dace9b41600f6323a3b1def Mon Sep 17 00:00:00 2001 From: bakpakin Date: Sat, 30 Dec 2017 16:46:59 -0500 Subject: [PATCH] Add vars, split up headers, remove fiber->ret, add comparators, etc. --- .gitignore | 2 +- 2 | 91 ---- Makefile | 14 +- client/main.c | 359 ++++++-------- core/asm.c | 103 ++-- core/compile.c | 510 +++++++++++++------ core/compile.h | 23 +- core/fiber.c | 3 +- core/gc.c | 18 +- core/math.c | 268 ++++++++++ core/opcodes.h | 2 - core/parse.c | 113 ++--- core/sourcemap.c | 77 +++ core/sourcemap.h | 44 ++ core/stl.c | 1021 +++++++------------------------------- core/string.c | 41 +- core/strtod.c | 177 +++++-- core/syscalls.c | 146 ------ core/vm.c | 107 ++-- dsttest/basic.dst | 7 +- include/dst/dst.h | 575 +-------------------- include/dst/dstconfig.h | 121 +++++ include/dst/dststate.h | 55 ++ include/dst/dststl.h | 56 +++ include/dst/dsttypes.h | 455 +++++++++++++++++ unittests/compile_test.c | 17 +- 26 files changed, 2144 insertions(+), 2261 deletions(-) delete mode 100644 2 create mode 100644 core/math.c create mode 100644 core/sourcemap.c create mode 100644 core/sourcemap.h delete mode 100644 core/syscalls.c create mode 100644 include/dst/dstconfig.h create mode 100644 include/dst/dststate.h create mode 100644 include/dst/dststl.h create mode 100644 include/dst/dsttypes.h diff --git a/.gitignore b/.gitignore index bc526576..ceb1a868 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ # Target /client/dst -./dst +dst # Generated files *.gen.h diff --git a/2 b/2 deleted file mode 100644 index 7618d93f..00000000 --- a/2 +++ /dev/null @@ -1,91 +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 -#include "compile.h" - -void dst_compile_slotpool_init(DstSlotPool *pool) { - pool->s = NULL; - pool->count = 0; - pool->max; - pool->cap = 0; -} - -void dst_compile_slotpool_deinit(DstSlotPool *pool) { - free(pool->s); - pool->s = NULL; - pool->cap = 0; - pool->max = 0; - pool->count = 0; -} - -void dst_compile_slotpool_extend(DstSlotPool *pool, int32_t extra) { - int32_t i; - int32_t newcap = pool->cap + extra; - if (newcap > pool->cap) { - newcap *= 2; - pool->s = realloc(pool->s, newcap * sizeof(DstSlot)); - if (NULL == pool->s) { - DST_OUT_OF_MEMORY; - } - } - pool->cap = newcap; - /* Mark all new slots as free */ - for (i = pool->count; i < newcap; i++) { - pool->s[i].flags = 0; - } -} - -DstSlot *dst_compile_slotpool_alloc(DstSlotPool *pool) { - int32_t oldcount = pool->count; - int32_t newcount = oldcount == 0xF0 ? 0x101 : oldcount + 1; - int32_t index = newcount - 1; - while (pool->count < pool->cap) { - if (!(pool->s[pool->count].flags & DST_SLOT_NOTEMPTY)) { - return pool->s + pool->count; - } - pool->free++; - } - dst_compile_slotpool_extend(pool, newcount - oldcount); - pool->s[index].flags = DST_SLOT_NOTEMPTY; - pool->s[index].index = index; - return pool->s + index; -} - -void dst_compile_slotpool_freeindex(DstSlotPool *pool, int32_t index) { - if (index > 0 && index < pool->count) { - pool->s[index].flags = 0; - if (index < pool->free) - pool->free = index; - } -} - -void dst_compile_slotpool_free(DstSlotPool *pool, DstSlot *s) { - DstSlot *oldfree = pool->s + pool->free; - if (s >= pool->s && s < (pool->s + pool->count)) { - if (s < oldfree) { - pool->free = s - pool->s; - } - s->flags = 0; - } -} - diff --git a/Makefile b/Makefile index df92f51f..2cb6d364 100644 --- a/Makefile +++ b/Makefile @@ -26,13 +26,13 @@ PREFIX?=/usr/local BINDIR=$(PREFIX)/bin VERSION=\"0.0.0-beta\" -CFLAGS=-std=c99 -Wall -Wextra -Wfatal-errors -I./include -I./libs -g -DDST_VERSION=$(VERSION) +CFLAGS=-std=c99 -Wall -Wextra -I./include -I./libs -g -DDST_VERSION=$(VERSION) PREFIX=/usr/local DST_TARGET=dst DST_XXD=xxd DEBUGGER=lldb -DST_INTERNAL_HEADERS=$(addprefix core/,symcache.h opcodes.h strtod.h compile.h gc.h) -DST_HEADERS=$(addprefix include/dst/,dst.h) +DST_INTERNAL_HEADERS=$(addprefix core/,symcache.h opcodes.h strtod.h compile.h gc.h sourcemap.h) +DST_HEADERS=$(addprefix include/dst/,dst.h dstconfig.h dsttypes.h dststate.h dststl.h) ############################# ##### Generated headers ##### @@ -60,13 +60,13 @@ $(DST_XXD): libs/xxd.c DST_CORE_SOURCES=$(addprefix core/,\ array.c asm.c buffer.c compile.c\ - fiber.c func.c gc.c parse.c string.c strtod.c\ - struct.c symcache.c syscalls.c table.c tuple.c userdata.c util.c\ + fiber.c func.c gc.c math.c parse.c sourcemap.c string.c stl.c strtod.c\ + struct.c symcache.c table.c tuple.c userdata.c util.c\ value.c vm.c wrap.c) DST_CORE_OBJECTS=$(patsubst %.c,%.o,$(DST_CORE_SOURCES)) -$(DST_TARGET): $(DST_CORE_OBJECTS) - $(CC) $(CFLAGS) -o $(DST_TARGET) $(DST_CORE_OBJECTS) +$(DST_TARGET): client/main.o $(DST_CORE_OBJECTS) + $(CC) $(CFLAGS) -o $(DST_TARGET) $^ # Compile all .c to .o %.o: %.c $(DST_ALL_HEADERS) diff --git a/client/main.c b/client/main.c index 40cfbd36..ed4511b3 100644 --- a/client/main.c +++ b/client/main.c @@ -24,196 +24,159 @@ #include #include -static int client_strequal(const char *a, const char *b) { - while (*a) - if (*a++ != *b++) return 0; - return *a == *b; -} - -static int client_strequal_witharg(const char *a, const char *b) { - while (*b) - if (*a++ != *b++) return 0; - return *a == '='; -} - #define DST_CLIENT_HELP 1 #define DST_CLIENT_VERBOSE 2 #define DST_CLIENT_VERSION 4 #define DST_CLIENT_REPL 8 -#define DST_CLIENT_NOCOLOR 16 -#define DST_CLIENT_UNKNOWN 32 +#define DST_CLIENT_UNKNOWN 16 -static void printf_flags(int64_t flags, const char *col, const char *fmt, const char *arg) { - if (!(flags & DST_CLIENT_NOCOLOR)) - printf("\x1B[%sm", col); - printf(fmt, arg); - if (!(flags & DST_CLIENT_NOCOLOR)) - printf("\x1B[0m"); +static DstValue env; + +static int client_strequal(const char *a, const char *b) { + while (*a) if (*a++ != *b++) return 0; + return *a == *b; } -/* Simple read line functionality */ -static char *dst_getline() { - char *line = malloc(100); - char *linep = line; - size_t lenmax = 100; - size_t len = lenmax; - int c; - if (line == NULL) - return NULL; - for (;;) { - c = fgetc(stdin); - if (c == EOF) - break; - if (--len == 0) { - len = lenmax; - char *linen = realloc(linep, lenmax *= 2); - if (linen == NULL) { - free(linep); - return NULL; - } - line = linen + (line - linep); - linep = linen; - } - if ((*line++ = c) == '\n') - break; - } - *line = '\0'; - return linep; +static int client_strequal_witharg(const char *a, const char *b) { + while (*b) if (*a++ != *b++) return 0; + return *a == '='; } -/* Compile and run an ast */ -static int debug_compile_and_run(Dst *vm, DstValue ast, int64_t flags) { - DstValue func = dst_compile(vm, vm->env, ast); - /* Check for compilation errors */ - if (func.type != DST_FUNCTION) { - printf_flags(flags, "31", "compiler error: %s\n", (const char *)dst_to_string(vm, func)); - return 1; - } - /* Execute function */ - if (dst_run(vm, func)) { - printf_flags(flags, "31", "vm error: %s\n", (const char *)dst_to_string(vm, vm->ret)); - return 1; - } - return 0; -} - -/* Parse a file and execute it */ -static int debug_run(Dst *vm, FILE *in, int64_t flags) { +/* Load source from a file */ +static const uint8_t *loadsource(const char *fpath, int32_t *len) { + FILE *f = fopen(fpath, "rb"); + long fsize; + size_t fsizet; uint8_t *source = NULL; - uint32_t sourceSize = 0; - long bufsize; + if (fseek(f, 0, SEEK_END)) goto error; + fsize = ftell(f); + if (fsize > INT32_MAX || fsize < 0) goto error; + fsizet = fsize; + if (fseek(f, 0, SEEK_SET)) goto error; + if (!fsize) goto error; + source = malloc(fsize); + if (fread(source, 1, fsize, f) != fsizet) goto error; + if (fclose(f)) goto error; + *len = (int32_t) fsizet; + return source; - /* Read file into memory */ - if (!fseek(in, 0L, SEEK_END) == 0) goto file_error; - bufsize = ftell(in); - if (bufsize == -1) goto file_error; - sourceSize = (uint32_t) bufsize; - source = malloc(bufsize); - if (!source) goto file_error; - if (fseek(in, 0L, SEEK_SET) != 0) goto file_error; - fread(source, sizeof(char), bufsize, in); - if (ferror(in) != 0) goto file_error; - - while (source) { - source = dst_parseb(vm, 0, source, sourceSize); - } - - /* Finish up */ - fclose(in); - return 0; - - /* Handle errors */ - file_error: - if (source) { - free(source); - } - printf_flags(flags, "31", "parse error: could not read file%s\n", ""); - fclose(in); - return 1; - - char buffer[2048] = {0}; - const char *reader = buffer; - for (;;) { - int status = dst_parsec(vm, ) - while (p.status != DST_PARSER_ERROR && p.status != DST_PARSER_FULL) { - if (*reader == '\0') { - if (!fgets(buffer, sizeof(buffer), in)) { - /* Check that parser is complete */ - if (p.status != DST_PARSER_FULL && p.status != DST_PARSER_ROOT) { - printf_flags(flags, "31", "parse error: unexpected end of source%s\n", ""); - return 1; - } - /* Otherwise we finished the file with no problems */ - return 0; - } - reader = buffer; - } - reader += dst_parse_cstring(&p, reader); - } - /* Check if file read in correctly */ - if (p.error) { - printf_flags(flags, "31", "parse error: %s\n", p.error); - break; - } - /* Check that parser is complete */ - if (p.status != DST_PARSER_FULL && p.status != DST_PARSER_ROOT) { - printf_flags(flags, "31", "parse error: unexpected end of source%s\n", ""); - break; - } - if (debug_compile_and_run(vm, dst_parse_consume(&p), flags)) { - break; - } - } - return 1; + error: + free(source); + return NULL; } -/* A simple repl */ -static int debug_repl(Dst *vm, uint64_t flags) { - char *buffer, *reader; - DstParser p; - buffer = reader = NULL; +/* simple repl */ +static int repl() { + DstBuffer b; + dst_buffer_init(&b, 256); for (;;) { - /* Init parser */ - dst_parser(&p, vm); - while (p.status != DST_PARSER_ERROR && p.status != DST_PARSER_FULL) { - if (p.status == DST_PARSER_ERROR || p.status == DST_PARSER_FULL) + int c; + DstParseResult res; + DstCompileResult cres; + DstCompileOptions opts; + if (b.count == 0) + printf("> "); + else + printf(">> "); + for (;;) { + c = fgetc(stdin); + if (c == EOF) { + printf("\n"); + goto done; + } + dst_buffer_push_u8(&b, c); + if (c == '\n') break; + } + res = dst_parse(b.data, b.count); + switch (res.status) { + case DST_PARSE_NODATA: + b.count = 0; + break; + case DST_PARSE_UNEXPECTED_EOS: + break; + case DST_PARSE_ERROR: + dst_puts(dst_formatc("syntax error at %d: %S\n", + res.bytes_read + 1, res.error)); + b.count = 0; + break; + case DST_PARSE_OK: + { + opts.source = res.value; + opts.flags = 0; + opts.sourcemap = res.map; + opts.env = env; + cres = dst_compile(opts); + if (cres.status == DST_COMPILE_OK) { + DstFunction *f = dst_compile_func(cres); + DstValue ret; + if (dst_run(dst_wrap_function(f), &ret)) { + dst_puts(dst_formatc("runtime error: %v\n", ret)); + } else { + dst_puts(dst_formatc("%v\n", ret)); + } + } else { + dst_puts(dst_formatc("compile error at %d: %S\n", + cres.error_start + 1, cres.error)); + } + b.count = 0; + } break; - if (!reader || *reader == '\0') { - printf_flags(flags, "33", "> %s", ""); - if (buffer) - free(buffer); - buffer = dst_getline(); - if (!buffer || *buffer == '\0') - return 0; - reader = buffer; - } - reader += dst_parse_cstring(&p, reader); } - /* Check if file read in correctly */ - if (p.error) { - printf_flags(flags, "31", "parse error: %s\n", p.error); - buffer = reader = NULL; - continue; - } - /* Check that parser is complete */ - if (p.status != DST_PARSER_FULL && p.status != DST_PARSER_ROOT) { - printf_flags(flags, "31", "parse error: unexpected end of source%s\n", ""); - continue; - } - dst_env_putc(vm, vm->env, "_", vm->ret); - dst_env_putc(vm, vm->env, "-env-", dst_wrap_table(vm->env)); - if (!debug_compile_and_run(vm, dst_parse_consume(&p), flags)) { - printf_flags(flags, "36", "%s\n", (const char *) dst_description(vm, vm->ret)); + } + done: + dst_buffer_deinit(&b); + return 0; +} + +/* Run file */ +static void runfile(const uint8_t *src, int32_t len) { + DstCompileOptions opts; + DstCompileResult cres; + DstParseResult res; + const uint8_t *s = src; + const uint8_t *end = src + len; + while (s < end) { + res = dst_parse(s, end - s); + switch (res.status) { + case DST_PARSE_NODATA: + return; + case DST_PARSE_UNEXPECTED_EOS: + case DST_PARSE_ERROR: + dst_puts(dst_formatc("syntax error at %d: %S\n", + s - src + res.bytes_read + 1, res.error)); + break; + case DST_PARSE_OK: + { + opts.source = res.value; + opts.flags = 0; + opts.sourcemap = res.map; + opts.env = env; + cres = dst_compile(opts); + if (cres.status == DST_COMPILE_OK) { + DstValue ret = dst_wrap_nil(); + DstFunction *f = dst_compile_func(cres); + if (dst_run(dst_wrap_function(f), &ret)) { + dst_puts(dst_formatc("runtime error: %v\n", ret)); + } else { + dst_puts(dst_formatc("runtime error: %v\n", ret)); + break; + } + } else { + dst_puts(dst_formatc("compile error at %d: %S\n", + s - src + cres.error_start + 1, cres.error)); + } + } + break; } + s += res.bytes_read; } } -int main(int argc, const char **argv) { - Dst vm; +int main(int argc, char **argv) { int status = -1; int i; int fileRead = 0; - uint32_t memoryInterval = 4096; + uint32_t gcinterval = 8192; uint64_t flags = 0; /* Read the arguments. Ignore files. */ @@ -231,22 +194,15 @@ int main(int argc, const char **argv) { flags |= DST_CLIENT_VERBOSE; } else if (client_strequal(arg + 2, "repl")) { flags |= DST_CLIENT_REPL; - } else if (client_strequal(arg + 2, "nocolor")) { - flags |= DST_CLIENT_NOCOLOR; - } else if (client_strequal_witharg(arg + 2, "memchunk")) { - int64_t val = memoryInterval; - const uint8_t *end = (const uint8_t *)(arg + 2); + } else if (client_strequal_witharg(arg + 2, "gcinterval")) { + int status = 0; + int32_t m; + const uint8_t *start = (const uint8_t *)(arg + 13); + const uint8_t *end = start; while (*end) ++end; - int status = dst_read_integer((const uint8_t *)arg + 11, end, &val); - if (status) { - if (val > 0xFFFFFFFF) { - memoryInterval = 0xFFFFFFFF; - } else if (val < 0) { - memoryInterval = 0; - } else { - memoryInterval = val; - } - } + m = dst_scan_integer(start, end - start, &status); + if (!status) + gcinterval = m; } else { flags |= DST_CLIENT_UNKNOWN; } @@ -267,9 +223,6 @@ int main(int argc, const char **argv) { case 'r': flags |= DST_CLIENT_REPL; break; - case 'c': - flags |= DST_CLIENT_NOCOLOR; - break; default: flags |= DST_CLIENT_UNKNOWN; break; @@ -284,14 +237,13 @@ int main(int argc, const char **argv) { printf( "Usage:\n" "%s -opts --fullopt1 --fullopt2 file1 file2...\n" "\n" - " -h --help : Shows this information.\n" - " -V --verbose : Show more output.\n" - " -r --repl : Launch a repl after all files are processed.\n" - " -c --nocolor : Don't use VT100 color codes in the repl.\n" - " -v --version : Print the version number and exit.\n" - " --memchunk=[int] : Set the amount of memory to allocate before\n" - " forcing a collection in bytes. Max is 2^32-1,\n" - " min is 0.\n\n", + " -h --help Shows this information.\n" + " -V --verbose Show more output.\n" + " -r --repl Launch a repl after all files are processed.\n" + " -v --version Print the version number and exit.\n" + " --gcinterval=[int] Set the amount of memory to allocate before\n" + " forcing a collection in bytes. Max is 2^31-1,\n" + " min is 0.\n\n", argv[0]); return 0; } @@ -301,26 +253,31 @@ int main(int argc, const char **argv) { } /* Set up VM */ - dst_init(&vm); - vm.memoryInterval = memoryInterval; - dst_stl_load(&vm); + dst_init(); + dst_vm_gc_interval = gcinterval; + env = dst_loadstl(DST_LOAD_ROOT); /* Read the arguments. Only process files. */ for (i = 1; i < argc; ++i) { const char *arg = argv[i]; if (*arg != '-') { - FILE *f; - f = fopen(arg, "rb"); fileRead = 1; - status = debug_run(&vm, f, flags); + int32_t len; + const uint8_t *s = loadsource(arg, &len); + if (NULL == s) { + printf("could not load file %s\n", arg); + } else { + runfile(s, len); + } } } + /* Run a repl if nothing else happened, or the flag is set */ if (!fileRead || (flags & DST_CLIENT_REPL)) { - status = debug_repl(&vm, flags); + status = repl(); } - dst_deinit(&vm); + dst_deinit(); return status; } diff --git a/core/asm.c b/core/asm.c index 209bf1cc..6f86b870 100644 --- a/core/asm.c +++ b/core/asm.c @@ -25,6 +25,7 @@ #include #include "opcodes.h" #include "gc.h" +#include "sourcemap.h" /* Bytecode op argument types */ @@ -139,7 +140,6 @@ static const DstInstructionDef dst_ops[] = { {"load-integer", DIT_SI, DOP_LOAD_INTEGER}, {"load-nil", DIT_S, DOP_LOAD_NIL}, {"load-self", DIT_S, DOP_LOAD_SELF}, - {"load-syscall", DIT_SU, DOP_LOAD_SYSCALL}, {"load-true", DIT_S, DOP_LOAD_TRUE}, {"load-upvalue", DIT_SES, DOP_LOAD_UPVALUE}, {"move-far", DIT_SS, DOP_MOVE_FAR}, @@ -164,8 +164,7 @@ static const DstInstructionDef dst_ops[] = { {"shift-right-immediate", DIT_SSI, DOP_SHIFT_RIGHT_IMMEDIATE}, {"shift-right-unsigned", DIT_SSS, DOP_SHIFT_RIGHT_UNSIGNED}, {"shift-right-unsigned-immediate", DIT_SSS, DOP_SHIFT_RIGHT_UNSIGNED_IMMEDIATE}, - {"subtract", DIT_SSS, 0x1F}, - {"syscall", DIT_SU, DOP_SYSCALL}, + {"subtract", DIT_SSS, DOP_SUBTRACT}, {"tailcall", DIT_S, DOP_TAILCALL}, {"transfer", DIT_SSS, DOP_TRANSFER}, {"typecheck", DIT_ST, DOP_TYPECHECK}, @@ -376,38 +375,38 @@ static uint32_t read_instruction( { if (dst_tuple_length(argt) != 2) dst_asm_error(a, map, "expected 1 argument: (op, slot)"); - instr |= doarg(a, dst_parse_submap_index(map, 1), DST_OAT_SLOT, 1, 3, 0, argt[1]); + instr |= doarg(a, dst_sourcemap_index(map, 1), DST_OAT_SLOT, 1, 3, 0, argt[1]); break; } case DIT_L: { if (dst_tuple_length(argt) != 2) dst_asm_error(a, map, "expected 1 argument: (op, label)"); - instr |= doarg(a, dst_parse_submap_index(map, 1), DST_OAT_LABEL, 1, 3, 1, argt[1]); + instr |= doarg(a, dst_sourcemap_index(map, 1), DST_OAT_LABEL, 1, 3, 1, argt[1]); break; } case DIT_SS: { if (dst_tuple_length(argt) != 3) dst_asm_error(a, map, "expected 2 arguments: (op, slot, slot)"); - instr |= doarg(a, dst_parse_submap_index(map, 1), DST_OAT_SLOT, 1, 1, 0, argt[1]); - instr |= doarg(a, dst_parse_submap_index(map, 2), DST_OAT_SLOT, 2, 2, 0, argt[2]); + instr |= doarg(a, dst_sourcemap_index(map, 1), DST_OAT_SLOT, 1, 1, 0, argt[1]); + instr |= doarg(a, dst_sourcemap_index(map, 2), DST_OAT_SLOT, 2, 2, 0, argt[2]); break; } case DIT_SL: { if (dst_tuple_length(argt) != 3) dst_asm_error(a, map, "expected 2 arguments: (op, slot, label)"); - instr |= doarg(a, dst_parse_submap_index(map, 1), DST_OAT_SLOT, 1, 1, 0, argt[1]); - instr |= doarg(a, dst_parse_submap_index(map, 2), DST_OAT_LABEL, 2, 2, 1, argt[2]); + instr |= doarg(a, dst_sourcemap_index(map, 1), DST_OAT_SLOT, 1, 1, 0, argt[1]); + instr |= doarg(a, dst_sourcemap_index(map, 2), DST_OAT_LABEL, 2, 2, 1, argt[2]); break; } case DIT_ST: { if (dst_tuple_length(argt) != 3) dst_asm_error(a, map, "expected 2 arguments: (op, slot, type)"); - instr |= doarg(a, dst_parse_submap_index(map, 1), DST_OAT_SLOT, 1, 1, 0, argt[1]); - instr |= doarg(a, dst_parse_submap_index(map, 2), DST_OAT_TYPE, 2, 2, 0, argt[2]); + instr |= doarg(a, dst_sourcemap_index(map, 1), DST_OAT_SLOT, 1, 1, 0, argt[1]); + instr |= doarg(a, dst_sourcemap_index(map, 2), DST_OAT_TYPE, 2, 2, 0, argt[2]); break; } case DIT_SI: @@ -415,17 +414,17 @@ static uint32_t read_instruction( { if (dst_tuple_length(argt) != 3) dst_asm_error(a, map, "expected 2 arguments: (op, slot, integer)"); - instr |= doarg(a, dst_parse_submap_index(map, 1), DST_OAT_SLOT, 1, 1, 0, argt[1]); - instr |= doarg(a, dst_parse_submap_index(map, 2), DST_OAT_INTEGER, 2, 2, idef->type == DIT_SI, argt[2]); + instr |= doarg(a, dst_sourcemap_index(map, 1), DST_OAT_SLOT, 1, 1, 0, argt[1]); + instr |= doarg(a, dst_sourcemap_index(map, 2), DST_OAT_INTEGER, 2, 2, idef->type == DIT_SI, argt[2]); break; } case DIT_SSS: { if (dst_tuple_length(argt) != 4) dst_asm_error(a, map, "expected 3 arguments: (op, slot, slot, slot)"); - instr |= doarg(a, dst_parse_submap_index(map, 1), DST_OAT_SLOT, 1, 1, 0, argt[1]); - instr |= doarg(a, dst_parse_submap_index(map, 2), DST_OAT_SLOT, 2, 1, 0, argt[2]); - instr |= doarg(a, dst_parse_submap_index(map, 3), DST_OAT_SLOT, 3, 1, 0, argt[3]); + instr |= doarg(a, dst_sourcemap_index(map, 1), DST_OAT_SLOT, 1, 1, 0, argt[1]); + instr |= doarg(a, dst_sourcemap_index(map, 2), DST_OAT_SLOT, 2, 1, 0, argt[2]); + instr |= doarg(a, dst_sourcemap_index(map, 3), DST_OAT_SLOT, 3, 1, 0, argt[3]); break; } case DIT_SSI: @@ -433,9 +432,9 @@ static uint32_t read_instruction( { if (dst_tuple_length(argt) != 4) dst_asm_error(a, map, "expected 3 arguments: (op, slot, slot, integer)"); - instr |= doarg(a, dst_parse_submap_index(map, 1), DST_OAT_SLOT, 1, 1, 0, argt[1]); - instr |= doarg(a, dst_parse_submap_index(map, 2), DST_OAT_SLOT, 2, 1, 0, argt[2]); - instr |= doarg(a, dst_parse_submap_index(map, 3), DST_OAT_INTEGER, 3, 1, idef->type == DIT_SSI, argt[3]); + instr |= doarg(a, dst_sourcemap_index(map, 1), DST_OAT_SLOT, 1, 1, 0, argt[1]); + instr |= doarg(a, dst_sourcemap_index(map, 2), DST_OAT_SLOT, 2, 1, 0, argt[2]); + instr |= doarg(a, dst_sourcemap_index(map, 3), DST_OAT_INTEGER, 3, 1, idef->type == DIT_SSI, argt[3]); break; } case DIT_SES: @@ -444,23 +443,23 @@ static uint32_t read_instruction( uint32_t env; if (dst_tuple_length(argt) != 4) dst_asm_error(a, map, "expected 3 arguments: (op, slot, environment, envslot)"); - instr |= doarg(a, dst_parse_submap_index(map, 1), DST_OAT_SLOT, 1, 1, 0, argt[1]); - env = doarg(a, dst_parse_submap_index(map, 2), DST_OAT_ENVIRONMENT, 0, 1, 0, argt[2]); + instr |= doarg(a, dst_sourcemap_index(map, 1), DST_OAT_SLOT, 1, 1, 0, argt[1]); + env = doarg(a, dst_sourcemap_index(map, 2), DST_OAT_ENVIRONMENT, 0, 1, 0, argt[2]); instr |= env << 16; for (env += 1; env > 0; env--) { b = b->parent; if (NULL == b) - dst_asm_error(a, dst_parse_submap_index(map, 2), "invalid environment index"); + dst_asm_error(a, dst_sourcemap_index(map, 2), "invalid environment index"); } - instr |= doarg(b, dst_parse_submap_index(map, 3), DST_OAT_SLOT, 3, 1, 0, argt[3]); + instr |= doarg(b, dst_sourcemap_index(map, 3), DST_OAT_SLOT, 3, 1, 0, argt[3]); break; } case DIT_SC: { if (dst_tuple_length(argt) != 3) dst_asm_error(a, map, "expected 2 arguments: (op, slot, constant)"); - instr |= doarg(a, dst_parse_submap_index(map, 1), DST_OAT_SLOT, 1, 1, 0, argt[1]); - instr |= doarg(a, dst_parse_submap_index(map, 2), DST_OAT_CONSTANT, 2, 2, 0, argt[2]); + instr |= doarg(a, dst_sourcemap_index(map, 1), DST_OAT_SLOT, 1, 1, 0, argt[1]); + instr |= doarg(a, dst_sourcemap_index(map, 2), DST_OAT_CONSTANT, 2, 2, 0, argt[2]); break; } } @@ -585,15 +584,15 @@ static DstAssembleResult dst_asm1(DstAssembler *parent, DstAssembleOptions opts) x = dst_struct_get(st, dst_csymbolv("slots")); if (dst_seq_view(x, &arr, &count)) { const DstValue *slotmap = - dst_parse_submap_value(opts.sourcemap, dst_csymbolv("slots")); + dst_sourcemap_value(opts.sourcemap, dst_csymbolv("slots")); for (i = 0; i < count; i++) { - const DstValue *imap = dst_parse_submap_index(slotmap, i); + const DstValue *imap = dst_sourcemap_index(slotmap, i); DstValue v = arr[i]; if (dst_checktype(v, DST_TUPLE)) { const DstValue *t = dst_unwrap_tuple(v); int32_t j; for (j = 0; j < dst_tuple_length(t); j++) { - const DstValue *tjmap = dst_parse_submap_index(imap, j); + const DstValue *tjmap = dst_sourcemap_index(imap, j); if (!dst_checktype(t[j], DST_SYMBOL)) dst_asm_error(&a, tjmap, "slot names must be symbols"); dst_table_put(&a.slots, t[j], dst_wrap_integer(i)); @@ -610,9 +609,9 @@ static DstAssembleResult dst_asm1(DstAssembler *parent, DstAssembleOptions opts) x = dst_struct_get(st, dst_csymbolv("captures")); if (dst_seq_view(x, &arr, &count)) { const DstValue *emap = - dst_parse_submap_value(opts.sourcemap, dst_csymbolv("captures")); + dst_sourcemap_value(opts.sourcemap, dst_csymbolv("captures")); for (i = 0; i < count; i++) { - const DstValue *imap = dst_parse_submap_index(emap, i); + const DstValue *imap = dst_sourcemap_index(emap, i); dst_asm_assert(&a, dst_checktype(arr[i], DST_SYMBOL), imap, "environment must be a symbol"); if (dst_asm_addenv(&a, arr[i]) < 0) { dst_asm_error(&a, imap, "environment not found"); @@ -624,14 +623,14 @@ static DstAssembleResult dst_asm1(DstAssembler *parent, DstAssembleOptions opts) x = dst_struct_get(st, dst_csymbolv("constants")); if (dst_seq_view(x, &arr, &count)) { const DstValue *cmap = - dst_parse_submap_value(opts.sourcemap, dst_csymbolv("constants")); + dst_sourcemap_value(opts.sourcemap, dst_csymbolv("constants")); def->constants_length = count; def->constants = malloc(sizeof(DstValue) * count); if (NULL == def->constants) { DST_OUT_OF_MEMORY; } for (i = 0; i < count; i++) { - const DstValue *imap = dst_parse_submap_index(cmap, i); + const DstValue *imap = dst_sourcemap_index(cmap, i); DstValue ct = arr[i]; if (dst_checktype(ct, DST_TUPLE) && dst_tuple_length(dst_unwrap_tuple(ct)) > 1 && @@ -663,11 +662,11 @@ static DstAssembleResult dst_asm1(DstAssembler *parent, DstAssembleOptions opts) x = dst_struct_get(st, dst_csymbolv("bytecode")); if (dst_seq_view(x, &arr, &count)) { const DstValue *bmap = - dst_parse_submap_value(opts.sourcemap, dst_csymbolv("bytecode")); + dst_sourcemap_value(opts.sourcemap, dst_csymbolv("bytecode")); /* Do labels and find length */ int32_t blength = 0; for (i = 0; i < count; ++i) { - const DstValue *imap = dst_parse_submap_index(bmap, i); + const DstValue *imap = dst_sourcemap_index(bmap, i); DstValue instr = arr[i]; if (dst_checktype(instr, DST_SYMBOL)) { dst_table_put(&a.labels, instr, dst_wrap_integer(blength)); @@ -685,7 +684,7 @@ static DstAssembleResult dst_asm1(DstAssembler *parent, DstAssembleOptions opts) } /* Do bytecode */ for (i = 0; i < count; ++i) { - const DstValue *imap = dst_parse_submap_index(bmap, i); + const DstValue *imap = dst_sourcemap_index(bmap, i); DstValue instr = arr[i]; if (dst_checktype(instr, DST_SYMBOL)) { continue; @@ -716,7 +715,7 @@ static DstAssembleResult dst_asm1(DstAssembler *parent, DstAssembleOptions opts) x = dst_struct_get(st, dst_csymbolv("sourcemap")); if (dst_seq_view(x, &arr, &count)) { const DstValue *bmap = - dst_parse_submap_value(opts.sourcemap, dst_csymbolv("sourcemap")); + dst_sourcemap_value(opts.sourcemap, dst_csymbolv("sourcemap")); dst_asm_assert(&a, count != 2 * def->bytecode_length, bmap, "sourcemap must have twice the length of the bytecode"); def->sourcemap = malloc(sizeof(int32_t) * 2 * count); for (i = 0; i < count; i += 2) { @@ -724,12 +723,12 @@ static DstAssembleResult dst_asm1(DstAssembler *parent, DstAssembleOptions opts) DstValue end = arr[i + 1]; if (!(dst_checktype(start, DST_INTEGER) || dst_unwrap_integer(start) < 0)) { - const DstValue *submap = dst_parse_submap_index(bmap, i); + const DstValue *submap = dst_sourcemap_index(bmap, i); dst_asm_error(&a, submap, "expected positive integer"); } if (!(dst_checktype(end, DST_INTEGER) || dst_unwrap_integer(end) < 0)) { - const DstValue *submap = dst_parse_submap_index(bmap, i + 1); + const DstValue *submap = dst_sourcemap_index(bmap, i + 1); dst_asm_error(&a, submap, "expected positive integer"); } def->sourcemap[i] = dst_unwrap_integer(start); @@ -853,11 +852,11 @@ static DstValue dst_asm_decode_instruction(uint32_t instr) { DstValue dst_disasm(DstFuncDef *def) { int32_t i; DstArray *bcode = dst_array(def->bytecode_length); - DstArray *constants = dst_array(def->constants_length); + DstArray *constants; DstTable *ret = dst_table(10); - dst_table_put(ret, dst_csymbolv("arity"), dst_wrap_integer(def->arity)); + if (def->arity) + dst_table_put(ret, dst_csymbolv("arity"), dst_wrap_integer(def->arity)); dst_table_put(ret, dst_csymbolv("bytecode"), dst_wrap_array(bcode)); - dst_table_put(ret, dst_csymbolv("constants"), dst_wrap_array(constants)); if (def->sourcepath) { dst_table_put(ret, dst_csymbolv("sourcepath"), dst_wrap_string(def->sourcepath)); } @@ -869,17 +868,21 @@ DstValue dst_disasm(DstFuncDef *def) { } /* Add constants */ - for (i = 0; i < def->constants_length; i++) { - DstValue src = def->constants[i]; - DstValue dest; - if (dst_checktype(src, DST_TUPLE)) { - dest = tup2(dst_csymbolv("quote"), src); - } else { - dest = src; + if (def->constants_length > 0) { + constants = dst_array(def->constants_length); + dst_table_put(ret, dst_csymbolv("constants"), dst_wrap_array(constants)); + for (i = 0; i < def->constants_length; i++) { + DstValue src = def->constants[i]; + DstValue dest; + if (dst_checktype(src, DST_TUPLE)) { + dest = tup2(dst_csymbolv("quote"), src); + } else { + dest = src; + } + constants->data[i] = dest; } - constants->data[i] = dest; + constants->count = def->constants_length; } - constants->count = def->constants_length; /* Add bytecode */ for (i = 0; i < def->bytecode_length; i++) { diff --git a/core/compile.c b/core/compile.c index 7f1a8877..73b609e3 100644 --- a/core/compile.c +++ b/core/compile.c @@ -21,21 +21,27 @@ */ #include +#include #include "compile.h" #include "gc.h" +#include "sourcemap.h" /* Lazily sort the optimizers */ /*static int optimizers_sorted = 0;*/ /* Lookups for specials and optimizable c functions. */ /*DstCFunctionOptimizer dst_compiler_optimizers[255];*/ -/*DstSpecial dst_compiler_specials[16];*/ /* Throw an error with a dst string */ void dst_compile_error(DstCompiler *c, const DstValue *sourcemap, const uint8_t *m) { - c->results.error_start = dst_unwrap_integer(sourcemap[0]); - c->results.error_end = dst_unwrap_integer(sourcemap[1]); - c->results.error = m; + if (NULL != sourcemap) { + c->result.error_start = dst_unwrap_integer(sourcemap[0]); + c->result.error_end = dst_unwrap_integer(sourcemap[1]); + } else { + c->result.error_start = -1; + c->result.error_end = -1; + } + c->result.error = m; longjmp(c->on_error, 1); } @@ -47,20 +53,22 @@ void dst_compile_cerror(DstCompiler *c, const DstValue *sourcemap, const char *m /* Use these to get sub options. They will traverse the source map so * compiler errors make sense. Then modify the returned options. */ DstFormOptions dst_compile_getopts_index(DstFormOptions opts, int32_t index) { - const DstValue *sourcemap = dst_parse_submap_index(opts.sourcemap, index); + const DstValue *sourcemap = dst_sourcemap_index(opts.sourcemap, index); DstValue nextval = dst_getindex(opts.x, index); opts.x = nextval; opts.sourcemap = sourcemap; return opts; } + DstFormOptions dst_compile_getopts_key(DstFormOptions opts, DstValue key) { - const DstValue *sourcemap = dst_parse_submap_key(opts.sourcemap, key); + const DstValue *sourcemap = dst_sourcemap_key(opts.sourcemap, key); opts.x = key; opts.sourcemap = sourcemap; return opts; } + DstFormOptions dst_compile_getopts_value(DstFormOptions opts, DstValue key) { - const DstValue *sourcemap = dst_parse_submap_value(opts.sourcemap, key); + const DstValue *sourcemap = dst_sourcemap_value(opts.sourcemap, key); DstValue nextval = dst_get(opts.x, key); opts.x = nextval; opts.sourcemap = sourcemap; @@ -163,6 +171,12 @@ static void slotsym(DstScope *scope, const uint8_t *sym, DstSlot s) { static int32_t addconst(DstCompiler *c, const DstValue *sourcemap, DstValue x) { DstScope *scope = dst_compile_topscope(c); int32_t i, index, newcount; + /* Get the topmost function scope */ + while (scope > c->scopes) { + if (scope->flags & DST_SCOPE_FUNCTION) + break; + scope--; + } for (i = 0; i < scope->ccount; i++) { if (dst_equals(x, scope->consts[i])) return i; @@ -185,7 +199,7 @@ static int32_t addconst(DstCompiler *c, const DstValue *sourcemap, DstValue x) { } /* Enter a new scope */ -void dst_compile_scope(DstCompiler *c, int newfn) { +void dst_compile_scope(DstCompiler *c, int flags) { int32_t newcount, oldcount; DstScope *scope; oldcount = c->scopecount; @@ -222,7 +236,7 @@ void dst_compile_scope(DstCompiler *c, int newfn) { scope->scap = 0; scope->smax = -1; - scope->flags = newfn ? DST_SCOPE_FUNCTION : 0; + scope->flags = flags; } /* Leave a scope. */ @@ -302,7 +316,7 @@ DstSlot dst_compile_resolve( DstValue ref = dst_get(check, dst_csymbolv("ref")); if (dst_checktype(ref, DST_ARRAY)) { DstSlot ret = dst_compile_constantslot(ref); - ret.flags |= DST_SLOT_REF; + ret.flags |= DST_SLOT_REF | DST_SLOT_NAMED | DST_SLOT_MUTABLE; return ret; } else { DstValue value = dst_get(check, dst_csymbolv("value")); @@ -393,6 +407,16 @@ void dst_compile_emit(DstCompiler *c, const DstValue *sourcemap, uint32_t instr) c->buffer[index] = instr; } +/* Helper */ +static int32_t slotalloc_temp(DstScope *scope, int32_t max, int32_t nth) { + int32_t ret = slotalloc_index(scope); + if (ret > max) { + slotfree_index(scope, ret); + ret = 0xF0 + nth; + } + return ret; +} + /* Realize any slot to a local slot. Call this to get a slot index * that can be used in an instruction. */ static int32_t dst_compile_preread( @@ -410,11 +434,7 @@ static int32_t dst_compile_preread( if (s.flags & DST_SLOT_CONSTANT) { int32_t cindex; - ret = slotalloc_index(scope); - if (ret > max) { - slotfree_index(scope, ret); - ret = 0xF0 + nth; - } + ret = slotalloc_temp(scope, max, nth); /* Use instructions for loading certain constants */ switch (dst_type(s.constant)) { case DST_NIL: @@ -454,28 +474,18 @@ static int32_t dst_compile_preread( DOP_GET_INDEX); } } else if (s.envindex > 0 || s.index > max) { - /* Get a local slot to shadow the environment or far slot */ - ret = slotalloc_index(scope); - if (ret > max) { - slotfree_index(scope, ret); - ret = 0xF0 + nth; - } - /* Move the remote slot into the local space */ - if (s.envindex > 0) { - /* Load the higher slot */ - dst_compile_emit(c, sourcemap, - ((uint32_t)(s.index) << 24) | - ((uint32_t)(s.envindex) << 16) | - ((uint32_t)(ret) << 8) | - DOP_LOAD_UPVALUE); - } else { - /* Slot is a far slot: greater than 0xFF. Get - * the far data and bring it to the near slot. */ - dst_compile_emit(c, sourcemap, - ((uint32_t)(s.index) << 16) | - ((uint32_t)(ret) << 8) | + ret = slotalloc_temp(scope, max, nth); + dst_compile_emit(c, sourcemap, + ((uint32_t)(s.index) << 24) | + ((uint32_t)(s.envindex) << 16) | + ((uint32_t)(ret) << 8) | + DOP_LOAD_UPVALUE); + } else if (s.index > max) { + ret = slotalloc_temp(scope, max, nth); + dst_compile_emit(c, sourcemap, + ((uint32_t)(s.index) << 16) | + ((uint32_t)(ret) << 8) | DOP_MOVE_NEAR); - } } else { /* We have a normal slot that fits in the required bit width */ ret = s.index; @@ -492,96 +502,91 @@ static void dst_compile_postread(DstCompiler *c, DstSlot s, int32_t index) { } } -/* Get a write slot index to emit an instruction. */ -static int32_t dst_compile_prewrite( +/* Move values from one slot to another. The destination must be mutable. */ +static void dst_compile_copy( DstCompiler *c, const DstValue *sourcemap, - int32_t nth, - DstSlot s) { - int32_t ret = 0; - if (s.flags & DST_SLOT_CONSTANT) { - if (!(s.flags & DST_SLOT_REF)) { - dst_compile_cerror(c, sourcemap, "cannot write to constant"); - } - } else if (s.envindex > 0 || s.index > 0xFF) { - DstScope *scope = dst_compile_topscope(c); - /* Get a local slot to shadow the environment or far slot */ - ret = slotalloc_index(scope); - if (ret > 0xFF) { - slotfree_index(scope, ret); - ret = 0xF0 + nth; - } - /* Move the remote slot into the local space */ - if (s.envindex > 0) { - /* Load the higher slot */ - dst_compile_emit(c, sourcemap, - ((uint32_t)(s.index) << 24) | - ((uint32_t)(s.envindex) << 16) | - ((uint32_t)(ret) << 8) | - DOP_LOAD_UPVALUE); + DstSlot dest, + DstSlot src) { + int writeback = 0; + int32_t destlocal = -1; + int32_t srclocal = -1; + int32_t reflocal = -1; + DstScope *scope = dst_compile_topscope(c); + + /* Only write to mutable slots */ + if (!(dest.flags & DST_SLOT_MUTABLE)) { + dst_compile_cerror(c, sourcemap, "cannot write to constant"); + } + + /* Short circuit if dest and source are equal */ + if (dest.flags == src.flags && + dest.index == src.index && + dest.envindex == src.envindex) { + if (dest.flags & DST_SLOT_REF) { + if (dst_equals(dest.constant, src.constant)) + return; } else { - /* Slot is a far slot: greater than 0xFF. Get - * the far data and bring it to the near slot. */ - dst_compile_emit(c, sourcemap, - ((uint32_t)(s.index) << 16) | - ((uint32_t)(ret) << 8) | - DOP_MOVE_NEAR); + return; } - } else { - /* We have a normal slot that fits in the required bit width */ - ret = s.index; } - return ret; -} -/* Release a write index after emitting the instruction */ -static void dst_compile_postwrite( - DstCompiler *c, - const DstValue *sourcemap, - DstSlot s, - int32_t index) { + /* Process: src -> srclocal -> destlocal -> dest */ + + /* src -> srclocal */ + srclocal = dst_compile_preread(c, sourcemap, 0xFF, 1, src); - /* Set the ref */ - if (s.flags & DST_SLOT_REF) { - DstScope *scope = dst_compile_topscope(c); - int32_t cindex = addconst(c, sourcemap, s.constant); - int32_t refindex = slotalloc_index(scope); - if (refindex > 0xFF) { - slotfree_index(scope, refindex); - refindex = 0xFF; - } - dst_compile_emit(c, sourcemap, - (cindex << 16) | - (refindex << 8) | - DOP_LOAD_CONSTANT); + /* Pull down dest (find destlocal) */ + if (dest.flags & DST_SLOT_REF) { + writeback = 1; + destlocal = srclocal; + reflocal = slotalloc_temp(scope, 0xFF, 2); dst_compile_emit(c, sourcemap, - (index << 16) | - (refindex << 8) | - DOP_PUT_INDEX); - slotfree_index(scope, refindex); - return; + (addconst(c, sourcemap, dest.constant) << 16) | + (reflocal << 8) | + DOP_LOAD_CONSTANT); + } else if (dest.envindex > 0) { + writeback = 2; + destlocal = srclocal; + } else if (dest.index > 0xFF) { + writeback = 3; + destlocal = srclocal; + } else { + destlocal = dest.index; } - /* We need to save the data in the local slot to the original slot */ - if (s.envindex > 0) { - /* Load the higher slot */ + /* srclocal -> destlocal */ + if (srclocal != destlocal) { + dst_compile_emit(c, sourcemap, + ((uint32_t)(srclocal) << 16) | + ((uint32_t)(destlocal) << 8) | + DOP_MOVE_NEAR); + } + + /* destlocal -> dest */ + if (writeback == 1) { + dst_compile_emit(c, sourcemap, + (destlocal << 16) | + (reflocal << 8) | + DOP_PUT_INDEX); + } else if (writeback == 2) { dst_compile_emit(c, sourcemap, - ((uint32_t)(s.index) << 24) | - ((uint32_t)(s.envindex) << 16) | - ((uint32_t)(index) << 8) | + ((uint32_t)(dest.index) << 24) | + ((uint32_t)(dest.envindex) << 16) | + ((uint32_t)(destlocal) << 8) | DOP_SET_UPVALUE); - } else if (s.index != index) { - /* There was a local remapping */ - dst_compile_emit(c, sourcemap, - ((uint32_t)(s.index) << 16) | - ((uint32_t)(index) << 8) | + } else if (writeback == 3) { + dst_compile_emit(c, sourcemap, + ((uint32_t)(dest.index) << 16) | + ((uint32_t)(destlocal) << 8) | DOP_MOVE_FAR); } - if (index != s.index || s.envindex > 0) { - /* We need to free the temporary slot */ - DstScope *scope = dst_compile_topscope(c); - slotfree_index(scope, index); + + /* Cleanup */ + if (reflocal >= 0) { + slotfree_index(scope, reflocal); } + dst_compile_postread(c, src, srclocal); } /* Generate the return instruction for a slot. */ @@ -685,6 +690,201 @@ static void dst_compile_pushtuple( } } +/* Quote */ +DstSlot dst_compile_quote(DstFormOptions opts, int32_t argn, const DstValue *argv) { + if (argn != 1) + dst_compile_cerror(opts.compiler, opts.sourcemap, "expected 1 argument"); + return dst_compile_constantslot(argv[0]); +} + +/* Var */ +DstSlot dst_compile_var(DstFormOptions opts, int32_t argn, const DstValue *argv) { + DstScope *scope = dst_compile_topscope(opts.compiler); + DstFormOptions subopts; + DstSlot ret; + if (argn != 2) + dst_compile_cerror(opts.compiler, opts.sourcemap, "expected 2 arguments"); + if (!dst_checktype(argv[0], DST_SYMBOL)) + dst_compile_cerror(opts.compiler, opts.sourcemap, "expected symbol"); + subopts = dst_compile_getopts_index(opts, 2); + subopts.flags &= ~DST_FOPTS_TAIL; + ret = dst_compile_value(subopts); + if (scope->flags & DST_SCOPE_TOP) { + DstCompiler *c = opts.compiler; + const DstValue *sm = opts.sourcemap; + DstSlot refslot, refarrayslot; + /* Global var, generate var */ + DstTable *reftab = dst_table(1); + DstArray *ref = dst_array(1); + dst_array_push(ref, dst_wrap_nil()); + dst_table_put(reftab, dst_csymbolv("ref"), dst_wrap_array(ref)); + dst_put(opts.compiler->env, argv[0], dst_wrap_table(reftab)); + refslot = dst_compile_constantslot(dst_wrap_array(ref)); + refarrayslot = refslot; + refslot.flags |= DST_SLOT_REF | DST_SLOT_NAMED | DST_SLOT_MUTABLE; + /* Generate code to set ref */ + int32_t refarrayindex = dst_compile_preread(c, sm, 0xFF, 1, refarrayslot); + int32_t retindex = dst_compile_preread(c, sm, 0xFF, 2, ret); + dst_compile_emit(c, sm, + (retindex << 16) | + (refarrayindex << 8) | + DOP_PUT_INDEX); + dst_compile_postread(c, refarrayslot, refarrayindex); + dst_compile_postread(c, ret, retindex); + dst_compile_freeslot(c, refarrayslot); + ret = refslot; + } else { + /* Non root scope, bring to local slot */ + DstSlot localslot = dst_compile_gettarget(opts); + localslot.flags |= DST_SLOT_NAMED | DST_SLOT_MUTABLE; + dst_compile_copy(opts.compiler, opts.sourcemap, localslot, ret); + slotsym(scope, dst_unwrap_symbol(argv[0]), localslot); + ret = localslot; + } + return ret; +} + +/* Varset */ +DstSlot dst_compile_varset(DstFormOptions opts, int32_t argn, const DstValue *argv) { + DstFormOptions subopts; + DstSlot ret, dest; + if (argn != 2) + dst_compile_cerror(opts.compiler, opts.sourcemap, "expected 2 arguments"); + if (!dst_checktype(argv[0], DST_SYMBOL)) + dst_compile_cerror(opts.compiler, opts.sourcemap, "expected symbol"); + subopts = dst_compile_getopts_index(opts, 2); + subopts.flags &= ~DST_FOPTS_TAIL; + dest = dst_compile_resolve(opts.compiler, opts.sourcemap, dst_unwrap_symbol(argv[0])); + if (!(dest.flags & DST_SLOT_MUTABLE)) { + dst_compile_cerror(opts.compiler, opts.sourcemap, "cannot set constant"); + } + subopts.flags |= DST_FOPTS_HINT; + subopts.hint = dest; + ret = dst_compile_value(subopts); + return ret; +} + +/* Def */ +DstSlot dst_compile_def(DstFormOptions opts, int32_t argn, const DstValue *argv) { + DstScope *scope = dst_compile_topscope(opts.compiler); + DstFormOptions subopts; + DstSlot ret; + if (argn != 2) + dst_compile_cerror(opts.compiler, opts.sourcemap, "expected 2 arguments"); + if (!dst_checktype(argv[0], DST_SYMBOL)) + dst_compile_cerror(opts.compiler, opts.sourcemap, "expected symbol"); + subopts = dst_compile_getopts_index(opts, 2); + subopts.flags &= ~DST_FOPTS_TAIL; + ret = dst_compile_value(subopts); + ret.flags |= DST_SLOT_NAMED; + if (scope->flags & DST_SCOPE_TOP) { + /* Global def, generate code to store in env when executed */ + DstCompiler *c = opts.compiler; + const DstValue *sm = opts.sourcemap; + /* Root scope, add to def table */ + DstSlot envslot = dst_compile_constantslot(c->env); + DstSlot nameslot = dst_compile_constantslot(argv[0]); + DstSlot valsymslot = dst_compile_constantslot(dst_csymbolv("value")); + DstSlot tableslot = dst_compile_constantslot(dst_wrap_cfunction(dst_stl_table)); + /* Create env entry */ + int32_t valsymindex = dst_compile_preread(c, sm, 0xFF, 1, valsymslot); + int32_t retindex = dst_compile_preread(c, sm, 0xFFFF, 2, ret); + dst_compile_emit(c, sm, + (retindex << 16) | + (valsymindex << 8) | + DOP_PUSH_2); + dst_compile_postread(c, ret, retindex); + dst_compile_postread(c, valsymslot, valsymindex); + dst_compile_freeslot(c, valsymslot); + int32_t tableindex = dst_compile_preread(opts.compiler, opts.sourcemap, 0xFF, 1, tableslot); + dst_compile_emit(c, sm, + (tableindex << 16) | + (tableindex << 8) | + DOP_CALL); + /* Add env entry to env */ + int32_t nameindex = dst_compile_preread(opts.compiler, opts.sourcemap, 0xFF, 2, nameslot); + int32_t envindex = dst_compile_preread(opts.compiler, opts.sourcemap, 0xFF, 3, envslot); + dst_compile_emit(opts.compiler, opts.sourcemap, + (tableindex << 24) | + (nameindex << 16) | + (envindex << 8) | + DOP_PUT); + dst_compile_postread(opts.compiler, envslot, envindex); + dst_compile_postread(opts.compiler, nameslot, nameindex); + dst_compile_postread(c, tableslot, tableindex); + dst_compile_freeslot(c, tableslot); + dst_compile_freeslot(c, envslot); + dst_compile_freeslot(c, tableslot); + } else { + /* Non root scope, simple slot alias */ + slotsym(scope, dst_unwrap_symbol(argv[0]), ret); + } + return ret; +} + +/* Do */ +DstSlot dst_compile_do(DstFormOptions opts, int32_t argn, const DstValue *argv) { + int32_t i; + DstSlot ret; + dst_compile_scope(opts.compiler, 0); + for (i = 0; i < argn; i++) { + DstFormOptions subopts = dst_compile_getopts_index(opts, i + 1); + subopts.x = argv[i]; + if (i == argn - 1) { + subopts.flags |= DST_FOPTS_TAIL; + } else { + subopts.flags &= ~DST_FOPTS_TAIL; + } + ret = dst_compile_value(subopts); + if (i != argn - 1) { + dst_compile_freeslot(opts.compiler, ret); + } + } + dst_compile_popscope(opts.compiler); + return ret; +} + +/* Keep in lexographic order */ +static const DstSpecial dst_compiler_specials[] = { + {"def", dst_compile_def}, + {"do", dst_compile_do}, + {"quote", dst_compile_quote}, + {"var", dst_compile_var}, + {"varset", dst_compile_varset} +}; + +static int dst_strcompare(const uint8_t *str, const char *other) { + int32_t len = dst_string_length(str); + int32_t index; + for (index = 0; index < len; index++) { + uint8_t c = str[index]; + uint8_t k = ((const uint8_t *)other)[index]; + if (c < k) return -1; + if (c > k) return 1; + if (k == '\0') break; + } + return (other[index] == '\0') ? 0 : -1; +} + +/* Find an instruction definition given its name */ +static const DstSpecial *dst_finds(const uint8_t *key) { + const DstSpecial *low = dst_compiler_specials; + const DstSpecial *hi = dst_compiler_specials + + (sizeof(dst_compiler_specials) / sizeof(DstSpecial)); + while (low < hi) { + const DstSpecial *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; +} + /* Compile a tuplle */ DstSlot dst_compile_tuple(DstFormOptions opts) { DstSlot head; @@ -698,18 +898,21 @@ DstSlot dst_compile_tuple(DstFormOptions opts) { return dst_compile_constantslot(opts.x); } if (dst_checktype(tup[0], DST_SYMBOL)) { - /* Check specials */ - } else { + const DstSpecial *s = dst_finds(dst_unwrap_symbol(tup[0])); + if (NULL != s) { + return s->compile(opts, dst_tuple_length(tup) - 1, tup + 1); + } + } + if (!headcompiled) { head = dst_compile_value(subopts); headcompiled = 1; - if ((head.flags & DST_SLOT_CONSTANT)) { + /* + if ((head.flags & DST_SLOT_CONSTANT)) { if (dst_checktype(head.constant, DST_CFUNCTION)) { - /* Cfunction optimization */ printf("add cfunction optimization here...\n"); } - /* Could also later check for other optimizations here, such - * as function inlining and aot evaluation on pure functions. */ } + */ } /* Compile a normal function call */ { @@ -750,11 +953,7 @@ DstSlot dst_compile_value(DstFormOptions opts) { case DST_SYMBOL: { const uint8_t *sym = dst_unwrap_symbol(opts.x); - if (dst_string_length(sym) > 0 && sym[0] != ':') { - ret = dst_compile_resolve(opts.compiler, opts.sourcemap, sym); - } else { - ret = dst_compile_constantslot(opts.x); - } + ret = dst_compile_resolve(opts.compiler, opts.sourcemap, sym); break; } case DST_TUPLE: @@ -772,6 +971,9 @@ DstSlot dst_compile_value(DstFormOptions opts) { } if ((opts.flags & DST_FOPTS_TAIL) && !dst_compile_did_return(opts.compiler)) { dst_compile_return(opts.compiler, opts.sourcemap, ret); + } else if (opts.flags & DST_FOPTS_HINT) { + dst_compile_copy(opts.compiler, opts.sourcemap, opts.hint, ret); + ret = opts.hint; } opts.compiler->recursion_guard++; return ret; @@ -807,7 +1009,9 @@ static DstFuncDef *dst_compile_pop_funcdef(DstCompiler *c) { if (NULL == def->constants) { DST_OUT_OF_MEMORY; } - memcpy(def->constants, scope->consts, def->constants_length * sizeof(DstValue)); + memcpy(def->constants, + scope->consts, + def->constants_length * sizeof(DstValue)); } /* Copy bytecode */ @@ -817,7 +1021,9 @@ static DstFuncDef *dst_compile_pop_funcdef(DstCompiler *c) { if (NULL == def->bytecode) { DST_OUT_OF_MEMORY; } - memcpy(def->bytecode, c->buffer + scope->bytecode_start, def->bytecode_length * sizeof(uint32_t)); + memcpy(def->bytecode, + c->buffer + scope->bytecode_start, + def->bytecode_length * sizeof(uint32_t)); } /* Copy source map over */ @@ -826,7 +1032,9 @@ static DstFuncDef *dst_compile_pop_funcdef(DstCompiler *c) { if (NULL == def->sourcemap) { DST_OUT_OF_MEMORY; } - memcpy(def->sourcemap, c->mapbuffer + 2 * scope->bytecode_start, def->bytecode_length * 2 * sizeof(int32_t)); + memcpy(def->sourcemap, + c->mapbuffer + 2 * scope->bytecode_start, + def->bytecode_length * 2 * sizeof(int32_t)); } /* Reset bytecode gen */ @@ -847,31 +1055,8 @@ static DstFuncDef *dst_compile_pop_funcdef(DstCompiler *c) { return def; } -/* Merge an environment */ - - - -/* Load an environment */ -void dst_compile_loadenv(DstCompiler *c, DstValue env) { - int32_t count, cap; - const DstValue *hmap; - DstValue defs = dst_get(env, dst_csymbolv("defs")); - /*DstValue vars = dst_get(env, dst_csymbol("vars"));*/ - /* TODO - add global vars via single element arrays. */ - if (dst_hashtable_view(defs, &hmap, &count, &cap)) { - DstScope *scope = dst_compile_topscope(c); - int32_t i; - for (i = 0; i < cap; i += 2) { - const uint8_t *sym; - if (!dst_checktype(hmap[i], DST_SYMBOL)) continue; - sym = dst_unwrap_symbol(hmap[i]); - slotsym(scope, sym, dst_compile_constantslot(hmap[i+1])); - } - } -} - /* Initialize a compiler */ -static void dst_compile_init(DstCompiler *c) { +static void dst_compile_init(DstCompiler *c, DstValue env) { c->scopecount = 0; c->scopecap = 0; c->scopes = NULL; @@ -880,8 +1065,9 @@ static void dst_compile_init(DstCompiler *c) { c->buffer = NULL; c->mapbuffer = NULL; c->recursion_guard = DST_RECURSION_GUARD; + c->env = env; - /* Push an empty function scope. This will be the global scope. */ + /* Push an empty scope. This will be the global scope. */ dst_compile_scope(c, 0); dst_compile_topscope(c)->flags |= DST_SCOPE_TOP; @@ -901,7 +1087,7 @@ static void dst_compile_deinit(DstCompiler *c) { } /* Compile a single form */ -DstCompileResults dst_compile_one(DstCompiler *c, DstCompileOptions opts) { +DstCompileResult dst_compile_one(DstCompiler *c, DstCompileOptions opts) { DstFormOptions fopts; DstSlot s; @@ -910,13 +1096,13 @@ DstCompileResults dst_compile_one(DstCompiler *c, DstCompileOptions opts) { dst_compile_popscope(c); if (setjmp(c->on_error)) { - c->results.status = DST_COMPILE_ERROR; - c->results.funcdef = NULL; - return c->results; + c->result.status = DST_COMPILE_ERROR; + c->result.funcdef = NULL; + return c->result; } /* Push a function scope */ - dst_compile_scope(c, 1); + dst_compile_scope(c, DST_SCOPE_FUNCTION | DST_SCOPE_TOP); /* Set the global environment */ c->env = opts.env; @@ -930,18 +1116,18 @@ DstCompileResults dst_compile_one(DstCompiler *c, DstCompileOptions opts) { /* Compile the value */ s = dst_compile_value(fopts); - c->results.funcdef = dst_compile_pop_funcdef(c); - c->results.status = DST_COMPILE_OK; + c->result.funcdef = dst_compile_pop_funcdef(c); + c->result.status = DST_COMPILE_OK; - return c->results; + return c->result; } /* Compile a form. */ -DstCompileResults dst_compile(DstCompileOptions opts) { +DstCompileResult dst_compile(DstCompileOptions opts) { DstCompiler c; - DstCompileResults res; + DstCompileResult res; - dst_compile_init(&c); + dst_compile_init(&c, opts.env); res = dst_compile_one(&c, opts); @@ -950,7 +1136,7 @@ DstCompileResults dst_compile(DstCompileOptions opts) { return res; } -DstFunction *dst_compile_func(DstCompileResults res) { +DstFunction *dst_compile_func(DstCompileResult res) { if (res.status != DST_COMPILE_OK) { return NULL; } diff --git a/core/compile.h b/core/compile.h index acb6809b..801eee9b 100644 --- a/core/compile.h +++ b/core/compile.h @@ -67,10 +67,8 @@ struct DstSlot { */ #define DST_SCOPE_FUNCTION 1 -#define DST_SCOPE_LASTSLOT 2 -#define DST_SCOPE_FIRSTSLOT 4 -#define DST_SCOPE_ENV 8 -#define DST_SCOPE_TOP 16 +#define DST_SCOPE_ENV 2 +#define DST_SCOPE_TOP 4 /* A lexical scope during compilation */ struct DstScope { @@ -101,7 +99,7 @@ struct DstScope { int32_t envcap; int32_t bytecode_start; - uint32_t flags; + int flags; }; #define dst_compile_topscope(c) ((c)->scopes + (c)->scopecount - 1) @@ -122,7 +120,7 @@ struct DstCompiler { /* Hold the environment */ DstValue env; - DstCompileResults results; + DstCompileResult result; }; #define DST_FOPTS_TAIL 0x10000 @@ -152,22 +150,9 @@ typedef struct DstSpecial { /* An array of optimizers sorted by key */ extern DstCFunctionOptimizer dst_compiler_optimizers[255]; -/* An array of special forms */ -extern DstSpecial dst_compiler_specials[16]; - /* Dispatch to correct form compiler */ DstSlot dst_compile_value(DstFormOptions opts); -/* Compile special forms */ -DstSlot dst_compile_do(DstFormOptions opts, int32_t argn, const DstValue *argv); -DstSlot dst_compile_fn(DstFormOptions opts, int32_t argn, const DstValue *argv); -DstSlot dst_compile_cond(DstFormOptions opts, int32_t argn, const DstValue *argv); -DstSlot dst_compile_while(DstFormOptions opts, int32_t argn, const DstValue *argv); -DstSlot dst_compile_quote(DstFormOptions opts, int32_t argn, const DstValue *argv); -DstSlot dst_compile_def(DstFormOptions opts, int32_t argn, const DstValue *argv); -DstSlot dst_compile_var(DstFormOptions opts, int32_t argn, const DstValue *argv); -DstSlot dst_compile_varset(DstFormOptions opts, int32_t argn, const DstValue *argv); - /****************************************************/ void dst_compile_error(DstCompiler *c, const DstValue *sourcemap, const uint8_t *m); diff --git a/core/fiber.c b/core/fiber.c index d92af6f0..44bb3fd3 100644 --- a/core/fiber.c +++ b/core/fiber.c @@ -47,7 +47,6 @@ DstFiber *dst_fiber_reset(DstFiber *fiber) { fiber->stacktop = DST_FRAME_SIZE; fiber->status = DST_FIBER_DEAD; fiber->parent = NULL; - fiber->ret = dst_wrap_nil(); return fiber; } @@ -233,7 +232,7 @@ void dst_fiber_cframe(DstFiber *fiber) { /* Create a cframe for a tail call */ void dst_fiber_cframe_tail(DstFiber *fiber) { int32_t size = (fiber->stacktop - fiber->frametop) - DST_FRAME_SIZE; - int32_t nextframetop = fiber->frame + size;; + int32_t nextframetop = fiber->frame + size; int32_t nextstacktop = nextframetop + DST_FRAME_SIZE; if (fiber->frame == 0) { diff --git a/core/gc.c b/core/gc.c index 9cdf37fd..be32ae4d 100644 --- a/core/gc.c +++ b/core/gc.c @@ -26,7 +26,7 @@ /* GC State */ void *dst_vm_blocks; -uint32_t dst_vm_memory_interval; +uint32_t dst_vm_gc_interval; uint32_t dst_vm_next_collection; /* Roots */ @@ -333,6 +333,22 @@ int dst_gcunroot(DstValue root) { return 0; } +/* Remove a root value from the GC. This sets the effective reference count to 0. */ +int dst_gcunrootall(DstValue root) { + DstValue *vtop = dst_vm_roots + dst_vm_root_count; + DstValue *v = dst_vm_roots; + int ret = 0; + /* Search from top to bottom as access is most likely LIFO */ + for (v = dst_vm_roots; v < vtop; v++) { + if (dst_equals(root, *v)) { + *v = dst_vm_roots[--dst_vm_root_count]; + vtop--; + ret = 1; + } + } + return ret; +} + /* Free all allocated memory */ void dst_clear_memory() { DstGCMemoryHeader *current = dst_vm_blocks; diff --git a/core/math.c b/core/math.c new file mode 100644 index 00000000..a4da665f --- /dev/null +++ b/core/math.c @@ -0,0 +1,268 @@ +/* +* 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 +#include + +/* Convert a number to an integer */ +int dst_int(int32_t argn, DstValue *argv, DstValue *ret) { + if (argn != 1) { + *ret = dst_cstringv("expected 1 argument"); + return 1; + } + switch (dst_type(argv[0])) { + default: + *ret = dst_cstringv("could not convert to integer"); + return 1; + case DST_REAL: + *ret = dst_wrap_integer((int32_t) dst_unwrap_real(argv[0])); + break; + case DST_INTEGER: + *ret = argv[0]; + break; + } + return 0; +} + +/* Convert a number to a real number */ +int dst_real(int32_t argn, DstValue *argv, DstValue *ret) { + if (argn != 1) { + *ret = dst_cstringv("expected 1 argument"); + return 1; + } + switch (dst_type(argv[0])) { + default: + *ret = dst_cstringv("could not convert to real"); + return 1; + case DST_REAL: + *ret = argv[0]; + break; + case DST_INTEGER: + *ret = dst_wrap_real((double) dst_unwrap_integer(argv[0])); + break; + } + return 0; +} + +#define ADD(x, y) ((x) + (y)) +#define SUB(x, y) ((x) - (y)) +#define MUL(x, y) ((x) * (y)) +#define MOD(x, y) ((x) % (y)) +#define DIV(x, y) ((x) / (y)) + +#define DST_DEFINE_BINOP(name, op, rop, onerr)\ +DstValue dst_op_##name(DstValue lhs, DstValue rhs) {\ + if (!(dst_checktype(lhs, DST_INTEGER) || dst_checktype(lhs, DST_REAL))) onerr;\ + if (!(dst_checktype(rhs, DST_INTEGER) || dst_checktype(rhs, DST_REAL))) onerr;\ + return dst_checktype(lhs, DST_INTEGER)\ + ? (dst_checktype(rhs, DST_INTEGER)\ + ? dst_wrap_integer(op(dst_unwrap_integer(lhs), dst_unwrap_integer(rhs)))\ + : dst_wrap_real(rop((double)dst_unwrap_integer(lhs), dst_unwrap_real(rhs))))\ + : (dst_checktype(rhs, DST_INTEGER)\ + ? dst_wrap_real(rop(dst_unwrap_real(lhs), (double)dst_unwrap_integer(rhs)))\ + : dst_wrap_real(rop(dst_unwrap_real(lhs), dst_unwrap_real(rhs))));\ +} + +DST_DEFINE_BINOP(add, ADD, ADD, return dst_wrap_nil()) +DST_DEFINE_BINOP(subtract, SUB, SUB, return dst_wrap_nil()) +DST_DEFINE_BINOP(multiply, MUL, MUL, return dst_wrap_nil()) + +#define DST_DEFINE_DIVIDER_OP(name, op, rop)\ +DstValue dst_op_##name(DstValue lhs, DstValue rhs) {\ + if (!(dst_checktype(lhs, DST_INTEGER) || dst_checktype(lhs, DST_REAL))) return dst_wrap_nil();\ + if (!(dst_checktype(rhs, DST_INTEGER) || dst_checktype(rhs, DST_REAL))) return dst_wrap_nil();\ + return dst_checktype(lhs, DST_INTEGER)\ + ? (dst_checktype(rhs, DST_INTEGER)\ + ? (dst_unwrap_integer(rhs) == 0 || ((dst_unwrap_integer(lhs) == INT32_MIN) && (dst_unwrap_integer(rhs) == -1)))\ + ? dst_wrap_nil()\ + : dst_wrap_integer(op(dst_unwrap_integer(lhs), dst_unwrap_integer(rhs)))\ + : dst_wrap_real(rop((double)dst_unwrap_integer(lhs), dst_unwrap_real(rhs))))\ + : (dst_checktype(rhs, DST_INTEGER)\ + ? dst_wrap_real(rop(dst_unwrap_real(lhs), (double)dst_unwrap_integer(rhs)))\ + : dst_wrap_real(rop(dst_unwrap_real(lhs), dst_unwrap_real(rhs))));\ +} + +DST_DEFINE_DIVIDER_OP(divide, DIV, DIV) +DST_DEFINE_DIVIDER_OP(modulo, MOD, fmod) + +#define DST_DEFINE_REDUCER(name, fop, start)\ +int dst_##name(int32_t argn, DstValue *argv, DstValue *ret) {\ + int32_t i;\ + DstValue accum = dst_wrap_integer(start);\ + for (i = 0; i < argn; i++) {\ + accum = fop(accum, argv[i]);\ + }\ + if (dst_checktype(accum, DST_NIL)) {\ + *ret = dst_cstringv("expected number");\ + return 1;\ + }\ + *ret = accum;\ + return 0;\ +} + +DST_DEFINE_REDUCER(add, dst_op_add, 0) +DST_DEFINE_REDUCER(subtract, dst_op_subtract, 0) +DST_DEFINE_REDUCER(multiply, dst_op_multiply, 1) + +#define DST_DEFINE_DIVIDER(name)\ +int dst_##name(int32_t argn, DstValue *argv, DstValue *ret) {\ + int32_t i;\ + DstValue accum;\ + if (argn < 1) {\ + *ret = dst_cstringv("expected at least one argument");\ + return 1;\ + } else if (argn == 1) {\ + accum = dst_wrap_real(1);\ + i = 0;\ + } else {\ + accum = argv[0];\ + i = 1;\ + }\ + for (; i < argn; i++) {\ + accum = dst_op_##name(accum, argv[i]);\ + }\ + if (dst_checktype(accum, DST_NIL)) {\ + *ret = dst_cstringv("expected number or division error");\ + return 1;\ + }\ + *ret = accum;\ + return 0;\ +} + +DST_DEFINE_DIVIDER(divide) +DST_DEFINE_DIVIDER(modulo) + +#undef ADD +#undef SUB +#undef MUL +#undef MOD +#undef DST_DEFINE_BINOP + +int dst_bnot(int32_t argn, DstValue *argv, DstValue *ret) { + if (argn != 1) { + *ret = dst_cstringv("expected 1 argument"); + return 1; + } + if (!dst_checktype(argv[0], DST_INTEGER)) { + *ret = dst_cstringv("expected integer"); + return 1; + } + *ret = dst_wrap_integer(~dst_unwrap_integer(argv[0])); + return 0; +} + +#define DST_DEFINE_BITOP(name, op, start)\ +int dst_##name(int32_t argn, DstValue *argv, DstValue *ret) {\ + int32_t i;\ + int32_t accum = start;\ + for (i = 0; i < argn; i++) {\ + DstValue arg = argv[i];\ + if (!dst_checktype(arg, DST_INTEGER)) {\ + *ret = dst_cstringv("expected integer");\ + return -1;\ + }\ + accum op dst_unwrap_integer(arg);\ + }\ + *ret = dst_wrap_integer(accum);\ + return 0;\ +} + +DST_DEFINE_BITOP(band, &=, -1) +DST_DEFINE_BITOP(bor, |=, 0) +DST_DEFINE_BITOP(bxor, ^=, 0) + +#define DST_DEFINE_MATHOP(name, fop)\ +int dst_##name(int32_t argn, DstValue *argv, DstValue *ret) {\ + if (argn != 1) {\ + *ret = dst_cstringv("expected 1 argument");\ + return 1;\ + }\ + if (dst_checktype(argv[0], DST_INTEGER)) {\ + argv[0] = dst_wrap_real(dst_unwrap_integer(argv[0]));\ + }\ + if (!dst_checktype(argv[0], DST_REAL)) {\ + *ret = dst_cstringv("expected number");\ + return 1;\ + }\ + *ret = dst_wrap_real(fop(dst_unwrap_real(argv[0])));\ + return 0;\ +} + +DST_DEFINE_MATHOP(acos, acos) +DST_DEFINE_MATHOP(asin, asin) +DST_DEFINE_MATHOP(atan, atan) +DST_DEFINE_MATHOP(cos, cos) +DST_DEFINE_MATHOP(cosh, cosh) +DST_DEFINE_MATHOP(sin, sin) +DST_DEFINE_MATHOP(sinh, sinh) +DST_DEFINE_MATHOP(tan, tan) +DST_DEFINE_MATHOP(tanh, tanh) +DST_DEFINE_MATHOP(exp, exp) +DST_DEFINE_MATHOP(log, log) +DST_DEFINE_MATHOP(log10, log10) +DST_DEFINE_MATHOP(sqrt, sqrt) +DST_DEFINE_MATHOP(ceil, ceil) +DST_DEFINE_MATHOP(fabs, fabs) +DST_DEFINE_MATHOP(floor, floor) + +#define DST_DEFINE_MATH2OP(name, fop)\ +int dst_##name(int32_t argn, DstValue *argv, DstValue *ret) {\ + if (argn != 2) {\ + *ret = dst_cstringv("expected 2 arguments");\ + return 1;\ + }\ + if (dst_checktype(argv[0], DST_INTEGER))\ + argv[0] = dst_wrap_real(dst_unwrap_integer(argv[0]));\ + if (dst_checktype(argv[1], DST_INTEGER))\ + argv[1] = dst_wrap_real(dst_unwrap_integer(argv[1]));\ + if (!dst_checktype(argv[0], DST_REAL) || !dst_checktype(argv[1], DST_REAL)) {\ + *ret = dst_cstringv("expected real");\ + return 1;\ + }\ + *ret =\ + dst_wrap_real(fop(dst_unwrap_real(argv[0]), dst_unwrap_real(argv[1])));\ + return 0;\ +}\ + +DST_DEFINE_MATH2OP(atan2, atan2) +DST_DEFINE_MATH2OP(pow, pow) +DST_DEFINE_MATH2OP(fmod, fmod) + +int dst_modf(int32_t argn, DstValue *argv, DstValue *ret) { + double intpart; + DstValue *tup; + if (argn != 1) { + *ret = dst_cstringv("expected 1 argument"); + return 1; + } + if (dst_checktype(argv[0], DST_INTEGER)) + argv[0] = dst_wrap_real(dst_unwrap_integer(argv[0])); + if (!dst_checktype(argv[0], DST_REAL)) { + *ret = dst_cstringv("expected real"); + return 1; + } + tup = dst_tuple_begin(2); + tup[0] = dst_wrap_real(modf(dst_unwrap_real(argv[0]), &intpart)); + tup[1] = dst_wrap_real(intpart); + *ret = dst_wrap_tuple(dst_tuple_end(tup)); + return 0; +} diff --git a/core/opcodes.h b/core/opcodes.h index 5da5a30b..f564aef7 100644 --- a/core/opcodes.h +++ b/core/opcodes.h @@ -79,8 +79,6 @@ enum DstOpCode { DOP_PUSH_ARRAY, DOP_CALL, DOP_TAILCALL, - DOP_SYSCALL, - DOP_LOAD_SYSCALL, DOP_TRANSFER, DOP_GET, DOP_PUT, diff --git a/core/parse.c b/core/parse.c index 643550f5..9b8d42ef 100644 --- a/core/parse.c +++ b/core/parse.c @@ -48,6 +48,7 @@ static int is_whitespace(uint8_t c) { || c == '\n' || c == '\r' || c == '\0' + || c == ';' || c == ','; } @@ -69,6 +70,7 @@ static int is_symbol_char_gen(uint8_t c) { if (c >= '0' && c <= '9') return 1; return (c == '!' || c == '$' || + c == '%' || c == '&' || c == '*' || c == '+' || @@ -89,9 +91,10 @@ static int is_symbol_char_gen(uint8_t c) { The table contains 256 bits, where each bit is 1 if the corresponding ascci code is a symbol char, and 0 -if not. */ +if not. The upper characters are also considered symbol +chars and are then checked for utf-8 compliance. */ static uint32_t symchars[256] = { - 0x00000000, 0x77ffec52, 0xd7ffffff, 0x57fffffe, + 0x00000000, 0x77ffec72, 0xd7ffffff, 0x57fffffe, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }; @@ -215,7 +218,13 @@ static const uint8_t *parse_recur( } /* Check for end of source */ - if (src >= end) goto unexpected_eos; + if (src >= end) { + if (qcount || recur != DST_RECURSION_GUARD) { + goto unexpected_eos; + } else { + goto nodata; + } + } /* Open mapping */ mapstart = src; @@ -248,7 +257,11 @@ static const uint8_t *parse_recur( } else { if (!valid_utf8(src, tokenend - src)) goto invalid_utf8; - ret = dst_symbolv(src, tokenend - src); + if (*src == ':') { + ret = dst_stringv(src + 1, tokenend - src - 1); + } else { + ret = dst_symbolv(src, tokenend - src); + } } } src = tokenend; @@ -382,7 +395,11 @@ static const uint8_t *parse_recur( } case '}': { - if (n & 1) goto struct_oddargs; + if (n & 1) { + if (istable) + goto table_oddargs; + goto struct_oddargs; + } if (istable) { DstTable *t = dst_table(n); DstTable *subt = dst_table(n); @@ -419,12 +436,6 @@ static const uint8_t *parse_recur( } } - /* Quote the returned value qcount times */ - while (qcount--) ret = quote(ret); - - /* Push the result to the stack */ - dst_array_push(&args->stack, ret); - /* Push source mapping */ if (dst_checktype(submapping, DST_NIL)) { /* We just parsed an atom */ @@ -439,11 +450,33 @@ static const uint8_t *parse_recur( submapping)); } + /* Quote the returned value qcount times */ + while (qcount--) { + int32_t start = mapstart - args->srcstart; + int32_t end = src - args->srcstart; + DstValue sourcemap = dst_array_pop(&args->mapstack); + DstValue* tup = dst_tuple_begin(2); + tup[0] = atom_map(start, end); + tup[1] = sourcemap; + ret = quote(ret); + dst_array_push(&args->mapstack, ds_map( + start, + end, + dst_wrap_tuple(dst_tuple_end(tup)))); + } + + /* Push the result to the stack */ + dst_array_push(&args->stack, ret); + /* Return the new source position for further calls */ return src; /* Errors below */ + nodata: + args->status = DST_PARSE_NODATA; + return NULL; + unexpected_eos: args->errmsg = "unexpected end of source"; args->status = DST_PARSE_UNEXPECTED_EOS; @@ -459,6 +492,11 @@ static const uint8_t *parse_recur( args->status = DST_PARSE_ERROR; return src; + table_oddargs: + args->errmsg = "table literal needs an even number of arguments"; + args->status = DST_PARSE_ERROR; + return src; + struct_oddargs: args->errmsg = "struct literal needs an even number of arguments"; args->status = DST_PARSE_ERROR; @@ -525,56 +563,3 @@ DstParseResult dst_parsec(const char *src) { while (src[len]) ++len; return dst_parse((const uint8_t *)src, len); } - -/* Get the sub source map by indexing a value. Used to traverse - * into arrays and tuples */ -const DstValue *dst_parse_submap_index(const DstValue *map, int32_t index) { - if (NULL != map && dst_tuple_length(map) >= 3) { - const DstValue *seq; - int32_t len; - if (dst_seq_view(map[2], &seq, &len)) { - if (index >= 0 && index < len) { - if (dst_checktype(seq[index], DST_TUPLE)) { - const DstValue *ret = dst_unwrap_tuple(seq[index]); - if (dst_tuple_length(ret) >= 2 && - dst_checktype(ret[0], DST_INTEGER) && - dst_checktype(ret[1], DST_INTEGER)) { - return ret; - } - } - } - } - } - return NULL; -} - -/* Traverse into tables and structs */ -static const DstValue *dst_parse_submap_kv(const DstValue *map, DstValue key, int kv) { - if (NULL != map && dst_tuple_length(map) >= 3) { - DstValue kvpair = dst_get(map[2], key); - if (dst_checktype(kvpair, DST_TUPLE)) { - const DstValue *kvtup = dst_unwrap_tuple(kvpair); - if (dst_tuple_length(kvtup) >= 2) { - if (dst_checktype(kvtup[kv], DST_TUPLE)) { - const DstValue *ret = dst_unwrap_tuple(kvtup[kv]); - if (dst_tuple_length(ret) >= 2 && - dst_checktype(ret[0], DST_INTEGER) && - dst_checktype(ret[1], DST_INTEGER)) { - return ret; - } - } - } - } - } - return NULL; -} - -/* Traverse into a key of a table or struct */ -const DstValue *dst_parse_submap_key(const DstValue *map, DstValue key) { - return dst_parse_submap_kv(map, key, 0); -} - -/* Traverse into a value of a table or struct */ -const DstValue *dst_parse_submap_value(const DstValue *map, DstValue key) { - return dst_parse_submap_kv(map, key, 1); -} diff --git a/core/sourcemap.c b/core/sourcemap.c new file mode 100644 index 00000000..5cfc3791 --- /dev/null +++ b/core/sourcemap.c @@ -0,0 +1,77 @@ +/* +* 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 +#include "sourcemap.h" + +/* Get the sub source map by indexing a value. Used to traverse + * into arrays and tuples */ +const DstValue *dst_sourcemap_index(const DstValue *map, int32_t index) { + if (NULL != map && dst_tuple_length(map) >= 3) { + const DstValue *seq; + int32_t len; + if (dst_seq_view(map[2], &seq, &len)) { + if (index >= 0 && index < len) { + if (dst_checktype(seq[index], DST_TUPLE)) { + const DstValue *ret = dst_unwrap_tuple(seq[index]); + if (dst_tuple_length(ret) >= 2 && + dst_checktype(ret[0], DST_INTEGER) && + dst_checktype(ret[1], DST_INTEGER)) { + return ret; + } + } + } + } + } + return NULL; +} + +/* Traverse into tables and structs */ +static const DstValue *dst_sourcemap_kv(const DstValue *map, DstValue key, int kv) { + if (NULL != map && dst_tuple_length(map) >= 3) { + DstValue kvpair = dst_get(map[2], key); + if (dst_checktype(kvpair, DST_TUPLE)) { + const DstValue *kvtup = dst_unwrap_tuple(kvpair); + if (dst_tuple_length(kvtup) >= 2) { + if (dst_checktype(kvtup[kv], DST_TUPLE)) { + const DstValue *ret = dst_unwrap_tuple(kvtup[kv]); + if (dst_tuple_length(ret) >= 2 && + dst_checktype(ret[0], DST_INTEGER) && + dst_checktype(ret[1], DST_INTEGER)) { + return ret; + } + } + } + } + } + return NULL; +} + +/* Traverse into a key of a table or struct */ +const DstValue *dst_sourcemap_key(const DstValue *map, DstValue key) { + return dst_sourcemap_kv(map, key, 0); +} + +/* Traverse into a value of a table or struct */ +const DstValue *dst_sourcemap_value(const DstValue *map, DstValue key) { + return dst_sourcemap_kv(map, key, 1); +} diff --git a/core/sourcemap.h b/core/sourcemap.h new file mode 100644 index 00000000..736af9ce --- /dev/null +++ b/core/sourcemap.h @@ -0,0 +1,44 @@ +/* +* 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_SOURCEMAP_H_defined +#define DST_SOURCEMAP_H_defined + +#include + +/* Get the sub source map by indexing a value. Used to traverse + * into arrays and tuples */ +const DstValue *dst_sourcemap_index(const DstValue *map, int32_t index); + +/* Traverse into a key of a table or struct */ +const DstValue *dst_sourcemap_key(const DstValue *map, DstValue key); + +/* Traverse into a value of a table or struct */ +const DstValue *dst_sourcemap_value(const DstValue *map, DstValue key); + +/* Try to rebuild a source map from given another map */ +const DstValue *dst_sourcemap_remap( + const DstValue *oldmap, + DstValue oldsource, + DstValue newsource); + +#endif diff --git a/core/stl.c b/core/stl.c index b5e13378..8039b88e 100644 --- a/core/stl.c +++ b/core/stl.c @@ -21,846 +21,205 @@ */ #include +#include -#define MAKE_BINOP(name, op)\ -static DstValue dst_stl_binop_##name(DstValue lhs, DstValue rhs) {\ - if (dst_checktype(lhs, DST_INTEGER))\ - if (dst_checktype(rhs, DST_INTEGER))\ - return dst_wrap_integer(dst_unwrap_integer(lhs) op dst_unwrap_integer(rhs));\ - else if (dst_checktype(rhs, DST_REAL))\ - return dst_wrap_real(dst_unwrap_integer(lhs) op dst_unwrap_real(lhs));\ - else\ - return dst_wrap_nil();\ - else if (dst_checktype(lhs, DST_REAL))\ - if (dst_checktype(rhs, DST_INTEGER))\ - return dst_wrap_real(dst_unwrap_real(lhs) op dst_unwrap_integer(rhs));\ - else if (dst_checktype(rhs, DST_REAL))\ - return dst_wrap_real(dst_unwrap_real(lhs) op dst_unwrap_real(rhs));\ - else\ - return dst_wrap_nil();\ - else\ - return dst_wrap_nil();\ +int dst_stl_print(int32_t argn, DstValue *argv, DstValue *ret) { + (void)ret; + + int32_t i; + for (i = 0; i < argn; ++i) { + int32_t j, len; + const uint8_t *vstr = dst_to_string(argv[i]); + len = dst_string_length(vstr); + for (j = 0; j < len; ++j) { + putc(vstr[j], stdout); + } + } + putc('\n', stdout); + return 0; } -#define SIMPLE_ACCUM_FUNCTION(name, op)\ -MAKE_BINOP(name, op)\ -int dst_stl_##name(DstValue *argv, int32_t argn) {\ - DstValue lhs, rhs;\ - int32_t j, count;\ - lhs = argv[0];\ - rhs = argv[1];\ - for (j = 1; j < argn; ++j) {\ - lhs = dst_stl_binop_##name(lhs, rhs);\ +int dst_stl_asm(int32_t argn, DstValue *argv, DstValue *ret) { + DstAssembleOptions opts; + DstAssembleResult res; + if (argn < 1) { + *ret = dst_cstringv("expected assembly source"); + return 1; + } + opts.source = argv[0]; + opts.sourcemap = (argn >= 2 && dst_checktype(argv[1], DST_TUPLE)) + ? dst_unwrap_tuple(argv[1]) + : NULL; + opts.flags = 0; + res = dst_asm(opts); + if (res.status == DST_ASSEMBLE_OK) { + *ret = dst_wrap_function(dst_asm_func(res)); + return 0; + } else { + *ret = dst_wrap_string(res.error); + return 1; + } +} + +int dst_stl_tuple(int32_t argn, DstValue *argv, DstValue *ret) { + *ret = dst_wrap_tuple(dst_tuple_n(argv, argn)); + return 0; +} + +int dst_stl_array(int32_t argn, DstValue *argv, DstValue *ret) { + DstArray *array = dst_array(argn); + array->count = argn; + memcpy(array->data, argv, argn * sizeof(DstValue)); + *ret = dst_wrap_array(array); + return 0; +} + +int dst_stl_table(int32_t argn, DstValue *argv, DstValue *ret) { + int32_t i; + DstTable *table = dst_table(argn/2); + if (argn & 1) { + *ret = dst_cstringv("expected even number of arguments"); + return 1; + } + for (i = 0; i < argn; i += 2) { + dst_table_put(table, argv[i], argv[i + 1]); + } + *ret = dst_wrap_table(table); + return 0; +} + +int dst_stl_struct(int32_t argn, DstValue *argv, DstValue *ret) { + int32_t i; + DstValue *st = dst_struct_begin(argn >> 1); + if (argn & 1) { + *ret = dst_cstringv("expected even number of arguments"); + return 1; + } + for (i = 0; i < argn; i += 2) { + dst_struct_put(st, argv[i], argv[i + 1]); + } + *ret = dst_wrap_struct(dst_struct_end(st)); + return 0; +} + +int dst_stl_get(int32_t argn, DstValue *argv, DstValue *ret) { + int32_t i; + DstValue ds; + if (argn < 1) { + *ret = dst_cstringv("expected at least 1 argument"); + return 1; + } + ds = argv[0]; + for (i = 1; i < argn; i++) { + ds = dst_get(ds, argv[i]); + if (dst_checktype(ds, DST_NIL)) + break; + } + *ret = ds; + return 0; +} + +int dst_stl_put(int32_t argn, DstValue *argv, DstValue *ret) { + DstValue ds, key, value; + if (argn < 3) { + *ret = dst_cstringv("expected at least 3 arguments"); + return 1; + } + if (dst_stl_get(argn - 2, argv, ret)) { + return 1; + } + ds = *ret; + key = argv[argn - 2]; + value = argv[argn - 1]; + dst_put(ds, key, value); + return 0; +} + +static int dst_stl_equal(int32_t argn, DstValue *argv, DstValue *ret) { + int32_t i; + for (i = 0; i < argn - 1; i++) { + if (!dst_equals(argv[i], argv[i+1])) { + *ret = dst_wrap_false(); + return 0; + } + } + *ret = dst_wrap_true(); + return 0; +} + +static int dst_stl_notequal(int32_t argn, DstValue *argv, DstValue *ret) { + int32_t i; + for (i = 0; i < argn - 1; i++) { + if (dst_equals(argv[i], argv[i+1])) { + *ret = dst_wrap_true(); + return 0; + } + } + *ret = dst_wrap_false(); + return 0; +} + +#define DST_DEFINE_COMPARATOR(name, pred)\ +static int dst_stl_##name(int32_t argn, DstValue *argv, DstValue *ret) {\ + int32_t i;\ + for (i = 0; i < argn - 1; i++) {\ + if (dst_compare(argv[i], argv[i+1]) pred) {\ + *ret = dst_wrap_false();\ + return 0;\ + }\ }\ - if (dst_checktype(lhs, DST_NIL))\ - dst_c_throwc(vm, "expected number");\ - dst_vm_fiber->ret = lhs; + *ret = dst_wrap_true();\ return 0;\ } -SIMPLE_ACCUM_FUNCTION(add, +) -SIMPLE_ACCUM_FUNCTION(mul, *) -SIMPLE_ACCUM_FUNCTION(sub, -) +DST_DEFINE_COMPARATOR(ascending, >= 0) +DST_DEFINE_COMPARATOR(descending, <= 0) +DST_DEFINE_COMPARATOR(notdescending, > 0) +DST_DEFINE_COMPARATOR(notascending, < 0) -/* Detect division by zero */ -MAKE_BINOP(div, /) -int dst_stl_div(Dst *vm) { - DstValue lhs, rhs; - uint32_t j, count; - count = dst_args(vm); - lhs = dst_arg(vm, 0); - for (j = 1; j < count; ++j) { - rhs = dst_arg(vm, j); - if (lhs.type == DST_INTEGER && rhs.type == DST_INTEGER && rhs.as.integer == 0) - dst_c_throwc(vm, "cannot integer divide by 0"); - lhs = dst_stl_binop_div(lhs, rhs); +static DstReg stl[] = { + {"print", dst_stl_print}, + {"table", dst_stl_table}, + {"array", dst_stl_array}, + {"tuple", dst_stl_tuple}, + {"struct", dst_stl_struct}, + {"asm", dst_stl_asm}, + {"get", dst_stl_get}, + {"put", dst_stl_put}, + {"+", dst_add}, + {"-", dst_subtract}, + {"*", dst_multiply}, + {"/", dst_divide}, + {"%", dst_modulo}, + {"cos", dst_cos}, + {"sin", dst_sin}, + {"tan", dst_tan}, + {"acos", dst_acos}, + {"asin", dst_asin}, + {"atan", dst_atan}, + {"exp", dst_exp}, + {"log", dst_log}, + {"log10", dst_log10}, + {"sqrt", dst_sqrt}, + {"floor", dst_floor}, + {"ceil", dst_ceil}, + {"=", dst_stl_equal}, + {"not=", dst_stl_notequal}, + {"<", dst_stl_ascending}, + {">", dst_stl_descending}, + {"<=", dst_stl_notdescending}, + {">=", dst_stl_notascending} +}; + +DstValue dst_loadstl(int flags) { + DstValue ret = dst_loadreg(stl, sizeof(stl)/sizeof(DstReg)); + if (flags & DST_LOAD_ROOT) { + dst_gcroot(ret); } - if (lhs.type == DST_NIL) - dst_c_throwc(vm, "expected number"); - dst_c_return(vm, lhs); -} - -#undef SIMPLE_ACCUM_FUNCTION - -#define BITWISE_FUNCTION(name, op) \ -int dst_stl_##name(Dst *vm) {\ - DstValue ret;\ - uint32_t i, count;\ - count = dst_args(vm);\ - ret = dst_arg(vm, 0);\ - if (ret.type != DST_INTEGER) {\ - dst_c_throwc(vm, "expected integer");\ - }\ - if (count < 2) {\ - dst_c_return(vm, ret);\ - }\ - for (i = 1; i < count; ++i) {\ - DstValue next = dst_arg(vm, i);\ - if (next.type != DST_INTEGER) {\ - dst_c_throwc(vm, "expected integer");\ - }\ - ret.as.integer = ret.as.integer op next.as.integer;\ - }\ - dst_c_return(vm, ret);\ -} - -BITWISE_FUNCTION(band, &) -BITWISE_FUNCTION(bor, |) -BITWISE_FUNCTION(bxor, ^) -BITWISE_FUNCTION(blshift, <<) -BITWISE_FUNCTION(brshift, >>) - -#undef BITWISE_FUNCTION - -int dst_stl_bnot(Dst *vm) { - DstValue in = dst_arg(vm, 0); - uint32_t count = dst_args(vm); - if (count != 1 || in.type != DST_INTEGER) { - dst_c_throwc(vm, "expected 1 integer argument"); - } - in.as.integer = ~in.as.integer; - dst_c_return(vm, in); -} - -#define COMPARE_FUNCTION(name, check)\ -int dst_stl_##name(Dst *vm) {\ - DstValue ret;\ - uint32_t i, count;\ - count = dst_args(vm);\ - ret.as.boolean = 1;\ - ret.type = DST_BOOLEAN;\ - if (count < 2) {\ - dst_c_return(vm, ret);\ - }\ - for (i = 1; i < count; ++i) {\ - DstValue lhs = dst_arg(vm, i - 1);\ - DstValue rhs = dst_arg(vm, i);\ - if (!(check)) {\ - ret.as.boolean = 0;\ - break;\ - }\ - }\ - dst_c_return(vm, ret);\ -} - -COMPARE_FUNCTION(lessthan, dst_compare(lhs, rhs) < 0) -COMPARE_FUNCTION(greaterthan, dst_compare(lhs, rhs) > 0) -COMPARE_FUNCTION(equal, dst_equals(lhs, rhs)) -COMPARE_FUNCTION(notequal, !dst_equals(lhs, rhs)) -COMPARE_FUNCTION(lessthaneq, dst_compare(lhs, rhs) <= 0) -COMPARE_FUNCTION(greaterthaneq, dst_compare(lhs, rhs) >= 0) - -#undef COMPARE_FUNCTION - -/* Boolean not */ -int dst_stl_not(Dst *vm) { - dst_c_return(vm, boolean(!dst_truthy(dst_arg(vm, 0)))); -} - -/****/ -/* Core */ -/****/ - -/* Empty a mutable datastructure */ -int dst_stl_clear(Dst *vm) { - DstValue x = dst_arg(vm, 0); - switch (x.type) { - default: - dst_c_throwc(vm, "cannot clear"); - case DST_ARRAY: - x.as.array->count = 0; - break; - case DST_BUFFER: - x.as.buffer->count = 0; - break; - case DST_TABLE: - dst_table_clear(x.as.table); - break; - } - dst_c_return(vm, x); -} - -/* Get length of object */ -int dst_stl_length(Dst *vm) { - dst_set_integer(vm, 0, dst_length(vm, 0)); - dst_return(vm, 0); - return 0; -} - -/* Get hash of a value */ -int dst_stl_hash(Dst *vm) { - dst_set_integer(vm, 0, dst_hash(vm, 0);); - dst_return(vm, 0); - return 0; -} - -/* Convert to integer */ -int dst_stl_to_int(Dst *vm) { - DstValue x = dst_arg(vm, 0); - if (x.type == DST_INTEGER) dst_c_return(vm, x); - if (x.type == DST_REAL) - dst_c_return(vm, integer((DstInteger) x.as.real)); - else - dst_c_throwc(vm, "expected number"); -} - -/* Convert to integer */ -int dst_stl_to_real(Dst *vm) { - DstValue x = dst_arg(vm, 0); - if (x.type == DST_REAL) dst_c_return(vm, x); - if (x.type == DST_INTEGER) - dst_c_return(vm, dst_wrap_real((DstReal) x.as.integer)); - else - dst_c_throwc(vm, "expected number"); -} - -/* Get a slice of a sequence */ -int dst_stl_slice(Dst *vm) { - uint32_t count = dst_args(vm); - int32_t from, to; - DstValue x; - const DstValue *data; - const uint8_t *cdata; - uint32_t length; - uint32_t newlength; - DstInteger num; - - /* Get data */ - x = dst_arg(vm, 0); - if (!dst_seq_view(x, &data, &length) && - !dst_chararray_view(x, &cdata, &length)) { - dst_c_throwc(vm, "expected array/tuple/buffer/symbol/string"); - } - - /* Get from index */ - if (count < 2) { - from = 0; - } else { - if (!dst_check_integer(vm, 1, &num)) - dst_c_throwc(vm, DST_EXPECTED_INTEGER); - from = dst_startrange(num, length); - } - - /* Get to index */ - if (count < 3) { - to = length; - } else { - if (!dst_check_integer(vm, 2, &num)) - dst_c_throwc(vm, DST_EXPECTED_INTEGER); - to = dst_endrange(num, length); - } - - /* Check from bad bounds */ - if (from < 0 || to < 0 || to < from) - dst_c_throwc(vm, "index out of bounds"); - - /* Build slice */ - newlength = to - from; - if (x.type == DST_TUPLE) { - DstValue *tup = dst_tuple_begin(vm, newlength); - dst_memcpy(tup, data + from, newlength * sizeof(DstValue)); - dst_c_return(vm, dst_wrap_tuple(dst_tuple_end(vm, tup))); - } else if (x.type == DST_ARRAY) { - DstArray *arr = dst_array(vm, newlength); - arr->count = newlength; - dst_memcpy(arr->data, data + from, newlength * sizeof(DstValue)); - dst_c_return(vm, dst_wrap_array(arr)); - } else if (x.type == DST_STRING) { - dst_c_return(vm, dst_wrap_string(dst_string_b(vm, x.as.string + from, newlength))); - } else if (x.type == DST_SYMBOL) { - dst_c_return(vm, dst_wrap_symbol(dst_string_b(vm, x.as.string + from, newlength))); - } else { /* buffer */ - DstBuffer *b = dst_buffer(vm, newlength); - dst_memcpy(b->data, x.as.buffer->data, newlength); - b->count = newlength; - dst_c_return(vm, dst_wrap_buffer(b)); - } -} - -/* Create array */ -int dst_stl_array(Dst *vm) { - uint32_t i; - uint32_t count = dst_args(vm); - DstArray *array = dst_array(vm, count); - for (i = 0; i < count; ++i) - array->data[i] = dst_arg(vm, i); - dst_c_return(vm, dst_wrap_array(array)); -} - -/* Create tuple */ -int dst_stl_tuple(Dst *vm) { - uint32_t i; - uint32_t count = dst_args(vm); - DstValue *tuple= dst_tuple_begin(vm, count); - for (i = 0; i < count; ++i) - tuple[i] = dst_arg(vm, i); - dst_c_return(vm, dst_wrap_tuple(dst_tuple_end(vm, tuple))); -} - -/* Create object */ -int dst_stl_table(Dst *vm) { - uint32_t i; - uint32_t count = dst_args(vm); - DstTable *table; - if (count % 2 != 0) - dst_c_throwc(vm, "expected even number of arguments"); - table = dst_table(vm, 4 * count); - for (i = 0; i < count; i += 2) - dst_table_put(vm, table, dst_arg(vm, i), dst_arg(vm, i + 1)); - dst_c_return(vm, dst_wrap_table(table)); -} - -/* Create struct */ -int dst_stl_struct(Dst *vm) { - uint32_t i; - uint32_t count = dst_args(vm); - DstValue *st; - if (count % 2 != 0) - dst_c_throwc(vm, "expected even number of arguments"); - st = dst_struct_begin(vm, count / 2); - for (i = 0; i < count; i += 2) - dst_struct_put(st, dst_arg(vm, i), dst_arg(vm, i + 1)); - dst_c_return(vm, dst_wrap_struct(dst_struct_end(vm, st))); -} - -/* Create a buffer */ -int dst_stl_buffer(Dst *vm) { - uint32_t i, count; - const uint8_t *dat; - uint32_t slen; - DstBuffer *buf = dst_buffer(vm, 10); - count = dst_args(vm); - for (i = 0; i < count; ++i) { - if (dst_chararray_view(dst_arg(vm, i), &dat, &slen)) - dst_buffer_append(vm, buf, dat, slen); - else - dst_c_throwc(vm, DST_EXPECTED_STRING); - } - dst_c_return(vm, dst_wrap_buffer(buf)); -} - -/* Create a string */ -int dst_stl_string(Dst *vm) { - uint32_t j; - uint32_t count = dst_args(vm); - uint32_t length = 0; - uint32_t index = 0; - uint8_t *str; - const uint8_t *dat; - uint32_t slen; - /* Find length and assert string arguments */ - for (j = 0; j < count; ++j) { - if (!dst_chararray_view(dst_arg(vm, j), &dat, &slen)) { - DstValue newarg; - dat = dst_to_string(vm, dst_arg(vm, j)); - slen = dst_string_length(dat); - newarg.type = DST_STRING; - newarg.as.string = dat; - dst_set_arg(vm, j, newarg); - } - length += slen; - } - /* Make string */ - str = dst_string_begin(vm, length); - for (j = 0; j < count; ++j) { - dst_chararray_view(dst_arg(vm, j), &dat, &slen); - dst_memcpy(str + index, dat, slen); - index += slen; - } - dst_c_return(vm, dst_wrap_string(dst_string_end(vm, str))); -} - -/* Create a symbol */ -int dst_stl_symbol(Dst *vm) { - int ret = dst_stl_string(vm); - if (ret == DST_RETURN_OK) { - vm->ret.type = DST_SYMBOL; + if (dst_checktype(ret, DST_TABLE)) { + DstTable *v = dst_table(1); + dst_table_put(v, dst_csymbolv("value"), ret); + dst_put(ret, dst_csymbolv("-env-"), dst_wrap_table(v)); } return ret; } - -/* Create a thread */ -int dst_stl_thread(Dst *vm) { - DstThread *t; - DstValue callee = dst_arg(vm, 0); - DstValue parent = dst_arg(vm, 1); - DstValue errorParent = dst_arg(vm, 2); - t = dst_thread(vm, callee, 10); - if (callee.type != DST_FUNCTION && callee.type != DST_CFUNCTION) - dst_c_throwc(vm, "expected function in thread constructor"); - if (parent.type == DST_THREAD) { - t->parent = parent.as.thread; - } else if (parent.type != DST_NIL) { - dst_c_throwc(vm, "expected thread/nil as parent"); - } else { - t->parent = vm->thread; - } - dst_c_return(vm, dst_wrap_thread(t)); -} - -/* Get current thread */ -int dst_stl_current(Dst *vm) { - dst_c_return(vm, dst_wrap_thread(vm->thread)); -} - -/* Get parent of a thread */ -/* TODO - consider implications of this function - * for sandboxing */ -int dst_stl_parent(Dst *vm) { - DstThread *t; - if (!dst_check_thread(vm, 0, &t)) - dst_c_throwc(vm, "expected thread"); - if (t->parent == NULL) - dst_c_return(vm, dst_wrap_nil()); - dst_c_return(vm, dst_wrap_thread(t->parent)); -} - -/* Get the status of a thread */ -int dst_stl_status(Dst *vm) { - DstThread *t; - const char *cstr; - if (!dst_check_thread(vm, 0, &t)) - dst_c_throwc(vm, "expected thread"); - switch (t->status) { - case DST_THREAD_PENDING: - cstr = "pending"; - break; - case DST_THREAD_ALIVE: - cstr = "alive"; - break; - case DST_THREAD_DEAD: - cstr = "dead"; - break; - case DST_THREAD_ERROR: - cstr = "error"; - break; - } - dst_c_return(vm, dst_string_cv(vm, cstr)); -} - -/* Associative get */ -int dst_stl_get(Dst *vm) { - DstValue ret; - uint32_t count; - const char *err; - count = dst_args(vm); - if (count != 2) - dst_c_throwc(vm, "expects 2 arguments"); - err = dst_get(dst_arg(vm, 0), dst_arg(vm, 1), &ret); - if (err != NULL) - dst_c_throwc(vm, err); - else - dst_c_return(vm, ret); -} - -/* Associative set */ -int dst_stl_set(Dst *vm) { - uint32_t count; - const char *err; - count = dst_args(vm); - if (count != 3) - dst_c_throwc(vm, "expects 3 arguments"); - err = dst_set(vm, dst_arg(vm, 0), dst_arg(vm, 1), dst_arg(vm, 2)); - if (err != NULL) - dst_c_throwc(vm, err); - else - dst_c_return(vm, dst_arg(vm, 0)); -} - -/* Push to end of array */ -int dst_stl_push(Dst *vm) { - DstValue ds = dst_arg(vm, 0); - if (ds.type != DST_ARRAY) - dst_c_throwc(vm, "expected array"); - dst_array_push(vm, ds.as.array, dst_arg(vm, 1)); - dst_c_return(vm, ds); -} - -/* Pop from end of array */ -int dst_stl_pop(Dst *vm) { - DstValue ds = dst_arg(vm, 0); - if (ds.type != DST_ARRAY) - dst_c_throwc(vm, "expected array"); - dst_c_return(vm, dst_array_pop(ds.as.array)); -} - -/* Peek at end of array */ -int dst_stl_peek(Dst *vm) { - DstValue ds = dst_arg(vm, 0); - if (ds.type != DST_ARRAY) - dst_c_throwc(vm, "expected array"); - dst_c_return(vm, dst_array_peek(ds.as.array)); -} - -/* Ensure array capacity */ -int dst_stl_ensure(Dst *vm) { - DstValue ds = dst_arg(vm, 0); - DstValue cap = dst_arg(vm, 1); - if (ds.type != DST_ARRAY) - dst_c_throwc(vm, "expected array"); - if (cap.type != DST_INTEGER) - dst_c_throwc(vm, DST_EXPECTED_INTEGER); - dst_array_ensure(vm, ds.as.array, (uint32_t) cap.as.integer); - dst_c_return(vm, ds); -} - -/* Get next key in struct or table */ -int dst_stl_next(Dst *vm) { - DstValue ds = dst_arg(vm, 0); - DstValue key = dst_arg(vm, 1); - if (ds.type == DST_TABLE) { - dst_c_return(vm, dst_table_next(ds.as.table, key)); - } else if (ds.type == DST_STRUCT) { - dst_c_return(vm, dst_struct_next(ds.as.st, key)); - } else { - dst_c_throwc(vm, "expected table or struct"); - } -} - -/* Print values for inspection */ -int dst_stl_print(Dst *vm) { - uint32_t j, count; - count = dst_args(vm); - for (j = 0; j < count; ++j) { - uint32_t i; - const uint8_t *string = dst_to_string(vm, dst_arg(vm, j)); - uint32_t len = dst_string_length(string); - for (i = 0; i < len; ++i) - fputc(string[i], stdout); - } - fputc('\n', stdout); - return DST_RETURN_OK; -} - -/* Long description */ -int dst_stl_description(Dst *vm) { - DstValue x = dst_arg(vm, 0); - const uint8_t *buf = dst_description(vm, x); - dst_c_return(vm, dst_wrap_string(buf)); -} - -/* Short description */ -int dst_stl_short_description(Dst *vm) { - DstValue x = dst_arg(vm, 0); - const uint8_t *buf = dst_short_description(vm, x); - dst_c_return(vm, dst_wrap_string(buf)); -} - -/* Exit */ -int dst_stl_exit(Dst *vm) { - int ret; - DstValue x = dst_arg(vm, 0); - ret = x.type == DST_INTEGER ? x.as.integer : (x.type == DST_REAL ? x.as.real : 0); - exit(ret); - return DST_RETURN_OK; -} - -/* Throw error */ -int dst_stl_error(Dst *vm) { - dst_c_throw(vm, dst_arg(vm, 0)); -} - -/***/ -/* Function reflection */ -/***/ - -int dst_stl_funcenv(Dst *vm) { - DstFunction *fn; - if (!dst_check_function(vm, 0, &fn)) - dst_c_throwc(vm, "expected function"); - if (fn->env) - dst_c_return(vm, dst_wrap_funcenv(fn->env)); - else - return DST_RETURN_OK; -} - -int dst_stl_funcdef(Dst *vm) { - DstFunction *fn; - if (!dst_check_function(vm, 0, &fn)) - dst_c_throwc(vm, "expected function"); - dst_c_return(vm, dst_wrap_funcdef(fn->def)); -} - -int dst_stl_funcparent(Dst *vm) { - DstFunction *fn; - if (!dst_check_function(vm, 0, &fn)) - dst_c_throwc(vm, "expected function"); - if (fn->parent) - dst_c_return(vm, dst_wrap_function(fn->parent)); - else - return DST_RETURN_OK; -} - -int dst_stl_def(Dst *vm) { - DstValue key = dst_arg(vm, 0); - if (dst_args(vm) != 2) { - dst_c_throwc(vm, "expected 2 arguments to global-def"); - } - if (key.type != DST_STRING && key.type != DST_SYMBOL) { - dst_c_throwc(vm, "expected string/symbol as first argument"); - } - key.type = DST_SYMBOL; - dst_env_put(vm, vm->env, key, dst_arg(vm, 1)); - dst_c_return(vm, dst_arg(vm, 1)); -} - -int dst_stl_var(Dst *vm) { - DstValue key = dst_arg(vm, 0); - if (dst_args(vm) != 2) { - dst_c_throwc(vm, "expected 2 arguments to global-var"); - } - if (key.type != DST_STRING && key.type != DST_SYMBOL) { - dst_c_throwc(vm, "expected string as first argument"); - } - key.type = DST_SYMBOL; - dst_env_putvar(vm, vm->env, key, dst_arg(vm, 1)); - dst_c_return(vm, dst_arg(vm, 1)); -} - -/****/ -/* IO */ -/****/ - -/* File type definition */ -static DstUserType dst_stl_filetype = { - "stl.file", - NULL, - NULL, - NULL, - NULL -}; - -/* Open a a file and return a userdata wrapper arounf the C file API. */ -int dst_stl_open(Dst *vm) { - const uint8_t *fname = dst_to_string(vm, dst_arg(vm, 0)); - const uint8_t *fmode = dst_to_string(vm, dst_arg(vm, 1)); - FILE *f; - FILE **fp; - if (dst_args(vm) < 2 || dst_arg(vm, 0).type != DST_STRING - || dst_arg(vm, 1).type != DST_STRING) - dst_c_throwc(vm, "expected filename and filemode"); - f = fopen((const char *)fname, (const char *)fmode); - if (!f) - dst_c_throwc(vm, "could not open file"); - fp = dst_userdata(vm, sizeof(FILE *), &dst_stl_filetype); - *fp = f; - dst_c_return(vm, dst_wrap_userdata(fp)); -} - -/* Read an entire file into memory */ -int dst_stl_slurp(Dst *vm) { - DstBuffer *b; - long fsize; - FILE *f; - FILE **fp = dst_check_userdata(vm, 0, &dst_stl_filetype); - if (fp == NULL) dst_c_throwc(vm, "expected file"); - if (!dst_check_buffer(vm, 1, &b)) b = dst_buffer(vm, 10); - f = *fp; - /* Read whole file */ - fseek(f, 0, SEEK_END); - fsize = ftell(f); - fseek(f, 0, SEEK_SET); - /* Ensure buffer size */ - dst_buffer_ensure(vm, b, b->count + fsize); - fread((char *)(b->data + b->count), fsize, 1, f); - b->count += fsize; - dst_c_return(vm, dst_wrap_buffer(b)); -} - -/* Read a certain number of bytes into memory */ -int dst_stl_read(Dst *vm) { - DstBuffer *b; - FILE *f; - int64_t len; - FILE **fp = dst_check_userdata(vm, 0, &dst_stl_filetype); - if (fp == NULL) dst_c_throwc(vm, "expected file"); - if (!(dst_check_integer(vm, 1, &len))) dst_c_throwc(vm, "expected integer"); - if (!dst_check_buffer(vm, 2, &b)) b = dst_buffer(vm, 10); - f = *fp; - /* Ensure buffer size */ - dst_buffer_ensure(vm, b, b->count + len); - b->count += fread((char *)(b->data + b->count), len, 1, f) * len; - dst_c_return(vm, dst_wrap_buffer(b)); -} - -/* Write bytes to a file */ -int dst_stl_write(Dst *vm) { - FILE *f; - const uint8_t *data; - uint32_t len; - FILE **fp = dst_check_userdata(vm, 0, &dst_stl_filetype); - if (fp == NULL) dst_c_throwc(vm, "expected file"); - if (!dst_chararray_view(dst_arg(vm, 1), &data, &len)) dst_c_throwc(vm, "expected string|buffer"); - f = *fp; - fwrite(data, len, 1, f); - return DST_RETURN_OK; -} - -/* Close a file */ -int dst_stl_close(Dst *vm) { - FILE **fp = dst_check_userdata(vm, 0, &dst_stl_filetype); - if (fp == NULL) dst_c_throwc(vm, "expected file"); - fclose(*fp); - dst_c_return(vm, dst_wrap_nil()); -} - -/****/ -/* Temporary */ -/****/ - -/* Force garbage collection */ -int dst_stl_gcollect(Dst *vm) { - dst_collect(vm); - return DST_RETURN_OK; -} - -/***/ -/* Compilation */ -/***/ - -/* Generate a unique symbol */ -static int dst_stl_gensym(Dst *vm) { - DstValue source = dst_arg(vm, 0); - const uint8_t *sym = NULL; - uint32_t len; - const uint8_t *data; - if (source.type == DST_NIL) { - sym = dst_string_cu(vm, ""); - } else if (dst_chararray_view(source, &data, &len)) { - sym = dst_string_bu(vm, data, len); - } else { - dst_c_throwc(vm, "exepcted string/buffer/symbol/nil"); - } - dst_c_return(vm, dst_wrap_symbol(sym)); -} - -/* Compile a value */ -static int dst_stl_compile(Dst *vm) { - DstTable *env = vm->env; - if (dst_arg(vm, 1).type == DST_TABLE) { - env = dst_arg(vm, 1).as.table; - } - dst_c_return(vm, dst_compile(vm, env, dst_arg(vm, 0))); -} - -/* Get vm->env */ -static int dst_stl_getenv(Dst *vm) { - dst_c_return(vm, dst_wrap_table(vm->env)); -} - -/* Set vm->env */ -static int dst_stl_setenv(Dst *vm) { - DstValue newEnv = dst_arg(vm, 0); - if (newEnv.type != DST_TABLE) { - dst_c_throwc(vm, "expected table"); - } - vm->env = newEnv.as.table; - return DST_RETURN_OK; -} - -/****/ -/* Bootstraping */ -/****/ - -static const DstModuleItem std_module[] = { - /* Arithmetic */ - {"+", dst_stl_add}, - {"*", dst_stl_mul}, - {"-", dst_stl_sub}, - {"/", dst_stl_div}, - /* Comparisons */ - {"<", dst_stl_lessthan}, - {">", dst_stl_greaterthan}, - {"=", dst_stl_equal}, - {"not=", dst_stl_notequal}, - {"<=", dst_stl_lessthaneq}, - {">=", dst_stl_greaterthaneq}, - /* Bitwise arithmetic */ - {"band", dst_stl_band}, - {"bor", dst_stl_bor}, - {"bxor", dst_stl_bxor}, - {"blshift", dst_stl_blshift}, - {"brshift", dst_stl_brshift}, - {"bnot", dst_stl_bnot}, - /* IO */ - {"open", dst_stl_open}, - {"slurp", dst_stl_slurp}, - {"read", dst_stl_read}, - {"write", dst_stl_write}, - /* Compile */ - {"gensym", dst_stl_gensym}, - {"getenv", dst_stl_getenv}, - {"setenv", dst_stl_setenv}, - {"compile", dst_stl_compile}, - /* Other */ - {"not", dst_stl_not}, - {"clear", dst_stl_clear}, - {"length", dst_stl_length}, - {"hash", dst_stl_hash}, - {"integer", dst_stl_to_int}, - {"real", dst_stl_to_real}, - {"type", dst_stl_type}, - {"slice", dst_stl_slice}, - {"array", dst_stl_array}, - {"tuple", dst_stl_tuple}, - {"table", dst_stl_table}, - {"struct", dst_stl_struct}, - {"buffer", dst_stl_buffer}, - {"string", dst_stl_string}, - {"symbol", dst_stl_symbol}, - {"thread", dst_stl_thread}, - {"status", dst_stl_status}, - {"current", dst_stl_current}, - {"parent", dst_stl_parent}, - {"print", dst_stl_print}, - {"description", dst_stl_description}, - {"short-description", dst_stl_short_description}, - {"exit!", dst_stl_exit}, - {"get", dst_stl_get}, - {"set!", dst_stl_set}, - {"next", dst_stl_next}, - {"error", dst_stl_error}, - {"push!", dst_stl_push}, - {"pop!", dst_stl_pop}, - {"peek", dst_stl_peek}, - {"ensure!", dst_stl_ensure}, - {"open", dst_stl_open}, - {"slurp", dst_stl_slurp}, - {"read", dst_stl_read}, - {"write", dst_stl_write}, - {"close", dst_stl_close}, - {"funcenv", dst_stl_funcenv}, - {"funcdef", dst_stl_funcdef}, - {"funcparent", dst_stl_funcparent}, - {"gcollect", dst_stl_gcollect}, - {"global-def", dst_stl_def}, - {"global-var", dst_stl_var}, - {NULL, NULL} -}; - -/* Load stl library into the current environment. Create stl module object - * only if it is not yet created. */ -void dst_stl_load(Dst *vm) { - DstValue maybeEnv = dst_table_get(vm->modules, dst_string_cvs(vm, "std")); - if (maybeEnv.type == DST_TABLE) { - /* Module already created, so merge into main vm. */ - dst_env_merge(vm, vm->env, maybeEnv.as.table); - } else { - /* Module not yet created */ - /* Load the normal c functions */ - dst_module_mutable(vm, "std", std_module); - /* Wrap stdin and stdout */ - FILE **inp = dst_userdata(vm, sizeof(FILE *), &dst_stl_filetype); - FILE **outp = dst_userdata(vm, sizeof(FILE *), &dst_stl_filetype); - FILE **errp = dst_userdata(vm, sizeof(FILE *), &dst_stl_filetype); - *inp = stdin; - *outp = stdout; - *errp = stderr; - dst_module_put(vm, "std", "stdin", dst_wrap_userdata(inp)); - dst_module_put(vm, "std", "stdout", dst_wrap_userdata(outp)); - dst_module_put(vm, "std", "stderr", dst_wrap_userdata(outp)); - /* Now merge */ - maybeEnv = dst_table_get(vm->modules, dst_string_cvs(vm, "std")); - dst_env_merge(vm, vm->env, maybeEnv.as.table); - } -} diff --git a/core/string.c b/core/string.c index 12aceefe..976f2610 100644 --- a/core/string.c +++ b/core/string.c @@ -363,23 +363,41 @@ static int is_print_ds(DstValue v) { /* VT100 Colors for types */ /* TODO - generalize into configurable headers and footers */ +/* + DST_NIL, + DST_FALSE, + DST_TRUE, + DST_FIBER, + DST_INTEGER, + DST_REAL, + DST_STRING, + DST_SYMBOL, + DST_ARRAY, + DST_TUPLE, + DST_TABLE, + DST_STRUCT, + DST_BUFFER, + DST_FUNCTION, + DST_CFUNCTION, + DST_USERDATA +*/ static const char *dst_type_colors[16] = { "\x1B[35m", - "\x1B[33m", - "\x1B[33m", "\x1B[35m", "\x1B[35m", - "\x1B[32m", + "", + "\x1B[33m", + "\x1B[33m", "\x1B[36m", "", "", "", "", - "\x1B[37m", - "\x1B[37m", - "\x1B[37m", - "\x1B[37m", - "\x1B[37m" + "", + "", + "", + "", + "" }; /* Forward declaration */ @@ -629,6 +647,13 @@ const uint8_t *dst_formatc(const char *format, ...) { dst_description_helper(&printer, va_arg(args, DstValue)); break; } + case 'C': + { + dst_printer_defaults(&printer); + printer.flags |= DST_PRINTFLAG_COLORIZE; + dst_description_helper(&printer, va_arg(args, DstValue)); + break; + } case 'q': { const uint8_t *str = va_arg(args, const uint8_t *); diff --git a/core/strtod.c b/core/strtod.c index fb6ffc10..bb477394 100644 --- a/core/strtod.c +++ b/core/strtod.c @@ -70,7 +70,7 @@ static uint8_t digit_lookup[128] = { /* Read in a mantissa and exponent of a certain base, and give * back the double value. Should properly handle 0s, Inifinties, and * denormalized numbers. (When the exponent values are too large) */ -static double dst_convert_mantissa_exp( +static double convert( int negative, uint64_t mantissa, int32_t base, @@ -117,41 +117,58 @@ static double dst_convert_mantissa_exp( } } } - - /* Build the number to return */ - return ldexp(mantissa, exponent2); + + return negative + ? -ldexp(mantissa, exponent2) + : ldexp(mantissa, exponent2); } +/* Result of scanning a number source */ +struct DstScanRes { + uint64_t mant; + int32_t ex; + int error; + int base; + int seenpoint; + int foundexp; + int neg; +}; + /* Get the mantissa and exponent of decimal number. The * mantissa will be stored in a 64 bit unsigned integer (always positive). * The exponent will be in a signed 32 bit integer. Will also check if * the decimal point has been seen. Returns -1 if there is an invalid * number. */ -DstValue dst_scan_number( +static struct DstScanRes dst_scan_impl( const uint8_t *str, int32_t len) { + struct DstScanRes res; const uint8_t *end = str + len; - int32_t seenpoint = 0; - uint64_t mant = 0; - int32_t neg = 0; - int32_t ex = 0; - int foundExp = 0; - /* Set some constants */ - int base = 10; + /* Initialize flags */ + int seenadigit = 0; + + /* Initialize result */ + res.mant = 0; + res.ex = 0; + res.error = 0; + res.base = 10; + res.seenpoint = 0; + res.foundexp = 0; + res.neg = 0; /* Prevent some kinds of overflow bugs relating to the exponent * overflowing. For example, if a string was passed 2GB worth of 0s after * the decimal point, exponent could wrap around and become positive. It's * easier to reject ridiculously large inputs than to check for overflows. * */ - if (len > INT32_MAX / base) goto error; + if (len > INT32_MAX / 40) goto error; /* Get sign */ if (str >= end) goto error; if (*str == '-') { - neg = 1; + res.neg = 1; str++; } else if (*str == '+') { str++; @@ -159,53 +176,59 @@ DstValue dst_scan_number( /* Skip leading zeros */ while (str < end && (*str == '0' || *str == '.')) { - if (seenpoint) ex--; + if (res.seenpoint) res.ex--; if (*str == '.') { - if (seenpoint) goto error; - seenpoint = 1; + if (res.seenpoint) goto error; + res.seenpoint = 1; } + seenadigit = 1; str++; } /* Parse significant digits */ while (str < end) { if (*str == '.') { - if (seenpoint) goto error; - seenpoint = 1; + if (res.seenpoint) goto error; + res.seenpoint = 1; } else if (*str == '&') { - foundExp = 1; + res.foundexp = 1; break; - } else if (base == 10 && (*str == 'E' || *str == 'e')) { - foundExp = 1; + } else if (res.base == 10 && (*str == 'E' || *str == 'e')) { + res.foundexp = 1; break; } else if (*str == 'x' || *str == 'X') { - if (seenpoint || mant > 0) goto error; - base = 16; - mant = 0; + if (res.seenpoint || res.mant > 0) goto error; + res.base = 16; + res.mant = 0; } else if (*str == 'r' || *str == 'R') { - if (seenpoint) goto error; - if (mant < 2 || mant > 36) goto error; - base = mant; - mant = 0; + if (res.seenpoint) goto error; + if (res.mant < 2 || res.mant > 36) goto error; + res.base = res.mant; + res.mant = 0; } else if (*str == '_') { ; /* underscores are ignored - can be used for separator */ } else { int digit = digit_lookup[*str & 0x7F]; - if (digit >= base) goto error; - if (seenpoint) ex--; - if (mant > 0x00ffffffffffffff) - ex++; + if (digit >= res.base) goto error; + if (res.seenpoint) res.ex--; + if (res.mant > 0x00ffffffffffffff) + res.ex++; else - mant = base * mant + digit; + res.mant = res.base * res.mant + digit; + seenadigit = 1; } str++; } + if (!seenadigit) + goto error; + /* Read exponent */ - if (str < end && foundExp) { + if (str < end && res.foundexp) { int eneg = 0; int ee = 0; + seenadigit = 0; str++; if (str >= end) goto error; if (*str == '-') { @@ -216,27 +239,81 @@ DstValue dst_scan_number( } /* Skip leading 0s in exponent */ while (str < end && *str == '0') str++; - while (str < end && ee < (INT32_MAX / base - base)) { + while (str < end && ee < (INT32_MAX / 40)) { int digit = digit_lookup[*str & 0x7F]; - if (digit >= base) goto error; - ee = base * ee + digit; + if (digit >= res.base) goto error; + ee = res.base * ee + digit; str++; + seenadigit = 1; } - if (eneg) ex -= ee; else ex += ee; - } else if (!seenpoint) { - /* Check for integer literal */ - int64_t i64 = neg ? -mant : mant; - if (i64 <= INT32_MAX && i64 >= INT32_MIN) - return dst_wrap_integer((int32_t) i64); - } else if (str < end) { + if (eneg) res.ex -= ee; else res.ex += ee; + } + + if (!seenadigit) goto error; - } - /* Convert mantissa and exponent into double */ - return dst_wrap_real(dst_convert_mantissa_exp(neg, mant, base, ex)); + return res; + /* return dst_wrap_real(dst_convert_mantissa_exp(neg, mant, base, ex)); */ error: - return dst_wrap_nil(); - + res.error = 1; + return res; } +/* Scan an integer from a string. If the string cannot be converted into + * and integer, set *err to 1 and return 0. */ +int32_t dst_scan_integer( + const uint8_t *str, + int32_t len, + int *err) { + struct DstScanRes res = dst_scan_impl(str, len); + int64_t i64; + if (res.error) + goto error; + i64 = res.neg ? -res.mant : res.mant; + if (i64 > INT32_MAX || i64 < INT32_MIN) + goto error; + if (NULL != err) + *err = 0; + return (int32_t) i64; + error: + if (NULL != err) + *err = 1; + return 0; +} + +/* Scan a real (double) from a string. If the string cannot be converted into + * and integer, set *err to 1 and return 0. */ +double dst_scan_real( + const uint8_t *str, + int32_t len, + int *err) { + struct DstScanRes res = dst_scan_impl(str, len); + if (res.error) { + if (NULL != err) + *err = 1; + return 0.0; + } else { + if (NULL != err) + *err = 0; + } + return convert(res.neg, res.mant, res.base, res.ex); +} + +/* Scans a number from a string. Can return either an integer or a real if + * the number cannot be represented as an integer. Will return nil in case of + * an error. */ +DstValue dst_scan_number( + const uint8_t *str, + int32_t len) { + struct DstScanRes res = dst_scan_impl(str, len); + if (res.error) + return dst_wrap_nil(); + if (!res.foundexp && !res.seenpoint) { + int64_t i64 = res.neg ? -res.mant : res.mant; + if (i64 <= INT32_MAX && i64 >= INT32_MIN) { + return dst_wrap_integer((int32_t) i64); + } + } + return dst_wrap_real(convert(res.neg, res.mant, res.base, res.ex)); +} diff --git a/core/syscalls.c b/core/syscalls.c deleted file mode 100644 index 500140a4..00000000 --- a/core/syscalls.c +++ /dev/null @@ -1,146 +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 -#include - -int dst_sys_print(DstValue *argv, int32_t argn) { - int32_t i; - for (i = 0; i < argn; ++i) { - int32_t j, len; - const uint8_t *vstr = dst_to_string(argv[i]); - len = dst_string_length(vstr); - for (j = 0; j < len; ++j) { - putc(vstr[j], stdout); - } - } - putc('\n', stdout); - return 0; -} - -int dst_sys_asm(DstValue *argv, int32_t argn) { - DstAssembleOptions opts; - DstAssembleResult res; - if (argn < 1) { - dst_vm_fiber->ret = dst_cstringv("expected assembly source"); - return 1; - } - opts.source = argv[0]; - opts.sourcemap = (argn >= 2 && dst_checktype(argv[1], DST_TUPLE)) - ? dst_unwrap_tuple(argv[1]) - : NULL; - opts.flags = 0; - res = dst_asm(opts); - if (res.status == DST_ASSEMBLE_OK) { - dst_vm_fiber->ret = dst_wrap_function(dst_asm_func(res)); - return 0; - } else { - dst_vm_fiber->ret = dst_wrap_string(res.error); - return 1; - } -} - -int dst_sys_tuple(DstValue *argv, int32_t argn) { - dst_vm_fiber->ret = dst_wrap_tuple(dst_tuple_n(argv, argn)); - return 0; -} - -int dst_sys_array(DstValue *argv, int32_t argn) { - DstArray *array = dst_array(argn); - array->count = argn; - memcpy(array->data, argv, argn * sizeof(DstValue)); - dst_vm_fiber->ret = dst_wrap_array(array); - return 0; -} - -int dst_sys_table(DstValue *argv, int32_t argn) { - int32_t i; - DstTable *table = dst_table(argn/2); - if (argn & 1) { - dst_vm_fiber->ret = dst_cstringv("expected even number of arguments"); - return 1; - } - for (i = 0; i < argn; i += 2) { - dst_table_put(table, argv[i], argv[i + 1]); - } - dst_vm_fiber->ret = dst_wrap_table(table); - return 0; -} - -int dst_sys_struct(DstValue *argv, int32_t argn) { - int32_t i; - DstValue *st = dst_struct_begin(argn/2); - if (argn & 1) { - dst_vm_fiber->ret = dst_cstringv("expected even number of arguments"); - return 1; - } - for (i = 0; i < argn; i += 2) { - dst_struct_put(st, argv[i], argv[i + 1]); - } - dst_vm_fiber->ret = dst_wrap_struct(dst_struct_end(st)); - return 0; -} - -int dst_sys_get(DstValue *argv, int32_t argn) { - int32_t i; - DstValue ds; - if (argn < 1) { - dst_vm_fiber->ret = dst_cstringv("expected at least 1 argument"); - return 1; - } - ds = argv[0]; - for (i = 1; i < argn; i++) { - ds = dst_get(ds, argv[i]); - if (dst_checktype(ds, DST_NIL)) - break; - } - dst_vm_fiber->ret = ds; - return 0; -} - -int dst_sys_put(DstValue *argv, int32_t argn) { - DstValue ds, key, value; - if (argn < 3) { - dst_vm_fiber->ret = dst_cstringv("expected at least 3 arguments"); - return 1; - } - if(dst_sys_get(argv, argn - 2)) - return 1; - ds = dst_vm_fiber->ret; - key = argv[argn - 2]; - value = argv[argn - 1]; - dst_put(ds, key, value); - return 0; -} - -const DstCFunction dst_vm_syscalls[256] = { - dst_sys_print, - dst_sys_asm, - dst_sys_tuple, - dst_sys_array, - dst_sys_struct, - dst_sys_table, - dst_sys_get, - dst_sys_put, - NULL -}; diff --git a/core/vm.c b/core/vm.c index 4342daeb..82bab640 100644 --- a/core/vm.c +++ b/core/vm.c @@ -52,12 +52,13 @@ static int dst_update_fiber() { } /* Start running the VM from where it left off. */ -int dst_continue() { +static int dst_continue(DstValue *returnreg) { /* VM state */ DstValue *stack; uint32_t *pc; DstFunction *func; + DstValue retreg; /* Eventually use computed gotos for more effient vm loop. */ #define vm_next() continue @@ -67,7 +68,7 @@ int dst_continue() { * Pulls out unsigned integers */ #define oparg(shift, mask) (((*pc) >> ((shift) << 3)) & (mask)) -#define vm_throw(e) do { dst_vm_fiber->ret = dst_cstringv((e)); goto vm_error; } while (0) +#define vm_throw(e) do { retreg = dst_cstringv((e)); goto vm_error; } while (0) #define vm_assert(cond, e) do {if (!(cond)) vm_throw((e)); } while (0) #define vm_binop_integer(op) \ @@ -133,7 +134,7 @@ int dst_continue() { vm_next(); case DOP_ERROR: - dst_vm_fiber->ret = stack[oparg(1, 0xFF)]; + retreg = stack[oparg(1, 0xFF)]; goto vm_error; case DOP_TYPECHECK: @@ -143,11 +144,11 @@ int dst_continue() { vm_next(); case DOP_RETURN: - dst_vm_fiber->ret = stack[oparg(1, 0xFFFFFF)]; + retreg = stack[oparg(1, 0xFFFFFF)]; goto vm_return; case DOP_RETURN_NIL: - dst_vm_fiber->ret = dst_wrap_nil(); + retreg = dst_wrap_nil(); goto vm_return; case DOP_ADD_INTEGER: @@ -184,10 +185,10 @@ int dst_continue() { vm_binop(*); case DOP_DIVIDE_INTEGER: - vm_assert(dst_unwrap_integer(stack[oparg(3, 0xFF)]) != 0, "integer divide by zero"); + vm_assert(dst_unwrap_integer(stack[oparg(3, 0xFF)]) != 0, "integer divide error"); vm_assert(!(dst_unwrap_integer(stack[oparg(3, 0xFF)]) == -1 && - dst_unwrap_integer(stack[oparg(2, 0xFF)]) == DST_INTEGER_MIN), - "integer divide overflow"); + dst_unwrap_integer(stack[oparg(2, 0xFF)]) == INT32_MIN), + "integer divide error"); vm_binop_integer(/); case DOP_DIVIDE_IMMEDIATE: @@ -198,9 +199,9 @@ int dst_continue() { * min value by -1). These checks could be omitted if the arg is not * 0 or -1. */ if (op2 == 0) - vm_throw("integer divide by zero"); - if (op2 == -1) - vm_throw("integer divide overflow"); + vm_throw("integer divide error"); + if (op2 == -1 && op1 == INT32_MIN) + vm_throw("integer divide error"); else stack[oparg(1, 0xFF)] = dst_wrap_integer(op1 / op2); pc++; @@ -217,10 +218,10 @@ int dst_continue() { vm_assert(dst_checktype(op1, DST_INTEGER) || dst_checktype(op1, DST_REAL), "expected number"); vm_assert(dst_checktype(op2, DST_INTEGER) || dst_checktype(op2, DST_REAL), "expected number"); if (dst_checktype(op2, DST_INTEGER) && dst_unwrap_integer(op2) == 0) - op2 = dst_wrap_real(0.0); + vm_throw("integer divide error"); if (dst_checktype(op2, DST_INTEGER) && dst_unwrap_integer(op2) == -1 && - dst_checktype(op1, DST_INTEGER) && dst_unwrap_integer(op1) == DST_INTEGER_MIN) - op2 = dst_wrap_real(-1.0); + dst_checktype(op1, DST_INTEGER) && dst_unwrap_integer(op1) == INT32_MIN) + vm_throw("integer divide error"); stack[oparg(1, 0xFF)] = dst_checktype(op1, DST_INTEGER) ? (dst_checktype(op2, DST_INTEGER) ? dst_wrap_integer(dst_unwrap_integer(op1) / dst_unwrap_integer(op2)) @@ -486,10 +487,11 @@ int dst_continue() { vm_checkgc_next(); } else if (dst_checktype(callee, DST_CFUNCTION)) { dst_fiber_cframe(dst_vm_fiber); - dst_vm_fiber->ret = dst_wrap_nil(); + retreg = dst_wrap_nil(); if (dst_unwrap_cfunction(callee)( + dst_vm_fiber->frametop - dst_vm_fiber->frame, dst_vm_fiber->data + dst_vm_fiber->frame, - dst_vm_fiber->frametop - dst_vm_fiber->frame)) { + &retreg)) { goto vm_error; } goto vm_return_cfunc; @@ -508,10 +510,11 @@ int dst_continue() { vm_checkgc_next(); } else if (dst_checktype(callee, DST_CFUNCTION)) { dst_fiber_cframe_tail(dst_vm_fiber); - dst_vm_fiber->ret = dst_wrap_nil(); + retreg = dst_wrap_nil(); if (dst_unwrap_cfunction(callee)( - dst_vm_fiber->data + dst_vm_fiber->frame, - dst_vm_fiber->frametop - dst_vm_fiber->frame)) { + dst_vm_fiber->frametop - dst_vm_fiber->frame, + dst_vm_fiber->data + dst_vm_fiber->frame, + &retreg)) { goto vm_error; } goto vm_return_cfunc; @@ -519,34 +522,12 @@ int dst_continue() { vm_throw("expected function"); } - case DOP_SYSCALL: - { - DstCFunction f = dst_vm_syscalls[oparg(2, 0xFF)]; - vm_assert(NULL != f, "invalid syscall"); - dst_fiber_cframe(dst_vm_fiber); - dst_vm_fiber->ret = dst_wrap_nil(); - if (f(dst_vm_fiber->data + dst_vm_fiber->frame, - dst_vm_fiber->frametop - dst_vm_fiber->frame)) { - goto vm_error; - } - goto vm_return_cfunc; - } - - case DOP_LOAD_SYSCALL: - { - DstCFunction f = dst_vm_syscalls[oparg(2, 0xFF)]; - vm_assert(NULL != f, "invalid syscall"); - stack[oparg(1, 0xFF)] = dst_wrap_cfunction(f); - pc++; - vm_next(); - } - case DOP_TRANSFER: { DstFiber *nextfiber; DstStackFrame *frame = dst_stack_frame(stack); DstValue temp = stack[oparg(2, 0xFF)]; - DstValue retvalue = stack[oparg(3, 0xFF)]; + retreg = stack[oparg(3, 0xFF)]; vm_assert(dst_checktype(temp, DST_FIBER) || dst_checktype(temp, DST_NIL), "expected fiber"); nextfiber = dst_checktype(temp, DST_FIBER) @@ -555,7 +536,7 @@ int dst_continue() { /* Check for root fiber */ if (NULL == nextfiber) { frame->pc = pc; - dst_vm_fiber->ret = retvalue; + *returnreg = retreg; return 0; } vm_assert(nextfiber->status == DST_FIBER_PENDING, "can only transfer to pending fiber"); @@ -563,7 +544,7 @@ int dst_continue() { dst_vm_fiber->status = DST_FIBER_PENDING; dst_vm_fiber = nextfiber; vm_init_fiber_state(); - stack[oparg(1, 0xFF)] = retvalue; + stack[oparg(1, 0xFF)] = retreg; pc++; vm_next(); } @@ -577,7 +558,7 @@ int dst_continue() { case DOP_PUT_INDEX: dst_setindex(stack[oparg(1, 0xFF)], - stack[oparg(3, 0xFF)], + stack[oparg(2, 0xFF)], oparg(3, 0xFF)); ++pc; vm_next(); @@ -599,11 +580,12 @@ int dst_continue() { /* Return from c function. Simpler than retuning from dst function */ vm_return_cfunc: { - DstValue ret = dst_vm_fiber->ret; dst_fiber_popframe(dst_vm_fiber); - if (dst_update_fiber()) + if (dst_update_fiber()) { + *returnreg = retreg; return 0; - stack[oparg(1, 0xFF)] = ret; + } + stack[oparg(1, 0xFF)] = retreg; pc++; vm_checkgc_next(); } @@ -611,13 +593,14 @@ int dst_continue() { /* Handle returning from stack frame. Expect return value in fiber->ret */ vm_return: { - DstValue ret = dst_vm_fiber->ret; dst_fiber_popframe(dst_vm_fiber); - if (dst_update_fiber()) + if (dst_update_fiber()) { + *returnreg = retreg; return 0; + } stack = dst_vm_fiber->data + dst_vm_fiber->frame; pc = dst_stack_frame(stack)->pc; - stack[oparg(1, 0xFF)] = ret; + stack[oparg(1, 0xFF)] = retreg; pc++; vm_checkgc_next(); } @@ -625,13 +608,14 @@ int dst_continue() { /* Handle errors from c functions and vm opcodes */ vm_error: { - DstValue ret = dst_vm_fiber->ret; dst_vm_fiber->status = DST_FIBER_ERROR; - if (dst_update_fiber()) + if (dst_update_fiber()) { + *returnreg = retreg; return 1; + } stack = dst_vm_fiber->data + dst_vm_fiber->frame; pc = dst_stack_frame(stack)->pc; - stack[oparg(1, 0xFF)] = ret; + stack[oparg(1, 0xFF)] = retreg; pc++; vm_checkgc_next(); } @@ -654,21 +638,24 @@ int dst_continue() { /* Run the vm with a given function. This function is * called to start the vm. */ -int dst_run(DstValue callee) { +int dst_run(DstValue callee, DstValue *returnreg) { if (NULL == dst_vm_fiber) { dst_vm_fiber = dst_fiber(0); } else { dst_fiber_reset(dst_vm_fiber); } if (dst_checktype(callee, DST_CFUNCTION)) { - dst_vm_fiber->ret = dst_wrap_nil(); + *returnreg = dst_wrap_nil(); dst_fiber_cframe(dst_vm_fiber); - return dst_unwrap_cfunction(callee)(dst_vm_fiber->data + dst_vm_fiber->frame, 0); + return dst_unwrap_cfunction(callee)( + 0, + dst_vm_fiber->data + dst_vm_fiber->frame, + returnreg); } else if (dst_checktype(callee, DST_FUNCTION)) { dst_fiber_funcframe(dst_vm_fiber, dst_unwrap_function(callee)); - return dst_continue(); + return dst_continue(returnreg); } - dst_vm_fiber->ret = dst_cstringv("expected function"); + *returnreg = dst_cstringv("expected function"); return 1; } @@ -681,7 +668,7 @@ int dst_init() { * a collection pretty much every cycle, which is * horrible for performance, but helps ensure * there are no memory bugs during dev */ - dst_vm_memory_interval = 0x0000000; + dst_vm_gc_interval = 0x0000000; dst_symcache_init(); /* Set thread */ dst_vm_fiber = NULL; diff --git a/dsttest/basic.dst b/dsttest/basic.dst index b825aa40..9bdd6bb8 100644 --- a/dsttest/basic.dst +++ b/dsttest/basic.dst @@ -1,4 +1,5 @@ -# A really basic for to compile. for testing the compiler. Will extend +# A really basic form to compile for testing the compiler. Will extend # as compiler is extended. -(∑ 1 2 3) - +(print (+ + (* 1 2 3 4 5 6 7 8 9 10) + -12839189321)) diff --git a/include/dst/dst.h b/include/dst/dst.h index 57dc1147..a390cfdf 100644 --- a/include/dst/dst.h +++ b/include/dst/dst.h @@ -28,553 +28,9 @@ #include #include -/* - * Detect OS and endianess. - * From webkit source. - */ - -/* Check Unix */ -#if defined(_AIX) \ - || defined(__APPLE__) /* Darwin */ \ - || defined(__FreeBSD__) || defined(__DragonFly__) \ - || defined(__FreeBSD_kernel__) \ - || defined(__GNU__) /* GNU/Hurd */ \ - || defined(__linux__) \ - || defined(__NetBSD__) \ - || defined(__OpenBSD__) \ - || defined(__QNXNTO__) \ - || defined(sun) || defined(__sun) /* Solaris */ \ - || defined(unix) || defined(__unix) || defined(__unix__) -#define DST_UNIX 1 -#endif - -/* Check Windows */ -#if defined(WIN32) || defined(_WIN32) -#define DST_WINDOWS 1 -#endif - -/* Check 64-bit vs 32-bit */ -#if ((defined(__x86_64__) || defined(_M_X64)) \ - && (defined(DST_UNIX) || defined(DST_WINDOWS))) \ - || (defined(__ia64__) && defined(__LP64__)) /* Itanium in LP64 mode */ \ - || defined(__alpha__) /* DEC Alpha */ \ - || (defined(__sparc__) && defined(__arch64__) || defined (__sparcv9)) /* BE */ \ - || defined(__s390x__) /* S390 64-bit (BE) */ \ - || (defined(__ppc64__) || defined(__PPC64__)) \ - || defined(__aarch64__) /* ARM 64-bit */ -#define DST_64 1 -#else -#define DST_32 1 -#endif - -/* Check big endian */ -#if defined(__MIPSEB__) /* MIPS 32-bit */ \ - || defined(__ppc__) || defined(__PPC__) /* CPU(PPC) - PowerPC 32-bit */ \ - || defined(__powerpc__) || defined(__powerpc) || defined(__POWERPC__) \ - || defined(_M_PPC) || defined(__PPC) \ - || defined(__ppc64__) || defined(__PPC64__) /* PowerPC 64-bit */ \ - || defined(__sparc) /* Sparc 32bit */ \ - || defined(__sparc__) /* Sparc 64-bit */ \ - || defined(__s390x__) /* S390 64-bit */ \ - || defined(__s390__) /* S390 32-bit */ \ - || defined(__ARMEB__) /* ARM big endian */ \ - || ((defined(__CC_ARM) || defined(__ARMCC__)) /* ARM RealView compiler */ \ - && defined(__BIG_ENDIAN)) -#define DST_BIG_ENDIAN 1 -#else -#define DST_LITTLE_ENDIAN 1 -#endif - -/* Handle runtime errors */ -#ifndef dst_exit -#include -#include -#define dst_exit(m) do { \ - printf("runtime error at line %d in file %s: %s\n",\ - __LINE__,\ - __FILE__,\ - (m));\ - exit(-1);\ -} while (0) -#endif - -#ifndef DST_NOASSERT -#define dst_assert(c, m) do { \ - if (!(c)) dst_exit((m)); \ -} while (0) -#endif - -/* What to do when out of memory */ -#ifndef DST_OUT_OF_MEMORY -#include -#define DST_OUT_OF_MEMORY do { printf("out of memory\n"); exit(1); } while (0) -#endif - -#define DST_INTEGER_MIN INT32_MIN -#define DST_INTEGER_MAX INT32_MAX - -/* Helper for debugging */ -#define dst_trace(x) dst_puts(dst_formatc("DST TRACE %s, %d: %v\n", __FILE__, __LINE__, x)) - -/* Prevent some recursive functions from recursing too deeply - * ands crashing (the parser). Instead, error out. */ -#define DST_RECURSION_GUARD 1000 - -#define DST_NANBOX - -#ifdef DST_NANBOX -typedef union DstValue DstValue; -#else -typedef struct DstValue DstValue; -#endif - -/* All of the dst types */ -typedef struct DstFunction DstFunction; -typedef struct DstArray DstArray; -typedef struct DstBuffer DstBuffer; -typedef struct DstTable DstTable; -typedef struct DstFiber DstFiber; - -/* Other structs */ -typedef struct DstReg DstReg; -typedef struct DstUserdataHeader DstUserdataHeader; -typedef struct DstFuncDef DstFuncDef; -typedef struct DstFuncEnv DstFuncEnv; -typedef struct DstStackFrame DstStackFrame; -typedef struct DstUserType DstUserType; -typedef int (*DstCFunction)(DstValue *argv, int32_t argn); - -typedef enum DstAssembleStatus DstAssembleStatus; -typedef struct DstAssembleResult DstAssembleResult; -typedef struct DstAssembleOptions DstAssembleOptions; -typedef enum DstCompileStatus DstCompileStatus; -typedef struct DstCompileOptions DstCompileOptions; -typedef struct DstCompileResults DstCompileResults; -typedef struct DstParseResult DstParseResult; -typedef enum DstParseStatus DstParseStatus; - -/* Names of all of the types */ -extern const char *dst_type_names[16]; - -/* Basic types for all Dst Values */ -typedef enum DstType { - DST_NIL, - DST_FALSE, - DST_TRUE, - DST_FIBER, - DST_INTEGER, - DST_REAL, - DST_STRING, - DST_SYMBOL, - DST_ARRAY, - DST_TUPLE, - DST_TABLE, - DST_STRUCT, - DST_BUFFER, - DST_FUNCTION, - DST_CFUNCTION, - DST_USERDATA -} DstType; - -/* We provide two possible implemenations of DstValues. The preferred - * nanboxing approach, and the standard C version. Code in the rest of the - * application must interact through exposed interface. */ - -/* Required interface for DstValue */ -/* wrap and unwrap for all types */ -/* Get type quickly */ -/* Check against type quickly */ -/* Small footprint */ -/* 32 bit integer support */ - -/* dst_type(x) - * dst_checktype(x, t) - * dst_wrap_##TYPE(x) - * dst_unwrap_##TYPE(x) - * dst_truthy(x) - * dst_memclear(p, n) - clear memory for hash tables to nils - * dst_u64(x) - get 64 bits of payload for hashing - */ - -#ifdef DST_NANBOX - -#include - -union DstValue { - uint64_t u64; - int64_t i64; - void *pointer; - const void *cpointer; - double real; -}; - -/* This representation uses 48 bit pointers. The trade off vs. the LuaJIT style - * 47 bit payload representaion is that the type bits are no long contiguous. Type - * checking can still be fast, but typewise polymorphism takes a bit longer. However, - * hopefully we can avoid some annoying problems that occur when trying to use 47 bit pointers - * in a 48 bit address space (Linux on ARM) */ - -/* |.......Tag.......|.......................Payload..................| */ -/* Non-double: t|11111111111|1ttt|xxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ -/* Types of NIL, TRUE, and FALSE must have payload set to all 1s. */ - -/* Double (no NaNs): x xxxxxxxxxxx xxxx xxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ - -/* A simple scheme for nan boxed values */ -/* normal doubles, denormalized doubles, and infinities are doubles */ -/* Quiet nan is nil. Sign bit should be 0. */ - -#define DST_NANBOX_TYPEBITS 0x0007000000000000lu -#define DST_NANBOX_TAGBITS 0xFFFF000000000000lu -#define DST_NANBOX_PAYLOADBITS 0x0000FFFFFFFFFFFFlu -#ifdef DST_64 -#define DST_NANBOX_POINTERBITS 0x0000FFFFFFFFFFFFlu -#else -#define DST_NANBOX_POINTERBITS 0x00000000FFFFFFFFlu -#endif - -#define dst_u64(x) ((x).u64) -#define dst_nanbox_lowtag(type) \ - ((((uint64_t)(type) & 0x8) << 12) | 0x7FF8 | (type)) -#define dst_nanbox_tag(type) \ - (dst_nanbox_lowtag(type) << 48) - -#define dst_nanbox_checkauxtype(x, type) \ - (((x).u64 & DST_NANBOX_TAGBITS) == dst_nanbox_tag((type))) - -/* Check if number is nan or if number is real double */ -#define dst_nanbox_isreal(x) \ - (!isnan((x).real) || dst_nanbox_checkauxtype((x), DST_REAL)) - -#define dst_type(x) \ - (isnan((x).real) \ - ? (((x).u64 & DST_NANBOX_TYPEBITS) >> 48) | (((x).u64 >> 60) & 0x8) \ - : DST_REAL) - -#define dst_checktype(x, t) \ - (((t) == DST_REAL) \ - ? dst_nanbox_isreal(x) \ - : dst_nanbox_checkauxtype((x), (t))) - -void *dst_nanbox_to_pointer(DstValue x); -void dst_nanbox_memempty(DstValue *mem, int32_t count); -void *dst_nanbox_memalloc_empty(int32_t count); -DstValue dst_nanbox_from_pointer(void *p, uint64_t tagmask); -DstValue dst_nanbox_from_cpointer(const void *p, uint64_t tagmask); -DstValue dst_nanbox_from_double(double d); -DstValue dst_nanbox_from_bits(uint64_t bits); - -#define dst_memempty(mem, len) dst_nanbox_memempty((mem), (len)) -#define dst_memalloc_empty(count) dst_nanbox_memalloc_empty(count) - -/* Todo - check for single mask operation */ -#define dst_truthy(x) \ - (!(dst_checktype((x), DST_NIL) || dst_checktype((x), DST_FALSE))) - -#define dst_nanbox_from_payload(t, p) \ - dst_nanbox_from_bits(dst_nanbox_tag(t) | (p)) - -#define dst_nanbox_wrap_(p, t) \ - dst_nanbox_from_pointer((p), dst_nanbox_tag(t) | 0x7FF8000000000000lu) - -#define dst_nanbox_wrap_c(p, t) \ - dst_nanbox_from_cpointer((p), dst_nanbox_tag(t) | 0x7FF8000000000000lu) - -/* Wrap the simple types */ -#define dst_wrap_nil() dst_nanbox_from_payload(DST_NIL, 1) -#define dst_wrap_true() dst_nanbox_from_payload(DST_TRUE, 1) -#define dst_wrap_false() dst_nanbox_from_payload(DST_FALSE, 1) -#define dst_wrap_boolean(b) dst_nanbox_from_payload((b) ? DST_TRUE : DST_FALSE, 1) -#define dst_wrap_integer(i) dst_nanbox_from_payload(DST_INTEGER, (uint32_t)(i)) -#define dst_wrap_real(r) dst_nanbox_from_double(r) - -/* Unwrap the simple types */ -#define dst_unwrap_boolean(x) \ - (((x).u64 >> 48) == dst_nanbox_lowtag(DST_TRUE)) -#define dst_unwrap_integer(x) \ - ((int32_t)((x).u64 & 0xFFFFFFFFlu)) -#define dst_unwrap_real(x) ((x).real) - -/* Wrap the pointer types */ -#define dst_wrap_struct(s) dst_nanbox_wrap_c((s), DST_STRUCT) -#define dst_wrap_tuple(s) dst_nanbox_wrap_c((s), DST_TUPLE) -#define dst_wrap_fiber(s) dst_nanbox_wrap_((s), DST_FIBER) -#define dst_wrap_array(s) dst_nanbox_wrap_((s), DST_ARRAY) -#define dst_wrap_table(s) dst_nanbox_wrap_((s), DST_TABLE) -#define dst_wrap_buffer(s) dst_nanbox_wrap_((s), DST_BUFFER) -#define dst_wrap_string(s) dst_nanbox_wrap_c((s), DST_STRING) -#define dst_wrap_symbol(s) dst_nanbox_wrap_c((s), DST_SYMBOL) -#define dst_wrap_userdata(s) dst_nanbox_wrap_((s), DST_USERDATA) -#define dst_wrap_pointer(s) dst_nanbox_wrap_((s), DST_USERDATA) -#define dst_wrap_function(s) dst_nanbox_wrap_((s), DST_FUNCTION) -#define dst_wrap_cfunction(s) dst_nanbox_wrap_((s), DST_CFUNCTION) - -/* Unwrap the pointer types */ -#define dst_unwrap_struct(x) ((const DstValue *)dst_nanbox_to_pointer(x)) -#define dst_unwrap_tuple(x) ((const DstValue *)dst_nanbox_to_pointer(x)) -#define dst_unwrap_fiber(x) ((DstFiber *)dst_nanbox_to_pointer(x)) -#define dst_unwrap_array(x) ((DstArray *)dst_nanbox_to_pointer(x)) -#define dst_unwrap_table(x) ((DstTable *)dst_nanbox_to_pointer(x)) -#define dst_unwrap_buffer(x) ((DstBuffer *)dst_nanbox_to_pointer(x)) -#define dst_unwrap_string(x) ((const uint8_t *)dst_nanbox_to_pointer(x)) -#define dst_unwrap_symbol(x) ((const uint8_t *)dst_nanbox_to_pointer(x)) -#define dst_unwrap_userdata(x) (dst_nanbox_to_pointer(x)) -#define dst_unwrap_pointer(x) (dst_nanbox_to_pointer(x)) -#define dst_unwrap_function(x) ((DstFunction *)dst_nanbox_to_pointer(x)) -#define dst_unwrap_cfunction(x) ((DstCFunction)dst_nanbox_to_pointer(x)) - -/* End of [#ifdef DST_NANBOX] */ -#else - -/* A general dst value type */ -struct DstValue { - union { - uint64_t u64; - double real; - int32_t integer; - void *pointer; - const void *cpointer; - } as; - DstType type; -}; - -#define dst_u64(x) ((x).as.u64) -#define dst_memempty(mem, count) memset((mem), 0, sizeof(DstValue) * (count)) -#define dst_memalloc_empty(count) calloc((count), sizeof(DstValue)) -#define dst_type(x) ((x).type) -#define dst_checktype(x, t) ((x).type == (t)) -#define dst_truthy(x) \ - ((x).type != DST_NIL && (x).type != DST_FALSE) - -#define dst_unwrap_struct(x) ((const DstValue *)(x).as.pointer) -#define dst_unwrap_tuple(x) ((const DstValue *)(x).as.pointer) -#define dst_unwrap_fiber(x) ((DstFiber *)(x).as.pointer) -#define dst_unwrap_array(x) ((DstArray *)(x).as.pointer) -#define dst_unwrap_table(x) ((DstTable *)(x).as.pointer) -#define dst_unwrap_buffer(x) ((DstBuffer *)(x).as.pointer) -#define dst_unwrap_string(x) ((const uint8_t *)(x).as.pointer) -#define dst_unwrap_symbol(x) ((const uint8_t *)(x).as.pointer) -#define dst_unwrap_userdata(x) ((x).as.pointer) -#define dst_unwrap_pointer(x) ((x).as.pointer) -#define dst_unwrap_function(x) ((DstFunction *)(x).as.pointer) -#define dst_unwrap_cfunction(x) ((DstCFunction)(x).as.pointer) -#define dst_unwrap_boolean(x) ((x).type == DST_TRUE) -#define dst_unwrap_integer(x) ((x).as.integer) -#define dst_unwrap_real(x) ((x).as.real) - -DstValue dst_wrap_nil(); -DstValue dst_wrap_real(double x); -DstValue dst_wrap_integer(int32_t x); -DstValue dst_wrap_true(); -DstValue dst_wrap_false(); -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_fiber(DstFiber *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_pointer(void *x); - -/* End of tagged union implementation */ -#endif - -/* Used for creating libraries of cfunctions. */ -struct DstReg { - const char *name; - DstCFunction function; -}; - -/* A lightweight green thread in dst. Does not correspond to - * operating system threads. */ -struct DstFiber { - DstValue ret; /* Return value */ - DstValue *data; - DstFiber *parent; - int32_t frame; /* Index of the stack frame */ - int32_t frametop; /* Index of top of stack frame */ - int32_t stacktop; /* Top of stack. Where values are pushed and popped from. */ - int32_t capacity; - enum { - DST_FIBER_PENDING = 0, - DST_FIBER_ALIVE, - DST_FIBER_DEAD, - DST_FIBER_ERROR - } status; -}; - -/* A stack frame on the fiber. Is stored along with the stack values. */ -struct DstStackFrame { - DstFunction *func; - uint32_t *pc; - int32_t prevframe; -}; - -/* Number of DstValues a frame takes up in the stack */ -#define DST_FRAME_SIZE ((sizeof(DstStackFrame) + sizeof(DstValue) - 1)/ sizeof(DstValue)) - -/* A dynamic array type. */ -struct DstArray { - DstValue *data; - int32_t count; - int32_t capacity; -}; - -/* A bytebuffer type. Used as a mutable string or string builder. */ -struct DstBuffer { - uint8_t *data; - int32_t count; - int32_t capacity; -}; - -/* A mutable associative data type. Backed by a hashtable. */ -struct DstTable { - DstValue *data; - int32_t count; - int32_t capacity; - int32_t deleted; -}; - -/* Some function defintion flags */ -#define DST_FUNCDEF_FLAG_VARARG 1 -#define DST_FUNCDEF_FLAG_NEEDSENV 4 - -/* A function definition. Contains information needed to instantiate closures. */ -struct DstFuncDef { - int32_t *environments; /* Which environments to capture from parent. */ - DstValue *constants; /* Contains strings, FuncDefs, etc. */ - uint32_t *bytecode; - - /* Various debug information */ - int32_t *sourcemap; - const uint8_t *source; - const uint8_t *sourcepath; - - uint32_t flags; - int32_t slotcount; /* The amount of stack space required for the function */ - int32_t arity; /* Not including varargs */ - int32_t constants_length; - int32_t bytecode_length; - int32_t environments_length; -}; - -/* A fuction environment */ -struct DstFuncEnv { - union { - DstFiber *fiber; - DstValue *values; - } as; - int32_t length; /* Size of environment */ - int32_t offset; /* Stack offset when values still on stack. If offset is <= 0, then - environment is no longer on the stack. */ -}; - -/* A function */ -struct DstFunction { - DstFuncDef *def; - /* Consider allocating envs with entire function struct */ - DstFuncEnv **envs; -}; - -/* Defines a type for userdata */ -struct DstUserType { - const char *name; - int (*serialize)(void *data, size_t len); - int (*deserialize)(); - void (*finalize)(void *data, size_t len); -}; - -/* Contains information about userdata */ -struct DstUserdataHeader { - const DstUserType *type; - size_t size; -}; - -/* Assemble structs */ -enum DstAssembleStatus { - DST_ASSEMBLE_OK, - DST_ASSEMBLE_ERROR -}; - -struct DstAssembleOptions { - const DstValue *sourcemap; - DstValue source; - uint32_t flags; -}; - -struct DstAssembleResult { - DstFuncDef *funcdef; - const uint8_t *error; - int32_t error_start; - int32_t error_end; - DstAssembleStatus status; -}; - -/* Compile structs */ -enum DstCompileStatus { - DST_COMPILE_OK, - DST_COMPILE_ERROR -}; - -struct DstCompileResults { - DstCompileStatus status; - DstFuncDef *funcdef; - const uint8_t *error; - int32_t error_start; - int32_t error_end; -}; - -struct DstCompileOptions { - uint32_t flags; - const DstValue *sourcemap; - DstValue source; - DstValue env; -}; - -/* Parse structs */ -enum DstParseStatus { - DST_PARSE_OK, - DST_PARSE_ERROR, - DST_PARSE_UNEXPECTED_EOS -}; - -struct DstParseResult { - DstValue value; - const uint8_t *error; - const DstValue *map; - int32_t bytes_read; - DstParseStatus status; -}; - -/* The VM state. Rather than a struct that is passed - * around, the vm state is global for simplicity. */ - -/* Garbage collection */ -extern void *dst_vm_blocks; -extern uint32_t dst_vm_memory_interval; -extern uint32_t dst_vm_next_collection; - -/* Immutable value cache */ -extern const uint8_t **dst_vm_cache; -extern uint32_t dst_vm_cache_capacity; -extern uint32_t dst_vm_cache_count; -extern uint32_t dst_vm_cache_deleted; - -/* Syscall table */ -extern const DstCFunction dst_vm_syscalls[256]; - -/* GC roots */ -extern DstValue *dst_vm_roots; -extern uint32_t dst_vm_root_count; -extern uint32_t dst_vm_root_capacity; - -/* GC roots - TODO consider a top level fiber pool (per thread?) */ -extern DstFiber *dst_vm_fiber; +#include "dstconfig.h" +#include "dsttypes.h" +#include "dststate.h" /* Array functions */ DstArray *dst_array(int32_t capacity); @@ -626,6 +82,7 @@ const uint8_t *dst_description(DstValue x); const uint8_t *dst_short_description(DstValue x); const uint8_t *dst_to_string(DstValue x); #define dst_cstringv(cstr) dst_wrap_string(dst_cstring(cstr)) +#define dst_stringv(str, len) dst_wrap_string(dst_string((str), (len))) const uint8_t *dst_formatc(const char *format, ...); void dst_puts(const uint8_t *str); @@ -714,38 +171,36 @@ extern const char dst_base64[65]; int32_t dst_array_calchash(const DstValue *array, int32_t len); int32_t dst_string_calchash(const uint8_t *str, int32_t len); DstValue dst_loadreg(DstReg *regs, size_t count); +DstValue dst_scan_number(const uint8_t *src, int32_t len); +int32_t dst_scan_integer(const uint8_t *str, int32_t len, int *err); +double dst_scan_real(const uint8_t *str, int32_t len, int *err); /* Parsing */ DstParseResult dst_parse(const uint8_t *src, int32_t len); DstParseResult dst_parsec(const char *src); -const DstValue *dst_parse_submap_index(const DstValue *map, int32_t index); -const DstValue *dst_parse_submap_key(const DstValue *map, DstValue key); -const DstValue *dst_parse_submap_value(const DstValue *map, DstValue key); /* VM functions */ int dst_init(); void dst_deinit(); -int dst_continue(); -int dst_run(DstValue callee); -DstValue dst_transfer(DstFiber *fiber, DstValue x); +int dst_run(DstValue callee, DstValue *returnreg); /* Compile */ +DstCompileResult dst_compile(DstCompileOptions opts); +DstFunction *dst_compile_func(DstCompileResult result); -/* Compile source code into FuncDef. */ -DstCompileResults dst_compile(DstCompileOptions opts); -DstFunction *dst_compile_func(DstCompileResults results); +/* STL */ +#define DST_LOAD_ROOT 1 +DstValue dst_loadstl(int flags); /* GC */ - void dst_mark(DstValue x); void dst_sweep(); void dst_collect(); void dst_clear_memory(); void dst_gcroot(DstValue root); int dst_gcunroot(DstValue root); - -/* Run garbage collection if needed */ +int dst_gcunrootall(DstValue root); #define dst_maybe_collect() do {\ - if (dst_vm_next_collection >= dst_vm_memory_interval) dst_collect(); } while (0) + if (dst_vm_next_collection >= dst_vm_gc_interval) dst_collect(); } while (0) #endif /* DST_H_defined */ diff --git a/include/dst/dstconfig.h b/include/dst/dstconfig.h new file mode 100644 index 00000000..ca15451e --- /dev/null +++ b/include/dst/dstconfig.h @@ -0,0 +1,121 @@ +/* +* 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_CONFIG_H_defined +#define DST_CONFIG_H_defined + +#include +#include "dst.h" + +/* + * Detect OS and endianess. + * From webkit source. + */ + +/* Check Unix */ +#if defined(_AIX) \ + || defined(__APPLE__) /* Darwin */ \ + || defined(__FreeBSD__) || defined(__DragonFly__) \ + || defined(__FreeBSD_kernel__) \ + || defined(__GNU__) /* GNU/Hurd */ \ + || defined(__linux__) \ + || defined(__NetBSD__) \ + || defined(__OpenBSD__) \ + || defined(__QNXNTO__) \ + || defined(sun) || defined(__sun) /* Solaris */ \ + || defined(unix) || defined(__unix) || defined(__unix__) +#define DST_UNIX 1 +#endif + +/* Check Windows */ +#if defined(WIN32) || defined(_WIN32) +#define DST_WINDOWS 1 +#endif + +/* Check 64-bit vs 32-bit */ +#if ((defined(__x86_64__) || defined(_M_X64)) \ + && (defined(DST_UNIX) || defined(DST_WINDOWS))) \ + || (defined(__ia64__) && defined(__LP64__)) /* Itanium in LP64 mode */ \ + || defined(__alpha__) /* DEC Alpha */ \ + || (defined(__sparc__) && defined(__arch64__) || defined (__sparcv9)) /* BE */ \ + || defined(__s390x__) /* S390 64-bit (BE) */ \ + || (defined(__ppc64__) || defined(__PPC64__)) \ + || defined(__aarch64__) /* ARM 64-bit */ +#define DST_64 1 +#else +#define DST_32 1 +#endif + +/* Check big endian */ +#if defined(__MIPSEB__) /* MIPS 32-bit */ \ + || defined(__ppc__) || defined(__PPC__) /* CPU(PPC) - PowerPC 32-bit */ \ + || defined(__powerpc__) || defined(__powerpc) || defined(__POWERPC__) \ + || defined(_M_PPC) || defined(__PPC) \ + || defined(__ppc64__) || defined(__PPC64__) /* PowerPC 64-bit */ \ + || defined(__sparc) /* Sparc 32bit */ \ + || defined(__sparc__) /* Sparc 64-bit */ \ + || defined(__s390x__) /* S390 64-bit */ \ + || defined(__s390__) /* S390 32-bit */ \ + || defined(__ARMEB__) /* ARM big endian */ \ + || ((defined(__CC_ARM) || defined(__ARMCC__)) /* ARM RealView compiler */ \ + && defined(__BIG_ENDIAN)) +#define DST_BIG_ENDIAN 1 +#else +#define DST_LITTLE_ENDIAN 1 +#endif + +/* Handle runtime errors */ +#ifndef dst_exit +#include +#include +#define dst_exit(m) do { \ + printf("runtime error at line %d in file %s: %s\n",\ + __LINE__,\ + __FILE__,\ + (m));\ + exit(-1);\ +} while (0) +#endif + +#ifndef DST_NOASSERT +#define dst_assert(c, m) do { \ + if (!(c)) dst_exit((m)); \ +} while (0) +#endif + +/* What to do when out of memory */ +#ifndef DST_OUT_OF_MEMORY +#include +#define DST_OUT_OF_MEMORY do { printf("out of memory\n"); exit(1); } while (0) +#endif + +/* Helper for debugging */ +#define dst_trace(x) dst_puts(dst_formatc("DST TRACE %s, %d: %v\n", __FILE__, __LINE__, x)) + +/* Prevent some recursive functions from recursing too deeply + * ands crashing (the parser). Instead, error out. */ +#define DST_RECURSION_GUARD 1000 + +/* Use nanboxed values - uses 8 bytes per value instead of 12 or 16. */ +#define DST_NANBOX + +#endif /* DST_CONFIG_H_defined */ diff --git a/include/dst/dststate.h b/include/dst/dststate.h new file mode 100644 index 00000000..d75bbf7f --- /dev/null +++ b/include/dst/dststate.h @@ -0,0 +1,55 @@ +/* +* 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_STATE_H_defined +#define DST_STATE_H_defined + +#include +#include "dstconfig.h" +#include "dsttypes.h" + +/* Names of all of the types */ +extern const char *dst_type_names[16]; + +/* The VM state. Rather than a struct that is passed + * around, the vm state is global for simplicity. */ + +/* Garbage collection */ +extern void *dst_vm_blocks; +extern uint32_t dst_vm_gc_interval; +extern uint32_t dst_vm_next_collection; + +/* Immutable value cache */ +extern const uint8_t **dst_vm_cache; +extern uint32_t dst_vm_cache_capacity; +extern uint32_t dst_vm_cache_count; +extern uint32_t dst_vm_cache_deleted; + +/* GC roots */ +extern DstValue *dst_vm_roots; +extern uint32_t dst_vm_root_count; +extern uint32_t dst_vm_root_capacity; + +/* GC roots - TODO consider a top level fiber pool (per thread?) */ +extern DstFiber *dst_vm_fiber; + +#endif /* DST_STATE_H_defined */ diff --git a/include/dst/dststl.h b/include/dst/dststl.h new file mode 100644 index 00000000..885211c1 --- /dev/null +++ b/include/dst/dststl.h @@ -0,0 +1,56 @@ +/* +* 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_MATH_H_defined +#define DST_MATH_H_defined + +#include "dsttypes.h" + +int dst_add(int32_t argn, DstValue *argv, DstValue *ret); +int dst_subtract(int32_t argn, DstValue *argv, DstValue *ret); +int dst_multiply(int32_t argn, DstValue *argv, DstValue *ret); +int dst_divide(int32_t argn, DstValue *argv, DstValue *ret); +int dst_modulo(int32_t argn, DstValue *argv, DstValue *ret); + +int dst_acos(int32_t argn, DstValue *argv, DstValue *ret); +int dst_asin(int32_t argn, DstValue *argv, DstValue *ret); +int dst_atan(int32_t argn, DstValue *argv, DstValue *ret); +int dst_cos(int32_t argn, DstValue *argv, DstValue *ret); +int dst_cosh(int32_t argn, DstValue *argv, DstValue *ret); +int dst_sin(int32_t argn, DstValue *argv, DstValue *ret); +int dst_sinh(int32_t argn, DstValue *argv, DstValue *ret); +int dst_tan(int32_t argn, DstValue *argv, DstValue *ret); +int dst_tanh(int32_t argn, DstValue *argv, DstValue *ret); +int dst_exp(int32_t argn, DstValue *argv, DstValue *ret); +int dst_log(int32_t argn, DstValue *argv, DstValue *ret); +int dst_log10(int32_t argn, DstValue *argv, DstValue *ret); +int dst_sqrt(int32_t argn, DstValue *argv, DstValue *ret); +int dst_ceil(int32_t argn, DstValue *argv, DstValue *ret); +int dst_fabs(int32_t argn, DstValue *argv, DstValue *ret); +int dst_floor(int32_t argn, DstValue *argv, DstValue *ret); + +int dst_stl_table(int32_t argn, DstValue *argv, DstValue *ret); +int dst_stl_array(int32_t argn, DstValue *argv, DstValue *ret); +int dst_stl_struct(int32_t argn, DstValue *argv, DstValue *ret); +int dst_stl_tuple(int32_t argn, DstValue *argv, DstValue *ret); + +#endif /* DST_MATH_H_defined */ diff --git a/include/dst/dsttypes.h b/include/dst/dsttypes.h new file mode 100644 index 00000000..e0364dce --- /dev/null +++ b/include/dst/dsttypes.h @@ -0,0 +1,455 @@ +/* +* 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_TYPES_H_defined +#define DST_TYPES_H_defined + +#include +#include "dstconfig.h" + +#ifdef DST_NANBOX +typedef union DstValue DstValue; +#else +typedef struct DstValue DstValue; +#endif + +/* All of the dst types */ +typedef struct DstFunction DstFunction; +typedef struct DstArray DstArray; +typedef struct DstBuffer DstBuffer; +typedef struct DstTable DstTable; +typedef struct DstFiber DstFiber; + +/* Other structs */ +typedef struct DstReg DstReg; +typedef struct DstUserdataHeader DstUserdataHeader; +typedef struct DstFuncDef DstFuncDef; +typedef struct DstFuncEnv DstFuncEnv; +typedef struct DstStackFrame DstStackFrame; +typedef struct DstUserType DstUserType; +typedef int (*DstCFunction)(int32_t argn, DstValue *argv, DstValue *ret); + +typedef enum DstAssembleStatus DstAssembleStatus; +typedef struct DstAssembleResult DstAssembleResult; +typedef struct DstAssembleOptions DstAssembleOptions; +typedef enum DstCompileStatus DstCompileStatus; +typedef struct DstCompileOptions DstCompileOptions; +typedef struct DstCompileResult DstCompileResult; +typedef struct DstParseResult DstParseResult; +typedef enum DstParseStatus DstParseStatus; + +/* Basic types for all Dst Values */ +typedef enum DstType { + DST_NIL, + DST_FALSE, + DST_TRUE, + DST_FIBER, + DST_INTEGER, + DST_REAL, + DST_STRING, + DST_SYMBOL, + DST_ARRAY, + DST_TUPLE, + DST_TABLE, + DST_STRUCT, + DST_BUFFER, + DST_FUNCTION, + DST_CFUNCTION, + DST_USERDATA +} DstType; + +/* We provide two possible implemenations of DstValues. The preferred + * nanboxing approach, and the standard C version. Code in the rest of the + * application must interact through exposed interface. */ + +/* Required interface for DstValue */ +/* wrap and unwrap for all types */ +/* Get type quickly */ +/* Check against type quickly */ +/* Small footprint */ +/* 32 bit integer support */ + +/* dst_type(x) + * dst_checktype(x, t) + * dst_wrap_##TYPE(x) + * dst_unwrap_##TYPE(x) + * dst_truthy(x) + * dst_memclear(p, n) - clear memory for hash tables to nils + * dst_u64(x) - get 64 bits of payload for hashing + */ + +#ifdef DST_NANBOX + +#include + +union DstValue { + uint64_t u64; + int64_t i64; + void *pointer; + const void *cpointer; + double real; +}; + +/* This representation uses 48 bit pointers. The trade off vs. the LuaJIT style + * 47 bit payload representaion is that the type bits are no long contiguous. Type + * checking can still be fast, but typewise polymorphism takes a bit longer. However, + * hopefully we can avoid some annoying problems that occur when trying to use 47 bit pointers + * in a 48 bit address space (Linux on ARM) */ + +/* |.......Tag.......|.......................Payload..................| */ +/* Non-double: t|11111111111|1ttt|xxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ +/* Types of NIL, TRUE, and FALSE must have payload set to all 1s. */ + +/* Double (no NaNs): x xxxxxxxxxxx xxxx xxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx */ + +/* A simple scheme for nan boxed values */ +/* normal doubles, denormalized doubles, and infinities are doubles */ +/* Quiet nan is nil. Sign bit should be 0. */ + +#define DST_NANBOX_TYPEBITS 0x0007000000000000lu +#define DST_NANBOX_TAGBITS 0xFFFF000000000000lu +#define DST_NANBOX_PAYLOADBITS 0x0000FFFFFFFFFFFFlu +#ifdef DST_64 +#define DST_NANBOX_POINTERBITS 0x0000FFFFFFFFFFFFlu +#else +#define DST_NANBOX_POINTERBITS 0x00000000FFFFFFFFlu +#endif + +#define dst_u64(x) ((x).u64) +#define dst_nanbox_lowtag(type) \ + ((((uint64_t)(type) & 0x8) << 12) | 0x7FF8 | (type)) +#define dst_nanbox_tag(type) \ + (dst_nanbox_lowtag(type) << 48) + +#define dst_nanbox_checkauxtype(x, type) \ + (((x).u64 & DST_NANBOX_TAGBITS) == dst_nanbox_tag((type))) + +/* Check if number is nan or if number is real double */ +#define dst_nanbox_isreal(x) \ + (!isnan((x).real) || dst_nanbox_checkauxtype((x), DST_REAL)) + +#define dst_type(x) \ + (isnan((x).real) \ + ? (((x).u64 & DST_NANBOX_TYPEBITS) >> 48) | (((x).u64 >> 60) & 0x8) \ + : DST_REAL) + +#define dst_checktype(x, t) \ + (((t) == DST_REAL) \ + ? dst_nanbox_isreal(x) \ + : dst_nanbox_checkauxtype((x), (t))) + +void *dst_nanbox_to_pointer(DstValue x); +void dst_nanbox_memempty(DstValue *mem, int32_t count); +void *dst_nanbox_memalloc_empty(int32_t count); +DstValue dst_nanbox_from_pointer(void *p, uint64_t tagmask); +DstValue dst_nanbox_from_cpointer(const void *p, uint64_t tagmask); +DstValue dst_nanbox_from_double(double d); +DstValue dst_nanbox_from_bits(uint64_t bits); + +#define dst_memempty(mem, len) dst_nanbox_memempty((mem), (len)) +#define dst_memalloc_empty(count) dst_nanbox_memalloc_empty(count) + +/* Todo - check for single mask operation */ +#define dst_truthy(x) \ + (!(dst_checktype((x), DST_NIL) || dst_checktype((x), DST_FALSE))) + +#define dst_nanbox_from_payload(t, p) \ + dst_nanbox_from_bits(dst_nanbox_tag(t) | (p)) + +#define dst_nanbox_wrap_(p, t) \ + dst_nanbox_from_pointer((p), dst_nanbox_tag(t) | 0x7FF8000000000000lu) + +#define dst_nanbox_wrap_c(p, t) \ + dst_nanbox_from_cpointer((p), dst_nanbox_tag(t) | 0x7FF8000000000000lu) + +/* Wrap the simple types */ +#define dst_wrap_nil() dst_nanbox_from_payload(DST_NIL, 1) +#define dst_wrap_true() dst_nanbox_from_payload(DST_TRUE, 1) +#define dst_wrap_false() dst_nanbox_from_payload(DST_FALSE, 1) +#define dst_wrap_boolean(b) dst_nanbox_from_payload((b) ? DST_TRUE : DST_FALSE, 1) +#define dst_wrap_integer(i) dst_nanbox_from_payload(DST_INTEGER, (uint32_t)(i)) +#define dst_wrap_real(r) dst_nanbox_from_double(r) + +/* Unwrap the simple types */ +#define dst_unwrap_boolean(x) \ + (((x).u64 >> 48) == dst_nanbox_lowtag(DST_TRUE)) +#define dst_unwrap_integer(x) \ + ((int32_t)((x).u64 & 0xFFFFFFFFlu)) +#define dst_unwrap_real(x) ((x).real) + +/* Wrap the pointer types */ +#define dst_wrap_struct(s) dst_nanbox_wrap_c((s), DST_STRUCT) +#define dst_wrap_tuple(s) dst_nanbox_wrap_c((s), DST_TUPLE) +#define dst_wrap_fiber(s) dst_nanbox_wrap_((s), DST_FIBER) +#define dst_wrap_array(s) dst_nanbox_wrap_((s), DST_ARRAY) +#define dst_wrap_table(s) dst_nanbox_wrap_((s), DST_TABLE) +#define dst_wrap_buffer(s) dst_nanbox_wrap_((s), DST_BUFFER) +#define dst_wrap_string(s) dst_nanbox_wrap_c((s), DST_STRING) +#define dst_wrap_symbol(s) dst_nanbox_wrap_c((s), DST_SYMBOL) +#define dst_wrap_userdata(s) dst_nanbox_wrap_((s), DST_USERDATA) +#define dst_wrap_pointer(s) dst_nanbox_wrap_((s), DST_USERDATA) +#define dst_wrap_function(s) dst_nanbox_wrap_((s), DST_FUNCTION) +#define dst_wrap_cfunction(s) dst_nanbox_wrap_((s), DST_CFUNCTION) + +/* Unwrap the pointer types */ +#define dst_unwrap_struct(x) ((const DstValue *)dst_nanbox_to_pointer(x)) +#define dst_unwrap_tuple(x) ((const DstValue *)dst_nanbox_to_pointer(x)) +#define dst_unwrap_fiber(x) ((DstFiber *)dst_nanbox_to_pointer(x)) +#define dst_unwrap_array(x) ((DstArray *)dst_nanbox_to_pointer(x)) +#define dst_unwrap_table(x) ((DstTable *)dst_nanbox_to_pointer(x)) +#define dst_unwrap_buffer(x) ((DstBuffer *)dst_nanbox_to_pointer(x)) +#define dst_unwrap_string(x) ((const uint8_t *)dst_nanbox_to_pointer(x)) +#define dst_unwrap_symbol(x) ((const uint8_t *)dst_nanbox_to_pointer(x)) +#define dst_unwrap_userdata(x) (dst_nanbox_to_pointer(x)) +#define dst_unwrap_pointer(x) (dst_nanbox_to_pointer(x)) +#define dst_unwrap_function(x) ((DstFunction *)dst_nanbox_to_pointer(x)) +#define dst_unwrap_cfunction(x) ((DstCFunction)dst_nanbox_to_pointer(x)) + +/* End of [#ifdef DST_NANBOX] */ +#else + +/* A general dst value type */ +struct DstValue { + union { + uint64_t u64; + double real; + int32_t integer; + void *pointer; + const void *cpointer; + } as; + DstType type; +}; + +#define dst_u64(x) ((x).as.u64) +#define dst_memempty(mem, count) memset((mem), 0, sizeof(DstValue) * (count)) +#define dst_memalloc_empty(count) calloc((count), sizeof(DstValue)) +#define dst_type(x) ((x).type) +#define dst_checktype(x, t) ((x).type == (t)) +#define dst_truthy(x) \ + ((x).type != DST_NIL && (x).type != DST_FALSE) + +#define dst_unwrap_struct(x) ((const DstValue *)(x).as.pointer) +#define dst_unwrap_tuple(x) ((const DstValue *)(x).as.pointer) +#define dst_unwrap_fiber(x) ((DstFiber *)(x).as.pointer) +#define dst_unwrap_array(x) ((DstArray *)(x).as.pointer) +#define dst_unwrap_table(x) ((DstTable *)(x).as.pointer) +#define dst_unwrap_buffer(x) ((DstBuffer *)(x).as.pointer) +#define dst_unwrap_string(x) ((const uint8_t *)(x).as.pointer) +#define dst_unwrap_symbol(x) ((const uint8_t *)(x).as.pointer) +#define dst_unwrap_userdata(x) ((x).as.pointer) +#define dst_unwrap_pointer(x) ((x).as.pointer) +#define dst_unwrap_function(x) ((DstFunction *)(x).as.pointer) +#define dst_unwrap_cfunction(x) ((DstCFunction)(x).as.pointer) +#define dst_unwrap_boolean(x) ((x).type == DST_TRUE) +#define dst_unwrap_integer(x) ((x).as.integer) +#define dst_unwrap_real(x) ((x).as.real) + +DstValue dst_wrap_nil(); +DstValue dst_wrap_real(double x); +DstValue dst_wrap_integer(int32_t x); +DstValue dst_wrap_true(); +DstValue dst_wrap_false(); +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_fiber(DstFiber *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_pointer(void *x); + +/* End of tagged union implementation */ +#endif + +/* Used for creating libraries of cfunctions. */ +struct DstReg { + const char *name; + DstCFunction function; +}; + +/* A lightweight green thread in dst. Does not correspond to + * operating system threads. */ +struct DstFiber { + DstValue *data; + DstFiber *parent; + int32_t frame; /* Index of the stack frame */ + int32_t frametop; /* Index of top of stack frame */ + int32_t stacktop; /* Top of stack. Where values are pushed and popped from. */ + int32_t capacity; + enum { + DST_FIBER_PENDING = 0, + DST_FIBER_ALIVE, + DST_FIBER_DEAD, + DST_FIBER_ERROR + } status; +}; + +/* A stack frame on the fiber. Is stored along with the stack values. */ +struct DstStackFrame { + DstFunction *func; + uint32_t *pc; + int32_t prevframe; +}; + +/* Number of DstValues a frame takes up in the stack */ +#define DST_FRAME_SIZE ((sizeof(DstStackFrame) + sizeof(DstValue) - 1)/ sizeof(DstValue)) + +/* A dynamic array type. */ +struct DstArray { + DstValue *data; + int32_t count; + int32_t capacity; +}; + +/* A bytebuffer type. Used as a mutable string or string builder. */ +struct DstBuffer { + uint8_t *data; + int32_t count; + int32_t capacity; +}; + +/* A mutable associative data type. Backed by a hashtable. */ +struct DstTable { + DstValue *data; + int32_t count; + int32_t capacity; + int32_t deleted; +}; + +/* Some function defintion flags */ +#define DST_FUNCDEF_FLAG_VARARG 1 +#define DST_FUNCDEF_FLAG_NEEDSENV 4 + +/* A function definition. Contains information needed to instantiate closures. */ +struct DstFuncDef { + int32_t *environments; /* Which environments to capture from parent. */ + DstValue *constants; /* Contains strings, FuncDefs, etc. */ + uint32_t *bytecode; + + /* Various debug information */ + int32_t *sourcemap; + const uint8_t *source; + const uint8_t *sourcepath; + + uint32_t flags; + int32_t slotcount; /* The amount of stack space required for the function */ + int32_t arity; /* Not including varargs */ + int32_t constants_length; + int32_t bytecode_length; + int32_t environments_length; +}; + +/* A fuction environment */ +struct DstFuncEnv { + union { + DstFiber *fiber; + DstValue *values; + } as; + int32_t length; /* Size of environment */ + int32_t offset; /* Stack offset when values still on stack. If offset is <= 0, then + environment is no longer on the stack. */ +}; + +/* A function */ +struct DstFunction { + DstFuncDef *def; + /* Consider allocating envs with entire function struct */ + DstFuncEnv **envs; +}; + +/* Defines a type for userdata */ +struct DstUserType { + const char *name; + int (*serialize)(void *data, size_t len); + int (*deserialize)(); + void (*finalize)(void *data, size_t len); +}; + +/* Contains information about userdata */ +struct DstUserdataHeader { + const DstUserType *type; + size_t size; +}; + +/* Assemble structs */ +enum DstAssembleStatus { + DST_ASSEMBLE_OK, + DST_ASSEMBLE_ERROR +}; + +struct DstAssembleOptions { + const DstValue *sourcemap; + DstValue source; + uint32_t flags; +}; + +struct DstAssembleResult { + DstFuncDef *funcdef; + const uint8_t *error; + int32_t error_start; + int32_t error_end; + DstAssembleStatus status; +}; + +/* Compile structs */ +enum DstCompileStatus { + DST_COMPILE_OK, + DST_COMPILE_ERROR +}; + +struct DstCompileResult { + DstCompileStatus status; + DstFuncDef *funcdef; + const uint8_t *error; + int32_t error_start; + int32_t error_end; +}; + +struct DstCompileOptions { + uint32_t flags; + const DstValue *sourcemap; + DstValue source; + DstValue env; +}; + +/* Parse structs */ +enum DstParseStatus { + DST_PARSE_OK, + DST_PARSE_ERROR, + DST_PARSE_UNEXPECTED_EOS, + DST_PARSE_NODATA +}; + +struct DstParseResult { + DstValue value; + const uint8_t *error; + const DstValue *map; + int32_t bytes_read; + DstParseStatus status; +}; + +#endif /* DST_TYPES_H_defined */ diff --git a/unittests/compile_test.c b/unittests/compile_test.c index 67c8caea..eb4a58bb 100644 --- a/unittests/compile_test.c +++ b/unittests/compile_test.c @@ -1,19 +1,30 @@ #include "unit.h" #include +#include int testprint(DstValue *argv, int32_t argn) { - printf("hello!\n"); + int32_t i; + for (i = 0; i < argn; i++) { + dst_puts(dst_formatc("%v\n", argv[i])); + } return 0; } DstReg testreg[] = { - {"print", testprint} + {"print", testprint}, + {"+", dst_add}, + {"-", dst_subtract}, + {"*", dst_multiply}, + {"/", dst_divide}, + {"%", dst_modulo}, + {"acos", dst_acos}, + {"asin", dst_asin} }; int main() { DstParseResult pres; DstCompileOptions opts; - DstCompileResults cres; + DstCompileResult cres; DstFunction *func; FILE *f = fopen("./dsttest/basic.dst", "rb");