#include #include #include #include #include #include #include "val.h" #include "vm.h" #include "mem.h" #include "dis.h" #include "com.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->fp + th->sp++] = v; #define POP() (th->stack[th->fp + --th->sp]) #define PEEK() (th->stack[th->fp + th->sp-1]) while (1) { ch = th->ch; if (S->do_trace) { printf("\t[%lu + %lu] : ",th->fp, th->sp); for (int i = 0; i < th->sp; i++) { print_val(th->stack[th->fp + 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_PUTS: println_val(PEEK()); break; case OP_PRINT: print_val(PEEK()); 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; } #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: { uint8_t len = RBYTE(); th->sp -= len-1; Val callee = POP(); if (!IS_FUNC(callee)) { fprintf(stderr, "can only call functions\n"); goto done; } ObjFunc *func = AS_FUNC(callee); StackFrame *sf = &th->rstack[th->rsp++]; sf->ch = th->ch; sf->ip = th->ip; sf->fp = th->fp; th->ch = &func->ch; th->ip = 0; th->fp = th->sp; th->sp = len; 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]; size_t orig_fp = th->fp; th->ch = sf->ch; th->ip = sf->ip; th->fp = sf->fp; th->sp += (orig_fp - th->fp); break; } case OP_HALT: status = 0; goto done; break; } } done:; return status; } #undef RBYTE #undef RSHORT #undef PUSH #undef POP #undef PEEK