#include #include #include #include #include #include #include #include "val.h" #include "vm.h" #include "mem.h" #include "dis.h" #include "com.h" #include "util.h" Thread thread_new(State *S) { Thread th = (Thread){ 0 }; for (int i = 0; i < STACKSIZE; i++) { th.stack[i] = VAL_NIL; } return th; } int runvm(State *S) { Thread *th = S->th; Chunk *ch = th->ch; if (S->do_disasm) { disasm_chunk(ch); puts("---"); } #define RBYTE() (ch->bc.d[th->ip++]) #define RSHORT() (th->ip += 2, (uint16_t)( ch->bc.d[th->ip-2] | ch->bc.d[th->ip-1] << 8 )) #define PUSH(v) th->stack[th->sp++] = v; #define POP() (th->stack[--th->sp]) #define PEEK() (th->stack[th->sp-1]) // 1 is TOS #define PEEKN(n) (th->stack[th->sp-n]) while (1) { ch = th->ch; if (S->do_trace) { printf("\t[%lu -> %lu] : ",th->fp, th->sp); for (int i = th->fp; i < th->sp; i++) { printf("(%d) ",i); print_val(th->stack[i]); printf(" ; "); } printf("\n%p ",(void *)ch); disasm_instr(ch, th->ip); } uint8_t instr = RBYTE(); switch (instr) { case OP_LOADK: { uint8_t cix = RBYTE(); Val v = ch->consts.d[cix]; PUSH(v); // printf(" (pushing "); // print_val(v); // printf(")\n"); break; } case OP_DROP: --th->sp; break; case OP_SKIP: { uint16_t offset = RSHORT(); th->ip += offset; break; } case OP_REDO: { uint16_t offset = RSHORT(); th->ip -= offset; break; } case OP_0BRANCH: { uint16_t offset = RSHORT(); bool cond = is_truthy(POP()); if (!cond) th->ip += offset; break; } case OP_GETGLOBAL: { uint8_t cix = RBYTE(); Val varname = ch->consts.d[cix]; CHECK(IS_STRING(varname), "global names must be string"); Val v = ht_get(S, &S->globals, AS_STRING(varname)); PUSH(v); if (IS_NIL(v)) { printf("warning: nil global read %s\n", AS_CSTRING(varname)); } break; } case OP_SETGLOBAL: { uint8_t cix = RBYTE(); Val varname = ch->consts.d[cix]; CHECK(IS_STRING(varname), "global names must be string"); Val v = PEEK(); ht_put(S, &S->globals, AS_STRING(varname), v); break; } case OP_GETLOCAL: { uint8_t lidx = RBYTE(); PUSH(th->stack[th->fp + lidx]); break; } case OP_SETLOCAL: { uint8_t lidx = RBYTE(); Val v = PEEK(); th->stack[th->fp + lidx] = v; break; } #define BINARY_OP(opcode, OP, RET_TYPE) \ case opcode: { \ Val b = POP(); \ Val a = POP(); \ CHECK(IS_NUM(a) && IS_NUM(b), "can only do arithmetic on num"); \ PUSH(RET_TYPE(AS_NUM(a) OP AS_NUM(b))); \ } \ break; #define ARITH_OP(opcode, OP) BINARY_OP(opcode, OP, VAL_NUM) #define BOOL_OP(opcode, OP) BINARY_OP(opcode, OP, VAL_BOOL) ARITH_OP(OP_ADD, +) ARITH_OP(OP_SUB, -) ARITH_OP(OP_MUL, *) ARITH_OP(OP_DIV, /) BOOL_OP(OP_CMP, <) #undef BINARY_OP #undef ARITH_OP #undef BOOL_OP case OP_EQU: { Val b = POP(); Val a = POP(); PUSH(VAL_BOOL(val_equal(a,b))); break; } case OP_MOD: { Val b = POP(); Val a = POP(); CHECK(IS_NUM(a) && IS_NUM(b), "can only do arithmetic on num"); PUSH(VAL_NUM(fmod(AS_NUM(a), AS_NUM(b)))); break; } case OP_NIL: PUSH(VAL_NIL); break; case OP_TRUE: PUSH(VAL_TRUE); break; case OP_FALSE: PUSH(VAL_FALSE); break; case OP_TAILCALL: case OP_CALL: { // nargs + 1 = function and args uint8_t len = RBYTE(); uint8_t nargs = len - 1; Val callee = PEEKN(len); if (IS_FUNC(callee)) { ObjFunc *func = AS_FUNC(callee); CHECK(nargs == func->arity, "func needs exactly %d args, but got %d",func->arity,nargs); CHECK(th->rsp < MAXDEPTH, "rstack overflow"); if (instr != OP_TAILCALL) { StackFrame *sf = &th->rstack[th->rsp++]; sf->ip = th->ip; sf->ch = th->ch; sf->fp = th->fp; th->ip = 0; th->ch = &func->ch; th->fp = th->sp - len; } else { // xxx might invalidate open upvalues memmove(&th->stack[th->fp], &th->stack[th->sp - len], len*sizeof(Val)); th->sp = th->fp + len; th->ip = 0; th->ch = &func->ch; StackFrame *cur_sf = &th->rstack[th->rsp]; cur_sf->ip = th->ip; cur_sf->ch = th->ch; cur_sf->fp = th->fp; } } else if (IS_CFUNC(callee)) { Val *firstarg = &th->stack[th->sp - nargs]; Val res = AS_CFUNC(callee)(S, nargs, firstarg); th->sp -= len; PUSH(res); } else if (IS_ARR(callee)) { ObjArr *arr = AS_ARR(callee); CHECK(len == 2, "can only index arr with single argument"); Val vix = PEEK(); CHECK(IS_NUM(vix), "can only index numerically"); size_t ix = (size_t)AS_NUM(vix); Val res = objarr_get(S, arr, ix); th->sp -= 2; PUSH(res); } else { ERROR("cannot call %s",typename_str(callee)); } break; } case OP_SETIDX: { Val vix = POP(); Val varr = POP(); Val v = PEEK(); CHECK(IS_NUM(vix), "can only index numerically"); CHECK(IS_ARR(varr), "can only set index on array"); size_t ix = (size_t)AS_NUM(vix); ObjArr *arr = AS_ARR(varr); objarr_put(S, arr, ix, v); break; } case OP_ENDSCOPE: { uint8_t nlocals = RBYTE(); Val retval = POP(); th->sp -= nlocals; PUSH(retval); break; } case OP_RET: { StackFrame *sf = &th->rstack[--th->rsp]; th->ip = sf->ip; th->ch = sf->ch; th->fp = sf->fp; break; } case OP_HALT: return 0; case OP_ARRNEW: { ObjArr *a = objarr_new(S); PUSH(VAL_OBJ(a)); break; } case OP_ARRAPPEND: { Val v = POP(); Val a = PEEK(); CHECK(IS_ARR(a), "can only append to array"); ObjArr *arr = AS_ARR(a); objarr_append(S, arr, v); break; } case OP_ARRLEN: { Val v = POP(); CHECK(IS_ARR(v), "can only get length of array"); ObjArr *arr = AS_ARR(v); PUSH(VAL_NUM(arr->len)); break; } default: ERROR("unknown opcode"); } } } #undef RBYTE #undef RSHORT #undef PUSH #undef POP #undef PEEK