#include "Code.h" #include #include #include "Code.h" #include "Constants.h" #include "Memory.h" void codeInit(Code* c) { *c = (Code){}; bufferInit(&c->code); } void codeDestroy(Code* c) { bufferDestroy(&c->code); memoryFree(c->stack); *c = (Code){}; } void codeReset(Code* c) { bufferReset(&c->code); } #define POP_VALUE(name) \ Value name; \ if(iPopValue(c, &name)) \ return true #define INT_VALUE(value) ((Value){.type = VT_INT32, .data = (value)}) #define CSTRING_VALUE(value) \ ((Value){.type = VT_CONSTANT_STRING, .data = (value)}) #define SET_ERROR(format, ...) \ snprintf( \ c->error.text, sizeof(c->error.text), \ format __VA_OPT__(, ) __VA_ARGS__) [[nodiscard]] static bool codeReadI32(Code* c, i32* i) { return bufferReadI32(&c->code, i); } [[nodiscard]] static const char* codeReadConstantString(Code* c) { return bufferReadString(&c->code); } static void codeSetPosition(Code* c, size_t pos) { c->code.readIndex = pos; } static i32 codeGetPosition(Code* c) { return (i32)c->code.readIndex; } static bool iPushValue(Code* c, Value v) { while(c->stackIndex >= c->maxStackSize) { size_t newSize = c->maxStackSize <= 0 ? 16 : (c->maxStackSize * 5) / 4; if(newSize >= MAX_STACK_VALUES) { SET_ERROR("Stack overflow"); return true; } Value* newValues = memoryAllocate(sizeof(Value) * newSize); if(newValues == nullptr) { SET_ERROR("Out of memory for stack"); return true; } memcpy(newValues, c->stack, sizeof(Value) * c->stackIndex); memoryFree(c->stack); c->stack = newValues; c->maxStackSize = newSize; } c->stack[c->stackIndex++] = v; return false; } static bool iPopValue(Code* c, Value* v) { if(c->stackIndex <= 0) { SET_ERROR("Pop on empty stack"); return true; } *v = c->stack[--c->stackIndex]; return false; } static bool iAdd(Code* c) { POP_VALUE(a); POP_VALUE(b); return iPushValue(c, INT_VALUE(a.data + b.data)); } static bool iPushConstantString(Code* c) { const char* s = codeReadConstantString(c); i32 address = (i32)((const u8*)s - c->code.data); return iPushValue(c, CSTRING_VALUE(address)); } static bool iPushInt(Code* c) { i32 i = 0; if(codeReadI32(c, &i)) { SET_ERROR("PushInt without value"); return true; } return iPushValue(c, INT_VALUE(i)); } static bool iPrint(Code* c) { POP_VALUE(a); switch(a.type) { case VT_INT32: printf("%d", a.data); break; case VT_CONSTANT_STRING: printf("%s", c->code.data + a.data); break; } return false; } static bool iPrintNewline() { putchar('\n'); return false; } static bool iJump(Code* c) { i32 jumpPos = 0; if(codeReadI32(c, &jumpPos)) { SET_ERROR("Jump without position"); return true; } codeSetPosition(c, (size_t)jumpPos); return false; } static bool iJumpIf(Code* c) { POP_VALUE(a); i32 jumpPos = 0; if(codeReadI32(c, &jumpPos)) { SET_ERROR("JumpIf without position"); return true; } if(a.data == 0) { codeSetPosition(c, (size_t)jumpPos); } return false; } static bool iJumpSub(Code* c) { i32 jumpPos = 0; if(codeReadI32(c, &jumpPos)) { SET_ERROR("JumpSub without position"); return true; } else if(iPushValue(c, INT_VALUE(codeGetPosition(c)))) { return true; } codeSetPosition(c, (size_t)jumpPos); return false; } static bool iReturn(Code* c) { POP_VALUE(a); codeSetPosition(c, (size_t)a.data); return false; } static bool iReadVariable(Code* c) { i32 address = 0; if(codeReadI32(c, &address)) { SET_ERROR("ReadVariable without address"); return true; } else if((size_t)address >= c->stackIndex) { SET_ERROR("ReadVariable with invalid address"); return true; } return iPushValue(c, c->stack[address]); } static bool iSetVariable(Code* c) { i32 address = 0; if(codeReadI32(c, &address)) { SET_ERROR("SetVariable without address"); return true; } else if((size_t)address >= c->stackIndex) { SET_ERROR("SetVariable with invalid address"); return true; } POP_VALUE(a); c->stack[address] = a; return false; } static bool iPushStackVariables(Code* c) { i32 amount = 0; if(codeReadI32(c, &amount)) { SET_ERROR("PushStackVariables without amount"); return true; } Value v = INT_VALUE(0); while(amount > 0) { if(iPushValue(c, v)) { return true; } amount--; } return false; } static bool execute(Code* c, Instruction command) { switch(command) { case ADD: return iAdd(c); case PUSH_CONSTANT_STRING: return iPushConstantString(c); case PUSH_INT64: return iPushInt(c); case PRINT: return iPrint(c); case PRINT_NEWLINE: return iPrintNewline(); case JUMP: return iJump(c); case JUMP_ON_0: return iJumpIf(c); case JUMP_SUB: return iJumpSub(c); case RETURN: return iReturn(c); case READ_VARIABLE: return iReadVariable(c); case SET_VARIABLE: return iSetVariable(c); case PUSH_STACK_VARIABLES: return iPushStackVariables(c); case STOP: return true; } return false; } void codeRun(Code* c) { while(true) { Instruction i = STOP; if(bufferReadU8(&c->code, &i) || execute(c, i)) { return; } } } bool codeHasRunError(const Code* code) { return hasError(&code->error); } const char* codeGetRunError(const Code* code) { return code->error.text; }