#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; int status = 1; 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]; if (!IS_STRING(varname)) { printf("global names must be string, not %s\n", typename_str(varname)); goto done; } 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]; if (!IS_STRING(varname)) { printf("global names must be string, not %s\n", typename_str(varname)); goto done; } 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(); \ if (!IS_NUM(a) || !IS_NUM(b)) { \ printf("can't do arithmetic on %s and %s\n", \ typename_str(a), typename_str(b)); \ goto done; \ } \ 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(); if (!IS_NUM(a) || !IS_NUM(b)) { printf("can't do arithmetic on %s and %s", typename_str(a), typename_str(b)); goto done; } 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_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"); 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 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_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: status = 0; goto done; break; 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; } } } done:; return status; } #undef RBYTE #undef RSHORT #undef PUSH #undef POP #undef PEEK