mirror of
https://github.com/janet-lang/janet
synced 2024-11-28 02:59:54 +00:00
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:
parent
3ccd688438
commit
3efd400025
4
.gitignore
vendored
4
.gitignore
vendored
@ -1,6 +1,6 @@
|
||||
# Target
|
||||
/client/gst
|
||||
gst
|
||||
/client/dst
|
||||
dst
|
||||
|
||||
# Generated files
|
||||
*.gen.h
|
||||
|
86
Makefile
86
Makefile
@ -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
|
||||
|
14
README.md
14
README.md
@ -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
|
||||
|
146
client/main.c
146
client/main.c
@ -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;
|
||||
}
|
||||
|
@ -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
331
core/bootstrap_parse.c
Normal 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
82
core/client.c
Normal 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;
|
||||
}
|
680
core/compile.c
680
core/compile.c
File diff suppressed because it is too large
Load Diff
102
core/env.c
102
core/env.c
@ -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);
|
||||
}
|
211
core/gc.c
211
core/gc.c
@ -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));
|
||||
frame = dst_mark_stackframe(vm, frame);
|
||||
if (thread->parent) {
|
||||
x.thread = thread->parent;
|
||||
goto begin;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case GST_FUNCTION:
|
||||
case DST_FUNCTION:
|
||||
if (gc_header(x.function)->color != vm->black) {
|
||||
GstFunction *f = x.function;
|
||||
DstFunction *f = x.function;
|
||||
gc_header(f)->color = vm->black;
|
||||
gst_mark_funcdef(vm, f->def);
|
||||
dst_mark_funcdef(vm, f->def);
|
||||
if (f->env)
|
||||
gst_mark_funcenv(vm, f->env);
|
||||
dst_mark_funcenv(vm, f->env);
|
||||
if (f->parent) {
|
||||
GstValueUnion pval;
|
||||
DstValueUnion pval;
|
||||
pval.function = f->parent;
|
||||
gst_mark(vm, pval, GST_FUNCTION);
|
||||
dst_mark(vm, pval, DST_FUNCTION);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case GST_TABLE:
|
||||
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;
|
||||
|
583
core/ids.c
583
core/ids.c
@ -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
537
core/internal.h
Normal 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 */
|
@ -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));
|
||||
}
|
431
core/parse.c
431
core/parse.c
@ -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;
|
||||
}
|
421
core/serialize.c
421
core/serialize.c
@ -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
1211
core/stl.c
File diff suppressed because it is too large
Load Diff
209
core/string.c
209
core/string.c
@ -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);
|
||||
}
|
||||
}
|
155
core/thread.c
155
core/thread.c
@ -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;
|
||||
}
|
||||
|
276
core/util.c
276
core/util.c
@ -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;
|
||||
}
|
198
core/value.c
198
core/value.c
@ -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
428
core/vm.c
@ -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
84
core/wrap.c
Normal 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;
|
||||
}
|
@ -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
|
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user