mirror of
https://github.com/janet-lang/janet
synced 2024-12-26 08:20:27 +00:00
Remove longjump/setjump from vm loop. Add out of memory
behavior option.
This commit is contained in:
parent
68f834f03b
commit
69624495ec
2
Makefile
2
Makefile
@ -6,7 +6,7 @@ TARGET=interp
|
|||||||
PREFIX=/usr/local
|
PREFIX=/usr/local
|
||||||
|
|
||||||
# C sources
|
# C sources
|
||||||
HEADERS=vm.h ds.h compile.h parse.h value.h datatypes.h gc.h util.h
|
HEADERS=vm.h ds.h compile.h parse.h value.h datatypes.h gc.h util.h gst.h
|
||||||
SOURCES=main.c parse.c value.c vm.c ds.c compile.c gc.c
|
SOURCES=main.c parse.c value.c vm.c ds.c compile.c gc.c
|
||||||
OBJECTS=$(patsubst %.c,%.o,$(SOURCES))
|
OBJECTS=$(patsubst %.c,%.o,$(SOURCES))
|
||||||
|
|
||||||
|
20
compile.h
20
compile.h
@ -2,6 +2,20 @@
|
|||||||
#define COMPILE_H_9VXF71HY
|
#define COMPILE_H_9VXF71HY
|
||||||
|
|
||||||
#include "datatypes.h"
|
#include "datatypes.h"
|
||||||
|
#include <setjmp.h>
|
||||||
|
|
||||||
|
typedef struct GstCompiler GstCompiler;
|
||||||
|
typedef struct GstScope GstScope;
|
||||||
|
|
||||||
|
/* Compilation state */
|
||||||
|
struct GstCompiler {
|
||||||
|
Gst *vm;
|
||||||
|
const char *error;
|
||||||
|
jmp_buf onError;
|
||||||
|
GstScope *tail;
|
||||||
|
GstArray *env;
|
||||||
|
GstBuffer *buffer;
|
||||||
|
};
|
||||||
|
|
||||||
/* Initialize the Compiler */
|
/* Initialize the Compiler */
|
||||||
void gst_compiler(GstCompiler *c, Gst *vm);
|
void gst_compiler(GstCompiler *c, Gst *vm);
|
||||||
@ -15,10 +29,4 @@ void gst_compiler_add_global_cfunction(GstCompiler *c, const char *name, GstCFun
|
|||||||
/* Compile a function that evaluates the given form. */
|
/* Compile a function that evaluates the given form. */
|
||||||
GstFunction *gst_compiler_compile(GstCompiler *c, GstValue form);
|
GstFunction *gst_compiler_compile(GstCompiler *c, GstValue form);
|
||||||
|
|
||||||
/* Macro expansion. Macro expansion happens prior to the compilation process
|
|
||||||
* and is completely separate. This allows the compilation to not have to worry
|
|
||||||
* about garbage collection and other issues that would complicate both the
|
|
||||||
* runtime and the compilation. */
|
|
||||||
int gst_macro_expand(Gst *vm, GstValue x, GstObject *macros, GstValue *out);
|
|
||||||
|
|
||||||
#endif /* end of include guard: COMPILE_H_9VXF71HY */
|
#endif /* end of include guard: COMPILE_H_9VXF71HY */
|
||||||
|
52
datatypes.h
52
datatypes.h
@ -2,7 +2,6 @@
|
|||||||
#define DATATYPES_H_PJJ035NT
|
#define DATATYPES_H_PJJ035NT
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <setjmp.h>
|
|
||||||
|
|
||||||
/* Flag for immutability in an otherwise mutable datastructure */
|
/* Flag for immutability in an otherwise mutable datastructure */
|
||||||
#define GST_IMMUTABLE 1
|
#define GST_IMMUTABLE 1
|
||||||
@ -36,19 +35,15 @@ typedef struct GstArray GstArray;
|
|||||||
typedef struct GstBuffer GstBuffer;
|
typedef struct GstBuffer GstBuffer;
|
||||||
typedef struct GstObject GstObject;
|
typedef struct GstObject GstObject;
|
||||||
typedef struct GstThread GstThread;
|
typedef struct GstThread GstThread;
|
||||||
typedef GstValue (*GstCFunction)(Gst * vm);
|
typedef int (*GstCFunction)(Gst * vm);
|
||||||
|
|
||||||
/* Implementation details */
|
/* Implementation details */
|
||||||
typedef struct GstParser GstParser;
|
|
||||||
typedef struct GstCompiler GstCompiler;
|
|
||||||
typedef struct GstFuncDef GstFuncDef;
|
typedef struct GstFuncDef GstFuncDef;
|
||||||
typedef struct GstFuncEnv GstFuncEnv;
|
typedef struct GstFuncEnv GstFuncEnv;
|
||||||
|
|
||||||
/* Definitely implementation details */
|
/* Definitely implementation details */
|
||||||
typedef struct GstStackFrame GstStackFrame;
|
typedef struct GstStackFrame GstStackFrame;
|
||||||
typedef struct GstParseState GstParseState;
|
|
||||||
typedef struct GstBucket GstBucket;
|
typedef struct GstBucket GstBucket;
|
||||||
typedef struct GstScope GstScope;
|
|
||||||
|
|
||||||
/* The general gst value type. Contains a large union and
|
/* The general gst value type. Contains a large union and
|
||||||
* the type information of the value */
|
* the type information of the value */
|
||||||
@ -155,6 +150,11 @@ struct GstStackFrame {
|
|||||||
uint16_t *pc;
|
uint16_t *pc;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* VM return status from c function */
|
||||||
|
#define GST_RETURN_OK 0
|
||||||
|
#define GST_RETURN_ERROR 1
|
||||||
|
#define GST_RETURN_CRASH 2
|
||||||
|
|
||||||
/* The VM state */
|
/* The VM state */
|
||||||
struct Gst {
|
struct Gst {
|
||||||
/* Garbage collection */
|
/* Garbage collection */
|
||||||
@ -166,47 +166,9 @@ struct Gst {
|
|||||||
GstThread *thread;
|
GstThread *thread;
|
||||||
/* Return state */
|
/* Return state */
|
||||||
const char *crash;
|
const char *crash;
|
||||||
jmp_buf jump;
|
GstValue ret; /* Returned value from gst_start. Also holds errors. */
|
||||||
GstValue error;
|
|
||||||
GstValue ret; /* Returned value from VMStart. Also holds errors. */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct GstParser {
|
|
||||||
Gst *vm;
|
|
||||||
const char *error;
|
|
||||||
GstParseState *data;
|
|
||||||
GstValue value;
|
|
||||||
uint32_t count;
|
|
||||||
uint32_t cap;
|
|
||||||
uint32_t index;
|
|
||||||
uint32_t flags;
|
|
||||||
enum {
|
|
||||||
GST_PARSER_PENDING = 0,
|
|
||||||
GST_PARSER_FULL,
|
|
||||||
GST_PARSER_ERROR
|
|
||||||
} status;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Compilation state */
|
|
||||||
struct GstCompiler {
|
|
||||||
Gst *vm;
|
|
||||||
const char *error;
|
|
||||||
jmp_buf onError;
|
|
||||||
GstScope *tail;
|
|
||||||
GstArray *env;
|
|
||||||
GstBuffer *buffer;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* String utils */
|
|
||||||
#define gst_string_raw(s) ((uint32_t *)(s) - 2)
|
|
||||||
#define gst_string_length(v) (gst_string_raw(v)[0])
|
|
||||||
#define gst_string_hash(v) (gst_string_raw(v)[1])
|
|
||||||
|
|
||||||
/* Tuple utils */
|
|
||||||
#define gst_tuple_raw(s) ((uint32_t *)(s) - 2)
|
|
||||||
#define gst_tuple_length(v) (gst_tuple_raw(v)[0])
|
|
||||||
#define gst_tuple_hash(v) (gst_tuple_raw(v)[1])
|
|
||||||
|
|
||||||
/* Bytecode */
|
/* Bytecode */
|
||||||
enum GstOpCode {
|
enum GstOpCode {
|
||||||
GST_OP_ADD = 0, /* Addition */
|
GST_OP_ADD = 0, /* Addition */
|
||||||
|
3
gc.c
3
gc.c
@ -184,7 +184,7 @@ void gst_sweep(Gst *vm) {
|
|||||||
static void *gst_alloc_prepare(Gst *vm, char *rawBlock, uint32_t size) {
|
static void *gst_alloc_prepare(Gst *vm, char *rawBlock, uint32_t size) {
|
||||||
GCMemoryHeader *mdata;
|
GCMemoryHeader *mdata;
|
||||||
if (rawBlock == NULL) {
|
if (rawBlock == NULL) {
|
||||||
gst_crash(vm, "out of memory");
|
GST_OUT_OF_MEMORY;
|
||||||
}
|
}
|
||||||
vm->nextCollection += size;
|
vm->nextCollection += size;
|
||||||
mdata = (GCMemoryHeader *)rawBlock;
|
mdata = (GCMemoryHeader *)rawBlock;
|
||||||
@ -216,7 +216,6 @@ void gst_collect(Gst *vm) {
|
|||||||
gst_mark(vm, &thread);
|
gst_mark(vm, &thread);
|
||||||
}
|
}
|
||||||
gst_mark(vm, &vm->ret);
|
gst_mark(vm, &vm->ret);
|
||||||
gst_mark(vm, &vm->error);
|
|
||||||
gst_sweep(vm);
|
gst_sweep(vm);
|
||||||
vm->nextCollection = 0;
|
vm->nextCollection = 0;
|
||||||
}
|
}
|
||||||
|
12
gst.h
Normal file
12
gst.h
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#ifndef gst_h_INCLUDED
|
||||||
|
#define gst_h_INCLUDED
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
#include "datatypes.h"
|
||||||
|
#include "vm.h"
|
||||||
|
#include "parse.h"
|
||||||
|
#include "compile.h"
|
||||||
|
#include "value.h"
|
||||||
|
|
||||||
|
#endif // gst_h_INCLUDED
|
||||||
|
|
14
main.c
14
main.c
@ -1,10 +1,6 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include "datatypes.h"
|
#include "gst.h"
|
||||||
#include "vm.h"
|
|
||||||
#include "parse.h"
|
|
||||||
#include "compile.h"
|
|
||||||
#include "value.h"
|
|
||||||
|
|
||||||
/* Simple printer for gst strings */
|
/* Simple printer for gst strings */
|
||||||
void string_put(FILE *out, uint8_t * string) {
|
void string_put(FILE *out, uint8_t * string) {
|
||||||
@ -15,16 +11,14 @@ void string_put(FILE *out, uint8_t * string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Test c function */
|
/* Test c function */
|
||||||
GstValue print(Gst *vm) {
|
int print(Gst *vm) {
|
||||||
uint32_t j, count;
|
uint32_t j, count;
|
||||||
GstValue nil;
|
|
||||||
count = gst_count_args(vm);
|
count = gst_count_args(vm);
|
||||||
for (j = 0; j < count; ++j) {
|
for (j = 0; j < count; ++j) {
|
||||||
string_put(stdout, gst_to_string(vm, gst_arg(vm, j)));
|
string_put(stdout, gst_to_string(vm, gst_arg(vm, j)));
|
||||||
fputc('\n', stdout);
|
fputc('\n', stdout);
|
||||||
}
|
}
|
||||||
nil.type = GST_NIL;
|
return GST_RETURN_OK;
|
||||||
return nil;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* A simple repl for debugging */
|
/* A simple repl for debugging */
|
||||||
@ -98,7 +92,7 @@ void debug_repl(FILE *in, FILE *out) {
|
|||||||
fprintf(out, "VM crash: %s\n", vm.crash);
|
fprintf(out, "VM crash: %s\n", vm.crash);
|
||||||
} else {
|
} else {
|
||||||
fprintf(out, "VM error: ");
|
fprintf(out, "VM error: ");
|
||||||
string_put(out, gst_to_string(&vm, vm.error));
|
string_put(out, gst_to_string(&vm, vm.ret));
|
||||||
printf("\n");
|
printf("\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
20
parse.h
20
parse.h
@ -3,6 +3,26 @@
|
|||||||
|
|
||||||
#include "datatypes.h"
|
#include "datatypes.h"
|
||||||
|
|
||||||
|
typedef struct GstParser GstParser;
|
||||||
|
typedef struct GstParseState GstParseState;
|
||||||
|
|
||||||
|
/* 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 flags;
|
||||||
|
enum {
|
||||||
|
GST_PARSER_PENDING = 0,
|
||||||
|
GST_PARSER_FULL,
|
||||||
|
GST_PARSER_ERROR
|
||||||
|
} status;
|
||||||
|
};
|
||||||
|
|
||||||
/* Some parser flags */
|
/* Some parser flags */
|
||||||
#define GST_PARSER_FLAG_INCOMMENT 1
|
#define GST_PARSER_FLAG_INCOMMENT 1
|
||||||
#define GST_PARSER_FLAG_EXPECTING_COMMENT 2
|
#define GST_PARSER_FLAG_EXPECTING_COMMENT 2
|
||||||
|
25
util.h
25
util.h
@ -1,6 +1,16 @@
|
|||||||
#ifndef util_h_INCLUDED
|
#ifndef util_h_INCLUDED
|
||||||
#define util_h_INCLUDED
|
#define util_h_INCLUDED
|
||||||
|
|
||||||
|
/* String utils */
|
||||||
|
#define gst_string_raw(s) ((uint32_t *)(s) - 2)
|
||||||
|
#define gst_string_length(v) (gst_string_raw(v)[0])
|
||||||
|
#define gst_string_hash(v) (gst_string_raw(v)[1])
|
||||||
|
|
||||||
|
/* Tuple utils */
|
||||||
|
#define gst_tuple_raw(s) ((uint32_t *)(s) - 2)
|
||||||
|
#define gst_tuple_length(v) (gst_tuple_raw(v)[0])
|
||||||
|
#define gst_tuple_hash(v) (gst_tuple_raw(v)[1])
|
||||||
|
|
||||||
/* Memcpy for moving memory */
|
/* Memcpy for moving memory */
|
||||||
#ifndef gst_memcpy
|
#ifndef gst_memcpy
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@ -36,5 +46,20 @@
|
|||||||
#define NULL ((void *)0)
|
#define NULL ((void *)0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* 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))
|
||||||
|
|
||||||
|
/* 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
|
||||||
|
|
||||||
#endif // util_h_INCLUDED
|
#endif // util_h_INCLUDED
|
||||||
|
|
||||||
|
57
value.c
57
value.c
@ -388,75 +388,78 @@ static uint8_t to_byte(GstNumber raw) {
|
|||||||
return (uint8_t) raw;
|
return (uint8_t) raw;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get a value out af an associated data structure. Can throw VM error. */
|
/* Get a value out af an associated data structure.
|
||||||
GstValue gst_get(Gst *vm, GstValue ds, GstValue key) {
|
* 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) {
|
||||||
int32_t index;
|
int32_t index;
|
||||||
GstValue ret;
|
GstValue ret;
|
||||||
switch (ds.type) {
|
switch (ds.type) {
|
||||||
case GST_ARRAY:
|
case GST_ARRAY:
|
||||||
gst_assert_type(vm, key, GST_NUMBER);
|
if (key.type != GST_NUMBER) return "expected numeric key";
|
||||||
index = to_index(key.data.number, ds.data.array->count);
|
index = to_index(key.data.number, ds.data.array->count);
|
||||||
if (index == -1) gst_error(vm, "invalid array access");
|
if (index == -1) return "invalid array access";
|
||||||
ret = ds.data.array->data[index];
|
ret = ds.data.array->data[index];
|
||||||
break;
|
break;
|
||||||
case GST_TUPLE:
|
case GST_TUPLE:
|
||||||
gst_assert_type(vm, key, GST_NUMBER);
|
if (key.type != GST_NUMBER) return "expected numeric key";
|
||||||
index = to_index(key.data.number, gst_tuple_length(ds.data.tuple));
|
index = to_index(key.data.number, gst_tuple_length(ds.data.tuple));
|
||||||
if (index < 0) gst_error(vm, "invalid tuple access");
|
if (index < 0) return "invalid tuple access";
|
||||||
ret = ds.data.tuple[index];
|
ret = ds.data.tuple[index];
|
||||||
break;
|
break;
|
||||||
case GST_BYTEBUFFER:
|
case GST_BYTEBUFFER:
|
||||||
gst_assert_type(vm, key, GST_NUMBER);
|
if (key.type != GST_NUMBER) return "expected numeric key";
|
||||||
index = to_index(key.data.number, ds.data.buffer->count);
|
index = to_index(key.data.number, ds.data.buffer->count);
|
||||||
if (index == -1) gst_error(vm, "invalid buffer access");
|
if (index == -1) return "invalid buffer access";
|
||||||
ret.type = GST_NUMBER;
|
ret.type = GST_NUMBER;
|
||||||
ret.data.number = ds.data.buffer->data[index];
|
ret.data.number = ds.data.buffer->data[index];
|
||||||
break;
|
break;
|
||||||
case GST_STRING:
|
case GST_STRING:
|
||||||
gst_assert_type(vm, key, GST_NUMBER);
|
if (key.type != GST_NUMBER) return "expected numeric key";
|
||||||
index = to_index(key.data.number, gst_string_length(ds.data.string));
|
index = to_index(key.data.number, gst_string_length(ds.data.string));
|
||||||
if (index == -1) gst_error(vm, "invalid string access");
|
if (index == -1) return "invalid string access";
|
||||||
ret.type = GST_NUMBER;
|
ret.type = GST_NUMBER;
|
||||||
ret.data.number = ds.data.string[index];
|
ret.data.number = ds.data.string[index];
|
||||||
break;
|
break;
|
||||||
case GST_OBJECT:
|
case GST_OBJECT:
|
||||||
return gst_object_get(ds.data.object, key);
|
ret = gst_object_get(ds.data.object, key);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
gst_error(vm, "cannot get");
|
return "cannot get";
|
||||||
}
|
}
|
||||||
return ret;
|
*out = ret;
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set a value in an associative data structure. Can throw VM error. */
|
/* Set a value in an associative data structure. Returns possible
|
||||||
void gst_set(Gst *vm, GstValue ds, GstValue key, GstValue value) {
|
* error message, and NULL if no error. */
|
||||||
|
const char *gst_set(Gst *vm, GstValue ds, GstValue key, GstValue value) {
|
||||||
int32_t index;
|
int32_t index;
|
||||||
switch (ds.type) {
|
switch (ds.type) {
|
||||||
case GST_ARRAY:
|
case GST_ARRAY:
|
||||||
if (ds.data.array->flags & GST_IMMUTABLE)
|
if (ds.data.array->flags & GST_IMMUTABLE)
|
||||||
goto immutable;
|
return "cannot set immutable value";
|
||||||
gst_assert_type(vm, key, GST_NUMBER);
|
if (key.type != GST_NUMBER) return "expected numeric key";
|
||||||
index = to_index(key.data.number, ds.data.array->count);
|
index = to_index(key.data.number, ds.data.array->count);
|
||||||
if (index == -1) gst_error(vm, "invalid array access");
|
if (index == -1) return "invalid array access";
|
||||||
ds.data.array->data[index] = value;
|
ds.data.array->data[index] = value;
|
||||||
break;
|
break;
|
||||||
case GST_BYTEBUFFER:
|
case GST_BYTEBUFFER:
|
||||||
if (ds.data.buffer->flags & GST_IMMUTABLE)
|
if (ds.data.buffer->flags & GST_IMMUTABLE)
|
||||||
goto immutable;
|
return "cannot set immutable value";
|
||||||
gst_assert_type(vm, key, GST_NUMBER);
|
if (key.type != GST_NUMBER) return "expected numeric key";
|
||||||
gst_assert_type(vm, value, GST_NUMBER);
|
if (value.type != GST_NUMBER) return "expected numeric value";
|
||||||
index = to_index(key.data.number, ds.data.buffer->count);
|
index = to_index(key.data.number, ds.data.buffer->count);
|
||||||
if (index == -1) gst_error(vm, "invalid buffer access");
|
if (index == -1) return "invalid buffer access";
|
||||||
ds.data.buffer->data[index] = to_byte(value.data.number);
|
ds.data.buffer->data[index] = to_byte(value.data.number);
|
||||||
break;
|
break;
|
||||||
case GST_OBJECT:
|
case GST_OBJECT:
|
||||||
if (ds.data.object->flags & GST_IMMUTABLE)
|
if (ds.data.object->flags & GST_IMMUTABLE)
|
||||||
goto immutable;
|
return "cannot set immutable value";
|
||||||
gst_object_put(vm, ds.data.object, key, value);
|
gst_object_put(vm, ds.data.object, key, value);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
gst_error(vm, "cannot set");
|
return "cannot set";
|
||||||
}
|
}
|
||||||
return;
|
return NULL;
|
||||||
immutable:
|
|
||||||
gst_error(vm, "cannot set immutable value");
|
|
||||||
}
|
}
|
||||||
|
4
value.h
4
value.h
@ -15,10 +15,10 @@ int gst_compare(GstValue x, GstValue y);
|
|||||||
int gst_equals(GstValue x, GstValue y);
|
int gst_equals(GstValue x, GstValue y);
|
||||||
|
|
||||||
/* Get a value from an associative gst object. Can throw errors. */
|
/* Get a value from an associative gst object. Can throw errors. */
|
||||||
GstValue gst_get(Gst *vm, GstValue ds, GstValue key);
|
const char *gst_get(GstValue ds, GstValue key, GstValue *out);
|
||||||
|
|
||||||
/* Set a value in an associative gst object. Can throw errors. */
|
/* Set a value in an associative gst object. Can throw errors. */
|
||||||
void gst_set(Gst *vm, GstValue ds, GstValue key, GstValue value);
|
const char *gst_set(Gst *vm, GstValue ds, GstValue key, GstValue value);
|
||||||
|
|
||||||
/* Load a c style string into a gst value (copies data) */
|
/* Load a c style string into a gst value (copies data) */
|
||||||
GstValue gst_load_cstring(Gst *vm, const char *string);
|
GstValue gst_load_cstring(Gst *vm, const char *string);
|
||||||
|
158
vm.c
158
vm.c
@ -1,23 +1,28 @@
|
|||||||
|
|
||||||
#include "vm.h"
|
#include "vm.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "value.h"
|
#include "value.h"
|
||||||
#include "ds.h"
|
#include "ds.h"
|
||||||
#include "gc.h"
|
#include "gc.h"
|
||||||
|
|
||||||
|
/* Macros for errors in the vm */
|
||||||
|
|
||||||
|
/* Exit from the VM normally */
|
||||||
|
#define gst_exit(vm, r) return ((vm)->ret = (r), GST_RETURN_OK)
|
||||||
|
|
||||||
|
/* Bail from the VM with an error string. */
|
||||||
|
#define gst_error(vm, e) return ((vm)->ret = gst_load_cstring((vm), (e)), GST_RETURN_ERROR)
|
||||||
|
|
||||||
|
/* Crash. Not catchable, unlike error. */
|
||||||
|
#define gst_crash(vm, e) return ((vm)->crash = (e), GST_RETURN_CRASH)
|
||||||
|
|
||||||
|
/* Error if the condition is false */
|
||||||
|
#define gst_assert(vm, cond, e) do {if (!(cond)){gst_error((vm), (e));}} while (0)
|
||||||
|
|
||||||
static const char GST_NO_UPVALUE[] = "no upvalue";
|
static const char GST_NO_UPVALUE[] = "no upvalue";
|
||||||
static const char GST_EXPECTED_FUNCTION[] = "expected function";
|
static const char GST_EXPECTED_FUNCTION[] = "expected function";
|
||||||
static const char GST_EXPECTED_NUMBER_ROP[] = "expected right operand to be number";
|
static const char GST_EXPECTED_NUMBER_ROP[] = "expected right operand to be number";
|
||||||
static const char GST_EXPECTED_NUMBER_LOP[] = "expected left operand to be number";
|
static const char GST_EXPECTED_NUMBER_LOP[] = "expected left operand to be number";
|
||||||
|
|
||||||
/* Get a literal */
|
|
||||||
static GstValue gst_vm_literal(Gst *vm, GstFunction *fn, uint16_t index) {
|
|
||||||
if (index > fn->def->literalsLen) {
|
|
||||||
gst_error(vm, GST_NO_UPVALUE);
|
|
||||||
}
|
|
||||||
return fn->def->literals[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Load a function into the VM. The function will be called with
|
/* Load a function into the VM. The function will be called with
|
||||||
* no arguments when run */
|
* no arguments when run */
|
||||||
static void gst_load(Gst *vm, GstValue callee) {
|
static void gst_load(Gst *vm, GstValue callee) {
|
||||||
@ -61,53 +66,18 @@ int gst_start(Gst *vm, GstValue func) {
|
|||||||
GstStackFrame frame;
|
GstStackFrame frame;
|
||||||
GstValue temp, v1, v2;
|
GstValue temp, v1, v2;
|
||||||
uint16_t *pc;
|
uint16_t *pc;
|
||||||
|
|
||||||
/* Load the callee */
|
/* Load the callee */
|
||||||
gst_load(vm, func);
|
gst_load(vm, func);
|
||||||
|
|
||||||
|
/* Intialize local state */
|
||||||
thread = *vm->thread;
|
thread = *vm->thread;
|
||||||
stack = thread.data + thread.count;
|
stack = thread.data + thread.count;
|
||||||
frame = *((GstStackFrame *)(stack - GST_FRAME_SIZE));
|
frame = *((GstStackFrame *)(stack - GST_FRAME_SIZE));
|
||||||
pc = frame.pc;
|
pc = frame.pc;
|
||||||
|
|
||||||
/* Set jmp_buf to jump back to for return. */
|
|
||||||
{
|
|
||||||
int n;
|
|
||||||
if ((n = setjmp(vm->jump))) {
|
|
||||||
/* Good return */
|
|
||||||
if (n == 1) {
|
|
||||||
return 0;
|
|
||||||
} else if (n == 2) {
|
|
||||||
/* Error. */
|
|
||||||
while (!frame.errorJump) {
|
|
||||||
/* Check for closure */
|
|
||||||
if (frame.env) {
|
|
||||||
frame.env->thread = NULL;
|
|
||||||
frame.env->stackOffset = frame.size;
|
|
||||||
frame.env->values = gst_alloc(vm, sizeof(GstValue) * frame.size);
|
|
||||||
gst_memcpy(frame.env->values,
|
|
||||||
thread.data + thread.count,
|
|
||||||
frame.size * sizeof(GstValue));
|
|
||||||
}
|
|
||||||
stack -= frame.prevSize + GST_FRAME_SIZE;
|
|
||||||
if (stack <= thread.data) {
|
|
||||||
thread.count = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
frame = *((GstStackFrame *)(stack - GST_FRAME_SIZE));
|
|
||||||
}
|
|
||||||
if (thread.count < GST_FRAME_SIZE)
|
|
||||||
return n;
|
|
||||||
/* Jump to the error location */
|
|
||||||
pc = frame.errorJump;
|
|
||||||
/* Set error */
|
|
||||||
stack[frame.errorSlot] = vm->error;
|
|
||||||
} else {
|
|
||||||
/* Crash. just return */
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Main interpreter loop */
|
/* Main interpreter loop */
|
||||||
|
mainloop:
|
||||||
for (;;) {
|
for (;;) {
|
||||||
|
|
||||||
switch (*pc) {
|
switch (*pc) {
|
||||||
@ -290,10 +260,16 @@ int gst_start(Gst *vm, GstValue func) {
|
|||||||
/* Call the function */
|
/* Call the function */
|
||||||
if (temp.type == GST_CFUNCTION) {
|
if (temp.type == GST_CFUNCTION) {
|
||||||
/* Save current state to vm thread */
|
/* Save current state to vm thread */
|
||||||
|
int status;
|
||||||
*((GstStackFrame *)(stack - GST_FRAME_SIZE)) = frame;
|
*((GstStackFrame *)(stack - GST_FRAME_SIZE)) = frame;
|
||||||
*vm->thread = thread;
|
*vm->thread = thread;
|
||||||
v2 = temp.data.cfunction(vm);
|
vm->ret.type = GST_NIL;
|
||||||
|
status = temp.data.cfunction(vm);
|
||||||
|
v1 = vm->ret;
|
||||||
|
if (status == GST_RETURN_OK)
|
||||||
goto ret;
|
goto ret;
|
||||||
|
else
|
||||||
|
goto vm_error;
|
||||||
} else {
|
} else {
|
||||||
for (; i < locals; ++i)
|
for (; i < locals; ++i)
|
||||||
stack[i].type = GST_NIL;
|
stack[i].type = GST_NIL;
|
||||||
@ -308,7 +284,9 @@ int gst_start(Gst *vm, GstValue func) {
|
|||||||
|
|
||||||
case GST_OP_CST: /* Load constant value */
|
case GST_OP_CST: /* Load constant value */
|
||||||
gst_assert(vm, frame.callee.type == GST_FUNCTION, GST_EXPECTED_FUNCTION);
|
gst_assert(vm, frame.callee.type == GST_FUNCTION, GST_EXPECTED_FUNCTION);
|
||||||
stack[pc[1]] = gst_vm_literal(vm, frame.callee.data.function, pc[2]);
|
if (pc[2] > frame.callee.data.function->def->literalsLen)
|
||||||
|
gst_error(vm, GST_NO_UPVALUE);
|
||||||
|
stack[pc[1]] = frame.callee.data.function->def->literals[pc[2]];
|
||||||
pc += 3;
|
pc += 3;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -343,7 +321,9 @@ int gst_start(Gst *vm, GstValue func) {
|
|||||||
frame.env->stackOffset = thread.count;
|
frame.env->stackOffset = thread.count;
|
||||||
frame.env->values = NULL;
|
frame.env->values = NULL;
|
||||||
}
|
}
|
||||||
temp = gst_vm_literal(vm, frame.callee.data.function, pc[2]);
|
if (pc[2] > frame.callee.data.function->def->literalsLen)
|
||||||
|
gst_error(vm, GST_NO_UPVALUE);
|
||||||
|
temp = frame.callee.data.function->def->literals[pc[2]];
|
||||||
if (temp.type != GST_NIL)
|
if (temp.type != GST_NIL)
|
||||||
gst_error(vm, "cannot create closure");
|
gst_error(vm, "cannot create closure");
|
||||||
fn = gst_alloc(vm, sizeof(GstFunction));
|
fn = gst_alloc(vm, sizeof(GstFunction));
|
||||||
@ -488,10 +468,16 @@ int gst_start(Gst *vm, GstValue func) {
|
|||||||
/* Call the function */
|
/* Call the function */
|
||||||
if (temp.type == GST_CFUNCTION) {
|
if (temp.type == GST_CFUNCTION) {
|
||||||
/* Save current state to vm thread */
|
/* Save current state to vm thread */
|
||||||
|
int status;
|
||||||
*((GstStackFrame *)(stack - GST_FRAME_SIZE)) = frame;
|
*((GstStackFrame *)(stack - GST_FRAME_SIZE)) = frame;
|
||||||
*vm->thread = thread;
|
*vm->thread = thread;
|
||||||
v2 = temp.data.cfunction(vm);
|
vm->ret.type = GST_NIL;
|
||||||
|
status = temp.data.cfunction(vm);
|
||||||
|
v1 = vm->ret;
|
||||||
|
if (status == GST_RETURN_OK)
|
||||||
goto ret;
|
goto ret;
|
||||||
|
else
|
||||||
|
goto vm_error;
|
||||||
} else {
|
} else {
|
||||||
pc = temp.data.function->def->byteCode;
|
pc = temp.data.function->def->byteCode;
|
||||||
}
|
}
|
||||||
@ -502,29 +488,38 @@ int gst_start(Gst *vm, GstValue func) {
|
|||||||
v2.type = GST_NIL;
|
v2.type = GST_NIL;
|
||||||
goto ret;
|
goto ret;
|
||||||
|
|
||||||
case GST_OP_GET:
|
case GST_OP_GET: /* Associative get */
|
||||||
temp = gst_get(vm, stack[pc[2]], stack[pc[3]]);
|
{
|
||||||
stack[pc[1]] = temp;
|
const char *err;
|
||||||
|
err = gst_get(stack[pc[2]], stack[pc[3]], stack + pc[1]);
|
||||||
|
if (err != NULL)
|
||||||
|
gst_error(vm, err);
|
||||||
pc += 4;
|
pc += 4;
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case GST_OP_SET:
|
case GST_OP_SET: /* Associative set */
|
||||||
gst_set(vm, stack[pc[1]], stack[pc[2]], stack[pc[3]]);
|
{
|
||||||
|
const char *err;
|
||||||
|
err = gst_set(vm, stack[pc[1]], stack[pc[2]], stack[pc[3]]);
|
||||||
|
if (err != NULL)
|
||||||
|
gst_error(vm, err);
|
||||||
pc += 4;
|
pc += 4;
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case GST_OP_ERR:
|
case GST_OP_ERR: /* Throw error */
|
||||||
vm->error = stack[pc[1]];
|
vm->ret = stack[pc[1]];
|
||||||
longjmp(vm->jump, 2);
|
goto vm_error;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GST_OP_TRY:
|
case GST_OP_TRY: /* Begin try block */
|
||||||
frame.errorSlot = pc[1];
|
frame.errorSlot = pc[1];
|
||||||
frame.errorJump = pc + *(uint32_t *)(pc + 2);
|
frame.errorJump = pc + *(uint32_t *)(pc + 2);
|
||||||
pc += 4;
|
pc += 4;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GST_OP_UTY:
|
case GST_OP_UTY: /* End try block */
|
||||||
frame.errorJump = NULL;
|
frame.errorJump = NULL;
|
||||||
pc++;
|
pc++;
|
||||||
break;
|
break;
|
||||||
@ -560,6 +555,36 @@ int gst_start(Gst *vm, GstValue func) {
|
|||||||
*vm->thread = thread;
|
*vm->thread = thread;
|
||||||
gst_maybe_collect(vm);
|
gst_maybe_collect(vm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Handle errors from c functions and vm opcodes */
|
||||||
|
vm_error:
|
||||||
|
while (!frame.errorJump) {
|
||||||
|
/* Check for closure */
|
||||||
|
if (frame.env) {
|
||||||
|
frame.env->thread = NULL;
|
||||||
|
frame.env->stackOffset = frame.size;
|
||||||
|
frame.env->values = gst_alloc(vm, sizeof(GstValue) * frame.size);
|
||||||
|
gst_memcpy(frame.env->values,
|
||||||
|
thread.data + thread.count,
|
||||||
|
frame.size * sizeof(GstValue));
|
||||||
|
}
|
||||||
|
stack -= frame.prevSize + GST_FRAME_SIZE;
|
||||||
|
if (stack <= thread.data) {
|
||||||
|
thread.count = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
frame = *((GstStackFrame *)(stack - GST_FRAME_SIZE));
|
||||||
|
}
|
||||||
|
/* If we completely unwind the stack we just return */
|
||||||
|
if (thread.count < GST_FRAME_SIZE)
|
||||||
|
return GST_RETURN_ERROR;
|
||||||
|
/* Jump to the error location */
|
||||||
|
pc = frame.errorJump;
|
||||||
|
/* Set error */
|
||||||
|
stack[frame.errorSlot] = vm->ret;
|
||||||
|
/* Resume vm */
|
||||||
|
goto mainloop;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get an argument from the stack */
|
/* Get an argument from the stack */
|
||||||
@ -567,7 +592,11 @@ GstValue gst_arg(Gst *vm, uint16_t index) {
|
|||||||
GstValue *stack = vm->thread->data + vm->thread->count;
|
GstValue *stack = vm->thread->data + vm->thread->count;
|
||||||
GstStackFrame *frame = (GstStackFrame *)(stack - GST_FRAME_SIZE);
|
GstStackFrame *frame = (GstStackFrame *)(stack - GST_FRAME_SIZE);
|
||||||
uint16_t frameSize = frame->size;
|
uint16_t frameSize = frame->size;
|
||||||
gst_assert(vm, frameSize > index, "cannot get arg out of stack bounds");
|
if (frameSize <= index) {
|
||||||
|
GstValue ret;
|
||||||
|
ret.type = GST_NIL;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
return stack[index];
|
return stack[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -576,7 +605,7 @@ void gst_set_arg(Gst* vm, uint16_t index, GstValue x) {
|
|||||||
GstValue *stack = vm->thread->data + vm->thread->count;
|
GstValue *stack = vm->thread->data + vm->thread->count;
|
||||||
GstStackFrame *frame = (GstStackFrame *)(stack - GST_FRAME_SIZE);
|
GstStackFrame *frame = (GstStackFrame *)(stack - GST_FRAME_SIZE);
|
||||||
uint16_t frameSize = frame->size;
|
uint16_t frameSize = frame->size;
|
||||||
gst_assert(vm, frameSize > index, "cannot set arg out of stack bounds");
|
if (frameSize <= index) return;
|
||||||
stack[index] = x;
|
stack[index] = x;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -590,7 +619,6 @@ uint16_t gst_count_args(Gst *vm) {
|
|||||||
/* Initialize the VM */
|
/* Initialize the VM */
|
||||||
void gst_init(Gst *vm) {
|
void gst_init(Gst *vm) {
|
||||||
vm->ret.type = GST_NIL;
|
vm->ret.type = GST_NIL;
|
||||||
vm->error.type = GST_NIL;
|
|
||||||
vm->crash = NULL;
|
vm->crash = NULL;
|
||||||
/* Garbage collection */
|
/* Garbage collection */
|
||||||
vm->blocks = NULL;
|
vm->blocks = NULL;
|
||||||
|
17
vm.h
17
vm.h
@ -4,23 +4,6 @@
|
|||||||
#include "datatypes.h"
|
#include "datatypes.h"
|
||||||
#include "value.h"
|
#include "value.h"
|
||||||
|
|
||||||
/* Exit from the VM normally */
|
|
||||||
#define gst_exit(vm, r) ((vm)->ret = (r), longjmp((vm)->jump, 1))
|
|
||||||
|
|
||||||
/* Bail from the VM with an error string. */
|
|
||||||
#define gst_error(vm, e) ((vm)->error = gst_load_cstring((vm), (e)), longjmp((vm)->jump, 2))
|
|
||||||
|
|
||||||
/* Crash. Not catchable, unlike error. */
|
|
||||||
#define gst_crash(vm, e) ((vm)->crash = (e), longjmp((vm)->jump, 3))
|
|
||||||
|
|
||||||
/* Error if the condition is false */
|
|
||||||
#define gst_assert(vm, cond, e) do \
|
|
||||||
{ if (!(cond)) { gst_error((vm), (e)); } } while (0)
|
|
||||||
|
|
||||||
/* Type assertion */
|
|
||||||
#define gst_assert_type(vm, f, t) \
|
|
||||||
gst_assert((vm), (f).type == (t), "Expected a different type.")
|
|
||||||
|
|
||||||
/* Initialize the VM */
|
/* Initialize the VM */
|
||||||
void gst_init(Gst * vm);
|
void gst_init(Gst * vm);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user