17#include <unordered_map>
20 std::string html_escape(
const std::string &s) {
22 out.reserve(s.size() * 12 / 10 + 8);
47 std::string js_escape(
const std::string &s) {
49 out.reserve(s.size() * 12 / 10 + 8);
80 if (
static_cast<unsigned char>(c) < 0x20) {
82 std::snprintf(buf,
sizeof(buf),
"\\x%02x",
static_cast<unsigned char>(c));
90 return html_escape(out);
115 while (!
match(
"}")) {
119 std::string func_name =
token->getTokenValue();
144 program->add_runtime_extern(
mod_name, mod_name1,
"mxvm_" + mod_id +
"_" + f.name, f.name);
146 program->add_extern(
mod_name, f.name,
true);
163 return (
token->getTokenValue() == s);
167 return (
token->getTokenType() == t);
172 throw mx::Exception(
"Syntax Error: Looking for: " + s +
" on line " + std::to_string(
token->getLine()));
177 throw mx::Exception(
"Syntax Error: Looking for: " + std::to_string(
static_cast<unsigned int>(t)) +
" on line " + std::to_string(
token->getLine()));
188 std::cerr <<
"Scanner error: " << e.
why() <<
"\n";
197 throw mx::Exception(
"Error: scanner index out of bounds.\n");
202 std::unique_ptr<ProgramNode> mainProgram =
nullptr;
203 std::vector<std::unique_ptr<ProgramNode>> allDeclarations;
205 for (uint64_t i = 0; i <
scanner.size(); ++i) {
207 std::string tokenValue = token.getTokenValue();
210 name = this->
operator[](i + 1).getTokenValue();
212 if (tokenValue ==
"program" || tokenValue ==
"object") {
216 if (tokenValue ==
"program") {
217 declaration->object =
false;
218 declaration->name = name;
219 mainProgram = std::move(declaration);
222 declaration->object =
true;
223 declaration->name = name;
225 allDeclarations.push_back(std::move(declaration));
232 mainProgram = std::make_unique<ProgramNode>();
233 mainProgram->name = name;
234 mainProgram->object =
false;
238 for (
auto &obj : allDeclarations) {
239 mainProgram->addInlineObject(std::move(obj));
248 std::string name = this->
operator[](index).getTokenValue();
251 if (this->
operator[](index).getTokenValue() ==
",")
254 return std::make_unique<ObjectNode>(name);
258 for (
const auto &statement : sectionNode->
statements) {
259 auto objNode =
dynamic_cast<ObjectNode *
>(statement.get());
261 std::string &name = objNode->name;
268 static std::set<std::string> processing_files;
269 if (processing_files.count(src)) {
272 processing_files.insert(src);
280 file.open(path + src +
".mxvm", std::ios::in);
281 if (!file.is_open()) {
284 std::ostringstream stream;
285 stream << file.rdbuf();
288 std::string full_path = path + src +
".mxvm";
290 if (!std::filesystem::exists(full_path)) {
296 if (!file_validator.
validate(full_path)) {
303 std::unique_ptr<Parser> parser(
new Parser(stream.str()));
306 parser->object_mode =
true;
311 auto ast = parser->parseAST();
315 for (
const auto &inlineObj : ast->inlineObjects) {
316 auto objProgram = std::make_unique<Program>();
317 objProgram->name = inlineObj->name;
318 objProgram->object =
true;
319 objProgram->object_external =
true;
320 objProgram->filename = path + src +
".mxvm";
322 for (
const auto §ion : inlineObj->sections) {
323 auto sectionNode =
dynamic_cast<SectionNode *
>(section.get());
328 parser->processDataSection(sectionNode, objProgram);
330 parser->processCodeSection(sectionNode, objProgram);
332 parser->processModuleSection(sectionNode, objProgram);
334 parser->processObjectSection(sectionNode, objProgram);
339 Program::base->add_object(objProgram->name, objProgram.get());
343 program->objects.push_back(std::move(objProgram));
345 processing_files.erase(src);
353 auto sectionNameToken = this->
operator[](index);
354 std::string sectionName = sectionNameToken.getTokenValue();
357 if (sectionName ==
"data") {
359 }
else if (sectionName ==
"code") {
361 }
else if (sectionName ==
"module") {
363 }
else if (sectionName ==
"object") {
369 auto section = std::make_unique<SectionNode>(sectionType);
372 if (index <
scanner.size() && this->operator[](index).getTokenValue() ==
"{") {
375 while (index <
scanner.size()) {
377 std::string value = token.getTokenValue();
384 if (value ==
"\n" || value ==
" " || value ==
"\t") {
389 if (value ==
"//" || value.starts_with(
"//") || value ==
"#" || value.starts_with(
"#")) {
392 section->addStatement(std::move(comment));
400 section->addStatement(std::move(variable));
405 auto module = parseModule(index);
407 section->addStatement(std::move(module));
414 section->addStatement(std::move(obj));
421 this->operator[](index + 1).getTokenValue() ==
":") {
424 section->addStatement(std::move(label));
430 label->function =
true;
431 section->addStatement(std::move(label));
437 section->addStatement(std::move(instruction));
452 std::string name = this->
operator[](index).getTokenValue();
455 if (this->
operator[](index).getTokenValue() ==
",")
458 return std::make_unique<ModuleNode>(name);
465 bool is_global =
false;
467 if (token.getTokenValue() ==
"export") {
475 std::string typeStr = token.getTokenValue();
477 if (typeStr ==
"int")
479 else if (typeStr ==
"float")
481 else if (typeStr ==
"string")
483 else if (typeStr ==
"ptr")
485 else if (typeStr ==
"array")
487 else if (typeStr ==
"byte")
500 std::string varName = nameToken.getTokenValue();
503 if (index <
scanner.size() && this->operator[](index).getTokenValue() ==
"=") {
507 if (index <
scanner.size() && this->operator[](index).getTokenValue() ==
"-"
514 std::string value = valueToken.getTokenValue();
518 if (value.starts_with(
"\"") && value.ends_with(
"\"")) {
519 value = value.substr(1, value.length() - 2);
523 auto node = std::make_unique<VariableNode>(varType, varName, value);
524 node->is_global = is_global;
531 auto node = std::make_unique<VariableNode>(varType, varName, add_op + value);
532 node->is_global = is_global;
539 auto node = std::make_unique<VariableNode>(varType, varName, value);
540 node->is_global = is_global;
547 auto node = std::make_unique<VariableNode>(varType, varName, value);
548 node->is_global = is_global;
554 auto node = std::make_unique<VariableNode>(varType, varName, value);
555 node->is_global = is_global;
564 buf_size = std::stoll(this->
operator[](index).getTokenValue(),
nullptr, 0);
567 throw mx::Exception(
"string buffer: " + varName +
" requires valid size");
569 auto node = std::make_unique<VariableNode>(varType, varName, buf_size);
570 node->is_global = is_global;
574 auto node = std::make_unique<VariableNode>(varType, varName);
575 node->is_global = is_global;
580 static std::unordered_map<std::string, Inc> instructionMap = {
581 {
"mov",
MOV}, {
"load",
LOAD}, {
"store",
STORE}, {
"add",
ADD}, {
"sub",
SUB}, {
"mul",
MUL}, {
"div",
DIV}, {
"or",
OR}, {
"and",
AND}, {
"xor",
XOR}, {
"not",
NOT}, {
"mod",
MOD}, {
"cmp",
CMP}, {
"fcmp",
FCMP}, {
"jmp",
JMP}, {
"je",
JE}, {
"jne",
JNE}, {
"jl",
JL}, {
"jle",
JLE}, {
"jg",
JG}, {
"jge",
JGE}, {
"jz",
JZ}, {
"jnz",
JNZ}, {
"ja",
JA}, {
"jb",
JB}, {
"jae",
JAE}, {
"jbe",
JBE}, {
"jc",
JC}, {
"jnc",
JNC}, {
"jp",
JP}, {
"jnp",
JNP}, {
"jo",
JO}, {
"jno",
JNO}, {
"js",
JS}, {
"jns",
JNS}, {
"print",
PRINT}, {
"exit",
EXIT}, {
"alloc",
ALLOC}, {
"free",
FREE}, {
"getline",
GETLINE}, {
"push",
PUSH}, {
"pop",
POP}, {
"stack_load",
STACK_LOAD}, {
"stack_store",
STACK_STORE}, {
"stack_sub",
STACK_SUB}, {
"call",
CALL}, {
"ret",
RET}, {
"string_print",
STRING_PRINT}, {
"done",
DONE}, {
"to_int",
TO_INT}, {
"to_float",
TO_FLOAT}, {
"invoke",
INVOKE}, {
"return",
RETURN}, {
"neg",
NEG}, {
"lea",
LEA}, {
"realloc",
REALLOC}};
587 std::string tokenValue = token.getTokenValue();
590 if (index + 1 <
scanner.size() && this->operator[](index + 1).getTokenValue() ==
":") {
595 auto instIt = instructionMap.find(tokenValue);
596 if (instIt == instructionMap.end()) {
597 throw mx::Exception(
"Error invalid instruction: " + tokenValue);
601 Inc instruction = instIt->second;
604 std::vector<Operand> operands;
606 while (index <
scanner.size()) {
608 std::string add_value;
609 if (index <
scanner.size() && this->operator[](index).getTokenValue() ==
"-") {
614 std::string value = operandToken.getTokenValue();
617 if (value ==
"\n" || value ==
"//" || value.starts_with(
"//") ||
618 value ==
"#" || value.starts_with(
"#") || value ==
"}") {
622 if (value ==
"," || value ==
" " || value ==
"\t") {
631 operand.
op = add_value + value;
632 operand.
op_value = std::stoll(add_value + value);
638 if (value.starts_with(
"0x") || value.starts_with(
"0X")) {
639 operand.
op_value = std::stoll(value,
nullptr, 16);
641 operand.
op_value = std::stoll(value,
nullptr, 16);
648 if (index + 1 <
scanner.size() && this->operator[](index + 1).getTokenValue() ==
".") {
652 value = this->
operator[](index).getTokenValue();
655 if (operand.
object.empty())
658 operand.
op = operand.
object +
"." + value;
659 operand.
label = value;
678 if (!operand.
op.empty()) {
679 operands.push_back(operand);
685 return std::make_unique<InstructionNode>(instruction, operands);
689 std::string commentText;
692 if (token.getTokenValue() ==
"//" || token.getTokenValue() ==
"#" ||
693 token.getTokenValue().starts_with(
"//") || token.getTokenValue().starts_with(
"#")) {
697 while (index <
scanner.size()) {
699 if (token.getTokenValue() ==
"\n") {
702 commentText += token.getTokenValue() +
" ";
706 return std::make_unique<CommentNode>(commentText);
719 std::string labelName = token.getTokenValue();
722 if (index <
scanner.size() && this->operator[](index).getTokenValue() ==
":") {
724 return std::make_unique<LabelNode>(labelName);
733 std::cout << ast->toString() << std::endl;
740 if (!
validator.validate(program->filename)) {
744 std::cerr << e.what() <<
"\n";
750 program->name = ast->name;
751 if (!ast->root_name.empty()) {
752 program->root_name = ast->root_name;
753 program->name = ast->root_name;
754 program->object =
false;
756 program->object =
true;
759 for (
const auto §ion : ast->sections) {
760 auto sectionNode =
dynamic_cast<SectionNode *
>(section.get());
775 for (
const auto &inlineObj : ast->inlineObjects) {
776 auto objProgram = std::make_unique<Program>();
777 objProgram->name = inlineObj->name;
778 objProgram->object =
true;
779 objProgram->object_external =
false;
780 objProgram->filename = program->filename;
784 for (
const auto §ion : inlineObj->sections) {
785 auto sectionNode =
dynamic_cast<SectionNode *
>(section.get());
803 Program::base->add_object(objProgram->name, objProgram.get());
805 program->objects.push_back(std::move(objProgram));
809 std::set<std::string> generated_objects;
811 std::function<void(std::unique_ptr<Program> &)> compileAllObjects;
813 [&](std::unique_ptr<Program> &p) {
816 for (
auto &obj : p->objects) {
817 if (obj && generated_objects.find(obj->name) == generated_objects.end()) {
819 generated_objects.insert(obj->name);
821 std::ofstream htmlFile(obj->name +
".html");
822 if (htmlFile.is_open()) {
828 compileAllObjects(obj);
831 compileAllObjects(program);
834 if (program->object ==
false) {
835 if (!program->validateNames(
validator)) {
836 throw mx::Exception(
"Could not validate variables/functions\n");
843 for (
const auto &objPtr : program->objects) {
846 for (
const auto &ext : objPtr->external) {
847 if (std::find(names.begin(), names.end(), std::make_pair(ext.mod, ext.name)) == names.end())
848 names.push_back(std::make_pair(ext.mod, ext.name));
855 out << R
"(<div class="object-section">
856 <div class="object-title">Object: <span style="color: lime;"><strong>)"
857 << html_escape(objPtr->name) << R"(</span></strong></div>)";
859 <div class="stats" style="margin-bottom:30px;">
860 <div class="stat-card">
861 <span class="number">)"
862 << objPtr->vars.size() << R"(</span>
863 <span class="label">Variables</span>
865 <div class="stat-card">
866 <span class="number">)"
867 << objPtr->inc.size() << R"(</span>
868 <span class="label">Instructions</span>
870 <div class="stat-card">
871 <span class="number">)"
872 << objPtr->labels.size() << R"(</span>
873 <span class="label">Labels</span>
875 <div class="stat-card">
876 <span class="number">)"
877 << objPtr->objects.size() << R"(</span>
878 <span class="label">Objects</span>
883 <div class="section">
884 <div class="section-header"><span class="icon">🧮</span> Variables</div>
885 <div class="section-content">)";
886 if (objPtr->vars.empty()) {
887 out << R
"(<div class="no-data">No variables defined</div>)";
889 out << R
"(<div class="variables-grid">)";
890 for (
const auto &var : objPtr->vars) {
891 out << R
"(<div class="variable-card">
892 <div class="variable-name">)"
893 << html_escape(var.first) << R"(</div>
894 <div class="variable-type">)";
895 switch (var.second.type) {
925 <div class="variable-value">)";
926 switch (var.second.type) {
929 out << var.second.var_value.int_value;
932 out << std::fixed << std::setprecision(6) << var.second.var_value.float_value;
939 if (var.second.var_value.ptr_value ==
nullptr)
942 out << var.second.var_value.ptr_value;
945 out << var.second.var_value.label_value;
948 out << var.second.var_value.int_value;
958 out << R"(<div class="section">
959 <div class="section-header"><span class="icon">🔖</span> Labels</div>
960 <div class="section-content">)";
961 if (objPtr->labels.empty()) {
962 out << R
"(<div class="no-data">No labels defined</div>)";
964 out << R
"(<table class="instructions-table">
973 for (
const auto &label : objPtr->labels) {
976 << html_escape(label.first) << R"(</td>
978 << "0x" << std::hex << label.second.first << std::dec << R
"(</td>
980 << (label.second.second ? "true" :
"false") << R
"(</td>
989 <div class="section">
990 <div class="section-header"><span class="icon">⚙</span> Instructions</div>
991 <div class="section-content">)";
992 if (objPtr->inc.empty()) {
993 out << R
"(<div class="no-data">No instructions defined</div>)";
995 out << R
"(<table class="instructions-table">
1000 <th>Instruction</th>
1004 <th>Extra Operands</th>
1008 for (
size_t i = 0; i < objPtr->inc.size(); ++i) {
1009 const auto &instr = objPtr->inc[i];
1011 <<
"<td>0x" << std::hex << std::uppercase << i << std::dec <<
"</td>"
1012 <<
"<td class=\"opcode\">0x" << std::hex << std::uppercase << static_cast<int>(instr.instruction) << std::dec <<
"</td>"
1013 <<
"<td class=\"opcode-name\">" << html_escape(
IncType[
static_cast<int>(instr.instruction)]) <<
"</td>"
1014 <<
"<td class=\"operand\">" << html_escape(instr.op1.op) <<
"</td>"
1015 <<
"<td class=\"operand\">" << html_escape(instr.op2.op) <<
"</td>"
1016 <<
"<td class=\"operand\">" << html_escape(instr.op3.op) <<
"</td>"
1017 <<
"<td class=\"operand\">";
1018 for (
size_t j = 0; j < instr.vop.size(); ++j) {
1021 out << html_escape(instr.vop[j].op);
1023 out <<
"</td></tr>";
1030 if (!objPtr->objects.empty()) {
1031 out << R
"(<div class="section">
1032 <div class="section-header"><span class="icon">🧩</span> Nested Objects</div>
1033 <div class="section-content">)";
1034 for (
const auto &nestedObj : objPtr->objects) {
1042 out << R"(<div class="section">
1043 <div class="section-header"><span class="icon">📜</span> Compiled Assembly</div>
1044 <div class="section-content">
1045 <textarea id="asm-)"
1046 << html_escape(objPtr->name) << R"(" readonly style="width:100%;height:300px;background:#222;color:#e0e0e0;border-radius:8px;padding:16px;font-family:'Fira Mono', 'Consolas', 'Courier New', monospace;font-size:1rem;">)";
1047 out << html_escape(objPtr->assembly_code);
1048 out << R"(</textarea>
1049 <div style="margin-top:10px;">)";
1050 out << "<button class=\"btn\" onclick=\"copyAsm('asm-" << js_escape(objPtr->name) <<
"')\">Copy</button>";
1051 out <<
"<button class=\"btn\" onclick=\"downloadAsm('asm-" << js_escape(objPtr->name) <<
"', '" << js_escape(objPtr->name) <<
".s')\">Download</button>";
1059 out << R
"(<!DOCTYPE html>
1062 <meta charset="UTF-8">
1063 <meta name="viewport" content="width=device-width, initial-scale=1.0">
1064 <title>MXVM Debug Report - )"
1065 << html_escape(program->name) << R"(</title>
1072 box-sizing: border-box;
1075 font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
1086 background: transparent;
1090 border-radius: 15px;
1092 margin-bottom: 30px;
1093 box-shadow: 0 8px 32px rgba(183, 28, 28, 0.2);
1094 border: 1px solid #b71c1c;
1099 margin-bottom: 10px;
1101 text-shadow: 1px 1px 2px #000;
1106 margin-bottom: 20px;
1114 background: #b71c1c;
1117 border-radius: 10px;
1120 box-shadow: 0 4px 15px rgba(183, 28, 28, 0.3);
1122 .stat-card .number {
1132 background: #181818;
1133 border-radius: 15px;
1134 margin-bottom: 30px;
1135 box-shadow: 0 8px 32px rgba(183, 28, 28, 0.1);
1136 border: 1px solid #b71c1c;
1140 background: #b71c1c;
1146 align-items: center;
1155 grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
1159 border: 1px solid #b71c1c;
1160 border-radius: 10px;
1163 transition: transform 0.2s, box-shadow 0.2s;
1165 .variable-card:hover {
1166 transform: translateY(-2px);
1167 box-shadow: 0 4px 20px rgba(183, 28, 28, 0.2);
1176 display: inline-block;
1177 background: #b71c1c;
1180 border-radius: 20px;
1183 margin-bottom: 10px;
1186 font-family: 'Courier New', monospace;
1187 background: #181818;
1188 border: 1px solid #b71c1c;
1192 word-break: break-all;
1194 .instructions-table {
1196 border-collapse: collapse;
1198 border-radius: 10px;
1200 box-shadow: 0 2px 10px rgba(183, 28, 28, 0.1);
1202 .instructions-table th {
1203 background: #b71c1c;
1209 .instructions-table td {
1211 border-bottom: 1px solid #b71c1c;
1214 .instructions-table tr:nth-child(even) {
1215 background: #181818;
1217 .instructions-table tr:hover {
1218 background: #2d2d2d;
1225 font-family: 'Courier New', monospace;
1230 border-left: 3px solid #b71c1c;
1237 margin-bottom: 15px;
1241 border-left: 4px solid #b71c1c;
1244 background: #b71c1c;
1252 transition: background 0.2s;
1255 background: #ff5252;
1260 display: inline-block;
1268 @media (max-width: 768px) {
1276 justify-content: center;
1279 grid-template-columns: 1fr;
1281 .instructions-table {
1288 bool mainHasContent = !program->vars.empty() || !program->inc.empty() || !program->labels.empty();
1289 bool hasObjects = !program->objects.empty();
1292 <div class="container">
1293 <div class="header">
1294 <h1>MXVM Debug Report</h1>
1295 <div class="subtitle">Program: <span style="color: blue;"><strong>)"
1296 << html_escape(program->name) << R"(</span></strong></div>
1298 <div class="stat-card">
1299 <span class="number">)"
1300 << program->vars.size() << R"(</span>
1301 <span class="label">Variables</span>
1303 <div class="stat-card">
1304 <span class="number">)"
1305 << program->inc.size() << R"(</span>
1306 <span class="label">Instructions</span>
1308 <div class="stat-card">
1309 <span class="number">)"
1310 << program->objects.size() << R"(</span>
1311 <span class="label">Objects</span>
1313 <div class="stat-card">
1314 <span class="number">)"
1315 << program->labels.size() << R"(</span>
1316 <span class="label">Labels</span>
1321 if (mainHasContent) {
1322 out << R
"(<div class="section">
1323 <div class="section-header">
1324 <span class="icon">🧮</span>
1327 <div class="section-content">)";
1329 if (program->vars.empty()) {
1330 out << R
"(<div class="no-data">No variables defined</div>)";
1332 out << R
"(<div class="variables-grid">)";
1333 for (
const auto &var : program->vars) {
1334 out << R
"(<div class="variable-card">
1335 <div class="variable-name">)"
1336 << html_escape(var.first) << R"(</div>
1337 <div class="variable-type">)";
1339 switch (var.second.type) {
1370 <div class="variable-value">)";
1372 switch (var.second.type) {
1375 out << var.second.var_value.int_value;
1378 out << std::fixed << std::setprecision(6) << var.second.var_value.float_value;
1385 if (var.second.var_value.ptr_value ==
nullptr)
1388 out << var.second.var_value.ptr_value;
1391 out << var.second.var_value.label_value;
1394 out << var.second.var_value.int_value;
1406 out << R"(<div class="section">
1407 <div class="section-header"><span class="icon">🔖</span> Labels</div>
1408 <div class="section-content">)";
1409 if (program->labels.empty()) {
1410 out << R
"(<div class="no-data">No labels defined</div>)";
1412 out << R
"(<table class="instructions-table">
1420 for (
const auto &label : program->labels) {
1421 out <<
"<tr><td>" << html_escape(label.first) <<
"</td><td>0x" << std::hex << label.second.first << std::dec << R
"(</td>
1423 << (label.second.second ? "true" :
"false") << R
"(</td>
1432 out << R"(<div class="section">
1433 <div class="section-header">
1434 <span class="icon">⚙</span>
1437 <div class="section-content">)";
1439 if (program->inc.empty()) {
1440 out << R
"(<div class="no-data">No instructions defined</div>)";
1442 out << R
"(<table class="instructions-table">
1447 <th>Instruction</th>
1451 <th>Extra Operands</th>
1456 for (
size_t i = 0; i < program->inc.size(); ++i) {
1457 const auto &instr = program->inc[i];
1459 <<
"<td>0x" << std::hex << std::uppercase << i << std::dec <<
"</td>"
1460 <<
"<td class=\"opcode\">0x" << std::hex << std::uppercase << static_cast<int>(instr.instruction) << std::dec <<
"</td>"
1461 <<
"<td class=\"opcode-name\">" << html_escape(
IncType[
static_cast<int>(instr.instruction)]) <<
"</td>"
1462 <<
"<td class=\"operand\">" << html_escape(instr.op1.op) <<
"</td>"
1463 <<
"<td class=\"operand\">" << html_escape(instr.op2.op) <<
"</td>"
1464 <<
"<td class=\"operand\">" << html_escape(instr.op3.op) <<
"</td>"
1465 <<
"<td class=\"operand\">";
1466 for (
size_t j = 0; j < instr.vop.size(); ++j) {
1469 out << html_escape(instr.vop[j].op);
1471 out <<
"</td></tr>";
1476 out << R"(<div class="section">
1477 <div class="section-header"><span class="icon">📜</span> Compiled Assembly</div>
1478 <div class="section-content">
1479 <textarea id="asm-main" readonly style="width:100%;height:300px;background:#222;color:#e0e0e0;border-radius:8px;padding:16px;font-family:'Fira Mono', 'Consolas', 'Courier New', monospace;font-size:1rem;">)";
1480 out << html_escape(program->assembly_code);
1481 out << R"(</textarea>
1482 <div style="margin-top:10px;">)";
1483 out << "<button class=\"btn\" onclick=\"copyAsm('asm-main')\">Copy</button>";
1484 out <<
"<button class=\"btn\" onclick=\"downloadAsm('asm-main', '" << js_escape(program->name) <<
".s')\">Download</button>";
1492 out << R
"(<div class="section">
1493 <div class="section-header">
1494 <span class="icon">📦</span>
1497 <div class="section-content">)";
1499 for (
const auto &obj : program->objects) {
1513 function copyAsm(id) {
1514 var textarea = document.getElementById(id);
1516 navigator.clipboard.writeText(textarea.value);
1518 function downloadAsm(id, filename) {
1519 var textarea = document.getElementById(id);
1520 var blob = new Blob([textarea.value], {type: 'text/plain'});
1521 var link = document.createElement('a');
1522 link.href = window.URL.createObjectURL(blob);
1523 link.download = filename;
1524 document.body.appendChild(link);
1526 document.body.removeChild(link);
1536 for (
const auto &statement : sectionNode->statements) {
1537 auto variableNode =
dynamic_cast<VariableNode *
>(statement.get());
1540 var.type = variableNode->type;
1541 var.var_name = program->name +
"." + variableNode->name;
1542 if (variableNode->hasInitializer) {
1547 var.var_value.buffer_size = variableNode->buffer_size;
1548 var.is_global = variableNode->is_global;
1549 var.obj_name = program->name;
1550 program->add_variable(var.var_name, var);
1556 for (
const auto &statement : sectionNode->statements) {
1557 auto moduleNode =
dynamic_cast<ModuleNode *
>(statement.get());
1559 std::string &name = moduleNode->name;
1566 std::string module_name =
"libmxvm_" + src;
1571 std::string shared_ext =
".so";
1573 shared_ext =
".dll";
1574#elif defined(__APPLE__)
1575 shared_ext =
".dylib";
1578 std::string module_path_so =
module_path +
"modules/" + src +
"/" + module_name + shared_ext;
1581 if (!std::filesystem::exists(module_path_so)) {
1582 std::string alt =
module_path +
"modules/" + src +
"/mxvm_" + src + shared_ext;
1583 if (std::filesystem::exists(alt))
1584 module_path_so = alt;
1587 std::string msys2prefix;
1589 const char *msys2env = std::getenv(
"MSYSTEM_PREFIX");
1590 if (msys2env !=
nullptr) {
1591 msys2prefix = msys2env;
1592 size_t pos = msys2prefix.find(
"/msys64/");
1593 if (
pos != std::string::npos) {
1594 msys2prefix = msys2prefix.substr(0,
pos + 8);
1600 std::string module_src = msys2prefix +
include_path + src +
"/" + src +
".mxvm";
1602 file.open(module_src, std::ios::in);
1603 if (!file.is_open()) {
1605 module_src =
module_path +
"modules/" + src +
"/" + src +
".mxvm";
1606 file.open(module_src, std::ios::in);
1608 if (!file.is_open()) {
1609 throw mx::Exception(
"Error could not find module source file: " + module_src);
1611 std::ostringstream data;
1612 data << file.rdbuf();
1614 if (mod_parser.scan() > 0) {
1615 if (mod_parser.parse() && mod_parser.generateProgramCode(this->parser_mode, src, module_path_so, program)) {
1618 throw mx::Exception(
"Error parsing module file.\n");
1624 std::unordered_map<std::string, size_t> labelMap;
1626 size_t instructionIndex = 0;
1627 for (
const auto &statement : sectionNode->statements) {
1628 if (
auto labelNode =
dynamic_cast<LabelNode *
>(statement.get())) {
1629 labelMap[labelNode->name] = instructionIndex;
1630 program->add_label(labelNode->name, instructionIndex, labelNode->function);
1631 }
else if (
dynamic_cast<InstructionNode *
>(statement.get())) {
1635 for (
const auto &statement : sectionNode->statements) {
1636 auto instructionNode =
dynamic_cast<InstructionNode *
>(statement.get());
1637 if (instructionNode) {
1639 instr.instruction = instructionNode->instruction;
1640 if (instructionNode->operands.size() > 0) {
1641 instr.op1 = instructionNode->operands[0];
1644 if (instructionNode->operands.size() > 1) {
1645 instr.op2 = instructionNode->operands[1];
1648 if (instructionNode->operands.size() > 2) {
1649 instr.op3 = instructionNode->operands[2];
1652 if (instructionNode->operands.size() > 3) {
1653 for (
size_t i = 3; i < instructionNode->operands.size(); ++i) {
1654 Operand extraOp = instructionNode->operands[i];
1656 instr.vop.push_back(extraOp);
1659 program->add_instruction(instr);
1668 if (value.starts_with(
"0x") || value.starts_with(
"0X")) {
1669 var.var_value.int_value = std::stoull(value,
nullptr, 16);
1671 var.var_value.int_value = std::stoull(value);
1673 var.var_value.type = type;
1677 var.var_value.float_value = std::stod(value);
1681 var.var_value.str_value = value;
1683 var.var_value.buffer_size = buf_size;
1687 var.var_value.ptr_value =
nullptr;
1688 if (value ==
"null" || value ==
"0") {
1689 var.var_value.ptr_value =
nullptr;
1690 var.var_value.str_value =
"null";
1696 var.var_value.label_value = value;
1709 var.var_value.int_value = 0;
1714 var.var_value.float_value = 0.0;
1719 var.var_value.str_value =
"";
1724 var.var_value.ptr_value =
nullptr;
1729 var.var_value.label_value =
"";
1740 if (!operand.label.empty()) {
1741 auto it = labelMap.find(operand.label);
1742 if (it != labelMap.end()) {
1743 operand.op_value =
static_cast<int>(it->second);
1749 auto program = std::make_unique<ProgramNode>();
1751 std::string tokenValue = this->
operator[](i).getTokenValue();
1752 program->object = (tokenValue ==
"object");
1757 program->name = nameToken.getTokenValue();
1758 if (program->object ==
false)
1759 program->root_name = nameToken.getTokenValue();
1763 if (i <
scanner.size() && this->operator[](i).getTokenValue() ==
"{") {
1768 std::string innerValue = innerToken.getTokenValue();
1770 if (innerValue ==
"}") {
1775 if (innerValue ==
"section") {
1778 program->addSection(std::move(section));
1790 std::string objectFileName = objProgram->name +
".s";
1791 std::ofstream objectFile(objectFileName);
1792 if (!objectFile.is_open()) {
1793 throw mx::Exception(
"Could not create object assembly file: " + objectFileName);
1796 std::ostringstream code_v;
1797 objProgram->generateCode(
platform, objProgram->object, code_v);
1798 objProgram->assembly_code = code_v.str();
1799 std::string opt_code = objProgram->gen_optimize(objProgram->assembly_code,
platform);
1800 objProgram->assembly_code = opt_code;
1801 objectFile << opt_code;
1804 std::cout <<
"Generated object assembly file: " << objectFileName << std::endl;
1811 const std::unique_ptr<Program> &objProgram) {
1812 for (
const auto &[labelName, labelInfo] : objProgram->labels) {
1817 for (
const auto &ext : objProgram->external) {
1822 for (
const auto &child : objProgram->objects) {
General-purpose exception with errno-aware factory method.
void add_filename(const std::string &fname)
Record a source filename for debug/error reporting.
void add_extern(const std::string &mod, const std::string &name, bool module)
Register an external function import.
static Base * base
pointer to the main program base
bool parse()
Parse the module declarations.
Mode parser_mode
interpret or compile
scan::TToken * token
current token pointer
uint64_t index
current token index
void require(const std::string &s)
Require the current token value to match, or throw.
ModuleParser(const Mode &m, const std::string &mod_name, const std::string &source)
Construct a module parser.
bool match(const std::string &s)
Check if the current token value matches and advance.
bool next()
Advance to the next token.
std::string mod_name
module name
scan::Scanner scanner
underlying scanner
scan::TToken operator[](size_t pos)
Access a token by position.
uint64_t scan()
Tokenize the module source.
bool generateProgramCode(const Mode &m, const std::string &mod_id, const std::string &mod_name, std::unique_ptr< Program > &program)
Register the module's external functions in a Program.
std::vector< ExternalFunction > functions
parsed external function declarations
scan::TToken at(size_t pos)
Access a token by position (bounds-checked).
AST node for an object import declaration.
std::string object_path
search path for .mxvm object files
std::unique_ptr< ModuleNode > parseModule(uint64_t &index)
Parser(const std::string &source)
Construct a parser from MXVM source text.
std::unique_ptr< ObjectNode > parseObject(uint64_t &index)
bool generateDebugHTML(std::ostream &out, std::unique_ptr< Program > &program)
Generate debug HTML output for the program.
void generateObjectAssemblyFile(std::unique_ptr< Program > &objProgram)
Generate a native assembly file for an object.
std::unique_ptr< CommentNode > parseComment(uint64_t &index)
auto operator[](size_t pos)
Access a token by position.
void collectObjectNames(std::vector< std::pair< std::string, std::string > > &names, const std::unique_ptr< Program > &program)
std::unique_ptr< ProgramNode > parseProgramOrObject(uint64_t &index)
Parse a top-level program or object node.
void registerObjectExterns(std::unique_ptr< Program > &mainProgram, const std::unique_ptr< Program > &objProgram)
Register external functions from an object into the main program.
void processObjectFile(const std::string &src, std::unique_ptr< Program > &program)
void processModuleFile(const std::string &src, std::unique_ptr< Program > &program)
void resolveLabelReference(Operand &operand, const std::unordered_map< std::string, size_t > &labelMap)
void setDefaultVariableValue(Variable &var, VarType type)
std::unique_ptr< LabelNode > parseLabel(uint64_t &index)
void processDataSection(SectionNode *sectionNode, std::unique_ptr< Program > &program)
std::unique_ptr< SectionNode > parseSection(uint64_t &index)
uint64_t scan()
Tokenize the source and return the token count.
std::string include_path
system module include path
void parse()
Parse all tokens into the internal AST.
void processModuleSection(SectionNode *sectionNode, std::unique_ptr< Program > &program)
void printObjectHTML(std::ostream &out, const std::unique_ptr< Program > &objPtr)
void setVariableValue(Variable &var, VarType type, const std::string &value, size_t buf_size=0)
std::string module_path
search path for .mxvm module files
std::unique_ptr< ProgramNode > parseAST()
Build the full AST from tokens.
bool generateProgramCode(const Mode &m, std::unique_ptr< Program > &program)
Walk the AST and populate a Program with instructions and variables.
friend class ModuleParser
void processObjectSection(SectionNode *sectionNode, std::unique_ptr< Program > &program)
std::unique_ptr< VariableNode > parseDataVariable(uint64_t &index)
void processCodeSection(SectionNode *sectionNode, std::unique_ptr< Program > &program)
std::unique_ptr< InstructionNode > parseCodeInstruction(uint64_t &index)
static std::string escapeNewLines(const std::string &text)
Escape newline characters in a string literal for assembly output.
Represents a program section (.data, .code, .module, .object).
std::vector< std::unique_ptr< ASTNode > > statements
SectionType
Section type discriminator.
Validates MXVM source for correct variable/label usage and instruction operands.
bool validate(const std::string &name)
Run full validation on the source program.
Exception thrown during scanning errors.
StringType why() const
Get the error description.
int64_t pos(const char *substr, const char *s)
Exception class, hex formatting utilities, and terminal color definitions.
std::string Col(const std::string &col, std::string color)
Wrap a string with ANSI color codes if terminal supports color.
AST node hierarchy for the MXVM parser using the Visitor pattern.
Intermediate code representation, execution engine, and native x64 code generation (SysV + Win64).
Parser that tokenizes and parses MXVM source into programs, handles modules/objects,...
std::vector< std::string > IncType
String representations of Inc opcodes, indexed by enum value.
Inc
MXVM instruction set opcodes.
@ REALLOC
Reallocate a dynamic memory block: realloc dest, elemSize, count.
const std::string BRIGHT_GREEN
bool instruct_mode
enable instruction trace mode
bool debug_mode
enable verbose debug output during parsing
VarType
MXVM variable type discriminator.
bool html_mode
enable HTML debug output
Mode
Execution mode: interpretation or native compilation.
token::Token< char > TToken
Default token type.
TokenType
Classification of scanned tokens.
@ TT_SYM
symbol / operator token
@ TT_HEX
hexadecimal numeric literal
@ TT_ID
identifier or keyword
@ TT_NUM
decimal numeric literal
Lexical scanner that tokenizes source text into typed tokens.
A single instruction operand (constant value or variable reference).
std::string op
textual operand value
std::string object
owning object name for member access
std::string label
label target for jump/call operands
int op_value
numeric operand value
A named variable with type, value, and optional object association.