29 auto qualifyVar = [&](
Operand &op) {
34 if (!op.object.empty())
36 if (op.op.find(
'.') != std::string::npos)
39 std::string qualified = this->
name +
"." + op.op;
40 if (
vars.find(qualified) !=
vars.end()) {
41 op.object = this->
name;
46 auto qualifyLabel = [&](
Operand &op) {
49 if (op.op.find(
'.') != std::string::npos)
52 op.object = this->
name;
54 op.op = this->
name +
"." + op.op;
61 for (
auto &vo : ci.
vop)
103 if (program !=
this) {
104 int64_t size = program->
inc.size();
105 for (
auto &i :
inc) {
108 for (
auto &lbl :
labels) {
109 flatten_label(program, lbl.second.first + size, lbl.first, lbl.second.second);
117 if (obj.get() != program)
118 obj->flatten(program);
127 std::cerr <<
"No instructions to execute\n";
136 std::cout << instr <<
"\n";
318 std::cerr <<
"Error: MOV destination: " + instr.
op1.
op +
" must be a variable, not a constant\n";
326 bool transferred =
false;
327 for (
auto &kv :
vars) {
328 if (&kv.second != &dest &&
330 kv.second.var_value.ptr_value == old_ptr) {
331 kv.second.var_value.owns =
true;
368 std::string v = instr.
op2.
op;
369 if (v ==
"null" || v ==
"NULL" || v ==
"0") {
372 uintptr_t addr = std::stoull(v,
nullptr, 0);
385 std::cerr <<
"Error: ADD destination: " + instr.
op1.
op +
" must be a variable, not a constant\n";
389 Variable *src1 =
nullptr, *src2 =
nullptr;
394 if (instr.
op3.
op.empty()) {
423 std::cerr <<
"Error: SUB destination must be a variable, not a constant\n";
428 Variable *src1 =
nullptr, *src2 =
nullptr;
433 if (instr.
op3.
op.empty()) {
462 std::cerr <<
"Error: MUL destination must be a variable, not a constant\n";
467 Variable *src1 =
nullptr, *src2 =
nullptr;
470 if (instr.
op3.
op.empty()) {
498 std::cerr <<
"Error: DIV destination must be a variable, not a constant\n";
503 Variable *src1 =
nullptr, *src2 =
nullptr;
506 if (instr.
op3.
op.empty()) {
558 else if (val1 < val2)
567 else if (val1 < val2)
576 else if (val1 < val2)
585 else if (val1 < val2)
594 else if (val1 < val2)
603 else if (val1 < val2)
612 else if (val1 < val2)
625 pc = it->second.first;
633 std::vector<Variable> tempArgs;
634 std::vector<Variable *>
args;
638 throw mx::Exception(
"PRINT format must be a string variable");
641 format = instr.
op1.
op;
651 return &tempArgs.back();
654 if (!instr.
op2.
op.empty()) {
655 args.push_back(getArgVariable(instr.
op2));
657 if (!instr.
op3.
op.empty()) {
658 args.push_back(getArgVariable(instr.
op3));
660 for (
const auto &vop : instr.
vop) {
661 if (!vop.op.empty()) {
662 args.push_back(getArgVariable(vop));
670 if (!instr.
op1.
op.empty()) {
674 exit_code = std::stoll(instr.
op1.
op,
nullptr, 0);
682 auto rax_pos = n.find(
"%");
683 if (rax_pos != std::string::npos) {
684 auto dot = n.find(
".");
685 if (dot != std::string::npos) {
686 std::string n_ = n.substr(dot + 1);
687 auto e =
vars.find(n_);
691 auto e =
vars.find(n);
696 auto pos = n.find(
".");
697 if (
pos == std::string::npos) {
698 auto it =
vars.find(
name +
"." + n);
699 if (it !=
vars.end()) {
703 auto it =
vars.find(n);
704 if (it !=
vars.end())
710 if (
pos == std::string::npos) {
711 auto it = obj.second->vars.find(
name +
"." + n);
712 if (it != obj.second->vars.end())
715 auto it = obj.second->vars.find(n);
716 if (it != obj.second->vars.end())
726 auto rax_pos = n.find(
"%");
728 if (rax_pos != std::string::npos) {
734 auto it = obj.second->vars.find(n);
735 if (it != obj.second->vars.end()) {
739 it = obj.second->vars.find(
name +
"." + n);
740 if (it != obj.second->vars.end())
756 auto it =
vars.find(variable.second->getTokenValue());
757 if (it ==
vars.end()) {
759 if (o->name == variable.first && !o->isVariable(variable.second->getTokenValue())) {
760 throw mx::Exception(
"Syntax Error: Argument variable not defined: Object: " +
761 variable.first +
" variable: " +
762 variable.second->getTokenValue() +
763 " at line " + std::to_string(variable.second->getLine()));
773 if (value.starts_with(
"0x") || value.starts_with(
"0X")) {
848 if (v1 == INT64_MIN && v2 == -1) {
849 throw mx::Exception(
"DIV: signed integer overflow (INT64_MIN / -1)");
870 size_t allocated_size = 0;
872 bool is_string_source =
false;
877 throw mx::Exception(
"LOAD source must be a valid pointer/string buffer");
886 is_string_source =
true;
889 throw mx::Exception(
"LOAD source must be a pointer variable");
892 if (ptr ==
nullptr) {
917 if (!instr.
op3.
op.empty()) {
921 index =
static_cast<size_t>(std::stoll(instr.
op3.
op,
nullptr, 0));
926 if (!instr.
vop.empty() && !instr.
vop[0].op.empty()) {
927 const auto &sizeOp = instr.
vop[0];
931 stride =
static_cast<size_t>(std::stoll(sizeOp.op,
nullptr, 0));
938 if (index > SIZE_MAX / stride) {
941 size_t offset = index * stride;
942 if (allocated_size > 0 && (offset + stride > allocated_size)) {
943 throw mx::Exception(
"LOAD: index out of bounds, offset " + std::to_string(offset) +
944 " + stride " + std::to_string(stride) +
945 " exceeds allocated size " + std::to_string(allocated_size));
949 char *
base =
static_cast<char *
>(ptr) + offset;
971 if (is_string_source && index < allocated_size) {
976 const char *str_ptr =
nullptr;
977 std::memcpy(&str_ptr,
base,
sizeof(
const char *));
978 if (str_ptr !=
nullptr) {
990 }
catch (
const std::exception &e) {
991 throw mx::Exception(std::string(
"LOAD: memory access error: ") + e.what());
1009 void *ptr =
nullptr;
1010 size_t allocated_size = 0;
1011 bool is_owned =
false;
1014 throw mx::Exception(
"STORE destination must be a variable or register");
1033 throw mx::Exception(
"STORE destination must be a pointer or integer containing a pointer address");
1037 if (!instr.
op3.
op.empty()) {
1041 index =
static_cast<size_t>(std::stoll(instr.
op3.
op,
nullptr, 0));
1046 if (!instr.
vop.empty() && !instr.
vop[0].op.empty()) {
1047 const auto &sizeOp = instr.
vop[0];
1051 stride =
static_cast<size_t>(std::stoll(sizeOp.op,
nullptr, 0));
1058 if (index > SIZE_MAX / stride) {
1061 size_t offset = index * stride;
1062 if (is_owned && allocated_size > 0 && (offset + stride > allocated_size)) {
1063 throw mx::Exception(
"STORE: index out of bounds, offset " + std::to_string(offset) +
1064 " + stride " + std::to_string(stride) +
1065 " exceeds allocated size " + std::to_string(allocated_size));
1068 char *
base =
static_cast<char *
>(ptr) + offset;
1071 int64_t cval = std::stoll(instr.
op1.
op,
nullptr, 0);
1072 std::memcpy(
base, &cval,
sizeof(int64_t));
1081 std::memcpy(
base, &byte_val, 1);
1092 if (is_owned && stride >=
sizeof(
char *)) {
1093 char *old_str =
nullptr;
1094 std::memcpy(&old_str,
base,
sizeof(
char *));
1097 if (old_str !=
nullptr) {
1101 char *new_str =
static_cast<char *
>(std::malloc(str_len));
1102 if (new_str ==
nullptr) {
1103 throw mx::Exception(
"STORE: failed to allocate memory for string copy");
1106 new_str[str_len - 1] =
'\0';
1107 std::memcpy(
base, &new_str,
sizeof(
char *));
1110 std::memcpy(
base, &cstr,
sizeof(
const char *));
1125 if (instr.
op3.
op.empty()) {
1142 if (instr.
op3.
op.empty()) {
1159 if (instr.
op3.
op.empty()) {
1172 throw mx::Exception(
"ALLOC destination must be a variable");
1178 if (!instr.
op2.
op.empty()) {
1182 size = std::stoll(instr.
op2.
op,
nullptr, 0);
1186 if (!instr.
op3.
op.empty()) {
1190 count = std::stoll(instr.
op3.
op,
nullptr, 0);
1193 if (size <= 0 || count <= 0) {
1194 throw mx::Exception(
"ALLOC: size and count must be positive");
1199 dest.
var_value.
ptr_value = calloc(
static_cast<size_t>(count),
static_cast<size_t>(size));
1201 throw mx::Exception(
"ALLOC failed: calloc returned nullptr");
1214 std::free(ptr_to_free);
1221 for (
auto &kv :
vars) {
1222 if (&kv.second != &var &&
1224 kv.second.var_value.ptr_value == ptr_to_free) {
1225 kv.second.var_value.ptr_value =
nullptr;
1226 kv.second.var_value.owns =
false;
1227 kv.second.var_value.ptr_size = 0;
1228 kv.second.var_value.ptr_count = 0;
1275 throw mx::Exception(
"Error NOT bitwise operation must be on integer value");
1283 std::cerr <<
"Error: MOD destination must be a variable, not a constant\n";
1288 Variable *src1 =
nullptr, *src2 =
nullptr;
1291 if (instr.
op3.
op.empty()) {
1316 int64_t v2 = (src2->type ==
VarType::VAR_FLOAT) ?
static_cast<int64_t
>(src2->var_value.float_value) : src2->var_value.int_value;
1324 std::cerr <<
"MOD only supports integer types\n";
1400 throw mx::Exception(
"STRING_PRINT destination must be a variable");
1405 throw mx::Exception(
"STRING_PRINT destination must be a string variable");
1409 std::vector<Variable> tempArgs;
1410 std::vector<Variable *>
args;
1414 throw mx::Exception(
"STRING_PRINT format must be a string variable");
1417 format = instr.
op2.
op;
1425 throw mx::Exception(
"string_print instruction variable not defined: " + op.
op);
1428 return &tempArgs.back();
1432 if (!instr.
op3.
op.empty()) {
1433 args.push_back(getArgVariable(instr.
op3));
1435 for (
const auto &vop : instr.
vop) {
1436 if (!vop.op.empty()) {
1437 args.push_back(getArgVariable(vop));
1441 std::ostringstream oss;
1442 size_t argIndex = 0;
1443 const char *fmt = format.c_str();
1446 for (
size_t i = 0; i < format.length(); ++i) {
1447 if (fmt[i] ==
'%' && i + 1 < format.length()) {
1450 while (j < format.length() &&
1451 (fmt[j] ==
'-' || fmt[j] ==
'+' || fmt[j] ==
' ' || fmt[j] ==
'#' || fmt[j] ==
'0' ||
1452 (fmt[j] >=
'0' && fmt[j] <=
'9') || fmt[j] ==
'.' ||
1453 fmt[j] ==
'l' || fmt[j] ==
'h' || fmt[j] ==
'z' || fmt[j] ==
'j' || fmt[j] ==
't')) {
1456 if (j < format.length() && fmt[j] ==
'%') {
1461 if (j >= format.length())
1463 if (!std::isalpha(fmt[j])) {
1467 std::string spec(fmt + start, fmt + j + 1);
1468 if (argIndex <
args.size()) {
1473 if (spec.find(
"%s") != std::string::npos) {
1474 std::snprintf(buffer,
sizeof(buffer), spec.c_str(),
static_cast<char *
>(arg->
var_value.
ptr_value));
1481 std::snprintf(buffer,
sizeof(buffer), spec.c_str(), arg->
var_value.
str_value.c_str());
1485 std::snprintf(buffer,
sizeof(buffer),
"%s",
"(unsupported)");
1502 std::ostringstream oss;
1503 size_t argIndex = 0;
1504 const char *fmt = format.c_str();
1506 for (
size_t i = 0; i < format.length(); ++i) {
1507 if (fmt[i] ==
'%' && i + 1 < format.length()) {
1510 while (j < format.length() &&
1511 (fmt[j] ==
'-' || fmt[j] ==
'+' || fmt[j] ==
' ' || fmt[j] ==
'#' || fmt[j] ==
'0' ||
1512 (fmt[j] >=
'0' && fmt[j] <=
'9') || fmt[j] ==
'.' ||
1513 fmt[j] ==
'l' || fmt[j] ==
'h' || fmt[j] ==
'z' || fmt[j] ==
'j' || fmt[j] ==
't')) {
1516 if (j < format.length() && fmt[j] ==
'%') {
1521 if (j >= format.length())
1523 if (!std::isalpha(fmt[j])) {
1527 std::string spec(fmt + start, fmt + j + 1);
1528 if (argIndex <
args.size()) {
1534 if (spec.find(
"%s") != std::string::npos) {
1535 std::snprintf(buffer,
sizeof(buffer), spec.c_str(),
static_cast<char *
>(arg->
var_value.
ptr_value));
1542 std::snprintf(buffer,
sizeof(buffer), spec.c_str(), arg->
var_value.
str_value.c_str());
1546 std::snprintf(buffer,
sizeof(buffer),
"%s",
"(unsupported)");
1558 std::cout << oss.str();
1563 throw mx::Exception(
"GETLINE destination must be a variable");
1567 std::getline(std::cin, input);
1569 switch (dest.
type) {
1591 int64_t int_val = std::stoll(instr.
op1.
op,
nullptr, 0);
1592 stack.push(int_val);
1595 throw mx::Exception(
"PUSH argument must be a variable or integer constant");
1627 if (
stack.empty()) {
1633 if (!std::holds_alternative<int64_t>(value)) {
1639 if (!std::holds_alternative<void *>(value)) {
1645 if (!std::holds_alternative<double>(value)) {
1651 if (!std::holds_alternative<std::string>(value)) {
1663 throw mx::Exception(
"STACK_LOAD destination must be a variable");
1668 if (!instr.
op2.
op.empty()) {
1672 index =
static_cast<size_t>(std::stoll(instr.
op2.
op,
nullptr, 0));
1676 if (index >=
stack.size()) {
1682 if (!std::holds_alternative<int64_t>(value)) {
1683 throw mx::Exception(
"STACK_LOAD type mismatch: expected integer");
1688 if (!std::holds_alternative<void *>(value)) {
1689 throw mx::Exception(
"STACK_LOAD type mismatch: expected pointer");
1694 if (!std::holds_alternative<double>(value)) {
1695 throw mx::Exception(
"STACK_LOAD type mismatch: expected float");
1700 if (!std::holds_alternative<std::string>(value)) {
1701 throw mx::Exception(
"STACK_LOAD type mismatch: expected string");
1706 throw mx::Exception(
"STACK_LOAD: unsupported variable type");
1712 throw mx::Exception(
"STACK_STORE source must be a variable");
1717 if (!instr.
op2.
op.empty()) {
1721 index =
static_cast<size_t>(std::stoll(instr.
op2.
op,
nullptr, 0));
1725 if (index >=
stack.size()) {
1738 throw mx::Exception(
"STACK_STORE: unsupported variable type");
1744 if (!instr.
op1.
op.empty()) {
1748 count =
static_cast<size_t>(std::stoll(instr.
op1.
op,
nullptr, 0));
1751 if (count >
stack.size()) {
1752 throw mx::Exception(
"STACK_SUB: not enough values on stack to pop " + std::to_string(count));
1754 for (
size_t i = 0; i < count; ++i) {
1760 if (instr.
op1.
op.empty()) {
1764 if (it !=
labels.end()) {
1765 stack.push(
static_cast<int64_t
>(
pc + 1));
1766 pc = it->second.first;
1773 if (
stack.empty()) {
1774 throw mx::Exception(
"RET: stack is empty, no return address");
1777 if (!std::holds_alternative<int64_t>(value)) {
1778 throw mx::Exception(
"RET: return address on stack is not an integer");
1780 pc =
static_cast<size_t>(std::get<int64_t>(value)) - 1;
1804 throw mx::Exception(
"to_int second argument must be a variable");
1807 throw mx::Exception(
"to_int first argument must be an integer variable");
1810 throw mx::Exception(
"to_int first argument must be a variable");
1838 throw mx::Exception(
"to_float second argument must be a variable");
1845 throw mx::Exception(
"to_float first argument must be a float variable");
1848 throw mx::Exception(
"to_float first argument must be a variable");
1912 throw mx::Exception(
"REALLOC destination must be a variable");
1918 if (!instr.
op2.
op.empty()) {
1922 size = std::stoll(instr.
op2.
op,
nullptr, 0);
1926 if (!instr.
op3.
op.empty()) {
1930 count = std::stoll(instr.
op3.
op,
nullptr, 0);
1933 if (size <= 0 || count < 0) {
1934 throw mx::Exception(
"REALLOC: size must be positive, count must be non-negative");
1937 size_t newBytes =
static_cast<size_t>(count) *
static_cast<size_t>(size);
1955 if (newPtr ==
nullptr) {
1956 throw mx::Exception(
"REALLOC failed: realloc returned nullptr");
1960 if (newBytes > oldBytes) {
1961 std::memset(
static_cast<char *
>(newPtr) + oldBytes, 0, newBytes - oldBytes);
2002 if (value ==
"null" || value ==
"NULL" || value ==
"0") {
2005 uintptr_t addr = std::stoull(value,
nullptr, 0);
2014 out <<
"Debug Information..\n";
2015 out <<
"=== INSTRUCTIONS ===\n";
2017 out <<
" (no instructions)\n";
2019 out << std::left << std::setw(10) <<
"Addr"
2020 << std::setw(20) <<
"Instruction"
2021 << std::setw(25) <<
"Operand1"
2022 << std::setw(25) <<
"Operand2"
2023 << std::setw(25) <<
"Operand3"
2024 << std::setw(30) <<
"Extra Operands" <<
"\n";
2025 out << std::string(130,
'-') <<
"\n";
2027 for (
size_t i = 0; i <
inc.size(); ++i) {
2028 const auto &instr =
inc[i];
2029 out << std::setw(10) << i;
2030 if (instr.instruction >= 0 && instr.instruction <
static_cast<int>(
IncType.size())) {
2031 out << std::setw(12) <<
IncType[instr.instruction];
2033 out << std::setw(12) <<
"UNKNOWN";
2035 out << std::setw(25) << (instr.op1.op.empty() ?
"-" : instr.op1.op);
2036 out << std::setw(25) << (instr.op2.op.empty() ?
"-" : instr.op2.op);
2037 out << std::setw(25) << (instr.op3.op.empty() ?
"-" : instr.op3.op);
2038 if (!instr.vop.empty()) {
2039 std::string extraOps;
2040 for (
size_t j = 0; j < instr.vop.size(); ++j) {
2043 extraOps += instr.vop[j].op;
2045 out << std::setw(30) << extraOps;
2047 out << std::setw(30) <<
"-";
2062 std::vector<Operand>
args;
2064 auto process_operand = [&](
Operand op) {
2072 if (instr.
op1.
op ==
"strlen" || instr.
op1.
op ==
"strncpy" ||
2073 instr.
op1.
op ==
"strncat" || instr.
op1.
op ==
"draw_text") {
2083 process_operand(instr.
op2);
2084 process_operand(instr.
op3);
2085 for (
const auto &vop : instr.
vop) {
2086 process_operand(vop);
2089 it->second.call(
this,
args);
2094 if (
stack.empty()) {
2095 out <<
"Stack aligned.\n";
2097 out <<
"Stack unaligned. {\n";
2098 for (
size_t i = 0; i <
stack.size(); ++i) {
2099 out <<
"\tAddress: [" << i <<
"] = ";
2101 if (std::holds_alternative<int64_t>(value)) {
2102 out << std::get<int64_t>(value);
2103 }
else if (std::holds_alternative<void *>(value)) {
2104 void *ptr = std::get<void *>(value);
2105 if (ptr ==
nullptr) {
2108 out <<
"0x" << std::hex << reinterpret_cast<uintptr_t>(ptr) << std::dec;
2110 }
else if (std::holds_alternative<double>(value)) {
2111 out << std::get<double>(value);
2112 }
else if (std::holds_alternative<std::string>(value)) {
2113 out <<
"\"" << std::get<std::string>(value) <<
"\"";
2129 throw mx::Exception(
"Could not create variable from operand: " + op.
op);
2160 throw mx::Exception(
"FCMP: unsupported operand type for var1");
2168 throw mx::Exception(
"FCMP: unsupported operand type for var2");
2173 }
else if (val1 < val2) {
General-purpose exception with errno-aware factory method.
static std::unordered_map< std::string, Program * > object_map
object name -> Program mapping
std::unordered_map< std::string, std::pair< uint64_t, bool > > labels
label -> (address, is_function)
std::vector< Instruction > inc
instruction stream
std::unordered_map< std::string, Variable > vars
variable symbol table
void add_label(const std::string &name, uint64_t address, bool f)
Register a label at the given instruction address.
static Base * base
pointer to the main program base
std::unordered_map< std::string, RuntimeFunction > external_functions
resolved runtime function bindings
void add_instruction(const Instruction &i)
Append an instruction to the instruction stream.
Variable createTempVariable(VarType type, const std::string &value)
Create a temporary unnamed variable with the given type and value.
void setVariableFromConstant(Variable &var, const std::string &value)
Set a variable's value from a constant string (integer, float, or string literal).
void exec_jns(const Instruction &instr)
void exec_jae(const Instruction &instr)
Variable & getVariable(const std::string &name)
Look up a variable by name, searching local scope then global.
void exec_mul(const Instruction &instr)
void exec_jc(const Instruction &instr)
void exec_free(const Instruction &instr)
void exec_load(const Instruction &instr)
int exec()
Execute the program via the interpreter.
void exec_exit(const Instruction &instr)
void flatten_label(Program *root, int64_t offset, const std::string &label, bool func)
Copy a label from a child object into the root label map with an address offset.
void divVariables(Variable &dest, Variable &src1, Variable &src2)
Divide src1 by src2 and store the result in dest.
void exec_push(const Instruction &instr)
Execute a PUSH instruction — save a variable or constant onto the VM stack.
void stop()
Halt the interpreter loop.
bool less_flag
comparison less-than flag
void exec_mod(const Instruction &instr)
void exec_jg(const Instruction &instr)
void add_standard()
Register standard library runtime functions.
void exec_jl(const Instruction &instr)
void exec_stack_load(const Instruction &instr)
bool isVariable(const std::string &name)
Check whether a variable exists in local or global scope.
void exec_to_int(const Instruction &instr)
void exec_jz(const Instruction &instr)
void exec_fcmp(const Instruction &instr)
std::vector< std::string > args
void flatten_external(Program *root, const std::string &e, RuntimeFunction &r)
Copy an external function binding from a child object into the root.
void print(std::ostream &out)
Print all instructions and data to an output stream.
void post(std::ostream &out)
Generate native data section (variable declarations) into the output stream.
void exec_to_float(const Instruction &instr)
void exec_done(const Instruction &instr)
void exec_store(const Instruction &instr)
Execute a STORE instruction — write a value into a pointer at a given offset.
void exec_sub(const Instruction &instr)
void exec_or(const Instruction &instr)
std::vector< std::unique_ptr< Program > > objects
void exec_mov(const Instruction &instr)
void exec_return(const Instruction &instr)
void exec_not(const Instruction &instr)
bool carry_flag
comparison carry flag
void exec_cmp(const Instruction &instr)
void exec_je(const Instruction &instr)
void exec_invoke(const Instruction &instr)
void exec_js(const Instruction &instr)
void exec_jo(const Instruction &instr)
Variable variableFromOperand(const Operand &op)
Resolve an Operand to its corresponding Variable.
void exec_realloc(const Instruction &instr)
Execute the REALLOC instruction in the interpreter.
void exec_add(const Instruction &instr)
void exec_and(const Instruction &instr)
void exec_xor(const Instruction &instr)
void exec_jnz(const Instruction &instr)
void flatten(Program *program)
Flatten child object programs into the root instruction stream.
void exec_neg(const Instruction &instr)
void exec_stack_sub(const Instruction &instr)
void exec_call(const Instruction &instr)
void exec_string_print(const Instruction &instr)
void exec_jne(const Instruction &instr)
void exec_jge(const Instruction &instr)
void exec_div(const Instruction &instr)
void exec_jmp(const Instruction &instr)
bool zero_flag
comparison zero flag
void exec_jle(const Instruction &instr)
void exec_getline(const Instruction &instr)
void exec_alloc(const Instruction &instr)
void exec_jbe(const Instruction &instr)
int getExitCode() const
Get the program's exit code after execution.
void exec_jb(const Instruction &instr)
void exec_jnp(const Instruction &instr)
bool greater_flag
comparison greater-than flag
void exec_lea(const Instruction &instr)
void addVariables(Variable &dest, Variable &src1, Variable &src2)
Add two variables and store the result in dest.
void flatten_inc(Program *root, Instruction &i)
Rewrite object-qualified operand references in a single instruction.
bool validateNames(Validator &v)
Validate all variable and label names against the Validator.
Operand result
return value operand from last invoke
void exec_jno(const Instruction &instr)
void exec_ja(const Instruction &instr)
void exec_pop(const Instruction &instr)
Execute a POP instruction — restore a value from the VM stack into a variable.
void exec_jp(const Instruction &instr)
void mulVariables(Variable &dest, Variable &src1, Variable &src2)
Multiply two variables and store the result in dest.
Program()
Default constructor — initializes execution state (pc, flags, etc.).
void setVariableFromString(Variable &var, const std::string &value)
Set a variable's value from a string representation, coercing types.
void exec_jnc(const Instruction &instr)
std::string printFormatted(const std::string &format, const std::vector< Variable * > &args, bool output=true)
Format and optionally print a printf-style format string with variable arguments.
void exec_print(const Instruction &instr)
void exec_ret(const Instruction &instr)
bool running
interpreter running flag
void exec_stack_store(const Instruction &instr)
void subVariables(Variable &dest, Variable &src1, Variable &src2)
Subtract src2 from src1 and store the result in dest.
Wraps a dynamically loaded native function from a shared library module.
Validates MXVM source for correct variable/label usage and instruction operands.
std::vector< VarNamePair > var_names
int64_t pos(const char *substr, const char *s)
Intermediate code representation, execution engine, and native x64 code generation (SysV + Win64).
std::vector< std::string > IncType
String representations of Inc opcodes, indexed by enum value.
@ REALLOC
Reallocate a dynamic memory block: realloc dest, elemSize, count.
bool instruct_mode
enable instruction trace mode
static void releaseOwnedPointer(Variable &v)
std::string toStringFromVarType(const VarType &v)
std::variant< int64_t, void *, double, std::string > StackValue
Stack element: integer, pointer, double, or string.
VarType
MXVM variable type discriminator.
A complete MXVM instruction with opcode, operands, and optional label.
std::vector< Operand > vop
variable-length operand list (for print, invoke, etc.)
Inc instruction
the opcode
Operand op3
up to three fixed operands
A single instruction operand (constant value or variable reference).
std::string op
textual operand value
uint64_t ptr_size
size of each element in allocated memory
bool owns
true if this value owns the allocated memory
uint64_t ptr_count
number of elements allocated
A named variable with type, value, and optional object association.
std::string toString() const
Convert this variable to a human-readable string.