mirror of
https://github.com/janet-lang/janet
synced 2024-12-24 23:40: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
|
||||
|
||||
# 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
|
||||
OBJECTS=$(patsubst %.c,%.o,$(SOURCES))
|
||||
|
||||
|
20
compile.h
20
compile.h
@ -2,6 +2,20 @@
|
||||
#define COMPILE_H_9VXF71HY
|
||||
|
||||
#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 */
|
||||
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. */
|
||||
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 */
|
||||
|
52
datatypes.h
52
datatypes.h
@ -2,7 +2,6 @@
|
||||
#define DATATYPES_H_PJJ035NT
|
||||
|
||||
#include <stdint.h>
|
||||
#include <setjmp.h>
|
||||
|
||||
/* Flag for immutability in an otherwise mutable datastructure */
|
||||
#define GST_IMMUTABLE 1
|
||||
@ -36,19 +35,15 @@ typedef struct GstArray GstArray;
|
||||
typedef struct GstBuffer GstBuffer;
|
||||
typedef struct GstObject GstObject;
|
||||
typedef struct GstThread GstThread;
|
||||
typedef GstValue (*GstCFunction)(Gst * vm);
|
||||
typedef int (*GstCFunction)(Gst * vm);
|
||||
|
||||
/* Implementation details */
|
||||
typedef struct GstParser GstParser;
|
||||
typedef struct GstCompiler GstCompiler;
|
||||
typedef struct GstFuncDef GstFuncDef;
|
||||
typedef struct GstFuncEnv GstFuncEnv;
|
||||
|
||||
/* Definitely implementation details */
|
||||
typedef struct GstStackFrame GstStackFrame;
|
||||
typedef struct GstParseState GstParseState;
|
||||
typedef struct GstBucket GstBucket;
|
||||
typedef struct GstScope GstScope;
|
||||
|
||||
/* The general gst value type. Contains a large union and
|
||||
* the type information of the value */
|
||||
@ -155,6 +150,11 @@ struct GstStackFrame {
|
||||
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 */
|
||||
struct Gst {
|
||||
/* Garbage collection */
|
||||
@ -166,47 +166,9 @@ struct Gst {
|
||||
GstThread *thread;
|
||||
/* Return state */
|
||||
const char *crash;
|
||||
jmp_buf jump;
|
||||
GstValue error;
|
||||
GstValue ret; /* Returned value from VMStart. Also holds errors. */
|
||||
GstValue ret; /* Returned value from gst_start. 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 */
|
||||
enum GstOpCode {
|
||||
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) {
|
||||
GCMemoryHeader *mdata;
|
||||
if (rawBlock == NULL) {
|
||||
gst_crash(vm, "out of memory");
|
||||
GST_OUT_OF_MEMORY;
|
||||
}
|
||||
vm->nextCollection += size;
|
||||
mdata = (GCMemoryHeader *)rawBlock;
|
||||
@ -216,7 +216,6 @@ void gst_collect(Gst *vm) {
|
||||
gst_mark(vm, &thread);
|
||||
}
|
||||
gst_mark(vm, &vm->ret);
|
||||
gst_mark(vm, &vm->error);
|
||||
gst_sweep(vm);
|
||||
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 <stdio.h>
|
||||
#include "datatypes.h"
|
||||
#include "vm.h"
|
||||
#include "parse.h"
|
||||
#include "compile.h"
|
||||
#include "value.h"
|
||||
#include "gst.h"
|
||||
|
||||
/* Simple printer for gst strings */
|
||||
void string_put(FILE *out, uint8_t * string) {
|
||||
@ -15,16 +11,14 @@ void string_put(FILE *out, uint8_t * string) {
|
||||
}
|
||||
|
||||
/* Test c function */
|
||||
GstValue print(Gst *vm) {
|
||||
int print(Gst *vm) {
|
||||
uint32_t j, count;
|
||||
GstValue nil;
|
||||
count = gst_count_args(vm);
|
||||
for (j = 0; j < count; ++j) {
|
||||
string_put(stdout, gst_to_string(vm, gst_arg(vm, j)));
|
||||
fputc('\n', stdout);
|
||||
}
|
||||
nil.type = GST_NIL;
|
||||
return nil;
|
||||
return GST_RETURN_OK;
|
||||
}
|
||||
|
||||
/* A simple repl for debugging */
|
||||
@ -98,7 +92,7 @@ void debug_repl(FILE *in, FILE *out) {
|
||||
fprintf(out, "VM crash: %s\n", vm.crash);
|
||||
} else {
|
||||
fprintf(out, "VM error: ");
|
||||
string_put(out, gst_to_string(&vm, vm.error));
|
||||
string_put(out, gst_to_string(&vm, vm.ret));
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
20
parse.h
20
parse.h
@ -3,6 +3,26 @@
|
||||
|
||||
#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 */
|
||||
#define GST_PARSER_FLAG_INCOMMENT 1
|
||||
#define GST_PARSER_FLAG_EXPECTING_COMMENT 2
|
||||
|
25
util.h
25
util.h
@ -1,6 +1,16 @@
|
||||
#ifndef 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 */
|
||||
#ifndef gst_memcpy
|
||||
#include <string.h>
|
||||
@ -36,5 +46,20 @@
|
||||
#define NULL ((void *)0)
|
||||
#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
|
||||
|
||||
|
57
value.c
57
value.c
@ -388,75 +388,78 @@ static uint8_t to_byte(GstNumber raw) {
|
||||
return (uint8_t) raw;
|
||||
}
|
||||
|
||||
/* Get a value out af an associated data structure. Can throw VM error. */
|
||||
GstValue gst_get(Gst *vm, GstValue ds, GstValue key) {
|
||||
/* 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) {
|
||||
int32_t index;
|
||||
GstValue ret;
|
||||
switch (ds.type) {
|
||||
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);
|
||||
if (index == -1) gst_error(vm, "invalid array access");
|
||||
if (index == -1) return "invalid array access";
|
||||
ret = ds.data.array->data[index];
|
||||
break;
|
||||
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));
|
||||
if (index < 0) gst_error(vm, "invalid tuple access");
|
||||
if (index < 0) return "invalid tuple access";
|
||||
ret = ds.data.tuple[index];
|
||||
break;
|
||||
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);
|
||||
if (index == -1) gst_error(vm, "invalid buffer access");
|
||||
if (index == -1) return "invalid buffer access";
|
||||
ret.type = GST_NUMBER;
|
||||
ret.data.number = ds.data.buffer->data[index];
|
||||
break;
|
||||
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));
|
||||
if (index == -1) gst_error(vm, "invalid string access");
|
||||
if (index == -1) return "invalid string access";
|
||||
ret.type = GST_NUMBER;
|
||||
ret.data.number = ds.data.string[index];
|
||||
break;
|
||||
case GST_OBJECT:
|
||||
return gst_object_get(ds.data.object, key);
|
||||
ret = gst_object_get(ds.data.object, key);
|
||||
break;
|
||||
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. */
|
||||
void gst_set(Gst *vm, GstValue ds, GstValue key, GstValue value) {
|
||||
/* 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) {
|
||||
int32_t index;
|
||||
switch (ds.type) {
|
||||
case GST_ARRAY:
|
||||
if (ds.data.array->flags & GST_IMMUTABLE)
|
||||
goto immutable;
|
||||
gst_assert_type(vm, key, GST_NUMBER);
|
||||
return "cannot set immutable value";
|
||||
if (key.type != GST_NUMBER) return "expected numeric key";
|
||||
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;
|
||||
break;
|
||||
case GST_BYTEBUFFER:
|
||||
if (ds.data.buffer->flags & GST_IMMUTABLE)
|
||||
goto immutable;
|
||||
gst_assert_type(vm, key, GST_NUMBER);
|
||||
gst_assert_type(vm, value, GST_NUMBER);
|
||||
return "cannot set immutable value";
|
||||
if (key.type != GST_NUMBER) return "expected numeric key";
|
||||
if (value.type != GST_NUMBER) return "expected numeric value";
|
||||
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);
|
||||
break;
|
||||
case GST_OBJECT:
|
||||
if (ds.data.object->flags & GST_IMMUTABLE)
|
||||
goto immutable;
|
||||
return "cannot set immutable value";
|
||||
gst_object_put(vm, ds.data.object, key, value);
|
||||
break;
|
||||
default:
|
||||
gst_error(vm, "cannot set");
|
||||
return "cannot set";
|
||||
}
|
||||
return;
|
||||
immutable:
|
||||
gst_error(vm, "cannot set immutable value");
|
||||
return NULL;
|
||||
}
|
||||
|
4
value.h
4
value.h
@ -15,10 +15,10 @@ int gst_compare(GstValue x, GstValue y);
|
||||
int gst_equals(GstValue x, GstValue y);
|
||||
|
||||
/* 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. */
|
||||
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) */
|
||||
GstValue gst_load_cstring(Gst *vm, const char *string);
|
||||
|
170
vm.c
170
vm.c
@ -1,23 +1,28 @@
|
||||
|
||||
#include "vm.h"
|
||||
#include "util.h"
|
||||
#include "value.h"
|
||||
#include "ds.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_EXPECTED_FUNCTION[] = "expected function";
|
||||
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";
|
||||
|
||||
/* 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
|
||||
* no arguments when run */
|
||||
static void gst_load(Gst *vm, GstValue callee) {
|
||||
@ -61,53 +66,18 @@ int gst_start(Gst *vm, GstValue func) {
|
||||
GstStackFrame frame;
|
||||
GstValue temp, v1, v2;
|
||||
uint16_t *pc;
|
||||
|
||||
/* Load the callee */
|
||||
gst_load(vm, func);
|
||||
|
||||
/* Intialize local state */
|
||||
thread = *vm->thread;
|
||||
stack = thread.data + thread.count;
|
||||
frame = *((GstStackFrame *)(stack - GST_FRAME_SIZE));
|
||||
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 */
|
||||
mainloop:
|
||||
for (;;) {
|
||||
|
||||
switch (*pc) {
|
||||
@ -290,10 +260,16 @@ int gst_start(Gst *vm, GstValue func) {
|
||||
/* Call the function */
|
||||
if (temp.type == GST_CFUNCTION) {
|
||||
/* Save current state to vm thread */
|
||||
int status;
|
||||
*((GstStackFrame *)(stack - GST_FRAME_SIZE)) = frame;
|
||||
*vm->thread = thread;
|
||||
v2 = temp.data.cfunction(vm);
|
||||
goto ret;
|
||||
vm->ret.type = GST_NIL;
|
||||
status = temp.data.cfunction(vm);
|
||||
v1 = vm->ret;
|
||||
if (status == GST_RETURN_OK)
|
||||
goto ret;
|
||||
else
|
||||
goto vm_error;
|
||||
} else {
|
||||
for (; i < locals; ++i)
|
||||
stack[i].type = GST_NIL;
|
||||
@ -308,7 +284,9 @@ int gst_start(Gst *vm, GstValue func) {
|
||||
|
||||
case GST_OP_CST: /* Load constant value */
|
||||
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;
|
||||
break;
|
||||
|
||||
@ -343,7 +321,9 @@ int gst_start(Gst *vm, GstValue func) {
|
||||
frame.env->stackOffset = thread.count;
|
||||
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)
|
||||
gst_error(vm, "cannot create closure");
|
||||
fn = gst_alloc(vm, sizeof(GstFunction));
|
||||
@ -488,10 +468,16 @@ int gst_start(Gst *vm, GstValue func) {
|
||||
/* Call the function */
|
||||
if (temp.type == GST_CFUNCTION) {
|
||||
/* Save current state to vm thread */
|
||||
int status;
|
||||
*((GstStackFrame *)(stack - GST_FRAME_SIZE)) = frame;
|
||||
*vm->thread = thread;
|
||||
v2 = temp.data.cfunction(vm);
|
||||
goto ret;
|
||||
vm->ret.type = GST_NIL;
|
||||
status = temp.data.cfunction(vm);
|
||||
v1 = vm->ret;
|
||||
if (status == GST_RETURN_OK)
|
||||
goto ret;
|
||||
else
|
||||
goto vm_error;
|
||||
} else {
|
||||
pc = temp.data.function->def->byteCode;
|
||||
}
|
||||
@ -502,29 +488,38 @@ int gst_start(Gst *vm, GstValue func) {
|
||||
v2.type = GST_NIL;
|
||||
goto ret;
|
||||
|
||||
case GST_OP_GET:
|
||||
temp = gst_get(vm, stack[pc[2]], stack[pc[3]]);
|
||||
stack[pc[1]] = temp;
|
||||
pc += 4;
|
||||
break;
|
||||
case GST_OP_GET: /* Associative get */
|
||||
{
|
||||
const char *err;
|
||||
err = gst_get(stack[pc[2]], stack[pc[3]], stack + pc[1]);
|
||||
if (err != NULL)
|
||||
gst_error(vm, err);
|
||||
pc += 4;
|
||||
break;
|
||||
}
|
||||
|
||||
case GST_OP_SET:
|
||||
gst_set(vm, stack[pc[1]], stack[pc[2]], stack[pc[3]]);
|
||||
pc += 4;
|
||||
break;
|
||||
case GST_OP_SET: /* Associative set */
|
||||
{
|
||||
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;
|
||||
break;
|
||||
}
|
||||
|
||||
case GST_OP_ERR:
|
||||
vm->error = stack[pc[1]];
|
||||
longjmp(vm->jump, 2);
|
||||
case GST_OP_ERR: /* Throw error */
|
||||
vm->ret = stack[pc[1]];
|
||||
goto vm_error;
|
||||
break;
|
||||
|
||||
case GST_OP_TRY:
|
||||
case GST_OP_TRY: /* Begin try block */
|
||||
frame.errorSlot = pc[1];
|
||||
frame.errorJump = pc + *(uint32_t *)(pc + 2);
|
||||
pc += 4;
|
||||
break;
|
||||
|
||||
case GST_OP_UTY:
|
||||
case GST_OP_UTY: /* End try block */
|
||||
frame.errorJump = NULL;
|
||||
pc++;
|
||||
break;
|
||||
@ -560,6 +555,36 @@ int gst_start(Gst *vm, GstValue func) {
|
||||
*vm->thread = thread;
|
||||
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 */
|
||||
@ -567,7 +592,11 @@ GstValue gst_arg(Gst *vm, uint16_t index) {
|
||||
GstValue *stack = vm->thread->data + vm->thread->count;
|
||||
GstStackFrame *frame = (GstStackFrame *)(stack - GST_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];
|
||||
}
|
||||
|
||||
@ -576,7 +605,7 @@ void gst_set_arg(Gst* vm, uint16_t index, GstValue x) {
|
||||
GstValue *stack = vm->thread->data + vm->thread->count;
|
||||
GstStackFrame *frame = (GstStackFrame *)(stack - GST_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;
|
||||
}
|
||||
|
||||
@ -590,7 +619,6 @@ uint16_t gst_count_args(Gst *vm) {
|
||||
/* Initialize the VM */
|
||||
void gst_init(Gst *vm) {
|
||||
vm->ret.type = GST_NIL;
|
||||
vm->error.type = GST_NIL;
|
||||
vm->crash = NULL;
|
||||
/* Garbage collection */
|
||||
vm->blocks = NULL;
|
||||
|
17
vm.h
17
vm.h
@ -4,23 +4,6 @@
|
||||
#include "datatypes.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 */
|
||||
void gst_init(Gst * vm);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user