diff --git a/parse.c b/parse.c index 7ab7fb0f..f21e4a8b 100644 --- a/parse.c +++ b/parse.c @@ -388,7 +388,6 @@ static int form_state(GstParser *p, uint8_t c) { static void dispatch_char(GstParser *p, uint8_t c) { int done = 0; ++p->index; - /* Handle comments */ if (p->flags & GST_PARSER_FLAG_INCOMMENT) { if (c == '\n') { @@ -405,7 +404,7 @@ static void dispatch_char(GstParser *p, uint8_t c) { return; } } - + /* Dispatch character to state */ while (!done && p->status == GST_PARSER_PENDING) { GstParseState *top = parser_peek(p); switch (top->type) { diff --git a/sample.gst b/sample.gst index 70a8b4ea..9c90d4a3 100644 --- a/sample.gst +++ b/sample.gst @@ -4,3 +4,9 @@ (a 2) (a 3) ) + +# Run call-for-each test + +(call-for-each print 1 2 3 4) + +(call-for-each (fn [a] (print a "hi")) 1 2 3 45) diff --git a/stl.c b/stl.c index a5061969..00387537 100644 --- a/stl.c +++ b/stl.c @@ -46,6 +46,7 @@ int gst_stl_callforeach(Gst *vm) { if (argCount) { for (i = 1; i < argCount; ++i) gst_call(vm, func, 1, vm->thread->data + vm->thread->count + i); + vm->ret.type = GST_NIL; return GST_RETURN_OK; } else { gst_c_throwc(vm, "expected at least one argument"); diff --git a/thread.c b/thread.c index 4f7987a7..d7261a94 100644 --- a/thread.c +++ b/thread.c @@ -19,10 +19,10 @@ GstThread *gst_thread(Gst *vm, GstValue callee, uint32_t capacity) { 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_expand_callable(vm, thread, callee); gst_thread_endframe(vm, thread); return thread; } @@ -83,10 +83,49 @@ void gst_thread_tuplepack(Gst *vm, GstThread *thread, uint32_t n) { } } +/* Expand a callee on the stack frame to its delegate function. This means that + * objects and userdata that have a "call" attribut in their class will be + * replaced with their delegate function. Call this before pushing any + * arguments to the stack. Returns the new stack. */ +GstValue *gst_thread_expand_callable(Gst *vm, GstThread *thread, GstValue callee) { + uint32_t i; + GstValue *stack; + GstObject *meta; + for (i = 0; i < 200; ++i) { + switch(callee.type) { + default: + return NULL; + case GST_FUNCTION: + stack = thread->data + thread->count; + gst_frame_callee(stack) = callee; + gst_frame_pc(stack) = callee.data.function->def->byteCode; + return stack; + case GST_CFUNCTION: + stack = thread->data + thread->count; + gst_frame_callee(stack) = callee; + gst_frame_pc(stack) = NULL; + return stack; + case GST_OBJECT: + meta = callee.data.object->meta; + if (meta == NULL) return NULL; + gst_thread_push(vm, thread, callee); + callee = gst_object_get_cstring(meta, "call"); + continue; + case GST_USERDATA: + meta = ((GstUserdataHeader *)callee.data.pointer - 1)->meta; + gst_thread_push(vm, thread, callee); + callee = gst_object_get_cstring(meta, "call"); + continue; + } + } + /* Callables nested to deeply */ + return NULL; +} + /* 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; + uint32_t frameOffset; GstValue *oldStack, *newStack; /* Push the frame */ @@ -100,43 +139,10 @@ GstValue *gst_thread_beginframe(Gst *vm, GstThread *thread, GstValue callee, uin 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; - } - } - + /* Get real callable */ + if (gst_thread_expand_callable(vm, thread, callee) == NULL) + return NULL; + /* Ensure the extra space and initialize to nil */ gst_thread_pushnil(vm, thread, arity); diff --git a/thread.h b/thread.h index e186ee51..71a1a99a 100644 --- a/thread.h +++ b/thread.h @@ -21,6 +21,12 @@ 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); +/* Expand a callee on the stack frame to its delegate function. This means that + * objects and userdata that have a "call" attribut in their class will be + * replaced with their delegate function. Call this before pushing any + * arguments to the stack. Returns the new stack. */ +GstValue *gst_thread_expand_callable(Gst *vm, GstThread *thread, GstValue callee); + /* 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); diff --git a/vm.c b/vm.c index 453c8881..a1f1c3e2 100644 --- a/vm.c +++ b/vm.c @@ -66,7 +66,7 @@ static int gst_continue_size(Gst *vm, uint32_t stackBase) { temp.data.number = v1.data.number op v2.data.number; \ stack[pc[1]] = temp; \ pc += 4; \ - break; + continue; case GST_OP_ADD: /* Addition */ OP_BINARY_MATH(+) @@ -87,7 +87,7 @@ static int gst_continue_size(Gst *vm, uint32_t stackBase) { temp.data.boolean = !gst_truthy(stack[pc[2]]); stack[pc[1]] = temp; pc += 3; - break; + continue; case GST_OP_NEG: /* Unary negation */ v1 = stack[pc[2]]; @@ -96,7 +96,7 @@ static int gst_continue_size(Gst *vm, uint32_t stackBase) { temp.data.number = -v1.data.number; stack[pc[1]] = temp; pc += 3; - break; + continue; case GST_OP_INV: /* Unary multiplicative inverse */ v1 = stack[pc[2]]; @@ -105,34 +105,34 @@ static int gst_continue_size(Gst *vm, uint32_t stackBase) { temp.data.number = 1 / v1.data.number; stack[pc[1]] = temp; pc += 3; - break; + continue; case GST_OP_FLS: /* Load False */ temp.type = GST_BOOLEAN; temp.data.boolean = 0; stack[pc[1]] = temp; pc += 2; - break; + continue; case GST_OP_TRU: /* Load True */ temp.type = GST_BOOLEAN; temp.data.boolean = 1; stack[pc[1]] = temp; pc += 2; - break; + continue; case GST_OP_NIL: /* Load Nil */ temp.type = GST_NIL; stack[pc[1]] = temp; pc += 2; - break; + continue; case GST_OP_I16: /* Load Small Integer */ temp.type = GST_NUMBER; temp.data.number = ((int16_t *)(pc))[2]; stack[pc[1]] = temp; pc += 3; - break; + continue; case GST_OP_UPV: /* Load Up Value */ case GST_OP_SUV: /* Set Up Value */ @@ -163,7 +163,7 @@ static int gst_continue_size(Gst *vm, uint32_t stackBase) { } pc += 4; } - break; + continue; case GST_OP_JIF: /* Jump If */ if (gst_truthy(stack[pc[1]])) { @@ -171,11 +171,11 @@ static int gst_continue_size(Gst *vm, uint32_t stackBase) { } else { pc += *((int32_t *)(pc + 2)); } - break; + continue; case GST_OP_JMP: /* Jump */ pc += *((int32_t *)(pc + 1)); - break; + continue; case GST_OP_CST: /* Load constant value */ v1 = gst_frame_callee(stack); @@ -184,26 +184,26 @@ static int gst_continue_size(Gst *vm, uint32_t stackBase) { gst_error(vm, GST_NO_UPVALUE); stack[pc[1]] = v1.data.function->def->literals[pc[2]]; pc += 3; - break; + continue; case GST_OP_I32: /* Load 32 bit integer */ temp.type = GST_NUMBER; temp.data.number = *((int32_t *)(pc + 2)); stack[pc[1]] = temp; pc += 4; - break; + continue; case GST_OP_F64: /* Load 64 bit float */ temp.type = GST_NUMBER; temp.data.number = (GstNumber) *((double *)(pc + 2)); stack[pc[1]] = temp; pc += 6; - break; + continue; case GST_OP_MOV: /* Move Values */ stack[pc[1]] = stack[pc[2]]; pc += 3; - break; + continue; case GST_OP_CLN: /* Create closure from constant FuncDef */ { @@ -239,21 +239,21 @@ static int gst_continue_size(Gst *vm, uint32_t stackBase) { temp.data.boolean = gst_equals(stack[pc[2]], stack[pc[3]]); stack[pc[1]] = temp; pc += 4; - break; + continue; case GST_OP_LTN: /* Less Than */ temp.type = GST_BOOLEAN; temp.data.boolean = (gst_compare(stack[pc[2]], stack[pc[3]]) == -1); stack[pc[1]] = temp; pc += 4; - break; + continue; case GST_OP_LTE: /* Less Than or Equal to */ temp.type = GST_BOOLEAN; temp.data.boolean = (gst_compare(stack[pc[2]], stack[pc[3]]) != 1); stack[pc[1]] = temp; pc += 4; - break; + continue; case GST_OP_ARR: /* Array literal */ { @@ -309,8 +309,8 @@ static int gst_continue_size(Gst *vm, uint32_t stackBase) { if (err != NULL) gst_error(vm, err); pc += 4; - break; } + continue; case GST_OP_SET: /* Associative set */ { @@ -319,8 +319,8 @@ static int gst_continue_size(Gst *vm, uint32_t stackBase) { if (err != NULL) gst_error(vm, err); pc += 4; - break; } + break; case GST_OP_ERR: /* Throw error */ vm->ret = stack[pc[1]]; @@ -330,40 +330,45 @@ static int gst_continue_size(Gst *vm, uint32_t stackBase) { gst_frame_errloc(stack) = pc[1]; gst_frame_errjmp(stack) = pc + *(uint32_t *)(pc + 2); pc += 4; - break; + continue; case GST_OP_UTY: /* End try block */ gst_frame_errjmp(stack) = NULL; pc++; - break; + continue; case GST_OP_RTN: /* Return nil */ stack = gst_thread_popframe(vm, &thread); if (thread.count < stackBase) { vm->ret.type = GST_NIL; + GST_STATE_WRITE(); return GST_RETURN_OK; } + pc = gst_frame_pc(stack); stack[gst_frame_ret(stack)].type = GST_NIL; - break; + continue; case GST_OP_RET: /* Return */ temp = stack[pc[1]]; stack = gst_thread_popframe(vm, &thread); if (thread.count < stackBase) { vm->ret = temp; + GST_STATE_WRITE(); return GST_RETURN_OK; } + pc = gst_frame_pc(stack); stack[gst_frame_ret(stack)] = temp; - break; + continue; case GST_OP_CAL: /* Call */ case GST_OP_TCL: /* Tail call */ { GstValue *oldStack; temp = stack[pc[1]]; + int isTCall = *pc == GST_OP_TCL; uint32_t i, arity, offset, size; uint16_t ret = pc[2]; - offset = (*pc == GST_OP_CAL) ? 4 : 3; + offset = isTCall ? 3 : 4; arity = pc[offset - 1]; /* Push new frame */ stack = gst_thread_beginframe(vm, &thread, temp, arity); @@ -375,7 +380,7 @@ static int gst_continue_size(Gst *vm, uint32_t stackBase) { /* Finish new frame */ gst_thread_endframe(vm, &thread); /* Check tail call - if so, replace frame. */ - if (*pc == GST_OP_TCL) { + if (isTCall) { stack = gst_thread_tail(vm, &thread); } else { gst_frame_ret(oldStack) = ret; @@ -384,7 +389,8 @@ static int gst_continue_size(Gst *vm, uint32_t stackBase) { temp = gst_frame_callee(stack); if (temp.type == GST_FUNCTION) { /* Save pc and set new pc */ - gst_frame_pc(oldStack) = pc + offset + arity; + if (!isTCall) + gst_frame_pc(oldStack) = pc + offset + arity; pc = temp.data.function->def->byteCode; } else { int status; @@ -393,12 +399,17 @@ static int gst_continue_size(Gst *vm, uint32_t stackBase) { 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) + if (thread.count < stackBase) { + GST_STATE_WRITE(); return status; - else + } else { stack[gst_frame_ret(stack)] = vm->ret; + if (isTCall) + pc = gst_frame_pc(stack); + else + pc += offset + arity; + } else goto vm_error; } @@ -416,7 +427,7 @@ static int gst_continue_size(Gst *vm, uint32_t stackBase) { stack[gst_frame_errloc(stack)] = vm->ret; break; - } /* end switch */ + } /* end switch */ /* TODO: Move collection only to places that allocate memory */ /* This, however, is good for testing to ensure no memory leaks */ @@ -434,28 +445,59 @@ int gst_continue(Gst *vm) { /* Run the vm with a given function */ int gst_run(Gst *vm, GstValue callee) { - vm->thread = gst_thread(vm, callee, 64); - return gst_continue(vm); -} - -/* Call a gst function */ -int gst_call(Gst *vm, GstValue callee, uint32_t arity, GstValue *args) { GstValue *stack; - uint32_t i; - stack = gst_thread_beginframe(vm, vm->thread, callee, arity); - for (i = 0; i < arity; ++i) - stack[i] = args[i]; - gst_thread_endframe(vm, vm->thread); - callee = gst_frame_callee(stack); - if (callee.type == GST_FUNCTION) { - return gst_continue(vm); - } else { + vm->thread = gst_thread(vm, callee, 64); + if (vm->thread == NULL) + return GST_RETURN_CRASH; + stack = gst_thread_stack(vm->thread); + /* If callee was not actually a function, get the delegate function */ + callee = gst_frame_callee(stack); + if (callee.type == GST_CFUNCTION) { int status; vm->ret.type = GST_NIL; status = callee.data.cfunction(vm); gst_thread_popframe(vm, vm->thread); return status; + } else { + return gst_continue(vm); + } +} + +/* Call a gst function */ +int gst_call(Gst *vm, GstValue callee, uint32_t arity, GstValue *args) { + GstValue *stack; + uint32_t i, size; + int status; + + /* Set the return position */ + stack = gst_thread_stack(vm->thread); + gst_frame_ret(stack) = gst_frame_size(stack); + + /* Add extra space for returning value */ + gst_thread_pushnil(vm, vm->thread, 1); + stack = gst_thread_beginframe(vm, vm->thread, callee, arity); + + /* Write args to stack */ + size = gst_frame_size(stack) - arity; + for (i = 0; i < arity; ++i) + stack[i + size] = args[i]; + gst_thread_endframe(vm, vm->thread); + + /* Call function */ + callee = gst_frame_callee(stack); + if (callee.type == GST_FUNCTION) { + gst_frame_pc(stack) = callee.data.function->def->byteCode; + status = gst_continue(vm); + } else { + vm->ret.type = GST_NIL; + status = callee.data.cfunction(vm); + gst_thread_popframe(vm, vm->thread); } + + /* Pop the extra nil */ + --gst_frame_size(gst_thread_stack(vm->thread)); + + return status; } /* Get an argument from the stack */