mirror of
https://github.com/janet-lang/janet
synced 2024-11-17 14:14:49 +00:00
Work on simplifying calling procedure
This commit is contained in:
parent
15dd15278c
commit
3274e87a45
4
Makefile
4
Makefile
@ -6,8 +6,8 @@ TARGET=interp
|
||||
PREFIX=/usr/local
|
||||
|
||||
# C sources
|
||||
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 disasm.c
|
||||
HEADERS=vm.h ds.h compile.h parse.h value.h datatypes.h gc.h util.h gst.h stl.h disasm.h thread.h
|
||||
SOURCES=main.c parse.c value.c vm.c ds.c compile.c gc.c stl.c disasm.c thread.c
|
||||
OBJECTS=$(patsubst %.c,%.o,$(SOURCES))
|
||||
|
||||
all: $(TARGET)
|
||||
|
12
compile.c
12
compile.c
@ -1246,22 +1246,22 @@ static Slot compile_form(GstCompiler *c, FormOptions opts, GstValue *form) {
|
||||
/* Free up some slots */
|
||||
compiler_drop_slot(c, scope, callee);
|
||||
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 (opts.isTail) {
|
||||
gst_buffer_push_u16(c->vm, buffer, GST_OP_TCL);
|
||||
gst_buffer_push_u16(c->vm, buffer, callee.index);
|
||||
gst_buffer_push_u16(c->vm, buffer, gst_tuple_length(form) - 1);
|
||||
ret.hasReturned = 1;
|
||||
ret.isNil = 1;
|
||||
} else {
|
||||
ret = compiler_get_target(c, opts);
|
||||
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, gst_tuple_length(form) - 1);
|
||||
}
|
||||
/* Write the location of all of the arguments */
|
||||
compiler_tracker_write(c, &tracker, 0);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
@ -165,8 +165,6 @@ struct Gst {
|
||||
/* Return state */
|
||||
const char *crash;
|
||||
GstValue ret; /* Returned value from gst_start. Also holds errors. */
|
||||
/* Temporary array for use in function dispatch */
|
||||
GstValue tempArray[GST_MAX_SEARCH_DEPTH];
|
||||
};
|
||||
|
||||
/* Bytecode */
|
||||
@ -210,7 +208,6 @@ enum GstOpCode {
|
||||
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 */
|
||||
};
|
||||
|
7
disasm.c
7
disasm.c
@ -193,14 +193,11 @@ void gst_dasm(FILE * out, uint16_t *byteCode, uint32_t len) {
|
||||
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);
|
||||
current += dasm_varg_op(out, current, "call", 2);
|
||||
break;
|
||||
case GST_OP_TCL:
|
||||
current += dasm_fixed_op(out, current, "tailCall", 0);
|
||||
current += dasm_varg_op(out, current, "tailCall", 1);
|
||||
break;
|
||||
}
|
||||
fprintf(out, "\n");
|
||||
|
1
gst.h
1
gst.h
@ -8,5 +8,6 @@
|
||||
#include "compile.h"
|
||||
#include "value.h"
|
||||
#include "stl.h"
|
||||
#include "thread.h"
|
||||
|
||||
#endif // gst_h_INCLUDED
|
||||
|
7
main.c
7
main.c
@ -48,7 +48,7 @@ void debug_repl(FILE *in, FILE *out) {
|
||||
if (out) {
|
||||
fprintf(out, "\n");
|
||||
fprintf(out, "%s\n", buffer);
|
||||
for (i = 0; i < p.index; ++i) {
|
||||
for (i = 1; i < p.index; ++i) {
|
||||
fprintf(out, " ");
|
||||
}
|
||||
fprintf(out, "^\n");
|
||||
@ -78,7 +78,10 @@ void debug_repl(FILE *in, FILE *out) {
|
||||
}
|
||||
|
||||
/* Print asm */
|
||||
/* gst_dasm_function(stdout, func.data.function); */
|
||||
//if (out) {
|
||||
//fprintf(out, "\n");
|
||||
//gst_dasm_function(out, func.data.function);
|
||||
//}
|
||||
|
||||
/* Execute function */
|
||||
if (gst_run(&vm, func)) {
|
||||
|
20
sample.gst
20
sample.gst
@ -1,18 +1,6 @@
|
||||
# GST is a general purpose language. It is small, not slow, and supports meta-
|
||||
# programming. It also should be structured and static enough to easily scale to
|
||||
# large programs. Lastly, it is interoperable with C and C++.
|
||||
|
||||
# Syntax - There is very little syntax. This simplifies parsing and makes macros
|
||||
# easier to implement, which are useful in metaprogramming.
|
||||
(+ 1 2 3)
|
||||
|
||||
# Unlike most lisps, it is not a pure functional language. Also unlike lisp, gst does
|
||||
# not make much use of a list data structure, instead using arrays and objects for
|
||||
# better performance at runtime.
|
||||
(do
|
||||
(:= a 1)
|
||||
(while (< a 1025)
|
||||
(print a)
|
||||
(:= a (* a 2))
|
||||
)
|
||||
(:= a (set-class {} {"call" (fn [self a] (set self "last" a) (print self) self)}))
|
||||
(a 1)
|
||||
(a 2)
|
||||
(a 3)
|
||||
)
|
||||
|
4
stl.c
4
stl.c
@ -44,8 +44,8 @@ int gst_stl_callforeach(Gst *vm) {
|
||||
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);
|
||||
for (i = 1; i < argCount; ++i)
|
||||
gst_call(vm, func, 1, vm->thread->data + vm->thread->count + i);
|
||||
return GST_RETURN_OK;
|
||||
} else {
|
||||
gst_c_throwc(vm, "expected at least one argument");
|
||||
|
233
thread.c
Normal file
233
thread.c
Normal file
@ -0,0 +1,233 @@
|
||||
#include "datatypes.h"
|
||||
#include "thread.h"
|
||||
#include "vm.h"
|
||||
#include "util.h"
|
||||
#include "ds.h"
|
||||
|
||||
/* Create a new thread */
|
||||
GstThread *gst_thread(Gst *vm, GstValue callee, uint32_t capacity) {
|
||||
GstThread *thread = gst_alloc(vm, sizeof(GstThread));
|
||||
GstValue *data, *stack;
|
||||
if (capacity < GST_FRAME_SIZE) capacity = GST_FRAME_SIZE;
|
||||
data = gst_alloc(vm, sizeof(GstValue) * capacity);
|
||||
thread->capacity = capacity;
|
||||
thread->count = GST_FRAME_SIZE;
|
||||
thread->data = data;
|
||||
thread->status = GST_THREAD_PENDING;
|
||||
stack = data + GST_FRAME_SIZE;
|
||||
gst_frame_size(stack) = 0;
|
||||
gst_frame_prevsize(stack) = 0;
|
||||
gst_frame_ret(stack) = 0;
|
||||
gst_frame_errloc(stack) = 0;
|
||||
gst_frame_callee(stack) = callee;
|
||||
gst_frame_pc(stack) = NULL;
|
||||
gst_frame_env(stack) = NULL;
|
||||
gst_frame_errjmp(stack) = NULL;
|
||||
gst_thread_endframe(vm, thread);
|
||||
return thread;
|
||||
}
|
||||
|
||||
/* Ensure that the thread has enough EXTRA capacity */
|
||||
void gst_thread_ensure_extra(Gst *vm, GstThread *thread, uint32_t extra) {
|
||||
GstValue *newData, *stack;
|
||||
uint32_t usedCapacity, neededCapacity, newCapacity;
|
||||
stack = thread->data + thread->count;
|
||||
usedCapacity = thread->count + gst_frame_size(stack) + GST_FRAME_SIZE;
|
||||
neededCapacity = usedCapacity + extra;
|
||||
if (thread->capacity >= neededCapacity) return;
|
||||
newCapacity = 2 * neededCapacity;
|
||||
newData = gst_alloc(vm, sizeof(GstValue) * newCapacity);
|
||||
gst_memcpy(newData, thread->data, sizeof(GstValue) * usedCapacity);
|
||||
thread->data = newData;
|
||||
thread->capacity = newCapacity;
|
||||
}
|
||||
|
||||
/* Push a value on the current stack frame*/
|
||||
void gst_thread_push(Gst *vm, GstThread *thread, GstValue x) {
|
||||
GstValue *stack;
|
||||
gst_thread_ensure_extra(vm, thread, 1);
|
||||
stack = thread->data + thread->count;
|
||||
stack[gst_frame_size(stack)++] = x;
|
||||
}
|
||||
|
||||
/* Push n nils onto the stack */
|
||||
void gst_thread_pushnil(Gst *vm, GstThread *thread, uint32_t n) {
|
||||
GstValue *stack, *current, *end;
|
||||
gst_thread_ensure_extra(vm, thread, n);
|
||||
stack = thread->data + thread->count;
|
||||
current = stack + gst_frame_size(stack);
|
||||
end = current + n;
|
||||
for (; current < end; ++current) {
|
||||
current->type = GST_NIL;
|
||||
}
|
||||
gst_frame_size(stack) += n;
|
||||
}
|
||||
|
||||
/* Package up extra args after and including n into tuple at n*/
|
||||
void gst_thread_tuplepack(Gst *vm, GstThread *thread, uint32_t n) {
|
||||
GstValue *stack = thread->data + thread->count;
|
||||
uint32_t size = gst_frame_size(stack);
|
||||
if (n >= size) {
|
||||
gst_thread_pushnil(vm, thread, n - size + 1);
|
||||
stack = thread->data + thread->count;
|
||||
stack[n].type = GST_TUPLE;
|
||||
stack[n].data.tuple = gst_tuple(vm, 0);
|
||||
} else {
|
||||
uint32_t i;
|
||||
GstValue *tuple = gst_tuple(vm, size - n);
|
||||
for (i = n; i < size; ++i)
|
||||
tuple[i - n] = stack[i];
|
||||
stack[n].type = GST_TUPLE;
|
||||
stack[n].data.tuple = tuple;
|
||||
gst_frame_size(stack) = n + 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Push a stack frame to a thread, with space for arity arguments. Returns the new
|
||||
* stack. */
|
||||
GstValue *gst_thread_beginframe(Gst *vm, GstThread *thread, GstValue callee, uint32_t arity) {
|
||||
uint32_t frameOffset, recurCount;
|
||||
GstValue *oldStack, *newStack;
|
||||
|
||||
/* Push the frame */
|
||||
gst_thread_ensure_extra(vm, thread, GST_FRAME_SIZE + arity + 4);
|
||||
oldStack = thread->data + thread->count;
|
||||
frameOffset = gst_frame_size(oldStack) + GST_FRAME_SIZE;
|
||||
newStack = oldStack + frameOffset;
|
||||
gst_frame_prevsize(newStack) = gst_frame_size(oldStack);
|
||||
gst_frame_env(newStack) = NULL;
|
||||
gst_frame_errjmp(newStack) = NULL;
|
||||
gst_frame_size(newStack) = 0;
|
||||
thread->count += frameOffset;
|
||||
|
||||
/* Get the true callee of next stack frame */
|
||||
recurCount = 200;
|
||||
recur:
|
||||
if (!recurCount) {
|
||||
return NULL;
|
||||
}
|
||||
switch(callee.type) {
|
||||
default: return NULL;
|
||||
case GST_FUNCTION:
|
||||
gst_frame_callee(newStack) = callee;
|
||||
gst_frame_pc(newStack) = callee.data.function->def->byteCode;
|
||||
break;
|
||||
case GST_CFUNCTION:
|
||||
gst_frame_callee(newStack) = callee;
|
||||
gst_frame_pc(newStack) = NULL;
|
||||
break;
|
||||
case GST_OBJECT:
|
||||
{
|
||||
GstObject *meta = callee.data.object->meta;
|
||||
if (meta == NULL) return NULL;
|
||||
gst_thread_push(vm, thread, callee);
|
||||
callee = gst_object_get_cstring(meta, "call");
|
||||
newStack = thread->data + thread->count;
|
||||
--recurCount;
|
||||
goto recur;
|
||||
}
|
||||
case GST_USERDATA:
|
||||
{
|
||||
GstObject *meta = ((GstUserdataHeader *)callee.data.pointer - 1)->meta;
|
||||
gst_thread_push(vm, thread, callee);
|
||||
callee = gst_object_get_cstring(meta, "call");
|
||||
newStack = thread->data + thread->count;
|
||||
--recurCount;
|
||||
goto recur;
|
||||
}
|
||||
}
|
||||
|
||||
/* Ensure the extra space and initialize to nil */
|
||||
gst_thread_pushnil(vm, thread, arity);
|
||||
|
||||
/* Return ok */
|
||||
return thread->data + thread->count;
|
||||
}
|
||||
|
||||
/* After pushing arguments to a stack frame created with gst_thread_beginframe, call this
|
||||
* to finalize the frame before starting a function call. */
|
||||
void gst_thread_endframe(Gst *vm, GstThread *thread) {
|
||||
GstValue *stack = thread->data + thread->count;
|
||||
GstValue callee = gst_frame_callee(stack);
|
||||
if (callee.type == GST_FUNCTION) {
|
||||
GstFunction *fn = callee.data.function;
|
||||
gst_frame_pc(stack) = fn->def->byteCode;
|
||||
if (fn->def->flags & GST_FUNCDEF_FLAG_VARARG) {
|
||||
uint32_t arity = fn->def->arity;
|
||||
gst_thread_tuplepack(vm, thread, arity);
|
||||
} else {
|
||||
uint32_t locals = fn->def->locals;
|
||||
if (gst_frame_size(stack) < locals) {
|
||||
gst_thread_pushnil(vm, thread, locals - gst_frame_size(stack));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Pop a stack frame from the thread. Returns the new stack frame, or
|
||||
* NULL if there are no more frames */
|
||||
GstValue *gst_thread_popframe(Gst *vm, GstThread *thread) {
|
||||
GstValue *stack = thread->data + thread->count;
|
||||
uint32_t prevsize = gst_frame_prevsize(stack);
|
||||
GstValue *nextstack = stack - GST_FRAME_SIZE - prevsize;
|
||||
GstFuncEnv *env = gst_frame_env(stack);
|
||||
|
||||
/* Check for closures */
|
||||
if (env != NULL) {
|
||||
uint32_t size = gst_frame_size(stack);
|
||||
env->thread = NULL;
|
||||
env->stackOffset = size;
|
||||
env->values = gst_alloc(vm, sizeof(GstValue) * size);
|
||||
gst_memcpy(env->values, stack, sizeof(GstValue) * size);
|
||||
}
|
||||
|
||||
/* Shrink stack */
|
||||
thread->count -= GST_FRAME_SIZE + prevsize;
|
||||
|
||||
/* Check if the stack is empty, and if so, return null */
|
||||
if (thread->count)
|
||||
return nextstack;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Move the current stack frame over its parent stack frame, allowing
|
||||
* for primitive tail calls. */
|
||||
GstValue *gst_thread_tail(Gst *vm, GstThread *thread) {
|
||||
GstFuncEnv *env;
|
||||
GstValue *stack = thread->data + thread->count;
|
||||
GstValue *nextStack = gst_thread_popframe(vm, thread);
|
||||
uint32_t i;
|
||||
|
||||
if (nextStack == NULL) return NULL;
|
||||
env = gst_frame_env(nextStack);
|
||||
|
||||
/* Check for old closures */
|
||||
if (env != NULL) {
|
||||
uint32_t size = gst_frame_size(stack);
|
||||
env->thread = NULL;
|
||||
env->stackOffset = size;
|
||||
env->values = gst_alloc(vm, sizeof(GstValue) * size);
|
||||
gst_memcpy(env->values, stack, sizeof(GstValue) * size);
|
||||
}
|
||||
|
||||
/* Modify new closure */
|
||||
env = gst_frame_env(stack);
|
||||
if (env != NULL) {
|
||||
env->stackOffset = thread->count;
|
||||
}
|
||||
|
||||
/* Copy over (some of) stack frame. Leave ret and prevsize untouched. */
|
||||
gst_frame_env(nextStack) = env;
|
||||
gst_frame_size(nextStack) = gst_frame_size(stack);
|
||||
gst_frame_pc(nextStack) = gst_frame_pc(stack);
|
||||
gst_frame_errjmp(nextStack) = gst_frame_errjmp(stack);
|
||||
gst_frame_errloc(nextStack) = gst_frame_errloc(stack);
|
||||
gst_frame_callee(nextStack) = gst_frame_callee(stack);
|
||||
|
||||
/* Copy stack arguments */
|
||||
for (i = 0; i < gst_frame_size(nextStack); ++i)
|
||||
nextStack[i] = stack[i];
|
||||
|
||||
return nextStack;
|
||||
}
|
41
thread.h
Normal file
41
thread.h
Normal file
@ -0,0 +1,41 @@
|
||||
#ifndef thread_h_INCLUDED
|
||||
#define thread_h_INCLUDED
|
||||
|
||||
#include "datatypes.h"
|
||||
|
||||
/* Get the current stack frame */
|
||||
#define gst_thread_stack(t) ((t)->data + (t)->count)
|
||||
|
||||
/* Create a new thread */
|
||||
GstThread *gst_thread(Gst *vm, GstValue callee, uint32_t capacity);
|
||||
|
||||
/* Ensure that the thread has enough EXTRA capacity */
|
||||
void gst_thread_ensure_extra(Gst *vm, GstThread *thread, uint32_t extra);
|
||||
|
||||
/* Push a value on the current stack frame*/
|
||||
void gst_thread_push(Gst *vm, GstThread *thread, GstValue x);
|
||||
|
||||
/* Push n nils onto the stack */
|
||||
void gst_thread_pushnil(Gst *vm, GstThread *thread, uint32_t n);
|
||||
|
||||
/* Package up extra args after and including n into tuple at n*/
|
||||
void gst_thread_tuplepack(Gst *vm, GstThread *thread, uint32_t n);
|
||||
|
||||
/* Push a stack frame to a thread, with space for arity arguments. Returns the new
|
||||
* stack. */
|
||||
GstValue *gst_thread_beginframe(Gst *vm, GstThread *thread, GstValue callee, uint32_t arity);
|
||||
|
||||
/* After pushing arguments to a stack frame created with gst_thread_beginframe, call this
|
||||
* to finalize the frame before starting a function call. */
|
||||
void gst_thread_endframe(Gst *vm, GstThread *thread);
|
||||
|
||||
/* Pop a stack frame from the thread. Returns the new stack frame, or
|
||||
* NULL if there are no more frames */
|
||||
GstValue *gst_thread_popframe(Gst *vm, GstThread *thread);
|
||||
|
||||
/* Move the current stack frame over its parent stack frame, allowing
|
||||
* for primitive tail calls. Return new stack. */
|
||||
GstValue *gst_thread_tail(Gst *vm, GstThread *thread);
|
||||
|
||||
#endif // thread_h_INCLUDED
|
||||
|
2
util.h
2
util.h
@ -51,7 +51,7 @@
|
||||
/* Size of stack frame in number of values */
|
||||
#define GST_FRAME_SIZE 5
|
||||
|
||||
/* Macros for referencing that a stack frame given a stack */
|
||||
/* Macros for referencing 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])
|
||||
|
393
vm.c
393
vm.c
@ -3,6 +3,7 @@
|
||||
#include "value.h"
|
||||
#include "ds.h"
|
||||
#include "gc.h"
|
||||
#include "thread.h"
|
||||
|
||||
/* Macros for errors in the vm */
|
||||
|
||||
@ -23,40 +24,6 @@ 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";
|
||||
|
||||
/* Load a function into the VM. The function will be called with
|
||||
* no arguments when run */
|
||||
static void gst_load(Gst *vm, GstValue callee) {
|
||||
uint32_t startCapacity;
|
||||
uint32_t locals, i;
|
||||
uint16_t *pc;
|
||||
GstValue *stack;
|
||||
GstThread *thread = gst_alloc(vm, sizeof(GstThread));
|
||||
if (callee.type == GST_FUNCTION) {
|
||||
locals = callee.data.function->def->locals;
|
||||
pc = callee.data.function->def->byteCode;
|
||||
} else if (callee.type == GST_CFUNCTION) {
|
||||
locals = 0;
|
||||
pc = NULL;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
startCapacity = locals + GST_FRAME_SIZE + 10;
|
||||
thread->data = gst_alloc(vm, sizeof(GstValue) * startCapacity);
|
||||
thread->capacity = startCapacity;
|
||||
thread->count = GST_FRAME_SIZE;
|
||||
vm->thread = thread;
|
||||
stack = thread->data + GST_FRAME_SIZE;
|
||||
gst_frame_prevsize(stack) = 0;
|
||||
gst_frame_size(stack) = locals;
|
||||
gst_frame_callee(stack) = callee;
|
||||
gst_frame_env(stack) = NULL;
|
||||
gst_frame_errjmp(stack) = NULL;
|
||||
gst_frame_pc(stack) = pc;
|
||||
/* Nil arguments */
|
||||
for (i = 0; i < locals; ++i)
|
||||
stack[i].type = GST_NIL;
|
||||
}
|
||||
|
||||
/* Contextual macro to state in function with VM */
|
||||
#define GST_STATE_SYNC() do { \
|
||||
thread = *vm->thread; \
|
||||
@ -358,7 +325,6 @@ static int gst_continue_size(Gst *vm, uint32_t stackBase) {
|
||||
case GST_OP_ERR: /* Throw error */
|
||||
vm->ret = stack[pc[1]];
|
||||
goto vm_error;
|
||||
break;
|
||||
|
||||
case GST_OP_TRY: /* Begin try block */
|
||||
gst_frame_errloc(stack) = pc[1];
|
||||
@ -372,201 +338,84 @@ static int gst_continue_size(Gst *vm, uint32_t stackBase) {
|
||||
break;
|
||||
|
||||
case GST_OP_RTN: /* Return nil */
|
||||
vm->ret.type = GST_NIL;
|
||||
goto ret;
|
||||
stack = gst_thread_popframe(vm, &thread);
|
||||
if (thread.count < stackBase) {
|
||||
vm->ret.type = GST_NIL;
|
||||
return GST_RETURN_OK;
|
||||
}
|
||||
stack[gst_frame_ret(stack)].type = GST_NIL;
|
||||
break;
|
||||
|
||||
case GST_OP_RET: /* Return */
|
||||
vm->ret = stack[pc[1]];
|
||||
goto ret;
|
||||
|
||||
case GST_OP_PSH: /* Push stack frame */
|
||||
{
|
||||
GstValue *nextStack;
|
||||
uint32_t argSlots, fullArgSlots, arity, prefixCount, varArgs, tupleCount, i, locals, nextCount;
|
||||
|
||||
/* Get arguments to op */
|
||||
temp = stack[pc[1]];
|
||||
arity = pc[2];
|
||||
|
||||
/* Get the size of next stack frame */
|
||||
prefixCount = 0;
|
||||
recur:
|
||||
switch(temp.type) {
|
||||
default: gst_error(vm, GST_EXPECTED_FUNCTION);
|
||||
case GST_FUNCTION:
|
||||
{
|
||||
GstFunction *fn = temp.data.function;
|
||||
locals = fn->def->locals;
|
||||
varArgs = fn->def->flags & GST_FUNCDEF_FLAG_VARARG;
|
||||
argSlots = fn->def->arity;
|
||||
if (arity + prefixCount > argSlots) {
|
||||
fullArgSlots = argSlots;
|
||||
tupleCount = arity + prefixCount - fullArgSlots;
|
||||
} else {
|
||||
fullArgSlots = arity + prefixCount;
|
||||
tupleCount = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case GST_CFUNCTION:
|
||||
{
|
||||
locals = argSlots = fullArgSlots = arity + prefixCount;
|
||||
varArgs = tupleCount = 0;
|
||||
break;
|
||||
}
|
||||
case GST_OBJECT:
|
||||
{
|
||||
GstObject *meta = temp.data.object->meta;
|
||||
if (meta == NULL) gst_error(vm, GST_EXPECTED_FUNCTION);
|
||||
vm->tempArray[prefixCount++] = temp;
|
||||
temp = gst_object_get_cstring(meta, "call");
|
||||
goto recur;
|
||||
}
|
||||
case GST_USERDATA:
|
||||
{
|
||||
GstObject *meta = ((GstUserdataHeader *)temp.data.pointer - 1)->meta;
|
||||
vm->tempArray[prefixCount++] = temp;
|
||||
temp = gst_object_get_cstring(meta, "call");
|
||||
goto recur;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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 prefix args to stack */
|
||||
for (i = 0; i < prefixCount; ++i)
|
||||
nextStack[i] = vm->tempArray[i];
|
||||
|
||||
/* Write arguments to new stack */
|
||||
for (; i < fullArgSlots; ++i)
|
||||
nextStack[i] = stack[pc[3 + i - prefixCount]];
|
||||
|
||||
/* Clear rest of 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, tupleCount);
|
||||
for (j = argSlots; j < arity; ++j)
|
||||
if (j < prefixCount)
|
||||
tuple[j - argSlots] = vm->tempArray[j - prefixCount];
|
||||
else
|
||||
tuple[j - argSlots] = stack[pc[3 + j - prefixCount]];
|
||||
nextStack[argSlots].type = GST_TUPLE;
|
||||
nextStack[argSlots].data.tuple = tuple;
|
||||
}
|
||||
|
||||
/* Increment pc */
|
||||
pc += 3 + arity;
|
||||
temp = stack[pc[1]];
|
||||
stack = gst_thread_popframe(vm, &thread);
|
||||
if (thread.count < stackBase) {
|
||||
vm->ret = temp;
|
||||
return GST_RETURN_OK;
|
||||
}
|
||||
stack[gst_frame_ret(stack)] = temp;
|
||||
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();
|
||||
vm->ret.type = GST_NIL;
|
||||
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;
|
||||
|
||||
/* 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 */
|
||||
ret:
|
||||
/* Check for closure */
|
||||
pop_frame(GST_RETURN_OK);
|
||||
pc = gst_frame_pc(stack);
|
||||
stack[gst_frame_ret(stack)] = vm->ret;
|
||||
break;
|
||||
{
|
||||
GstValue *oldStack;
|
||||
temp = stack[pc[1]];
|
||||
uint32_t i, arity, offset, size;
|
||||
uint16_t ret = pc[2];
|
||||
offset = (*pc == GST_OP_CAL) ? 4 : 3;
|
||||
arity = pc[offset - 1];
|
||||
/* Push new frame */
|
||||
stack = gst_thread_beginframe(vm, &thread, temp, arity);
|
||||
oldStack = stack - GST_FRAME_SIZE - gst_frame_prevsize(stack);
|
||||
/* Write arguments */
|
||||
size = gst_frame_size(stack);
|
||||
for (i = 0; i < arity; ++i)
|
||||
stack[i + size - arity] = oldStack[pc[offset + i]];
|
||||
/* Finish new frame */
|
||||
gst_thread_endframe(vm, &thread);
|
||||
/* Check tail call - if so, replace frame. */
|
||||
if (*pc == GST_OP_TCL) {
|
||||
stack = gst_thread_tail(vm, &thread);
|
||||
} else {
|
||||
gst_frame_ret(oldStack) = ret;
|
||||
}
|
||||
/* Call function */
|
||||
temp = gst_frame_callee(stack);
|
||||
if (temp.type == GST_FUNCTION) {
|
||||
/* Save pc and set new pc */
|
||||
gst_frame_pc(oldStack) = pc + offset + arity;
|
||||
pc = temp.data.function->def->byteCode;
|
||||
} else {
|
||||
int status;
|
||||
GST_STATE_WRITE();
|
||||
vm->ret.type = GST_NIL;
|
||||
status = temp.data.cfunction(vm);
|
||||
GST_STATE_SYNC();
|
||||
stack = gst_thread_popframe(vm, &thread);
|
||||
pc += offset + arity;
|
||||
if (status == GST_RETURN_OK)
|
||||
if (thread.count < stackBase)
|
||||
return status;
|
||||
else
|
||||
stack[gst_frame_ret(stack)] = vm->ret;
|
||||
else
|
||||
goto vm_error;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
/* Handle errors from c functions and vm opcodes */
|
||||
vm_error:
|
||||
while (gst_frame_errjmp(stack) == NULL)
|
||||
pop_frame(GST_RETURN_ERROR);
|
||||
while (gst_frame_errjmp(stack) == NULL) {
|
||||
stack = gst_thread_popframe(vm, &thread);
|
||||
if (thread.count < stackBase)
|
||||
return GST_RETURN_ERROR;
|
||||
}
|
||||
pc = gst_frame_errjmp(stack);
|
||||
stack[gst_frame_errloc(stack)] = vm->ret;
|
||||
break;
|
||||
|
||||
#undef pop_frame
|
||||
|
||||
} /* end switch */
|
||||
|
||||
/* TODO: Move collection only to places that allocate memory */
|
||||
@ -584,106 +433,34 @@ int gst_continue(Gst *vm) {
|
||||
}
|
||||
|
||||
/* Run the vm with a given function */
|
||||
int gst_run(Gst *vm, GstValue func) {
|
||||
gst_load(vm, func);
|
||||
int gst_run(Gst *vm, GstValue callee) {
|
||||
vm->thread = gst_thread(vm, callee, 64);
|
||||
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 */
|
||||
/* Call a gst function */
|
||||
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);
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* 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)
|
||||
uint32_t i;
|
||||
stack = gst_thread_beginframe(vm, vm->thread, callee, arity);
|
||||
for (i = 0; i < arity; ++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;
|
||||
}
|
||||
gst_thread_endframe(vm, vm->thread);
|
||||
callee = gst_frame_callee(stack);
|
||||
if (callee.type == GST_FUNCTION) {
|
||||
return gst_continue(vm);
|
||||
} else {
|
||||
int status;
|
||||
vm->ret.type = GST_NIL;
|
||||
status = callee.data.cfunction(vm);
|
||||
gst_thread_popframe(vm, vm->thread);
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
/* Get an argument from the stack */
|
||||
GstValue gst_arg(Gst *vm, uint16_t index) {
|
||||
GstValue *stack = vm->thread->data + vm->thread->count;
|
||||
GstValue *stack = gst_thread_stack(vm->thread);
|
||||
uint16_t frameSize = gst_frame_size(stack);
|
||||
if (frameSize <= index) {
|
||||
GstValue ret;
|
||||
@ -695,7 +472,7 @@ GstValue gst_arg(Gst *vm, uint16_t index) {
|
||||
|
||||
/* Put a value on the stack */
|
||||
void gst_set_arg(Gst* vm, uint16_t index, GstValue x) {
|
||||
GstValue *stack = vm->thread->data + vm->thread->count;
|
||||
GstValue *stack = gst_thread_stack(vm->thread);
|
||||
uint16_t frameSize = gst_frame_size(stack);
|
||||
if (frameSize <= index) return;
|
||||
stack[index] = x;
|
||||
@ -703,7 +480,7 @@ void gst_set_arg(Gst* vm, uint16_t index, GstValue x) {
|
||||
|
||||
/* Get the size of the VMStack */
|
||||
uint16_t gst_count_args(Gst *vm) {
|
||||
GstValue *stack = vm->thread->data + vm->thread->count;
|
||||
GstValue *stack = gst_thread_stack(vm->thread);
|
||||
return gst_frame_size(stack);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user