#include <cstdarg> #include <cstdio> #include <cstdlib> #include <cstring> #include <mxvm/icode.hpp> #include <mxvm/instruct.hpp> #include <string> static std::string getStringFromVar(mxvm::Program *program, const std::string &varName) { if (!program->isVariable(varName)) { throw mx::Exception("Argument must be a variable, got: " + varName); } mxvm::Variable &var = program->getVariable(varName); if (var.type == mxvm::VarType::VAR_STRING) { return var.var_value.str_value; } if (var.type == mxvm::VarType::VAR_POINTER) { mxvm::except_assert("Pointer argument '" + varName + "' is null", var.var_value.ptr_value != nullptr); return std::string(reinterpret_cast<const char *>(var.var_value.ptr_value)); } throw mx::Exception("Argument '" + varName + "' must be a string or pointer variable."); } static inline bool isStringLike(mxvm::Variable &v) { return v.type == mxvm::VarType::VAR_STRING || v.type == mxvm::VarType::VAR_POINTER; } static inline const char *asReadPtr(mxvm::Variable &v) { if (v.type == mxvm::VarType::VAR_POINTER) { mxvm::except_assert("null pointer", v.var_value.ptr_value != nullptr); return reinterpret_cast<const char *>(v.var_value.ptr_value); } return v.var_value.str_value.c_str(); } static inline char *asWritePtr(mxvm::Variable &v) { if (v.type == mxvm::VarType::VAR_POINTER) { mxvm::except_assert("null pointer", v.var_value.ptr_value != nullptr); return reinterpret_cast<char *>(v.var_value.ptr_value); } return nullptr; } extern "C" void mxvm_string_strlen(mxvm::Program *program, std::vector<mxvm::Operand> &operand) { if (operand.size() != 1) { throw mx::Exception("strlen requires exactly 1 argument (string | pointer)."); } std::string &name = operand[0].op; if (!program->isVariable(name)) { throw mx::Exception("strlen argument must be a declared variable (string | pointer): " + name); } mxvm::Variable &v = program->getVariable(name); const char *src = nullptr; switch (v.type) { case mxvm::VarType::VAR_STRING: src = v.var_value.str_value.c_str(); break; case mxvm::VarType::VAR_POINTER: case mxvm::VarType::VAR_EXTERN: mxvm::except_assert("strlen: null pointer in variable '" + v.var_name + "'", v.var_value.ptr_value != nullptr); src = reinterpret_cast<const char *>(v.var_value.ptr_value); break; default: throw mx::Exception("strlen argument '" + v.var_name + "' must be string or pointer variable (got different type)."); } int64_t length = static_cast<int64_t>(std::strlen(src)); program->vars["%rax"].type = mxvm::VarType::VAR_INTEGER; program->vars["%rax"].var_value.type = mxvm::VarType::VAR_INTEGER; program->vars["%rax"].var_value.int_value = length; } extern "C" void mxvm_string_strcmp(mxvm::Program *program, std::vector<mxvm::Operand> &operand) { if (operand.size() != 2) { throw mx::Exception("strcmp requires two pointer/string arguments."); } std::string &var1 = operand[0].op; std::string &var2 = operand[1].op; if (!program->isVariable(var1) || !program->isVariable(var2)) { throw mx::Exception("strcmp arguments must be variables (pointer or string)."); } mxvm::Variable &v1 = program->getVariable(var1); mxvm::Variable &v2 = program->getVariable(var2); const char *s1 = nullptr; const char *s2 = nullptr; if (v1.type == mxvm::VarType::VAR_POINTER) { mxvm::except_assert("strcmp: pointer: " + v1.var_name + " is null", (v1.var_value.ptr_value != nullptr)); s1 = reinterpret_cast<const char *>(v1.var_value.ptr_value); } else if (v1.type == mxvm::VarType::VAR_STRING) { s1 = v1.var_value.str_value.c_str(); } else { throw mx::Exception("strcmp first argument must be a pointer or string variable."); } if (v2.type == mxvm::VarType::VAR_POINTER) { mxvm::except_assert("strcmp: pointer: " + v2.var_name + " is null", (v2.var_value.ptr_value != nullptr)); s2 = reinterpret_cast<const char *>(v2.var_value.ptr_value); } else if (v2.type == mxvm::VarType::VAR_STRING) { s2 = v2.var_value.str_value.c_str(); } else { throw mx::Exception("strcmp second argument must be a pointer or string variable."); } int64_t cmp_result = strcmp(s1, s2); program->vars["%rax"].type = mxvm::VarType::VAR_INTEGER; program->vars["%rax"].var_value.type = mxvm::VarType::VAR_INTEGER; program->vars["%rax"].var_value.int_value = cmp_result; } extern "C" void mxvm_string_strncpy(mxvm::Program *program, std::vector<mxvm::Operand> &operand) { if (operand.size() != 3) throw mx::Exception("strncpy requires (dest, src, len)"); if (!program->isVariable(operand[0].op) || !program->isVariable(operand[1].op)) throw mx::Exception("strncpy arguments must be variables"); mxvm::Variable &dest = program->getVariable(operand[0].op); mxvm::Variable &src = program->getVariable(operand[1].op); int64_t n = operand[2].op_value; if (program->isVariable(operand[2].op)) { mxvm::Variable &iv = program->getVariable(operand[2].op); n = iv.var_value.int_value; } if (!isStringLike(dest)) throw mx::Exception("strncpy dest must be pointer or string"); if (!isStringLike(src)) throw mx::Exception("strncpy src must be pointer or string"); const char *sptr = asReadPtr(src); if (dest.type == mxvm::VarType::VAR_POINTER) { if (dest.var_value.buffer_size && (int64_t)dest.var_value.buffer_size < n + 1) throw mx::Exception("strncpy dest buffer too small"); char *dptr = asWritePtr(dest); std::strncpy(dptr, sptr, n); dptr[n] = '\0'; } else { dest.var_value.str_value.assign(sptr, (size_t)n); } program->vars["%rax"].type = mxvm::VarType::VAR_INTEGER; program->vars["%rax"].var_value.int_value = n; } extern "C" void mxvm_string_strncat(mxvm::Program *program, std::vector<mxvm::Operand> &operand) { if (operand.size() != 3) throw mx::Exception("strncat requires (dest, src, len)"); if (!program->isVariable(operand[0].op) || !program->isVariable(operand[1].op)) throw mx::Exception("strncat arguments must be variables"); mxvm::Variable &dest = program->getVariable(operand[0].op); mxvm::Variable &src = program->getVariable(operand[1].op); int64_t n = operand[2].op_value; if (program->isVariable(operand[2].op)) { mxvm::Variable &iv = program->getVariable(operand[2].op); n = iv.var_value.int_value; } if (!isStringLike(dest)) throw mx::Exception("strncat dest must be pointer or string"); if (!isStringLike(src)) throw mx::Exception("strncat src must be pointer or string"); const char *sptr = asReadPtr(src); if (dest.type == mxvm::VarType::VAR_POINTER) { if (dest.var_value.buffer_size) { size_t have = std::strlen(reinterpret_cast<const char *>(dest.var_value.ptr_value)); if (have + (size_t)n + 1 > dest.var_value.buffer_size) throw mx::Exception("strncat dest buffer too small"); } std::strncat(reinterpret_cast<char *>(dest.var_value.ptr_value), sptr, n); } else { dest.var_value.str_value.append(sptr, (size_t)n); } program->vars["%rax"].type = mxvm::VarType::VAR_INTEGER; program->vars["%rax"].var_value.int_value = n; } extern "C" void mxvm_string_snprintf(mxvm::Program *program, std::vector<mxvm::Operand> &operand) { if (operand.size() < 4) { throw mx::Exception("snprintf requires at least destination, size, format, and one argument."); } std::string &dest_var = operand[0].op; int64_t n = operand[1].op_value; std::string &fmt_var = operand[2].op; if (!program->isVariable(dest_var) || !program->isVariable(fmt_var)) { throw mx::Exception("snprintf destination: " + dest_var + " and format: " + fmt_var + " must be variables."); } if (program->isVariable(operand[1].op)) { mxvm::Variable &iv = program->getVariable(operand[1].op); n = iv.var_value.int_value; } mxvm::except_assert("snprintf size is zero", n != 0); mxvm::Variable &dest = program->getVariable(dest_var); mxvm::Variable &fmt = program->getVariable(fmt_var); if (fmt.type != mxvm::VarType::VAR_STRING) { throw mx::Exception("snprintf format must be a string variable."); } std::ostringstream oss; size_t argIndex = 3; const char *format = fmt.var_value.str_value.c_str(); char buffer[4096]; for (size_t i = 0; i < fmt.var_value.str_value.length(); ++i) { if (format[i] == '%' && i + 1 < fmt.var_value.str_value.length()) { size_t start = i; size_t j = i + 1; while (j < fmt.var_value.str_value.length() && (format[j] == '-' || format[j] == '+' || format[j] == ' ' || format[j] == '#' || format[j] == '0' || (format[j] >= '0' && format[j] <= '9') || format[j] == '.' || format[j] == 'l' || format[j] == 'h' || format[j] == 'z' || format[j] == 'j' || format[j] == 't')) { ++j; } if (j < fmt.var_value.str_value.length() && format[j] == '%') { oss << '%'; i = j; continue; } if (j >= fmt.var_value.str_value.length()) break; if (!std::isalpha(format[j])) { oss << format[i]; continue; } std::string spec(format + start, format + j + 1); if (argIndex < operand.size()) { std::string &arg_var = operand[argIndex++].op; if (!program->isVariable(arg_var)) { throw mx::Exception("snprintf argument must be a variable."); } mxvm::Variable &arg = program->getVariable(arg_var); if (arg.type == mxvm::VarType::VAR_INTEGER) { std::snprintf(buffer, sizeof(buffer), spec.c_str(), arg.var_value.int_value); } else if (arg.type == mxvm::VarType::VAR_POINTER || arg.type == mxvm::VarType::VAR_EXTERN) { std::snprintf(buffer, sizeof(buffer), spec.c_str(), arg.var_value.ptr_value); } else if (arg.type == mxvm::VarType::VAR_FLOAT) { std::snprintf(buffer, sizeof(buffer), spec.c_str(), arg.var_value.float_value); } else if (arg.type == mxvm::VarType::VAR_STRING) { std::snprintf(buffer, sizeof(buffer), spec.c_str(), arg.var_value.str_value.c_str()); } else if (arg.type == mxvm::VarType::VAR_BYTE) { std::snprintf(buffer, sizeof(buffer), spec.c_str(), arg.var_value.int_value); } else { std::snprintf(buffer, sizeof(buffer), "%s", "(unsupported)"); } oss << buffer; } else { oss << spec; } i = j; } else { oss << format[i]; } } if (dest.type == mxvm::VarType::VAR_POINTER) { mxvm::except_assert("snprintf dest pointer is null", dest.var_value.ptr_value != nullptr); std::strncpy(reinterpret_cast<char *>(dest.var_value.ptr_value), oss.str().c_str(), n); if (n > 0) { reinterpret_cast<char *>(dest.var_value.ptr_value)[n - 1] = '\0'; } } else if (dest.type == mxvm::VarType::VAR_STRING) { dest.var_value.str_value = oss.str().substr(0, n); } else { throw mx::Exception("snprintf destination must be a pointer or string variable."); } program->vars["%rax"].type = mxvm::VarType::VAR_INTEGER; program->vars["%rax"].var_value.type = mxvm::VarType::VAR_INTEGER; program->vars["%rax"].var_value.int_value = oss.str().length(); } extern "C" void mxvm_string_strfind(mxvm::Program *program, std::vector<mxvm::Operand> &operand) { if (operand.size() != 3) throw mx::Exception("strfind requires (haystack, needle, start)"); if (!program->isVariable(operand[0].op) || !program->isVariable(operand[1].op)) throw mx::Exception("strfind arguments must be variables"); mxvm::Variable &hay = program->getVariable(operand[0].op); mxvm::Variable &needle = program->getVariable(operand[1].op); int64_t start = operand[2].op_value; if (program->isVariable(operand[2].op)) { mxvm::Variable &iv = program->getVariable(operand[2].op); start = iv.var_value.int_value; } if (!isStringLike(hay) || !isStringLike(needle)) throw mx::Exception("strfind args must be pointer or string"); std::string H = asReadPtr(hay); std::string N = asReadPtr(needle); size_t pos = H.find(N, (size_t)start); int64_t result = (pos == std::string::npos) ? -1 : (int64_t)pos; program->vars["%rax"].type = mxvm::VarType::VAR_INTEGER; program->vars["%rax"].var_value.int_value = result; } extern "C" void mxvm_string_substr(mxvm::Program *program, std::vector<mxvm::Operand> &operand) { if (operand.size() != 5) throw mx::Exception("substr requires 5 args"); if (!program->isVariable(operand[0].op) || !program->isVariable(operand[2].op)) throw mx::Exception("substr dest/src must be variables"); mxvm::Variable &dest = program->getVariable(operand[0].op); mxvm::Variable &src = program->getVariable(operand[2].op); if (!isStringLike(dest) || !isStringLike(src)) throw mx::Exception("substr dest/src must be pointer or string"); int64_t size = operand[1].op_value; int64_t pos = operand[3].op_value; int64_t len = operand[4].op_value; if (program->isVariable(operand[1].op)) size = program->getVariable(operand[1].op).var_value.int_value; if (program->isVariable(operand[3].op)) pos = program->getVariable(operand[3].op).var_value.int_value; if (program->isVariable(operand[4].op)) len = program->getVariable(operand[4].op).var_value.int_value; std::string S = asReadPtr(src); if (pos < 0) pos = 0; std::string out = (pos > (int64_t)S.size()) ? "" : S.substr((size_t)pos, (size_t)len); if (dest.type == mxvm::VarType::VAR_POINTER) { mxvm::except_assert("substr dest ptr null", dest.var_value.ptr_value != nullptr); if (size <= 0 || (dest.var_value.buffer_size && (size_t)size > dest.var_value.buffer_size)) size = (int64_t)dest.var_value.buffer_size; std::strncpy(reinterpret_cast<char *>(dest.var_value.ptr_value), out.c_str(), (size_t)size); if (size > 0) reinterpret_cast<char *>(dest.var_value.ptr_value)[size - 1] = '\0'; } else { dest.var_value.str_value = out.substr(0, (size_t)size); } program->vars["%rax"].type = mxvm::VarType::VAR_INTEGER; program->vars["%rax"].var_value.int_value = (int64_t)out.size(); } extern "C" void mxvm_string_strat(mxvm::Program *program, std::vector<mxvm::Operand> &operand) { if (operand.size() == 2 && program->isVariable(operand[0].op)) { int64_t pos = 0; if (operand[1].type == mxvm::OperandType::OP_CONSTANT) { pos = operand[1].op_value; } else { mxvm::Variable &v = program->getVariable(operand[1].op); pos = v.var_value.int_value; } mxvm::Variable &var = program->getVariable(operand[0].op); program->vars["%rax"].type = mxvm::VarType::VAR_INTEGER; program->vars["%rax"].var_value.type = mxvm::VarType::VAR_INTEGER; program->vars["%rax"].var_value.int_value = static_cast<int64_t>(var.var_value.str_value[pos]); } } extern "C" void mxvm_string_pos(mxvm::Program *program, std::vector<mxvm::Operand> &operand) { if (operand.size() != 2) throw mx::Exception("pos requires 2 arguments: (substr, s)"); std::string sub = getStringFromVar(program, operand[0].op); std::string s = getStringFromVar(program, operand[1].op); size_t position = s.find(sub); int64_t result = (position == std::string::npos) ? 0 : static_cast<int64_t>(position + 1); program->vars["%rax"].var_value.int_value = result; program->vars["%rax"].type = mxvm::VarType::VAR_INTEGER; } extern "C" void mxvm_string_copy(mxvm::Program *program, std::vector<mxvm::Operand> &operand) { if (operand.size() != 3) throw mx::Exception("copy requires 3 arguments: (s, index, count)"); std::string s = getStringFromVar(program, operand[0].op); int64_t index = program->getVariable(operand[1].op).var_value.int_value; int64_t count = program->getVariable(operand[2].op).var_value.int_value; std::string result_str = s.substr(index - 1, count); char *new_buf = static_cast<char *>(malloc(result_str.length() + 1)); if (!new_buf) throw mx::Exception("malloc failed in copy()"); strcpy(new_buf, result_str.c_str()); program->vars["%rax"].var_value.ptr_value = new_buf; program->vars["%rax"].var_value.ptr_size = 1; program->vars["%rax"].var_value.ptr_count = static_cast<int64_t>(result_str.length() + 1); program->vars["%rax"].var_value.owns = true; program->vars["%rax"].type = mxvm::VarType::VAR_POINTER; } extern "C" void mxvm_string_insert(mxvm::Program *program, std::vector<mxvm::Operand> &operand) { if (operand.size() != 3) throw mx::Exception("insert requires 3 arguments: (source, dest, index)"); std::string source = getStringFromVar(program, operand[0].op); std::string dest = getStringFromVar(program, operand[1].op); int64_t index = program->getVariable(operand[2].op).var_value.int_value; dest.insert(index - 1, source); char *new_buf = static_cast<char *>(malloc(dest.length() + 1)); if (!new_buf) throw mx::Exception("malloc failed in insert()"); strcpy(new_buf, dest.c_str()); program->vars["%rax"].var_value.ptr_value = new_buf; program->vars["%rax"].var_value.ptr_size = 1; program->vars["%rax"].var_value.ptr_count = static_cast<int64_t>(dest.length() + 1); program->vars["%rax"].var_value.owns = true; program->vars["%rax"].type = mxvm::VarType::VAR_POINTER; } extern "C" void mxvm_string_delete(mxvm::Program *program, std::vector<mxvm::Operand> &operand) { if (operand.size() != 3) throw mx::Exception("delete requires 3 arguments: (s, index, count)"); std::string s = getStringFromVar(program, operand[0].op); int64_t index = program->getVariable(operand[1].op).var_value.int_value; int64_t count = program->getVariable(operand[2].op).var_value.int_value; s.erase(index - 1, count); char *new_buf = static_cast<char *>(malloc(s.length() + 1)); if (!new_buf) throw mx::Exception("malloc failed in delete()"); strcpy(new_buf, s.c_str()); program->vars["%rax"].var_value.ptr_value = new_buf; program->vars["%rax"].var_value.ptr_size = 1; program->vars["%rax"].var_value.ptr_count = static_cast<int64_t>(s.length() + 1); program->vars["%rax"].var_value.owns = true; program->vars["%rax"].type = mxvm::VarType::VAR_POINTER; } extern "C" void mxvm_string_inttostr(mxvm::Program *program, std::vector<mxvm::Operand> &operand) { if (operand.size() != 1) throw mx::Exception("inttostr requires 1 argument: (integer)"); int64_t val = program->getVariable(operand[0].op).var_value.int_value; std::string result_str = std::to_string(val); char *new_buf = static_cast<char *>(malloc(result_str.length() + 1)); if (!new_buf) throw mx::Exception("malloc failed in inttostr()"); strcpy(new_buf, result_str.c_str()); program->vars["%rax"].var_value.ptr_value = new_buf; program->vars["%rax"].var_value.ptr_size = 1; program->vars["%rax"].var_value.ptr_count = static_cast<int64_t>(result_str.length() + 1); program->vars["%rax"].var_value.owns = true; program->vars["%rax"].type = mxvm::VarType::VAR_POINTER; } extern "C" void mxvm_string_strtoint(mxvm::Program *program, std::vector<mxvm::Operand> &operand) { if (operand.size() != 1) throw mx::Exception("strtoint requires 1 argument: (string)"); std::string s = getStringFromVar(program, operand[0].op); int64_t result = 0; try { result = std::stoll(s); } catch (const std::exception &e) { result = 0; } program->vars["%rax"].var_value.int_value = result; program->vars["%rax"].type = mxvm::VarType::VAR_INTEGER; }