MXVM 1.8.1
Virtual Machine, Compiler, and Pascal Frontend
Loading...
Searching...
No Matches
parser.cpp
Go to the documentation of this file.
1
6#include "mxvm/parser.hpp"
7#include "mxvm/ast.hpp"
8#include "mxvm/icode.hpp"
10#include "scanner/scanner.hpp"
11#include <algorithm>
12#include <filesystem>
13#include <fstream>
14#include <iomanip>
15#include <iostream>
16#include <set>
17#include <unordered_map>
18
19namespace {
20 std::string html_escape(const std::string &s) {
21 std::string out;
22 out.reserve(s.size() * 12 / 10 + 8);
23 for (char c : s) {
24 switch (c) {
25 case '&':
26 out += "&amp;";
27 break;
28 case '<':
29 out += "&lt;";
30 break;
31 case '>':
32 out += "&gt;";
33 break;
34 case '"':
35 out += "&quot;";
36 break;
37 case '\'':
38 out += "&#39;";
39 break;
40 default:
41 out.push_back(c);
42 break;
43 }
44 }
45 return out;
46 }
47 std::string js_escape(const std::string &s) {
48 std::string out;
49 out.reserve(s.size() * 12 / 10 + 8);
50 for (char c : s) {
51 switch (c) {
52 case '\\':
53 out += "\\\\";
54 break;
55 case '\'':
56 out += "\\'";
57 break;
58 case '"':
59 out += "\\\"";
60 break;
61 case '\n':
62 out += "\\n";
63 break;
64 case '\r':
65 out += "\\r";
66 break;
67 case '\0':
68 out += "\\0";
69 break;
70 case '`':
71 out += "\\`";
72 break;
73 case '$':
74 out += "\\$";
75 break;
76 case '/':
77 out += "\\/";
78 break;
79 default:
80 if (static_cast<unsigned char>(c) < 0x20) {
81 char buf[8];
82 std::snprintf(buf, sizeof(buf), "\\x%02x", static_cast<unsigned char>(c));
83 out += buf;
84 } else {
85 out.push_back(c);
86 }
87 break;
88 }
89 }
90 return html_escape(out);
91 }
92} // namespace
93
94namespace mxvm {
95
96 bool debug_mode = false;
97 bool instruct_mode = false;
98 bool html_mode = false;
99
100 ModuleParser::ModuleParser(const Mode &mode, const std::string &m, const std::string &source) : mod_name(m), scanner(source), parser_mode(mode) {}
101
103 scanner.scan();
104 return scanner.size();
105 }
106
108 next();
109 require("module");
110 next();
112 std::string mod_name = token->getTokenValue();
113 next();
114 require("{");
115 while (!match("}")) {
116 if (match(types::TokenType::TT_ID) && token->getTokenValue() == "extern") {
117 next();
119 std::string func_name = token->getTokenValue();
120 functions.push_back({func_name, mod_name});
121 next();
122 continue;
123 }
124 next();
125 }
126 return true;
127 }
128
130 return at(pos);
131 }
132
134 if (pos < scanner.size())
135 return scanner[pos];
136
137 throw scan::ScanExcept("Out of bounds");
138 return scanner[0];
139 }
140
141 bool ModuleParser::generateProgramCode(const Mode &m, const std::string &mod_id, const std::string &mod_name1, std::unique_ptr<Program> &program) {
142 for (auto &f : functions) {
143 if (m == Mode::MODE_INTERPRET)
144 program->add_runtime_extern(mod_name, mod_name1, "mxvm_" + mod_id + "_" + f.name, f.name);
145 else
146 program->add_extern(mod_name, f.name, true);
147 }
148 return true;
149 }
150
152 while (index < scanner.size() && scanner[index].getTokenType() != types::TokenType::TT_STR && scanner[index].getTokenValue() == "\n") {
153 index++;
154 }
155 if (index < scanner.size()) {
156 token = &scanner[index++];
157 return true;
158 }
159 return false;
160 }
161
162 bool ModuleParser::match(const std::string &s) {
163 return (token->getTokenValue() == s);
164 }
165
167 return (token->getTokenType() == t);
168 }
169
170 void ModuleParser::require(const std::string &s) {
171 if (!match(s)) {
172 throw mx::Exception("Syntax Error: Looking for: " + s + " on line " + std::to_string(token->getLine()));
173 }
174 }
176 if (!match(t)) {
177 throw mx::Exception("Syntax Error: Looking for: " + std::to_string(static_cast<unsigned int>(t)) + " on line " + std::to_string(token->getLine()));
178 }
179 }
180
181 Parser::Parser(const std::string &source) : source_file(source), scanner(source), validator(source) {
182 }
183
184 uint64_t Parser::scan() {
185 try {
186 scanner.scan();
187 } catch (scan::ScanExcept &e) {
188 std::cerr << "Scanner error: " << e.why() << "\n";
189 }
190 return scanner.size();
191 }
192
193 auto Parser::operator[](size_t pos) {
194 if (pos < scanner.size()) {
195 return scanner[pos];
196 }
197 throw mx::Exception("Error: scanner index out of bounds.\n");
198 return scanner[0];
199 }
200
201 std::unique_ptr<ProgramNode> Parser::parseAST() {
202 std::unique_ptr<ProgramNode> mainProgram = nullptr;
203 std::vector<std::unique_ptr<ProgramNode>> allDeclarations;
204 std::string name;
205 for (uint64_t i = 0; i < scanner.size(); ++i) {
206 auto token = this->operator[](i);
207 std::string tokenValue = token.getTokenValue();
208
209 if (i + 1 < scanner.size())
210 name = this->operator[](i + 1).getTokenValue();
211
212 if (tokenValue == "program" || tokenValue == "object") {
213
214 auto declaration = parseProgramOrObject(i);
215 if (declaration) {
216 if (tokenValue == "program") {
217 declaration->object = false;
218 declaration->name = name;
219 mainProgram = std::move(declaration);
220 object_name = name;
221 } else {
222 declaration->object = true;
223 declaration->name = name;
224 object_name = name;
225 allDeclarations.push_back(std::move(declaration));
226 }
227 }
228 }
229 }
230
231 if (!mainProgram) {
232 mainProgram = std::make_unique<ProgramNode>();
233 mainProgram->name = name;
234 mainProgram->object = false;
235 object_name = name;
236 }
237
238 for (auto &obj : allDeclarations) {
239 mainProgram->addInlineObject(std::move(obj));
240 }
241
242 return mainProgram;
243 }
244
245 std::unique_ptr<ObjectNode> Parser::parseObject(uint64_t &index) {
246 if (index >= scanner.size())
247 return nullptr;
248 std::string name = this->operator[](index).getTokenValue();
249 index++;
250 if (index < scanner.size()) {
251 if (this->operator[](index).getTokenValue() == ",")
252 index++;
253 }
254 return std::make_unique<ObjectNode>(name);
255 }
256
257 void Parser::processObjectSection(SectionNode *sectionNode, std::unique_ptr<Program> &program) {
258 for (const auto &statement : sectionNode->statements) {
259 auto objNode = dynamic_cast<ObjectNode *>(statement.get());
260 if (objNode) {
261 std::string &name = objNode->name;
262 processObjectFile(name, program);
263 }
264 }
265 }
266
267 void Parser::processObjectFile(const std::string &src, std::unique_ptr<Program> &program) {
268 static std::set<std::string> processing_files;
269 if (processing_files.count(src)) {
270 return;
271 }
272 processing_files.insert(src);
273 std::fstream file;
274 std::string path;
275 if (object_path.ends_with("/"))
276 path = object_path;
277 else
278 path = object_path + "/";
279
280 file.open(path + src + ".mxvm", std::ios::in);
281 if (!file.is_open()) {
282 return;
283 }
284 std::ostringstream stream;
285 stream << file.rdbuf();
286 file.close();
287
288 std::string full_path = path + src + ".mxvm";
289
290 if (!std::filesystem::exists(full_path)) {
291 throw mx::Exception("Object file not found: " + full_path);
292 }
293
294 mxvm::Validator file_validator(stream.str());
295 try {
296 if (!file_validator.validate(full_path)) {
297 throw mx::Exception("Validation failed for: " + full_path);
298 }
299 } catch (mx::Exception &e) {
300 throw;
301 }
302
303 std::unique_ptr<Parser> parser(new Parser(stream.str()));
304 parser->module_path = module_path;
305 parser->object_path = object_path;
306 parser->object_mode = true;
307 parser->parser_mode = parser_mode;
308 parser->platform = platform;
309 parser->scan();
310
311 auto ast = parser->parseAST();
312 if (!ast)
313 return;
314
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";
321 objProgram->platform = platform;
322 for (const auto &section : inlineObj->sections) {
323 auto sectionNode = dynamic_cast<SectionNode *>(section.get());
324 if (!sectionNode)
325 continue;
326
327 if (sectionNode->type == SectionNode::DATA) {
328 parser->processDataSection(sectionNode, objProgram);
329 } else if (sectionNode->type == SectionNode::CODE) {
330 parser->processCodeSection(sectionNode, objProgram);
331 } else if (sectionNode->type == SectionNode::MODULE) {
332 parser->processModuleSection(sectionNode, objProgram);
333 } else if (sectionNode->type == SectionNode::OBJECT) {
334 parser->processObjectSection(sectionNode, objProgram);
335 }
336 }
337
338 if (Program::base != nullptr && objProgram->object)
339 Program::base->add_object(objProgram->name, objProgram.get());
340
341 registerObjectExterns(program, objProgram);
342
343 program->objects.push_back(std::move(objProgram));
344 }
345 processing_files.erase(src);
346 }
347 std::unique_ptr<SectionNode> Parser::parseSection(uint64_t &index) {
348 index++;
349
350 if (index >= scanner.size())
351 return nullptr;
352
353 auto sectionNameToken = this->operator[](index);
354 std::string sectionName = sectionNameToken.getTokenValue();
355
356 SectionNode::SectionType sectionType;
357 if (sectionName == "data") {
358 sectionType = SectionNode::DATA;
359 } else if (sectionName == "code") {
360 sectionType = SectionNode::CODE;
361 } else if (sectionName == "module") {
362 sectionType = SectionNode::MODULE;
363 } else if (sectionName == "object") {
364 sectionType = SectionNode::OBJECT;
365 } else {
366 return nullptr;
367 }
368
369 auto section = std::make_unique<SectionNode>(sectionType);
370 index++;
371
372 if (index < scanner.size() && this->operator[](index).getTokenValue() == "{") {
373 index++;
374
375 while (index < scanner.size()) {
376 auto token = this->operator[](index);
377 std::string value = token.getTokenValue();
378
379 if (value == "}") {
380 index++;
381 break;
382 }
383
384 if (value == "\n" || value == " " || value == "\t") {
385 index++;
386 continue;
387 }
388
389 if (value == "//" || value.starts_with("//") || value == "#" || value.starts_with("#")) {
390 auto comment = parseComment(index);
391 if (comment) {
392 section->addStatement(std::move(comment));
393 }
394 continue;
395 }
396
397 if (sectionType == SectionNode::DATA) {
398 auto variable = parseDataVariable(index);
399 if (variable) {
400 section->addStatement(std::move(variable));
401 } else {
402 index++;
403 }
404 } else if (sectionType == SectionNode::MODULE) {
405 auto module = parseModule(index);
406 if (module) {
407 section->addStatement(std::move(module));
408 } else {
409 index++;
410 }
411 } else if (sectionType == SectionNode::OBJECT) {
412 auto obj = parseObject(index);
413 if (obj) {
414 section->addStatement(std::move(obj));
415 } else {
416 index++;
417 }
418 } else if (sectionType == SectionNode::CODE) {
419 if (token.getTokenType() == types::TokenType::TT_ID &&
420 index + 1 < scanner.size() &&
421 this->operator[](index + 1).getTokenValue() == ":") {
422 auto label = parseLabel(index);
423 if (label) {
424 section->addStatement(std::move(label));
425 }
426 } else if (token.getTokenType() == types::TokenType::TT_ID && token.getTokenValue() == "function" && index + 2 < scanner.size() && this->operator[](index + 1).getTokenType() == types::TokenType::TT_ID && this->operator[](index + 2).getTokenValue() == ":") {
427 index++;
428 auto label = parseLabel(index);
429 if (label) {
430 label->function = true;
431 section->addStatement(std::move(label));
432 continue;
433 }
434 } else {
435 auto instruction = parseCodeInstruction(index);
436 if (instruction) {
437 section->addStatement(std::move(instruction));
438 } else {
439 index++;
440 }
441 }
442 }
443 }
444 }
445
446 return section;
447 }
448
449 std::unique_ptr<ModuleNode> Parser::parseModule(uint64_t &index) {
450 if (index >= scanner.size())
451 return nullptr;
452 std::string name = this->operator[](index).getTokenValue();
453 index++;
454 if (index < scanner.size()) {
455 if (this->operator[](index).getTokenValue() == ",")
456 index++;
457 }
458 return std::make_unique<ModuleNode>(name);
459 }
460
461 std::unique_ptr<VariableNode> Parser::parseDataVariable(uint64_t &index) {
462 if (index >= scanner.size())
463 return nullptr;
464
465 bool is_global = false;
466 auto token = this->operator[](index);
467 if (token.getTokenValue() == "export") {
468 is_global = true;
469 index++;
470 if (index >= scanner.size())
471 return nullptr;
472 token = this->operator[](index);
473 }
474
475 std::string typeStr = token.getTokenValue();
476 VarType varType;
477 if (typeStr == "int")
478 varType = VarType::VAR_INTEGER;
479 else if (typeStr == "float")
480 varType = VarType::VAR_FLOAT;
481 else if (typeStr == "string")
482 varType = VarType::VAR_STRING;
483 else if (typeStr == "ptr")
484 varType = VarType::VAR_POINTER;
485 else if (typeStr == "array")
486 varType = VarType::VAR_ARRAY;
487 else if (typeStr == "byte")
488 varType = VarType::VAR_BYTE;
489 else
490 return nullptr;
491
492 index++;
493 if (index >= scanner.size())
494 return nullptr;
495 auto nameToken = this->operator[](index);
496
497 if (nameToken.getTokenType() != types::TokenType::TT_ID) {
498 return nullptr;
499 }
500 std::string varName = nameToken.getTokenValue();
501 index++;
502
503 if (index < scanner.size() && this->operator[](index).getTokenValue() == "=") {
504 index++;
505
506 std::string add_op;
507 if (index < scanner.size() && this->operator[](index).getTokenValue() == "-"
508 && this->operator[](index).getTokenType() == types::TokenType::TT_SYM) {
509 add_op = "-";
510 index++;
511 }
512 if (index < scanner.size()) {
513 auto valueToken = this->operator[](index);
514 std::string value = valueToken.getTokenValue();
515 types::TokenType tokenType = valueToken.getTokenType();
516 switch (tokenType) {
518 if (value.starts_with("\"") && value.ends_with("\"")) {
519 value = value.substr(1, value.length() - 2);
520 }
521 index++;
522 {
523 auto node = std::make_unique<VariableNode>(varType, varName, value);
524 node->is_global = is_global;
525 return node;
526 }
527
529 index++;
530 {
531 auto node = std::make_unique<VariableNode>(varType, varName, add_op + value);
532 node->is_global = is_global;
533 return node;
534 }
535
537 index++;
538 {
539 auto node = std::make_unique<VariableNode>(varType, varName, value);
540 node->is_global = is_global;
541 return node;
542 }
543
545 index++;
546 {
547 auto node = std::make_unique<VariableNode>(varType, varName, value);
548 node->is_global = is_global;
549 return node;
550 }
551 default:
552 index++;
553 {
554 auto node = std::make_unique<VariableNode>(varType, varName, value);
555 node->is_global = is_global;
556 return node;
557 }
558 }
559 }
560 } else if (index < scanner.size() && this->operator[](index).getTokenValue() == "," && varType == VarType::VAR_STRING) {
561 index++;
562 size_t buf_size = 0;
563 if (index < scanner.size()) {
564 buf_size = std::stoll(this->operator[](index).getTokenValue(), nullptr, 0);
565 }
566 if (buf_size == 0) {
567 throw mx::Exception("string buffer: " + varName + " requires valid size");
568 }
569 auto node = std::make_unique<VariableNode>(varType, varName, buf_size);
570 node->is_global = is_global;
571 return node;
572 }
573
574 auto node = std::make_unique<VariableNode>(varType, varName);
575 node->is_global = is_global;
576 return node;
577 }
578
579 std::unique_ptr<InstructionNode> Parser::parseCodeInstruction(uint64_t &index) {
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}};
582
583 if (index >= scanner.size())
584 return nullptr;
585
586 auto token = this->operator[](index);
587 std::string tokenValue = token.getTokenValue();
588
589 if (token.getTokenType() == types::TokenType::TT_ID) {
590 if (index + 1 < scanner.size() && this->operator[](index + 1).getTokenValue() == ":") {
591 return nullptr;
592 }
593 }
594
595 auto instIt = instructionMap.find(tokenValue);
596 if (instIt == instructionMap.end()) {
597 throw mx::Exception("Error invalid instruction: " + tokenValue);
598 return nullptr;
599 }
600
601 Inc instruction = instIt->second;
602 index++;
603
604 std::vector<Operand> operands;
605
606 while (index < scanner.size()) {
607
608 std::string add_value;
609 if (index < scanner.size() && this->operator[](index).getTokenValue() == "-") {
610 add_value = "-";
611 index++;
612 }
613 auto operandToken = this->operator[](index);
614 std::string value = operandToken.getTokenValue();
615 types::TokenType tokenType = operandToken.getTokenType();
616
617 if (value == "\n" || value == "//" || value.starts_with("//") ||
618 value == "#" || value.starts_with("#") || value == "}") {
619 break;
620 }
621
622 if (value == "," || value == " " || value == "\t") {
623 index++;
624 continue;
625 }
626
627 Operand operand;
628
629 switch (tokenType) {
631 operand.op = add_value + value;
632 operand.op_value = std::stoll(add_value + value);
634 break;
635
637 operand.op = value;
638 if (value.starts_with("0x") || value.starts_with("0X")) {
639 operand.op_value = std::stoll(value, nullptr, 16);
640 } else {
641 operand.op_value = std::stoll(value, nullptr, 16);
642 }
644 break;
645
647
648 if (index + 1 < scanner.size() && this->operator[](index + 1).getTokenValue() == ".") {
649 operand.object = value;
650 index += 2;
651 if (index < scanner.size())
652 value = this->operator[](index).getTokenValue();
653 }
654
655 if (operand.object.empty())
656 operand.op = value;
657 else
658 operand.op = operand.object + "." + value;
659 operand.label = value;
661 break;
662
664 operand.op = value;
666 break;
667
669 if (value != ",") {
670 operand.op = value;
671 }
672 break;
673 default:
674 operand.op = value;
675 break;
676 }
677
678 if (!operand.op.empty()) {
679 operands.push_back(operand);
680 }
681
682 index++;
683 }
684
685 return std::make_unique<InstructionNode>(instruction, operands);
686 }
687
688 std::unique_ptr<CommentNode> Parser::parseComment(uint64_t &index) {
689 std::string commentText;
690
691 auto token = this->operator[](index);
692 if (token.getTokenValue() == "//" || token.getTokenValue() == "#" ||
693 token.getTokenValue().starts_with("//") || token.getTokenValue().starts_with("#")) {
694 index++;
695 }
696
697 while (index < scanner.size()) {
698 auto token = this->operator[](index);
699 if (token.getTokenValue() == "\n") {
700 break;
701 }
702 commentText += token.getTokenValue() + " ";
703 index++;
704 }
705
706 return std::make_unique<CommentNode>(commentText);
707 }
708
709 std::unique_ptr<LabelNode> Parser::parseLabel(uint64_t &index) {
710 if (index >= scanner.size())
711 return nullptr;
712
713 auto token = this->operator[](index);
714
715 if (token.getTokenType() != types::TokenType::TT_ID) {
716 return nullptr;
717 }
718
719 std::string labelName = token.getTokenValue();
720 index++;
721
722 if (index < scanner.size() && this->operator[](index).getTokenValue() == ":") {
723 index++;
724 return std::make_unique<LabelNode>(labelName);
725 }
726
727 return nullptr;
728 }
729
731 auto ast = parseAST();
732 if (ast) {
733 std::cout << ast->toString() << std::endl;
734 }
735 }
736
737 bool Parser::generateProgramCode(const Mode &mode, std::unique_ptr<Program> &program) {
738 parser_mode = mode;
739 try {
740 if (!validator.validate(program->filename)) {
741 return false;
742 }
743 } catch (mx::Exception &e) {
744 std::cerr << e.what() << "\n";
745 return false;
746 }
747
748 auto ast = parseAST();
749 if (ast) {
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;
755 } else {
756 program->object = true;
757 }
758
759 for (const auto &section : ast->sections) {
760 auto sectionNode = dynamic_cast<SectionNode *>(section.get());
761 if (!sectionNode)
762 continue;
763
764 if (sectionNode->type == SectionNode::DATA) {
765 processDataSection(sectionNode, program);
766 } else if (sectionNode->type == SectionNode::CODE) {
767 processCodeSection(sectionNode, program);
768 } else if (sectionNode->type == SectionNode::MODULE) {
769 processModuleSection(sectionNode, program);
770 } else if (sectionNode->type == SectionNode::OBJECT) {
771 processObjectSection(sectionNode, program);
772 }
773 }
774
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;
781 objProgram->platform = platform;
782 object_name = inlineObj->name;
783
784 for (const auto &section : inlineObj->sections) {
785 auto sectionNode = dynamic_cast<SectionNode *>(section.get());
786 if (!sectionNode)
787 continue;
788
789 if (sectionNode->type == SectionNode::DATA) {
790 processDataSection(sectionNode, objProgram);
791 } else if (sectionNode->type == SectionNode::CODE) {
792 processCodeSection(sectionNode, objProgram);
793 } else if (sectionNode->type == SectionNode::MODULE) {
794 processModuleSection(sectionNode, objProgram);
795 } else if (sectionNode->type == SectionNode::OBJECT) {
796 processObjectSection(sectionNode, objProgram);
797 }
798 }
799
800 registerObjectExterns(program, objProgram);
801
802 if (Program::base != nullptr && objProgram->object)
803 Program::base->add_object(objProgram->name, objProgram.get());
804
805 program->objects.push_back(std::move(objProgram));
806 }
807
808 if (mode == Mode::MODE_COMPILE) {
809 std::set<std::string> generated_objects;
810
811 std::function<void(std::unique_ptr<Program> &)> compileAllObjects;
812 compileAllObjects =
813 [&](std::unique_ptr<Program> &p) {
814 if (!p)
815 return;
816 for (auto &obj : p->objects) {
817 if (obj && generated_objects.find(obj->name) == generated_objects.end()) {
819 generated_objects.insert(obj->name);
820 if (html_mode) {
821 std::ofstream htmlFile(obj->name + ".html");
822 if (htmlFile.is_open()) {
823 generateDebugHTML(htmlFile, obj);
824 std::cout << Col("MXVM: Generated ", mx::Color::BRIGHT_GREEN) << "Debug HTML for: " << obj->name << "\n";
825 }
826 }
827 }
828 compileAllObjects(obj);
829 }
830 };
831 compileAllObjects(program);
832 }
833
834 if (program->object == false) {
835 if (!program->validateNames(validator)) {
836 throw mx::Exception("Could not validate variables/functions\n");
837 }
838 }
839 }
840 return true;
841 }
842 void Parser::collectObjectNames(std::vector<std::pair<std::string, std::string>> &names, const std::unique_ptr<Program> &program) {
843 for (const auto &objPtr : program->objects) {
844 if (!objPtr)
845 continue;
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));
849 }
850 collectObjectNames(names, objPtr);
851 }
852 }
853
854 void Parser::printObjectHTML(std::ostream &out, const std::unique_ptr<Program> &objPtr) {
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>)";
858 out << R"(
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>
864 </div>
865 <div class="stat-card">
866 <span class="number">)"
867 << objPtr->inc.size() << R"(</span>
868 <span class="label">Instructions</span>
869 </div>
870 <div class="stat-card">
871 <span class="number">)"
872 << objPtr->labels.size() << R"(</span>
873 <span class="label">Labels</span>
874 </div>
875 <div class="stat-card">
876 <span class="number">)"
877 << objPtr->objects.size() << R"(</span>
878 <span class="label">Objects</span>
879 </div>
880 </div>
881 )";
882 out << R"(
883 <div class="section">
884 <div class="section-header"><span class="icon">&#x1F9EE;</span> Variables</div>
885 <div class="section-content">)";
886 if (objPtr->vars.empty()) {
887 out << R"(<div class="no-data">No variables defined</div>)";
888 } else {
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) {
897 out << "Integer";
898 break;
900 out << "Float";
901 break;
903 out << "String";
904 break;
906 out << "Pointer";
907 break;
909 out << "Label";
910 break;
912 out << "External";
913 break;
915 out << "Array";
916 break;
918 out << "Byte";
919 break;
920 default:
921 out << "Unknown";
922 break;
923 }
924 out << R"(</div>
925 <div class="variable-value">)";
926 switch (var.second.type) {
929 out << var.second.var_value.int_value;
930 break;
932 out << std::fixed << std::setprecision(6) << var.second.var_value.float_value;
933 break;
935 out << "\"" << html_escape(Program::escapeNewLines(var.second.var_value.str_value)) << "\"";
936 break;
939 if (var.second.var_value.ptr_value == nullptr)
940 out << "null";
941 else
942 out << var.second.var_value.ptr_value;
943 break;
945 out << var.second.var_value.label_value;
946 break;
947 default:
948 out << var.second.var_value.int_value;
949 break;
950 }
951 out << R"(</div>
952 </div>)";
953 }
954 out << R"(</div>)";
955 }
956 out << R"(</div>
957 </div>)";
958 out << R"(<div class="section">
959 <div class="section-header"><span class="icon">&#x1F516;</span> Labels</div>
960 <div class="section-content">)";
961 if (objPtr->labels.empty()) {
962 out << R"(<div class="no-data">No labels defined</div>)";
963 } else {
964 out << R"(<table class="instructions-table">
965 <thead>
966 <tr>
967 <th>Name</th>
968 <th>Address</th>
969 <th>Function</th>
970 </tr>
971 </thead>
972 <tbody>)";
973 for (const auto &label : objPtr->labels) {
974 out << R"(<tr>
975 <td>)"
976 << html_escape(label.first) << R"(</td>
977 <td>)"
978 << "0x" << std::hex << label.second.first << std::dec << R"(</td>
979 <td>)"
980 << (label.second.second ? "true" : "false") << R"(</td>
981 </tr>)";
982 }
983 out << R"(</tbody>
984 </table>)";
985 }
986 out << R"(</div>
987 </div>)";
988 out << R"(
989 <div class="section">
990 <div class="section-header"><span class="icon">&#9881;</span> Instructions</div>
991 <div class="section-content">)";
992 if (objPtr->inc.empty()) {
993 out << R"(<div class="no-data">No instructions defined</div>)";
994 } else {
995 out << R"(<table class="instructions-table">
996 <thead>
997 <tr>
998 <th>#</th>
999 <th>Opcode</th>
1000 <th>Instruction</th>
1001 <th>Operand 1</th>
1002 <th>Operand 2</th>
1003 <th>Operand 3</th>
1004 <th>Extra Operands</th>
1005 </tr>
1006 </thead>
1007 <tbody>)";
1008 for (size_t i = 0; i < objPtr->inc.size(); ++i) {
1009 const auto &instr = objPtr->inc[i];
1010 out << "<tr>"
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) {
1019 if (j > 0)
1020 out << ", ";
1021 out << html_escape(instr.vop[j].op);
1022 }
1023 out << "</td></tr>";
1024 }
1025 out << R"(</tbody>
1026 </table>)";
1027 }
1028 out << R"(</div>
1029 </div>)";
1030 if (!objPtr->objects.empty()) {
1031 out << R"(<div class="section">
1032 <div class="section-header"><span class="icon">&#x1F9E9;</span> Nested Objects</div>
1033 <div class="section-content">)";
1034 for (const auto &nestedObj : objPtr->objects) {
1035 if (nestedObj) {
1036 printObjectHTML(out, nestedObj);
1037 }
1038 }
1039 out << R"(</div>
1040 </div>)";
1041 }
1042 out << R"(<div class="section">
1043 <div class="section-header"><span class="icon">&#x1F4DC;</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>";
1052 out << R"(</div>
1053 </div>
1054</div>
1055)";
1056 }
1057
1058 bool Parser::generateDebugHTML(std::ostream &out, std::unique_ptr<Program> &program) {
1059 out << R"(<!DOCTYPE html>
1060 <html lang="en">
1061 <head>
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>
1066 </head>
1067 <body>
1068 <style>
1069 * {
1070 margin: 0;
1071 padding: 0;
1072 box-sizing: border-box;
1073 }
1074 body {
1075 font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
1076 background: #000;
1077 color: #e0e0e0;
1078 line-height: 1.6;
1079 min-height: 100vh;
1080 }
1081 .container {
1082 width: 100%;
1083 max-width: 100vw;
1084 margin: 0 auto;
1085 padding: 20px;
1086 background: transparent;
1087 }
1088 .header {
1089 background: #222;
1090 border-radius: 15px;
1091 padding: 30px;
1092 margin-bottom: 30px;
1093 box-shadow: 0 8px 32px rgba(183, 28, 28, 0.2);
1094 border: 1px solid #b71c1c;
1095 }
1096 .header h1 {
1097 color: #ff5252;
1098 font-size: 2.5rem;
1099 margin-bottom: 10px;
1100 font-weight: 700;
1101 text-shadow: 1px 1px 2px #000;
1102 }
1103 .header .subtitle {
1104 color: #e57373;
1105 font-size: 1.2rem;
1106 margin-bottom: 20px;
1107 }
1108 .stats {
1109 display: flex;
1110 gap: 20px;
1111 flex-wrap: wrap;
1112 }
1113 .stat-card {
1114 background: #b71c1c;
1115 color: #fff;
1116 padding: 15px 25px;
1117 border-radius: 10px;
1118 text-align: center;
1119 min-width: 150px;
1120 box-shadow: 0 4px 15px rgba(183, 28, 28, 0.3);
1121 }
1122 .stat-card .number {
1123 font-size: 2rem;
1124 font-weight: bold;
1125 display: block;
1126 }
1127 .stat-card .label {
1128 font-size: 0.9rem;
1129 opacity: 0.9;
1130 }
1131 .section {
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;
1137 overflow: hidden;
1138 }
1139 .section-header {
1140 background: #b71c1c;
1141 color: #fff;
1142 padding: 20px 30px;
1143 font-size: 1.4rem;
1144 font-weight: 600;
1145 display: flex;
1146 align-items: center;
1147 gap: 10px;
1148 }
1149 .section-content {
1150 padding: 30px;
1151 }
1152 .variables-grid {
1153 display: grid;
1154 gap: 15px;
1155 grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
1156 }
1157 .variable-card {
1158 background: #222;
1159 border: 1px solid #b71c1c;
1160 border-radius: 10px;
1161 padding: 20px;
1162 color: #fff;
1163 transition: transform 0.2s, box-shadow 0.2s;
1164 }
1165 .variable-card:hover {
1166 transform: translateY(-2px);
1167 box-shadow: 0 4px 20px rgba(183, 28, 28, 0.2);
1168 }
1169 .variable-name {
1170 font-weight: 600;
1171 font-size: 1.1rem;
1172 color: #ff5252;
1173 margin-bottom: 8px;
1174 }
1175 .variable-type {
1176 display: inline-block;
1177 background: #b71c1c;
1178 color: #fff;
1179 padding: 4px 12px;
1180 border-radius: 20px;
1181 font-size: 0.8rem;
1182 font-weight: 500;
1183 margin-bottom: 10px;
1184 }
1185 .variable-value {
1186 font-family: 'Courier New', monospace;
1187 background: #181818;
1188 border: 1px solid #b71c1c;
1189 border-radius: 5px;
1190 padding: 10px;
1191 color: #e0e0e0;
1192 word-break: break-all;
1193 }
1194 .instructions-table {
1195 width: 100%;
1196 border-collapse: collapse;
1197 background: #222;
1198 border-radius: 10px;
1199 overflow: hidden;
1200 box-shadow: 0 2px 10px rgba(183, 28, 28, 0.1);
1201 }
1202 .instructions-table th {
1203 background: #b71c1c;
1204 color: #fff;
1205 padding: 15px;
1206 text-align: left;
1207 font-weight: 600;
1208 }
1209 .instructions-table td {
1210 padding: 12px 15px;
1211 border-bottom: 1px solid #b71c1c;
1212 color: #e0e0e0;
1213 }
1214 .instructions-table tr:nth-child(even) {
1215 background: #181818;
1216 }
1217 .instructions-table tr:hover {
1218 background: #2d2d2d;
1219 }
1220 .opcode {
1221 font-weight: 600;
1222 color: #ff5252;
1223 }
1224 .operand {
1225 font-family: 'Courier New', monospace;
1226 color: #e0e0e0;
1227 }
1228 .object-section {
1229 margin-left: 20px;
1230 border-left: 3px solid #b71c1c;
1231 padding-left: 20px;
1232 }
1233 .object-title {
1234 font-size: 1.2rem;
1235 font-weight: 600;
1236 color: #ff5252;
1237 margin-bottom: 15px;
1238 padding: 10px 15px;
1239 background: #222;
1240 border-radius: 8px;
1241 border-left: 4px solid #b71c1c;
1242 }
1243 .btn {
1244 background: #b71c1c;
1245 color: white;
1246 border: none;
1247 padding: 8px 16px;
1248 border-radius: 5px;
1249 cursor: pointer;
1250 margin-right: 10px;
1251 font-weight: 600;
1252 transition: background 0.2s;
1253 }
1254 .btn:hover {
1255 background: #ff5252;
1256 }
1257 .icon {
1258 width: 20px;
1259 height: 20px;
1260 display: inline-block;
1261 }
1262 .no-data {
1263 text-align: center;
1264 color: #e57373;
1265 font-style: italic;
1266 padding: 40px;
1267 }
1268 @media (max-width: 768px) {
1269 .container {
1270 padding: 10px;
1271 }
1272 .header h1 {
1273 font-size: 2rem;
1274 }
1275 .stats {
1276 justify-content: center;
1277 }
1278 .variables-grid {
1279 grid-template-columns: 1fr;
1280 }
1281 .instructions-table {
1282 font-size: 0.9rem;
1283 }
1284 }
1285 </style>
1286 )";
1287
1288 bool mainHasContent = !program->vars.empty() || !program->inc.empty() || !program->labels.empty();
1289 bool hasObjects = !program->objects.empty();
1290
1291 out << R"(
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>
1297 <div class="stats">
1298 <div class="stat-card">
1299 <span class="number">)"
1300 << program->vars.size() << R"(</span>
1301 <span class="label">Variables</span>
1302 </div>
1303 <div class="stat-card">
1304 <span class="number">)"
1305 << program->inc.size() << R"(</span>
1306 <span class="label">Instructions</span>
1307 </div>
1308 <div class="stat-card">
1309 <span class="number">)"
1310 << program->objects.size() << R"(</span>
1311 <span class="label">Objects</span>
1312 </div>
1313 <div class="stat-card">
1314 <span class="number">)"
1315 << program->labels.size() << R"(</span>
1316 <span class="label">Labels</span>
1317 </div>
1318 </div>
1319 </div>)";
1320
1321 if (mainHasContent) {
1322 out << R"(<div class="section">
1323 <div class="section-header">
1324 <span class="icon">&#x1F9EE;</span>
1325 Variables
1326 </div>
1327 <div class="section-content">)";
1328
1329 if (program->vars.empty()) {
1330 out << R"(<div class="no-data">No variables defined</div>)";
1331 } else {
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">)";
1338
1339 switch (var.second.type) {
1341 out << "Integer";
1342 break;
1343 case VarType::VAR_FLOAT:
1344 out << "Float";
1345 break;
1347 out << "String";
1348 break;
1350 out << "Pointer";
1351 break;
1352 case VarType::VAR_LABEL:
1353 out << "Label";
1354 break;
1356 out << "External";
1357 break;
1358 case VarType::VAR_ARRAY:
1359 out << "Array";
1360 break;
1361 case VarType::VAR_BYTE:
1362 out << "Byte";
1363 break;
1364 default:
1365 out << "Unknown";
1366 break;
1367 }
1368
1369 out << R"(</div>
1370 <div class="variable-value">)";
1371
1372 switch (var.second.type) {
1374 case VarType::VAR_BYTE:
1375 out << var.second.var_value.int_value;
1376 break;
1377 case VarType::VAR_FLOAT:
1378 out << std::fixed << std::setprecision(6) << var.second.var_value.float_value;
1379 break;
1381 out << "\"" << html_escape(Program::escapeNewLines(var.second.var_value.str_value)) << "\"";
1382 break;
1385 if (var.second.var_value.ptr_value == nullptr)
1386 out << "null";
1387 else
1388 out << var.second.var_value.ptr_value;
1389 break;
1390 case VarType::VAR_LABEL:
1391 out << var.second.var_value.label_value;
1392 break;
1393 default:
1394 out << var.second.var_value.int_value;
1395 break;
1396 }
1397
1398 out << R"(</div>
1399 </div>)";
1400 }
1401 out << R"(</div>)";
1402 }
1403 out << R"(</div>
1404 </div>)";
1405
1406 out << R"(<div class="section">
1407 <div class="section-header"><span class="icon">&#x1F516;</span> Labels</div>
1408 <div class="section-content">)";
1409 if (program->labels.empty()) {
1410 out << R"(<div class="no-data">No labels defined</div>)";
1411 } else {
1412 out << R"(<table class="instructions-table">
1413 <thead>
1414 <tr>
1415 <th>Name</th>
1416 <th>Function</th>
1417 </tr>
1418 </thead>
1419 <tbody>)";
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>
1422 <td>)"
1423 << (label.second.second ? "true" : "false") << R"(</td>
1424 </tr>)";
1425 }
1426 out << R"(</tbody>
1427 </table>)";
1428 }
1429 out << R"(</div>
1430 </div>)";
1431
1432 out << R"(<div class="section">
1433 <div class="section-header">
1434 <span class="icon">&#9881;</span>
1435 Instructions
1436 </div>
1437 <div class="section-content">)";
1438
1439 if (program->inc.empty()) {
1440 out << R"(<div class="no-data">No instructions defined</div>)";
1441 } else {
1442 out << R"(<table class="instructions-table">
1443 <thead>
1444 <tr>
1445 <th>#</th>
1446 <th>Opcode</th>
1447 <th>Instruction</th>
1448 <th>Operand 1</th>
1449 <th>Operand 2</th>
1450 <th>Operand 3</th>
1451 <th>Extra Operands</th>
1452 </tr>
1453 </thead>
1454 <tbody>)";
1455
1456 for (size_t i = 0; i < program->inc.size(); ++i) {
1457 const auto &instr = program->inc[i];
1458 out << "<tr>"
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) {
1467 if (j > 0)
1468 out << ", ";
1469 out << html_escape(instr.vop[j].op);
1470 }
1471 out << "</td></tr>";
1472 }
1473 out << R"(</tbody>
1474 </table>)";
1475 }
1476 out << R"(<div class="section">
1477 <div class="section-header"><span class="icon">&#x1F4DC;</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>";
1485 out << R"(</div>
1486 </div>
1487 </div>
1488 )";
1489 }
1490
1491 if (hasObjects) {
1492 out << R"(<div class="section">
1493 <div class="section-header">
1494 <span class="icon">&#x1F4E6;</span>
1495 Objects
1496 </div>
1497 <div class="section-content">)";
1498
1499 for (const auto &obj : program->objects) {
1500 if (obj) {
1501 printObjectHTML(out, obj);
1502 }
1503 }
1504
1505 out << R"(</div>
1506 </div>)";
1507 }
1508
1509 out << R"(
1510 </div>)";
1511 out << R"(
1512 <script>
1513 function copyAsm(id) {
1514 var textarea = document.getElementById(id);
1515 textarea.select();
1516 navigator.clipboard.writeText(textarea.value);
1517 }
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);
1525 link.click();
1526 document.body.removeChild(link);
1527 }
1528 </script>
1529 </body>
1530 </html>
1531 )";
1532 return true;
1533 }
1534
1535 void Parser::processDataSection(SectionNode *sectionNode, std::unique_ptr<Program> &program) {
1536 for (const auto &statement : sectionNode->statements) {
1537 auto variableNode = dynamic_cast<VariableNode *>(statement.get());
1538 if (variableNode) {
1539 Variable var;
1540 var.type = variableNode->type;
1541 var.var_name = program->name + "." + variableNode->name;
1542 if (variableNode->hasInitializer) {
1543 setVariableValue(var, variableNode->type, variableNode->initialValue);
1544 } else {
1545 setDefaultVariableValue(var, variableNode->type);
1546 }
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);
1551 }
1552 }
1553 }
1554
1555 void Parser::processModuleSection(SectionNode *sectionNode, std::unique_ptr<Program> &program) {
1556 for (const auto &statement : sectionNode->statements) {
1557 auto moduleNode = dynamic_cast<ModuleNode *>(statement.get());
1558 if (moduleNode) {
1559 std::string &name = moduleNode->name;
1560 processModuleFile(name, program);
1561 }
1562 }
1563 }
1564
1565 void Parser::processModuleFile(const std::string &src, std::unique_ptr<Program> &program) {
1566 std::string module_name = "libmxvm_" + src;
1567 if (!module_path.ends_with("/"))
1568 module_path += "/";
1569 if (!include_path.ends_with("/"))
1570 include_path += "/";
1571 std::string shared_ext = ".so";
1572#ifdef _WIN32
1573 shared_ext = ".dll";
1574#elif defined(__APPLE__)
1575 shared_ext = ".dylib";
1576#endif
1577
1578 std::string module_path_so = module_path + "modules/" + src + "/" + module_name + shared_ext;
1579#ifdef _WIN32
1580 // On Windows, DLLs may not have the "lib" prefix (e.g. mxvm_sdl.dll vs libmxvm_sdl.dll)
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;
1585 }
1586#endif
1587 std::string msys2prefix;
1588#ifdef _WIN32
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);
1595 } else {
1596 msys2prefix = "";
1597 }
1598 }
1599#endif
1600 std::string module_src = msys2prefix + include_path + src + "/" + src + ".mxvm";
1601 std::fstream file;
1602 file.open(module_src, std::ios::in);
1603 if (!file.is_open()) {
1604 // Fall back to looking in the module directory
1605 module_src = module_path + "modules/" + src + "/" + src + ".mxvm";
1606 file.open(module_src, std::ios::in);
1607 }
1608 if (!file.is_open()) {
1609 throw mx::Exception("Error could not find module source file: " + module_src);
1610 }
1611 std::ostringstream data;
1612 data << file.rdbuf();
1613 ModuleParser mod_parser(this->parser_mode, src, data.str());
1614 if (mod_parser.scan() > 0) {
1615 if (mod_parser.parse() && mod_parser.generateProgramCode(this->parser_mode, src, module_path_so, program)) {
1616 return;
1617 } else {
1618 throw mx::Exception("Error parsing module file.\n");
1619 }
1620 }
1621 }
1622
1623 void Parser::processCodeSection(SectionNode *sectionNode, std::unique_ptr<Program> &program) {
1624 std::unordered_map<std::string, size_t> labelMap;
1625
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())) {
1632 instructionIndex++;
1633 }
1634 }
1635 for (const auto &statement : sectionNode->statements) {
1636 auto instructionNode = dynamic_cast<InstructionNode *>(statement.get());
1637 if (instructionNode) {
1638 Instruction instr;
1639 instr.instruction = instructionNode->instruction;
1640 if (instructionNode->operands.size() > 0) {
1641 instr.op1 = instructionNode->operands[0];
1642 resolveLabelReference(instr.op1, labelMap);
1643 }
1644 if (instructionNode->operands.size() > 1) {
1645 instr.op2 = instructionNode->operands[1];
1646 resolveLabelReference(instr.op2, labelMap);
1647 }
1648 if (instructionNode->operands.size() > 2) {
1649 instr.op3 = instructionNode->operands[2];
1650 resolveLabelReference(instr.op3, labelMap);
1651 }
1652 if (instructionNode->operands.size() > 3) {
1653 for (size_t i = 3; i < instructionNode->operands.size(); ++i) {
1654 Operand extraOp = instructionNode->operands[i];
1655 resolveLabelReference(extraOp, labelMap);
1656 instr.vop.push_back(extraOp);
1657 }
1658 }
1659 program->add_instruction(instr);
1660 }
1661 }
1662 }
1663
1664 void Parser::setVariableValue(Variable &var, VarType type, const std::string &value, size_t buf_size) {
1665 switch (type) {
1666 case VarType::VAR_BYTE:
1668 if (value.starts_with("0x") || value.starts_with("0X")) {
1669 var.var_value.int_value = std::stoull(value, nullptr, 16);
1670 } else {
1671 var.var_value.int_value = std::stoull(value);
1672 }
1673 var.var_value.type = type;
1674 break;
1675
1676 case VarType::VAR_FLOAT:
1677 var.var_value.float_value = std::stod(value);
1678 var.var_value.type = VarType::VAR_FLOAT;
1679 break;
1681 var.var_value.str_value = value;
1682 var.var_value.type = VarType::VAR_STRING;
1683 var.var_value.buffer_size = buf_size;
1684 break;
1685
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";
1691 }
1692 var.var_value.type = VarType::VAR_POINTER;
1693 break;
1694
1695 case VarType::VAR_LABEL:
1696 var.var_value.label_value = value;
1697 var.var_value.type = VarType::VAR_LABEL;
1698 break;
1699
1700 default:
1701 setDefaultVariableValue(var, type);
1702 break;
1703 }
1704 }
1705
1707 switch (type) {
1709 var.var_value.int_value = 0;
1710 var.var_value.type = VarType::VAR_INTEGER;
1711 break;
1712
1713 case VarType::VAR_FLOAT:
1714 var.var_value.float_value = 0.0;
1715 var.var_value.type = VarType::VAR_FLOAT;
1716 break;
1717
1719 var.var_value.str_value = "";
1720 var.var_value.type = VarType::VAR_STRING;
1721 break;
1722
1724 var.var_value.ptr_value = nullptr;
1725 var.var_value.type = VarType::VAR_POINTER;
1726 break;
1727
1728 case VarType::VAR_LABEL:
1729 var.var_value.label_value = "";
1730 var.var_value.type = VarType::VAR_LABEL;
1731 break;
1732
1733 default:
1734 var.var_value.type = VarType::VAR_NULL;
1735 break;
1736 }
1737 }
1738
1739 void Parser::resolveLabelReference(Operand &operand, const std::unordered_map<std::string, size_t> &labelMap) {
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);
1744 }
1745 }
1746 }
1747
1748 std::unique_ptr<ProgramNode> Parser::parseProgramOrObject(uint64_t &i) {
1749 auto program = std::make_unique<ProgramNode>();
1750
1751 std::string tokenValue = this->operator[](i).getTokenValue();
1752 program->object = (tokenValue == "object");
1753 i++;
1754
1755 if (i < scanner.size()) {
1756 auto nameToken = this->operator[](i);
1757 program->name = nameToken.getTokenValue();
1758 if (program->object == false)
1759 program->root_name = nameToken.getTokenValue();
1760 i++;
1761 }
1762
1763 if (i < scanner.size() && this->operator[](i).getTokenValue() == "{") {
1764 i++;
1765
1766 while (i < scanner.size()) {
1767 auto innerToken = this->operator[](i);
1768 std::string innerValue = innerToken.getTokenValue();
1769
1770 if (innerValue == "}") {
1771 i++;
1772 break;
1773 }
1774
1775 if (innerValue == "section") {
1776 auto section = parseSection(i);
1777 if (section) {
1778 program->addSection(std::move(section));
1779 }
1780 continue;
1781 }
1782 i++;
1783 }
1784 }
1785
1786 return program;
1787 }
1788
1789 void Parser::generateObjectAssemblyFile(std::unique_ptr<Program> &objProgram) {
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);
1794 }
1795
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;
1802 objectFile.close();
1803 if (debug_mode) {
1804 std::cout << "Generated object assembly file: " << objectFileName << std::endl;
1805 }
1806 if (Program::base != nullptr)
1807 Program::base->add_filename(objectFileName);
1808 }
1809
1810 void Parser::registerObjectExterns(std::unique_ptr<Program> &mainProgram,
1811 const std::unique_ptr<Program> &objProgram) {
1812 for (const auto &[labelName, labelInfo] : objProgram->labels) {
1813 if (labelInfo.second && Program::base != nullptr)
1814 Program::base->add_extern(objProgram->name, labelName, false);
1815 }
1816
1817 for (const auto &ext : objProgram->external) {
1818 if (Program::base != nullptr)
1819 Program::base->add_extern(ext.mod, ext.name, true);
1820 }
1821
1822 for (const auto &child : objProgram->objects) {
1823 if (child)
1824 registerObjectExterns(mainProgram, child);
1825 }
1826 }
1827} // namespace mxvm
General-purpose exception with errno-aware factory method.
Definition exception.hpp:38
void add_filename(const std::string &fname)
Record a source filename for debug/error reporting.
Definition icode.cpp:257
void add_extern(const std::string &mod, const std::string &name, bool module)
Register an external function import.
Definition icode.cpp:266
static Base * base
pointer to the main program base
Definition icode.hpp:197
bool parse()
Parse the module declarations.
Definition parser.cpp:107
Mode parser_mode
interpret or compile
Definition parser.hpp:247
scan::TToken * token
current token pointer
Definition parser.hpp:246
uint64_t index
current token index
Definition parser.hpp:245
void require(const std::string &s)
Require the current token value to match, or throw.
Definition parser.cpp:170
ModuleParser(const Mode &m, const std::string &mod_name, const std::string &source)
Construct a module parser.
Definition parser.cpp:100
bool match(const std::string &s)
Check if the current token value matches and advance.
Definition parser.cpp:162
bool next()
Advance to the next token.
Definition parser.cpp:151
std::string mod_name
module name
Definition parser.hpp:243
scan::Scanner scanner
underlying scanner
Definition parser.hpp:244
scan::TToken operator[](size_t pos)
Access a token by position.
Definition parser.cpp:129
uint64_t scan()
Tokenize the module source.
Definition parser.cpp:102
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.
Definition parser.cpp:141
std::vector< ExternalFunction > functions
parsed external function declarations
Definition parser.hpp:242
scan::TToken at(size_t pos)
Access a token by position (bounds-checked).
Definition parser.cpp:133
AST node for an object import declaration.
Definition ast.hpp:104
std::string object_path
search path for .mxvm object files
Definition parser.hpp:133
std::unique_ptr< ModuleNode > parseModule(uint64_t &index)
Definition parser.cpp:449
scan::Scanner scanner
Definition parser.hpp:129
Parser(const std::string &source)
Construct a parser from MXVM source text.
Definition parser.cpp:181
std::unique_ptr< ObjectNode > parseObject(uint64_t &index)
Definition parser.cpp:245
bool generateDebugHTML(std::ostream &out, std::unique_ptr< Program > &program)
Generate debug HTML output for the program.
Definition parser.cpp:987
void generateObjectAssemblyFile(std::unique_ptr< Program > &objProgram)
Generate a native assembly file for an object.
Definition parser.cpp:1394
std::unique_ptr< CommentNode > parseComment(uint64_t &index)
Definition parser.cpp:688
auto operator[](size_t pos)
Access a token by position.
Definition parser.cpp:193
void collectObjectNames(std::vector< std::pair< std::string, std::string > > &names, const std::unique_ptr< Program > &program)
Definition parser.cpp:842
std::string source_file
Definition parser.hpp:128
std::unique_ptr< ProgramNode > parseProgramOrObject(uint64_t &index)
Parse a top-level program or object node.
Definition parser.cpp:1353
void registerObjectExterns(std::unique_ptr< Program > &mainProgram, const std::unique_ptr< Program > &objProgram)
Register external functions from an object into the main program.
Definition parser.cpp:1415
void processObjectFile(const std::string &src, std::unique_ptr< Program > &program)
Definition parser.cpp:267
void processModuleFile(const std::string &src, std::unique_ptr< Program > &program)
Definition parser.cpp:1170
void resolveLabelReference(Operand &operand, const std::unordered_map< std::string, size_t > &labelMap)
Definition parser.cpp:1344
void setDefaultVariableValue(Variable &var, VarType type)
Definition parser.cpp:1311
std::unique_ptr< LabelNode > parseLabel(uint64_t &index)
Definition parser.cpp:709
void processDataSection(SectionNode *sectionNode, std::unique_ptr< Program > &program)
Definition parser.cpp:1140
std::unique_ptr< SectionNode > parseSection(uint64_t &index)
Definition parser.cpp:347
uint64_t scan()
Tokenize the source and return the token count.
Definition parser.cpp:184
std::string include_path
system module include path
Definition parser.hpp:134
void parse()
Parse all tokens into the internal AST.
Definition parser.cpp:730
void processModuleSection(SectionNode *sectionNode, std::unique_ptr< Program > &program)
Definition parser.cpp:1160
std::string object_name
Definition parser.hpp:136
Platform platform
Definition parser.hpp:137
void printObjectHTML(std::ostream &out, const std::unique_ptr< Program > &objPtr)
Definition parser.cpp:854
void setVariableValue(Variable &var, VarType type, const std::string &value, size_t buf_size=0)
Definition parser.cpp:1269
std::string module_path
search path for .mxvm module files
Definition parser.hpp:132
std::unique_ptr< ProgramNode > parseAST()
Build the full AST from tokens.
Definition parser.cpp:201
Validator validator
Definition parser.hpp:130
bool generateProgramCode(const Mode &m, std::unique_ptr< Program > &program)
Walk the AST and populate a Program with instructions and variables.
Definition parser.cpp:737
Mode parser_mode
Definition parser.hpp:131
friend class ModuleParser
Definition parser.hpp:60
void processObjectSection(SectionNode *sectionNode, std::unique_ptr< Program > &program)
Definition parser.cpp:257
std::unique_ptr< VariableNode > parseDataVariable(uint64_t &index)
Definition parser.cpp:461
void processCodeSection(SectionNode *sectionNode, std::unique_ptr< Program > &program)
Definition parser.cpp:1228
std::unique_ptr< InstructionNode > parseCodeInstruction(uint64_t &index)
Definition parser.cpp:579
static std::string escapeNewLines(const std::string &text)
Escape newline characters in a string literal for assembly output.
Definition icode.cpp:296
Represents a program section (.data, .code, .module, .object).
Definition ast.hpp:27
std::vector< std::unique_ptr< ASTNode > > statements
Definition ast.hpp:37
SectionType
Section type discriminator.
Definition ast.hpp:30
Validates MXVM source for correct variable/label usage and instruction operands.
Definition valid.hpp:69
bool validate(const std::string &name)
Run full validation on the source program.
Definition valid.cpp:368
Exception thrown during scanning errors.
Definition scanner.hpp:80
StringType why() const
Get the error description.
Definition scanner.hpp:89
int64_t pos(const char *substr, const char *s)
Definition cstring.c:51
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.
Definition instruct.hpp:82
Inc
MXVM instruction set opcodes.
Definition instruct.hpp:21
@ JLE
Definition instruct.hpp:40
@ NOT
Definition instruct.hpp:33
@ POP
Definition instruct.hpp:53
@ JNO
Definition instruct.hpp:74
@ MUL
Definition instruct.hpp:28
@ JNZ
Definition instruct.hpp:44
@ SUB
Definition instruct.hpp:27
@ MOD
Definition instruct.hpp:34
@ ALLOC
Definition instruct.hpp:49
@ JMP
Definition instruct.hpp:36
@ GETLINE
Definition instruct.hpp:51
@ STRING_PRINT
Definition instruct.hpp:59
@ JBE
Definition instruct.hpp:68
@ JE
Definition instruct.hpp:37
@ LEA
Definition instruct.hpp:77
@ RETURN
Definition instruct.hpp:64
@ JNP
Definition instruct.hpp:72
@ JO
Definition instruct.hpp:73
@ JA
Definition instruct.hpp:45
@ JS
Definition instruct.hpp:75
@ STACK_LOAD
Definition instruct.hpp:54
@ EXIT
Definition instruct.hpp:48
@ CMP
Definition instruct.hpp:35
@ DIV
Definition instruct.hpp:29
@ AND
Definition instruct.hpp:31
@ JGE
Definition instruct.hpp:42
@ JL
Definition instruct.hpp:39
@ OR
Definition instruct.hpp:30
@ LOAD
Definition instruct.hpp:24
@ DONE
Definition instruct.hpp:60
@ JZ
Definition instruct.hpp:43
@ STACK_SUB
Definition instruct.hpp:56
@ MOV
Definition instruct.hpp:23
@ JNC
Definition instruct.hpp:70
@ JC
Definition instruct.hpp:69
@ PRINT
Definition instruct.hpp:47
@ RET
Definition instruct.hpp:58
@ JAE
Definition instruct.hpp:67
@ STACK_STORE
Definition instruct.hpp:55
@ CALL
Definition instruct.hpp:57
@ XOR
Definition instruct.hpp:32
@ INVOKE
Definition instruct.hpp:63
@ FREE
Definition instruct.hpp:50
@ TO_INT
Definition instruct.hpp:61
@ ADD
Definition instruct.hpp:26
@ PUSH
Definition instruct.hpp:52
@ JNS
Definition instruct.hpp:76
@ JG
Definition instruct.hpp:41
@ JB
Definition instruct.hpp:46
@ REALLOC
Reallocate a dynamic memory block: realloc dest, elemSize, count.
Definition instruct.hpp:78
@ TO_FLOAT
Definition instruct.hpp:62
@ JP
Definition instruct.hpp:71
@ JNE
Definition instruct.hpp:38
@ NEG
Definition instruct.hpp:65
@ FCMP
Definition instruct.hpp:66
@ STORE
Definition instruct.hpp:25
const std::string BRIGHT_GREEN
Definition exception.hpp:78
Definition ast.hpp:14
bool instruct_mode
enable instruction trace mode
Definition parser.cpp:97
bool debug_mode
enable verbose debug output during parsing
Definition parser.cpp:96
VarType
MXVM variable type discriminator.
Definition instruct.hpp:180
bool html_mode
enable HTML debug output
Definition parser.cpp:98
Mode
Execution mode: interpretation or native compilation.
Definition parser.hpp:36
@ MODE_INTERPRET
Definition parser.hpp:37
@ MODE_COMPILE
Definition parser.hpp:38
token::Token< char > TToken
Default token type.
Definition scanner.hpp:19
TokenType
Classification of scanned tokens.
Definition types.hpp:16
@ TT_SYM
symbol / operator token
Definition types.hpp:19
@ TT_HEX
hexadecimal numeric literal
Definition types.hpp:22
@ TT_STR
string literal
Definition types.hpp:20
@ TT_ID
identifier or keyword
Definition types.hpp:17
@ TT_NUM
decimal numeric literal
Definition types.hpp:21
Lexical scanner that tokenizes source text into typed tokens.
A single instruction operand (constant value or variable reference).
Definition instruct.hpp:159
std::string op
textual operand value
Definition instruct.hpp:161
std::string object
owning object name for member access
Definition instruct.hpp:164
std::string label
label target for jump/call operands
Definition instruct.hpp:160
int op_value
numeric operand value
Definition instruct.hpp:162
OperandType type
Definition instruct.hpp:163
A named variable with type, value, and optional object association.
Definition instruct.hpp:292