mirror of
https://github.com/janet-lang/janet
synced 2024-11-28 02:59:54 +00:00
Redo function call bytecode interface to be simpler and allow
for an apply like structure in the language
This commit is contained in:
parent
f4a6f4073f
commit
01e8749f39
@ -42,6 +42,10 @@ int debug_compile_and_run(Gst *vm, GstValue ast, GstValue env) {
|
|||||||
printf("Compiler error: %s\n", c.error);
|
printf("Compiler error: %s\n", c.error);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
/* Print disasm */
|
||||||
|
printf("%c[31m===== Begin Disassembly =====\n", 27);
|
||||||
|
gst_dasm_function(stdout, func.data.function);
|
||||||
|
printf("===== End Disassembly =====%c[0m\n", 27);
|
||||||
/* Execute function */
|
/* Execute function */
|
||||||
if (gst_run(vm, func)) {
|
if (gst_run(vm, func)) {
|
||||||
if (vm->crash) {
|
if (vm->crash) {
|
||||||
@ -132,7 +136,7 @@ int debug_repl(Gst *vm) {
|
|||||||
/* Add _ to environemt */
|
/* Add _ to environemt */
|
||||||
st = gst_struct_begin(vm, 1);
|
st = gst_struct_begin(vm, 1);
|
||||||
gst_struct_put(st, gst_string_cv(vm, "_"), vm->ret);
|
gst_struct_put(st, gst_string_cv(vm, "_"), vm->ret);
|
||||||
if (0 == debug_compile_and_run(vm, gst_parse_consume(&p), gst_wrap_struct(gst_struct_end(vm, st)))) {
|
if (!debug_compile_and_run(vm, gst_parse_consume(&p), gst_wrap_struct(gst_struct_end(vm, st)))) {
|
||||||
printf("%s\n", gst_to_string(vm, vm->ret));
|
printf("%s\n", gst_to_string(vm, vm->ret));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -684,71 +684,6 @@ static Slot compile_if(GstCompiler *c, FormOptions opts, const GstValue *form) {
|
|||||||
return condition;
|
return condition;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Try catch special */
|
|
||||||
static Slot compile_try(GstCompiler *c, FormOptions opts, const GstValue *form) {
|
|
||||||
GstScope *scope = c->tail;
|
|
||||||
GstBuffer *buffer = c->buffer;
|
|
||||||
Slot body;
|
|
||||||
uint16_t errorIndex;
|
|
||||||
uint32_t countAtTry, countTemp, countAtJump;
|
|
||||||
countAtJump = 0;
|
|
||||||
/* Check argument count */
|
|
||||||
if (gst_tuple_length(form) < 3 || gst_tuple_length(form) > 4)
|
|
||||||
c_error(c, "try takes either 2 or 3 arguments");
|
|
||||||
/* Check for symbol to bind error to */
|
|
||||||
if (form[1].type != GST_STRING)
|
|
||||||
c_error(c, "expected string at start of try");
|
|
||||||
/* Add subscope for error variable */
|
|
||||||
GstScope *subScope = compiler_push_scope(c, 1);
|
|
||||||
errorIndex = compiler_declare_symbol(c, subScope, form[1]);
|
|
||||||
/* Leave space for try instruction */
|
|
||||||
countAtTry = buffer->count;
|
|
||||||
buffer->count += sizeof(uint32_t) + 2 * sizeof(uint16_t);
|
|
||||||
/* Compile the body */
|
|
||||||
body = compile_value(c, opts, form[2]);
|
|
||||||
if (opts.isTail) {
|
|
||||||
compiler_return(c, body);
|
|
||||||
} else {
|
|
||||||
/* If we need to jump over the catch, do so */
|
|
||||||
if (gst_tuple_length(form) == 4) {
|
|
||||||
countAtJump = buffer->count;
|
|
||||||
buffer->count += sizeof(int32_t) + sizeof(uint16_t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* Reinsert try jump with correct index */
|
|
||||||
countTemp = buffer->count;
|
|
||||||
buffer->count = countAtTry;
|
|
||||||
gst_buffer_push_u16(c->vm, buffer, GST_OP_TRY);
|
|
||||||
gst_buffer_push_u16(c->vm, buffer, errorIndex);
|
|
||||||
gst_buffer_push_i32(c->vm, buffer, (countTemp - countAtTry) / 2);
|
|
||||||
buffer->count = countTemp;
|
|
||||||
/* Compile catch path */
|
|
||||||
if (gst_tuple_length(form) == 4) {
|
|
||||||
Slot catch;
|
|
||||||
countAtJump = buffer->count;
|
|
||||||
catch = compile_value(c, opts, form[3]);
|
|
||||||
if (opts.isTail) compiler_return(c, catch);
|
|
||||||
compiler_drop_slot(c, scope, catch);
|
|
||||||
} else if (opts.isTail) {
|
|
||||||
compiler_return(c, nil_slot());
|
|
||||||
}
|
|
||||||
/* Reset the second jump length */
|
|
||||||
if (!opts.isTail && gst_tuple_length(form) == 4) {
|
|
||||||
countTemp = buffer->count;
|
|
||||||
buffer->count = countAtJump;
|
|
||||||
gst_buffer_push_u16(c->vm, buffer, GST_OP_JMP);
|
|
||||||
gst_buffer_push_i32(c->vm, buffer, (countTemp - countAtJump) / 2);
|
|
||||||
buffer->count = countTemp;
|
|
||||||
}
|
|
||||||
/* Untry */
|
|
||||||
gst_buffer_push_u16(c->vm, buffer, GST_OP_UTY);
|
|
||||||
/* Pop the error scope */
|
|
||||||
compiler_pop_scope(c);
|
|
||||||
if (opts.isTail)
|
|
||||||
body.hasReturned = 1;
|
|
||||||
return body;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* While special */
|
/* While special */
|
||||||
static Slot compile_while(GstCompiler *c, FormOptions opts, const GstValue *form) {
|
static Slot compile_while(GstCompiler *c, FormOptions opts, const GstValue *form) {
|
||||||
Slot cond;
|
Slot cond;
|
||||||
@ -885,14 +820,6 @@ static SpecialFormHelper get_special(const GstValue *form) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 't':
|
|
||||||
{
|
|
||||||
if (gst_string_length(name) == 3 &&
|
|
||||||
name[1] == 'r' &&
|
|
||||||
name[2] == 'y') {
|
|
||||||
return compile_try;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case 'w':
|
case 'w':
|
||||||
{
|
{
|
||||||
if (gst_string_length(name) == 5 &&
|
if (gst_string_length(name) == 5 &&
|
||||||
@ -991,11 +918,15 @@ static Slot compile_form(GstCompiler *c, FormOptions opts, const 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);
|
||||||
|
/* Prepare next stack frame */
|
||||||
|
gst_buffer_push_u16(c->vm, buffer, GST_OP_PSK);
|
||||||
|
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);
|
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.hasReturned = 1;
|
||||||
ret.isNil = 1;
|
ret.isNil = 1;
|
||||||
} else {
|
} else {
|
||||||
@ -1003,10 +934,7 @@ static Slot compile_form(GstCompiler *c, FormOptions opts, const GstValue *form)
|
|||||||
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, 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -191,29 +191,23 @@ 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_ERR:
|
|
||||||
current += dasm_fixed_op(out, current, "error", 1);
|
|
||||||
break;
|
|
||||||
case GST_OP_TRY:
|
|
||||||
dasm_print_arg(out, "try");
|
|
||||||
dasm_print_slot(out, current[1]);
|
|
||||||
dasm_print_i32(out, *(int32_t *)(current + 2));
|
|
||||||
current += 4;
|
|
||||||
break;
|
|
||||||
case GST_OP_UTY:
|
|
||||||
current += dasm_fixed_op(out, current, "untry", 0);
|
|
||||||
break;
|
|
||||||
case GST_OP_RET:
|
case GST_OP_RET:
|
||||||
current += dasm_fixed_op(out, current, "return", 1);
|
current += dasm_fixed_op(out, current, "return", 1);
|
||||||
break;
|
break;
|
||||||
case GST_OP_RTN:
|
case GST_OP_RTN:
|
||||||
current += dasm_fixed_op(out, current, "returnNil", 0);
|
current += dasm_fixed_op(out, current, "returnNil", 0);
|
||||||
break;
|
break;
|
||||||
|
case GST_OP_PSK:
|
||||||
|
current += dasm_varg_op(out, current, "pushArgs", 0);
|
||||||
|
break;
|
||||||
|
case GST_OP_PAR:
|
||||||
|
current += dasm_fixed_op(out, current, "pushSeq", 1);
|
||||||
|
break;
|
||||||
case GST_OP_CAL:
|
case GST_OP_CAL:
|
||||||
current += dasm_varg_op(out, current, "call", 2);
|
current += dasm_fixed_op(out, current, "call", 2);
|
||||||
break;
|
break;
|
||||||
case GST_OP_TCL:
|
case GST_OP_TCL:
|
||||||
current += dasm_varg_op(out, current, "tailCall", 1);
|
current += dasm_fixed_op(out, current, "tailCall", 1);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
fprintf(out, "\n");
|
fprintf(out, "\n");
|
||||||
|
@ -267,7 +267,9 @@ void gst_collect(Gst *vm) {
|
|||||||
/* Thread can be null */
|
/* Thread can be null */
|
||||||
if (vm->thread)
|
if (vm->thread)
|
||||||
gst_mark_value(vm, gst_wrap_thread(vm->thread));
|
gst_mark_value(vm, gst_wrap_thread(vm->thread));
|
||||||
|
if (vm->modules)
|
||||||
gst_mark_value(vm, gst_wrap_object(vm->modules));
|
gst_mark_value(vm, gst_wrap_object(vm->modules));
|
||||||
|
if (vm->registry)
|
||||||
gst_mark_value(vm, gst_wrap_object(vm->registry));
|
gst_mark_value(vm, gst_wrap_object(vm->registry));
|
||||||
gst_mark_value(vm, vm->ret);
|
gst_mark_value(vm, vm->ret);
|
||||||
if (vm->scratch)
|
if (vm->scratch)
|
||||||
|
@ -39,7 +39,7 @@
|
|||||||
* Byte 208: Array - [u32 length]*[value... elements]
|
* Byte 208: Array - [u32 length]*[value... elements]
|
||||||
* Byte 209: Tuple - [u32 length]*[value... elements]
|
* Byte 209: Tuple - [u32 length]*[value... elements]
|
||||||
* Byte 210: Thread - [u8 state][u32 frames]*[[value callee][value env]
|
* Byte 210: Thread - [u8 state][u32 frames]*[[value callee][value env]
|
||||||
* [u32 pcoffset][u32 erroffset][u16 ret][u16 errloc][u16 size]*[value ...stack]
|
* [u32 pcoffset][u16 ret][u16 args][u16 size]*[value ...stack]
|
||||||
* Byte 211: Object - [value parent][u32 length]*2*[value... kvs]
|
* Byte 211: Object - [value parent][u32 length]*2*[value... kvs]
|
||||||
* Byte 212: FuncDef - [u32 locals][u32 arity][u32 flags][u32 literallen]*[value...
|
* Byte 212: FuncDef - [u32 locals][u32 arity][u32 flags][u32 literallen]*[value...
|
||||||
* literals][u32 bytecodelen]*[u16... bytecode]
|
* literals][u32 bytecodelen]*[u16... bytecode]
|
||||||
@ -242,8 +242,8 @@ static const char *gst_deserialize_impl(
|
|||||||
/* Add frames */
|
/* Add frames */
|
||||||
for (i = 0; i < length; ++i) {
|
for (i = 0; i < length; ++i) {
|
||||||
GstValue callee, env;
|
GstValue callee, env;
|
||||||
uint32_t pcoffset, erroffset;
|
uint32_t pcoffset;
|
||||||
uint16_t ret, errloc, size, j;
|
uint16_t ret, args, size, j;
|
||||||
/* Create a new frame */
|
/* Create a new frame */
|
||||||
if (i > 0)
|
if (i > 0)
|
||||||
gst_thread_beginframe(vm, t, nil, 0);
|
gst_thread_beginframe(vm, t, nil, 0);
|
||||||
@ -253,20 +253,18 @@ static const char *gst_deserialize_impl(
|
|||||||
err = gst_deserialize_impl(vm, data, end, &data, visited, &env);
|
err = gst_deserialize_impl(vm, data, end, &data, visited, &env);
|
||||||
if (err != NULL) return err;
|
if (err != NULL) return err;
|
||||||
read_u32(pcoffset);
|
read_u32(pcoffset);
|
||||||
read_u32(erroffset);
|
|
||||||
read_u16(ret);
|
read_u16(ret);
|
||||||
read_u16(errloc);
|
read_u16(args);
|
||||||
read_u16(size);
|
read_u16(size);
|
||||||
/* Set up the stack */
|
/* Set up the stack */
|
||||||
stack = gst_thread_stack(t);
|
stack = gst_thread_stack(t);
|
||||||
if (callee.type == GST_FUNCTION) {
|
if (callee.type == GST_FUNCTION) {
|
||||||
gst_frame_pc(stack) = callee.data.function->def->byteCode + pcoffset;
|
gst_frame_pc(stack) = callee.data.function->def->byteCode + pcoffset;
|
||||||
gst_frame_errjmp(stack) = callee.data.function->def->byteCode + erroffset;
|
|
||||||
if (env.type == GST_FUNCENV)
|
if (env.type == GST_FUNCENV)
|
||||||
gst_frame_env(stack) = env.data.env;
|
gst_frame_env(stack) = env.data.env;
|
||||||
}
|
}
|
||||||
gst_frame_ret(stack) = ret;
|
gst_frame_ret(stack) = ret;
|
||||||
gst_frame_errloc(stack) = errloc;
|
gst_frame_args(stack) = args;
|
||||||
gst_frame_size(stack) = size;
|
gst_frame_size(stack) = size;
|
||||||
gst_frame_prevsize(stack) = prevsize;
|
gst_frame_prevsize(stack) = prevsize;
|
||||||
prevsize = size;
|
prevsize = size;
|
||||||
|
@ -249,7 +249,7 @@ int gst_stl_object(Gst *vm) {
|
|||||||
GstObject *object;
|
GstObject *object;
|
||||||
if (count % 2 != 0)
|
if (count % 2 != 0)
|
||||||
gst_c_throwc(vm, "expected even number of arguments");
|
gst_c_throwc(vm, "expected even number of arguments");
|
||||||
object = gst_object(vm, count * 2);
|
object = gst_object(vm, 4 * count);
|
||||||
for (i = 0; i < count; i += 2)
|
for (i = 0; i < count; i += 2)
|
||||||
gst_object_put(vm, object, gst_arg(vm, i), gst_arg(vm, i + 1));
|
gst_object_put(vm, object, gst_arg(vm, i), gst_arg(vm, i + 1));
|
||||||
gst_c_return(vm, gst_wrap_object(object));
|
gst_c_return(vm, gst_wrap_object(object));
|
||||||
@ -457,6 +457,7 @@ static const GstModuleItem const std_module[] = {
|
|||||||
{"exit", gst_stl_exit},
|
{"exit", gst_stl_exit},
|
||||||
{"rawget", gst_stl_rawget},
|
{"rawget", gst_stl_rawget},
|
||||||
{"rawset", gst_stl_rawset},
|
{"rawset", gst_stl_rawset},
|
||||||
|
{"next", gst_stl_next},
|
||||||
{"error", gst_stl_error},
|
{"error", gst_stl_error},
|
||||||
{"serialize", gst_stl_serialize},
|
{"serialize", gst_stl_serialize},
|
||||||
{"open", gst_stl_open},
|
{"open", gst_stl_open},
|
||||||
|
@ -36,10 +36,9 @@ GstThread *gst_thread(Gst *vm, GstValue callee, uint32_t capacity) {
|
|||||||
gst_frame_size(stack) = 0;
|
gst_frame_size(stack) = 0;
|
||||||
gst_frame_prevsize(stack) = 0;
|
gst_frame_prevsize(stack) = 0;
|
||||||
gst_frame_ret(stack) = 0;
|
gst_frame_ret(stack) = 0;
|
||||||
gst_frame_errloc(stack) = 0;
|
gst_frame_args(stack) = 0;
|
||||||
gst_frame_pc(stack) = NULL;
|
gst_frame_pc(stack) = NULL;
|
||||||
gst_frame_env(stack) = NULL;
|
gst_frame_env(stack) = NULL;
|
||||||
gst_frame_errjmp(stack) = NULL;
|
|
||||||
gst_frame_callee(stack) = callee;
|
gst_frame_callee(stack) = callee;
|
||||||
gst_thread_endframe(vm, thread);
|
gst_thread_endframe(vm, thread);
|
||||||
thread->parent = NULL;
|
thread->parent = NULL;
|
||||||
@ -115,7 +114,6 @@ GstValue *gst_thread_beginframe(Gst *vm, GstThread *thread, GstValue callee, uin
|
|||||||
newStack = oldStack + frameOffset;
|
newStack = oldStack + frameOffset;
|
||||||
gst_frame_prevsize(newStack) = gst_frame_size(oldStack);
|
gst_frame_prevsize(newStack) = gst_frame_size(oldStack);
|
||||||
gst_frame_env(newStack) = NULL;
|
gst_frame_env(newStack) = NULL;
|
||||||
gst_frame_errjmp(newStack) = NULL;
|
|
||||||
gst_frame_size(newStack) = 0;
|
gst_frame_size(newStack) = 0;
|
||||||
gst_frame_callee(newStack) = callee;
|
gst_frame_callee(newStack) = callee;
|
||||||
thread->count += frameOffset;
|
thread->count += frameOffset;
|
||||||
@ -144,6 +142,8 @@ void gst_thread_endframe(Gst *vm, GstThread *thread) {
|
|||||||
gst_thread_pushnil(vm, thread, locals - gst_frame_size(stack));
|
gst_thread_pushnil(vm, thread, locals - gst_frame_size(stack));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
stack = thread->data + thread->count;
|
||||||
|
gst_frame_args(stack) = gst_frame_size(stack) + GST_FRAME_SIZE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,44 +173,3 @@ GstValue *gst_thread_popframe(Gst *vm, GstThread *thread) {
|
|||||||
else
|
else
|
||||||
return NULL;
|
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;
|
|
||||||
}
|
|
||||||
|
207
core/vm.c
207
core/vm.c
@ -22,33 +22,23 @@
|
|||||||
|
|
||||||
#include <gst/gst.h>
|
#include <gst/gst.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) do { (vm)->ret = gst_string_cv((vm), (e)); goto vm_error; } while (0)
|
|
||||||
|
|
||||||
/* Crash. Not catchable, unlike error. */
|
|
||||||
#define gst_crash(vm, e) return ((vm)->crash = (e), GST_RETURN_CRASH)
|
|
||||||
|
|
||||||
/* Error if the condition is false */
|
|
||||||
#define gst_assert(vm, cond, e) do {if (!(cond)){gst_error((vm), (e));}} while (0)
|
|
||||||
|
|
||||||
static const char GST_NO_UPVALUE[] = "no upvalue";
|
static const char GST_NO_UPVALUE[] = "no upvalue";
|
||||||
static const char GST_EXPECTED_FUNCTION[] = "expected function";
|
static const char GST_EXPECTED_FUNCTION[] = "expected function";
|
||||||
static const char GST_EXPECTED_NUMBER_ROP[] = "expected right operand to be number";
|
static const char GST_EXPECTED_NUMBER_ROP[] = "expected right operand to be number";
|
||||||
static const char GST_EXPECTED_NUMBER_LOP[] = "expected left operand to be number";
|
static const char GST_EXPECTED_NUMBER_LOP[] = "expected left operand to be number";
|
||||||
|
|
||||||
/* Start running the VM from where it left off. Continue running
|
/* Start running the VM from where it left off. */
|
||||||
* until the stack size is smaller than minStackSize. */
|
int gst_continue(Gst *vm) {
|
||||||
static int gst_continue_size(Gst *vm, uint32_t stackBase) {
|
|
||||||
/* VM state */
|
/* VM state */
|
||||||
GstValue *stack;
|
GstValue *stack;
|
||||||
GstValue temp, v1, v2;
|
GstValue temp, v1, v2;
|
||||||
uint16_t *pc;
|
uint16_t *pc;
|
||||||
|
|
||||||
|
#define gst_exit(vm, r) return ((vm)->ret = (r), GST_RETURN_OK)
|
||||||
|
#define gst_error(vm, e) do { (vm)->ret = gst_string_cv((vm), (e)); goto vm_error; } while (0)
|
||||||
|
#define gst_crash(vm, e) return ((vm)->crash = (e), GST_RETURN_CRASH)
|
||||||
|
#define gst_assert(vm, cond, e) do {if (!(cond)){gst_error((vm), (e));}} while (0)
|
||||||
|
|
||||||
/* Intialize local state */
|
/* Intialize local state */
|
||||||
stack = vm->thread->data + vm->thread->count;
|
stack = vm->thread->data + vm->thread->count;
|
||||||
pc = gst_frame_pc(stack);
|
pc = gst_frame_pc(stack);
|
||||||
@ -188,20 +178,9 @@ static int gst_continue_size(Gst *vm, uint32_t stackBase) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GST_OP_TRY: /* Begin try block */
|
|
||||||
gst_frame_errloc(stack) = pc[1];
|
|
||||||
gst_frame_errjmp(stack) = pc + *(uint32_t *)(pc + 2);
|
|
||||||
pc += 4;
|
|
||||||
continue;
|
|
||||||
|
|
||||||
case GST_OP_UTY: /* End try block */
|
|
||||||
gst_frame_errjmp(stack) = NULL;
|
|
||||||
pc++;
|
|
||||||
continue;
|
|
||||||
|
|
||||||
case GST_OP_RTN: /* Return nil */
|
case GST_OP_RTN: /* Return nil */
|
||||||
stack = gst_thread_popframe(vm, vm->thread);
|
stack = gst_thread_popframe(vm, vm->thread);
|
||||||
if (vm->thread->count < stackBase) {
|
if (vm->thread->count < GST_FRAME_SIZE) {
|
||||||
vm->ret.type = GST_NIL;
|
vm->ret.type = GST_NIL;
|
||||||
return GST_RETURN_OK;
|
return GST_RETURN_OK;
|
||||||
}
|
}
|
||||||
@ -212,7 +191,7 @@ static int gst_continue_size(Gst *vm, uint32_t stackBase) {
|
|||||||
case GST_OP_RET: /* Return */
|
case GST_OP_RET: /* Return */
|
||||||
temp = stack[pc[1]];
|
temp = stack[pc[1]];
|
||||||
stack = gst_thread_popframe(vm, vm->thread);
|
stack = gst_thread_popframe(vm, vm->thread);
|
||||||
if (vm->thread->count < stackBase) {
|
if (vm->thread->count < GST_FRAME_SIZE) {
|
||||||
vm->ret = temp;
|
vm->ret = temp;
|
||||||
return GST_RETURN_OK;
|
return GST_RETURN_OK;
|
||||||
}
|
}
|
||||||
@ -220,71 +199,132 @@ static int gst_continue_size(Gst *vm, uint32_t stackBase) {
|
|||||||
stack[gst_frame_ret(stack)] = temp;
|
stack[gst_frame_ret(stack)] = temp;
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
case GST_OP_PSK: /* Push stack */
|
||||||
|
{
|
||||||
|
uint16_t arity = pc[1];
|
||||||
|
uint16_t i;
|
||||||
|
uint16_t newBase = gst_frame_size(stack) + GST_FRAME_SIZE;
|
||||||
|
gst_frame_args(stack) = newBase;
|
||||||
|
gst_thread_ensure_extra(vm, vm->thread, GST_FRAME_SIZE + arity);
|
||||||
|
stack = gst_thread_stack(vm->thread);
|
||||||
|
gst_frame_size(stack) += GST_FRAME_SIZE + arity;
|
||||||
|
/* Nil stuff */
|
||||||
|
for (i = 0; i < GST_FRAME_SIZE; ++i)
|
||||||
|
stack[newBase + i - GST_FRAME_SIZE].type = GST_NIL;
|
||||||
|
/* Write arguments */
|
||||||
|
for (i = 0; i < arity; ++i)
|
||||||
|
stack[newBase + i] = stack[pc[2 + i]];
|
||||||
|
pc += 2 + arity;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GST_OP_PAR: /* Push array or tuple */
|
||||||
|
{
|
||||||
|
uint32_t count, i, oldsize;
|
||||||
|
const GstValue *data;
|
||||||
|
temp = stack[pc[1]];
|
||||||
|
if (temp.type == GST_TUPLE) {
|
||||||
|
count = gst_tuple_length(temp.data.tuple);
|
||||||
|
data = temp.data.tuple;
|
||||||
|
} else if (temp.type == GST_ARRAY){
|
||||||
|
count = temp.data.array->count;
|
||||||
|
data = temp.data.array->data;
|
||||||
|
} else {
|
||||||
|
gst_error(vm, "expected array or tuple");
|
||||||
|
}
|
||||||
|
oldsize = gst_frame_size(stack);
|
||||||
|
gst_thread_pushnil(vm, vm->thread, count);
|
||||||
|
stack = gst_thread_stack(vm->thread);
|
||||||
|
for (i = 0; i < count; ++i)
|
||||||
|
stack[oldsize + i] = data[i];
|
||||||
|
pc += 2;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case GST_OP_CAL: /* Call */
|
case GST_OP_CAL: /* Call */
|
||||||
|
{
|
||||||
|
uint16_t newStackIndex = gst_frame_args(stack);
|
||||||
|
uint16_t size = gst_frame_size(stack);
|
||||||
|
temp = stack[pc[1]];
|
||||||
|
gst_frame_ret(stack) = pc[2];
|
||||||
|
gst_frame_pc(stack) = pc + 3;
|
||||||
|
if (newStackIndex < GST_FRAME_SIZE)
|
||||||
|
gst_error(vm, "invalid call instruction");
|
||||||
|
vm->thread->count += newStackIndex;
|
||||||
|
stack = gst_thread_stack(vm->thread);
|
||||||
|
gst_frame_size(stack) = size - newStackIndex;
|
||||||
|
gst_frame_prevsize(stack) = newStackIndex;
|
||||||
|
gst_frame_callee(stack) = temp;
|
||||||
|
}
|
||||||
|
goto common_function_call;
|
||||||
|
|
||||||
case GST_OP_TCL: /* Tail call */
|
case GST_OP_TCL: /* Tail call */
|
||||||
{
|
{
|
||||||
GstValue *oldStack;
|
uint16_t newStackIndex = gst_frame_args(stack);
|
||||||
|
uint16_t size = gst_frame_size(stack);
|
||||||
|
uint16_t i;
|
||||||
temp = stack[pc[1]];
|
temp = stack[pc[1]];
|
||||||
int isTCall = *pc == GST_OP_TCL;
|
/* Check for closures */
|
||||||
uint32_t i, arity, offset, size;
|
if (gst_frame_env(stack)) {
|
||||||
uint16_t ret = pc[2];
|
GstFuncEnv *env = gst_frame_env(stack);
|
||||||
offset = isTCall ? 3 : 4;
|
env->thread = NULL;
|
||||||
arity = pc[offset - 1];
|
env->stackOffset = size;
|
||||||
/* Push new frame */
|
env->values = gst_alloc(vm, sizeof(GstValue) * size);
|
||||||
if (temp.type != GST_FUNCTION && temp.type != GST_CFUNCTION)
|
gst_memcpy(env->values, stack, sizeof(GstValue) * size);
|
||||||
gst_error(vm, GST_EXPECTED_FUNCTION);
|
|
||||||
stack = gst_thread_beginframe(vm, 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, vm->thread);
|
|
||||||
/* Check tail call - if so, replace frame. */
|
|
||||||
if (isTCall) {
|
|
||||||
stack = gst_thread_tail(vm, vm->thread);
|
|
||||||
} else {
|
|
||||||
gst_frame_ret(oldStack) = ret;
|
|
||||||
}
|
}
|
||||||
/* Call function */
|
if (newStackIndex)
|
||||||
|
for (i = 0; i < size - newStackIndex; ++i)
|
||||||
|
stack[i] = stack[newStackIndex + i];
|
||||||
|
gst_frame_size(stack) = size - newStackIndex;
|
||||||
|
gst_frame_callee(stack) = temp;
|
||||||
|
}
|
||||||
|
goto common_function_call;
|
||||||
|
|
||||||
|
/* Code common to all function calls */
|
||||||
|
common_function_call:
|
||||||
|
gst_frame_args(stack) = 0;
|
||||||
|
gst_frame_env(stack) = NULL;
|
||||||
|
gst_thread_endframe(vm, vm->thread);
|
||||||
|
stack = vm->thread->data + vm->thread->count;
|
||||||
|
gst_frame_args(stack) = 0;
|
||||||
temp = gst_frame_callee(stack);
|
temp = gst_frame_callee(stack);
|
||||||
if (temp.type == GST_FUNCTION) {
|
if (temp.type == GST_FUNCTION) {
|
||||||
/* Save pc and set new pc */
|
|
||||||
if (!isTCall)
|
|
||||||
gst_frame_pc(oldStack) = pc + offset + arity;
|
|
||||||
pc = temp.data.function->def->byteCode;
|
pc = temp.data.function->def->byteCode;
|
||||||
} else {
|
} else if (temp.type == GST_CFUNCTION) {
|
||||||
int status;
|
int status;
|
||||||
gst_frame_pc(stack) = pc;
|
|
||||||
vm->ret.type = GST_NIL;
|
vm->ret.type = GST_NIL;
|
||||||
status = temp.data.cfunction(vm);
|
status = temp.data.cfunction(vm);
|
||||||
stack = gst_thread_popframe(vm, vm->thread);
|
stack = gst_thread_popframe(vm, vm->thread);
|
||||||
if (status == GST_RETURN_OK) {
|
if (status == GST_RETURN_OK) {
|
||||||
if (vm->thread->count < stackBase) {
|
if (vm->thread->count < GST_FRAME_SIZE) {
|
||||||
return status;
|
return status;
|
||||||
} else {
|
} else {
|
||||||
stack[gst_frame_ret(stack)] = vm->ret;
|
stack[gst_frame_ret(stack)] = vm->ret;
|
||||||
if (isTCall)
|
|
||||||
pc = gst_frame_pc(stack);
|
pc = gst_frame_pc(stack);
|
||||||
else
|
|
||||||
pc += offset + arity;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
goto vm_error;
|
goto vm_error;
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
|
gst_error(vm, GST_EXPECTED_FUNCTION);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case GST_OP_YLD: /* Yield to new thread */
|
||||||
|
temp = stack[pc[1]];
|
||||||
|
v1 = stack[pc[2]];
|
||||||
|
gst_assert(vm, v1.type == GST_THREAD, "expected thread");
|
||||||
|
gst_assert(vm, v1.data.thread->status != GST_THREAD_DEAD, "cannot rejoin dead thread");
|
||||||
|
gst_frame_pc(stack) = pc + 3;
|
||||||
|
vm->thread = v1.data.thread;
|
||||||
|
stack = vm->thread->data + vm->thread->count;
|
||||||
|
pc = gst_frame_pc(stack);
|
||||||
|
continue;
|
||||||
|
|
||||||
/* Faster implementations of standard functions
|
/* Faster implementations of standard functions
|
||||||
* These opcodes are nto strictlyre required and can
|
* These opcodes are nto strictlyre required and can
|
||||||
* be reimplemented with stanard library functions */
|
* be reimplemented with stanard library functions */
|
||||||
|
|
||||||
case GST_OP_ERR: /* Throw error */
|
|
||||||
vm->ret = stack[pc[1]];
|
|
||||||
goto vm_error;
|
|
||||||
|
|
||||||
#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]]; \
|
||||||
@ -375,7 +415,7 @@ static int gst_continue_size(Gst *vm, uint32_t stackBase) {
|
|||||||
{
|
{
|
||||||
uint32_t i = 3;
|
uint32_t i = 3;
|
||||||
uint32_t kvs = pc[2];
|
uint32_t kvs = pc[2];
|
||||||
GstObject *o = gst_object(vm, 2 * kvs + 2);
|
GstObject *o = gst_object(vm, 2 * kvs);
|
||||||
kvs = kvs + 3;
|
kvs = kvs + 3;
|
||||||
while (i < kvs) {
|
while (i < kvs) {
|
||||||
v1 = stack[pc[i++]];
|
v1 = stack[pc[i++]];
|
||||||
@ -403,30 +443,16 @@ static int gst_continue_size(Gst *vm, uint32_t stackBase) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GST_OP_YLD: /* Yield from function */
|
|
||||||
temp = stack[pc[1]];
|
|
||||||
if (vm->thread->parent == NULL) {
|
|
||||||
vm->ret = temp;
|
|
||||||
return GST_RETURN_OK;
|
|
||||||
}
|
|
||||||
gst_frame_pc(stack) = pc + 2;
|
|
||||||
vm->thread = vm->thread->parent;
|
|
||||||
stack = vm->thread->data + vm->thread->count;
|
|
||||||
pc = gst_frame_pc(stack);
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* Handle errors from c functions and vm opcodes */
|
/* Handle errors from c functions and vm opcodes */
|
||||||
vm_error:
|
vm_error:
|
||||||
if (stack == NULL)
|
if (stack == NULL || vm->thread->parent == NULL)
|
||||||
return GST_RETURN_ERROR;
|
return GST_RETURN_ERROR;
|
||||||
while (gst_frame_errjmp(stack) == NULL) {
|
vm->thread->status = GST_THREAD_DEAD;
|
||||||
stack = gst_thread_popframe(vm, vm->thread);
|
vm->thread = vm->thread->parent;
|
||||||
if (vm->thread->count < stackBase)
|
stack = vm->thread->data + vm->thread->count;
|
||||||
return GST_RETURN_ERROR;
|
pc = gst_frame_pc(stack);
|
||||||
}
|
continue;
|
||||||
pc = gst_frame_errjmp(stack);
|
|
||||||
stack[gst_frame_errloc(stack)] = vm->ret;
|
|
||||||
break;
|
|
||||||
|
|
||||||
} /* end switch */
|
} /* end switch */
|
||||||
|
|
||||||
@ -439,11 +465,6 @@ static int gst_continue_size(Gst *vm, uint32_t stackBase) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 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. This function is
|
/* Run the vm with a given function. This function is
|
||||||
* called to start the vm. */
|
* called to start the vm. */
|
||||||
int gst_run(Gst *vm, GstValue callee) {
|
int gst_run(Gst *vm, GstValue callee) {
|
||||||
|
@ -79,17 +79,16 @@
|
|||||||
/* Stack frame manipulation */
|
/* Stack frame manipulation */
|
||||||
|
|
||||||
/* Size of stack frame in number of values */
|
/* Size of stack frame in number of values */
|
||||||
#define GST_FRAME_SIZE 5
|
#define GST_FRAME_SIZE 4
|
||||||
|
|
||||||
/* Macros for referencing 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_callee(s) (*(s - 1))
|
||||||
#define gst_frame_size(s) ((s - 2)->data.hws[0])
|
#define gst_frame_size(s) ((s - 2)->data.hws[0])
|
||||||
#define gst_frame_prevsize(s) ((s - 2)->data.hws[1])
|
#define gst_frame_prevsize(s) ((s - 2)->data.hws[1])
|
||||||
#define gst_frame_errloc(s) ((s - 2)->data.hws[2])
|
#define gst_frame_args(s) ((s - 2)->data.hws[2])
|
||||||
#define gst_frame_ret(s) ((s - 2)->data.hws[3])
|
#define gst_frame_ret(s) ((s - 2)->data.hws[3])
|
||||||
#define gst_frame_pc(s) ((s - 3)->data.u16p)
|
#define gst_frame_pc(s) ((s - 3)->data.u16p)
|
||||||
#define gst_frame_errjmp(s) ((s - 4)->data.u16p)
|
#define gst_frame_env(s) ((s - 4)->data.env)
|
||||||
#define gst_frame_env(s) ((s - 5)->data.env)
|
|
||||||
|
|
||||||
/* C function helpers */
|
/* C function helpers */
|
||||||
|
|
||||||
@ -109,7 +108,7 @@
|
|||||||
#ifndef GST_OUT_OF_MEMORY
|
#ifndef GST_OUT_OF_MEMORY
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#define GST_OUT_OF_MEMORY do { printf("out of memory.\n"); exit(1); } while (0)
|
#define GST_OUT_OF_MEMORY do { printf("out of memory\n"); exit(1); } while (0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Max search depth for classes. */
|
/* Max search depth for classes. */
|
||||||
@ -196,7 +195,7 @@ struct GstValue {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/* A lightweight thread in gst. Does not correspond to
|
/* A lightweight thread in gst. Does not correspond to
|
||||||
* operating system threads. Used in coroutines. */
|
* operating system threads. Used in coroutines and continuations. */
|
||||||
struct GstThread {
|
struct GstThread {
|
||||||
uint32_t count;
|
uint32_t count;
|
||||||
uint32_t capacity;
|
uint32_t capacity;
|
||||||
@ -292,7 +291,7 @@ struct Gst {
|
|||||||
GstObject *registry;
|
GstObject *registry;
|
||||||
/* Return state */
|
/* Return state */
|
||||||
const char *crash;
|
const char *crash;
|
||||||
GstValue ret; /* Returned value from gst_start. Also holds errors. */
|
GstValue ret; /* Returned value from gst_start. */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Bytecode */
|
/* Bytecode */
|
||||||
@ -323,11 +322,10 @@ 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_ERR, /* Throw error */
|
|
||||||
GST_OP_TRY, /* Begin try block */
|
|
||||||
GST_OP_UTY, /* End try block */
|
|
||||||
GST_OP_RET, /* Return from function */
|
GST_OP_RET, /* Return from function */
|
||||||
GST_OP_RTN, /* Return nil */
|
GST_OP_RTN, /* Return nil */
|
||||||
|
GST_OP_PSK, /* Push stack */
|
||||||
|
GST_OP_PAR, /* Push array or tuple */
|
||||||
GST_OP_CAL, /* Call function */
|
GST_OP_CAL, /* Call function */
|
||||||
GST_OP_TCL, /* Tail call */
|
GST_OP_TCL, /* Tail call */
|
||||||
GST_OP_YLD /* Yield from function */
|
GST_OP_YLD /* Yield from function */
|
||||||
@ -419,8 +417,6 @@ void gst_thread_tuplepack(Gst *vm, GstThread *thread, uint32_t n);
|
|||||||
GstValue *gst_thread_beginframe(Gst *vm, GstThread *thread, GstValue callee, uint32_t arity);
|
GstValue *gst_thread_beginframe(Gst *vm, GstThread *thread, GstValue callee, uint32_t arity);
|
||||||
void gst_thread_endframe(Gst *vm, GstThread *thread);
|
void gst_thread_endframe(Gst *vm, GstThread *thread);
|
||||||
GstValue *gst_thread_popframe(Gst *vm, GstThread *thread);
|
GstValue *gst_thread_popframe(Gst *vm, GstThread *thread);
|
||||||
GstValue *gst_thread_tail(Gst *vm, GstThread *thread);
|
|
||||||
|
|
||||||
|
|
||||||
/****/
|
/****/
|
||||||
/* Value manipulation */
|
/* Value manipulation */
|
||||||
|
20
libs/pp.gst
Normal file
20
libs/pp.gst
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
# Pretty print
|
||||||
|
|
||||||
|
# Reindent a function to be more deeply indented
|
||||||
|
(: reindent (fn [x] x))
|
||||||
|
|
||||||
|
(: handler {
|
||||||
|
"number" tostring
|
||||||
|
"nil" tostring
|
||||||
|
"boolean" tostring
|
||||||
|
"userdata" tostring
|
||||||
|
"cfunction" tostring
|
||||||
|
"function" tostring
|
||||||
|
"string" tostring # change to unquote string
|
||||||
|
"buffer" tostring
|
||||||
|
"array" tostring
|
||||||
|
"tuple" tostring
|
||||||
|
"object" tostring
|
||||||
|
"struct" tostring
|
||||||
|
"thread" tostring
|
||||||
|
})
|
Loading…
Reference in New Issue
Block a user