#include "Code.h" #include #include #include "Code.h" #include "Constants.h" #include "Memory.h" #include "SystemFunctions.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 CSTRING_VALUE(value) \ ((Value){.type = VT_CONSTANT_STRING, .data = (value)}) #define SET_ERROR(format, ...) \ snprintf( \ c->error.text, sizeof(c->error.text), "%zu | " format, \ c->code.readIndex __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 iPop(Code* c) { POP_VALUE(a); return false; } static bool iAdd(Code* c) { POP_VALUE(a); POP_VALUE(b); return iPushValue(c, INT_VALUE(b.data + a.data)); } static bool iSub(Code* c) { POP_VALUE(a); POP_VALUE(b); return iPushValue(c, INT_VALUE(b.data - a.data)); } static bool iMul(Code* c) { POP_VALUE(a); POP_VALUE(b); return iPushValue(c, INT_VALUE(b.data * a.data)); } static bool iDiv(Code* c) { POP_VALUE(a); POP_VALUE(b); return iPushValue(c, INT_VALUE(b.data / a.data)); } static bool iAnd(Code* c) { POP_VALUE(a); POP_VALUE(b); return iPushValue(c, INT_VALUE(b.data && a.data)); } static bool iOr(Code* c) { POP_VALUE(a); POP_VALUE(b); return iPushValue(c, INT_VALUE(b.data || a.data)); } static bool iNot(Code* c) { POP_VALUE(a); return iPushValue(c, INT_VALUE(!a.data)); } static bool iEqual(Code* c) { POP_VALUE(a); POP_VALUE(b); return iPushValue(c, INT_VALUE(b.data == a.data)); } static bool iNotEqual(Code* c) { POP_VALUE(a); POP_VALUE(b); return iPushValue(c, INT_VALUE(b.data != a.data)); } static bool iGreater(Code* c) { POP_VALUE(a); POP_VALUE(b); return iPushValue(c, INT_VALUE(b.data > a.data)); } static bool iSmaller(Code* c) { POP_VALUE(a); POP_VALUE(b); return iPushValue(c, INT_VALUE(b.data < a.data)); } static bool iGreaterOrEqual(Code* c) { POP_VALUE(a); POP_VALUE(b); return iPushValue(c, INT_VALUE(b.data >= a.data)); } static bool iSmallerOrEqual(Code* c) { POP_VALUE(a); POP_VALUE(b); return iPushValue(c, INT_VALUE(b.data <= a.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 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; i32 offset = 0; if(codeReadI32(c, &jumpPos)) { SET_ERROR("JumpSub without position"); return true; } else if(codeReadI32(c, &offset)) { SET_ERROR("JumpSub without offset"); return true; } i32 returnIndex = (i32)c->stackIndex - (offset + 1); if(returnIndex < 1 && returnIndex >= (i32)c->maxStackSize) { SET_ERROR("JumpSub with invalid return index"); return true; } c->stack[returnIndex].data = codeGetPosition(c); c->stack[returnIndex - 1].data = c->localVariableIndex; c->localVariableIndex = returnIndex; codeSetPosition(c, (size_t)jumpPos); return false; } static bool iReturn(Code* c) { i32 popAmount = 0; if(codeReadI32(c, &popAmount)) { SET_ERROR("Return without pop amount"); return true; } c->stackIndex -= (size_t)popAmount; POP_VALUE(returnAddress); POP_VALUE(variableIndex); codeSetPosition(c, (size_t)returnAddress.data); c->localVariableIndex = variableIndex.data; return false; } static bool iReturnValue(Code* c) { POP_VALUE(value); if(iReturn(c)) { return true; } return iPushValue(c, value); } static i32 iConvertAddress(Code* c, i32 address) { if(address >= 0) { return address; } return c->localVariableIndex - address; } static bool iReadVariable(Code* c) { i32 address = 0; if(codeReadI32(c, &address)) { SET_ERROR("ReadVariable without address"); return true; } address = iConvertAddress(c, address); if((size_t)address >= c->stackIndex) { SET_ERROR( "ReadVariable with invalid address %d %zu", address, c->stackIndex); 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; } address = iConvertAddress(c, address); 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 iCallSystem(Code* c) { i32 amountIndex = 0; if(codeReadI32(c, &amountIndex)) { SET_ERROR("CallSystem without argument amount"); return true; } i32 amount = amountIndex & 0xFF; i32 index = amountIndex >> 8; if(amount < 0 || amount > (i32)MAX_SYSTEM_FUNCTION_ARGUMENTS) { SET_ERROR("CallSystem with invalid amount"); return true; } Value values[MAX_SYSTEM_FUNCTION_ARGUMENTS]; for(i32 i = amount - 1; i >= 0; i--) { if(iPopValue(c, values + i)) { return true; } } SystemFunction f = getSystemFunction(index); if(f == nullptr) { SET_ERROR("Invalid system function index"); return true; } Value r = {}; if(f(c, &r, values, amount)) { return true; } return iPushValue(c, r); } static bool execute(Code* c, Instruction command) { switch(command) { case ADD: return iAdd(c); case SUB: return iSub(c); case MUL: return iMul(c); case DIV: return iDiv(c); case PUSH_CONSTANT_STRING: return iPushConstantString(c); case PUSH_INT32: return iPushInt(c); case POP: return iPop(c); case JUMP: return iJump(c); case JUMP_ON_0: return iJumpIf(c); case JUMP_SUB: return iJumpSub(c); case RETURN_VALUE: return iReturnValue(c); case READ_VARIABLE: return iReadVariable(c); case SET_VARIABLE: return iSetVariable(c); case PUSH_STACK_VARIABLES: return iPushStackVariables(c); case AND: return iAnd(c); case OR: return iOr(c); case NOT: return iNot(c); case EQUAL: return iEqual(c); case NOT_EQUAL: return iNotEqual(c); case GREATER: return iGreater(c); case SMALLER: return iSmaller(c); case GREATER_OR_EQUAL: return iGreaterOrEqual(c); case SMALLER_OR_EQUAL: return iSmallerOrEqual(c); case CALL_SYSTEM: return iCallSystem(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; } #define DUMP(name) \ case name: fprintf(stderr, #name "\n"); break #define DUMP_INT(name) \ case name: { \ i32 v = 0; \ (void)codeReadI32(&c, &v); \ fprintf(stderr, #name " %d\n", v); \ break; \ } break #define DUMP_INT2(name) \ case name: { \ i32 v[2] = {}; \ (void)codeReadI32(&c, v); \ (void)codeReadI32(&c, v + 1); \ fprintf(stderr, #name " %d %d\n", v[0], v[1]); \ break; \ } break void codeDump(const Code* code) { Code c = *code; while(true) { size_t start = c.code.readIndex; Instruction i = STOP; if(bufferReadU8(&c.code, &i)) { return; } fprintf(stderr, "%4zu | ", start); switch(i) { DUMP(ADD); DUMP(SUB); DUMP(MUL); DUMP(DIV); DUMP_INT(PUSH_INT32); DUMP(POP); DUMP_INT(JUMP); DUMP_INT(JUMP_ON_0); DUMP_INT2(JUMP_SUB); DUMP_INT(RETURN_VALUE); DUMP_INT(READ_VARIABLE); DUMP_INT(SET_VARIABLE); DUMP_INT(PUSH_STACK_VARIABLES); DUMP(AND); DUMP(OR); DUMP(NOT); DUMP(EQUAL); DUMP(NOT_EQUAL); DUMP(GREATER); DUMP(SMALLER); DUMP(GREATER_OR_EQUAL); DUMP(SMALLER_OR_EQUAL); DUMP_INT(CALL_SYSTEM); DUMP(STOP); case PUSH_CONSTANT_STRING: fprintf( stderr, "PUSH_CONSTANT_STRING %s\n", codeReadConstantString(&c)); break; } } }