#include #include #include #include #include #include #include "val.h" #include "vm.h" #include "mem.h" #include "dis.h" Chunk chunk_new(State *S) { return (Chunk){ 0 }; } size_t chunk_wbc(State *S, Chunk *ch, uint8_t byte) { if (ch->bc.len == ch->bc.cap) { size_t newsz = (ch->bc.cap == 0 ? 8 : ch->bc.cap * 2); ch->bc.d = RENEW_ARR(S, ch->bc.d, uint8_t, ch->bc.cap, newsz); ch->bc.cap = newsz; } size_t ix = ch->bc.len; ch->bc.d[ix] = byte; ch->bc.len ++; return ix; } size_t chunk_wconst(State *S, Chunk *ch, Val v) { for (int i = 0; i < ch->consts.len; i ++) if (val_equal(v, ch->consts.d[i])) return i; if (ch->consts.len == ch->consts.cap) { size_t newsz = (ch->consts.cap == 0 ? 8 : ch->consts.cap *2); ch->consts.d = RENEW_ARR(S, ch->consts.d, Val, ch->consts.cap, newsz); ch->consts.cap = newsz; } size_t ix = ch->consts.len; ch->consts.d[ix] = v; ch->consts.len ++; return ix; } 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]) while (1) { uint8_t instr = RBYTE(); switch (instr) { case OP_RET: status = 0; goto done; break; 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_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); 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; } #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; } } done:; return status; } #undef RBYTE #undef RSHORT #undef PUSH #undef POP #undef PEEK