Huge number of changes. Still WIP. Refactoring and

changing C API model. This commit is not i a working state.
This commit is contained in:
bakpakin 2017-09-09 14:39:51 -04:00
parent 3ccd688438
commit 3efd400025
28 changed files with 3977 additions and 3969 deletions

4
.gitignore vendored
View File

@ -1,6 +1,6 @@
# Target
/client/gst
gst
/client/dst
dst
# Generated files
*.gen.h

View File

@ -1,87 +1,81 @@
# GST
# DST
######################################################
##### Set global variables for all gst Makefiles #####
######################################################
################################
##### Set global variables #####
################################
PREFIX?=/usr/local
BINDIR=$(PREFIX)/bin
VERSION=\"0.0.0-beta\"
CFLAGS=-std=c99 -Wall -Wextra -I./include -I./libs -g -DGST_VERSION=$(VERSION)
CFLAGS=-std=c99 -Wall -Wextra -I./include -I./libs -g -DDST_VERSION=$(VERSION)
PREFIX=/usr/local
GST_TARGET=gst
GST_XXD=xxd
DST_TARGET=dst
DST_XXD=xxd
# Use gdb. On mac use lldb
DEBUGGER=gdb
GST_INTERNAL_HEADERS=$(addprefix core/, cache.h)
GST_HEADERS=$(addprefix include/gst/, gst.h)
DST_INTERNAL_HEADERS=$(addprefix core/, internal.h bootstrap.h)
DST_HEADERS=$(addprefix include/dst/, dst.h)
#############################
##### Generated headers #####
#############################
GST_LANG_SOURCES=$(addprefix libs/, bootstrap.gst)
GST_LANG_HEADERS=$(patsubst %.gst,%.gen.h,$(GST_LANG_SOURCES))
DST_LANG_SOURCES=$(addprefix libs/, bootstrap.dst)
DST_LANG_HEADERS=$(patsubst %.dst,%.gen.h,$(DST_LANG_SOURCES))
all: $(GST_TARGET)
all: $(DST_TARGET)
#######################
##### Build tools #####
#######################
$(GST_XXD): libs/xxd.c
$(CC) -o $(GST_XXD) libs/xxd.c
$(DST_XXD): libs/xxd.c
$(CC) -o $(DST_XXD) libs/xxd.c
%.gen.h : %.gst $(GST_XXD)
./$(GST_XXD) $< $@ $(basename $(notdir $<))
%.gen.h: %.dst $(DST_XXD)
./$(DST_XXD) $< $@ $(basename $(notdir $<))
###################################
##### The core vm and runtime #####
###################################
GST_CORE_SOURCES=$(addprefix core/,\
compile.c parse.c stl.c ids.c util.c env.c module.c\
DST_CORE_SOURCES=$(addprefix core/,\
util.c wrap.c\
value.c vm.c ds.c gc.c thread.c serialize.c\
string.c)
GST_CORE_OBJECTS=$(patsubst %.c,%.o,$(GST_CORE_SOURCES))
string.c bootstrap_parse.c client.c)
DST_CORE_OBJECTS=$(patsubst %.c,%.o,$(DST_CORE_SOURCES))
######################
##### The client #####
######################
GST_CLIENT_SOURCES=client/main.c
GST_CLIENT_OBJECTS=$(patsubst %.c,%.o,$(GST_CLIENT_SOURCES))
$(GST_TARGET): $(GST_CLIENT_OBJECTS) $(GST_CORE_OBJECTS) $(GST_LANG_HEADERS)
$(CC) $(CFLAGS) -o $(GST_TARGET) $(GST_CLIENT_OBJECTS) $(GST_CORE_OBJECTS)
$(DST_TARGET): $(DST_CORE_OBJECTS) $(DST_LANG_HEADERS)
$(CC) $(CFLAGS) -o $(DST_TARGET) $(DST_CORE_OBJECTS)
# Compile all .c to .o
%.o : %.c $(GST_HEADERS) $(GST_INTERNAL_HEADERS) $(GST_LANG_HEADERS)
%.o: %.c $(DST_HEADERS) $(DST_INTERNAL_HEADERS) $(DST_LANG_HEADERS)
$(CC) $(CFLAGS) -o $@ -c $<
run: $(GST_TARGET)
@ ./$(GST_TARGET)
run: $(DST_TARGET)
@ ./$(DST_TARGET)
debug: $(GST_TARGET)
@ $(DEBUGGER) ./$(GST_TARGET)
debug: $(DST_TARGET)
@ $(DEBUGGER) ./$(DST_TARGET)
valgrind: $(GST_TARGET)
@ valgrind --leak-check=full -v ./$(GST_TARGET)
valgrind: $(DST_TARGET)
@ valgrind --leak-check=full -v ./$(DST_TARGET)
clean:
rm $(GST_TARGET) || true
rm $(GST_CORE_OBJECTS) || true
rm $(GST_CLIENT_OBJECTS) || true
rm $(GST_LANG_HEADERS) || true
rm $(DST_TARGET) || true
rm $(DST_CORE_OBJECTS) || true
rm $(DST_LANG_HEADERS) || true
rm vgcore.* || true
rm $(GST_XXD) || true
rm $(DST_XXD) || true
test: $(GST_TARGET)
@ ./$(GST_TARGET) gsttests/basic.gst
test: $(DST_TARGET)
@ ./$(DST_TARGET) dsttests/basic.dst
valtest: $(GST_TARGET)
valgrind --leak-check=full -v ./$(GST_TARGET) gsttests/basic.gst
valtest: $(DST_TARGET)
valgrind --leak-check=full -v ./$(DST_TARGET) dsttests/basic.dst
install: $(GST_TARGET)
cp $(GST_TARGET) $(BINDIR)/gst
install: $(DST_TARGET)
cp $(DST_TARGET) $(BINDIR)/dst
uninstall:
rm $(BINDIR)/gst
rm $(BINDIR)/dst
.PHONY: clean install run debug valgrind test valtest install uninstall

View File

@ -1,8 +1,8 @@
# gst
# dst
[![Build Status](https://travis-ci.org/bakpakin/gst.svg?branch=master)](https://travis-ci.org/bakpakin/gst)
[![Build Status](https://travis-ci.org/bakpakin/dst.svg?branch=master)](https://travis-ci.org/bakpakin/dst)
gst is a language and vm that is small and embeddable, has metaprogramming
dst is a language and vm that is small and embeddable, has metaprogramming
facilities, can interoperate with C, and has enough features to make it
a useful general purpose programming language. It is a variant of
Lisp with several native useful datatypes. Some of the more interesting and
@ -18,7 +18,7 @@ defined, like the module system and macros.
There is a repl for trying out the language, as well as the ability
to run script files. This client program is separate from the core runtime, so
gst could be embedded into other programs.
dst could be embedded into other programs.
## Compiling and Running
@ -32,10 +32,10 @@ To build the runtime and launch a repl.
To run some basic programs, run the client with one argument, the name of the
file to run. For example, the client that is built will be located in the
client directory, so running a program `script.gst` from the project directory
client directory, so running a program `script.dst` from the project directory
would be
```bash
client/gst script.gst
client/dst script.dst
```
You can also use the `--help` option to see more usage information for the vm.
@ -56,7 +56,7 @@ Simply run `make test` to run the currently minimal test suite.
formating functions.
* Macro/specials system that happens before compilation
* Module system. Something similar to node's require.
* Change name (gst is the name of many projects, including GNU Smalltalk).
* Change name (dst is the name of many projects, including GNU Smalltalk).
Maybe make logo :)?
* Change C API to be stack based for fewer potential memory management
problems. This could mean making current C API internal and use separate

View File

@ -22,7 +22,7 @@
#include <stdlib.h>
#include <stdio.h>
#include <gst/gst.h>
#include <dst/dst.h>
static int client_strequal(const char *a, const char *b) {
while (*a)
@ -36,23 +36,23 @@ static int client_strequal_witharg(const char *a, const char *b) {
return *a == '=';
}
#define GST_CLIENT_HELP 1
#define GST_CLIENT_VERBOSE 2
#define GST_CLIENT_VERSION 4
#define GST_CLIENT_REPL 8
#define GST_CLIENT_NOCOLOR 16
#define GST_CLIENT_UNKNOWN 32
#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
static void printf_flags(int64_t flags, const char *col, const char *fmt, const char *arg) {
if (!(flags & GST_CLIENT_NOCOLOR))
if (!(flags & DST_CLIENT_NOCOLOR))
printf("\x1B[%sm", col);
printf(fmt, arg);
if (!(flags & GST_CLIENT_NOCOLOR))
if (!(flags & DST_CLIENT_NOCOLOR))
printf("\x1B[0m");
}
/* Simple read line functionality */
static char *gst_getline() {
static char *dst_getline() {
char *line = malloc(100);
char *linep = line;
size_t lenmax = 100;
@ -82,34 +82,64 @@ static char *gst_getline() {
}
/* Compile and run an ast */
static int debug_compile_and_run(Gst *vm, GstValue ast, int64_t flags) {
GstValue func = gst_compile(vm, vm->env, 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 != GST_FUNCTION) {
printf_flags(flags, "31", "compiler error: %s\n", (const char *)gst_to_string(vm, func));
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 (gst_run(vm, func)) {
printf_flags(flags, "31", "vm error: %s\n", (const char *)gst_to_string(vm, vm->ret));
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(Gst *vm, FILE *in, int64_t flags) {
static int debug_run(Dst *vm, FILE *in, int64_t flags) {
uint8_t *source = NULL;
uint32_t sourceSize = 0;
long bufsize;
/* 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;
GstParser p;
for (;;) {
/* Init parser */
gst_parser(&p, vm);
while (p.status != GST_PARSER_ERROR && p.status != GST_PARSER_FULL) {
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 != GST_PARSER_FULL && p.status != GST_PARSER_ROOT) {
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;
}
@ -118,7 +148,7 @@ static int debug_run(Gst *vm, FILE *in, int64_t flags) {
}
reader = buffer;
}
reader += gst_parse_cstring(&p, reader);
reader += dst_parse_cstring(&p, reader);
}
/* Check if file read in correctly */
if (p.error) {
@ -126,11 +156,11 @@ static int debug_run(Gst *vm, FILE *in, int64_t flags) {
break;
}
/* Check that parser is complete */
if (p.status != GST_PARSER_FULL && p.status != GST_PARSER_ROOT) {
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, gst_parse_consume(&p), flags)) {
if (debug_compile_and_run(vm, dst_parse_consume(&p), flags)) {
break;
}
}
@ -138,26 +168,26 @@ static int debug_run(Gst *vm, FILE *in, int64_t flags) {
}
/* A simple repl */
static int debug_repl(Gst *vm, uint64_t flags) {
static int debug_repl(Dst *vm, uint64_t flags) {
char *buffer, *reader;
GstParser p;
DstParser p;
buffer = reader = NULL;
for (;;) {
/* Init parser */
gst_parser(&p, vm);
while (p.status != GST_PARSER_ERROR && p.status != GST_PARSER_FULL) {
if (p.status == GST_PARSER_ERROR || p.status == GST_PARSER_FULL)
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)
break;
if (!reader || *reader == '\0') {
printf_flags(flags, "33", "> %s", "");
if (buffer)
free(buffer);
buffer = gst_getline();
buffer = dst_getline();
if (!buffer || *buffer == '\0')
return 0;
reader = buffer;
}
reader += gst_parse_cstring(&p, reader);
reader += dst_parse_cstring(&p, reader);
}
/* Check if file read in correctly */
if (p.error) {
@ -166,20 +196,20 @@ static int debug_repl(Gst *vm, uint64_t flags) {
continue;
}
/* Check that parser is complete */
if (p.status != GST_PARSER_FULL && p.status != GST_PARSER_ROOT) {
if (p.status != DST_PARSER_FULL && p.status != DST_PARSER_ROOT) {
printf_flags(flags, "31", "parse error: unexpected end of source%s\n", "");
continue;
}
gst_env_putc(vm, vm->env, "_", vm->ret);
gst_env_putc(vm, vm->env, "-env-", gst_wrap_table(vm->env));
if (!debug_compile_and_run(vm, gst_parse_consume(&p), flags)) {
printf_flags(flags, "36", "%s\n", (const char *) gst_description(vm, vm->ret));
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));
}
}
}
int main(int argc, const char **argv) {
Gst vm;
Dst vm;
int status = -1;
int i;
int fileRead = 0;
@ -194,20 +224,20 @@ int main(int argc, const char **argv) {
if (arg[1] == '-') {
/* Option */
if (client_strequal(arg + 2, "help")) {
flags |= GST_CLIENT_HELP;
flags |= DST_CLIENT_HELP;
} else if (client_strequal(arg + 2, "version")) {
flags |= GST_CLIENT_VERSION;
flags |= DST_CLIENT_VERSION;
} else if (client_strequal(arg + 2, "verbose")) {
flags |= GST_CLIENT_VERBOSE;
flags |= DST_CLIENT_VERBOSE;
} else if (client_strequal(arg + 2, "repl")) {
flags |= GST_CLIENT_REPL;
flags |= DST_CLIENT_REPL;
} else if (client_strequal(arg + 2, "nocolor")) {
flags |= GST_CLIENT_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);
while (*end) ++end;
int status = gst_read_integer((const uint8_t *)arg + 11, end, &val);
int status = dst_read_integer((const uint8_t *)arg + 11, end, &val);
if (status) {
if (val > 0xFFFFFFFF) {
memoryInterval = 0xFFFFFFFF;
@ -218,7 +248,7 @@ int main(int argc, const char **argv) {
}
}
} else {
flags |= GST_CLIENT_UNKNOWN;
flags |= DST_CLIENT_UNKNOWN;
}
} else {
/* Flag */
@ -226,22 +256,22 @@ int main(int argc, const char **argv) {
while (*(++c)) {
switch (*c) {
case 'h':
flags |= GST_CLIENT_HELP;
flags |= DST_CLIENT_HELP;
break;
case 'v':
flags |= GST_CLIENT_VERSION;
flags |= DST_CLIENT_VERSION;
break;
case 'V':
flags |= GST_CLIENT_VERBOSE;
flags |= DST_CLIENT_VERBOSE;
break;
case 'r':
flags |= GST_CLIENT_REPL;
flags |= DST_CLIENT_REPL;
break;
case 'c':
flags |= GST_CLIENT_NOCOLOR;
flags |= DST_CLIENT_NOCOLOR;
break;
default:
flags |= GST_CLIENT_UNKNOWN;
flags |= DST_CLIENT_UNKNOWN;
break;
}
}
@ -250,7 +280,7 @@ int main(int argc, const char **argv) {
}
/* Handle flags and options */
if ((flags & GST_CLIENT_HELP) || (flags & GST_CLIENT_UNKNOWN)) {
if ((flags & DST_CLIENT_HELP) || (flags & DST_CLIENT_UNKNOWN)) {
printf( "Usage:\n"
"%s -opts --fullopt1 --fullopt2 file1 file2...\n"
"\n"
@ -265,15 +295,15 @@ int main(int argc, const char **argv) {
argv[0]);
return 0;
}
if (flags & GST_CLIENT_VERSION) {
printf("%s\n", GST_VERSION);
if (flags & DST_CLIENT_VERSION) {
printf("%s\n", DST_VERSION);
return 0;
}
/* Set up VM */
gst_init(&vm);
dst_init(&vm);
vm.memoryInterval = memoryInterval;
gst_stl_load(&vm);
dst_stl_load(&vm);
/* Read the arguments. Only process files. */
for (i = 1; i < argc; ++i) {
@ -286,11 +316,11 @@ int main(int argc, const char **argv) {
}
}
if (!fileRead || (flags & GST_CLIENT_REPL)) {
if (!fileRead || (flags & DST_CLIENT_REPL)) {
status = debug_repl(&vm, flags);
}
gst_deinit(&vm);
dst_deinit(&vm);
return status;
}

View File

@ -20,13 +20,19 @@
* IN THE SOFTWARE.
*/
#ifndef CACHE_H_LVYZMBLR
#define CACHE_H_LVYZMBLR
#ifndef DST_BOOTSTRAP_H_defined
#define DST_BOOTSTRAP_H_defined
#include <gst/gst.h>
#define PARSE_OK 0
#define PARSE_ERROR 1
#define PARSE_UNEXPECTED_EOS 2
void gst_cache_remove_string(Gst *vm, char *strmem);
void gst_cache_remove_tuple(Gst *vm, char *tuplemem);
void gst_cache_remove_struct(Gst *vm, char *structmem);
int dst_parseb(Dst *vm, uint32_t dest, const uint8_t *src, const uint8_t **newsrc, uint32_t len);
#endif /* end of include guard: CACHE_H_LVYZMBLR */
/* Parse a c string */
int dst_parsec(Dst *vm, uint32_t dest, const char *src);
/* Parse a DST char seq (Buffer, String, Symbol) */
int dst_parse(Dst *vm, uint32_t dest, uint32_t src);
#endif /* DST_BOOTSTRAP_H_defined */

331
core/bootstrap_parse.c Normal file
View File

@ -0,0 +1,331 @@
/*
* Copyright (c) 2017 Calvin Rose
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "internal.h"
#include "bootstrap.h"
/* Checks if a string slice is equal to a string constant */
static int check_str_const(const char *ref, const uint8_t *start, const uint8_t *end) {
while (*ref && start < end) {
if (*ref != *(char *)start) return 0;
++ref;
++start;
}
return !*ref && start == end;
}
/* Quote a value */
static DstValue quote(Dst *vm, DstValue x) {
DstValue *tuple = dst_tuple_begin(vm, 2);
tuple[0] = dst_string_cvs(vm, "quote");
tuple[1] = x;
return dst_wrap_tuple(dst_tuple_end(vm, tuple));
}
/* Check if a character is whitespace */
static int is_whitespace(uint8_t c) {
return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\0' || c == ',';
}
/* Check if a character is a valid symbol character */
static int is_symbol_char(uint8_t c) {
if (c >= 'a' && c <= 'z') return 1;
if (c >= 'A' && c <= 'Z') return 1;
if (c >= '0' && c <= ':') return 1;
if (c >= '<' && c <= '@') return 1;
if (c >= '*' && c <= '/') return 1;
if (c >= '#' && c <= '&') return 1;
if (c == '_') return 1;
if (c == '^') return 1;
if (c == '!') return 1;
return 0;
}
/* Get hex digit from a letter */
static int to_hex(uint8_t c) {
if (c >= '0' && c <= '9') {
return c - '0';
} else if (c >= 'a' && c <= 'f') {
return 10 + c - 'a';
} else if (c >= 'A' && c <= 'F') {
return 10 + c - 'A';
} else {
return -1;
}
}
typedef struct {
Dst *vm;
const uint8_t *end;
const char **errmsg;
int status;
} ParseArgs;
/* Entry point of recursive descent parser */
static const uint8_t *parse(
ParseArgs *args,
const uint8_t *src,
uint32_t recur) {
Dst *vm = args->vm;
const uint8_t *end = args->end;
uint32_t qcount = 0;
uint32_t retindex = dst_args(vm);
/* Prevent stack overflow */
if (recur == 0) goto too_much_recur;
/* Trim leading whitespace and count quotes */
while (src < end && (is_whitespace(*src) || *src == '\'')) {
if (*src == '\'') {
++qcount;
}
++src;
}
/* Check for end of source */
if (src >= end) goto unexpected_eos;
/* Detect token type based on first character */
switch (*src) {
/* Numbers, symbols, simple literals */
default: {
DstReal real;
DstInteger integer;
const uint8_t *tokenend = src;
if (!is_symbol_char(*src)) goto unexpected_character;
dst_setsize(vm, retindex + 1);
while (tokenend < end && is_symbol_char(*tokenend))
tokenend++;
if (tokenend >= end) goto unexpected_eos;
if (dst_read_integer(src, tokenend, &integer)) {
dst_set_integer(vm, retindex, integer);
} else if (dst_read_real(src, tokenend, &real, 0)) {
dst_set_real(vm, retindex, real);
} else if (check_str_const("nil", src, tokenend)) {
dst_nil(vm, retindex);
} else if (check_str_const("false", src, tokenend)) {
dst_false(vm, retindex);
} else if (check_str_const("true", src, tokenend)) {
dst_true(vm, retindex);
} else {
if (*src >= '0' && *src <= '9') {
goto sym_nodigits;
} else {
dst_symbol(vm, retindex, src, tokenend - src);
}
}
src = tokenend;
break;
}
case ':': {
const uint8_t *tokenend = ++src;
dst_setsize(vm, retindex + 1);
while (tokenend < end && is_symbol_char(*tokenend))
tokenend++;
if (tokenend >= end) goto unexpected_eos;
dst_string(vm, retindex, src, tokenend - src);
src = tokenend;
break;
}
/* String literals */
case '"': {
const uint8_t *strend = ++src;
uint32_t len = 0;
int containsEscape = 0;
/* Preprocess string to check for escapes and string end */
while (strend < end && *strend != '"') {
len++;
if (*strend++ == '\\') {
constainsEscape = 1;
if (strend >= end) goto unexpected_eos;
if (*strend == 'h') {
strend += 2;
if (strend >= end) goto unexpected_eos;
}
}
}
if (containsEscape) {
uint8_t *buf = dst_string_begin(vm, len);
uint8_t *write = buf;
const uint8_t *scan = src;
while (scan < strend) {
if (*scan == '\\') {
scan++;
switch (*++scan) {
case 'n': *write++ = '\n'; break;
case 'r': *write++ = '\r'; break;
case 't': *write++ = '\t'; break;
case 'f': *write++ = '\f'; break;
case '0': *write++ = '\0'; break;
case '"': *write++ = '"'; break;
case '\'': *write++ = '\''; break;
case 'z': *write++ = '\0'; break;
case 'e': *write++ = 27; break;
case 'h': {
int d1 = to_hex(scan[0]);
int d2 = to_hex(scan[1]);
if (d1 < 0 || d2 < 0) goto invalid_hex;
*write = 16 * d1 + d2;
break;
}
default:
goto unknown_strescape;
}
} else {
*write++ = *scan++;
}
}
dst_string_end(vm, retindex, buf);
} else {
dst_string(vm, retindex, src, strend - src);
}
src = strend + 1;
break;
}
/* Data Structure literals */
case: '(':
case: '[':
case: '{': {
uint8_t close;
uint32_t tmpindex;
switch (*src++) {
case '(': close = ')'; break;
case '[': close = ']'; break;
case '{': close = '}'; break;
default: close = ')'; break;
}
/* Recursively parse inside literal */
while (*src != close) {
src = parse(args, src, recur - 1);
if (*(args->errmsg) || !src) return src;
}
src++;
tmpindex = dst_args(vm);
dst_push_space(vm, 1);
switch (close) {
case ')':
dst_tuple_n(vm, tmpindex, retindex, tmpindex - retindex);
break;
case ']':
dst_array_n(vm, tmpindex, retindex, tmpindex - retindex);
break;
case '}':
if ((tmpindex - retindex) % 2) goto struct_oddargs;
dst_struct_n(vm, tmpindex, retindex, tmpindex - retindex);
break;
}
dst_move(vm, retindex, tmpindex);
dst_setsize(vm, retindex + 1);
break;
}
}
/* Quote the returned value qcount times */
while (qcount--) {
dst_set_arg(vm, retindex, quote(vm, dst_arg(vm, retindex)));
}
/* Return the new source position for further calls */
return src;
/* Errors below */
unexpected_eos:
*(args->errmsg) = "unexpected end of source";
args->status = PARSE_UNEXPECTED_EOS;
return NULL;
unexpected_character:
*(args->errmsg) = "unexpected character";
args->status = PARSE_ERROR;
return src;
sym_nodigits:
*(args->errmsg) = "symbols cannot start with digits";
args->status = PARSE_ERROR;
return src;
struct_oddargs:
*(args->errmsg) = "struct literal needs an even number of arguments";
args->status = PARSE_ERROR;
return src;
unknown_strescape:
*(args->errmsg) = "unknown string escape sequence";
args->status = PARSE_ERROR;
return src;
invalid_hex:
*(args->errmsg) = "invalid hex escape in string";
args->status = PARSE_ERROR;
return src;
too_much_recur:
*(args->errmsg) = "recursed too deeply in parsing";
args->status = PARSE_ERROR;
return src;
}
/* Parse an array of bytes */
int dst_parseb(Dst *vm, uint32_t dest, const uint8_t *src, const uint8_t **newsrc, uint32_t len) {
ParseArgs args;
uint32_t toploc = dst_args(vm);
const uint8_t *srcrest;
args.vm = vm;
args.status = PARSE_OK;
args.end = src + len;
args.errmsg = NULL;
srcrest = parse(&args, src, 2048) || src;
if (newsrc) {
*newsrc = srcrest;
}
if (args.errmsg) {
/* Error */
dst_cstring(vm, dest, args.errmsg);
} else {
/* Success */
dst_move(vm, dest, toploc);
}
dst_setsize(vm, toploc);
return args.status;
}
/* Parse a c string */
int dst_parsec(Dst *vm, uint32_t dest, const char *src) {
uint32_t len = 0;
while (src[len]) ++len;
return dst_parseb(vm, dest, (const uint8_t *)src, NULL, len);
}
/* Parse a DST char seq (Buffer, String, Symbol) */
int dst_parse(Dst *vm, uint32_t dest, uint32_t src) {
uint32_t len;
const uint8_t *bytes = dst_bytes(vm, src, &len);
return dst_parseb(vm, dest, bytes, NULL, len);
}

82
core/client.c Normal file
View File

@ -0,0 +1,82 @@
/*
* 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 <stdlib.h>
#include <stdio.h>
#include <dst/dst.h>
#include "bootstrap.h"
/* 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;
}
int main(int argc, const char **argv) {
Dst *vm = dst_init();
for (;;) {
char *line = dst_getline();
if (line) {
uint32_t len = 0;
int status = dst_parsec(vm, 0, line);
if (status == PARSE_OK) {
dst_description(vm, 0, 0);
}
const uint8_t *b = dst_bytes(vm, 0, &len);
for (uint32_t i = 0; i < len; ++i) {
putc(b[i]);
}
putc('\n');
} else {
break;
}
}
dst_deinit(vm);
return 0;
}

File diff suppressed because it is too large Load Diff

974
core/ds.c

File diff suppressed because it is too large Load Diff

View File

@ -20,59 +20,59 @@
* IN THE SOFTWARE.
*/
#include <gst/gst.h>
#include "internal.h"
static GstTable *gst_env_keytab(Gst *vm, GstTable *env, const char *keyword) {
GstTable *tab;
GstValue key = gst_string_cv(vm, keyword);
GstValue maybeTab = gst_table_get(env, key);
if (maybeTab.type != GST_TABLE) {
tab = gst_table(vm, 10);
gst_table_put(vm, env, key, gst_wrap_table(tab));
static DstTable *dst_env_keytab(Dst *vm, DstTable *env, const char *keyword) {
DstTable *tab;
DstValue key = dst_string_cv(vm, keyword);
DstValue maybeTab = dst_table_get(env, key);
if (maybeTab.type != DST_TABLE) {
tab = dst_table(vm, 10);
dst_table_put(vm, env, key, dst_wrap_table(tab));
} else {
tab = maybeTab.data.table;
}
return tab;
}
GstTable *gst_env_nils(Gst *vm, GstTable *env) {
return gst_env_keytab(vm, env, "nils");
DstTable *dst_env_nils(Dst *vm, DstTable *env) {
return dst_env_keytab(vm, env, "nils");
}
GstTable *gst_env_meta(Gst *vm, GstTable *env) {
return gst_env_keytab(vm, env, "meta");
DstTable *dst_env_meta(Dst *vm, DstTable *env) {
return dst_env_keytab(vm, env, "meta");
}
/* Add many global variables and bind to nil */
static void mergenils(Gst *vm, GstTable *destEnv, GstTable *nils) {
const GstValue *data = nils->data;
static void mergenils(Dst *vm, DstTable *destEnv, DstTable *nils) {
const DstValue *data = nils->data;
uint32_t len = nils->capacity;
uint32_t i;
GstTable *destNils = gst_env_nils(vm, destEnv);
DstTable *destNils = dst_env_nils(vm, destEnv);
for (i = 0; i < len; i += 2) {
if (data[i].type == GST_SYMBOL) {
gst_table_put(vm, destEnv, data[i], gst_wrap_nil());
gst_table_put(vm, destNils, data[i], gst_wrap_boolean(1));
if (data[i].type == DST_SYMBOL) {
dst_table_put(vm, destEnv, data[i], dst_wrap_nil());
dst_table_put(vm, destNils, data[i], dst_wrap_boolean(1));
}
}
}
/* Add many global variable metadata */
static void mergemeta(Gst *vm, GstTable *destEnv, GstTable *meta) {
const GstValue *data = meta->data;
static void mergemeta(Dst *vm, DstTable *destEnv, DstTable *meta) {
const DstValue *data = meta->data;
uint32_t len = meta->capacity;
uint32_t i;
GstTable *destMeta = gst_env_meta(vm, destEnv);
DstTable *destMeta = dst_env_meta(vm, destEnv);
for (i = 0; i < len; i += 2) {
if (data[i].type == GST_SYMBOL) {
gst_table_put(vm, destMeta, data[i], data[i + 1]);
if (data[i].type == DST_SYMBOL) {
dst_table_put(vm, destMeta, data[i], data[i + 1]);
}
}
}
/* Simple strequal between gst string ans c string, no 0s in b allowed */
/* Simple strequal between dst string ans c string, no 0s in b allowed */
static int streq(const char *str, const uint8_t *b) {
uint32_t len = gst_string_length(b);
uint32_t len = dst_string_length(b);
uint32_t i;
const uint8_t *ustr = (const uint8_t *)str;
for (i = 0; i < len; ++i) {
@ -83,52 +83,52 @@ static int streq(const char *str, const uint8_t *b) {
}
/* Add many global variables */
void gst_env_merge(Gst *vm, GstTable *destEnv, GstTable *srcEnv) {
const GstValue *data = srcEnv->data;
void dst_env_merge(Dst *vm, DstTable *destEnv, DstTable *srcEnv) {
const DstValue *data = srcEnv->data;
uint32_t len = srcEnv->capacity;
uint32_t i;
for (i = 0; i < len; i += 2) {
if (data[i].type == GST_SYMBOL) {
gst_table_put(vm, destEnv, data[i], data[i + 1]);
} else if (data[i].type == GST_STRING) {
if (data[i].type == DST_SYMBOL) {
dst_table_put(vm, destEnv, data[i], data[i + 1]);
} else if (data[i].type == DST_STRING) {
const uint8_t *k = data[i].data.string;
if (streq("nils", k)) {
if (data[i + 1].type == GST_TABLE)
if (data[i + 1].type == DST_TABLE)
mergenils(vm, destEnv, data[i + 1].data.table);
} else if (streq("meta", k)) {
if (data[i + 1].type == GST_TABLE)
if (data[i + 1].type == DST_TABLE)
mergemeta(vm, destEnv, data[i + 1].data.table);
}
}
}
}
void gst_env_put(Gst *vm, GstTable *env, GstValue key, GstValue value) {
GstTable *meta = gst_env_meta(vm, env);
gst_table_put(vm, meta, key, gst_wrap_nil());
gst_table_put(vm, env, key, value);
if (value.type == GST_NIL) {
gst_table_put(vm, gst_env_nils(vm, env), key, gst_wrap_boolean(1));
void dst_env_put(Dst *vm, DstTable *env, DstValue key, DstValue value) {
DstTable *meta = dst_env_meta(vm, env);
dst_table_put(vm, meta, key, dst_wrap_nil());
dst_table_put(vm, env, key, value);
if (value.type == DST_NIL) {
dst_table_put(vm, dst_env_nils(vm, env), key, dst_wrap_boolean(1));
}
}
void gst_env_putc(Gst *vm, GstTable *env, const char *key, GstValue value) {
GstValue keyv = gst_string_cvs(vm, key);
gst_env_put(vm, env, keyv, value);
void dst_env_putc(Dst *vm, DstTable *env, const char *key, DstValue value) {
DstValue keyv = dst_string_cvs(vm, key);
dst_env_put(vm, env, keyv, value);
}
void gst_env_putvar(Gst *vm, GstTable *env, GstValue key, GstValue value) {
GstTable *meta = gst_env_meta(vm, env);
GstTable *newmeta = gst_table(vm, 4);
GstArray *ref = gst_array(vm, 1);
void dst_env_putvar(Dst *vm, DstTable *env, DstValue key, DstValue value) {
DstTable *meta = dst_env_meta(vm, env);
DstTable *newmeta = dst_table(vm, 4);
DstArray *ref = dst_array(vm, 1);
ref->count = 1;
ref->data[0] = value;
gst_table_put(vm, env, key, gst_wrap_array(ref));
gst_table_put(vm, newmeta, gst_string_cv(vm, "mutable"), gst_wrap_boolean(1));
gst_table_put(vm, meta, key, gst_wrap_table(newmeta));
dst_table_put(vm, env, key, dst_wrap_array(ref));
dst_table_put(vm, newmeta, dst_string_cv(vm, "mutable"), dst_wrap_boolean(1));
dst_table_put(vm, meta, key, dst_wrap_table(newmeta));
}
void gst_env_putvarc(Gst *vm, GstTable *env, const char *key, GstValue value) {
GstValue keyv = gst_string_cvs(vm, key);
gst_env_putvar(vm, env, keyv, value);
void dst_env_putvarc(Dst *vm, DstTable *env, const char *key, DstValue value) {
DstValue keyv = dst_string_cvs(vm, key);
dst_env_putvar(vm, env, keyv, value);
}

227
core/gc.c
View File

@ -20,8 +20,7 @@
* IN THE SOFTWARE.
*/
#include <gst/gst.h>
#include "cache.h"
#include "internal.h"
/* The metadata header associated with an allocated block of memory */
#define gc_header(mem) ((GCMemoryHeader *)(mem) - 1)
@ -35,31 +34,31 @@ struct GCMemoryHeader {
};
/* Mark a chunk of memory as reachable for the gc */
void gst_mark_mem(Gst *vm, void *mem) {
void dst_mark_mem(Dst *vm, void *mem) {
gc_header(mem)->color = vm->black;
}
/* Helper to mark function environments */
static void gst_mark_funcenv(Gst *vm, GstFuncEnv *env) {
static void dst_mark_funcenv(Dst *vm, DstFuncEnv *env) {
if (gc_header(env)->color != vm->black) {
gc_header(env)->color = vm->black;
if (env->thread) {
GstValueUnion x;
DstValueUnion x;
x.thread = env->thread;
gst_mark(vm, x, GST_THREAD);
dst_mark(vm, x, DST_THREAD);
}
if (env->values) {
uint32_t count = env->stackOffset;
uint32_t i;
gc_header(env->values)->color = vm->black;
for (i = 0; i < count; ++i)
gst_mark_value(vm, env->values[i]);
dst_mark_value(vm, env->values[i]);
}
}
}
/* GC helper to mark a FuncDef */
static void gst_mark_funcdef(Gst *vm, GstFuncDef *def) {
static void dst_mark_funcdef(Dst *vm, DstFuncDef *def) {
if (gc_header(def)->color != vm->black) {
gc_header(def)->color = vm->black;
gc_header(def->byteCode)->color = vm->black;
@ -68,140 +67,140 @@ static void gst_mark_funcdef(Gst *vm, GstFuncDef *def) {
count = def->literalsLen;
gc_header(def->literals)->color = vm->black;
for (i = 0; i < count; ++i)
gst_mark_value(vm, def->literals[i]);
dst_mark_value(vm, def->literals[i]);
}
}
}
/* Helper to mark a stack frame. Returns the next stackframe. */
static GstValue *gst_mark_stackframe(Gst *vm, GstValue *stack) {
static DstValue *dst_mark_stackframe(Dst *vm, DstValue *stack) {
uint32_t i;
gst_mark_value(vm, gst_frame_callee(stack));
if (gst_frame_env(stack) != NULL)
gst_mark_funcenv(vm, gst_frame_env(stack));
for (i = 0; i < gst_frame_size(stack); ++i)
gst_mark_value(vm, stack[i]);
return stack + gst_frame_size(stack) + GST_FRAME_SIZE;
dst_mark_value(vm, dst_frame_callee(stack));
if (dst_frame_env(stack) != NULL)
dst_mark_funcenv(vm, dst_frame_env(stack));
for (i = 0; i < dst_frame_size(stack); ++i)
dst_mark_value(vm, stack[i]);
return stack + dst_frame_size(stack) + DST_FRAME_SIZE;
}
/* Wrapper for marking values */
void gst_mark_value(Gst *vm, GstValue x) {
gst_mark(vm, x.data, x.type);
void dst_mark_value(Dst *vm, DstValue x) {
dst_mark(vm, x.data, x.type);
}
/* Mark allocated memory associated with a value. This is
* the main function for doing the garbage collection mark phase. */
void gst_mark(Gst *vm, GstValueUnion x, GstType type) {
void dst_mark(Dst *vm, DstValueUnion x, DstType type) {
/* Allow for explicit tail recursion */
begin:
switch (type) {
default:
break;
case GST_STRING:
case GST_SYMBOL:
gc_header(gst_string_raw(x.string))->color = vm->black;
case DST_STRING:
case DST_SYMBOL:
gc_header(dst_string_raw(x.string))->color = vm->black;
break;
case GST_BYTEBUFFER:
case DST_BYTEBUFFER:
gc_header(x.buffer)->color = vm->black;
gc_header(x.buffer->data)->color = vm->black;
break;
case GST_ARRAY:
case DST_ARRAY:
if (gc_header(x.array)->color != vm->black) {
uint32_t i, count;
count = x.array->count;
gc_header(x.array)->color = vm->black;
gc_header(x.array->data)->color = vm->black;
for (i = 0; i < count; ++i)
gst_mark_value(vm, x.array->data[i]);
dst_mark_value(vm, x.array->data[i]);
}
break;
case GST_TUPLE:
if (gc_header(gst_tuple_raw(x.tuple))->color != vm->black) {
case DST_TUPLE:
if (gc_header(dst_tuple_raw(x.tuple))->color != vm->black) {
uint32_t i, count;
count = gst_tuple_length(x.tuple);
gc_header(gst_tuple_raw(x.tuple))->color = vm->black;
count = dst_tuple_length(x.tuple);
gc_header(dst_tuple_raw(x.tuple))->color = vm->black;
for (i = 0; i < count; ++i)
gst_mark_value(vm, x.tuple[i]);
dst_mark_value(vm, x.tuple[i]);
}
break;
case GST_STRUCT:
if (gc_header(gst_struct_raw(x.st))->color != vm->black) {
case DST_STRUCT:
if (gc_header(dst_struct_raw(x.st))->color != vm->black) {
uint32_t i, count;
count = gst_struct_capacity(x.st);
gc_header(gst_struct_raw(x.st))->color = vm->black;
count = dst_struct_capacity(x.st);
gc_header(dst_struct_raw(x.st))->color = vm->black;
for (i = 0; i < count; ++i)
gst_mark_value(vm, x.st[i]);
dst_mark_value(vm, x.st[i]);
}
break;
case GST_THREAD:
case DST_THREAD:
if (gc_header(x.thread)->color != vm->black) {
GstThread *thread = x.thread;
GstValue *frame = thread->data + GST_FRAME_SIZE;
GstValue *end = thread->data + thread->count;
DstThread *thread = x.thread;
DstValue *frame = thread->data + DST_FRAME_SIZE;
DstValue *end = thread->data + thread->count;
gc_header(thread)->color = vm->black;
gc_header(thread->data)->color = vm->black;
while (frame <= end)
frame = gst_mark_stackframe(vm, frame);
if (thread->parent)
gst_mark_value(vm, gst_wrap_thread(thread->parent));
if (thread->errorParent)
gst_mark_value(vm, gst_wrap_thread(thread->errorParent));
}
break;
case GST_FUNCTION:
if (gc_header(x.function)->color != vm->black) {
GstFunction *f = x.function;
gc_header(f)->color = vm->black;
gst_mark_funcdef(vm, f->def);
if (f->env)
gst_mark_funcenv(vm, f->env);
if (f->parent) {
GstValueUnion pval;
pval.function = f->parent;
gst_mark(vm, pval, GST_FUNCTION);
frame = dst_mark_stackframe(vm, frame);
if (thread->parent) {
x.thread = thread->parent;
goto begin;
}
}
break;
case GST_TABLE:
case DST_FUNCTION:
if (gc_header(x.function)->color != vm->black) {
DstFunction *f = x.function;
gc_header(f)->color = vm->black;
dst_mark_funcdef(vm, f->def);
if (f->env)
dst_mark_funcenv(vm, f->env);
if (f->parent) {
DstValueUnion pval;
pval.function = f->parent;
dst_mark(vm, pval, DST_FUNCTION);
}
}
break;
case DST_TABLE:
if (gc_header(x.table)->color != vm->black) {
uint32_t i;
gc_header(x.table)->color = vm->black;
gc_header(x.table->data)->color = vm->black;
for (i = 0; i < x.table->capacity; i += 2) {
gst_mark_value(vm, x.table->data[i]);
gst_mark_value(vm, x.table->data[i + 1]);
dst_mark_value(vm, x.table->data[i]);
dst_mark_value(vm, x.table->data[i + 1]);
}
}
break;
case GST_USERDATA:
if (gc_header(gst_udata_header(x.pointer))->color != vm->black) {
GstUserdataHeader *h = gst_udata_header(x.pointer);
case DST_USERDATA:
{
DstUserdataHeader *h = dst_udata_header(x.pointer);
gc_header(h)->color = vm->black;
if (h->type->gcmark)
h->type->gcmark(vm, x.pointer, h->size);
}
break;
case GST_FUNCENV:
gst_mark_funcenv(vm, x.env);
case DST_FUNCENV:
dst_mark_funcenv(vm, x.env);
break;
case GST_FUNCDEF:
gst_mark_funcdef(vm, x.def);
case DST_FUNCDEF:
dst_mark_funcdef(vm, x.def);
break;
}
}
/* Iterate over all allocated memory, and free memory that is not
* marked as reachable. Flip the gc color flag for next sweep. */
void gst_sweep(Gst *vm) {
void dst_sweep(Dst *vm) {
GCMemoryHeader *previous = NULL;
GCMemoryHeader *current = vm->blocks;
GCMemoryHeader *next;
@ -214,20 +213,20 @@ void gst_sweep(Gst *vm) {
vm->blocks = next;
}
if (current->tags) {
if (current->tags & GST_MEMTAG_STRING)
gst_cache_remove_string(vm, (char *)(current + 1));
if (current->tags & GST_MEMTAG_STRUCT)
gst_cache_remove_struct(vm, (char *)(current + 1));
if (current->tags & GST_MEMTAG_TUPLE)
gst_cache_remove_tuple(vm, (char *)(current + 1));
if (current->tags & GST_MEMTAG_USER) {
GstUserdataHeader *h = (GstUserdataHeader *)(current + 1);
if (current->tags & DST_MEMTAG_STRING)
dst_cache_remove_string(vm, (char *)(current + 1));
if (current->tags & DST_MEMTAG_STRUCT)
dst_cache_remove_struct(vm, (char *)(current + 1));
if (current->tags & DST_MEMTAG_TUPLE)
dst_cache_remove_tuple(vm, (char *)(current + 1));
if (current->tags & DST_MEMTAG_USER) {
DstUserdataHeader *h = (DstUserdataHeader *)(current + 1);
if (h->type->finalize) {
h->type->finalize(vm, h + 1, h->size);
}
}
}
gst_raw_free(current);
dst_raw_free(current);
} else {
previous = current;
}
@ -238,10 +237,10 @@ void gst_sweep(Gst *vm) {
}
/* Prepare a memory block */
static void *gst_alloc_prepare(Gst *vm, char *rawBlock, uint32_t size) {
static void *dst_alloc_prepare(Dst *vm, char *rawBlock, uint32_t size) {
GCMemoryHeader *mdata;
if (rawBlock == NULL) {
GST_OUT_OF_MEMORY;
return NULL;
}
vm->nextCollection += size;
mdata = (GCMemoryHeader *)rawBlock;
@ -253,48 +252,78 @@ static void *gst_alloc_prepare(Gst *vm, char *rawBlock, uint32_t size) {
}
/* Allocate some memory that is tracked for garbage collection */
void *gst_alloc(Gst *vm, uint32_t size) {
void *dst_alloc(Dst *vm, uint32_t size) {
uint32_t totalSize = size + sizeof(GCMemoryHeader);
return gst_alloc_prepare(vm, gst_raw_alloc(totalSize), totalSize);
void *mem = dst_alloc_prepare(vm, dst_raw_alloc(totalSize), totalSize);
if (!mem) {
DST_LOW_MEMORY;
dst_collect(vm);
mem = dst_alloc_prepare(vm, dst_raw_alloc(totalSize), totalSize);
if (!mem) {
DST_OUT_OF_MEMORY;
}
}
return mem;
}
/* Allocate some zeroed memory that is tracked for garbage collection */
void *gst_zalloc(Gst *vm, uint32_t size) {
void *dst_zalloc(Dst *vm, uint32_t size) {
uint32_t totalSize = size + sizeof(GCMemoryHeader);
return gst_alloc_prepare(vm, gst_raw_calloc(1, totalSize), totalSize);
void *mem = dst_alloc_prepare(vm, dst_raw_calloc(1, totalSize), totalSize);
if (!mem) {
DST_LOW_MEMORY;
dst_collect(vm);
mem = dst_alloc_prepare(vm, dst_raw_calloc(1, totalSize), totalSize);
if (!mem) {
DST_OUT_OF_MEMORY;
}
}
return mem;
}
/* Tag some memory to mark it with special properties */
void gst_mem_tag(void *mem, uint32_t tags) {
void dst_mem_tag(void *mem, uint32_t tags) {
GCMemoryHeader *mh = (GCMemoryHeader *)mem - 1;
mh->tags |= tags;
}
/* Run garbage collection */
void gst_collect(Gst *vm) {
void dst_collect(Dst *vm) {
DstValue x;
/* Thread can be null */
if (vm->thread)
gst_mark_value(vm, gst_wrap_thread(vm->thread));
gst_mark_value(vm, gst_wrap_table(vm->modules));
gst_mark_value(vm, gst_wrap_table(vm->registry));
gst_mark_value(vm, gst_wrap_table(vm->env));
gst_mark_value(vm, vm->ret);
gst_sweep(vm);
if (vm->thread) {
x.type = DST_THREAD;
x.data.thread = vm->thread;
dst_mark_value(vm, x);
}
x.type = DST_TABLE;
x.data.table = vm->modules;
dst_mark_value(vm, x);
x.data.table = vm->registry;
dst_mark_value(vm, x);
x.data.table = vm->env;
dst_mark_value(vm, x);
dst_mark_value(vm, vm->ret);
dst_sweep(vm);
vm->nextCollection = 0;
}
/* Run garbage collection if needed */
void gst_maybe_collect(Gst *vm) {
void dst_maybe_collect(Dst *vm) {
if (vm->nextCollection >= vm->memoryInterval)
gst_collect(vm);
dst_collect(vm);
}
/* Free all allocated memory */
void gst_clear_memory(Gst *vm) {
void dst_clear_memory(Dst *vm) {
GCMemoryHeader *current = vm->blocks;
while (current) {
GCMemoryHeader *next = current->next;
gst_raw_free(current);
dst_raw_free(current);
current = next;
}
vm->blocks = NULL;

View File

@ -1,583 +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 <gst/gst.h>
#include "cache.h"
/****/
/* Cache */
/****/
/* Calculate hash for string */
static uint32_t gst_string_calchash(const uint8_t *str, uint32_t len) {
const uint8_t *end = str + len;
uint32_t hash = 5381;
while (str < end)
hash = (hash << 5) + hash + *str++;
return hash;
}
/* Calculate hash for tuple (and struct) */
static uint32_t gst_tuple_calchash(const GstValue *tuple, uint32_t len) {
const GstValue *end = tuple + len;
uint32_t hash = 5381;
while (tuple < end)
hash = (hash << 5) + hash + gst_hash(*tuple++);
return hash;
}
/* Check if two not necesarrily finalized immutable values
* are equal. Does caching logic */
static int gst_cache_equal(GstValue x, GstValue y) {
uint32_t i, len;
if (x.type != y.type) return 0;
switch (x.type) {
/* Don't bother implementing equality checks for all types. We only care
* about immutable data structures */
default:
return 0;
case GST_STRING:
if (gst_string_hash(x.data.string) != gst_string_hash(y.data.string)) return 0;
if (gst_string_length(x.data.string) != gst_string_length(y.data.string)) return 0;
len = gst_string_length(x.data.string);
for (i = 0; i < len; ++i)
if (x.data.string[i] != y.data.string[i])
return 0;
return 1;
case GST_STRUCT:
if (gst_struct_hash(x.data.st) != gst_struct_hash(y.data.st)) return 0;
if (gst_struct_length(x.data.st) != gst_struct_length(y.data.st)) return 0;
len = gst_struct_capacity(x.data.st);
for (i = 0; i < len; ++i)
if (!gst_equals(x.data.st[i], y.data.st[i]))
return 0;
return 1;
case GST_TUPLE:
if (gst_tuple_hash(x.data.tuple) != gst_tuple_hash(y.data.tuple)) return 0;
if (gst_tuple_length(x.data.tuple) != gst_tuple_length(y.data.tuple)) return 0;
len = gst_tuple_length(x.data.tuple);
for (i = 0; i < len; ++i)
if (!gst_equals(x.data.tuple[i], y.data.tuple[i]))
return 0;
return 1;
}
}
/* Check if a value x is equal to a string. Special version of
* gst_cache_equal */
static int gst_cache_strequal(GstValue x, const uint8_t *str, uint32_t len, uint32_t hash) {
uint32_t i;
if (x.type != GST_STRING) return 0;
if (gst_string_hash(x.data.string) != hash) return 0;
if (gst_string_length(x.data.string) != len) return 0;
for (i = 0; i < len; ++i)
if (x.data.string[i] != str[i])
return 0;
return 1;
}
/* Find an item in the cache and return its location.
* If the item is not found, return the location
* where one would put it. */
static GstValue *gst_cache_find(Gst *vm, GstValue key, int *success) {
uint32_t bounds[4];
uint32_t i, j, index;
uint32_t hash = gst_hash(key);
GstValue *firstEmpty = NULL;
index = hash % vm->cache_capacity;
bounds[0] = index;
bounds[1] = vm->cache_capacity;
bounds[2] = 0;
bounds[3] = index;
for (j = 0; j < 4; j += 2)
for (i = bounds[j]; i < bounds[j+1]; ++i) {
GstValue test = vm->cache[i];
/* Check empty spots */
if (test.type == GST_NIL) {
if (firstEmpty == NULL)
firstEmpty = vm->cache + i;
goto notfound;
}
/* Check for marked deleted - use booleans as deleted */
if (test.type == GST_BOOLEAN) {
if (firstEmpty == NULL)
firstEmpty = vm->cache + i;
continue;
}
if (gst_cache_equal(test, key)) {
/* Replace first deleted */
*success = 1;
if (firstEmpty != NULL) {
*firstEmpty = test;
vm->cache[i].type = GST_BOOLEAN;
return firstEmpty;
}
return vm->cache + i;
}
}
notfound:
*success = 0;
return firstEmpty;
}
/* Find an item in the cache and return its location.
* If the item is not found, return the location
* where one would put it. Special case of gst_cache_find */
static GstValue *gst_cache_strfind(Gst *vm,
const uint8_t *str,
uint32_t len,
uint32_t hash,
int *success) {
uint32_t bounds[4];
uint32_t i, j, index;
GstValue *firstEmpty = NULL;
index = hash % vm->cache_capacity;
bounds[0] = index;
bounds[1] = vm->cache_capacity;
bounds[2] = 0;
bounds[3] = index;
for (j = 0; j < 4; j += 2)
for (i = bounds[j]; i < bounds[j+1]; ++i) {
GstValue test = vm->cache[i];
/* Check empty spots */
if (test.type == GST_NIL) {
if (firstEmpty == NULL)
firstEmpty = vm->cache + i;
goto notfound;
}
/* Check for marked deleted - use booleans as deleted */
if (test.type == GST_BOOLEAN) {
if (firstEmpty == NULL)
firstEmpty = vm->cache + i;
continue;
}
if (gst_cache_strequal(test, str, len, hash)) {
/* Replace first deleted */
*success = 1;
if (firstEmpty != NULL) {
*firstEmpty = test;
vm->cache[i].type = GST_BOOLEAN;
return firstEmpty;
}
return vm->cache + i;
}
}
notfound:
*success = 0;
return firstEmpty;
}
/* Resize the cache. */
static void gst_cache_resize(Gst *vm, uint32_t newCapacity) {
uint32_t i, oldCapacity;
GstValue *oldCache = vm->cache;
GstValue *newCache = gst_raw_calloc(1, newCapacity * sizeof(GstValue));
if (newCache == NULL)
GST_OUT_OF_MEMORY;
oldCapacity = vm->cache_capacity;
vm->cache = newCache;
vm->cache_capacity = newCapacity;
vm->cache_deleted = 0;
/* Add all of the old strings back */
for (i = 0; i < oldCapacity; ++i) {
int status;
GstValue *bucket;
GstValue x = oldCache[i];
if (x.type != GST_NIL && x.type != GST_BOOLEAN) {
bucket = gst_cache_find(vm, x, &status);
if (status || bucket == NULL) {
/* there was a problem with the algorithm. */
break;
}
*bucket = x;
}
}
/* Free the old cache */
gst_raw_free(oldCache);
}
/* Add a value to the cache given we know it is not
* already in the cache and we have a bucket. */
static GstValue gst_cache_add_bucket(Gst *vm, GstValue x, GstValue *bucket) {
if ((vm->cache_count + vm->cache_deleted) * 2 > vm->cache_capacity) {
int status;
gst_cache_resize(vm, vm->cache_count * 4);
bucket = gst_cache_find(vm, x, &status);
}
/* Mark the memory for the gc */
switch (x.type) {
default:
break;
case GST_STRING:
gst_mem_tag(gst_string_raw(x.data.string), GST_MEMTAG_STRING);
break;
case GST_STRUCT:
gst_mem_tag(gst_struct_raw(x.data.st), GST_MEMTAG_STRUCT);
break;
case GST_TUPLE:
gst_mem_tag(gst_tuple_raw(x.data.tuple), GST_MEMTAG_TUPLE);
break;
}
/* Add x to the cache */
vm->cache_count++;
*bucket = x;
return x;
}
/* Add a value to the cache */
static GstValue gst_cache_add(Gst *vm, GstValue x) {
int status = 0;
GstValue *bucket = gst_cache_find(vm, x, &status);
if (!status) {
return gst_cache_add_bucket(vm, x, bucket);
} else {
return *bucket;
}
}
/* Remove a value from the cache */
static void gst_cache_remove(Gst *vm, GstValue x) {
int status = 0;
GstValue *bucket = gst_cache_find(vm, x, &status);
if (status) {
vm->cache_count--;
vm->cache_deleted++;
bucket->type = GST_BOOLEAN;
}
}
/* Remove a string from cache (called from gc) */
void gst_cache_remove_string(Gst *vm, char *strmem) {
GstValue x;
x.type = GST_STRING;
x.data.string = (const uint8_t *)(strmem + 2 * sizeof(uint32_t));
gst_cache_remove(vm, x);
}
/* Remove a tuple from cache (called from gc) */
void gst_cache_remove_tuple(Gst *vm, char *tuplemem) {
GstValue x;
x.type = GST_TUPLE;
x.data.tuple = (const GstValue *)(tuplemem + 2 * sizeof(uint32_t));
gst_cache_remove(vm, x);
}
/* Remove a struct from cache (called from gc) */
void gst_cache_remove_struct(Gst *vm, char *structmem) {
GstValue x;
x.type = GST_STRUCT;
x.data.st = (const GstValue *)(structmem + 2 * sizeof(uint32_t));
gst_cache_remove(vm, x);
}
/****/
/* Struct Functions */
/****/
/* Begin creation of a struct */
GstValue *gst_struct_begin(Gst *vm, uint32_t count) {
char *data = gst_zalloc(vm, sizeof(uint32_t) * 2 + 4 * count * sizeof(GstValue));
GstValue *st = (GstValue *) (data + 2 * sizeof(uint32_t));
gst_struct_length(st) = count;
return st;
}
/* Find an item in a struct */
static const GstValue *gst_struct_find(const GstValue *st, GstValue key) {
uint32_t cap = gst_struct_capacity(st);
uint32_t index = (gst_hash(key) % (cap / 2)) * 2;
uint32_t i;
for (i = index; i < cap; i += 2)
if (st[i].type == GST_NIL || gst_equals(st[i], key))
return st + i;
for (i = 0; i < index; i += 2)
if (st[i].type == GST_NIL || gst_equals(st[i], key))
return st + i;
return NULL;
}
/* Put a kv pair into a struct that has not yet been fully constructed.
* Behavior is undefined if too many keys are added, or if a key is added
* twice. Nil keys and values are ignored. */
void gst_struct_put(GstValue *st, GstValue key, GstValue value) {
uint32_t cap = gst_struct_capacity(st);
uint32_t hash = gst_hash(key);
uint32_t index = (hash % (cap / 2)) * 2;
uint32_t i, j, dist;
uint32_t bounds[4] = {index, cap, 0, index};
if (key.type == GST_NIL || value.type == GST_NIL) return;
for (dist = 0, j = 0; j < 4; j += 2)
for (i = bounds[j]; i < bounds[j + 1]; i += 2, dist += 2) {
int status;
uint32_t otherhash, otherindex, otherdist;
/* We found an empty slot, so just add key and value */
if (st[i].type == GST_NIL) {
st[i] = key;
st[i + 1] = value;
return;
}
/* Robinhood hashing - check if colliding kv pair
* is closer to their source than current. */
otherhash = gst_hash(st[i]);
otherindex = (otherhash % (cap / 2)) * 2;
otherdist = (i + cap - otherindex) % cap;
if (dist < otherdist)
status = -1;
else if (otherdist < dist)
status = 1;
else if (hash < otherhash)
status = -1;
else if (otherhash < hash)
status = 1;
else
status = gst_compare(key, st[i]);
/* If other is closer to their ideal slot */
if (status == 1) {
/* Swap current kv pair with pair in slot */
GstValue t1, t2;
t1 = st[i];
t2 = st[i + 1];
st[i] = key;
st[i + 1] = value;
key = t1;
value = t2;
/* Save dist and hash of new kv pair */
dist = otherdist;
hash = otherhash;
} else if (status == 0) {
/* This should not happen - it means
* than a key was added to the struct more than once */
return;
}
}
}
/* Finish building a struct */
const GstValue *gst_struct_end(Gst *vm, GstValue *st) {
GstValue cached;
GstValue check;
gst_struct_hash(st) = gst_tuple_calchash(st, gst_struct_capacity(st));
check.type = GST_STRUCT;
check.data.st = (const GstValue *) st;
cached = gst_cache_add(vm, check);
return cached.data.st;
}
/* Get an item from a struct */
GstValue gst_struct_get(const GstValue *st, GstValue key) {
const GstValue *bucket = gst_struct_find(st, key);
if (!bucket || bucket[0].type == GST_NIL) {
GstValue ret;
ret.type = GST_NIL;
return ret;
} else {
return bucket[1];
}
}
/* Get the next key in a struct */
GstValue gst_struct_next(const GstValue *st, GstValue key) {
const GstValue *bucket, *end;
end = st + gst_struct_capacity(st);
if (key.type == GST_NIL) {
bucket = st;
} else {
bucket = gst_struct_find(st, key);
if (!bucket || bucket[0].type == GST_NIL)
return gst_wrap_nil();
bucket += 2;
}
for (; bucket < end; bucket += 2) {
if (bucket[0].type != GST_NIL)
return bucket[0];
}
return gst_wrap_nil();
}
/****/
/* Tuple functions */
/****/
/* Create a new empty tuple of the given size. Expected to be
* mutated immediately */
GstValue *gst_tuple_begin(Gst *vm, uint32_t length) {
char *data = gst_alloc(vm, 2 * sizeof(uint32_t) + length * sizeof(GstValue));
GstValue *tuple = (GstValue *)(data + (2 * sizeof(uint32_t)));
gst_tuple_length(tuple) = length;
return tuple;
}
/* Finish building a tuple */
const GstValue *gst_tuple_end(Gst *vm, GstValue *tuple) {
GstValue cached;
GstValue check;
gst_tuple_hash(tuple) = gst_tuple_calchash(tuple, gst_tuple_length(tuple));
check.type = GST_TUPLE;
check.data.tuple = (const GstValue *) tuple;
cached = gst_cache_add(vm, check);
return cached.data.tuple;
}
/****/
/* String Functions */
/****/
/* Begin building a string */
uint8_t *gst_string_begin(Gst *vm, uint32_t length) {
char *data = gst_alloc(vm, 2 * sizeof(uint32_t) + length + 1);
uint8_t *str = (uint8_t *) (data + 2 * sizeof(uint32_t));
gst_string_length(str) = length;
str[length] = 0;
return str;
}
/* Finish building a string */
const uint8_t *gst_string_end(Gst *vm, uint8_t *str) {
GstValue cached;
GstValue check;
gst_string_hash(str) = gst_string_calchash(str, gst_string_length(str));
check.type = GST_STRING;
check.data.string = (const uint8_t *) str;
cached = gst_cache_add(vm, check);
return cached.data.string;
}
/* Load a buffer as a string */
const uint8_t *gst_string_b(Gst *vm, const uint8_t *buf, uint32_t len) {
uint32_t hash = gst_string_calchash(buf, len);
int status = 0;
GstValue *bucket = gst_cache_strfind(vm, buf, len, hash, &status);
if (status) {
return bucket->data.string;
} else {
uint32_t newbufsize = len + 2 * sizeof(uint32_t) + 1;
uint8_t *str = (uint8_t *)(gst_alloc(vm, newbufsize) + 2 * sizeof(uint32_t));
gst_memcpy(str, buf, len);
gst_string_length(str) = len;
gst_string_hash(str) = hash;
str[len] = 0;
return gst_cache_add_bucket(vm, gst_wrap_string(str), bucket).data.string;
}
}
static void inc_counter(uint8_t *digits, int base, int len) {
int i;
uint8_t carry = 1;
for (i = len - 1; i >= 0; --i) {
digits[i] += carry;
carry = 0;
if (digits[i] == base) {
digits[i] = 0;
carry = 1;
}
}
}
/* Generate a unique symbol */
const uint8_t *gst_string_bu(Gst *vm, const uint8_t *buf, uint32_t len) {
static const char base64[] =
"0123456789"
"abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"+-";
GstValue *bucket;
uint32_t hash;
uint8_t counter[6] = {63, 63, 63, 63, 63, 63};
/* Leave spaces for 6 base 64 digits and two dashes. That means 64^6 possible symbols, which
* is enough */
uint32_t newlen = len + 8;
uint32_t newbufsize = newlen + 2 * sizeof(uint32_t) + 1;
uint8_t *str = (uint8_t *)(gst_alloc(vm, newbufsize) + 2 * sizeof(uint32_t));
gst_string_length(str) = newlen;
gst_memcpy(str, buf, len);
str[len] = '-';
str[len + 1] = '-';
str[newlen] = 0;
uint8_t *saltbuf = str + len + 2;
int status = 1;
while (status) {
int i;
inc_counter(counter, 64, 6);
for (i = 0; i < 6; ++i)
saltbuf[i] = base64[counter[i]];
hash = gst_string_calchash(str, newlen);
bucket = gst_cache_strfind(vm, str, newlen, hash, &status);
}
gst_string_hash(str) = hash;
return gst_cache_add_bucket(vm, gst_wrap_string(str), bucket).data.string;
}
/* Generate a unique string from a cstring */
const uint8_t *gst_string_cu(Gst *vm, const char *s) {
uint32_t len = 0;
while (s[len]) ++len;
return gst_string_bu(vm, (const uint8_t *)s, len);
}
/* Load a c string */
const uint8_t *gst_string_c(Gst *vm, const char *str) {
uint32_t len = 0;
while (str[len]) ++len;
return gst_string_b(vm, (const uint8_t *)str, len);
}
/* Load a c string and return it as a GstValue */
GstValue gst_string_cv(Gst *vm, const char *str) {
GstValue ret;
const uint8_t *data = gst_string_c(vm, str);
ret.type = GST_STRING;
ret.data.string = data;
return ret;
}
/* Load a c string and return it as a GstValue. Return the symbol. */
GstValue gst_string_cvs(Gst *vm, const char *str) {
GstValue ret;
/* Only put strings in cache */
const uint8_t *data = gst_string_c(vm, str);
ret.type = GST_SYMBOL;
ret.data.string = data;
return ret;
}
/* Compares two strings */
int gst_string_compare(const uint8_t *lhs, const uint8_t *rhs) {
uint32_t xlen = gst_string_length(lhs);
uint32_t ylen = gst_string_length(rhs);
uint32_t len = xlen > ylen ? ylen : xlen;
uint32_t i;
for (i = 0; i < len; ++i) {
if (lhs[i] == rhs[i]) {
continue;
} else if (lhs[i] < rhs[i]) {
return -1; /* x is less than y */
} else {
return 1; /* y is less than x */
}
}
if (xlen == ylen) {
return 0;
} else {
return xlen < ylen ? -1 : 1;
}
}

537
core/internal.h Normal file
View File

@ -0,0 +1,537 @@
/*
* Copyright (c) 2017 Calvin Rose
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#ifndef DST_INTERNAL_H_defined
#define DST_INTERNAL_H_defined
#include <dst/dst.h>
#include <setjmp.h>
/* String utils */
#define dst_string_raw(s) ((uint32_t *)(s) - 2)
#define dst_string_length(s) (dst_string_raw(s)[0])
#define dst_string_hash(s) (dst_string_raw(s)[1])
/* Tuple utils */
#define dst_tuple_raw(t) ((uint32_t *)(t) - 2)
#define dst_tuple_length(t) (dst_tuple_raw(t)[0])
#define dst_tuple_hash(t) (dst_tuple_raw(t)[1])
/* Struct utils */
#define dst_struct_raw(t) ((uint32_t *)(t) - 2)
#define dst_struct_length(t) (dst_struct_raw(t)[0])
#define dst_struct_capacity(t) (dst_struct_length(t) * 4)
#define dst_struct_hash(t) (dst_struct_raw(t)[1])
/* Userdata utils */
#define dst_udata_header(u) ((DstUserdataHeader *)(u) - 1)
#define dst_udata_type(u) (dst_udata_header(u)->type)
#define dst_udata_size(u) (dst_udata_header(u)->size)
/* Memcpy for moving memory */
#ifndef dst_memcpy
#include <string.h>
#define dst_memcpy memcpy
#endif
/* Allocation */
#ifndef dst_raw_alloc
#include <stdlib.h>
#define dst_raw_alloc malloc
#endif
/* Zero allocation */
#ifndef dst_raw_calloc
#include <stdlib.h>
#define dst_raw_calloc calloc
#endif
/* Realloc */
#ifndef dst_raw_realloc
#include <stdlib.h>
#define dst_raw_realloc realloc
#endif
/* Free */
#ifndef dst_raw_free
#include <stdlib.h>
#define dst_raw_free free
#endif
/* Null */
#ifndef NULL
#define NULL ((void *)0)
#endif
/* Stack frame manipulation */
/* Size of stack frame in number of values */
#define DST_FRAME_SIZE 5
/* Prevent some recursive functions from recursing too deeply
* ands crashing. */
#define DST_RECURSION_GUARD 2056
/* Macros for referencing a stack frame given a stack */
#define dst_frame_callee(s) (*(s - 1))
#define dst_frame_size(s) ((s - 2)->data.dwords[0])
#define dst_frame_prevsize(s) ((s - 2)->data.dwords[1])
#define dst_frame_args(s) ((s - 3)->data.dwords[0])
#define dst_frame_ret(s) ((s - 3)->data.dwords[1])
#define dst_frame_pc(s) ((s - 4)->data.u16p)
#define dst_frame_env(s) ((s - 5)->data.env)
/* C function helpers */
/* Return in a c function */
#define dst_c_return(vm, x) do { (vm)->ret = (x); return DST_RETURN_OK; } while (0)
/* Throw error from a c function */
#define dst_c_throw(vm, e) do { (vm)->ret = (e); return DST_RETURN_ERROR; } while (0)
/* Throw c string error from a c function */
#define dst_c_throwc(vm, e) dst_c_throw((vm), dst_string_cv((vm), (e)))
/* Assert from a c function */
#define dst_c_assert(vm, cond, e) do { if (cond) dst_c_throw((vm), (e)); } while (0)
/* What to do when out of memory */
#ifndef DST_OUT_OF_MEMORY
#include <stdlib.h>
#include <stdio.h>
#define DST_OUT_OF_MEMORY do { printf("out of memory\n"); exit(1); } while (0)
#endif
/* What to do when memory is low */
#ifndef DST_LOW_MEMORY
#include <stdlib.h>
#include <stdio.h>
#define DST_LOW_MEMORY do { printf("low memory\n"); } while (0)
#endif
/* A general dst value type */
typedef struct DstValue DstValue;
/* All of the dst types */
typedef double DstReal;
typedef int64_t DstInteger;
typedef int DstBoolean;
typedef struct DstFunction DstFunction;
typedef struct DstArray DstArray;
typedef struct DstBuffer DstBuffer;
typedef struct DstTable DstTable;
typedef struct DstThread DstThread;
/* Other structs */
typedef struct DstUserdataHeader DstUserdataHeader;
typedef struct DstFuncDef DstFuncDef;
typedef struct DstFuncEnv DstFuncEnv;
typedef union DstValueUnion DstValueUnion;
typedef struct DstModuleItem DstModuleItem;
typedef struct DstUserType DstUserType;
typedef struct DstParser DstParser;
typedef struct DstParseState DstParseState;
/* C Api data types */
struct DstModuleItem {
const char *name;
DstCFunction data;
};
/* Union datatype */
union DstValueUnion {
DstBoolean boolean;
DstReal real;
DstInteger integer;
DstArray *array;
DstBuffer *buffer;
DstTable *table;
DstThread *thread;
const DstValue *tuple;
DstCFunction cfunction;
DstFunction *function;
DstFuncEnv *env;
DstFuncDef *def;
const DstValue *st;
const uint8_t *string;
/* Indirectly used union members */
uint16_t *u16p;
uint32_t dwords[2];
uint16_t words[4];
uint8_t bytes[8];
void *pointer;
const char *cstring;
};
/* The general dst value type. Contains a large union and
* the type information of the value */
struct DstValue {
DstType type;
DstValueUnion data;
};
/* A lightweight green thread in dst. Does not correspond to
* operating system threads. */
struct DstThread {
uint32_t count;
uint32_t capacity;
DstValue *data;
DstThread *parent;
enum {
DST_THREAD_PENDING = 0,
DST_THREAD_ALIVE,
DST_THREAD_DEAD,
DST_THREAD_ERROR
} status;
};
/* A dynamic array type. */
struct DstArray {
uint32_t count;
uint32_t capacity;
DstValue *data;
};
/* A bytebuffer type. Used as a mutable string or string builder. */
struct DstBuffer {
uint32_t count;
uint32_t capacity;
uint8_t *data;
};
/* A mutable associative data type. Backed by a hashtable. */
struct DstTable {
uint32_t count;
uint32_t capacity;
uint32_t deleted;
DstValue *data;
};
/* Some function defintion flags */
#define DST_FUNCDEF_FLAG_VARARG 1
#define DST_FUNCDEF_FLAG_NEEDSPARENT 2
#define DST_FUNCDEF_FLAG_NEEDSENV 4
/* A function definition. Contains information need to instantiate closures. */
struct DstFuncDef {
uint32_t locals;
uint32_t arity; /* Not including varargs */
uint32_t literalsLen;
uint32_t byteCodeLen;
uint32_t flags;
DstValue *literals; /* Contains strings, FuncDefs, etc. */
uint16_t *byteCode;
};
/* A fuction environment */
struct DstFuncEnv {
DstThread *thread; /* When nil, index the local values */
uint32_t stackOffset; /* Used as environment size when off stack */
DstValue *values;
};
/* A function */
struct DstFunction {
DstFuncDef *def;
DstFuncEnv *env;
DstFunction *parent;
};
/* Defines a type for userdata */
struct DstUserType {
const char *name;
int (*serialize)(Dst *vm, void *data, uint32_t len);
int (*deserialize)(Dst *vm);
void (*finalize)(Dst *vm, void *data, uint32_t len);
};
/* Contains information about userdata */
struct DstUserdataHeader {
uint32_t size;
const DstUserType *type;
};
/* The VM state */
struct Dst {
/* Garbage collection */
void *blocks;
uint32_t memoryInterval;
uint32_t nextCollection;
uint32_t black : 1;
/* Immutable value cache */
DstValue *cache;
uint32_t cache_capacity;
uint32_t cache_count;
uint32_t cache_deleted;
/* GC roots */
DstThread *thread;
DstTable *modules;
DstTable *registry;
DstTable *env;
/* Return state */
DstValue ret;
uint32_t flags;
};
/* Bytecode */
enum DstOpCode {
DST_OP_FLS, /* Load false */
DST_OP_TRU, /* Load true */
DST_OP_NIL, /* Load nil */
DST_OP_UPV, /* Load upvalue */
DST_OP_JIF, /* Jump if */
DST_OP_JMP, /* Jump */
DST_OP_SUV, /* Set upvalue */
DST_OP_CST, /* Load constant */
DST_OP_I16, /* Load 16 bit signed integer */
DST_OP_I32, /* Load 32 bit signed integer */
DST_OP_I64, /* Load 64 bit signed integer */
DST_OP_F64, /* Load 64 bit IEEE double */
DST_OP_MOV, /* Move value */
DST_OP_CLN, /* Create a closure */
DST_OP_ARR, /* Create array */
DST_OP_DIC, /* Create object */
DST_OP_TUP, /* Create tuple */
DST_OP_RET, /* Return from function */
DST_OP_RTN, /* Return nil */
DST_OP_PSK, /* Push stack */
DST_OP_PAR, /* Push array or tuple */
DST_OP_CAL, /* Call function */
DST_OP_TCL, /* Tail call */
DST_OP_TRN /* Transfer to new thread */
};
/****/
/* Internal buffer functions */
/****/
void dst_value_buffer_ensure(Dst *vm, DstBuffer *buffer, uint32_t capacity);
void dst_buffer_append_bytes(Dst *vm, DstBuffer *buffer, const uint8_t *string, uint32_t length);
void dst_buffer_append_cstring(Dst *vm, DstBuffer *buffer, const char *cstring);
/* Define a push function for pushing a certain type to the buffer */
#define BUFFER_DEFINE(name, type) \
static void dst_buffer_push_##name(Dst *vm, DstBuffer *buffer, type x) { \
union { type t; uint8_t bytes[sizeof(type)]; } u; \
u.t = x; dst_buffer_append(vm, buffer, u.bytes, sizeof(type)); \
}
/****/
/* Table functions */
/****/
DstArray *dst_make_array(Dst *vm, uint32_t capacity);
/****/
/* Tuple functions */
/****/
DstValue *dst_tuple_begin(Dst *vm, uint32_t length);
const DstValue *dst_tuple_end(Dst *vm, DstValue *tuple);
/****/
/* String/Symbol functions */
/****/
uint8_t *dst_string_begin(Dst *vm, uint32_t len);
void dst_string_end(Dst *vm, uint32_t dest, uint8_t *str);
const uint8_t *dst_string_b(Dst *vm, const uint8_t *buf, uint32_t len);
const uint8_t *dst_string_c(Dst *vm, const char *cstring);
DstValue dst_string_cv(Dst *vm, const char *string);
DstValue dst_string_cvs(Dst *vm, const char *string);
int dst_string_compare(const uint8_t *lhs, const uint8_t *rhs);
const uint8_t *dst_string_bu(Dst *vm, const uint8_t *buf, uint32_t len);
const uint8_t *dst_string_cu(Dst *vm, const char *s);
/****/
/* Struct functions */
/****/
DstValue *dst_struct_begin(Dst *vm, uint32_t count);
void dst_struct_put(DstValue *st, DstValue key, DstValue value);
const DstValue *dst_struct_end(Dst *vm, DstValue *st);
DstValue dst_struct_get(const DstValue *st, DstValue key);
DstValue dst_struct_next(const DstValue *st, DstValue key);
/****/
/* Table functions */
/****/
DstTable *dst_make_table(Dst *vm, uint32_t capacity);
DstValue dst_table_get(DstTable *t, DstValue key);
DstValue dst_table_remove(DstTable *t, DstValue key);
void dst_table_put(Dst *vm, DstTable *t, DstValue key, DstValue value);
DstValue dst_table_next(DstTable *o, DstValue key);
/****/
/* Threads */
/****/
#define dst_thread_stack(t) ((t)->data + (t)->count)
DstThread *dst_thread(Dst *vm, DstValue callee, uint32_t capacity);
DstThread *dst_thread_reset(Dst *vm, DstThread *thread, DstValue callee);
void dst_thread_ensure_extra(Dst *vm, DstThread *thread, uint32_t extra);
void dst_thread_push(Dst *vm, DstThread *thread, DstValue x);
void dst_thread_pushnil(Dst *vm, DstThread *thread, uint32_t n);
void dst_thread_tuplepack(Dst *vm, DstThread *thread, uint32_t n);
DstValue *dst_thread_beginframe(Dst *vm, DstThread *thread, DstValue callee, uint32_t arity);
void dst_thread_endframe(Dst *vm, DstThread *thread);
DstValue *dst_thread_popframe(Dst *vm, DstThread *thread);
uint32_t dst_thread_countframes(DstThread *thread);
/****/
/* Serialization */
/****/
const char *dst_deserialize_internal(
Dst *vm,
const uint8_t *data,
uint32_t len,
DstValue *out,
const uint8_t **nextData);
const char *dst_serialize_internal(Dst *vm, DstBuffer *buffer, DstValue x);
/****/
/* GC */
/****/
#define DST_MEMTAG_STRING 4
#define DST_MEMTAG_TUPLE 8
#define DST_MEMTAG_STRUCT 16
#define DST_MEMTAG_USER 32
void dst_mark_value(Dst *vm, DstValue x);
void dst_mark(Dst *vm, DstValueUnion x, DstType type);
void dst_sweep(Dst *vm);
void *dst_alloc(Dst *vm, uint32_t size);
void *dst_zalloc(Dst *vm, uint32_t size);
void dst_mem_tag(void *mem, uint32_t tags);
void dst_collect(Dst *vm);
void dst_maybe_collect(Dst *vm);
void dst_clear_memory(Dst *vm);
void dst_mark_mem(Dst *vm, void *mem);
/****/
/* VM */
/****/
DstValue dst_arg(Dst *vm, uint32_t index);
void dst_set_arg(Dst *vm, uint32_t index, DstValue x);
uint32_t dst_args(Dst *vm);
/***/
/* Stl */
/***/
void dst_stl_load(Dst *vm);
/****/
/* C Api */
/****/
void dst_module(Dst *vm, const char *name, const DstModuleItem *mod);
void dst_module_mutable(Dst *vm, const char *name, const DstModuleItem *mod);
void dst_module_put(Dst *vm, const char *packagename, const char *name, DstValue x);
DstValue dst_module_get(Dst *vm, const char *packagename);
void dst_register_put(Dst *vm, const char *packagename, DstValue mod);
DstValue dst_register_get(Dst *vm, const char *name);
int dst_callc(Dst *vm, DstCFunction fn, int numargs, ...);
/* Treat similar types through uniform interfaces for iteration */
int dst_seq_view(DstValue seq, const DstValue **data, uint32_t *len);
int dst_chararray_view(DstValue str, const uint8_t **data, uint32_t *len);
int dst_hashtable_view(DstValue tab, const DstValue **data, uint32_t *cap);
/****/
/* Caching for immutable data */
/****/
void dst_cache_remove_string(Dst *vm, char *strmem);
void dst_cache_remove_tuple(Dst *vm, char *tuplemem);
void dst_cache_remove_struct(Dst *vm, char *structmem);
/****/
/* Misc */
/****/
uint32_t dst_index(Dst *vm, int i);
int dst_read_real(const uint8_t *string, const uint8_t *end, double *ret, int forceInt);
int dst_read_integer(const uint8_t *string, const uint8_t *end, int64_t *ret);
DstReal dst_integer_to_real(DstInteger x);
DstInteger dst_real_to_integer(DstReal x);
uint32_t dst_startrange(DstInteger raw, uint32_t len);
uint32_t dst_endrange(DstInteger raw, uint32_t len);
void dst_env_merge(Dst *vm, DstTable *destEnv, DstTable *srcEnv);
DstTable *dst_env_nils(Dst *vm, DstTable *env);
DstTable *dst_env_meta(Dst *vm, DstTable *env);
void dst_env_put(Dst *vm, DstTable *env, DstValue key, DstValue value);
void dst_env_putc(Dst *vm, DstTable *env, const char *key, DstValue value);
void dst_env_putvar(Dst *vm, DstTable *env, DstValue key, DstValue value);
void dst_env_putvarc(Dst *vm, DstTable *env, const char *key, DstValue value);
/****/
/* Value functions */
/****/
int dst_value_truthy(DstValue v);
int dst_value_equals(DstValue x, DstValue y);
uint32_t dst_value_hash(DstValue x);
int dst_value_compare(DstValue x, DstValue y);
/* Wrap data in GstValue */
DstValue dst_wrap_nil();
DstValue dst_wrap_real(DstReal x);
DstValue dst_wrap_integer(DstInteger x);
DstValue dst_wrap_boolean(int x);
DstValue dst_wrap_string(const uint8_t *x);
DstValue dst_wrap_symbol(const uint8_t *x);
DstValue dst_wrap_array(DstArray *x);
DstValue dst_wrap_tuple(const DstValue *x);
DstValue dst_wrap_struct(const DstValue *x);
DstValue dst_wrap_thread(DstThread *x);
DstValue dst_wrap_buffer(DstBuffer *x);
DstValue dst_wrap_function(DstFunction *x);
DstValue dst_wrap_cfunction(DstCFunction x);
DstValue dst_wrap_table(DstTable *x);
DstValue dst_wrap_userdata(void *x);
DstValue dst_wrap_funcenv(DstFuncEnv *x);
DstValue dst_wrap_funcdef(DstFuncDef *x);
/* Check data from arguments */
int dst_check_nil(Dst *vm, uint32_t i);
int dst_check_real(Dst *vm, uint32_t i);
int dst_check_integer(Dst *vm, uint32_t i);
int dst_check_boolean(Dst *vm, uint32_t i);
int dst_check_string(Dst *vm, uint32_t i);
int dst_check_symbol(Dst *vm, uint32_t i);
int dst_check_array(Dst *vm, uint32_t i);
int dst_check_tuple(Dst *vm, uint32_t i);
int dst_check_struct(Dst *vm, uint32_t i);
int dst_check_thread(Dst *vm, uint32_t i);
int dst_check_buffer(Dst *vm, uint32_t i);
int dst_check_function(Dst *vm, uint32_t i);
int dst_check_cfunction(Dst *vm, uint32_t i);
int dst_check_table(Dst *vm, uint32_t i);
int dst_check_funcenv(Dst *vm, uint32_t i);
int dst_check_funcdef(Dst *vm, uint32_t i);
void dst_check_userdata(Dst *vm, uint32_t i;
#endif /* DST_INTERNAL_H_defined */

View File

@ -20,82 +20,82 @@
* IN THE SOFTWARE.
*/
#include <gst/gst.h>
#include "internal.h"
static void gst_cmodule_register(Gst *vm, const char *name, const GstModuleItem *mod) {
static void dst_cmodule_register(Dst *vm, const char *name, const DstModuleItem *mod) {
uint32_t startLength;
GstBuffer *buffer = gst_buffer(vm, 10);
gst_buffer_append_cstring(vm, buffer, name);
gst_buffer_push(vm, buffer, '.');
DstBuffer *buffer = dst_buffer(vm, 10);
dst_buffer_append_cstring(vm, buffer, name);
dst_buffer_push(vm, buffer, '.');
startLength = buffer->count;
while (mod->name != NULL) {
GstValue key;
DstValue key;
buffer->count = startLength;
gst_buffer_append_cstring(vm, buffer, mod->name);
key = gst_wrap_symbol(gst_buffer_to_string(vm, buffer));
gst_table_put(vm, vm->registry, key, gst_wrap_cfunction(mod->data));
gst_table_put(vm, vm->registry, gst_wrap_cfunction(mod->data), key);
dst_buffer_append_cstring(vm, buffer, mod->name);
key = dst_wrap_symbol(dst_buffer_to_string(vm, buffer));
dst_table_put(vm, vm->registry, key, dst_wrap_cfunction(mod->data));
dst_table_put(vm, vm->registry, dst_wrap_cfunction(mod->data), key);
mod++;
}
}
static GstValue gst_cmodule_table(Gst *vm, const GstModuleItem *mod) {
GstTable *module = gst_table(vm, 10);
static DstValue dst_cmodule_table(Dst *vm, const DstModuleItem *mod) {
DstTable *module = dst_table(vm, 10);
while (mod->name != NULL) {
GstValue key = gst_string_cvs(vm, mod->name);
gst_table_put(vm, module, key, gst_wrap_cfunction(mod->data));
DstValue key = dst_string_cvs(vm, mod->name);
dst_table_put(vm, module, key, dst_wrap_cfunction(mod->data));
mod++;
}
return gst_wrap_table(module);
return dst_wrap_table(module);
}
static GstValue gst_cmodule_struct(Gst *vm, const GstModuleItem *mod) {
static DstValue dst_cmodule_struct(Dst *vm, const DstModuleItem *mod) {
uint32_t count = 0;
const GstModuleItem *m = mod;
GstValue *st;
const DstModuleItem *m = mod;
DstValue *st;
while (m->name != NULL) {
++count;
++m;
}
st = gst_struct_begin(vm, count);
st = dst_struct_begin(vm, count);
m = mod;
while (m->name != NULL) {
gst_struct_put(st,
gst_string_cvs(vm, m->name),
gst_wrap_cfunction(m->data));
dst_struct_put(st,
dst_string_cvs(vm, m->name),
dst_wrap_cfunction(m->data));
++m;
}
return gst_wrap_struct(gst_struct_end(vm, st));
return dst_wrap_struct(dst_struct_end(vm, st));
}
void gst_module(Gst *vm, const char *packagename, const GstModuleItem *mod) {
gst_table_put(vm, vm->modules, gst_string_cvs(vm, packagename), gst_cmodule_struct(vm, mod));
gst_cmodule_register(vm, packagename, mod);
void dst_module(Dst *vm, const char *packagename, const DstModuleItem *mod) {
dst_table_put(vm, vm->modules, dst_string_cvs(vm, packagename), dst_cmodule_struct(vm, mod));
dst_cmodule_register(vm, packagename, mod);
}
void gst_module_mutable(Gst *vm, const char *packagename, const GstModuleItem *mod) {
gst_table_put(vm, vm->modules, gst_string_cvs(vm, packagename), gst_cmodule_table(vm, mod));
gst_cmodule_register(vm, packagename, mod);
void dst_module_mutable(Dst *vm, const char *packagename, const DstModuleItem *mod) {
dst_table_put(vm, vm->modules, dst_string_cvs(vm, packagename), dst_cmodule_table(vm, mod));
dst_cmodule_register(vm, packagename, mod);
}
void gst_module_put(Gst *vm, const char *packagename, const char *name, GstValue v) {
GstValue modtable = gst_table_get(vm->modules, gst_string_cvs(vm, packagename));
if (modtable.type == GST_TABLE) {
GstTable *table = modtable.data.table;
if (v.type == GST_CFUNCTION) {
GstValue key;
GstBuffer *buffer = gst_buffer(vm, 10);
gst_buffer_append_cstring(vm, buffer, packagename);
gst_buffer_push(vm, buffer, '.');
gst_buffer_append_cstring(vm, buffer, name);
key = gst_wrap_string(gst_buffer_to_string(vm, buffer));
gst_table_put(vm, vm->registry, key, v);
gst_table_put(vm, vm->registry, v, key);
void dst_module_put(Dst *vm, const char *packagename, const char *name, DstValue v) {
DstValue modtable = dst_table_get(vm->modules, dst_string_cvs(vm, packagename));
if (modtable.type == DST_TABLE) {
DstTable *table = modtable.data.table;
if (v.type == DST_CFUNCTION) {
DstValue key;
DstBuffer *buffer = dst_buffer(vm, 10);
dst_buffer_append_cstring(vm, buffer, packagename);
dst_buffer_push(vm, buffer, '.');
dst_buffer_append_cstring(vm, buffer, name);
key = dst_wrap_string(dst_buffer_to_string(vm, buffer));
dst_table_put(vm, vm->registry, key, v);
dst_table_put(vm, vm->registry, v, key);
}
gst_table_put(vm, table, gst_string_cvs(vm, name), v);
dst_table_put(vm, table, dst_string_cvs(vm, name), v);
}
}
GstValue gst_module_get(Gst *vm, const char *packagename) {
return gst_table_get(vm->modules, gst_string_cvs(vm, packagename));
DstValue dst_module_get(Dst *vm, const char *packagename) {
return dst_table_get(vm->modules, dst_string_cvs(vm, packagename));
}

View File

@ -1,431 +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 <gst/gst.h>
static const char UNEXPECTED_CLOSING_DELIM[] = "unexpected closing delimiter";
/* Handle error in parsing */
#define p_error(p, e) ((p)->error = (e), (p)->status = GST_PARSER_ERROR)
/* Get the top ParseState in the parse stack */
static GstParseState *parser_peek(GstParser *p) {
if (!p->count) {
return NULL;
}
return p->data + (p->count - 1);
}
/* Remove the top state from the ParseStack */
static GstParseState *parser_pop(GstParser * p) {
if (!p->count) {
p_error(p, "parser stack underflow");
return NULL;
}
return p->data + --p->count;
}
/* Quote a value */
static GstValue quote(GstParser *p, GstValue x) {
GstValue *tuple = gst_tuple_begin(p->vm, 2);
tuple[0] = gst_string_cvs(p->vm, "quote");
tuple[1] = x;
return gst_wrap_tuple(gst_tuple_end(p->vm, tuple));
}
/* Add a new, empty ParseState to the ParseStack. */
static void parser_push(GstParser *p, ParseType type, uint8_t character) {
GstParseState *top;
if (p->count >= p->cap) {
uint32_t newCap = 2 * p->count + 2;
GstParseState *data = gst_alloc(p->vm, newCap * sizeof(GstParseState));
gst_memcpy(data, p->data, p->cap * sizeof(GstParseState));
p->data = data;
p->cap = newCap;
}
++p->count;
top = parser_peek(p);
if (!top) return;
top->type = type;
top->quoteCount = p->quoteCount;
p->quoteCount = 0;
switch (type) {
case PTYPE_STRING:
top->buf.string.state = STRING_STATE_BASE;
top->buf.string.buffer = gst_buffer(p->vm, 10);
break;
case PTYPE_TOKEN:
top->buf.string.buffer = gst_buffer(p->vm, 10);
break;
case PTYPE_FORM:
top->buf.form.array = gst_array(p->vm, 10);
if (character == '(') top->buf.form.endDelimiter = ')';
if (character == '[') top->buf.form.endDelimiter = ']';
if (character == '{') top->buf.form.endDelimiter = '}';
break;
}
}
/* Append a value to the top-most state in the Parser's stack. */
static void parser_append(GstParser *p, GstValue x) {
GstParseState *oldtop = parser_pop(p);
GstParseState *top = parser_peek(p);
while (oldtop->quoteCount--)
x = quote(p, x);
if (!top) {
p->value = x;
p->status = GST_PARSER_FULL;
return;
}
switch (top->type) {
case PTYPE_FORM:
gst_array_push(p->vm, top->buf.form.array, x);
break;
default:
p_error(p, "expected container type");
break;
}
}
/* Check if a character is whitespace */
static int is_whitespace(uint8_t c) {
return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\0' || c == ',';
}
/* Check if a character is a valid symbol character */
static int is_symbol_char(uint8_t c) {
if (c >= 'a' && c <= 'z') return 1;
if (c >= 'A' && c <= 'Z') return 1;
if (c >= '0' && c <= ':') return 1;
if (c >= '<' && c <= '@') return 1;
if (c >= '*' && c <= '/') return 1;
if (c >= '#' && c <= '&') return 1;
if (c == '_') return 1;
if (c == '^') return 1;
if (c == '!') return 1;
return 0;
}
/* Checks if a string slice is equal to a string constant */
static int check_str_const(const char *ref, const uint8_t *start, const uint8_t *end) {
while (*ref && start < end) {
if (*ref != *(char *)start) return 0;
++ref;
++start;
}
return !*ref && start == end;
}
/* Build from the token buffer */
static GstValue build_token(GstParser *p, GstBuffer *buf) {
GstValue x;
GstReal real;
GstInteger integer;
uint8_t *data = buf->data;
uint8_t *back = data + buf->count;
if (gst_read_integer(data, back, &integer)) {
x.type = GST_INTEGER;
x.data.integer = integer;
} else if (gst_read_real(data, back, &real, 0)) {
x.type = GST_REAL;
x.data.real = real;
} else if (check_str_const("nil", data, back)) {
x.type = GST_NIL;
x.data.boolean = 0;
} else if (check_str_const("false", data, back)) {
x.type = GST_BOOLEAN;
x.data.boolean = 0;
} else if (check_str_const("true", data, back)) {
x.type = GST_BOOLEAN;
x.data.boolean = 1;
} else {
if (buf->data[0] >= '0' && buf->data[0] <= '9') {
p_error(p, "symbols cannot start with digits");
x.type = GST_NIL;
} else if (buf->data[0] == ':' && buf->count >= 2) {
x.type = GST_STRING;
x.data.string = gst_string_b(p->vm, buf->data + 1, buf->count - 1);
} else {
x.type = GST_SYMBOL;
x.data.string = gst_buffer_to_string(p->vm, buf);
}
}
return x;
}
/* Handle parsing a token */
static int token_state(GstParser *p, uint8_t c) {
GstParseState *top = parser_peek(p);
GstBuffer *buf = top->buf.string.buffer;
if (is_whitespace(c) || c == ')' || c == ']' || c == '}') {
parser_append(p, build_token(p, buf));
return !(c == ')' || c == ']' || c == '}');
} else if (is_symbol_char(c)) {
gst_buffer_push(p->vm, buf, c);
return 1;
} else {
p_error(p, "expected symbol character");
return 1;
}
}
/* Get hex digit from a letter */
static int to_hex(uint8_t c) {
if (c >= '0' && c <= '9') {
return c - '0';
} else if (c >= 'a' && c <= 'f') {
return 10 + c - 'a';
} else if (c >= 'A' && c <= 'F') {
return 10 + c - 'A';
} else {
return -1;
}
}
/* Handle parsing a string literal */
static int string_state(GstParser *p, uint8_t c) {
int digit;
GstParseState *top = parser_peek(p);
switch (top->buf.string.state) {
case STRING_STATE_BASE:
if (c == '\\') {
top->buf.string.state = STRING_STATE_ESCAPE;
} else if (c == '"') {
GstValue x;
x.type = GST_STRING;
x.data.string = gst_buffer_to_string(p->vm, top->buf.string.buffer);
parser_append(p, x);
} else {
gst_buffer_push(p->vm, top->buf.string.buffer, c);
}
break;
case STRING_STATE_ESCAPE:
{
uint8_t next;
switch (c) {
case 'n': next = '\n'; break;
case 'r': next = '\r'; break;
case 't': next = '\t'; break;
case 'f': next = '\f'; break;
case '0': next = '\0'; break;
case '"': next = '"'; break;
case '\'': next = '\''; break;
case 'z': next = '\0'; break;
case 'e': next = 27; break;
case 'h':
top->buf.string.state = STRING_STATE_ESCAPE_HEX;
top->buf.string.count = 0;
top->buf.string.accum = 0;
return 1;
default:
p_error(p, "unknown string escape sequence");
return 1;
}
gst_buffer_push(p->vm, top->buf.string.buffer, next);
top->buf.string.state = STRING_STATE_BASE;
}
break;
case STRING_STATE_ESCAPE_HEX:
digit = to_hex(c);
if (digit < 0) {
p_error(p, "invalid hexidecimal digit");
return 1;
} else {
top->buf.string.accum *= 16;
top->buf.string.accum += digit;
}
top->buf.string.accum += digit;
if (++top->buf.string.count == 2) {
gst_buffer_push(p->vm, top->buf.string.buffer, top->buf.string.accum);
top->buf.string.state = STRING_STATE_BASE;
}
break;
case STRING_STATE_ESCAPE_UNICODE:
break;
}
return 1;
}
/* Root state of the parser */
static int root_state(GstParser *p, uint8_t c) {
if (is_whitespace(c)) return 1;
p->status = GST_PARSER_PENDING;
if (c == ']' || c == ')' || c == '}') {
p_error(p, UNEXPECTED_CLOSING_DELIM);
return 1;
}
if (c == '(' || c == '[' || c == '{') {
parser_push(p, PTYPE_FORM, c);
return 1;
}
if (c == '"') {
parser_push(p, PTYPE_STRING, c);
return 1;
}
if (c == '\'') {
p->quoteCount++;
return 1;
}
if (is_symbol_char(c)) {
parser_push(p, PTYPE_TOKEN, c);
return 0;
}
p_error(p, "unexpected character");
return 1;
}
/* Handle parsing a form */
static int form_state(GstParser *p, uint8_t c) {
GstParseState *top = parser_peek(p);
if (c == top->buf.form.endDelimiter) {
GstArray *array = top->buf.form.array;
GstValue x;
if (c == ']') {
x.type = GST_ARRAY;
x.data.array = array;
} else if (c == ')') {
GstValue *tup;
tup = gst_tuple_begin(p->vm, array->count);
gst_memcpy(tup, array->data, array->count * sizeof(GstValue));
x.type = GST_TUPLE;
x.data.tuple = gst_tuple_end(p->vm, tup);
} else { /* c == '{' */
uint32_t i;
if (array->count % 2 != 0) {
p_error(p, "table literal must have even number of elements");
return 1;
}
x.type = GST_TABLE;
x.data.table = gst_table(p->vm, array->count);
for (i = 0; i < array->count; i += 2) {
gst_table_put(p->vm, x.data.table, array->data[i], array->data[i + 1]);
}
}
parser_append(p, x);
return 1;
}
return root_state(p, c);
}
/* Handle a character */
void gst_parse_byte(GstParser *p, uint8_t c) {
int done = 0;
/* Update position in source */
if (c == '\n') {
p->line++;
p->index = 0;
p->comment = GST_PCOMMENT_EXPECTING;
} else {
++p->index;
}
/* Check comments */
switch (p->comment) {
case GST_PCOMMENT_NOT:
break;
case GST_PCOMMENT_EXPECTING:
if (c == '#') {
p->comment = GST_PCOMMENT_INSIDE;
return;
} else if (!is_whitespace(c)) {
p->comment = GST_PCOMMENT_NOT;
}
break;
case GST_PCOMMENT_INSIDE:
return;
}
/* Dispatch character to state */
while (!done) {
GstParseState *top = parser_peek(p);
if (!top) {
done = root_state(p, c);
} else {
switch (top->type) {
case PTYPE_TOKEN:
done = token_state(p, c);
break;
case PTYPE_FORM:
done = form_state(p, c);
break;
case PTYPE_STRING:
done = string_state(p, c);
break;
}
}
}
}
/* Parse a C style string. The first value encountered when parsed is put
* in p->value. The string variable is then updated to the next char that
* was not read. Returns 1 if any values were read, otherwise returns 0.
* Returns the number of bytes read.
*/
int gst_parse_cstring(GstParser *p, const char *string) {
int bytesRead = 0;
if (!string)
return 0;
while ((p->status == GST_PARSER_PENDING || p->status == GST_PARSER_ROOT)
&& (string[bytesRead] != '\0')) {
gst_parse_byte(p, string[bytesRead++]);
}
return bytesRead;
}
/* Parse a gst string */
int gst_parse_string(GstParser *p, const uint8_t *string) {
uint32_t i;
if (!string)
return 0;
for (i = 0; i < gst_string_length(string); ++i) {
if (p->status != GST_PARSER_PENDING && p->status != GST_PARSER_ROOT) break;
gst_parse_byte(p, string[i]);
}
return i;
}
/* Check if a parser has a value that needs to be handled. If
* so, the parser will not parse any more input until that value
* is consumed. */
int gst_parse_hasvalue(GstParser *p) {
return p->status == GST_PARSER_FULL;
}
/* Gets a value from the parser */
GstValue gst_parse_consume(GstParser *p) {
p->status = GST_PARSER_ROOT;
return p->value;
}
/* Parser initialization (memory allocation) */
void gst_parser(GstParser *p, Gst *vm) {
p->vm = vm;
GstParseState *data = gst_alloc(vm, sizeof(GstParseState) * 10);
p->cap = 10;
p->data = data;
p->count = 0;
p->index = 0;
p->line = 1;
p->quoteCount = 0;
p->error = NULL;
p->status = GST_PARSER_ROOT;
p->value.type = GST_NIL;
p->comment = GST_PCOMMENT_EXPECTING;
}

View File

@ -20,7 +20,8 @@
* IN THE SOFTWARE.
*/
#include <gst/gst.h>
#include <dst/dst.h>
#include "internal.h"
/**
* Data format
@ -37,7 +38,7 @@
* Byte 207: Buffer - [u32 capacity][u32 length]*[u8... characters]
* Byte 208: Array - [u32 length]*[value... elements]
* Byte 209: Tuple - [u32 length]*[value... elements]
* Byte 210: Thread - [value parent][value errorParent][u8 state][u32 frames]*[[value callee][value env]
* Byte 210: Thread - [value parent][u8 state][u32 frames]*[[value callee][value env]
* [u32 pcoffset][u32 ret][u32 args][u32 size]*[value ...stack]]
* Byte 211: Table - [u32 length]*2*[value... kvs]
* Byte 212: FuncDef - [u32 locals][u32 arity][u32 flags][u32 literallen]*[value...
@ -62,7 +63,7 @@ static uint32_t bytes2u32(const uint8_t *bytes) {
uint8_t bytes[4];
uint32_t u32;
} u;
gst_memcpy(u.bytes, bytes, 4 * sizeof(uint8_t));
dst_memcpy(u.bytes, bytes, 4 * sizeof(uint8_t));
return u.u32;
}
@ -72,7 +73,7 @@ static uint16_t bytes2u16(const uint8_t *bytes) {
uint8_t bytes[2];
uint16_t u16;
} u;
gst_memcpy(u.bytes, bytes, 2 * sizeof(uint8_t));
dst_memcpy(u.bytes, bytes, 2 * sizeof(uint8_t));
return u.u16;
}
@ -82,7 +83,7 @@ static double bytes2dbl(const uint8_t *bytes) {
uint8_t bytes[8];
double dbl;
} u;
gst_memcpy(u.bytes, bytes, 8 * sizeof(uint8_t));
dst_memcpy(u.bytes, bytes, 8 * sizeof(uint8_t));
return u.dbl;
}
@ -92,26 +93,26 @@ static int64_t bytes2int(const uint8_t *bytes) {
uint8_t bytes[8];
int64_t i;
} u;
gst_memcpy(u.bytes, bytes, 8 * sizeof(uint8_t));
dst_memcpy(u.bytes, bytes, 8 * sizeof(uint8_t));
return u.i;
}
/* Read a string and turn it into a gst value. Returns
/* Read a string and turn it into a dst value. Returns
* an error message if there is an error message during
* deserialization. If successful, the resulting value is
* passed by reference. */
static const char *gst_deserialize_impl(
Gst *vm,
static const char *dst_deserialize_impl(
Dst *vm,
const uint8_t *data,
const uint8_t *end,
const uint8_t **newData,
GstArray *visited,
GstValue *out,
DstArray *visited,
DstValue *out,
int depth) {
GstValue ret;
ret.type = GST_NIL;
GstValue *buffer;
DstValue ret;
ret.type = DST_NIL;
DstValue *buffer;
uint32_t length, i;
const char *err;
@ -140,7 +141,7 @@ static const char *gst_deserialize_impl(
/* Small integer */
if (*data < 201) {
ret.type = GST_INTEGER;
ret.type = DST_INTEGER;
ret.data.integer = *data - 100;
*newData = data + 1;
*out = ret;
@ -154,119 +155,110 @@ static const char *gst_deserialize_impl(
return "unable to deserialize";
case 201: /* Nil */
ret.type = GST_NIL;
ret.type = DST_NIL;
break;
case 202: /* True */
ret.type = GST_BOOLEAN;
ret.type = DST_BOOLEAN;
ret.data.boolean = 1;
break;
case 203: /* False */
ret.type = GST_BOOLEAN;
ret.type = DST_BOOLEAN;
ret.data.boolean = 0;
break;
case 204: /* Long number (double) */
ret.type = GST_REAL;
ret.type = DST_REAL;
read_dbl(ret.data.real);
break;
case 205: /* String */
case 219: /* Symbol */
ret.type = data[-1] == 205 ? GST_STRING : GST_SYMBOL;
ret.type = data[-1] == 205 ? DST_STRING : DST_SYMBOL;
read_u32(length);
deser_datacheck(length);
ret.data.string = gst_string_b(vm, data, length);
ret.data.string = dst_string_b(vm, data, length);
data += length;
gst_array_push(vm, visited, ret);
dst_array_push(vm, visited, ret);
break;
case 206: /* Struct */
ret.type = GST_STRUCT;
ret.type = DST_STRUCT;
read_u32(length);
buffer = gst_struct_begin(vm, length);
buffer = dst_struct_begin(vm, length);
for (i = 0; i < length; ++i) {
GstValue k, v;
if ((err = gst_deserialize_impl(vm, data, end, &data, visited, &k, depth)))
DstValue k, v;
if ((err = dst_deserialize_impl(vm, data, end, &data, visited, &k, depth)))
return err;
if ((err = gst_deserialize_impl(vm, data, end, &data, visited, &v, depth)))
if ((err = dst_deserialize_impl(vm, data, end, &data, visited, &v, depth)))
return err;
gst_struct_put(buffer, k, v);
dst_struct_put(buffer, k, v);
}
ret.data.st = gst_struct_end(vm, buffer);
gst_array_push(vm, visited, ret);
ret.data.st = dst_struct_end(vm, buffer);
dst_array_push(vm, visited, ret);
break;
case 207: /* Buffer */
{
uint32_t cap;
ret.type = GST_BYTEBUFFER;
ret.type = DST_BYTEBUFFER;
read_u32(cap);
read_u32(length);
deser_datacheck(length);
ret.data.buffer = gst_alloc(vm, sizeof(GstBuffer));
ret.data.buffer->data = gst_alloc(vm, cap);
gst_memcpy(ret.data.buffer->data, data, length);
ret.data.buffer = dst_alloc(vm, sizeof(DstBuffer));
ret.data.buffer->data = dst_alloc(vm, cap);
dst_memcpy(ret.data.buffer->data, data, length);
ret.data.buffer->count = length;
ret.data.buffer->capacity = cap;
gst_array_push(vm, visited, ret);
dst_array_push(vm, visited, ret);
}
break;
case 208: /* Array */
ret.type = GST_ARRAY;
ret.type = DST_ARRAY;
read_u32(length);
buffer = gst_alloc(vm, length * sizeof(GstValue));
ret.data.array = gst_alloc(vm, sizeof(GstArray));
buffer = dst_alloc(vm, length * sizeof(DstValue));
ret.data.array = dst_alloc(vm, sizeof(DstArray));
ret.data.array->data = buffer;
ret.data.array->count = length;
ret.data.array->capacity = length;
gst_array_push(vm, visited, ret);
dst_array_push(vm, visited, ret);
for (i = 0; i < length; ++i)
if ((err = gst_deserialize_impl(vm, data, end, &data, visited, buffer + i, depth)))
if ((err = dst_deserialize_impl(vm, data, end, &data, visited, buffer + i, depth)))
return err;
break;
case 209: /* Tuple */
ret.type = GST_TUPLE;
ret.type = DST_TUPLE;
read_u32(length);
buffer = gst_tuple_begin(vm, length);
buffer = dst_tuple_begin(vm, length);
for (i = 0; i < length; ++i)
if ((err = gst_deserialize_impl(vm, data, end, &data, visited, buffer + i, depth)))
if ((err = dst_deserialize_impl(vm, data, end, &data, visited, buffer + i, depth)))
return err;
ret.type = GST_TUPLE;
ret.data.tuple = gst_tuple_end(vm, buffer);
gst_array_push(vm, visited, ret);
ret.type = DST_TUPLE;
ret.data.tuple = dst_tuple_end(vm, buffer);
dst_array_push(vm, visited, ret);
break;
case 210: /* Thread */
{
GstThread *t;
GstValue *stack;
DstThread *t;
DstValue *stack;
uint16_t prevsize = 0;
uint8_t statusbyte;
t = gst_thread(vm, gst_wrap_nil(), 64);
ret = gst_wrap_thread(t);
gst_array_push(vm, visited, ret);
err = gst_deserialize_impl(vm, data, end, &data, visited, &ret, depth);
t = dst_thread(vm, dst_wrap_nil(), 64);
ret = dst_wrap_thread(t);
dst_array_push(vm, visited, ret);
err = dst_deserialize_impl(vm, data, end, &data, visited, &ret, depth);
if (err != NULL) return err;
if (ret.type == GST_NIL) {
if (ret.type == DST_NIL) {
t->parent = NULL;
} else if (ret.type == GST_THREAD) {
} else if (ret.type == DST_THREAD) {
t->parent = ret.data.thread;
} else {
return "expected thread parent to be thread";
}
err = gst_deserialize_impl(vm, data, end, &data, visited, &ret, depth);
if (err != NULL) return err;
if (ret.type == GST_NIL) {
t->errorParent = NULL;
} else if (ret.type == GST_THREAD) {
t->errorParent = ret.data.thread;
} else {
return "expected thread error parent to be thread";
}
deser_assert(data < end, UEB);
statusbyte = *data++;
read_u32(length);
@ -274,43 +266,43 @@ static const char *gst_deserialize_impl(
t->status = statusbyte % 4;
/* Add frames */
for (i = 0; i < length; ++i) {
GstValue callee, env;
DstValue callee, env;
uint32_t pcoffset;
uint16_t ret, args, size, j;
/* Read the stack */
err = gst_deserialize_impl(vm, data, end, &data, visited, &callee, depth);
err = dst_deserialize_impl(vm, data, end, &data, visited, &callee, depth);
if (err != NULL) return err;
err = gst_deserialize_impl(vm, data, end, &data, visited, &env, depth);
err = dst_deserialize_impl(vm, data, end, &data, visited, &env, depth);
if (err != NULL) return err;
if (env.type != GST_FUNCENV && env.type != GST_NIL)
if (env.type != DST_FUNCENV && env.type != DST_NIL)
return "expected funcenv in stackframe";
/* Create a new frame */
if (i > 0)
gst_thread_beginframe(vm, t, gst_wrap_nil(), 0);
dst_thread_beginframe(vm, t, dst_wrap_nil(), 0);
read_u32(pcoffset);
read_u32(ret);
read_u32(args);
read_u32(size);
/* Set up the stack */
stack = gst_thread_stack(t);
if (callee.type == GST_FUNCTION) {
gst_frame_pc(stack) = callee.data.function->def->byteCode + pcoffset;
stack = dst_thread_stack(t);
if (callee.type == DST_FUNCTION) {
dst_frame_pc(stack) = callee.data.function->def->byteCode + pcoffset;
}
gst_frame_ret(stack) = ret;
gst_frame_args(stack) = args;
gst_frame_size(stack) = size;
gst_frame_prevsize(stack) = prevsize;
gst_frame_callee(stack) = callee;
if (env.type == GST_NIL)
gst_frame_env(stack) = NULL;
dst_frame_ret(stack) = ret;
dst_frame_args(stack) = args;
dst_frame_size(stack) = size;
dst_frame_prevsize(stack) = prevsize;
dst_frame_callee(stack) = callee;
if (env.type == DST_NIL)
dst_frame_env(stack) = NULL;
else
gst_frame_env(stack) = env.data.env;
dst_frame_env(stack) = env.data.env;
prevsize = size;
/* Push stack args */
for (j = 0; j < size; ++j) {
GstValue temp;
err = gst_deserialize_impl(vm, data, end, &data, visited, &temp, depth);
gst_thread_push(vm, t, temp);
DstValue temp;
err = dst_deserialize_impl(vm, data, end, &data, visited, &temp, depth);
dst_thread_push(vm, t, temp);
}
}
}
@ -318,45 +310,45 @@ static const char *gst_deserialize_impl(
case 211: /* Table */
read_u32(length);
ret = gst_wrap_table(gst_table(vm, 2 * length));
gst_array_push(vm, visited, ret);
ret = dst_wrap_table(dst_table(vm, 2 * length));
dst_array_push(vm, visited, ret);
for (i = 0; i < length; ++i) {
GstValue key, value;
err = gst_deserialize_impl(vm, data, end, &data, visited, &key, depth);
DstValue key, value;
err = dst_deserialize_impl(vm, data, end, &data, visited, &key, depth);
if (err != NULL) return err;
err = gst_deserialize_impl(vm, data, end, &data, visited, &value, depth);
err = dst_deserialize_impl(vm, data, end, &data, visited, &value, depth);
if (err != NULL) return err;
gst_table_put(vm, ret.data.table, key, value);
dst_table_put(vm, ret.data.table, key, value);
}
break;
case 212: /* Funcdef */
{
GstFuncDef *def;
DstFuncDef *def;
uint32_t locals, arity, literalsLen, byteCodeLen, flags;
read_u32(locals);
read_u32(arity);
read_u32(flags);
read_u32(literalsLen);
def = gst_alloc(vm, sizeof(GstFuncDef));
ret = gst_wrap_funcdef(def);
gst_array_push(vm, visited, ret);
def = dst_alloc(vm, sizeof(DstFuncDef));
ret = dst_wrap_funcdef(def);
dst_array_push(vm, visited, ret);
def->locals = locals;
def->arity = arity;
def->flags = flags;
def->literalsLen = literalsLen;
if (literalsLen > 0) {
def->literals = gst_alloc(vm, literalsLen * sizeof(GstValue));
def->literals = dst_alloc(vm, literalsLen * sizeof(DstValue));
} else {
def->literals = NULL;
}
for (i = 0; i < literalsLen; ++i) {
err = gst_deserialize_impl(vm, data, end, &data, visited, def->literals + i, depth);
err = dst_deserialize_impl(vm, data, end, &data, visited, def->literals + i, depth);
if (err != NULL) return err;
}
read_u32(byteCodeLen);
deser_datacheck(byteCodeLen * sizeof(uint16_t));
def->byteCode = gst_alloc(vm, byteCodeLen * sizeof(uint16_t));
def->byteCode = dst_alloc(vm, byteCodeLen * sizeof(uint16_t));
def->byteCodeLen = byteCodeLen;
for (i = 0; i < byteCodeLen; ++i) {
read_u16(def->byteCode[i]);
@ -366,22 +358,22 @@ static const char *gst_deserialize_impl(
case 213: /* Funcenv */
{
GstValue thread;
ret.type = GST_FUNCENV;
ret.data.env = gst_alloc(vm, sizeof(GstFuncEnv));
gst_array_push(vm, visited, ret);
err = gst_deserialize_impl(vm, data, end, &data, visited, &thread, depth);
DstValue thread;
ret.type = DST_FUNCENV;
ret.data.env = dst_alloc(vm, sizeof(DstFuncEnv));
dst_array_push(vm, visited, ret);
err = dst_deserialize_impl(vm, data, end, &data, visited, &thread, depth);
if (err != NULL) return err;
read_u32(length);
ret.data.env->stackOffset = length;
if (thread.type == GST_THREAD) {
if (thread.type == DST_THREAD) {
ret.data.env->thread = thread.data.thread;
} else {
ret.data.env->thread = NULL;
ret.data.env->values = gst_alloc(vm, sizeof(GstValue) * length);
ret.data.env->values = dst_alloc(vm, sizeof(DstValue) * length);
for (i = 0; i < length; ++i) {
GstValue item;
err = gst_deserialize_impl(vm, data, end, &data, visited, &item, depth);
DstValue item;
err = dst_deserialize_impl(vm, data, end, &data, visited, &item, depth);
if (err != NULL) return err;
ret.data.env->values[i] = item;
}
@ -391,29 +383,29 @@ static const char *gst_deserialize_impl(
case 214: /* Function */
{
GstValue parent, def, env;
ret.type = GST_FUNCTION;
ret.data.function = gst_alloc(vm, sizeof(GstFunction));
gst_array_push(vm, visited, ret);
err = gst_deserialize_impl(vm, data, end, &data, visited, &parent, depth);
DstValue parent, def, env;
ret.type = DST_FUNCTION;
ret.data.function = dst_alloc(vm, sizeof(DstFunction));
dst_array_push(vm, visited, ret);
err = dst_deserialize_impl(vm, data, end, &data, visited, &parent, depth);
if (err != NULL) return err;
err = gst_deserialize_impl(vm, data, end, &data, visited, &env, depth);
err = dst_deserialize_impl(vm, data, end, &data, visited, &env, depth);
if (err != NULL) return err;
err = gst_deserialize_impl(vm, data, end, &data, visited, &def, depth);
err = dst_deserialize_impl(vm, data, end, &data, visited, &def, depth);
if (err != NULL) return err;
if (parent.type == GST_NIL) {
if (parent.type == DST_NIL) {
ret.data.function->parent = NULL;
} else if (parent.type == GST_FUNCTION) {
} else if (parent.type == DST_FUNCTION) {
ret.data.function->parent = parent.data.function;
} else {
deser_error("expected function");
}
deser_assert(def.type == GST_FUNCDEF, "expected funcdef");
deser_assert(def.type == DST_FUNCDEF, "expected funcdef");
ret.data.function->def = def.data.def;
if (env.type == GST_NIL) {
if (env.type == DST_NIL) {
ret.data.function->env = NULL;
} else {
deser_assert(env.type == GST_FUNCENV, "expected funcenv");
deser_assert(env.type == DST_FUNCENV, "expected funcenv");
ret.data.function->env = env.data.env;
}
}
@ -428,13 +420,13 @@ static const char *gst_deserialize_impl(
case 216: /* C function */
{
GstValue id;
DstValue id;
read_u32(length);
deser_datacheck(length);
id = gst_wrap_string(gst_string_b(vm, data, length));
id = dst_wrap_string(dst_string_b(vm, data, length));
data += length;
ret = gst_table_get(vm->registry, id);
if (ret.type != GST_CFUNCTION) {
ret = dst_table_get(vm->registry, id);
if (ret.type != DST_CFUNCTION) {
deser_error("unabled to deserialize c function");
}
break;
@ -448,7 +440,7 @@ static const char *gst_deserialize_impl(
break;
case 218: /* Integer */
ret.type = GST_INTEGER;
ret.type = DST_INTEGER;
read_i64(ret.data.integer);
break;
}
@ -458,46 +450,46 @@ static const char *gst_deserialize_impl(
}
/* Load a value from data */
const char *gst_deserialize(
Gst *vm,
const char *dst_deserialize_internal(
Dst *vm,
const uint8_t *data,
uint32_t len,
GstValue *out,
DstValue *out,
const uint8_t **nextData) {
GstValue ret;
DstValue ret;
const char *err;
GstArray *visited = gst_array(vm, 10);
err = gst_deserialize_impl(vm, data, data + len, nextData, visited, &ret, GST_RECURSION_GUARD);
DstArray *visited = dst_array(vm, 10);
err = dst_deserialize_impl(vm, data, data + len, nextData, visited, &ret, DST_RECURSION_GUARD);
if (err != NULL) return err;
*out = ret;
return NULL;
}
/* Allow appending other types to buffers */
BUFFER_DEFINE(real, GstReal)
BUFFER_DEFINE(integer, GstInteger)
BUFFER_DEFINE(real, DstReal)
BUFFER_DEFINE(integer, DstInteger)
BUFFER_DEFINE(u32, uint32_t)
BUFFER_DEFINE(u16, uint16_t)
/* Serialize a value and write to a buffer. Returns possible
* error messages. */
static const char *gst_serialize_impl(
Gst *vm,
GstBuffer *buffer,
GstTable *visited,
static const char *dst_serialize_impl(
Dst *vm,
DstBuffer *buffer,
DstTable *visited,
uint32_t *nextId,
GstValue x,
DstValue x,
int depth) {
uint32_t i, count;
const char *err;
GstValue check;
DstValue check;
#define write_byte(b) gst_buffer_push(vm, buffer, (b))
#define write_u32(b) gst_buffer_push_u32(vm, buffer, (b))
#define write_u16(b) gst_buffer_push_u16(vm, buffer, (b))
#define write_dbl(b) gst_buffer_push_real(vm, buffer, (b))
#define write_int(b) gst_buffer_push_integer(vm, buffer, (b))
#define write_byte(b) dst_buffer_push(vm, buffer, (b))
#define write_u32(b) dst_buffer_push_u32(vm, buffer, (b))
#define write_u16(b) dst_buffer_push_u16(vm, buffer, (b))
#define write_dbl(b) dst_buffer_push_real(vm, buffer, (b))
#define write_int(b) dst_buffer_push_integer(vm, buffer, (b))
/*printf("Type: %d\n", x.type);*/
/* Check if we have gone too deep */
@ -507,18 +499,18 @@ static const char *gst_serialize_impl(
/* Check non reference types - if successful, return NULL */
switch (x.type) {
case GST_USERDATA:
case GST_NIL:
case DST_USERDATA:
case DST_NIL:
write_byte(201);
return NULL;
case GST_BOOLEAN:
case DST_BOOLEAN:
write_byte(x.data.boolean ? 202 : 203);
return NULL;
case GST_REAL:
case DST_REAL:
write_byte(204);
write_dbl(x.data.real);
return NULL;
case GST_INTEGER:
case DST_INTEGER:
if (x.data.integer <= 100 && x.data.integer >= -100) {
write_byte(x.data.integer + 100);
} else {
@ -531,8 +523,8 @@ static const char *gst_serialize_impl(
}
/* Check if already seen - if so, use reference */
check = gst_table_get(visited, x);
if (check.type == GST_INTEGER) {
check = dst_table_get(visited, x);
if (check.type == DST_INTEGER) {
write_byte(217);
write_u32((uint32_t) check.data.integer);
return NULL;
@ -544,37 +536,37 @@ static const char *gst_serialize_impl(
* if they are treated like other reference types because they cannot
* be added to the visited table before recursing into serializing their
* arguments */
if (x.type == GST_STRUCT || x.type == GST_TUPLE) {
if (x.type == GST_STRUCT) {
const GstValue *data;
if (x.type == DST_STRUCT || x.type == DST_TUPLE) {
if (x.type == DST_STRUCT) {
const DstValue *data;
write_byte(206);
gst_hashtable_view(x, &data, &count);
write_u32(gst_struct_length(x.data.st));
dst_hashtable_view(x, &data, &count);
write_u32(dst_struct_length(x.data.st));
for (i = 0; i < count; i += 2) {
if (data[i].type != GST_NIL) {
err = gst_serialize_impl(vm, buffer, visited, nextId, data[i], depth);
if (data[i].type != DST_NIL) {
err = dst_serialize_impl(vm, buffer, visited, nextId, data[i], depth);
if (err != NULL) return err;
err = gst_serialize_impl(vm, buffer, visited, nextId, data[i + 1], depth);
err = dst_serialize_impl(vm, buffer, visited, nextId, data[i + 1], depth);
if (err != NULL) return err;
}
}
} else {
write_byte(209);
count = gst_tuple_length(x.data.tuple);
count = dst_tuple_length(x.data.tuple);
write_u32(count);
for (i = 0; i < count; ++i) {
err = gst_serialize_impl(vm, buffer, visited, nextId, x.data.tuple[i], depth);
err = dst_serialize_impl(vm, buffer, visited, nextId, x.data.tuple[i], depth);
if (err != NULL) return err;
}
}
/* Record reference after serialization */
gst_table_put(vm, visited, x, gst_wrap_integer(*nextId));
dst_table_put(vm, visited, x, dst_wrap_integer(*nextId));
*nextId = *nextId + 1;
return NULL;
}
/* Record reference before serialization */
gst_table_put(vm, visited, x, gst_wrap_integer(*nextId));
dst_table_put(vm, visited, x, dst_wrap_integer(*nextId));
*nextId = *nextId + 1;
/* Check reference types */
@ -582,21 +574,21 @@ static const char *gst_serialize_impl(
default:
return "unable to serialize type";
case GST_STRING:
case GST_SYMBOL:
write_byte(x.type == GST_STRING ? 205 : 219);
count = gst_string_length(x.data.string);
case DST_STRING:
case DST_SYMBOL:
write_byte(x.type == DST_STRING ? 205 : 219);
count = dst_string_length(x.data.string);
write_u32(count);
for (i = 0; i < count; ++i) {
write_byte(x.data.string[i]);
}
break;
case GST_CFUNCTION:
case DST_CFUNCTION:
write_byte(216);
{
GstValue id = gst_table_get(vm->registry, x);
count = gst_string_length(id.data.string);
DstValue id = dst_table_get(vm->registry, x);
count = dst_string_length(id.data.string);
write_u32(count);
for (i = 0; i < count; ++i) {
write_byte(id.data.string[i]);
@ -604,24 +596,24 @@ static const char *gst_serialize_impl(
}
break;
case GST_TABLE:
case DST_TABLE:
{
const GstValue *data;
const DstValue *data;
write_byte(211);
gst_hashtable_view(x, &data, &count);
dst_hashtable_view(x, &data, &count);
write_u32(x.data.table->count);
for (i = 0; i < count; i += 2) {
if (data[i].type != GST_NIL) {
err = gst_serialize_impl(vm, buffer, visited, nextId, data[i], depth);
if (data[i].type != DST_NIL) {
err = dst_serialize_impl(vm, buffer, visited, nextId, data[i], depth);
if (err != NULL) return err;
err = gst_serialize_impl(vm, buffer, visited, nextId, data[i + 1], depth);
err = dst_serialize_impl(vm, buffer, visited, nextId, data[i + 1], depth);
if (err != NULL) return err;
}
}
}
break;
case GST_BYTEBUFFER:
case DST_BYTEBUFFER:
write_byte(207);
count = x.data.buffer->count;
write_u32(x.data.buffer->capacity);
@ -631,32 +623,27 @@ static const char *gst_serialize_impl(
}
break;
case GST_ARRAY:
case DST_ARRAY:
write_byte(208);
count = x.data.array->count;
write_u32(count);
for (i = 0; i < count; ++i) {
err = gst_serialize_impl(vm, buffer, visited, nextId, x.data.array->data[i], depth);
err = dst_serialize_impl(vm, buffer, visited, nextId, x.data.array->data[i], depth);
if (err != NULL) return err;
}
break;
case GST_THREAD:
case DST_THREAD:
{
GstThread *t = x.data.thread;
const GstValue *stack = t->data + GST_FRAME_SIZE;
uint32_t framecount = gst_thread_countframes(t);
DstThread *t = x.data.thread;
const DstValue *stack = t->data + DST_FRAME_SIZE;
uint32_t framecount = dst_thread_countframes(t);
uint32_t i;
write_byte(210);
if (t->parent)
err = gst_serialize_impl(vm, buffer, visited, nextId, gst_wrap_thread(t->parent), depth);
err = dst_serialize_impl(vm, buffer, visited, nextId, dst_wrap_thread(t->parent), depth);
else
err = gst_serialize_impl(vm, buffer, visited, nextId, gst_wrap_nil(), depth);
if (t->errorParent)
err = gst_serialize_impl(vm, buffer, visited, nextId,
gst_wrap_thread(t->errorParent), depth);
else
err = gst_serialize_impl(vm, buffer, visited, nextId, gst_wrap_nil(), depth);
err = dst_serialize_impl(vm, buffer, visited, nextId, dst_wrap_nil(), depth);
if (err != NULL) return err;
/* Write the status byte */
write_byte(t->status);
@ -665,44 +652,44 @@ static const char *gst_serialize_impl(
/* Write stack frames */
for (i = 0; i < framecount; ++i) {
uint32_t j, size;
GstValue callee = gst_frame_callee(stack);
GstFuncEnv *env = gst_frame_env(stack);
err = gst_serialize_impl(vm, buffer, visited, nextId, callee, depth);
DstValue callee = dst_frame_callee(stack);
DstFuncEnv *env = dst_frame_env(stack);
err = dst_serialize_impl(vm, buffer, visited, nextId, callee, depth);
if (err != NULL) return err;
if (env)
err = gst_serialize_impl(vm, buffer, visited, nextId, gst_wrap_funcenv(env), depth);
err = dst_serialize_impl(vm, buffer, visited, nextId, dst_wrap_funcenv(env), depth);
else
err = gst_serialize_impl(vm, buffer, visited, nextId, gst_wrap_nil(), depth);
err = dst_serialize_impl(vm, buffer, visited, nextId, dst_wrap_nil(), depth);
if (err != NULL) return err;
if (callee.type == GST_FUNCTION) {
write_u32(gst_frame_pc(stack) - callee.data.function->def->byteCode);
if (callee.type == DST_FUNCTION) {
write_u32(dst_frame_pc(stack) - callee.data.function->def->byteCode);
} else {
write_u32(0);
}
write_u32(gst_frame_ret(stack));
write_u32(gst_frame_args(stack));
size = gst_frame_size(stack);
write_u32(dst_frame_ret(stack));
write_u32(dst_frame_args(stack));
size = dst_frame_size(stack);
write_u32(size);
for (j = 0; j < size; ++j) {
err = gst_serialize_impl(vm, buffer, visited, nextId, stack[j], depth);
err = dst_serialize_impl(vm, buffer, visited, nextId, stack[j], depth);
if (err != NULL) return err;
}
/* Next stack frame */
stack += size + GST_FRAME_SIZE;
stack += size + DST_FRAME_SIZE;
}
}
break;
case GST_FUNCDEF: /* Funcdef */
case DST_FUNCDEF: /* Funcdef */
{
GstFuncDef *def = x.data.def;
DstFuncDef *def = x.data.def;
write_byte(212);
write_u32(def->locals);
write_u32(def->arity);
write_u32(def->flags);
write_u32(def->literalsLen);
for (i = 0; i < def->literalsLen; ++i) {
err = gst_serialize_impl(vm, buffer, visited, nextId, def->literals[i], depth);
err = dst_serialize_impl(vm, buffer, visited, nextId, def->literals[i], depth);
if (err != NULL) return err;
}
write_u32(def->byteCodeLen);
@ -712,38 +699,38 @@ static const char *gst_serialize_impl(
}
break;
case GST_FUNCENV: /* Funcenv */
case DST_FUNCENV: /* Funcenv */
{
GstFuncEnv *env = x.data.env;
DstFuncEnv *env = x.data.env;
write_byte(213);
if (env->thread) {
err = gst_serialize_impl(vm, buffer, visited, nextId, gst_wrap_thread(env->thread), depth);
err = dst_serialize_impl(vm, buffer, visited, nextId, dst_wrap_thread(env->thread), depth);
if (err != NULL) return err;
write_u32(env->stackOffset);
} else {
write_byte(201); /* Write nil */
write_u32(env->stackOffset);
for (i = 0; i < env->stackOffset; ++i) {
err = gst_serialize_impl(vm, buffer, visited, nextId, env->values[i], depth);
err = dst_serialize_impl(vm, buffer, visited, nextId, env->values[i], depth);
if (err != NULL) return err;
}
}
}
break;
case GST_FUNCTION: /* Function */
case DST_FUNCTION: /* Function */
{
GstValue pv, ev, dv;
GstFunction *fn = x.data.function;
DstValue pv, ev, dv;
DstFunction *fn = x.data.function;
write_byte(214);
pv = fn->parent ? gst_wrap_function(fn->parent) : gst_wrap_nil();
dv = gst_wrap_funcdef(fn->def);
ev = fn->env ? gst_wrap_funcenv(fn->env) : gst_wrap_nil();
err = gst_serialize_impl(vm, buffer, visited, nextId, pv, depth);
pv = fn->parent ? dst_wrap_function(fn->parent) : dst_wrap_nil();
dv = dst_wrap_funcdef(fn->def);
ev = fn->env ? dst_wrap_funcenv(fn->env) : dst_wrap_nil();
err = dst_serialize_impl(vm, buffer, visited, nextId, pv, depth);
if (err != NULL) return err;
err = gst_serialize_impl(vm, buffer, visited, nextId, ev, depth);
err = dst_serialize_impl(vm, buffer, visited, nextId, ev, depth);
if (err != NULL) return err;
err = gst_serialize_impl(vm, buffer, visited, nextId, dv, depth);
err = dst_serialize_impl(vm, buffer, visited, nextId, dv, depth);
if (err != NULL) return err;
}
break;
@ -754,12 +741,12 @@ static const char *gst_serialize_impl(
}
/* Serialize an object */
const char *gst_serialize(Gst *vm, GstBuffer *buffer, GstValue x) {
const char *dst_serialize_internal(Dst *vm, DstBuffer *buffer, DstValue x) {
uint32_t nextId = 0;
uint32_t oldCount = buffer->count;
const char *err;
GstTable *visited = gst_table(vm, 10);
err = gst_serialize_impl(vm, buffer, visited, &nextId, x, GST_RECURSION_GUARD);
DstTable *visited = dst_table(vm, 10);
err = dst_serialize_impl(vm, buffer, visited, &nextId, x, DST_RECURSION_GUARD);
if (err != NULL) {
buffer->count = oldCount;
}

1211
core/stl.c

File diff suppressed because it is too large Load Diff

View File

@ -20,23 +20,23 @@
* IN THE SOFTWARE.
*/
#include <gst/gst.h>
#include "internal.h"
/* Temporary buffer size */
#define GST_BUFSIZE 36
#define DST_BUFSIZE 36
static const uint8_t *real_to_string(Gst *vm, GstReal x) {
uint8_t buf[GST_BUFSIZE];
int count = snprintf((char *) buf, GST_BUFSIZE, "%.21gF", x);
return gst_string_b(vm, buf, (uint32_t) count);
static const uint8_t *real_to_string(Dst *vm, DstReal x) {
uint8_t buf[DST_BUFSIZE];
int count = snprintf((char *) buf, DST_BUFSIZE, "%.21gF", x);
return dst_string_b(vm, buf, (uint32_t) count);
}
static const uint8_t *integer_to_string(Gst *vm, GstInteger x) {
uint8_t buf[GST_BUFSIZE];
static const uint8_t *integer_to_string(Dst *vm, DstInteger x) {
uint8_t buf[DST_BUFSIZE];
int neg = 0;
uint8_t *hi, *low;
uint32_t count = 0;
if (x == 0) return gst_string_c(vm, "0");
if (x == 0) return dst_string_c(vm, "0");
if (x < 0) {
neg = 1;
x = -x;
@ -56,16 +56,16 @@ static const uint8_t *integer_to_string(Gst *vm, GstInteger x) {
*low++ = *hi;
*hi-- = temp;
}
return gst_string_b(vm, buf, (uint32_t) count);
return dst_string_b(vm, buf, (uint32_t) count);
}
static const char *HEX_CHARACTERS = "0123456789abcdef";
#define HEX(i) (((uint8_t *) HEX_CHARACTERS)[(i)])
/* Returns a string description for a pointer. Max titlelen is GST_BUFSIZE
/* Returns a string description for a pointer. Max titlelen is DST_BUFSIZE
* - 5 - 2 * sizeof(void *). */
static const uint8_t *string_description(Gst *vm, const char *title, void *pointer) {
uint8_t buf[GST_BUFSIZE];
static const uint8_t *string_description(Dst *vm, const char *title, void *pointer) {
uint8_t buf[DST_BUFSIZE];
uint8_t *c = buf;
uint32_t i;
union {
@ -87,12 +87,12 @@ static const uint8_t *string_description(Gst *vm, const char *title, void *point
*c++ = HEX(byte & 0xF);
}
*c++ = '>';
return gst_string_b(vm, buf, c - buf);
return dst_string_b(vm, buf, c - buf);
}
/* Gets the string value of userdata. Allocates memory, so is slower than
* string_description. */
static const uint8_t *string_udata(Gst *vm, const char *title, void *pointer) {
static const uint8_t *string_udata(Dst *vm, const char *title, void *pointer) {
uint32_t strlen = 0;
uint8_t *c, *buf;
uint32_t i;
@ -102,7 +102,7 @@ static const uint8_t *string_udata(Gst *vm, const char *title, void *pointer) {
} pbuf;
while (title[strlen]) ++strlen;
c = buf = gst_alloc(vm, strlen + 5 + 2 * sizeof(void *));
c = buf = dst_alloc(vm, strlen + 5 + 2 * sizeof(void *));
pbuf.p = pointer;
*c++ = '<';
for (i = 0; title[i]; ++i)
@ -117,180 +117,187 @@ static const uint8_t *string_udata(Gst *vm, const char *title, void *pointer) {
*c++ = HEX(byte & 0xF);
}
*c++ = '>';
return gst_string_b(vm, buf, c - buf);
return dst_string_b(vm, buf, c - buf);
}
#undef GST_BUFSIZE
#undef DST_BUFSIZE
void gst_escape_string(Gst *vm, GstBuffer *b, const uint8_t *str) {
void dst_escape_string(Dst *vm, DstBuffer *b, const uint8_t *str) {
uint32_t i;
gst_buffer_push(vm, b, '"');
for (i = 0; i < gst_string_length(str); ++i) {
dst_buffer_push(vm, b, '"');
for (i = 0; i < dst_string_length(str); ++i) {
uint8_t c = str[i];
switch (c) {
case '"':
gst_buffer_push(vm, b, '\\');
gst_buffer_push(vm, b, '"');
dst_buffer_push(vm, b, '\\');
dst_buffer_push(vm, b, '"');
break;
case '\n':
gst_buffer_push(vm, b, '\\');
gst_buffer_push(vm, b, 'n');
dst_buffer_push(vm, b, '\\');
dst_buffer_push(vm, b, 'n');
break;
case '\r':
gst_buffer_push(vm, b, '\\');
gst_buffer_push(vm, b, 'r');
dst_buffer_push(vm, b, '\\');
dst_buffer_push(vm, b, 'r');
break;
case '\0':
gst_buffer_push(vm, b, '\\');
gst_buffer_push(vm, b, '0');
dst_buffer_push(vm, b, '\\');
dst_buffer_push(vm, b, '0');
break;
default:
gst_buffer_push(vm, b, c);
dst_buffer_push(vm, b, c);
break;
}
}
gst_buffer_push(vm, b, '"');
dst_buffer_push(vm, b, '"');
}
/* Returns a string pointer with the description of the string */
const uint8_t *gst_short_description(Gst *vm, GstValue x) {
const uint8_t *dst_short_description(Dst *vm, DstValue x) {
switch (x.type) {
case GST_NIL:
return gst_string_c(vm, "nil");
case GST_BOOLEAN:
case DST_NIL:
return dst_string_c(vm, "nil");
case DST_BOOLEAN:
if (x.data.boolean)
return gst_string_c(vm, "true");
return dst_string_c(vm, "true");
else
return gst_string_c(vm, "false");
case GST_REAL:
return dst_string_c(vm, "false");
case DST_REAL:
return real_to_string(vm, x.data.real);
case GST_INTEGER:
case DST_INTEGER:
return integer_to_string(vm, x.data.integer);
case GST_ARRAY:
case DST_ARRAY:
return string_description(vm, "array", x.data.pointer);
case GST_TUPLE:
case DST_TUPLE:
return string_description(vm, "tuple", x.data.pointer);
case GST_STRUCT:
case DST_STRUCT:
return string_description(vm, "struct", x.data.pointer);
case GST_TABLE:
case DST_TABLE:
return string_description(vm, "table", x.data.pointer);
case GST_SYMBOL:
case DST_SYMBOL:
return x.data.string;
case GST_STRING:
case DST_STRING:
{
GstBuffer *buf = gst_buffer(vm, gst_string_length(x.data.string) + 4);
gst_escape_string(vm, buf, x.data.string);
return gst_buffer_to_string(vm, buf);
DstBuffer *buf = dst_buffer(vm, dst_string_length(x.data.string) + 4);
dst_escape_string(vm, buf, x.data.string);
return dst_buffer_to_string(vm, buf);
}
case GST_BYTEBUFFER:
case DST_BYTEBUFFER:
return string_description(vm, "buffer", x.data.pointer);
case GST_CFUNCTION:
case DST_CFUNCTION:
return string_description(vm, "cfunction", x.data.pointer);
case GST_FUNCTION:
case DST_FUNCTION:
return string_description(vm, "function", x.data.pointer);
case GST_THREAD:
case DST_THREAD:
return string_description(vm, "thread", x.data.pointer);
case GST_USERDATA:
return string_udata(vm, gst_udata_type(x.data.pointer)->name, x.data.pointer);
case GST_FUNCENV:
case DST_USERDATA:
return string_udata(vm, dst_udata_type(x.data.pointer)->name, x.data.pointer);
case DST_FUNCENV:
return string_description(vm, "funcenv", x.data.pointer);
case GST_FUNCDEF:
case DST_FUNCDEF:
return string_description(vm, "funcdef", x.data.pointer);
}
}
static DstValue wrap_integer(DstInteger i) {
DstValue v;
v.type = DST_INTEGER;
v.data.integer = i;
return v;
}
/* Static debug print helper */
static GstInteger gst_description_helper(Gst *vm, GstBuffer *b, GstTable *seen, GstValue x, GstInteger next, int depth) {
GstValue check = gst_table_get(seen, x);
static DstInteger dst_description_helper(Dst *vm, DstBuffer *b, DstTable *seen, DstValue x, DstInteger next, int depth) {
DstValue check = dst_table_get(seen, x);
const uint8_t *str;
/* Prevent a stack overflow */
if (depth++ > GST_RECURSION_GUARD)
if (depth++ > DST_RECURSION_GUARD)
return -1;
if (check.type == GST_INTEGER) {
if (check.type == DST_INTEGER) {
str = integer_to_string(vm, check.data.integer);
gst_buffer_append_cstring(vm, b, "<visited ");
gst_buffer_append(vm, b, str, gst_string_length(str));
gst_buffer_append_cstring(vm, b, ">");
dst_buffer_append_cstring(vm, b, "<visited ");
dst_buffer_append(vm, b, str, dst_string_length(str));
dst_buffer_append_cstring(vm, b, ">");
} else {
uint8_t open, close;
uint32_t len, i;
const GstValue *data;
const DstValue *data;
switch (x.type) {
default:
str = gst_short_description(vm, x);
gst_buffer_append(vm, b, str, gst_string_length(str));
str = dst_short_description(vm, x);
dst_buffer_append(vm, b, str, dst_string_length(str));
return next;
case GST_STRING:
gst_escape_string(vm, b, x.data.string);
case DST_STRING:
dst_escape_string(vm, b, x.data.string);
return next;
case GST_SYMBOL:
gst_buffer_append(vm, b, x.data.string, gst_string_length(x.data.string));
case DST_SYMBOL:
dst_buffer_append(vm, b, x.data.string, dst_string_length(x.data.string));
return next;
case GST_NIL:
gst_buffer_append_cstring(vm, b, "nil");
case DST_NIL:
dst_buffer_append_cstring(vm, b, "nil");
return next;
case GST_BOOLEAN:
gst_buffer_append_cstring(vm, b, x.data.boolean ? "true" : "false");
case DST_BOOLEAN:
dst_buffer_append_cstring(vm, b, x.data.boolean ? "true" : "false");
return next;
case GST_STRUCT:
case DST_STRUCT:
open = '<'; close = '>';
break;
case GST_TABLE:
case DST_TABLE:
open = '{'; close = '}';
break;
case GST_TUPLE:
case DST_TUPLE:
open = '('; close = ')';
break;
case GST_ARRAY:
case DST_ARRAY:
open = '['; close = ']';
break;
}
gst_table_put(vm, seen, x, gst_wrap_integer(next++));
gst_buffer_push(vm, b, open);
if (gst_hashtable_view(x, &data, &len)) {
dst_table_put(vm, seen, x, wrap_integer(next++));
dst_buffer_push(vm, b, open);
if (dst_hashtable_view(x, &data, &len)) {
int isfirst = 1;
for (i = 0; i < len; i += 2) {
if (data[i].type != GST_NIL) {
if (data[i].type != DST_NIL) {
if (isfirst)
isfirst = 0;
else
gst_buffer_push(vm, b, ' ');
next = gst_description_helper(vm, b, seen, data[i], next, depth);
dst_buffer_push(vm, b, ' ');
next = dst_description_helper(vm, b, seen, data[i], next, depth);
if (next == -1)
gst_buffer_append_cstring(vm, b, "...");
gst_buffer_push(vm, b, ' ');
next = gst_description_helper(vm, b, seen, data[i + 1], next, depth);
dst_buffer_append_cstring(vm, b, "...");
dst_buffer_push(vm, b, ' ');
next = dst_description_helper(vm, b, seen, data[i + 1], next, depth);
if (next == -1)
gst_buffer_append_cstring(vm, b, "...");
dst_buffer_append_cstring(vm, b, "...");
}
}
} else if (gst_seq_view(x, &data, &len)) {
} else if (dst_seq_view(x, &data, &len)) {
for (i = 0; i < len; ++i) {
next = gst_description_helper(vm, b, seen, data[i], next, depth);
next = dst_description_helper(vm, b, seen, data[i], next, depth);
if (next == -1)
return -1;
if (i != len - 1)
gst_buffer_push(vm, b, ' ');
dst_buffer_push(vm, b, ' ');
}
}
gst_buffer_push(vm, b, close);
dst_buffer_push(vm, b, close);
}
return next;
}
/* Debug print. Returns a description of an object as a buffer. */
const uint8_t *gst_description(Gst *vm, GstValue x) {
GstBuffer *buf = gst_buffer(vm, 10);
gst_description_helper(vm, buf, gst_table(vm, 10), x, 0, 0);
return gst_buffer_to_string(vm, buf);
const uint8_t *dst_description(Dst *vm, DstValue x) {
DstBuffer *buf = dst_buffer(vm, 10);
dst_description_helper(vm, buf, dst_table(vm, 10), x, 0, 0);
return dst_buffer_to_string(vm, buf);
}
const uint8_t *gst_to_string(Gst *vm, GstValue x) {
if (x.type == GST_STRING || x.type == GST_SYMBOL) {
const uint8_t *dst_to_string(Dst *vm, DstValue x) {
if (x.type == DST_STRING || x.type == DST_SYMBOL) {
return x.data.string;
} else if (x.type == GST_BYTEBUFFER) {
return gst_buffer_to_string(vm, x.data.buffer);
} else if (x.type == DST_BYTEBUFFER) {
return dst_buffer_to_string(vm, x.data.buffer);
} else {
return gst_description(vm, x);
return dst_description(vm, x);
}
}

View File

@ -20,155 +20,154 @@
* IN THE SOFTWARE.
*/
#include <gst/gst.h>
#include "internal.h"
/* Create a new thread */
GstThread *gst_thread(Gst *vm, GstValue callee, uint32_t capacity) {
GstThread *thread = gst_alloc(vm, sizeof(GstThread));
if (capacity < GST_FRAME_SIZE) capacity = GST_FRAME_SIZE;
thread->data = gst_alloc(vm, sizeof(GstValue) * capacity);
DstThread *dst_thread(Dst *vm, DstValue callee, uint32_t capacity) {
DstThread *thread = dst_alloc(vm, sizeof(DstThread));
if (capacity < DST_FRAME_SIZE) capacity = DST_FRAME_SIZE;
thread->data = dst_alloc(vm, sizeof(DstValue) * capacity);
thread->capacity = capacity;
return gst_thread_reset(vm, thread, callee);
return dst_thread_reset(vm, thread, callee);
}
/* Clear a thread (reset it) */
GstThread *gst_thread_reset(Gst *vm, GstThread *thread, GstValue callee) {
GstValue *stack;
thread->count = GST_FRAME_SIZE;
thread->status = GST_THREAD_PENDING;
stack = thread->data + GST_FRAME_SIZE;
gst_frame_size(stack) = 0;
gst_frame_prevsize(stack) = 0;
gst_frame_ret(stack) = 0;
gst_frame_args(stack) = 0;
gst_frame_pc(stack) = NULL;
gst_frame_env(stack) = NULL;
gst_frame_callee(stack) = callee;
gst_thread_endframe(vm, thread);
DstThread *dst_thread_reset(Dst *vm, DstThread *thread, DstValue callee) {
DstValue *stack;
thread->count = DST_FRAME_SIZE;
thread->status = DST_THREAD_PENDING;
stack = thread->data + DST_FRAME_SIZE;
dst_frame_size(stack) = 0;
dst_frame_prevsize(stack) = 0;
dst_frame_ret(stack) = 0;
dst_frame_args(stack) = 0;
dst_frame_pc(stack) = NULL;
dst_frame_env(stack) = NULL;
dst_frame_callee(stack) = callee;
dst_thread_endframe(vm, thread);
thread->parent = NULL; /* Need to set parent manually */
thread->errorParent = NULL;
return thread;
}
/* Ensure that the thread has enough EXTRA capacity */
void gst_thread_ensure_extra(Gst *vm, GstThread *thread, uint32_t extra) {
GstValue *newData, *stack;
void dst_thread_ensure_extra(Dst *vm, DstThread *thread, uint32_t extra) {
DstValue *newData, *stack;
uint32_t usedCapacity, neededCapacity, newCapacity;
stack = thread->data + thread->count;
usedCapacity = thread->count + gst_frame_size(stack) + GST_FRAME_SIZE;
usedCapacity = thread->count + dst_frame_size(stack) + DST_FRAME_SIZE;
neededCapacity = usedCapacity + extra;
if (thread->capacity >= neededCapacity) return;
newCapacity = 2 * neededCapacity;
newData = gst_alloc(vm, sizeof(GstValue) * newCapacity);
gst_memcpy(newData, thread->data, sizeof(GstValue) * usedCapacity);
newData = dst_alloc(vm, sizeof(DstValue) * newCapacity);
dst_memcpy(newData, thread->data, sizeof(DstValue) * usedCapacity);
thread->data = newData;
thread->capacity = newCapacity;
}
/* Push a value on the current stack frame*/
void gst_thread_push(Gst *vm, GstThread *thread, GstValue x) {
GstValue *stack;
gst_thread_ensure_extra(vm, thread, 1);
void dst_thread_push(Dst *vm, DstThread *thread, DstValue x) {
DstValue *stack;
dst_thread_ensure_extra(vm, thread, 1);
stack = thread->data + thread->count;
stack[gst_frame_size(stack)++] = x;
stack[dst_frame_size(stack)++] = x;
}
/* Push n nils onto the stack */
void gst_thread_pushnil(Gst *vm, GstThread *thread, uint32_t n) {
GstValue *stack, *current, *end;
gst_thread_ensure_extra(vm, thread, n);
void dst_thread_pushnil(Dst *vm, DstThread *thread, uint32_t n) {
DstValue *stack, *current, *end;
dst_thread_ensure_extra(vm, thread, n);
stack = thread->data + thread->count;
current = stack + gst_frame_size(stack);
current = stack + dst_frame_size(stack);
end = current + n;
for (; current < end; ++current) {
current->type = GST_NIL;
current->type = DST_NIL;
}
gst_frame_size(stack) += n;
dst_frame_size(stack) += n;
}
/* Package up extra args after and including n into tuple at n*/
void gst_thread_tuplepack(Gst *vm, GstThread *thread, uint32_t n) {
GstValue *stack = thread->data + thread->count;
uint32_t size = gst_frame_size(stack);
void dst_thread_tuplepack(Dst *vm, DstThread *thread, uint32_t n) {
DstValue *stack = thread->data + thread->count;
uint32_t size = dst_frame_size(stack);
if (n >= size) {
/* Push one extra nil to ensure space for tuple */
gst_thread_pushnil(vm, thread, n - size + 1);
dst_thread_pushnil(vm, thread, n - size + 1);
stack = thread->data + thread->count;
stack[n].type = GST_TUPLE;
stack[n].data.tuple = gst_tuple_end(vm, gst_tuple_begin(vm, 0));
gst_frame_size(stack) = n + 1;
stack[n].type = DST_TUPLE;
stack[n].data.tuple = dst_tuple_end(vm, dst_tuple_begin(vm, 0));
dst_frame_size(stack) = n + 1;
} else {
uint32_t i;
GstValue *tuple = gst_tuple_begin(vm, size - n);
DstValue *tuple = dst_tuple_begin(vm, size - n);
for (i = n; i < size; ++i)
tuple[i - n] = stack[i];
stack[n].type = GST_TUPLE;
stack[n].data.tuple = gst_tuple_end(vm, tuple);
stack[n].type = DST_TUPLE;
stack[n].data.tuple = dst_tuple_end(vm, tuple);
}
}
/* Push a stack frame to a thread, with space for arity arguments. Returns the new
* stack. */
GstValue *gst_thread_beginframe(Gst *vm, GstThread *thread, GstValue callee, uint32_t arity) {
DstValue *dst_thread_beginframe(Dst *vm, DstThread *thread, DstValue callee, uint32_t arity) {
uint32_t frameOffset;
GstValue *oldStack, *newStack;
DstValue *oldStack, *newStack;
/* Push the frame */
gst_thread_ensure_extra(vm, thread, GST_FRAME_SIZE + arity + 4);
dst_thread_ensure_extra(vm, thread, DST_FRAME_SIZE + arity + 4);
oldStack = thread->data + thread->count;
frameOffset = gst_frame_size(oldStack) + GST_FRAME_SIZE;
frameOffset = dst_frame_size(oldStack) + DST_FRAME_SIZE;
newStack = oldStack + frameOffset;
gst_frame_prevsize(newStack) = gst_frame_size(oldStack);
gst_frame_env(newStack) = NULL;
gst_frame_size(newStack) = 0;
gst_frame_callee(newStack) = callee;
dst_frame_prevsize(newStack) = dst_frame_size(oldStack);
dst_frame_env(newStack) = NULL;
dst_frame_size(newStack) = 0;
dst_frame_callee(newStack) = callee;
thread->count += frameOffset;
/* Ensure the extra space and initialize to nil */
gst_thread_pushnil(vm, thread, arity);
dst_thread_pushnil(vm, thread, arity);
/* Return ok */
return thread->data + thread->count;
}
/* After pushing arguments to a stack frame created with gst_thread_beginframe, call this
/* After pushing arguments to a stack frame created with dst_thread_beginframe, call this
* to finalize the frame before starting a function call. */
void gst_thread_endframe(Gst *vm, GstThread *thread) {
GstValue *stack = thread->data + thread->count;
GstValue callee = gst_frame_callee(stack);
if (callee.type == GST_FUNCTION) {
GstFunction *fn = callee.data.function;
void dst_thread_endframe(Dst *vm, DstThread *thread) {
DstValue *stack = thread->data + thread->count;
DstValue callee = dst_frame_callee(stack);
if (callee.type == DST_FUNCTION) {
DstFunction *fn = callee.data.function;
uint32_t locals = fn->def->locals;
gst_frame_pc(stack) = fn->def->byteCode;
if (fn->def->flags & GST_FUNCDEF_FLAG_VARARG) {
dst_frame_pc(stack) = fn->def->byteCode;
if (fn->def->flags & DST_FUNCDEF_FLAG_VARARG) {
uint32_t arity = fn->def->arity;
gst_thread_tuplepack(vm, thread, arity);
dst_thread_tuplepack(vm, thread, arity);
}
if (gst_frame_size(stack) < locals) {
gst_thread_pushnil(vm, thread, locals - gst_frame_size(stack));
if (dst_frame_size(stack) < locals) {
dst_thread_pushnil(vm, thread, locals - dst_frame_size(stack));
}
}
}
/* Pop a stack frame from the thread. Returns the new stack frame, or
* NULL if there are no more frames */
GstValue *gst_thread_popframe(Gst *vm, GstThread *thread) {
GstValue *stack = thread->data + thread->count;
uint32_t prevsize = gst_frame_prevsize(stack);
GstValue *nextstack = stack - GST_FRAME_SIZE - prevsize;
GstFuncEnv *env = gst_frame_env(stack);
DstValue *dst_thread_popframe(Dst *vm, DstThread *thread) {
DstValue *stack = thread->data + thread->count;
uint32_t prevsize = dst_frame_prevsize(stack);
DstValue *nextstack = stack - DST_FRAME_SIZE - prevsize;
DstFuncEnv *env = dst_frame_env(stack);
/* Check for closures */
if (env != NULL) {
uint32_t size = gst_frame_size(stack);
uint32_t size = dst_frame_size(stack);
env->thread = NULL;
env->stackOffset = size;
env->values = gst_alloc(vm, sizeof(GstValue) * size);
gst_memcpy(env->values, stack, sizeof(GstValue) * size);
env->values = dst_alloc(vm, sizeof(DstValue) * size);
dst_memcpy(env->values, stack, sizeof(DstValue) * size);
}
/* Shrink stack */
thread->count -= GST_FRAME_SIZE + prevsize;
thread->count -= DST_FRAME_SIZE + prevsize;
/* Check if the stack is empty, and if so, return null */
if (thread->count)
@ -178,13 +177,13 @@ GstValue *gst_thread_popframe(Gst *vm, GstThread *thread) {
}
/* Count the number of stack frames in a thread */
uint32_t gst_thread_countframes(GstThread *thread) {
uint32_t dst_thread_countframes(DstThread *thread) {
uint32_t count = 0;
const GstValue *stack = thread->data + GST_FRAME_SIZE;
const GstValue *laststack = thread->data + thread->count;
const DstValue *stack = thread->data + DST_FRAME_SIZE;
const DstValue *laststack = thread->data + thread->count;
while (stack <= laststack) {
++count;
stack += gst_frame_size(stack) + GST_FRAME_SIZE;
stack += dst_frame_size(stack) + DST_FRAME_SIZE;
}
return count;
}

View File

@ -20,70 +20,7 @@
* IN THE SOFTWARE.
*/
#include <gst/gst.h>
/* Wrapper functions wrap a data type that is used from C into a
* gst value, which can then be used in gst. */
GstValue gst_wrap_nil() {
GstValue y;
y.type = GST_NIL;
return y;
}
int gst_check_nil(Gst *vm, uint32_t i) {
GstValue a = gst_arg(vm, i);
return a.type == GST_NIL;
}
#define GST_WRAP_DEFINE(NAME, TYPE, GTYPE, UM)\
GstValue gst_wrap_##NAME(TYPE x) {\
GstValue y;\
y.type = GTYPE;\
y.data.UM = x;\
return y;\
}\
\
int gst_check_##NAME(Gst *vm, uint32_t i, TYPE (*out)) {\
GstValue a = gst_arg(vm, i);\
if (a.type != GTYPE) return 0;\
*out = a.data.UM;\
return 1;\
}\
GST_WRAP_DEFINE(real, GstReal, GST_REAL, real)
GST_WRAP_DEFINE(integer, GstInteger, GST_INTEGER, integer)
GST_WRAP_DEFINE(boolean, int, GST_BOOLEAN, boolean)
GST_WRAP_DEFINE(string, const uint8_t *, GST_STRING, string)
GST_WRAP_DEFINE(symbol, const uint8_t *, GST_SYMBOL, string)
GST_WRAP_DEFINE(array, GstArray *, GST_ARRAY, array)
GST_WRAP_DEFINE(tuple, const GstValue *, GST_TUPLE, tuple)
GST_WRAP_DEFINE(struct, const GstValue *, GST_STRUCT, st)
GST_WRAP_DEFINE(thread, GstThread *, GST_THREAD, thread)
GST_WRAP_DEFINE(buffer, GstBuffer *, GST_BYTEBUFFER, buffer)
GST_WRAP_DEFINE(function, GstFunction *, GST_FUNCTION, function)
GST_WRAP_DEFINE(cfunction, GstCFunction, GST_CFUNCTION, cfunction)
GST_WRAP_DEFINE(table, GstTable *, GST_TABLE, table)
GST_WRAP_DEFINE(funcenv, GstFuncEnv *, GST_FUNCENV, env)
GST_WRAP_DEFINE(funcdef, GstFuncDef *, GST_FUNCDEF, def)
#undef GST_WRAP_DEFINE
GstValue gst_wrap_userdata(void *x) {
GstValue ret;
ret.type = GST_USERDATA;
ret.data.pointer = x;
return ret;
}
void *gst_check_userdata(Gst *vm, uint32_t i, const GstUserType *type) {
GstValue x = gst_arg(vm, i);
GstUserdataHeader *h;
if (x.type != GST_USERDATA) return NULL;
h = gst_udata_header(x.data.pointer);
if (h->type != type) return NULL;
return x.data.pointer;
}
#include "internal.h"
/****/
/* Parsing utils */
@ -105,7 +42,7 @@ static double exp10(int power) {
}
}
int gst_read_integer(const uint8_t *string, const uint8_t *end, int64_t *ret) {
int dst_read_integer(const uint8_t *string, const uint8_t *end, int64_t *ret) {
int sign = 1, x = 0;
int64_t accum = 0;
if (*string == '-') {
@ -129,7 +66,7 @@ int gst_read_integer(const uint8_t *string, const uint8_t *end, int64_t *ret) {
/* Read a real from a string. Returns if successfuly
* parsed a real from the enitre input string.
* If returned 1, output is int ret.*/
int gst_read_real(const uint8_t *string, const uint8_t *end, double *ret, int forceInt) {
int dst_read_real(const uint8_t *string, const uint8_t *end, double *ret, int forceInt) {
int sign = 1, x = 0;
double accum = 0, exp = 1, place = 1;
/* Check the sign */
@ -147,7 +84,7 @@ int gst_read_real(const uint8_t *string, const uint8_t *end, double *ret, int fo
/* Read the exponent */
++string;
if (string >= end) return 0;
if (!gst_read_real(string, end, &exp, 1))
if (!dst_read_real(string, end, &exp, 1))
return 0;
exp = exp10(exp);
break;
@ -173,14 +110,14 @@ int gst_read_real(const uint8_t *string, const uint8_t *end, double *ret, int fo
/* Read both tuples and arrays as c pointers + uint32_t length. Return 1 if the
* view can be constructed, 0 if an invalid type. */
int gst_seq_view(GstValue seq, const GstValue **data, uint32_t *len) {
if (seq.type == GST_ARRAY) {
int dst_seq_view(DstValue seq, const DstValue **data, uint32_t *len) {
if (seq.type == DST_ARRAY) {
*data = seq.data.array->data;
*len = seq.data.array->count;
return 1;
} else if (seq.type == GST_TUPLE) {
} else if (seq.type == DST_TUPLE) {
*data = seq.data.st;
*len = gst_tuple_length(seq.data.st);
*len = dst_tuple_length(seq.data.st);
return 1;
}
return 0;
@ -188,12 +125,12 @@ int gst_seq_view(GstValue seq, const GstValue **data, uint32_t *len) {
/* Read both strings and buffer as unsigned character array + uint32_t len.
* Returns 1 if the view can be constructed and 0 if the type is invalid. */
int gst_chararray_view(GstValue str, const uint8_t **data, uint32_t *len) {
if (str.type == GST_STRING || str.type == GST_SYMBOL) {
int dst_chararray_view(DstValue str, const uint8_t **data, uint32_t *len) {
if (str.type == DST_STRING || str.type == DST_SYMBOL) {
*data = str.data.string;
*len = gst_string_length(str.data.string);
*len = dst_string_length(str.data.string);
return 1;
} else if (str.type == GST_BYTEBUFFER) {
} else if (str.type == DST_BYTEBUFFER) {
*data = str.data.buffer->data;
*len = str.data.buffer->count;
return 1;
@ -204,28 +141,28 @@ int gst_chararray_view(GstValue str, const uint8_t **data, uint32_t *len) {
/* Read both structs and tables as the entries of a hashtable with
* identical structure. Returns 1 if the view can be constructed and
* 0 if the type is invalid. */
int gst_hashtable_view(GstValue tab, const GstValue **data, uint32_t *cap) {
if (tab.type == GST_TABLE) {
int dst_hashtable_view(DstValue tab, const DstValue **data, uint32_t *cap) {
if (tab.type == DST_TABLE) {
*data = tab.data.table->data;
*cap = tab.data.table->capacity;
return 1;
} else if (tab.type == GST_STRUCT) {
} else if (tab.type == DST_STRUCT) {
*data = tab.data.st;
*cap = gst_struct_capacity(tab.data.st);
*cap = dst_struct_capacity(tab.data.st);
return 1;
}
return 0;
}
GstReal gst_integer_to_real(GstInteger x) {
return (GstReal) x;
DstReal dst_integer_to_real(DstInteger x) {
return (DstReal) x;
}
GstInteger gst_real_to_integer(GstReal x) {
return (GstInteger) x;
DstInteger dst_real_to_integer(DstReal x) {
return (DstInteger) x;
}
GstInteger gst_startrange(GstInteger raw, uint32_t len) {
uint32_t dst_startrange(DstInteger raw, uint32_t len) {
if (raw >= len)
return -1;
if (raw < 0)
@ -233,7 +170,7 @@ GstInteger gst_startrange(GstInteger raw, uint32_t len) {
return raw;
}
GstInteger gst_endrange(GstInteger raw, uint32_t len) {
uint32_t dst_endrange(DstInteger raw, uint32_t len) {
if (raw > len)
return -1;
if (raw < 0)
@ -241,17 +178,176 @@ GstInteger gst_endrange(GstInteger raw, uint32_t len) {
return raw;
}
int gst_callc(Gst *vm, GstCFunction fn, int numargs, ...) {
static DstValue cfunction(DstCFunction fn) {
DstValue n;
n.type = DST_CFUNCTION;
n.data.cfunction = fn;
return n;
}
int dst_callc(Dst *vm, DstCFunction fn, int numargs, ...) {
int result, i;
va_list args;
GstValue *stack;
DstValue *stack;
va_start(args, numargs);
stack = gst_thread_beginframe(vm, vm->thread, gst_wrap_cfunction(fn), numargs);
stack = dst_thread_beginframe(vm, vm->thread, cfunction(fn), numargs);
for (i = 0; i < numargs; ++i) {
stack[i] = va_arg(args, GstValue);
stack[i] = va_arg(args, DstValue);
}
va_end(args);
result = fn(vm);
gst_thread_popframe(vm, vm->thread);
dst_thread_popframe(vm, vm->thread);
return result;
}
/* Stack manipulation functions */
int dst_checkerr(Dst *vm) {
return !!vm->flags;
}
/* Get an argument from the stack */
DstValue dst_arg(Dst *vm, uint32_t index) {
DstValue *stack = dst_thread_stack(vm->thread);
uint32_t frameSize = dst_frame_size(stack);
if (frameSize <= index) {
DstValue ret;
ret.type = DST_NIL;
return ret;
}
return stack[index];
}
/* Put a value on the stack */
void dst_set_arg(Dst* vm, uint32_t index, DstValue x) {
DstValue *stack = dst_thread_stack(vm->thread);
uint32_t frameSize = dst_frame_size(stack);
if (frameSize <= index) return;
stack[index] = x;
}
/* Get the size of the VMStack */
uint32_t dst_args(Dst *vm) {
DstValue *stack = dst_thread_stack(vm->thread);
return dst_frame_size(stack);
}
void dst_addsize(Dst *vm, uint32_t n) {
dst_thread_pushnil(vm, vm->thread, n);
}
void dst_setsize(Dst *vm, uint32_t n) {
DstValue *stack = dst_thread_stack(vm->thread);
uint32_t frameSize = dst_frame_size(stack);
if (frameSize < n) {
dst_thread_ensure_extra(vm, vm->thread, n - frameSize);
}
dst_frame_size(stack) = n;
}
void dst_swap(Dst *vm, uint32_t x, uint32_t y) {
DstValue oldx = dst_arg(vm, x);
DstValue oldy = dst_arg(vm, y);
dst_set_arg(vm, x, oldy);
dst_set_arg(vm, y, oldx);
}
void dst_move(Dst *vm, uint32_t dest, uint32_t src) {
dst_set_arg(vm, dest, dst_arg(vm, src));
}
void dst_nil(Dst *vm, uint32_t dest) {
DstValue n;
n.type = DST_NIL;
dst_set_arg(vm, dest, n);
}
void dst_true(Dst *vm, uint32_t dest) {
dst_set_boolean(vm, dest, 1);
}
void dst_false(Dst *vm, uint32_t dest) {
dst_set_boolean(vm, dest, 0);
}
/* Boolean Functions */
void dst_set_boolean(Dst *vm, uint32_t dest, int val) {
DstValue n;
n.type = DST_BOOLEAN;
n.data.boolean = val;
dst_set_arg(vm, dest, n);
}
int dst_get_boolean(Dst *vm, uint32_t b) {
DstValue x = dst_arg(vm, b);
if (x.type != DST_BOOLEAN) {
return 0;
}
return x.data.boolean;
}
/* Integer functions */
void dst_set_integer(Dst *vm, uint32_t dest, int64_t val) {
DstValue n;
n.type = DST_INTEGER;
n.data.integer = val;
dst_set_arg(vm, dest, n);
}
int64_t dst_get_integer(Dst *vm, uint32_t i) {
DstValue x = dst_arg(vm, i);
if (x.type != DST_INTEGER) {
return 0;
}
return x.data.integer;
}
/* Real functions */
void dst_set_real(Dst *vm, uint32_t dest, double val) {
DstValue n;
n.type = DST_REAL;
n.data.real = val;
dst_set_arg(vm, dest, n);
}
double dst_get_real(Dst *vm, uint32_t r) {
DstValue x = dst_arg(vm, r);
if (x.type != DST_REAL) {
return 0.0;
}
return x.data.real;
}
/* CFunction functions */
void dst_set_cfunction(Dst *vm, uint32_t dest, DstCFunction cfn) {
DstValue n;
n.type = DST_CFUNCTION;
n.data.cfunction = cfn;
dst_set_arg(vm, dest, n);
}
DstCFunction dst_get_cfunction(Dst *vm, uint32_t cfn) {
DstValue x = dst_arg(vm, cfn);
if (x.type != DST_CFUNCTION) {
return NULL;
}
return x.data.cfunction;
}
void dst_return(Dst *vm, uint32_t index) {
vm->ret = dst_arg(vm, index);
}
void dst_throw(Dst *vm, uint32_t index) {
vm->flags = 1;
vm->ret = dst_arg(vm, index);
}
void dst_cerr(Dst *vm, const char *message) {
vm->flags = 1;
vm->ret = dst_string_cv(vm, message);
}
int dst_checktype(Dst *vm, uint32_t n, DstType type) {
return dst_arg(vm, n).type == type;
}

View File

@ -20,31 +20,30 @@
* IN THE SOFTWARE.
*/
#include <gst/gst.h>
#include <stdio.h>
#include "internal.h"
/* Boolean truth definition */
int gst_truthy(GstValue v) {
return v.type != GST_NIL && !(v.type == GST_BOOLEAN && !v.data.boolean);
int dst_value_truthy(DstValue v) {
return v.type != DST_NIL && !(v.type == DST_BOOLEAN && !v.data.boolean);
}
/* Check if two values are equal. This is strict equality with no conversion. */
int gst_equals(GstValue x, GstValue y) {
int dst_value_equals(DstValue x, DstValue y) {
int result = 0;
if (x.type != y.type) {
result = 0;
} else {
switch (x.type) {
case GST_NIL:
case DST_NIL:
result = 1;
break;
case GST_BOOLEAN:
case DST_BOOLEAN:
result = (x.data.boolean == y.data.boolean);
break;
case GST_REAL:
case DST_REAL:
result = (x.data.real == y.data.real);
break;
case GST_INTEGER:
case DST_INTEGER:
result = (x.data.integer == y.data.integer);
break;
default:
@ -57,24 +56,24 @@ int gst_equals(GstValue x, GstValue y) {
}
/* Computes a hash value for a function */
uint32_t gst_hash(GstValue x) {
uint32_t dst_value_hash(DstValue x) {
uint32_t hash = 0;
switch (x.type) {
case GST_NIL:
case DST_NIL:
hash = 0;
break;
case GST_BOOLEAN:
case DST_BOOLEAN:
hash = x.data.boolean;
break;
case GST_STRING:
case GST_SYMBOL:
hash = gst_string_hash(x.data.string);
case DST_STRING:
case DST_SYMBOL:
hash = dst_string_hash(x.data.string);
break;
case GST_TUPLE:
hash = gst_tuple_hash(x.data.tuple);
case DST_TUPLE:
hash = dst_tuple_hash(x.data.tuple);
break;
case GST_STRUCT:
hash = gst_struct_hash(x.data.st);
case DST_STRUCT:
hash = dst_struct_hash(x.data.st);
break;
default:
if (sizeof(double) == sizeof(void *)) {
@ -92,40 +91,40 @@ uint32_t gst_hash(GstValue x) {
/* Compares x to y. If they are equal retuns 0. If x is less, returns -1.
* If y is less, returns 1. All types are comparable
* and should have strict ordering. */
int gst_compare(GstValue x, GstValue y) {
int dst_value_compare(DstValue x, DstValue y) {
if (x.type == y.type) {
switch (x.type) {
case GST_NIL:
case DST_NIL:
return 0;
case GST_BOOLEAN:
case DST_BOOLEAN:
if (x.data.boolean == y.data.boolean) {
return 0;
} else {
return x.data.boolean ? 1 : -1;
}
case GST_REAL:
case DST_REAL:
if (x.data.real == y.data.real) {
return 0;
} else {
return x.data.real > y.data.real ? 1 : -1;
}
case GST_INTEGER:
case DST_INTEGER:
if (x.data.integer == y.data.integer) {
return 0;
} else {
return x.data.integer > y.data.integer ? 1 : -1;
}
case GST_STRING:
return gst_string_compare(x.data.string, y.data.string);
case DST_STRING:
return dst_string_compare(x.data.string, y.data.string);
/* Lower indices are most significant */
case GST_TUPLE:
case DST_TUPLE:
{
uint32_t i;
uint32_t xlen = gst_tuple_length(x.data.tuple);
uint32_t ylen = gst_tuple_length(y.data.tuple);
uint32_t xlen = dst_tuple_length(x.data.tuple);
uint32_t ylen = dst_tuple_length(y.data.tuple);
uint32_t count = xlen < ylen ? xlen : ylen;
for (i = 0; i < count; ++i) {
int comp = gst_compare(x.data.tuple[i], y.data.tuple[i]);
int comp = dst_value_compare(x.data.tuple[i], y.data.tuple[i]);
if (comp != 0) return comp;
}
if (xlen < ylen)
@ -148,106 +147,77 @@ int gst_compare(GstValue x, GstValue y) {
return 1;
}
/* Get a value out af an associated data structure.
* Returns possible c error message, and NULL for no error. The
* useful return value is written to out on success */
const char *gst_get(GstValue ds, GstValue key, GstValue *out) {
GstInteger index;
GstValue ret;
switch (ds.type) {
case GST_ARRAY:
if (key.type != GST_INTEGER) return "expected integer key";
index = gst_startrange(key.data.integer, ds.data.array->count);
if (index < 0) return "invalid array access";
ret = ds.data.array->data[index];
break;
case GST_TUPLE:
if (key.type != GST_INTEGER) return "expected integer key";
index = gst_startrange(key.data.integer, gst_tuple_length(ds.data.tuple));
if (index < 0) return "invalid tuple access";
ret = ds.data.tuple[index];
break;
case GST_BYTEBUFFER:
if (key.type != GST_INTEGER) return "expected integer key";
index = gst_startrange(key.data.integer, ds.data.buffer->count);
if (index < 0) return "invalid buffer access";
ret.type = GST_INTEGER;
ret.data.integer = ds.data.buffer->data[index];
break;
case GST_STRING:
case GST_SYMBOL:
if (key.type != GST_INTEGER) return "expected integer key";
index = gst_startrange(key.data.integer, gst_string_length(ds.data.string));
if (index < 0) return "invalid string access";
ret.type = GST_INTEGER;
ret.data.integer = ds.data.string[index];
break;
case GST_STRUCT:
ret = gst_struct_get(ds.data.st, key);
break;
case GST_TABLE:
ret = gst_table_get(ds.data.table, key);
break;
default:
return "cannot get";
}
*out = ret;
return NULL;
int dst_truthy(Dst *vm, uint32_t x) {
return dst_value_truthy(dst_arg(vm, x));
}
/* Set a value in an associative data structure. Returns possible
* error message, and NULL if no error. */
const char *gst_set(Gst *vm, GstValue ds, GstValue key, GstValue value) {
GstInteger index;
switch (ds.type) {
case GST_ARRAY:
if (key.type != GST_INTEGER) return "expected integer key";
index = gst_startrange(key.data.integer, ds.data.array->count);
if (index < 0) return "invalid array access";
ds.data.array->data[index] = value;
break;
case GST_BYTEBUFFER:
if (key.type != GST_INTEGER) return "expected integer key";
if (value.type != GST_INTEGER) return "expected integer value";
index = gst_startrange(key.data.integer, ds.data.buffer->count);
if (index < 0) return "invalid buffer access";
ds.data.buffer->data[index] = (uint8_t) value.data.integer;
break;
case GST_TABLE:
gst_table_put(vm, ds.data.table, key, value);
break;
default:
return "cannot set";
}
return NULL;
uint32_t dst_hash(Dst *vm, uint32_t x) {
return dst_value_hash(dst_arg(vm, x));
}
int dst_compare(Dst *vm, uint32_t x, uint32_t y) {
return dst_value_compare(dst_arg(vm, x), dst_arg(vm, y));
}
int dst_equals(Dst *vm, uint32_t x, uint32_t y) {
return dst_value_equals(dst_arg(vm, x), dst_arg(vm, y));
}
/* Get the length of an object. Returns errors for invalid types */
GstInteger gst_length(Gst *vm, GstValue x) {
GstInteger length;
uint32_t dst_length(Dst *vm, uint32_t n) {
DstValue x = dst_arg(vm, n);
uint32_t length;
switch (x.type) {
default:
vm->ret = gst_string_cv(vm, "cannot get length");
return GST_RETURN_ERROR;
case GST_STRING:
length = gst_string_length(x.data.string);
vm->ret = dst_string_cv(vm, "cannot get length");
vm->flags = 1;
return 0;
case DST_STRING:
length = dst_string_length(x.data.string);
break;
case GST_ARRAY:
case DST_ARRAY:
length = x.data.array->count;
break;
case GST_BYTEBUFFER:
case DST_BYTEBUFFER:
length = x.data.buffer->count;
break;
case GST_TUPLE:
length = gst_tuple_length(x.data.tuple);
case DST_TUPLE:
length = dst_tuple_length(x.data.tuple);
break;
case GST_STRUCT:
length = gst_struct_length(x.data.st);
case DST_STRUCT:
length = dst_struct_length(x.data.st);
break;
case GST_TABLE:
case DST_TABLE:
length = x.data.table->count;
break;
}
return length;
}
/* Get the capacity of an object. Returns errors for invalid types */
uint32_t dst_capacity(Dst *vm, uint32_t n) {
DstValue x = dst_arg(vm, n);
uint32_t cap;
switch (x.type) {
default:
vm->ret = dst_string_cv(vm, "cannot get capacity");
vm->flags = 1;
return 0;
case DST_STRING:
cap = dst_string_length(x.data.string);
break;
case DST_ARRAY:
cap = x.data.array->capacity;
break;
case DST_BYTEBUFFER:
cap = x.data.buffer->capacity;
break;
case DST_TUPLE:
cap = dst_tuple_length(x.data.tuple);
break;
case DST_STRUCT:
cap = dst_struct_length(x.data.st);
break;
case DST_TABLE:
cap = x.data.table->capacity;
break;
}
return cap;
}

428
core/vm.c
View File

@ -20,28 +20,28 @@
* IN THE SOFTWARE.
*/
#include <gst/gst.h>
#include "internal.h"
static const char GST_NO_UPVALUE[] = "no upvalue";
static const char GST_EXPECTED_FUNCTION[] = "expected function";
static const char DST_NO_UPVALUE[] = "no upvalue";
static const char DST_EXPECTED_FUNCTION[] = "expected function";
/* Start running the VM from where it left off. */
int gst_continue(Gst *vm) {
int dst_continue(Dst *vm) {
/* VM state */
GstValue *stack;
DstValue *stack;
uint16_t *pc;
/* Some temporary values */
GstValue temp, v1, v2;
DstValue temp, v1, v2;
#define gst_exit(vm, r) return ((vm)->ret = (r), GST_RETURN_OK)
#define gst_error(vm, e) do { (vm)->ret = gst_string_cv((vm), (e)); goto vm_error; } while (0)
#define gst_assert(vm, cond, e) do {if (!(cond)){gst_error((vm), (e));}} while (0)
#define dst_exit(vm, r) return ((vm)->ret = (r), DST_RETURN_OK)
#define dst_error(vm, e) do { (vm)->ret = dst_string_cv((vm), (e)); goto vm_error; } while (0)
#define dst_assert(vm, cond, e) do {if (!(cond)){dst_error((vm), (e));}} while (0)
/* Intialize local state */
vm->thread->status = GST_THREAD_ALIVE;
stack = gst_thread_stack(vm->thread);
pc = gst_frame_pc(stack);
vm->thread->status = DST_THREAD_ALIVE;
stack = dst_thread_stack(vm->thread);
pc = dst_frame_pc(stack);
/* Main interpreter loop */
for (;;) {
@ -49,59 +49,59 @@ int gst_continue(Gst *vm) {
switch (*pc) {
default:
gst_error(vm, "unknown opcode");
dst_error(vm, "unknown opcode");
break;
case GST_OP_FLS: /* Load False */
temp.type = GST_BOOLEAN;
case DST_OP_FLS: /* Load False */
temp.type = DST_BOOLEAN;
temp.data.boolean = 0;
stack[pc[1]] = temp;
pc += 2;
continue;
case GST_OP_TRU: /* Load True */
temp.type = GST_BOOLEAN;
case DST_OP_TRU: /* Load True */
temp.type = DST_BOOLEAN;
temp.data.boolean = 1;
stack[pc[1]] = temp;
pc += 2;
continue;
case GST_OP_NIL: /* Load Nil */
temp.type = GST_NIL;
case DST_OP_NIL: /* Load Nil */
temp.type = DST_NIL;
stack[pc[1]] = temp;
pc += 2;
continue;
case GST_OP_I16: /* Load Small Integer */
temp.type = GST_INTEGER;
case DST_OP_I16: /* Load Small Integer */
temp.type = DST_INTEGER;
temp.data.integer = ((int16_t *)(pc))[2];
stack[pc[1]] = temp;
pc += 3;
continue;
case GST_OP_UPV: /* Load Up Value */
case GST_OP_SUV: /* Set Up Value */
case DST_OP_UPV: /* Load Up Value */
case DST_OP_SUV: /* Set Up Value */
{
GstValue *upv;
GstFunction *fn;
GstFuncEnv *env;
DstValue *upv;
DstFunction *fn;
DstFuncEnv *env;
uint16_t level = pc[2];
temp = gst_frame_callee(stack);
gst_assert(vm, temp.type == GST_FUNCTION, GST_EXPECTED_FUNCTION);
temp = dst_frame_callee(stack);
dst_assert(vm, temp.type == DST_FUNCTION, DST_EXPECTED_FUNCTION);
fn = temp.data.function;
if (level == 0)
upv = stack + pc[3];
else {
while (fn && --level)
fn = fn->parent;
gst_assert(vm, fn, GST_NO_UPVALUE);
dst_assert(vm, fn, DST_NO_UPVALUE);
env = fn->env;
if (env->thread)
upv = env->thread->data + env->stackOffset + pc[3];
else
upv = env->values + pc[3];
}
if (pc[0] == GST_OP_UPV) {
if (pc[0] == DST_OP_UPV) {
stack[pc[1]] = *upv;
} else {
*upv = stack[pc[1]];
@ -110,107 +110,107 @@ int gst_continue(Gst *vm) {
}
continue;
case GST_OP_JIF: /* Jump If */
if (gst_truthy(stack[pc[1]])) {
case DST_OP_JIF: /* Jump If */
if (dst_value_truthy(stack[pc[1]])) {
pc += 4;
} else {
pc += *((int32_t *)(pc + 2));
}
continue;
case GST_OP_JMP: /* Jump */
case DST_OP_JMP: /* Jump */
pc += *((int32_t *)(pc + 1));
continue;
case GST_OP_CST: /* Load constant value */
v1 = gst_frame_callee(stack);
gst_assert(vm, v1.type == GST_FUNCTION, GST_EXPECTED_FUNCTION);
case DST_OP_CST: /* Load constant value */
v1 = dst_frame_callee(stack);
dst_assert(vm, v1.type == DST_FUNCTION, DST_EXPECTED_FUNCTION);
if (pc[2] > v1.data.function->def->literalsLen)
gst_error(vm, GST_NO_UPVALUE);
dst_error(vm, DST_NO_UPVALUE);
stack[pc[1]] = v1.data.function->def->literals[pc[2]];
pc += 3;
continue;
case GST_OP_I32: /* Load 32 bit integer */
temp.type = GST_INTEGER;
case DST_OP_I32: /* Load 32 bit integer */
temp.type = DST_INTEGER;
temp.data.integer = *((int32_t *)(pc + 2));
stack[pc[1]] = temp;
pc += 4;
continue;
case GST_OP_I64: /* Load 64 bit integer */
temp.type = GST_INTEGER;
temp.data.integer = (GstInteger) *((int64_t *)(pc + 2));
case DST_OP_I64: /* Load 64 bit integer */
temp.type = DST_INTEGER;
temp.data.integer = (DstInteger) *((int64_t *)(pc + 2));
stack[pc[1]] = temp;
pc += 6;
continue;
case GST_OP_F64: /* Load 64 bit float */
temp.type = GST_REAL;
temp.data.real = (GstReal) *((double *)(pc + 2));
case DST_OP_F64: /* Load 64 bit float */
temp.type = DST_REAL;
temp.data.real = (DstReal) *((double *)(pc + 2));
stack[pc[1]] = temp;
pc += 6;
continue;
case GST_OP_MOV: /* Move Values */
case DST_OP_MOV: /* Move Values */
stack[pc[1]] = stack[pc[2]];
pc += 3;
continue;
case GST_OP_CLN: /* Create closure from constant FuncDef */
case DST_OP_CLN: /* Create closure from constant FuncDef */
{
GstFunction *fn;
v1 = gst_frame_callee(stack);
DstFunction *fn;
v1 = dst_frame_callee(stack);
temp = v1.data.function->def->literals[pc[2]];
if (temp.type != GST_FUNCDEF)
gst_error(vm, "cannot create closure from non-funcdef");
fn = gst_alloc(vm, sizeof(GstFunction));
if (temp.type != DST_FUNCDEF)
dst_error(vm, "cannot create closure from non-funcdef");
fn = dst_alloc(vm, sizeof(DstFunction));
fn->def = temp.data.def;
if (temp.data.def->flags & GST_FUNCDEF_FLAG_NEEDSPARENT)
if (temp.data.def->flags & DST_FUNCDEF_FLAG_NEEDSPARENT)
fn->parent = v1.data.function;
else
fn->parent = NULL;
if (v1.type != GST_FUNCTION)
gst_error(vm, GST_EXPECTED_FUNCTION);
if (gst_frame_env(stack) == NULL && (fn->def->flags & GST_FUNCDEF_FLAG_NEEDSENV)) {
gst_frame_env(stack) = gst_alloc(vm, sizeof(GstFuncEnv));
gst_frame_env(stack)->thread = vm->thread;
gst_frame_env(stack)->stackOffset = vm->thread->count;
gst_frame_env(stack)->values = NULL;
if (v1.type != DST_FUNCTION)
dst_error(vm, DST_EXPECTED_FUNCTION);
if (dst_frame_env(stack) == NULL && (fn->def->flags & DST_FUNCDEF_FLAG_NEEDSENV)) {
dst_frame_env(stack) = dst_alloc(vm, sizeof(DstFuncEnv));
dst_frame_env(stack)->thread = vm->thread;
dst_frame_env(stack)->stackOffset = vm->thread->count;
dst_frame_env(stack)->values = NULL;
}
if (pc[2] > v1.data.function->def->literalsLen)
gst_error(vm, GST_NO_UPVALUE);
if (fn->def->flags & GST_FUNCDEF_FLAG_NEEDSENV)
fn->env = gst_frame_env(stack);
dst_error(vm, DST_NO_UPVALUE);
if (fn->def->flags & DST_FUNCDEF_FLAG_NEEDSENV)
fn->env = dst_frame_env(stack);
else
fn->env = NULL;
temp.type = GST_FUNCTION;
temp.type = DST_FUNCTION;
temp.data.function = fn;
stack[pc[1]] = temp;
pc += 3;
}
break;
case GST_OP_RTN: /* Return nil */
temp.type = GST_NIL;
case DST_OP_RTN: /* Return nil */
temp.type = DST_NIL;
goto vm_return;
case GST_OP_RET: /* Return */
case DST_OP_RET: /* Return */
temp = stack[pc[1]];
goto vm_return;
case GST_OP_PSK: /* Push stack */
case DST_OP_PSK: /* Push stack */
{
uint16_t arity = pc[1];
uint16_t i;
uint16_t newBase = gst_frame_size(stack) + GST_FRAME_SIZE;
gst_frame_args(stack) = newBase;
gst_thread_ensure_extra(vm, vm->thread, GST_FRAME_SIZE + arity);
stack = gst_thread_stack(vm->thread);
gst_frame_size(stack) += GST_FRAME_SIZE + arity;
uint16_t newBase = dst_frame_size(stack) + DST_FRAME_SIZE;
dst_frame_args(stack) = newBase;
dst_thread_ensure_extra(vm, vm->thread, DST_FRAME_SIZE + arity);
stack = dst_thread_stack(vm->thread);
dst_frame_size(stack) += DST_FRAME_SIZE + arity;
/* Nil stuff */
for (i = 0; i < GST_FRAME_SIZE; ++i)
stack[newBase + i - GST_FRAME_SIZE].type = GST_NIL;
for (i = 0; i < DST_FRAME_SIZE; ++i)
stack[newBase + i - DST_FRAME_SIZE].type = DST_NIL;
/* Write arguments */
for (i = 0; i < arity; ++i)
stack[newBase + i] = stack[pc[2 + i]];
@ -218,214 +218,215 @@ int gst_continue(Gst *vm) {
}
break;
case GST_OP_PAR: /* Push array or tuple */
case DST_OP_PAR: /* Push array or tuple */
{
uint32_t count, i, oldsize;
const GstValue *data;
const DstValue *data;
temp = stack[pc[1]];
if (temp.type == GST_TUPLE) {
count = gst_tuple_length(temp.data.tuple);
if (temp.type == DST_TUPLE) {
count = dst_tuple_length(temp.data.tuple);
data = temp.data.tuple;
} else if (temp.type == GST_ARRAY){
} else if (temp.type == DST_ARRAY){
count = temp.data.array->count;
data = temp.data.array->data;
} else {
gst_error(vm, "expected array or tuple");
dst_error(vm, "expected array or tuple");
}
oldsize = gst_frame_size(stack);
gst_thread_pushnil(vm, vm->thread, count);
stack = gst_thread_stack(vm->thread);
oldsize = dst_frame_size(stack);
dst_thread_pushnil(vm, vm->thread, count);
stack = dst_thread_stack(vm->thread);
for (i = 0; i < count; ++i)
stack[oldsize + i] = data[i];
/*gst_frame_size(stack) += count;*/
/*dst_frame_size(stack) += count;*/
pc += 2;
}
break;
case GST_OP_CAL: /* Call */
case DST_OP_CAL: /* Call */
{
uint16_t newStackIndex = gst_frame_args(stack);
uint16_t size = gst_frame_size(stack);
uint16_t newStackIndex = dst_frame_args(stack);
uint16_t size = dst_frame_size(stack);
temp = stack[pc[1]];
gst_frame_size(stack) = newStackIndex - GST_FRAME_SIZE;
gst_frame_ret(stack) = pc[2];
gst_frame_pc(stack) = pc + 3;
if (newStackIndex < GST_FRAME_SIZE)
gst_error(vm, "invalid call instruction");
dst_frame_size(stack) = newStackIndex - DST_FRAME_SIZE;
dst_frame_ret(stack) = pc[2];
dst_frame_pc(stack) = pc + 3;
if (newStackIndex < DST_FRAME_SIZE)
dst_error(vm, "invalid call instruction");
vm->thread->count += newStackIndex;
stack = gst_thread_stack(vm->thread);
gst_frame_size(stack) = size - newStackIndex;
gst_frame_prevsize(stack) = newStackIndex - GST_FRAME_SIZE;
gst_frame_callee(stack) = temp;
stack = dst_thread_stack(vm->thread);
dst_frame_size(stack) = size - newStackIndex;
dst_frame_prevsize(stack) = newStackIndex - DST_FRAME_SIZE;
dst_frame_callee(stack) = temp;
}
goto common_function_call;
case GST_OP_TCL: /* Tail call */
case DST_OP_TCL: /* Tail call */
{
uint16_t newStackIndex = gst_frame_args(stack);
uint16_t size = gst_frame_size(stack);
uint16_t newStackIndex = dst_frame_args(stack);
uint16_t size = dst_frame_size(stack);
uint16_t i;
temp = stack[pc[1]];
/* Check for closures */
if (gst_frame_env(stack)) {
GstFuncEnv *env = gst_frame_env(stack);
if (dst_frame_env(stack)) {
DstFuncEnv *env = dst_frame_env(stack);
env->thread = NULL;
env->stackOffset = size;
env->values = gst_alloc(vm, sizeof(GstValue) * size);
gst_memcpy(env->values, stack, sizeof(GstValue) * size);
env->values = dst_alloc(vm, sizeof(DstValue) * size);
dst_memcpy(env->values, stack, sizeof(DstValue) * size);
}
if (newStackIndex)
for (i = 0; i < size - newStackIndex; ++i)
stack[i] = stack[newStackIndex + i];
gst_frame_size(stack) = size - newStackIndex;
gst_frame_callee(stack) = temp;
dst_frame_size(stack) = size - newStackIndex;
dst_frame_callee(stack) = temp;
}
goto common_function_call;
/* Code common to all function calls */
common_function_call:
gst_frame_args(stack) = 0;
gst_frame_env(stack) = NULL;
gst_thread_endframe(vm, vm->thread);
dst_frame_args(stack) = 0;
dst_frame_env(stack) = NULL;
dst_thread_endframe(vm, vm->thread);
stack = vm->thread->data + vm->thread->count;
temp = gst_frame_callee(stack);
if (temp.type == GST_FUNCTION) {
temp = dst_frame_callee(stack);
if (temp.type == DST_FUNCTION) {
pc = temp.data.function->def->byteCode;
} else if (temp.type == GST_CFUNCTION) {
} else if (temp.type == DST_CFUNCTION) {
int status;
vm->ret.type = GST_NIL;
vm->ret.type = DST_NIL;
status = temp.data.cfunction(vm);
if (status == GST_RETURN_OK) {
if (status) {
goto vm_error;
} else {
temp = vm->ret;
goto vm_return;
} else {
goto vm_error;
}
} else {
gst_error(vm, GST_EXPECTED_FUNCTION);
dst_error(vm, DST_EXPECTED_FUNCTION);
}
break;
case GST_OP_ARR: /* Array literal */
case DST_OP_ARR: /* Array literal */
{
uint32_t i;
uint32_t arrayLen = pc[2];
GstArray *array = gst_array(vm, arrayLen);
DstArray *array = dst_make_array(vm, arrayLen);
array->count = arrayLen;
for (i = 0; i < arrayLen; ++i)
array->data[i] = stack[pc[3 + i]];
temp.type = GST_ARRAY;
temp.type = DST_ARRAY;
temp.data.array = array;
stack[pc[1]] = temp;
pc += 3 + arrayLen;
}
break;
case GST_OP_DIC: /* Table literal */
case DST_OP_DIC: /* Table literal */
{
uint32_t i = 3;
uint32_t kvs = pc[2];
GstTable *t = gst_table(vm, 2 * kvs);
DstTable *t = dst_make_table(vm, 2 * kvs);
kvs = kvs + 3;
while (i < kvs) {
v1 = stack[pc[i++]];
v2 = stack[pc[i++]];
gst_table_put(vm, t, v1, v2);
dst_table_put(vm, t, v1, v2);
}
temp.type = GST_TABLE;
temp.type = DST_TABLE;
temp.data.table = t;
stack[pc[1]] = temp;
pc += kvs;
}
break;
case GST_OP_TUP: /* Tuple literal */
case DST_OP_TUP: /* Tuple literal */
{
uint32_t i;
uint32_t len = pc[2];
GstValue *tuple = gst_tuple_begin(vm, len);
DstValue *tuple = dst_tuple_begin(vm, len);
for (i = 0; i < len; ++i)
tuple[i] = stack[pc[3 + i]];
temp.type = GST_TUPLE;
temp.data.tuple = gst_tuple_end(vm, tuple);
temp.type = DST_TUPLE;
temp.data.tuple = dst_tuple_end(vm, tuple);
stack[pc[1]] = temp;
pc += 3 + len;
}
break;
case GST_OP_TRN: /* Transfer */
case DST_OP_TRN: /* Transfer */
temp = stack[pc[2]]; /* The thread */
v1 = stack[pc[3]]; /* The value to pass in */
if (temp.type != GST_THREAD && temp.type != GST_NIL)
gst_error(vm, "expected thread");
if (temp.type == GST_NIL && vm->thread->parent) {
temp = gst_wrap_thread(vm->thread->parent);
if (temp.type != DST_THREAD && temp.type != DST_NIL)
dst_error(vm, "expected thread");
if (temp.type == DST_NIL && vm->thread->parent) {
temp.type = DST_THREAD;
temp.data.thread = vm->thread->parent;
}
if (temp.type == GST_THREAD) {
if (temp.data.thread->status != GST_THREAD_PENDING)
gst_error(vm, "can only enter pending thread");
if (temp.type == DST_THREAD) {
if (temp.data.thread->status != DST_THREAD_PENDING)
dst_error(vm, "can only enter pending thread");
}
gst_frame_ret(stack) = pc[1];
vm->thread->status = GST_THREAD_PENDING;
gst_frame_pc(stack) = pc + 4;
if (temp.type == GST_NIL) {
dst_frame_ret(stack) = pc[1];
vm->thread->status = DST_THREAD_PENDING;
dst_frame_pc(stack) = pc + 4;
if (temp.type == DST_NIL) {
vm->ret = v1;
return GST_RETURN_OK;
return 0;
}
temp.data.thread->status = GST_THREAD_ALIVE;
temp.data.thread->status = DST_THREAD_ALIVE;
vm->thread = temp.data.thread;
stack = gst_thread_stack(temp.data.thread);
if (gst_frame_callee(stack).type != GST_FUNCTION)
stack = dst_thread_stack(temp.data.thread);
if (dst_frame_callee(stack).type != DST_FUNCTION)
goto vm_return;
stack[gst_frame_ret(stack)] = v1;
pc = gst_frame_pc(stack);
stack[dst_frame_ret(stack)] = v1;
pc = dst_frame_pc(stack);
continue;
/* Handle returning from stack frame. Expect return value in temp. */
vm_return:
stack = gst_thread_popframe(vm, vm->thread);
while (vm->thread->count < GST_FRAME_SIZE ||
vm->thread->status == GST_THREAD_DEAD ||
vm->thread->status == GST_THREAD_ERROR) {
vm->thread->status = GST_THREAD_DEAD;
stack = dst_thread_popframe(vm, vm->thread);
while (vm->thread->count < DST_FRAME_SIZE ||
vm->thread->status == DST_THREAD_DEAD ||
vm->thread->status == DST_THREAD_ERROR) {
vm->thread->status = DST_THREAD_DEAD;
if (vm->thread->parent) {
vm->thread = vm->thread->parent;
if (vm->thread->status == GST_THREAD_ALIVE) {
if (vm->thread->status == DST_THREAD_ALIVE) {
/* If the parent thread is still alive,
we are inside a cfunction */
vm->ret = temp;
return GST_RETURN_OK;
return 0;
}
stack = vm->thread->data + vm->thread->count;
} else {
vm->ret = temp;
return GST_RETURN_OK;
return 0;
}
}
vm->thread->status = GST_THREAD_ALIVE;
pc = gst_frame_pc(stack);
stack[gst_frame_ret(stack)] = temp;
vm->thread->status = DST_THREAD_ALIVE;
pc = dst_frame_pc(stack);
stack[dst_frame_ret(stack)] = temp;
continue;
/* Handle errors from c functions and vm opcodes */
vm_error:
vm->thread->status = GST_THREAD_ERROR;
while (vm->thread->count < GST_FRAME_SIZE ||
vm->thread->status == GST_THREAD_DEAD ||
vm->thread->status == GST_THREAD_ERROR) {
if (vm->thread->errorParent == NULL)
return GST_RETURN_ERROR;
vm->thread = vm->thread->errorParent;
if (vm->thread->status == GST_THREAD_ALIVE) {
vm->thread->status = DST_THREAD_ERROR;
while (vm->thread->count < DST_FRAME_SIZE ||
vm->thread->status == DST_THREAD_DEAD ||
vm->thread->status == DST_THREAD_ERROR) {
if (vm->thread->parent == NULL)
return 1;
vm->thread = vm->thread->parent;
if (vm->thread->status == DST_THREAD_ALIVE) {
/* If the parent thread is still alive,
we are inside a cfunction */
return GST_RETURN_ERROR;
return 1;
}
}
vm->thread->status = GST_THREAD_ALIVE;
vm->thread->status = DST_THREAD_ALIVE;
stack = vm->thread->data + vm->thread->count;
stack[gst_frame_ret(stack)] = vm->ret;
pc = gst_frame_pc(stack);
stack[dst_frame_ret(stack)] = vm->ret;
pc = dst_frame_pc(stack);
continue;
} /* end switch */
@ -433,7 +434,7 @@ int gst_continue(Gst *vm) {
/* Check for collection every cycle. If the instruction definitely does
* not allocate memory, it can use continue instead of break to
* skip this check */
gst_maybe_collect(vm);
dst_maybe_collect(vm);
} /* end for */
@ -441,66 +442,41 @@ int gst_continue(Gst *vm) {
/* Run the vm with a given function. This function is
* called to start the vm. */
int gst_run(Gst *vm, GstValue callee) {
int dst_run(Dst *vm, DstValue callee) {
int result;
if (vm->thread &&
(vm->thread->status == GST_THREAD_DEAD ||
vm->thread->status == GST_THREAD_ALIVE)) {
(vm->thread->status == DST_THREAD_DEAD ||
vm->thread->status == DST_THREAD_ALIVE)) {
/* Reuse old thread */
gst_thread_reset(vm, vm->thread, callee);
dst_thread_reset(vm, vm->thread, callee);
} else {
/* Create new thread */
vm->thread = gst_thread(vm, callee, 64);
vm->thread = dst_thread(vm, callee, 64);
}
if (callee.type == GST_CFUNCTION) {
vm->ret.type = GST_NIL;
if (callee.type == DST_CFUNCTION) {
vm->ret.type = DST_NIL;
result = callee.data.cfunction(vm);
} else if (callee.type == GST_FUNCTION) {
result = gst_continue(vm);
} else if (callee.type == DST_FUNCTION) {
result = dst_continue(vm);
} else {
vm->ret = gst_string_cv(vm, "expected function");
return GST_RETURN_ERROR;
vm->ret = dst_string_cv(vm, "expected function");
return 1;
}
/* Handle yields */
while (result == GST_RETURN_OK && vm->thread->status == GST_THREAD_PENDING) {
while (!result && vm->thread->status == DST_THREAD_PENDING) {
/* Send back in the value yielded - TODO - do something useful with this */
GstValue *stack = gst_thread_stack(vm->thread);
stack[gst_frame_ret(stack)] = vm->ret;
DstValue *stack = dst_thread_stack(vm->thread);
stack[dst_frame_ret(stack)] = vm->ret;
/* Resume */
result = gst_continue(vm);
result = dst_continue(vm);
}
return result;
}
/* Get an argument from the stack */
GstValue gst_arg(Gst *vm, uint32_t index) {
GstValue *stack = gst_thread_stack(vm->thread);
uint32_t frameSize = gst_frame_size(stack);
if (frameSize <= index) {
GstValue ret;
ret.type = GST_NIL;
return ret;
}
return stack[index];
}
/* Put a value on the stack */
void gst_set_arg(Gst* vm, uint32_t index, GstValue x) {
GstValue *stack = gst_thread_stack(vm->thread);
uint32_t frameSize = gst_frame_size(stack);
if (frameSize <= index) return;
stack[index] = x;
}
/* Get the size of the VMStack */
uint32_t gst_count_args(Gst *vm) {
GstValue *stack = gst_thread_stack(vm->thread);
return gst_frame_size(stack);
}
/* Initialize the VM */
void gst_init(Gst *vm) {
vm->ret.type = GST_NIL;
/* Setup functions */
Dst *dst_init() {
Dst *vm = dst_raw_alloc(sizeof(Dst));
vm->ret.type = DST_NIL;
/* Garbage collection */
vm->blocks = NULL;
vm->nextCollection = 0;
@ -510,30 +486,34 @@ void gst_init(Gst *vm) {
* there are no memory bugs during dev */
vm->memoryInterval = 0;
vm->black = 0;
/* Add thread */
vm->thread = NULL;
/* Set up the cache */
vm->cache = gst_raw_calloc(1, 128 * sizeof(GstValue));
vm->cache = dst_raw_calloc(1, 128 * sizeof(DstValue));
vm->cache_capacity = vm->cache == NULL ? 0 : 128;
vm->cache_count = 0;
vm->cache_deleted = 0;
/* Set up global env */
vm->modules = gst_table(vm, 10);
vm->registry = gst_table(vm, 10);
vm->env = gst_table(vm, 10);
vm->modules = dst_make_table(vm, 10);
vm->registry = dst_make_table(vm, 10);
vm->env = dst_make_table(vm, 10);
/* Set thread */
vm->thread = dst_thread(vm, vm->ret, 100);
dst_thread_pushnil(vm, vm->thread, 10);
return vm;
}
/* Clear all memory associated with the VM */
void gst_deinit(Gst *vm) {
gst_clear_memory(vm);
void dst_deinit(Dst *vm) {
dst_clear_memory(vm);
vm->thread = NULL;
vm->modules = NULL;
vm->registry = NULL;
vm->ret.type = GST_NIL;
vm->ret.type = DST_NIL;
/* Deinit the cache */
gst_raw_free(vm->cache);
dst_raw_free(vm->cache);
vm->cache = NULL;
vm->cache_count = 0;
vm->cache_capacity = 0;
vm->cache_deleted = 0;
/* Free the vm */
dst_raw_free(vm);
}

84
core/wrap.c Normal file
View File

@ -0,0 +1,84 @@
/*
* Copyright (c) 2017 Calvin Rose
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include <dst/dst.h>
#include "internal.h"
/* Wrapper functions wrap a data type that is used from C into a
* gst value, which can then be used in gst. */
DstValue dst_wrap_nil() {
GstValue y;
y.type = GST_NIL;
return y;
}
int dst_check_nil(Gst *vm, uint32_t i) {
DstValue a = dst_arg(vm, i);
return a.type == DST_NIL;
}
#define DST_WRAP_DEFINE(NAME, TYPE, DTYPE, UM)\
DstValue dst_wrap_##NAME(TYPE x) {\
DstValue y;\
y.type = DTYPE;\
y.data.UM = x;\
return y;\
}\
\
int dst_check_##NAME(Dst *vm, uint32_t i) {\
return dst_arg(vm, i).type == DTYPE;\
}\
DST_WRAP_DEFINE(real, DstReal, DST_REAL, real)
DST_WRAP_DEFINE(integer, DstInteger, DST_INTEGER, integer)
DST_WRAP_DEFINE(boolean, int, DST_BOOLEAN, boolean)
DST_WRAP_DEFINE(string, const uint8_t *, DST_STRING, string)
DST_WRAP_DEFINE(symbol, const uint8_t *, DST_SYMBOL, string)
DST_WRAP_DEFINE(array, DstArray *, DST_ARRAY, array)
DST_WRAP_DEFINE(tuple, const DstValue *, DST_TUPLE, tuple)
DST_WRAP_DEFINE(struct, const DstValue *, DST_STRUCT, st)
DST_WRAP_DEFINE(thread, DstThread *, DST_THREAD, thread)
DST_WRAP_DEFINE(buffer, DstBuffer *, DST_BYTEBUFFER, buffer)
DST_WRAP_DEFINE(function, DstFunction *, DST_FUNCTION, function)
DST_WRAP_DEFINE(cfunction, DstCFunction, DST_CFUNCTION, cfunction)
DST_WRAP_DEFINE(table, DstTable *, DST_TABLE, table)
DST_WRAP_DEFINE(funcenv, DstFuncEnv *, DST_FUNCENV, env)
DST_WRAP_DEFINE(funcdef, DstFuncDef *, DST_FUNCDEF, def)
#undef DST_WRAP_DEFINE
DstValue dst_wrap_userdata(void *x) {
DstValue ret;
ret.type = DST_USERDATA;
ret.data.pointer = x;
return ret;
}
void *dst_check_userdata(Dst *vm, uint32_t i, const DstUserType *type) {
DstValue x = dst_arg(vm, i);
DstUserdataHeader *h;
if (x.type != DST_USERDATA) return NULL;
h = dst_udata_header(x.data.pointer);
if (h->type != type) return NULL;
return x.data.pointer;
}

View File

@ -1,653 +0,0 @@
/*
* Copyright (c) 2017 Calvin Rose
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#ifndef GST_H_defined
#define GST_H_defined
#include <stdint.h>
#include <stdarg.h>
#include <setjmp.h>
/* String utils */
#define gst_string_raw(s) ((uint32_t *)(s) - 2)
#define gst_string_length(s) (gst_string_raw(s)[0])
#define gst_string_hash(s) (gst_string_raw(s)[1])
/* Tuple utils */
#define gst_tuple_raw(t) ((uint32_t *)(t) - 2)
#define gst_tuple_length(t) (gst_tuple_raw(t)[0])
#define gst_tuple_hash(t) (gst_tuple_raw(t)[1])
/* Struct utils */
#define gst_struct_raw(t) ((uint32_t *)(t) - 2)
#define gst_struct_length(t) (gst_struct_raw(t)[0])
#define gst_struct_capacity(t) (gst_struct_length(t) * 4)
#define gst_struct_hash(t) (gst_struct_raw(t)[1])
/* Userdata utils */
#define gst_udata_header(u) ((GstUserdataHeader *)(u) - 1)
#define gst_udata_type(u) (gst_udata_header(u)->type)
#define gst_udata_size(u) (gst_udata_header(u)->size)
/* Memcpy for moving memory */
#ifndef gst_memcpy
#include <string.h>
#define gst_memcpy memcpy
#endif
/* Allocation */
#ifndef gst_raw_alloc
#include <stdlib.h>
#define gst_raw_alloc malloc
#endif
/* Zero allocation */
#ifndef gst_raw_calloc
#include <stdlib.h>
#define gst_raw_calloc calloc
#endif
/* Realloc */
#ifndef gst_raw_realloc
#include <stdlib.h>
#define gst_raw_realloc realloc
#endif
/* Free */
#ifndef gst_raw_free
#include <stdlib.h>
#define gst_raw_free free
#endif
/* Null */
#ifndef NULL
#define NULL ((void *)0)
#endif
/* Stack frame manipulation */
/* Size of stack frame in number of values */
#define GST_FRAME_SIZE 5
/* Prevent some recursive functions from recursing too deeply
* ands crashing. */
#define GST_RECURSION_GUARD 2056
/* Macros for referencing a stack frame given a stack */
#define gst_frame_callee(s) (*(s - 1))
#define gst_frame_size(s) ((s - 2)->data.dwords[0])
#define gst_frame_prevsize(s) ((s - 2)->data.dwords[1])
#define gst_frame_args(s) ((s - 3)->data.dwords[0])
#define gst_frame_ret(s) ((s - 3)->data.dwords[1])
#define gst_frame_pc(s) ((s - 4)->data.u16p)
#define gst_frame_env(s) ((s - 5)->data.env)
/* C function helpers */
/* Return in a c function */
#define gst_c_return(vm, x) do { (vm)->ret = (x); return GST_RETURN_OK; } while (0)
/* Throw error from a c function */
#define gst_c_throw(vm, e) do { (vm)->ret = (e); return GST_RETURN_ERROR; } while (0)
/* Throw c string error from a c function */
#define gst_c_throwc(vm, e) gst_c_throw((vm), gst_string_cv((vm), (e)))
/* Assert from a c function */
#define gst_c_assert(vm, cond, e) do {if (cond) gst_c_throw((vm), (e)); } while (0)
/* What to do when out of memory */
#ifndef GST_OUT_OF_MEMORY
#include <stdlib.h>
#include <stdio.h>
#define GST_OUT_OF_MEMORY do { printf("out of memory\n"); exit(1); } while (0)
#endif
/* Various types */
typedef enum GstType {
GST_NIL = 0,
GST_REAL,
GST_INTEGER,
GST_BOOLEAN,
GST_STRING,
GST_SYMBOL,
GST_ARRAY,
GST_TUPLE,
GST_TABLE,
GST_STRUCT,
GST_THREAD,
GST_BYTEBUFFER,
GST_FUNCTION,
GST_CFUNCTION,
GST_USERDATA,
GST_FUNCENV,
GST_FUNCDEF
} GstType;
/* The state of the virtual machine */
typedef struct Gst Gst;
/* A general gst value type */
typedef struct GstValue GstValue;
/* All of the gst types */
typedef double GstReal;
typedef int64_t GstInteger;
typedef int GstBoolean;
typedef struct GstFunction GstFunction;
typedef struct GstArray GstArray;
typedef struct GstBuffer GstBuffer;
typedef struct GstTable GstTable;
typedef struct GstThread GstThread;
typedef int (*GstCFunction)(Gst * vm);
/* Other structs */
typedef struct GstUserdataHeader GstUserdataHeader;
typedef struct GstFuncDef GstFuncDef;
typedef struct GstFuncEnv GstFuncEnv;
typedef union GstValueUnion GstValueUnion;
typedef struct GstModuleItem GstModuleItem;
typedef struct GstUserType GstUserType;
typedef struct GstParser GstParser;
typedef struct GstParseState GstParseState;
/* C Api data types */
struct GstModuleItem {
const char *name;
GstCFunction data;
};
/* Union datatype */
union GstValueUnion {
GstBoolean boolean;
GstReal real;
GstInteger integer;
GstArray *array;
GstBuffer *buffer;
GstTable *table;
GstThread *thread;
const GstValue *tuple;
GstCFunction cfunction;
GstFunction *function;
GstFuncEnv *env;
GstFuncDef *def;
const GstValue *st;
const uint8_t *string;
/* Indirectly used union members */
uint16_t *u16p;
uint32_t dwords[2];
uint16_t words[4];
uint8_t bytes[8];
void *pointer;
const char *cstring;
};
/* The general gst value type. Contains a large union and
* the type information of the value */
struct GstValue {
GstType type;
GstValueUnion data;
};
/* A lightweight green thread in gst. Does not correspond to
* operating system threads. */
struct GstThread {
uint32_t count;
uint32_t capacity;
GstValue *data;
GstThread *parent;
GstThread *errorParent;
enum {
GST_THREAD_PENDING = 0,
GST_THREAD_ALIVE,
GST_THREAD_DEAD,
GST_THREAD_ERROR
} status;
};
/* A dynamic array type. */
struct GstArray {
uint32_t count;
uint32_t capacity;
GstValue *data;
};
/* A bytebuffer type. Used as a mutable string or string builder. */
struct GstBuffer {
uint32_t count;
uint32_t capacity;
uint8_t *data;
};
/* A mutable associative data type. Backed by a hashtable. */
struct GstTable {
uint32_t count;
uint32_t capacity;
uint32_t deleted;
GstValue *data;
};
/* Some function defintion flags */
#define GST_FUNCDEF_FLAG_VARARG 1
#define GST_FUNCDEF_FLAG_NEEDSPARENT 2
#define GST_FUNCDEF_FLAG_NEEDSENV 4
/* A function definition. Contains information need to instantiate closures. */
struct GstFuncDef {
uint32_t locals;
uint32_t arity; /* Not including varargs */
uint32_t literalsLen;
uint32_t byteCodeLen;
uint32_t flags;
GstValue *literals; /* Contains strings, FuncDefs, etc. */
uint16_t *byteCode;
};
/* A fuction environment */
struct GstFuncEnv {
GstThread *thread; /* When nil, index the local values */
uint32_t stackOffset; /* Used as environment size when off stack */
GstValue *values;
};
/* A function */
struct GstFunction {
GstFuncDef *def;
GstFuncEnv *env;
GstFunction *parent;
};
/* Defines a type for userdata */
struct GstUserType {
const char *name;
GstValue (*serialize)(Gst *vm, void *data, uint32_t len);
GstValue (*deserialize)(Gst *vm, GstValue in);
void (*finalize)(Gst *vm, void *data, uint32_t len);
void (*gcmark)(Gst *vm, void *data, uint32_t len);
};
/* Contains information about userdata */
struct GstUserdataHeader {
uint32_t size;
const GstUserType *type;
};
/* VM return status from c function */
#define GST_RETURN_OK 0
#define GST_RETURN_ERROR 1
/* The VM state */
struct Gst {
/* Garbage collection */
void *blocks;
uint32_t memoryInterval;
uint32_t nextCollection;
uint32_t black : 1;
/* Immutable value cache */
GstValue *cache;
uint32_t cache_capacity;
uint32_t cache_count;
uint32_t cache_deleted;
/* GC roots */
GstThread *thread;
GstTable *modules;
GstTable *registry;
GstTable *env;
/* Return state */
GstValue ret;
};
/* The type of a ParseState */
typedef enum ParseType {
PTYPE_FORM,
PTYPE_STRING,
PTYPE_TOKEN
} ParseType;
/* Contain a parse state that goes on the parse stack */
struct GstParseState {
ParseType type;
uint32_t quoteCount;
union {
struct {
uint8_t endDelimiter;
GstArray *array;
} form;
struct {
GstBuffer *buffer;
uint32_t count;
uint32_t accum;
enum {
STRING_STATE_BASE,
STRING_STATE_ESCAPE,
STRING_STATE_ESCAPE_UNICODE,
STRING_STATE_ESCAPE_HEX
} state;
} string;
} buf;
};
/* Holds the parsing state */
struct GstParser {
Gst *vm;
const char *error;
GstParseState *data;
GstValue value;
uint32_t count;
uint32_t cap;
uint32_t index;
uint32_t line;
uint32_t quoteCount;
enum {
GST_PARSER_PENDING = 0,
GST_PARSER_FULL,
GST_PARSER_ERROR,
GST_PARSER_ROOT
} status;
enum {
GST_PCOMMENT_NOT,
GST_PCOMMENT_EXPECTING,
GST_PCOMMENT_INSIDE
} comment;
};
/* Bytecode */
enum GstOpCode {
GST_OP_FLS, /* Load false */
GST_OP_TRU, /* Load true */
GST_OP_NIL, /* Load nil */
GST_OP_UPV, /* Load upvalue */
GST_OP_JIF, /* Jump if */
GST_OP_JMP, /* Jump */
GST_OP_SUV, /* Set upvalue */
GST_OP_CST, /* Load constant */
GST_OP_I16, /* Load 16 bit signed integer */
GST_OP_I32, /* Load 32 bit signed integer */
GST_OP_I64, /* Load 64 bit signed integer */
GST_OP_F64, /* Load 64 bit IEEE double */
GST_OP_MOV, /* Move value */
GST_OP_CLN, /* Create a closure */
GST_OP_ARR, /* Create array */
GST_OP_DIC, /* Create object */
GST_OP_TUP, /* Create tuple */
GST_OP_RET, /* Return from function */
GST_OP_RTN, /* Return nil */
GST_OP_PSK, /* Push stack */
GST_OP_PAR, /* Push array or tuple */
GST_OP_CAL, /* Call function */
GST_OP_TCL, /* Tail call */
GST_OP_TRN /* Transfer to new thread */
};
/****/
/* Buffer functions */
/****/
GstBuffer *gst_buffer(Gst *vm, uint32_t capacity);
void gst_buffer_ensure(Gst *vm, GstBuffer *buffer, uint32_t capacity);
int gst_buffer_get(GstBuffer *buffer, uint32_t index);
void gst_buffer_push(Gst *vm, GstBuffer *buffer, uint8_t c);
void gst_buffer_append(Gst *vm, GstBuffer *buffer, const uint8_t *string, uint32_t length);
void gst_buffer_append_cstring(Gst *vm, GstBuffer *buffer, const char *cstring);
const uint8_t *gst_buffer_to_string(Gst *vm, GstBuffer *buffer);
/* Define a push function for pushing a certain type to the buffer */
#define BUFFER_DEFINE(name, type) \
static void gst_buffer_push_##name(Gst *vm, GstBuffer *buffer, type x) { \
union { type t; uint8_t bytes[sizeof(type)]; } u; \
u.t = x; gst_buffer_append(vm, buffer, u.bytes, sizeof(type)); \
}
/****/
/* Array functions */
/****/
GstArray *gst_array(Gst *vm, uint32_t capacity);
GstValue gst_array_get(GstArray *array, uint32_t index);
int gst_array_set(GstArray *array, uint32_t index, GstValue x);
void gst_array_ensure(Gst *vm, GstArray *array, uint32_t capacity);
void gst_array_push(Gst *vm, GstArray *array, GstValue x);
GstValue gst_array_pop(GstArray *array);
GstValue gst_array_peek(GstArray *array);
/****/
/* Userdata functions */
/****/
void *gst_userdata(Gst *vm, uint32_t size, const GstUserType *utype);
/****/
/* Tuple functions */
/****/
GstValue *gst_tuple_begin(Gst *vm, uint32_t length);
const GstValue *gst_tuple_end(Gst *vm, GstValue *tuple);
/****/
/* String/Symbol functions */
/****/
uint8_t *gst_string_begin(Gst *vm, uint32_t len);
const uint8_t *gst_string_end(Gst *vm, uint8_t *str);
const uint8_t *gst_string_b(Gst *vm, const uint8_t *buf, uint32_t len);
const uint8_t *gst_string_c(Gst *vm, const char *cstring);
GstValue gst_string_cv(Gst *vm, const char *string);
GstValue gst_string_cvs(Gst *vm, const char *string);
int gst_string_compare(const uint8_t *lhs, const uint8_t *rhs);
const uint8_t *gst_string_bu(Gst *vm, const uint8_t *buf, uint32_t len);
const uint8_t *gst_string_cu(Gst *vm, const char *s);
/****/
/* Struct functions */
/****/
GstValue *gst_struct_begin(Gst *vm, uint32_t count);
void gst_struct_put(GstValue *st, GstValue key, GstValue value);
const GstValue *gst_struct_end(Gst *vm, GstValue *st);
GstValue gst_struct_get(const GstValue *st, GstValue key);
GstValue gst_struct_next(const GstValue *st, GstValue key);
/****/
/* Table functions */
/****/
GstTable *gst_table(Gst *vm, uint32_t capacity);
GstValue gst_table_get(GstTable *t, GstValue key);
GstValue gst_table_remove(GstTable *t, GstValue key);
void gst_table_put(Gst *vm, GstTable *t, GstValue key, GstValue value);
void gst_table_clear(GstTable *t);
GstValue gst_table_next(GstTable *o, GstValue key);
/****/
/* Threads */
/****/
#define gst_thread_stack(t) ((t)->data + (t)->count)
GstThread *gst_thread(Gst *vm, GstValue callee, uint32_t capacity);
GstThread *gst_thread_reset(Gst *vm, GstThread *thread, GstValue callee);
void gst_thread_ensure_extra(Gst *vm, GstThread *thread, uint32_t extra);
void gst_thread_push(Gst *vm, GstThread *thread, GstValue x);
void gst_thread_pushnil(Gst *vm, GstThread *thread, uint32_t n);
void gst_thread_tuplepack(Gst *vm, GstThread *thread, uint32_t n);
GstValue *gst_thread_beginframe(Gst *vm, GstThread *thread, GstValue callee, uint32_t arity);
void gst_thread_endframe(Gst *vm, GstThread *thread);
GstValue *gst_thread_popframe(Gst *vm, GstThread *thread);
uint32_t gst_thread_countframes(GstThread *thread);
/****/
/* Value manipulation */
/****/
int gst_truthy(GstValue x);
int gst_compare(GstValue x, GstValue y);
int gst_equals(GstValue x, GstValue y);
const char *gst_get(GstValue ds, GstValue key, GstValue *out);
const char *gst_set(Gst *vm, GstValue ds, GstValue key, GstValue value);
const uint8_t *gst_to_string(Gst *vm, GstValue x);
const uint8_t *gst_description(Gst *vm, GstValue x);
const uint8_t *gst_short_description(Gst *vm, GstValue x);
uint32_t gst_hash(GstValue x);
GstInteger gst_length(Gst *vm, GstValue x);
/****/
/* Serialization */
/****/
const char *gst_deserialize(
Gst *vm,
const uint8_t *data,
uint32_t len,
GstValue *out,
const uint8_t **nextData);
const char *gst_serialize(Gst *vm, GstBuffer *buffer, GstValue x);
/***/
/* Parsing */
/***/
void gst_parser(GstParser *p, Gst *vm);
int gst_parse_cstring(GstParser *p, const char *string);
int gst_parse_string(GstParser *p, const uint8_t *string);
void gst_parse_byte(GstParser *p, uint8_t byte);
int gst_parse_hasvalue(GstParser *p);
GstValue gst_parse_consume(GstParser *p);
/***/
/* Compilation */
/***/
GstValue gst_compile(Gst *vm, GstTable *env, GstValue form);
/****/
/* GC */
/****/
#define GST_MEMTAG_STRING 4
#define GST_MEMTAG_TUPLE 8
#define GST_MEMTAG_STRUCT 16
#define GST_MEMTAG_USER 32
void gst_mark_value(Gst *vm, GstValue x);
void gst_mark(Gst *vm, GstValueUnion x, GstType type);
void gst_sweep(Gst *vm);
void *gst_alloc(Gst *vm, uint32_t size);
void *gst_zalloc(Gst *vm, uint32_t size);
void gst_mem_tag(void *mem, uint32_t tags);
void gst_collect(Gst *vm);
void gst_maybe_collect(Gst *vm);
void gst_clear_memory(Gst *vm);
void gst_mark_mem(Gst *vm, void *mem);
/****/
/* VM */
/****/
void gst_init(Gst *vm);
void gst_deinit(Gst *vm);
int gst_run(Gst *vm, GstValue func);
int gst_continue(Gst *vm);
GstValue gst_arg(Gst *vm, uint32_t index);
void gst_set_arg(Gst *vm, uint32_t index, GstValue x);
uint32_t gst_count_args(Gst *vm);
/***/
/* Stl */
/***/
void gst_stl_load(Gst *vm);
/****/
/* C Api */
/****/
void gst_module(Gst *vm, const char *name, const GstModuleItem *mod);
void gst_module_mutable(Gst *vm, const char *name, const GstModuleItem *mod);
void gst_module_put(Gst *vm, const char *packagename, const char *name, GstValue x);
GstValue gst_module_get(Gst *vm, const char *packagename);
void gst_register_put(Gst *vm, const char *packagename, GstValue mod);
GstValue gst_register_get(Gst *vm, const char *name);
int gst_callc(Gst *vm, GstCFunction fn, int numargs, ...);
/* Wrap data in GstValue */
GstValue gst_wrap_nil();
GstValue gst_wrap_real(GstReal x);
GstValue gst_wrap_integer(GstInteger x);
GstValue gst_wrap_boolean(int x);
GstValue gst_wrap_string(const uint8_t *x);
GstValue gst_wrap_symbol(const uint8_t *x);
GstValue gst_wrap_array(GstArray *x);
GstValue gst_wrap_tuple(const GstValue *x);
GstValue gst_wrap_struct(const GstValue *x);
GstValue gst_wrap_thread(GstThread *x);
GstValue gst_wrap_buffer(GstBuffer *x);
GstValue gst_wrap_function(GstFunction *x);
GstValue gst_wrap_cfunction(GstCFunction x);
GstValue gst_wrap_table(GstTable *x);
GstValue gst_wrap_userdata(void *x);
GstValue gst_wrap_funcenv(GstFuncEnv *x);
GstValue gst_wrap_funcdef(GstFuncDef *x);
/* Check data from arguments */
int gst_check_nil(Gst *vm, uint32_t i);
int gst_check_real(Gst *vm, uint32_t i, GstReal (*x));
int gst_check_integer(Gst *vm, uint32_t i, GstInteger (*x));
int gst_check_boolean(Gst *vm, uint32_t i, int (*x));
int gst_check_string(Gst *vm, uint32_t i, const uint8_t *(*x));
int gst_check_symbol(Gst *vm, uint32_t i, const uint8_t *(*x));
int gst_check_array(Gst *vm, uint32_t i, GstArray *(*x));
int gst_check_tuple(Gst *vm, uint32_t i, const GstValue *(*x));
int gst_check_struct(Gst *vm, uint32_t i, const GstValue *(*x));
int gst_check_thread(Gst *vm, uint32_t i, GstThread *(*x));
int gst_check_buffer(Gst *vm, uint32_t i, GstBuffer *(*x));
int gst_check_function(Gst *vm, uint32_t i, GstFunction *(*x));
int gst_check_cfunction(Gst *vm, uint32_t i, GstCFunction (*x));
int gst_check_table(Gst *vm, uint32_t i, GstTable *(*x));
int gst_check_funcenv(Gst *vm, uint32_t i, GstFuncEnv *(*x));
int gst_check_funcdef(Gst *vm, uint32_t i, GstFuncDef *(*x));
void *gst_check_userdata(Gst *vm, uint32_t i, const GstUserType *type);
/* Treat similar types through uniform interfaces */
int gst_seq_view(GstValue seq, const GstValue **data, uint32_t *len);
int gst_chararray_view(GstValue str, const uint8_t **data, uint32_t *len);
int gst_hashtable_view(GstValue tab, const GstValue **data, uint32_t *cap);
/****/
/* Misc */
/****/
int gst_read_real(const uint8_t *string, const uint8_t *end, double *ret, int forceInt);
int gst_read_integer(const uint8_t *string, const uint8_t *end, int64_t *ret);
GstReal gst_integer_to_real(GstInteger x);
GstInteger gst_real_to_integer(GstReal x);
GstInteger gst_startrange(GstInteger raw, uint32_t len);
GstInteger gst_endrange(GstInteger raw, uint32_t len);
void gst_env_merge(Gst *vm, GstTable *destEnv, GstTable *srcEnv);
GstTable *gst_env_nils(Gst *vm, GstTable *env);
GstTable *gst_env_meta(Gst *vm, GstTable *env);
void gst_env_put(Gst *vm, GstTable *env, GstValue key, GstValue value);
void gst_env_putc(Gst *vm, GstTable *env, const char *key, GstValue value);
void gst_env_putvar(Gst *vm, GstTable *env, GstValue key, GstValue value);
void gst_env_putvarc(Gst *vm, GstTable *env, const char *key, GstValue value);
const uint8_t *gst_description(Gst *vm, GstValue x);
#endif // GST_H_defined

View File

@ -29,7 +29,7 @@
#include <stdint.h>
#define BUFSIZE 1024
#define PERLINE 16
#define PERLINE 10
int main(int argc, const char **argv) {
@ -60,7 +60,7 @@ int main(int argc, const char **argv) {
/* Write the header */
fprintf(out, "/* Auto generated - DO NOT EDIT */\n\n");
fprintf(out, "#include <stdint.h>\n\n");
fprintf(out, "const uint8_t gst_gen_%s[] = {", argv[3]);
fprintf(out, "const uint8_t dst_gen_%s[] = {", argv[3]);
/* Read in chunks from buffer */
while ((bytesRead = fread(buf, 1, sizeof(buf), in)) > 0) {