mirror of
https://github.com/janet-lang/janet
synced 2025-02-02 10:19:10 +00:00
Redo calling convetion for more code reuse. Allow calling gst functions from c.
This commit is contained in:
parent
169e3de5a7
commit
b986e1b967
6
Makefile
6
Makefile
@ -1,13 +1,13 @@
|
|||||||
# TIL
|
# TIL
|
||||||
|
|
||||||
CFLAGS=-std=c99 -Wall -Wextra -Wpedantic -g -O3
|
CFLAGS=-std=c99 -Wall -Wextra -Wpedantic -g
|
||||||
|
|
||||||
TARGET=interp
|
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 gst.h stl.h
|
HEADERS=vm.h ds.h compile.h parse.h value.h datatypes.h gc.h util.h gst.h stl.h disasm.h
|
||||||
SOURCES=main.c parse.c value.c vm.c ds.c compile.c gc.c stl.c
|
SOURCES=main.c parse.c value.c vm.c ds.c compile.c gc.c stl.c disasm.c
|
||||||
OBJECTS=$(patsubst %.c,%.o,$(SOURCES))
|
OBJECTS=$(patsubst %.c,%.o,$(SOURCES))
|
||||||
|
|
||||||
all: $(TARGET)
|
all: $(TARGET)
|
||||||
|
11
compile.c
11
compile.c
@ -1246,21 +1246,22 @@ static Slot compile_form(GstCompiler *c, FormOptions opts, GstValue *form) {
|
|||||||
/* Free up some slots */
|
/* Free up some slots */
|
||||||
compiler_drop_slot(c, scope, callee);
|
compiler_drop_slot(c, scope, callee);
|
||||||
compiler_tracker_free(c, scope, &tracker);
|
compiler_tracker_free(c, scope, &tracker);
|
||||||
|
/* Push arguments to stack */
|
||||||
|
gst_buffer_push_u16(c->vm, buffer, GST_OP_PSH);
|
||||||
|
gst_buffer_push_u16(c->vm, buffer, callee.index);
|
||||||
|
gst_buffer_push_u16(c->vm, buffer, gst_tuple_length(form) - 1);
|
||||||
|
/* Write the location of all of the arguments */
|
||||||
|
compiler_tracker_write(c, &tracker, 0);
|
||||||
/* If this is in tail position do a tail call. */
|
/* If this is in tail position do a tail call. */
|
||||||
if (opts.isTail) {
|
if (opts.isTail) {
|
||||||
gst_buffer_push_u16(c->vm, buffer, GST_OP_TCL);
|
gst_buffer_push_u16(c->vm, buffer, GST_OP_TCL);
|
||||||
gst_buffer_push_u16(c->vm, buffer, callee.index);
|
|
||||||
ret.hasReturned = 1;
|
ret.hasReturned = 1;
|
||||||
ret.isNil = 1;
|
ret.isNil = 1;
|
||||||
} else {
|
} else {
|
||||||
ret = compiler_get_target(c, opts);
|
ret = compiler_get_target(c, opts);
|
||||||
gst_buffer_push_u16(c->vm, buffer, GST_OP_CAL);
|
gst_buffer_push_u16(c->vm, buffer, GST_OP_CAL);
|
||||||
gst_buffer_push_u16(c->vm, buffer, callee.index);
|
|
||||||
gst_buffer_push_u16(c->vm, buffer, ret.index);
|
gst_buffer_push_u16(c->vm, buffer, ret.index);
|
||||||
}
|
}
|
||||||
gst_buffer_push_u16(c->vm, buffer, gst_tuple_length(form) - 1);
|
|
||||||
/* Write the location of all of the arguments */
|
|
||||||
compiler_tracker_write(c, &tracker, 0);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
32
datatypes.h
32
datatypes.h
@ -44,7 +44,6 @@ typedef struct GstFuncDef GstFuncDef;
|
|||||||
typedef struct GstFuncEnv GstFuncEnv;
|
typedef struct GstFuncEnv GstFuncEnv;
|
||||||
|
|
||||||
/* Definitely implementation details */
|
/* Definitely implementation details */
|
||||||
typedef struct GstStackFrame GstStackFrame;
|
|
||||||
typedef struct GstBucket GstBucket;
|
typedef struct GstBucket GstBucket;
|
||||||
|
|
||||||
/* The general gst value type. Contains a large union and
|
/* The general gst value type. Contains a large union and
|
||||||
@ -63,6 +62,11 @@ struct GstValue {
|
|||||||
GstCFunction cfunction;
|
GstCFunction cfunction;
|
||||||
GstFunction *function;
|
GstFunction *function;
|
||||||
uint8_t *string;
|
uint8_t *string;
|
||||||
|
/* Indirectly used union members */
|
||||||
|
uint16_t *u16p;
|
||||||
|
GstFuncEnv *env;
|
||||||
|
uint16_t hws[4];
|
||||||
|
uint8_t bytes[8];
|
||||||
void *pointer;
|
void *pointer;
|
||||||
} data;
|
} data;
|
||||||
};
|
};
|
||||||
@ -80,9 +84,6 @@ struct GstThread {
|
|||||||
} status;
|
} status;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Size of stack frame */
|
|
||||||
#define GST_FRAME_SIZE ((sizeof(GstStackFrame) + sizeof(GstValue) + 1) / sizeof(GstValue))
|
|
||||||
|
|
||||||
/* A dynamic array type. Useful for implementing a stack. */
|
/* A dynamic array type. Useful for implementing a stack. */
|
||||||
struct GstArray {
|
struct GstArray {
|
||||||
uint32_t count;
|
uint32_t count;
|
||||||
@ -150,18 +151,6 @@ struct GstUserdataHeader {
|
|||||||
GstObject *meta;
|
GstObject *meta;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* A stack frame in the VM */
|
|
||||||
struct GstStackFrame {
|
|
||||||
GstValue callee;
|
|
||||||
uint16_t size;
|
|
||||||
uint16_t prevSize;
|
|
||||||
uint16_t ret;
|
|
||||||
uint16_t errorSlot;
|
|
||||||
uint16_t *errorJump;
|
|
||||||
GstFuncEnv *env;
|
|
||||||
uint16_t *pc;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* VM return status from c function */
|
/* VM return status from c function */
|
||||||
#define GST_RETURN_OK 0
|
#define GST_RETURN_OK 0
|
||||||
#define GST_RETURN_ERROR 1
|
#define GST_RETURN_ERROR 1
|
||||||
@ -203,8 +192,6 @@ enum GstOpCode {
|
|||||||
GST_OP_UPV, /* Load upvalue */
|
GST_OP_UPV, /* Load upvalue */
|
||||||
GST_OP_JIF, /* Jump if */
|
GST_OP_JIF, /* Jump if */
|
||||||
GST_OP_JMP, /* Jump */
|
GST_OP_JMP, /* Jump */
|
||||||
GST_OP_CAL, /* Call function */
|
|
||||||
GST_OP_RET, /* Return from function */
|
|
||||||
GST_OP_SUV, /* Set upvalue */
|
GST_OP_SUV, /* Set upvalue */
|
||||||
GST_OP_CST, /* Load constant */
|
GST_OP_CST, /* Load constant */
|
||||||
GST_OP_I32, /* Load 32 bit signed integer */
|
GST_OP_I32, /* Load 32 bit signed integer */
|
||||||
@ -217,13 +204,16 @@ enum GstOpCode {
|
|||||||
GST_OP_ARR, /* Create array */
|
GST_OP_ARR, /* Create array */
|
||||||
GST_OP_DIC, /* Create object */
|
GST_OP_DIC, /* Create object */
|
||||||
GST_OP_TUP, /* Create tuple */
|
GST_OP_TUP, /* Create tuple */
|
||||||
GST_OP_TCL, /* Tail call */
|
|
||||||
GST_OP_RTN, /* Return nil */
|
|
||||||
GST_OP_SET, /* Assocaitive set */
|
GST_OP_SET, /* Assocaitive set */
|
||||||
GST_OP_GET, /* Associative get */
|
GST_OP_GET, /* Associative get */
|
||||||
GST_OP_ERR, /* Throw error */
|
GST_OP_ERR, /* Throw error */
|
||||||
GST_OP_TRY, /* Begin try block */
|
GST_OP_TRY, /* Begin try block */
|
||||||
GST_OP_UTY /* End try block */
|
GST_OP_UTY, /* End try block */
|
||||||
|
GST_OP_RET, /* Return from function */
|
||||||
|
GST_OP_RTN, /* Return nil */
|
||||||
|
GST_OP_PSH, /* Push a stack frame */
|
||||||
|
GST_OP_CAL, /* Call function */
|
||||||
|
GST_OP_TCL /* Tail call */
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
27
disasm.c
27
disasm.c
@ -118,12 +118,6 @@ void gst_dasm(FILE * out, uint16_t *byteCode, uint32_t len) {
|
|||||||
dasm_print_i32(out, ((int32_t *)(current + 1))[0]);
|
dasm_print_i32(out, ((int32_t *)(current + 1))[0]);
|
||||||
current += 3;
|
current += 3;
|
||||||
break;
|
break;
|
||||||
case GST_OP_CAL:
|
|
||||||
current += dasm_varg_op(out, current, "call", 2);
|
|
||||||
break;
|
|
||||||
case GST_OP_RET:
|
|
||||||
current += dasm_fixed_op(out, current, "return", 1);
|
|
||||||
break;
|
|
||||||
case GST_OP_SUV:
|
case GST_OP_SUV:
|
||||||
dasm_print_arg(out, "setUpValue");
|
dasm_print_arg(out, "setUpValue");
|
||||||
dasm_print_slot(out, current[1]);
|
dasm_print_slot(out, current[1]);
|
||||||
@ -175,12 +169,6 @@ void gst_dasm(FILE * out, uint16_t *byteCode, uint32_t len) {
|
|||||||
case GST_OP_TUP:
|
case GST_OP_TUP:
|
||||||
current += dasm_varg_op(out, current, "tuple", 1);
|
current += dasm_varg_op(out, current, "tuple", 1);
|
||||||
break;
|
break;
|
||||||
case GST_OP_TCL:
|
|
||||||
current += dasm_varg_op(out, current, "tailCall", 1);
|
|
||||||
break;
|
|
||||||
case GST_OP_RTN:
|
|
||||||
current += dasm_fixed_op(out, current, "returnNil", 0);
|
|
||||||
break;
|
|
||||||
case GST_OP_GET:
|
case GST_OP_GET:
|
||||||
current += dasm_fixed_op(out, current, "get", 3);
|
current += dasm_fixed_op(out, current, "get", 3);
|
||||||
break;
|
break;
|
||||||
@ -199,6 +187,21 @@ void gst_dasm(FILE * out, uint16_t *byteCode, uint32_t len) {
|
|||||||
case GST_OP_UTY:
|
case GST_OP_UTY:
|
||||||
current += dasm_fixed_op(out, current, "untry", 0);
|
current += dasm_fixed_op(out, current, "untry", 0);
|
||||||
break;
|
break;
|
||||||
|
case GST_OP_RET:
|
||||||
|
current += dasm_fixed_op(out, current, "return", 1);
|
||||||
|
break;
|
||||||
|
case GST_OP_RTN:
|
||||||
|
current += dasm_fixed_op(out, current, "returnNil", 0);
|
||||||
|
break;
|
||||||
|
case GST_OP_PSH:
|
||||||
|
current += dasm_varg_op(out, current, "push", 1);
|
||||||
|
break;
|
||||||
|
case GST_OP_CAL:
|
||||||
|
current += dasm_fixed_op(out, current, "call", 1);
|
||||||
|
break;
|
||||||
|
case GST_OP_TCL:
|
||||||
|
current += dasm_fixed_op(out, current, "tailCall", 0);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
fprintf(out, "\n");
|
fprintf(out, "\n");
|
||||||
}
|
}
|
||||||
|
36
ds.c
36
ds.c
@ -216,7 +216,7 @@ static GstBucket *gst_object_find(GstObject *o, GstValue key) {
|
|||||||
return (GstBucket *)0;
|
return (GstBucket *)0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get a value out of the dictionary */
|
/* Get a value out of the object */
|
||||||
GstValue gst_object_get(GstObject *o, GstValue key) {
|
GstValue gst_object_get(GstObject *o, GstValue key) {
|
||||||
GstBucket *bucket = gst_object_find(o, key);
|
GstBucket *bucket = gst_object_find(o, key);
|
||||||
if (bucket) {
|
if (bucket) {
|
||||||
@ -228,6 +228,40 @@ GstValue gst_object_get(GstObject *o, GstValue key) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Get a value of the object with a cstring key */
|
||||||
|
GstValue gst_object_get_cstring(GstObject *obj, const char *key) {
|
||||||
|
const char *end = key;
|
||||||
|
while (*end++);
|
||||||
|
uint32_t len = end - key;
|
||||||
|
uint32_t hash = gst_cstring_calchash((uint8_t *)key, len);
|
||||||
|
uint32_t index = hash % obj->capacity;
|
||||||
|
GstBucket *bucket = obj->buckets[index];
|
||||||
|
while (bucket) {
|
||||||
|
if (bucket->key.type == GST_STRING) {
|
||||||
|
uint8_t *s = bucket->key.data.string;
|
||||||
|
if (gst_string_length(s) == len) {
|
||||||
|
if (!gst_string_hash(s))
|
||||||
|
gst_string_hash(s) = gst_string_calchash(s);
|
||||||
|
if (gst_string_hash(s) == hash) {
|
||||||
|
uint32_t i;
|
||||||
|
for (i = 0; i < len; ++i)
|
||||||
|
if (s[i] != key[i])
|
||||||
|
goto notequal;
|
||||||
|
return bucket->value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
notequal:
|
||||||
|
bucket = bucket->next;
|
||||||
|
}
|
||||||
|
/* Return nil */
|
||||||
|
{
|
||||||
|
GstValue ret;
|
||||||
|
ret.type = GST_NIL;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Remove an entry from the dictionary */
|
/* Remove an entry from the dictionary */
|
||||||
GstValue gst_object_remove(Gst * vm, GstObject *o, GstValue key) {
|
GstValue gst_object_remove(Gst * vm, GstObject *o, GstValue key) {
|
||||||
GstBucket *bucket, *previous;
|
GstBucket *bucket, *previous;
|
||||||
|
5
ds.h
5
ds.h
@ -87,9 +87,12 @@ void *gst_userdata(Gst *vm, uint32_t size, GstObject *meta);
|
|||||||
/* Create a new object */
|
/* Create a new object */
|
||||||
GstObject *gst_object(Gst *vm, uint32_t capacity);
|
GstObject *gst_object(Gst *vm, uint32_t capacity);
|
||||||
|
|
||||||
/* Get a value out of the dictionary */
|
/* Get a value out of the object */
|
||||||
GstValue gst_object_get(GstObject *obj, GstValue key);
|
GstValue gst_object_get(GstObject *obj, GstValue key);
|
||||||
|
|
||||||
|
/* Get a value of of an object with a string key */
|
||||||
|
GstValue gst_object_get_cstring(GstObject *obj, const char *key);
|
||||||
|
|
||||||
/* Get a Value from the dictionary, but remove it at the same
|
/* Get a Value from the dictionary, but remove it at the same
|
||||||
* time. */
|
* time. */
|
||||||
GstValue gst_object_remove(Gst *vm, GstObject *obj, GstValue key);
|
GstValue gst_object_remove(Gst *vm, GstObject *obj, GstValue key);
|
||||||
|
20
gc.c
20
gc.c
@ -55,16 +55,15 @@ static void gst_mark_funcdef(Gst *vm, GstFuncDef *def) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Helper to mark a stack frame. Returns the next frame. */
|
/* Helper to mark a stack frame. Returns the next stackframe. */
|
||||||
static GstStackFrame *gst_mark_stackframe(Gst *vm, GstStackFrame *frame) {
|
static GstValue *gst_mark_stackframe(Gst *vm, GstValue *stack) {
|
||||||
uint32_t i;
|
uint32_t i;
|
||||||
GstValue *stack = (GstValue *)frame + GST_FRAME_SIZE;
|
gst_mark(vm, &gst_frame_callee(stack));
|
||||||
gst_mark(vm, &frame->callee);
|
if (gst_frame_env(stack) != NULL)
|
||||||
if (frame->env)
|
gst_mark_funcenv(vm, gst_frame_env(stack));
|
||||||
gst_mark_funcenv(vm, frame->env);
|
for (i = 0; i < gst_frame_size(stack); ++i)
|
||||||
for (i = 0; i < frame->size; ++i)
|
|
||||||
gst_mark(vm, stack + i);
|
gst_mark(vm, stack + i);
|
||||||
return (GstStackFrame *)(stack + frame->size);
|
return stack + gst_frame_size(stack) + GST_FRAME_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Mark allocated memory associated with a value. This is
|
/* Mark allocated memory associated with a value. This is
|
||||||
@ -110,9 +109,8 @@ void gst_mark(Gst *vm, GstValue *x) {
|
|||||||
case GST_THREAD:
|
case GST_THREAD:
|
||||||
if (gc_header(x->data.thread)->color != vm->black) {
|
if (gc_header(x->data.thread)->color != vm->black) {
|
||||||
GstThread *thread = x->data.thread;
|
GstThread *thread = x->data.thread;
|
||||||
GstStackFrame *frame = (GstStackFrame *)thread->data;
|
GstValue *frame = thread->data + GST_FRAME_SIZE;
|
||||||
GstStackFrame *end = (GstStackFrame *)(thread->data +
|
GstValue *end = thread->data + thread->count;
|
||||||
thread->count - GST_FRAME_SIZE);
|
|
||||||
gc_header(thread)->color = vm->black;
|
gc_header(thread)->color = vm->black;
|
||||||
gc_header(thread->data)->color = vm->black;
|
gc_header(thread->data)->color = vm->black;
|
||||||
while (frame <= end)
|
while (frame <= end)
|
||||||
|
6
main.c
6
main.c
@ -1,6 +1,7 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include "gst.h"
|
#include "gst.h"
|
||||||
|
#include "disasm.h"
|
||||||
|
|
||||||
/* Simple printer for gst strings */
|
/* Simple printer for gst strings */
|
||||||
static void string_put(FILE *out, uint8_t * string) {
|
static void string_put(FILE *out, uint8_t * string) {
|
||||||
@ -76,8 +77,11 @@ void debug_repl(FILE *in, FILE *out) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Print asm */
|
||||||
|
/* gst_dasm_function(stdout, func.data.function); */
|
||||||
|
|
||||||
/* Execute function */
|
/* Execute function */
|
||||||
if (gst_start(&vm, func)) {
|
if (gst_run(&vm, func)) {
|
||||||
if (out) {
|
if (out) {
|
||||||
if (vm.crash) {
|
if (vm.crash) {
|
||||||
fprintf(out, "VM crash: %s\n", vm.crash);
|
fprintf(out, "VM crash: %s\n", vm.crash);
|
||||||
|
27
stl.c
27
stl.c
@ -1,4 +1,4 @@
|
|||||||
/* This implemets a standard library in gst. Some of this
|
/* This implements a standard library in gst. Some of this
|
||||||
* will eventually be ported over to gst if possible */
|
* will eventually be ported over to gst if possible */
|
||||||
#include "stl.h"
|
#include "stl.h"
|
||||||
#include "gst.h"
|
#include "gst.h"
|
||||||
@ -38,11 +38,36 @@ int gst_stl_setclass(Gst *vm) {
|
|||||||
gst_c_return(vm, x);
|
gst_c_return(vm, x);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Call a function */
|
||||||
|
int gst_stl_callforeach(Gst *vm) {
|
||||||
|
GstValue func = gst_arg(vm, 0);
|
||||||
|
uint32_t argCount = gst_count_args(vm);
|
||||||
|
uint32_t i;
|
||||||
|
if (argCount) {
|
||||||
|
for (i = 0; i < argCount - 1; ++i)
|
||||||
|
gst_call(vm, func, 1, vm->thread->data + vm->thread->count + 1 + i);
|
||||||
|
return GST_RETURN_OK;
|
||||||
|
} else {
|
||||||
|
gst_c_throwc(vm, "expected at least one argument");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Exit */
|
||||||
|
int gst_stl_exit(Gst *vm) {
|
||||||
|
int ret;
|
||||||
|
GstValue exitValue = gst_arg(vm, 0);
|
||||||
|
ret = (exitValue.type == GST_NUMBER) ? exitValue.data.number : 0;
|
||||||
|
exit(ret);
|
||||||
|
return GST_RETURN_OK;
|
||||||
|
}
|
||||||
|
|
||||||
/* Load core */
|
/* Load core */
|
||||||
void gst_stl_load_core(GstCompiler *c) {
|
void gst_stl_load_core(GstCompiler *c) {
|
||||||
gst_compiler_add_global_cfunction(c, "print", gst_stl_print);
|
gst_compiler_add_global_cfunction(c, "print", gst_stl_print);
|
||||||
gst_compiler_add_global_cfunction(c, "get-class", gst_stl_getclass);
|
gst_compiler_add_global_cfunction(c, "get-class", gst_stl_getclass);
|
||||||
gst_compiler_add_global_cfunction(c, "set-class", gst_stl_setclass);
|
gst_compiler_add_global_cfunction(c, "set-class", gst_stl_setclass);
|
||||||
|
gst_compiler_add_global_cfunction(c, "call-for-each", gst_stl_callforeach);
|
||||||
|
gst_compiler_add_global_cfunction(c, "exit", gst_stl_exit);
|
||||||
}
|
}
|
||||||
|
|
||||||
/****/
|
/****/
|
||||||
|
15
util.h
15
util.h
@ -46,6 +46,21 @@
|
|||||||
#define NULL ((void *)0)
|
#define NULL ((void *)0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* Stack frame manipulation */
|
||||||
|
|
||||||
|
/* Size of stack frame in number of values */
|
||||||
|
#define GST_FRAME_SIZE 5
|
||||||
|
|
||||||
|
/* Macros for referencing that a stack frame given a stack */
|
||||||
|
#define gst_frame_callee(s) (*(s - 1))
|
||||||
|
#define gst_frame_size(s) ((s - 2)->data.hws[0])
|
||||||
|
#define gst_frame_prevsize(s) ((s - 2)->data.hws[1])
|
||||||
|
#define gst_frame_errloc(s) ((s - 2)->data.hws[2])
|
||||||
|
#define gst_frame_ret(s) ((s - 2)->data.hws[3])
|
||||||
|
#define gst_frame_pc(s) ((s - 3)->data.u16p)
|
||||||
|
#define gst_frame_errjmp(s) ((s - 4)->data.u16p)
|
||||||
|
#define gst_frame_env(s) ((s - 5)->data.env)
|
||||||
|
|
||||||
/* C function helpers */
|
/* C function helpers */
|
||||||
|
|
||||||
/* Return in a c function */
|
/* Return in a c function */
|
||||||
|
17
value.c
17
value.c
@ -155,9 +155,14 @@ uint8_t *gst_to_string(Gst *vm, GstValue x) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Simple hash function */
|
/* GST string version */
|
||||||
static uint32_t djb2(const uint8_t * str) {
|
uint32_t gst_string_calchash(const uint8_t *str) {
|
||||||
const uint8_t * end = str + gst_string_length(str);
|
return gst_cstring_calchash(str, gst_string_length(str));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Simple hash function (djb2) */
|
||||||
|
uint32_t gst_cstring_calchash(const uint8_t *str, uint32_t len) {
|
||||||
|
const uint8_t *end = str + len;
|
||||||
uint32_t hash = 5381;
|
uint32_t hash = 5381;
|
||||||
while (str < end)
|
while (str < end)
|
||||||
hash = (hash << 5) + hash + *str++;
|
hash = (hash << 5) + hash + *str++;
|
||||||
@ -165,7 +170,7 @@ static uint32_t djb2(const uint8_t * str) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Simple hash function to get tuple hash */
|
/* Simple hash function to get tuple hash */
|
||||||
static uint32_t tuple_hash(GstValue *tuple) {
|
static uint32_t tuple_calchash(GstValue *tuple) {
|
||||||
uint32_t i;
|
uint32_t i;
|
||||||
uint32_t count = gst_tuple_length(tuple);
|
uint32_t count = gst_tuple_length(tuple);
|
||||||
uint32_t hash = 5387;
|
uint32_t hash = 5387;
|
||||||
@ -264,13 +269,13 @@ uint32_t gst_hash(GstValue x) {
|
|||||||
if (gst_string_hash(x.data.string))
|
if (gst_string_hash(x.data.string))
|
||||||
hash = gst_string_hash(x.data.string);
|
hash = gst_string_hash(x.data.string);
|
||||||
else
|
else
|
||||||
hash = gst_string_hash(x.data.string) = djb2(x.data.string);
|
hash = gst_string_hash(x.data.string) = gst_string_calchash(x.data.string);
|
||||||
break;
|
break;
|
||||||
case GST_TUPLE:
|
case GST_TUPLE:
|
||||||
if (gst_tuple_hash(x.data.tuple))
|
if (gst_tuple_hash(x.data.tuple))
|
||||||
hash = gst_tuple_hash(x.data.tuple);
|
hash = gst_tuple_hash(x.data.tuple);
|
||||||
else
|
else
|
||||||
hash = gst_tuple_hash(x.data.tuple) = tuple_hash(x.data.tuple);
|
hash = gst_tuple_hash(x.data.tuple) = tuple_calchash(x.data.tuple);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
/* Cast the pointer */
|
/* Cast the pointer */
|
||||||
|
6
value.h
6
value.h
@ -23,6 +23,12 @@ 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);
|
||||||
|
|
||||||
|
/* Simple hash function (djb2) */
|
||||||
|
uint32_t gst_string_calchash(const uint8_t *str);
|
||||||
|
|
||||||
|
/* C string hash version */
|
||||||
|
uint32_t gst_cstring_calchash(const uint8_t *str, uint32_t len);
|
||||||
|
|
||||||
/* Convert any gst value into a string */
|
/* Convert any gst value into a string */
|
||||||
uint8_t *gst_to_string(Gst *vm, GstValue x);
|
uint8_t *gst_to_string(Gst *vm, GstValue x);
|
||||||
|
|
||||||
|
621
vm.c
621
vm.c
@ -10,7 +10,7 @@
|
|||||||
#define gst_exit(vm, r) return ((vm)->ret = (r), GST_RETURN_OK)
|
#define gst_exit(vm, r) return ((vm)->ret = (r), GST_RETURN_OK)
|
||||||
|
|
||||||
/* Bail from the VM with an error string. */
|
/* Bail from the VM with an error string. */
|
||||||
#define gst_error(vm, e) return ((vm)->ret = gst_load_cstring((vm), (e)), GST_RETURN_ERROR)
|
#define gst_error(vm, e) do { (vm)->ret = gst_load_cstring((vm), (e)); goto vm_error; } while (0)
|
||||||
|
|
||||||
/* Crash. Not catchable, unlike error. */
|
/* Crash. Not catchable, unlike error. */
|
||||||
#define gst_crash(vm, e) return ((vm)->crash = (e), GST_RETURN_CRASH)
|
#define gst_crash(vm, e) return ((vm)->crash = (e), GST_RETURN_CRASH)
|
||||||
@ -29,7 +29,7 @@ static void gst_load(Gst *vm, GstValue callee) {
|
|||||||
uint32_t startCapacity;
|
uint32_t startCapacity;
|
||||||
uint32_t locals, i;
|
uint32_t locals, i;
|
||||||
uint16_t *pc;
|
uint16_t *pc;
|
||||||
GstStackFrame *frame;
|
GstValue *stack;
|
||||||
GstThread *thread = gst_alloc(vm, sizeof(GstThread));
|
GstThread *thread = gst_alloc(vm, sizeof(GstThread));
|
||||||
if (callee.type == GST_FUNCTION) {
|
if (callee.type == GST_FUNCTION) {
|
||||||
locals = callee.data.function->def->locals;
|
locals = callee.data.function->def->locals;
|
||||||
@ -45,43 +45,51 @@ static void gst_load(Gst *vm, GstValue callee) {
|
|||||||
thread->capacity = startCapacity;
|
thread->capacity = startCapacity;
|
||||||
thread->count = GST_FRAME_SIZE;
|
thread->count = GST_FRAME_SIZE;
|
||||||
vm->thread = thread;
|
vm->thread = thread;
|
||||||
frame = (GstStackFrame *)thread->data;
|
stack = thread->data + GST_FRAME_SIZE;
|
||||||
frame->prevSize = 0;
|
gst_frame_prevsize(stack) = 0;
|
||||||
frame->size = locals;
|
gst_frame_size(stack) = locals;
|
||||||
frame->callee = callee;
|
gst_frame_callee(stack) = callee;
|
||||||
frame->errorJump = NULL;
|
gst_frame_env(stack) = NULL;
|
||||||
frame->env = NULL;
|
gst_frame_errjmp(stack) = NULL;
|
||||||
frame->pc = pc;
|
gst_frame_pc(stack) = pc;
|
||||||
/* Nil arguments */
|
/* Nil arguments */
|
||||||
for (i = 0; i < locals; ++i)
|
for (i = 0; i < locals; ++i)
|
||||||
thread->data[GST_FRAME_SIZE + i].type = GST_NIL;
|
stack[i].type = GST_NIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Contextual macro to state in function with VM */
|
||||||
|
#define GST_STATE_SYNC() do { \
|
||||||
|
thread = *vm->thread; \
|
||||||
|
stack = thread.data + thread.count; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
/* Start running the VM */
|
/* Write local state back to VM */
|
||||||
int gst_start(Gst *vm, GstValue func) {
|
#define GST_STATE_WRITE() do { \
|
||||||
|
*vm->thread = thread; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/* Start running the VM from where it left off. Continue running
|
||||||
|
* until the stack size is smaller than minStackSize. */
|
||||||
|
static int gst_continue_size(Gst *vm, uint32_t stackBase) {
|
||||||
/* VM state */
|
/* VM state */
|
||||||
GstThread thread;
|
GstThread thread;
|
||||||
GstValue *stack;
|
GstValue *stack;
|
||||||
GstStackFrame frame;
|
|
||||||
GstValue temp, v1, v2;
|
GstValue temp, v1, v2;
|
||||||
uint16_t *pc;
|
uint16_t *pc;
|
||||||
|
|
||||||
/* Load the callee */
|
|
||||||
gst_load(vm, func);
|
|
||||||
|
|
||||||
/* Intialize local state */
|
/* Intialize local state */
|
||||||
thread = *vm->thread;
|
GST_STATE_SYNC();
|
||||||
stack = thread.data + thread.count;
|
pc = gst_frame_pc(stack);
|
||||||
frame = *((GstStackFrame *)(stack - GST_FRAME_SIZE));
|
|
||||||
pc = frame.pc;
|
|
||||||
|
|
||||||
/* Main interpreter loop */
|
/* Main interpreter loop */
|
||||||
mainloop:
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
|
|
||||||
switch (*pc) {
|
switch (*pc) {
|
||||||
|
|
||||||
|
default:
|
||||||
|
gst_error(vm, "unknown opcode");
|
||||||
|
break;
|
||||||
|
|
||||||
#define OP_BINARY_MATH(op) \
|
#define OP_BINARY_MATH(op) \
|
||||||
v1 = stack[pc[2]]; \
|
v1 = stack[pc[2]]; \
|
||||||
v2 = stack[pc[3]]; \
|
v2 = stack[pc[3]]; \
|
||||||
@ -161,12 +169,14 @@ int gst_start(Gst *vm, GstValue func) {
|
|||||||
|
|
||||||
case GST_OP_UPV: /* Load Up Value */
|
case GST_OP_UPV: /* Load Up Value */
|
||||||
case GST_OP_SUV: /* Set Up Value */
|
case GST_OP_SUV: /* Set Up Value */
|
||||||
gst_assert(vm, frame.callee.type == GST_FUNCTION, GST_EXPECTED_FUNCTION);
|
|
||||||
{
|
{
|
||||||
GstValue *upv;
|
GstValue *upv;
|
||||||
GstFunction *fn = frame.callee.data.function;
|
GstFunction *fn;
|
||||||
GstFuncEnv *env;
|
GstFuncEnv *env;
|
||||||
uint16_t level = pc[2];
|
uint16_t level = pc[2];
|
||||||
|
temp = gst_frame_callee(stack);
|
||||||
|
gst_assert(vm, temp.type == GST_FUNCTION, GST_EXPECTED_FUNCTION);
|
||||||
|
fn = temp.data.function;
|
||||||
if (level == 0)
|
if (level == 0)
|
||||||
upv = stack + pc[3];
|
upv = stack + pc[3];
|
||||||
else {
|
else {
|
||||||
@ -200,116 +210,12 @@ int gst_start(Gst *vm, GstValue func) {
|
|||||||
pc += *((int32_t *)(pc + 1));
|
pc += *((int32_t *)(pc + 1));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GST_OP_CAL: /* Call */
|
|
||||||
{
|
|
||||||
int varArgs;
|
|
||||||
uint32_t expectedArity;
|
|
||||||
uint32_t normalArity;
|
|
||||||
temp = stack[pc[1]];
|
|
||||||
uint32_t arity = pc[3];
|
|
||||||
uint32_t oldCount = thread.count;
|
|
||||||
uint32_t i, locals;
|
|
||||||
GstValue *oldBase;
|
|
||||||
frame.pc = pc + 4 + arity;
|
|
||||||
frame.ret = pc[2];
|
|
||||||
|
|
||||||
/* Save current stack frame */
|
|
||||||
*((GstStackFrame *)(stack - GST_FRAME_SIZE)) = frame;
|
|
||||||
|
|
||||||
/* Get the size of next stack frame */
|
|
||||||
if (temp.type == GST_FUNCTION) {
|
|
||||||
GstFunction *fn = temp.data.function;
|
|
||||||
locals = fn->def->locals;
|
|
||||||
varArgs = fn->def->flags & GST_FUNCDEF_FLAG_VARARG;
|
|
||||||
expectedArity = fn->def->arity;
|
|
||||||
if (arity > expectedArity)
|
|
||||||
normalArity = expectedArity;
|
|
||||||
else
|
|
||||||
normalArity = arity;
|
|
||||||
} else if (temp.type == GST_CFUNCTION) {
|
|
||||||
locals = normalArity = expectedArity = arity;
|
|
||||||
varArgs = 0;
|
|
||||||
} else {
|
|
||||||
gst_error(vm, GST_EXPECTED_FUNCTION);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Push next stack frame */
|
|
||||||
{
|
|
||||||
uint32_t nextCount = thread.count + frame.size + GST_FRAME_SIZE;
|
|
||||||
/* Ensure capacity */
|
|
||||||
if (nextCount + locals > thread.capacity) {
|
|
||||||
uint32_t newCap = (nextCount + locals) * 2;
|
|
||||||
GstValue *newData = gst_alloc(vm, sizeof(GstValue) * newCap);
|
|
||||||
gst_memcpy(newData, thread.data, thread.capacity * sizeof(GstValue));
|
|
||||||
thread.data = newData;
|
|
||||||
thread.capacity = newCap;
|
|
||||||
}
|
|
||||||
thread.count = nextCount;
|
|
||||||
|
|
||||||
/* Ensure values start out as nil so as to not confuse
|
|
||||||
* the garabage collector */
|
|
||||||
for (i = nextCount; i < nextCount + locals; ++i)
|
|
||||||
thread.data[i].type = GST_NIL;
|
|
||||||
|
|
||||||
/* Set up the new stack frame */
|
|
||||||
stack = thread.data + thread.count;
|
|
||||||
frame.prevSize = frame.size;
|
|
||||||
frame.size = locals;
|
|
||||||
frame.env = NULL;
|
|
||||||
frame.callee = temp;
|
|
||||||
frame.errorJump = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Prepare to copy arguments to next stack frame */
|
|
||||||
oldBase = thread.data + oldCount;
|
|
||||||
|
|
||||||
/* Write arguments to new stack */
|
|
||||||
for (i = 0; i < normalArity; ++i)
|
|
||||||
stack[i] = oldBase[pc[4 + i]];
|
|
||||||
|
|
||||||
/* Clear stack */
|
|
||||||
for (; i < locals; ++i)
|
|
||||||
stack[i].type = GST_NIL;
|
|
||||||
|
|
||||||
/* Check for varargs and put them in a tuple */
|
|
||||||
if (varArgs) {
|
|
||||||
GstValue *tuple;
|
|
||||||
uint32_t j;
|
|
||||||
tuple = gst_tuple(vm, arity - expectedArity);
|
|
||||||
for (j = expectedArity; j < arity; ++j)
|
|
||||||
tuple[j - expectedArity] = stack[j];
|
|
||||||
stack[expectedArity].type = GST_TUPLE;
|
|
||||||
stack[expectedArity].data.tuple = tuple;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 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;
|
|
||||||
vm->ret.type = GST_NIL;
|
|
||||||
status = temp.data.cfunction(vm);
|
|
||||||
v2 = vm->ret;
|
|
||||||
if (status == GST_RETURN_OK)
|
|
||||||
goto ret;
|
|
||||||
else
|
|
||||||
goto vm_error;
|
|
||||||
} else {
|
|
||||||
pc = temp.data.function->def->byteCode;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case GST_OP_RET: /* Return */
|
|
||||||
v2 = stack[pc[1]];
|
|
||||||
goto ret;
|
|
||||||
|
|
||||||
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);
|
v1 = gst_frame_callee(stack);
|
||||||
if (pc[2] > frame.callee.data.function->def->literalsLen)
|
gst_assert(vm, v1.type == GST_FUNCTION, GST_EXPECTED_FUNCTION);
|
||||||
|
if (pc[2] > v1.data.function->def->literalsLen)
|
||||||
gst_error(vm, GST_NO_UPVALUE);
|
gst_error(vm, GST_NO_UPVALUE);
|
||||||
stack[pc[1]] = frame.callee.data.function->def->literals[pc[2]];
|
stack[pc[1]] = v1.data.function->def->literals[pc[2]];
|
||||||
pc += 3;
|
pc += 3;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -335,24 +241,25 @@ int gst_start(Gst *vm, GstValue func) {
|
|||||||
case GST_OP_CLN: /* Create closure from constant FuncDef */
|
case GST_OP_CLN: /* Create closure from constant FuncDef */
|
||||||
{
|
{
|
||||||
GstFunction *fn;
|
GstFunction *fn;
|
||||||
if (frame.callee.type != GST_FUNCTION)
|
v1 = gst_frame_callee(stack);
|
||||||
|
if (v1.type != GST_FUNCTION)
|
||||||
gst_error(vm, GST_EXPECTED_FUNCTION);
|
gst_error(vm, GST_EXPECTED_FUNCTION);
|
||||||
if (!frame.env) {
|
if (gst_frame_env(stack) == NULL) {
|
||||||
frame.env = gst_alloc(vm, sizeof(GstFuncEnv));
|
gst_frame_env(stack) = gst_alloc(vm, sizeof(GstFuncEnv));
|
||||||
*vm->thread = thread;
|
*vm->thread = thread;
|
||||||
frame.env->thread = vm->thread;
|
gst_frame_env(stack)->thread = vm->thread;
|
||||||
frame.env->stackOffset = thread.count;
|
gst_frame_env(stack)->stackOffset = thread.count;
|
||||||
frame.env->values = NULL;
|
gst_frame_env(stack)->values = NULL;
|
||||||
}
|
}
|
||||||
if (pc[2] > frame.callee.data.function->def->literalsLen)
|
if (pc[2] > v1.data.function->def->literalsLen)
|
||||||
gst_error(vm, GST_NO_UPVALUE);
|
gst_error(vm, GST_NO_UPVALUE);
|
||||||
temp = frame.callee.data.function->def->literals[pc[2]];
|
temp = v1.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));
|
||||||
fn->def = (GstFuncDef *) temp.data.pointer;
|
fn->def = (GstFuncDef *) temp.data.pointer;
|
||||||
fn->parent = frame.callee.data.function;
|
fn->parent = v1.data.function;
|
||||||
fn->env = frame.env;
|
fn->env = gst_frame_env(stack);
|
||||||
temp.type = GST_FUNCTION;
|
temp.type = GST_FUNCTION;
|
||||||
temp.data.function = fn;
|
temp.data.function = fn;
|
||||||
stack[pc[1]] = temp;
|
stack[pc[1]] = temp;
|
||||||
@ -428,110 +335,6 @@ int gst_start(Gst *vm, GstValue func) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GST_OP_TCL: /* Tail call */
|
|
||||||
{
|
|
||||||
int varArgs;
|
|
||||||
uint32_t expectedArity;
|
|
||||||
uint32_t normalArity;
|
|
||||||
temp = stack[pc[1]];
|
|
||||||
uint32_t arity = pc[2];
|
|
||||||
uint16_t locals;
|
|
||||||
uint32_t i, workspace, totalCapacity;
|
|
||||||
|
|
||||||
/* Check for closures */
|
|
||||||
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));
|
|
||||||
frame.env = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get size of new stack frame */
|
|
||||||
if (temp.type == GST_CFUNCTION) {
|
|
||||||
locals = normalArity = expectedArity = arity;
|
|
||||||
varArgs = 0;
|
|
||||||
} else if (temp.type == GST_FUNCTION) {
|
|
||||||
locals = temp.data.function->def->locals;
|
|
||||||
varArgs = temp.data.function->def->flags & GST_FUNCDEF_FLAG_VARARG;
|
|
||||||
expectedArity = temp.data.function->def->arity;
|
|
||||||
if (arity > expectedArity)
|
|
||||||
normalArity = expectedArity;
|
|
||||||
else
|
|
||||||
normalArity = arity;
|
|
||||||
} else {
|
|
||||||
gst_error(vm, GST_EXPECTED_FUNCTION);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get enough space for manipulating args - at least twice current frame size */
|
|
||||||
if (arity > frame.size) {
|
|
||||||
workspace = arity;
|
|
||||||
} else {
|
|
||||||
workspace = frame.size;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Ensure stack has enough space for copies of arguments */
|
|
||||||
totalCapacity = thread.count + arity + workspace + locals;
|
|
||||||
if (totalCapacity > thread.capacity) {
|
|
||||||
uint32_t newCap = totalCapacity * 2;
|
|
||||||
GstValue *newData = gst_alloc(vm, sizeof(GstValue) * newCap);
|
|
||||||
gst_memcpy(newData, thread.data, thread.capacity * sizeof(GstValue));
|
|
||||||
thread.data = newData;
|
|
||||||
thread.capacity = newCap;
|
|
||||||
stack = thread.data + thread.count;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Copy the arguments into the extra space */
|
|
||||||
for (i = 0; i < normalArity; ++i)
|
|
||||||
stack[workspace + i] = stack[pc[3 + i]];
|
|
||||||
|
|
||||||
/* Copy the end of the stack to the parameter position */
|
|
||||||
gst_memcpy(stack, stack + workspace, normalArity * sizeof(GstValue));
|
|
||||||
|
|
||||||
/* Update the stack frame */
|
|
||||||
frame.callee = temp;
|
|
||||||
frame.size = locals;
|
|
||||||
|
|
||||||
/* Nil the non argument part of the stack for gc */
|
|
||||||
for (i = normalArity; i < frame.size; ++i)
|
|
||||||
stack[i].type = GST_NIL;
|
|
||||||
|
|
||||||
/* Check for varargs and put them in a tuple */
|
|
||||||
if (varArgs) {
|
|
||||||
GstValue *tuple;
|
|
||||||
uint32_t j;
|
|
||||||
tuple = gst_tuple(vm, arity - expectedArity);
|
|
||||||
for (j = expectedArity; j < arity; ++j)
|
|
||||||
tuple[j - expectedArity] = stack[workspace + j];
|
|
||||||
stack[expectedArity].type = GST_TUPLE;
|
|
||||||
stack[expectedArity].data.tuple = tuple;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 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;
|
|
||||||
vm->ret.type = GST_NIL;
|
|
||||||
status = temp.data.cfunction(vm);
|
|
||||||
v2 = vm->ret;
|
|
||||||
if (status == GST_RETURN_OK)
|
|
||||||
goto ret;
|
|
||||||
else
|
|
||||||
goto vm_error;
|
|
||||||
} else {
|
|
||||||
pc = temp.data.function->def->byteCode;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case GST_OP_RTN: /* Return nil */
|
|
||||||
v2.type = GST_NIL;
|
|
||||||
goto ret;
|
|
||||||
|
|
||||||
case GST_OP_GET: /* Associative get */
|
case GST_OP_GET: /* Associative get */
|
||||||
{
|
{
|
||||||
const char *err;
|
const char *err;
|
||||||
@ -558,84 +361,296 @@ int gst_start(Gst *vm, GstValue func) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case GST_OP_TRY: /* Begin try block */
|
case GST_OP_TRY: /* Begin try block */
|
||||||
frame.errorSlot = pc[1];
|
gst_frame_errloc(stack) = pc[1];
|
||||||
frame.errorJump = pc + *(uint32_t *)(pc + 2);
|
gst_frame_errjmp(stack) = pc + *(uint32_t *)(pc + 2);
|
||||||
pc += 4;
|
pc += 4;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GST_OP_UTY: /* End try block */
|
case GST_OP_UTY: /* End try block */
|
||||||
frame.errorJump = NULL;
|
gst_frame_errjmp(stack) = NULL;
|
||||||
pc++;
|
pc++;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
case GST_OP_RTN: /* Return nil */
|
||||||
gst_error(vm, "unknown opcode");
|
vm->ret.type = GST_NIL;
|
||||||
|
goto ret;
|
||||||
|
|
||||||
|
case GST_OP_RET: /* Return */
|
||||||
|
vm->ret = stack[pc[1]];
|
||||||
|
goto ret;
|
||||||
|
|
||||||
|
case GST_OP_PSH: /* Push stack frame */
|
||||||
|
{
|
||||||
|
GstValue *nextStack;
|
||||||
|
uint32_t expectedArity, normalArity, arity, varArgs, i, locals, nextCount;
|
||||||
|
|
||||||
|
/* Get arguments to op */
|
||||||
|
temp = stack[pc[1]];
|
||||||
|
arity = pc[2];
|
||||||
|
|
||||||
|
/* Get the size of next stack frame */
|
||||||
|
if (temp.type == GST_FUNCTION) {
|
||||||
|
GstFunction *fn = temp.data.function;
|
||||||
|
locals = fn->def->locals;
|
||||||
|
varArgs = fn->def->flags & GST_FUNCDEF_FLAG_VARARG;
|
||||||
|
expectedArity = fn->def->arity;
|
||||||
|
if (arity > expectedArity)
|
||||||
|
normalArity = expectedArity;
|
||||||
|
else
|
||||||
|
normalArity = arity;
|
||||||
|
} else if (temp.type == GST_CFUNCTION) {
|
||||||
|
locals = normalArity = expectedArity = arity;
|
||||||
|
varArgs = 0;
|
||||||
|
} else {
|
||||||
|
gst_error(vm, GST_EXPECTED_FUNCTION);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get next frame size */
|
||||||
|
nextCount = thread.count + gst_frame_size(stack) + GST_FRAME_SIZE;
|
||||||
|
|
||||||
|
/* Ensure capacity */
|
||||||
|
if (nextCount + locals > thread.capacity) {
|
||||||
|
uint32_t newCap = (nextCount + locals) * 2;
|
||||||
|
GstValue *newData = gst_alloc(vm, sizeof(GstValue) * newCap);
|
||||||
|
gst_memcpy(newData, thread.data, thread.capacity * sizeof(GstValue));
|
||||||
|
thread.data = newData;
|
||||||
|
thread.capacity = newCap;
|
||||||
|
stack = thread.data + thread.count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set up the new stack frame */
|
||||||
|
nextStack = thread.data + nextCount;
|
||||||
|
gst_frame_prevsize(nextStack) = gst_frame_size(stack);
|
||||||
|
gst_frame_size(nextStack) = locals;
|
||||||
|
gst_frame_ret(nextStack) = 0;
|
||||||
|
gst_frame_env(nextStack) = NULL;
|
||||||
|
gst_frame_callee(nextStack) = temp;
|
||||||
|
gst_frame_errjmp(nextStack) = NULL;
|
||||||
|
|
||||||
|
/* Write arguments to new stack */
|
||||||
|
for (i = 0; i < normalArity; ++i)
|
||||||
|
nextStack[i] = stack[pc[3 + i]];
|
||||||
|
|
||||||
|
/* Clear stack */
|
||||||
|
for (; i < locals; ++i)
|
||||||
|
nextStack[i].type = GST_NIL;
|
||||||
|
|
||||||
|
/* Check for varargs and put them in a tuple */
|
||||||
|
if (varArgs) {
|
||||||
|
GstValue *tuple;
|
||||||
|
uint32_t j;
|
||||||
|
tuple = gst_tuple(vm, arity - expectedArity);
|
||||||
|
for (j = expectedArity; j < arity; ++j)
|
||||||
|
tuple[j - expectedArity] = stack[pc[3 + j]];
|
||||||
|
nextStack[expectedArity].type = GST_TUPLE;
|
||||||
|
nextStack[expectedArity].data.tuple = tuple;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Increment pc */
|
||||||
|
pc += 3 + arity;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GST_OP_CAL: /* Call */
|
||||||
|
case GST_OP_TCL: /* Tail call */
|
||||||
|
if (pc[0] == GST_OP_CAL) {
|
||||||
|
gst_frame_ret(stack) = pc[1];
|
||||||
|
gst_frame_pc(stack) = pc + 2;
|
||||||
|
thread.count += gst_frame_size(stack) + GST_FRAME_SIZE;
|
||||||
|
stack = thread.data + thread.count;
|
||||||
|
} else {
|
||||||
|
uint32_t i;
|
||||||
|
GstValue *nextStack = stack + gst_frame_size(stack) + GST_FRAME_SIZE;
|
||||||
|
uint32_t nextSize = gst_frame_size(nextStack);
|
||||||
|
/* Check for closures */
|
||||||
|
if (gst_frame_env(stack) != NULL) {
|
||||||
|
gst_frame_env(stack)->thread = NULL;
|
||||||
|
gst_frame_env(stack)->stackOffset = gst_frame_size(stack);
|
||||||
|
gst_frame_env(stack)->values = gst_alloc(vm, sizeof(GstValue) * gst_frame_size(stack));
|
||||||
|
gst_memcpy(gst_frame_env(stack)->values,
|
||||||
|
thread.data + thread.count,
|
||||||
|
gst_frame_size(stack) * sizeof(GstValue));
|
||||||
|
}
|
||||||
|
/* Copy over most of stack frame */
|
||||||
|
gst_frame_callee(stack) = gst_frame_callee(nextStack);
|
||||||
|
gst_frame_size(stack) = gst_frame_size(nextStack);
|
||||||
|
gst_frame_env(stack) = NULL;
|
||||||
|
gst_frame_errjmp(stack) = NULL;
|
||||||
|
/* Replace current stack frame with next */
|
||||||
|
for (i = 0; i < nextSize; ++i)
|
||||||
|
stack[i] = nextStack[i];
|
||||||
|
}
|
||||||
|
v1 = gst_frame_callee(stack);
|
||||||
|
if (v1.type == GST_FUNCTION) {
|
||||||
|
pc = v1.data.function->def->byteCode;
|
||||||
|
} else if (v1.type == GST_CFUNCTION) {
|
||||||
|
int status;
|
||||||
|
GST_STATE_WRITE();
|
||||||
|
status = v1.data.cfunction(vm);
|
||||||
|
GST_STATE_SYNC();
|
||||||
|
if (status == GST_RETURN_OK)
|
||||||
|
goto ret;
|
||||||
|
else
|
||||||
|
goto vm_error;
|
||||||
|
} else {
|
||||||
|
gst_error(vm, GST_EXPECTED_FUNCTION);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
/* Macro for popping stack frame */
|
||||||
|
#define pop_frame(onUnderflow) do { \
|
||||||
|
if (gst_frame_env(stack) != NULL) { \
|
||||||
|
gst_frame_env(stack)->thread = NULL; \
|
||||||
|
gst_frame_env(stack)->stackOffset = gst_frame_size(stack); \
|
||||||
|
gst_frame_env(stack)->values = gst_alloc(vm, sizeof(GstValue) * gst_frame_size(stack)); \
|
||||||
|
gst_memcpy(gst_frame_env(stack)->values, \
|
||||||
|
thread.data + thread.count, \
|
||||||
|
gst_frame_size(stack) * sizeof(GstValue)); \
|
||||||
|
} \
|
||||||
|
if (thread.count <= stackBase) { \
|
||||||
|
thread.count -= gst_frame_prevsize(stack) + GST_FRAME_SIZE; \
|
||||||
|
return (onUnderflow); \
|
||||||
|
} \
|
||||||
|
thread.count -= gst_frame_prevsize(stack) + GST_FRAME_SIZE; \
|
||||||
|
stack = thread.data + thread.count; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
/* Label for return */
|
/* Label for return */
|
||||||
ret:
|
ret:
|
||||||
{
|
/* Check for closure */
|
||||||
/* Check for closure */
|
pop_frame(GST_RETURN_OK);
|
||||||
if (frame.env) {
|
pc = gst_frame_pc(stack);
|
||||||
frame.env->thread = NULL;
|
stack[gst_frame_ret(stack)] = vm->ret;
|
||||||
frame.env->stackOffset = frame.size;
|
break;
|
||||||
frame.env->values = gst_alloc(vm, sizeof(GstValue) * frame.size);
|
|
||||||
gst_memcpy(frame.env->values,
|
/* Handle errors from c functions and vm opcodes */
|
||||||
thread.data + thread.count,
|
vm_error:
|
||||||
frame.size * sizeof(GstValue));
|
while (gst_frame_errjmp(stack) == NULL)
|
||||||
}
|
pop_frame(GST_RETURN_ERROR);
|
||||||
if (thread.count <= GST_FRAME_SIZE)
|
pc = gst_frame_errjmp(stack);
|
||||||
gst_exit(vm, v2);
|
stack[gst_frame_errloc(stack)] = vm->ret;
|
||||||
stack -= frame.prevSize + GST_FRAME_SIZE;
|
break;
|
||||||
thread.count -= frame.prevSize + GST_FRAME_SIZE;
|
|
||||||
frame = *((GstStackFrame *)(stack - GST_FRAME_SIZE));
|
#undef pop_frame
|
||||||
pc = frame.pc;
|
|
||||||
stack[frame.ret] = v2;
|
} /* end switch */
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* TODO: Move collection only to places that allocate memory */
|
/* TODO: Move collection only to places that allocate memory */
|
||||||
/* This, however, is good for testing to ensure no memory leaks */
|
/* This, however, is good for testing to ensure no memory leaks */
|
||||||
*vm->thread = thread;
|
*vm->thread = thread;
|
||||||
gst_maybe_collect(vm);
|
gst_maybe_collect(vm);
|
||||||
|
|
||||||
|
} /* end for */
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Continue running the VM after it has stopped */
|
||||||
|
int gst_continue(Gst *vm) {
|
||||||
|
return gst_continue_size(vm, vm->thread->count);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Run the vm with a given function */
|
||||||
|
int gst_run(Gst *vm, GstValue func) {
|
||||||
|
gst_load(vm, func);
|
||||||
|
return gst_continue(vm);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Raw function call implementation for use from c code. Beware code
|
||||||
|
* duplication between this function and GST_OP_PSH and GST_OP_CAL/GST_OP_TCL */
|
||||||
|
int gst_call(Gst *vm, GstValue callee, uint32_t arity, GstValue *args) {
|
||||||
|
GstThread thread;
|
||||||
|
GstValue *stack;
|
||||||
|
uint32_t expectedArity, normalArity, varArgs, i, locals, nextCount, size;
|
||||||
|
|
||||||
|
/* Initialize some state */
|
||||||
|
GST_STATE_SYNC();
|
||||||
|
|
||||||
|
/* Get the size of next stack frame */
|
||||||
|
if (callee.type == GST_FUNCTION) {
|
||||||
|
GstFunction *fn = callee.data.function;
|
||||||
|
locals = fn->def->locals;
|
||||||
|
varArgs = fn->def->flags & GST_FUNCDEF_FLAG_VARARG;
|
||||||
|
expectedArity = fn->def->arity;
|
||||||
|
gst_frame_pc(stack) = fn->def->byteCode;
|
||||||
|
if (arity > expectedArity)
|
||||||
|
normalArity = expectedArity;
|
||||||
|
else
|
||||||
|
normalArity = arity;
|
||||||
|
} else if (callee.type == GST_CFUNCTION) {
|
||||||
|
locals = normalArity = expectedArity = arity;
|
||||||
|
varArgs = 0;
|
||||||
|
} else {
|
||||||
|
gst_c_throwc(vm, GST_EXPECTED_FUNCTION);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Handle errors from c functions and vm opcodes */
|
/* Get next frame size */
|
||||||
vm_error:
|
nextCount = thread.count + gst_frame_size(stack) + GST_FRAME_SIZE;
|
||||||
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;
|
|
||||||
|
|
||||||
|
/* Ensure capacity */
|
||||||
|
if (nextCount + locals > thread.capacity) {
|
||||||
|
uint32_t newCap = (nextCount + locals) * 2;
|
||||||
|
GstValue *newData = gst_alloc(vm, sizeof(GstValue) * newCap);
|
||||||
|
gst_memcpy(newData, thread.data, thread.capacity * sizeof(GstValue));
|
||||||
|
thread.data = newData;
|
||||||
|
thread.capacity = newCap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Save modified thread object */
|
||||||
|
thread.count = nextCount;
|
||||||
|
*vm->thread = thread;
|
||||||
|
|
||||||
|
/* Set up the new stack frame */
|
||||||
|
size = gst_frame_size(stack);
|
||||||
|
stack = thread.data + nextCount;
|
||||||
|
gst_frame_prevsize(stack) = size;
|
||||||
|
gst_frame_size(stack) = locals;
|
||||||
|
gst_frame_env(stack) = NULL;
|
||||||
|
gst_frame_callee(stack) = callee;
|
||||||
|
gst_frame_errjmp(stack) = NULL;
|
||||||
|
|
||||||
|
/* Write arguments to new stack */
|
||||||
|
for (i = 0; i < normalArity; ++i)
|
||||||
|
stack[i] = args[i];
|
||||||
|
|
||||||
|
/* Clear stack */
|
||||||
|
for (; i < locals; ++i)
|
||||||
|
stack[i].type = GST_NIL;
|
||||||
|
|
||||||
|
/* Check for varargs and put them in a tuple */
|
||||||
|
if (varArgs) {
|
||||||
|
GstValue *tuple;
|
||||||
|
uint32_t j;
|
||||||
|
tuple = gst_tuple(vm, arity - expectedArity);
|
||||||
|
for (j = expectedArity; j < arity; ++j)
|
||||||
|
tuple[j - expectedArity] = args[j];
|
||||||
|
stack[expectedArity].type = GST_TUPLE;
|
||||||
|
stack[expectedArity].data.tuple = tuple;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Call the function */
|
||||||
|
if (callee.type == GST_FUNCTION) {
|
||||||
|
return gst_continue_size(vm, thread.count);
|
||||||
|
} else {
|
||||||
|
int status = callee.data.cfunction(vm);
|
||||||
|
GST_STATE_SYNC();
|
||||||
|
/* Check for closures */
|
||||||
|
if (gst_frame_env(stack) != NULL) {
|
||||||
|
gst_frame_env(stack)->thread = NULL;
|
||||||
|
gst_frame_env(stack)->stackOffset = gst_frame_size(stack);
|
||||||
|
gst_frame_env(stack)->values = gst_alloc(vm, sizeof(GstValue) * gst_frame_size(stack));
|
||||||
|
gst_memcpy(gst_frame_env(stack)->values,
|
||||||
|
thread.data + thread.count,
|
||||||
|
gst_frame_size(stack) * sizeof(GstValue));
|
||||||
|
}
|
||||||
|
vm->thread->count -= gst_frame_prevsize(stack) + GST_FRAME_SIZE;
|
||||||
|
return status;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get an argument from the stack */
|
/* Get an argument from the stack */
|
||||||
GstValue gst_arg(Gst *vm, uint16_t index) {
|
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);
|
uint16_t frameSize = gst_frame_size(stack);
|
||||||
uint16_t frameSize = frame->size;
|
|
||||||
if (frameSize <= index) {
|
if (frameSize <= index) {
|
||||||
GstValue ret;
|
GstValue ret;
|
||||||
ret.type = GST_NIL;
|
ret.type = GST_NIL;
|
||||||
@ -647,8 +662,7 @@ GstValue gst_arg(Gst *vm, uint16_t index) {
|
|||||||
/* Put a value on the stack */
|
/* Put a value on the stack */
|
||||||
void gst_set_arg(Gst* vm, uint16_t index, GstValue x) {
|
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);
|
uint16_t frameSize = gst_frame_size(stack);
|
||||||
uint16_t frameSize = frame->size;
|
|
||||||
if (frameSize <= index) return;
|
if (frameSize <= index) return;
|
||||||
stack[index] = x;
|
stack[index] = x;
|
||||||
}
|
}
|
||||||
@ -656,8 +670,7 @@ void gst_set_arg(Gst* vm, uint16_t index, GstValue x) {
|
|||||||
/* Get the size of the VMStack */
|
/* Get the size of the VMStack */
|
||||||
uint16_t gst_count_args(Gst *vm) {
|
uint16_t gst_count_args(Gst *vm) {
|
||||||
GstValue *stack = vm->thread->data + vm->thread->count;
|
GstValue *stack = vm->thread->data + vm->thread->count;
|
||||||
GstStackFrame *frame = (GstStackFrame *)(stack - GST_FRAME_SIZE);
|
return gst_frame_size(stack);
|
||||||
return frame->size;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Initialize the VM */
|
/* Initialize the VM */
|
||||||
|
8
vm.h
8
vm.h
@ -11,7 +11,13 @@ void gst_init(Gst * vm);
|
|||||||
void gst_deinit(Gst * vm);
|
void gst_deinit(Gst * vm);
|
||||||
|
|
||||||
/* Start running the VM with a given entry point */
|
/* Start running the VM with a given entry point */
|
||||||
int gst_start(Gst * vm, GstValue func);
|
int gst_run(Gst *vm, GstValue func);
|
||||||
|
|
||||||
|
/* Start running the VM from where it left off. */
|
||||||
|
int gst_continue(Gst *vm);
|
||||||
|
|
||||||
|
/* Call a gst value */
|
||||||
|
int gst_call(Gst *vm, GstValue callee, uint32_t arity, GstValue *args);
|
||||||
|
|
||||||
/* Run garbage collection */
|
/* Run garbage collection */
|
||||||
void gst_collect(Gst * vm);
|
void gst_collect(Gst * vm);
|
||||||
|
Loading…
Reference in New Issue
Block a user