MXVM 1.8.1
Virtual Machine, Compiler, and Pascal Frontend
Loading...
Searching...
No Matches
icode.hpp
Go to the documentation of this file.
1
6#ifndef __CODEGEN_H_
7#define __CODEGEN_H_
8#include "ast.hpp"
9#include <algorithm>
10#include <cctype>
11#include <cstdlib>
12#include <map>
13#include <memory>
14#include <set>
15#include <sstream>
16#include <stdexcept>
17#include <string>
18#include <unordered_map>
19#include <unordered_set>
20#include <utility>
21#include <vector>
22#define MXVM_BOUNDS_CHECK
23
24namespace pascal {
25
29 struct ArrayInfo {
30 std::string elementType;
31 int lowerBound = 0;
32 int upperBound = -1;
33 int size = 0;
34 int elementSize = 8;
35 bool elementIsArray = false;
36 bool isDynamic = false;
37 std::unique_ptr<ArrayInfo> elementArray;
38
39 ArrayInfo() = default;
40 ArrayInfo(const ArrayInfo &other)
44 if (other.elementArray) {
45 elementArray = std::make_unique<ArrayInfo>(*other.elementArray);
46 }
47 }
48 ArrayInfo(ArrayInfo &&other) noexcept = default;
49 ArrayInfo &operator=(const ArrayInfo &other) {
50 if (this != &other) {
52 lowerBound = other.lowerBound;
53 upperBound = other.upperBound;
54 size = other.size;
57 isDynamic = other.isDynamic;
58 if (other.elementArray) {
59 elementArray = std::make_unique<ArrayInfo>(*other.elementArray);
60 } else {
61 elementArray.reset();
62 }
63 }
64 return *this;
65 }
66 ArrayInfo &operator=(ArrayInfo &&other) noexcept = default;
67 };
68
69 class CodeGenVisitor;
70
85
93 public:
94 virtual ~BuiltinFunctionHandler() = default;
96 virtual bool canHandle(const std::string &funcName) const = 0;
98 virtual void generate(CodeGenVisitor &visitor, const std::string &funcName, const std::vector<std::unique_ptr<ASTNode>> &arguments) = 0;
100 virtual bool generateWithResult(CodeGenVisitor &visitor, const std::string &funcName, const std::vector<std::unique_ptr<ASTNode>> &arguments) { return false; }
102 virtual VarType getReturnType(const std::string &funcName) const { return VarType::UNKNOWN; }
103 };
104
112 private:
113 std::vector<std::unique_ptr<BuiltinFunctionHandler>> handlers;
114
115 public:
117 void registerHandler(std::unique_ptr<BuiltinFunctionHandler> handler) { handlers.push_back(std::move(handler)); }
122 BuiltinFunctionHandler *findHandler(const std::string &funcName) {
123 for (auto &h : handlers)
124 if (h->canHandle(funcName))
125 return h.get();
126 return nullptr;
127 }
128 };
129
132 public:
133 bool canHandle(const std::string &funcName) const override;
134 void generate(CodeGenVisitor &visitor, const std::string &funcName, const std::vector<std::unique_ptr<ASTNode>> &arguments) override;
135 bool generateWithResult(CodeGenVisitor &visitor, const std::string &funcName, const std::vector<std::unique_ptr<ASTNode>> &arguments) override;
136 };
137
140 public:
141 bool canHandle(const std::string &funcName) const override;
142 void generate(CodeGenVisitor &visitor, const std::string &funcName, const std::vector<std::unique_ptr<ASTNode>> &arguments) override;
143 bool generateWithResult(CodeGenVisitor &visitor, const std::string &funcName, const std::vector<std::unique_ptr<ASTNode>> &arguments) override;
144 };
145
148 bool canHandle(const std::string &funcName) const override;
149 void generate(CodeGenVisitor &visitor, const std::string &funcName, const std::vector<std::unique_ptr<ASTNode>> &arguments) override;
150 bool generateWithResult(CodeGenVisitor &visitor, const std::string &funcName, const std::vector<std::unique_ptr<ASTNode>> &arguments) override;
151 VarType getReturnType(const std::string &funcName) const override;
152 };
153
156 public:
157 bool canHandle(const std::string &funcName) const override;
158 void generate(CodeGenVisitor &visitor, const std::string &funcName, const std::vector<std::unique_ptr<ASTNode>> &arguments) override;
159 bool generateWithResult(CodeGenVisitor &visitor, const std::string &funcName, const std::vector<std::unique_ptr<ASTNode>> &arguments) override;
160 };
161
164 public:
165 bool canHandle(const std::string &funcName) const override;
166 void generate(CodeGenVisitor &visitor, const std::string &funcName, const std::vector<std::unique_ptr<ASTNode>> &arguments) override;
167 bool generateWithResult(CodeGenVisitor &visitor, const std::string &funcName, const std::vector<std::unique_ptr<ASTNode>> &arguments) override;
168 VarType getReturnType(const std::string &funcName) const override;
169 };
170
178 class CodeGenVisitor : public ASTVisitor {
179 private:
180 std::unordered_map<std::string, std::string> compileTimeConstants;
181 std::unordered_set<std::string> usedRealConstants;
183 std::unordered_map<std::string, std::string> currentParamTypes;
184 std::set<std::string> usedModules;
185 std::vector<std::string> objectDeps;
186 bool isUnit = false;
187 std::unordered_map<std::string, std::string> externalFuncs;
188 std::unordered_set<std::string> importedUnitNames;
189 std::unordered_map<std::string, ArrayInfo> importedArrayInfo;
190 std::unordered_map<std::string, VarType> importedVarTypes;
191 std::vector<std::string> globalArrays;
192 std::unordered_map<std::string, std::vector<std::string>> functionScopedArrays;
193 std::vector<std::string> scopeHierarchy;
194 struct Scope {
195 std::string name;
196 std::vector<ProcDeclNode *> nestedProcs;
197 std::vector<FuncDeclNode *> nestedFuncs;
198 };
199 std::vector<Scope> scopeStack;
200 std::unordered_map<std::string, std::string> typeAliases;
201 std::map<std::string, std::pair<std::string, std::string>> constInitialValues;
202 std::map<std::string, size_t> bufferStringVars;
203 std::map<std::string, std::string> currentParamLocations;
204 std::vector<std::pair<ProcDeclNode *, std::vector<std::string>>> deferredProcs;
205 std::vector<std::string> allTempPtrs;
206 std::vector<bool> tempPtrInUse;
207
208 std::string generateRealConstantName() { return "real_const_" + std::to_string(realConstantCounter++); }
209
210 bool isRealNumber(const std::string &s) const {
211 if (s.empty())
212 return false;
213 bool hasDot = false, hasExp = false;
214 for (char c : s) {
215 if (c == '.')
216 hasDot = true;
217 if (c == 'e' || c == 'E')
218 hasExp = true;
219 }
220 if (!hasDot && !hasExp)
221 return false;
222 char *end = nullptr;
223 std::strtod(s.c_str(), &end);
224 return end && *end == '\0';
225 }
226
227 std::string mangleWithScope(const std::string &baseName, const std::vector<std::string> &scopePath) const {
228 if (scopePath.empty() || scopePath.size() <= 1) {
229 return baseName;
230 }
231 std::string m;
232 for (size_t i = 1; i < scopePath.size(); ++i) {
233 if (i > 1)
234 m += "_";
235 m += scopePath[i];
236 }
237 m += "_" + baseName;
238 return m;
239 }
240
241 std::string mangleVariableName(const std::string &varName) const {
242 return mangleWithScope(varName, scopeHierarchy);
243 }
244
245 std::string getCurrentScopeName() const {
246 if (scopeHierarchy.empty() || scopeHierarchy.back() == "__global__")
247 return "";
248
249 std::string currentScope = scopeHierarchy.back();
250 auto &funcs = generatingDeferredCode ? deferredFuncs : std::vector<std::pair<FuncDeclNode *, std::vector<std::string>>>();
251 bool isFunc = false;
252 for (const auto &df : deferredFuncs) {
253 if (df.first->name == currentScope) {
254 isFunc = true;
255 break;
256 }
257 }
258
259 std::vector<std::string> parentScope(scopeHierarchy.begin(), scopeHierarchy.end() - 1);
260 return (isFunc ? "FUNC_" : "PROC_") + mangleWithScope(currentScope, parentScope);
261 }
262
264 struct FuncInfo {
265 std::vector<VarType> paramTypes;
267 };
268
269 std::unordered_map<std::string, FuncInfo> funcSignatures;
270 std::unordered_map<std::string, VarType> varTypes;
271 std::unordered_map<int, VarType> slotToType;
272
273 bool needsEmptyString = false;
274 std::string funcLabel(const char *prefix, const std::string &name) { return std::string(prefix) + name; }
275
276 VarType getVarType(const std::string &name) const {
277 std::string mangledName = findMangledName(name);
278 auto slotIt = varSlot.find(mangledName);
279 if (slotIt != varSlot.end()) {
280 auto typeIt = slotToType.find(slotIt->second);
281 if (typeIt != slotToType.end())
282 return typeIt->second;
283 }
284 auto it = varTypes.find(name);
285 return it != varTypes.end() ? it->second : VarType::UNKNOWN;
286 }
287
288 bool isRealConstSymbol(const std::string &s) const { return s.rfind("real_const_", 0) == 0; }
289
290 std::string coerceToIntImmediate(const std::string &v) {
291 if (isFloatLiteral(v)) {
292 long long x = static_cast<long long>(std::stod(v));
293 return std::to_string(x);
294 }
295 if (isRealConstSymbol(v)) {
296 auto it = realConstants.find(v);
297 if (it != realConstants.end()) {
298 long long x = static_cast<long long>(std::stod(it->second));
299 return std::to_string(x);
300 }
301 }
302 return v;
303 }
304
305 void setVarType(const std::string &name, VarType t) { varTypes[name] = t; }
306 void setSlotType(int slot, VarType t) { slotToType[slot] = t; }
307 bool isStringVar(const std::string &name) const {
308 auto t = getVarType(name);
309 return t == VarType::STRING || t == VarType::PTR;
310 }
311 std::string emptyString() {
312 needsEmptyString = true;
313 return "empty_str";
314 }
316 std::vector<std::string> registers = {"rax", "rbx", "rcx", "rdx", "rsi", "rdi", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"};
317 const std::vector<std::string> ptrRegisters = {
318 "arg0", "arg1", "arg2", "arg3", "arg4", "arg5", "arg6", "arg7", "arg8", "arg9"};
319 std::vector<std::string> floatRegisters;
320 std::vector<bool> floatRegInUse;
322
324 floatRegisters = {"xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7"};
325 floatRegInUse.resize(floatRegisters.size(), false);
326 }
327
328 std::vector<bool> regInUse;
329 std::vector<bool> ptrRegInUse;
330
331 std::vector<std::pair<FuncDeclNode *, std::vector<std::string>>> deferredFuncs;
334 std::vector<std::string> currentFuncLocalSlots;
335 bool functionSetReturn = false;
336 int nextSlot = 0;
337 int nextTemp = 0;
339
340 std::unordered_map<std::string, int> varSlot;
341 std::vector<std::string> prolog;
342 std::vector<std::string> instructions;
343 std::vector<std::string> evalStack;
344 std::vector<std::pair<std::string, std::string>> stringLiterals;
345 std::unordered_set<std::string> usedStrings;
346 std::unordered_map<int, std::string> slotToName;
347 std::unordered_map<std::string, std::vector<std::string>> recordsToFreeInScope;
348 std::unordered_map<std::string, std::vector<std::string>> tempPtrByScope;
349 std::unordered_map<std::string, bool> declaredProcs;
350 std::unordered_map<std::string, bool> declaredFuncs;
352
353 void addTempPtr(const std::string &v) {
354 tempPtrByScope[getCurrentScopeName()].push_back(v);
355 }
356
366 std::unordered_map<std::string, ValueLocation> valueLocations;
367
368 static bool endsWithColon(const std::string &s) { return !s.empty() && s.back() == ':'; }
369 bool isRegisterSlot(int slot) const { return slot < -1; }
370 bool isTempVar(const std::string &name) const { return name.find("__t") != std::string::npos; }
371
372 std::string slotVar(int slot) const {
373 auto it = slotToName.find(slot);
374 if (it != slotToName.end())
375 return it->second;
376 if (slot < -1 && slot >= -(int)registers.size()) {
377 int regIndex = -2 - slot;
378 if (regIndex >= 0 && regIndex < (int)registers.size())
379 return registers[regIndex];
380 }
381 if (slot <= -100) {
382 int regIndex = -100 - slot;
383 if (regIndex >= 0 && regIndex < (int)floatRegisters.size())
384 return floatRegisters[regIndex];
385 }
386 return "v" + std::to_string(slot);
387 }
388
389 std::string allocReg() {
390 for (size_t i = 1; i < regInUse.size(); ++i)
391 if (!regInUse[i]) {
392 regInUse[i] = true;
393 return registers[i];
394 }
395 std::string name = "_ir" + std::to_string(nextSpillReg++);
396 registers.push_back(name);
397 regInUse.push_back(true);
398 return name;
399 }
400
401 std::string allocPtrReg() {
402 for (size_t i = 0; i < ptrRegInUse.size(); ++i)
403 if (!ptrRegInUse[i]) {
404 ptrRegInUse[i] = true;
405 return ptrRegisters[i];
406 }
407
408 size_t i = ptrRegisters.size() - 1;
409 ptrRegInUse[i] = true;
410 return ptrRegisters[i];
411 }
412 std::string allocFloatReg() {
413 for (size_t i = 0; i < floatRegInUse.size(); ++i) {
414 if (!floatRegInUse[i]) {
415 floatRegInUse[i] = true;
416 return floatRegisters[i];
417 }
418 }
420 size_t newIndex = floatRegisters.size() - 1;
421 floatRegInUse[newIndex] = true;
422 return floatRegisters[newIndex];
423 }
424
426 int currentSize = floatRegisters.size();
427 floatRegisters.push_back("xmm" + std::to_string(currentSize));
428 floatRegInUse.push_back(false);
429 }
430
431 void freePtrReg(const std::string &reg) {
432 for (size_t i = 0; i < ptrRegisters.size(); ++i)
433 if (ptrRegisters[i] == reg) {
434 ptrRegInUse[i] = false;
435 return;
436 }
437 }
438
439 void freeReg(const std::string &reg) {
440 for (size_t i = 0; i < registers.size(); ++i)
441 if (registers[i] == reg) {
442 regInUse[i] = false;
443 return;
444 }
445 freePtrReg(reg);
446 releaseTempPtr(reg);
447 }
448 void freeFloatReg(const std::string &reg) {
449 for (size_t i = 0; i < floatRegisters.size(); ++i)
450 if (floatRegisters[i] == reg) {
451 floatRegInUse[i] = false;
452 return;
453 }
454 }
455 bool isFloatReg(const std::string &name) const { return std::find(floatRegisters.begin(), floatRegisters.end(), name) != floatRegisters.end(); }
456 bool isPtrReg(const std::string &name) const { return std::find(ptrRegisters.begin(), ptrRegisters.end(), name) != ptrRegisters.end(); }
457 bool isReg(const std::string &name) const {
458 return std::find(registers.begin(), registers.end(), name) != registers.end() || std::find(floatRegisters.begin(), floatRegisters.end(), name) != floatRegisters.end() || isPtrReg(name) || isTempPtr(name);
459 }
460
461 int newSlotFor(const std::string &name) {
462 auto it = varSlot.find(name);
463 if (it != varSlot.end())
464 return it->second;
465 int slot = nextSlot++;
466 varSlot[name] = slot;
467 if (!name.empty() && name.find("__t") == std::string::npos)
468 slotToName[slot] = name;
469 return slot;
470 }
471
472 std::string newTemp() { return slotVar(newSlotFor("__t" + std::to_string(nextTemp++))); }
473 std::string newLabel(const std::string &prefix = "L") {
474 return prefix + "_" + std::to_string(labelCounter++);
475 }
476
477 void freeTempPtr(const std::string &ptrName) {
478 for (size_t i = 0; i < allTempPtrs.size(); ++i) {
479 if (allTempPtrs[i] == ptrName) {
480 if (tempPtrInUse[i]) {
481 emit1("free", ptrName);
482 tempPtrInUse[i] = false;
483 }
484 return;
485 }
486 }
487 }
488
489 void releaseTempPtr(const std::string &ptrName) {
490 for (size_t i = 0; i < allTempPtrs.size(); ++i) {
491 if (allTempPtrs[i] == ptrName) {
492 tempPtrInUse[i] = false;
493 return;
494 }
495 }
496 }
497
498 bool isTempPtr(const std::string &name) const {
499 return std::find(allTempPtrs.begin(), allTempPtrs.end(), name) != allTempPtrs.end();
500 }
501
502 std::string allocTempPtr(const std::string &forScope = "") {
503 std::string scope = forScope.empty() ? getCurrentScopeName() : forScope;
504 if (scope.empty()) {
505 scope = "";
506 }
507
508 for (size_t i = 0; i < allTempPtrs.size(); ++i) {
509 if (!tempPtrInUse[i]) {
510 tempPtrInUse[i] = true;
511 std::string tempName = allTempPtrs[i];
512 tempPtrByScope[scope].push_back(tempName);
513 return tempName;
514 }
515 }
516
517 std::string tempName = "_tmpptr" + std::to_string(nextTemp++);
518 allTempPtrs.push_back(tempName);
519 tempPtrInUse.push_back(true);
520
521 tempPtrByScope[scope].push_back(tempName);
522 return tempName;
523 }
524
525 void emit(const std::string &s) {
526 instructions.push_back(s);
527 }
528 void emitLabel(const std::string &label) { instructions.push_back(label + ":"); }
529 void emit1(const std::string &op, const std::string &a) { emit(op + " " + a); }
530 void emit2(const std::string &op, const std::string &a, const std::string &b) {
531 std::string A = a;
532 std::string B = b;
533 if (isRealNumber(B) && B.rfind("real_const_", 0) != 0)
535 emit(op + " " + A + ", " + B);
536 }
537 void emit3(const std::string &op, const std::string &a, const std::string &b, const std::string &c) {
538 instructions.push_back(op + " " + a + ", " + b + ", " + c);
539 if (op == "alloc") {
540 allocatedPtrs.insert(a);
541 }
542 }
543 void emit4(const std::string &op, const std::string &a, const std::string &b, const std::string &c, const std::string &d) { emit(op + " " + a + ", " + b + ", " + c + ", " + d); }
544
545 std::string escapeStringForMxvm(const std::string &raw) const {
546 std::ostringstream o;
547 o << "\"";
548 for (char c : raw) {
549 if (c == '\\')
550 o << "\\\\";
551 else if (c == '\"')
552 o << "\\\"";
553 else if (c == '\n')
554 o << "\\n";
555 else if (c == '\t')
556 o << "\\t";
557 else
558 o << c;
559 }
560 o << "\"";
561 return o.str();
562 }
563
564 std::string ensureFloatConstSymbol(const std::string &value) {
565 if (!isRealNumber(value))
566 return value;
567 if (value.rfind("real_const_", 0) == 0)
568 return value;
569 for (const auto &p : realConstants)
570 if (p.second == value)
571 return p.first;
572 std::string n = generateRealConstantName();
573 realConstants[n] = value;
574 usedRealConstants.insert(n);
575 return n;
576 }
577
578 std::string internString(const std::string &val) {
579 for (const auto &p : stringLiterals)
580 if (p.first == val)
581 return p.second;
582 std::string key = "str_" + std::to_string(stringLiterals.size());
583 stringLiterals.push_back({val, key});
584 return key;
585 }
586
587 std::string convertCharLiteral(const std::string &c) {
588 if (c.length() == 3 && c[0] == '\'' && c[2] == '\'')
589 return std::to_string((int)c[1]);
590 if (c.length() == 4 && c[0] == '\'' && c[1] == '\\' && c[3] == '\'') {
591 char e = c[2];
592 int v = 0;
593 switch (e) {
594 case 'n':
595 v = 10;
596 break;
597 case 't':
598 v = 9;
599 break;
600 case 'r':
601 v = 13;
602 break;
603 case '\\':
604 v = 92;
605 break;
606 case '\'':
607 v = 39;
608 break;
609 case '0':
610 v = 0;
611 break;
612 default:
613 v = (int)e;
614 break;
615 }
616 return std::to_string(v);
617 }
618 return c;
619 }
620
621 void pushValue(const std::string &v) {
622 evalStack.push_back(v);
623 if (isReg(v))
625 }
626 std::string popValue() {
627 if (evalStack.empty())
628 throw std::runtime_error("Evaluation stack underflow");
629 std::string v = evalStack.back();
630 evalStack.pop_back();
631 return v;
632 }
633
634 std::string eval(ASTNode *n) {
635 if (!n)
636 return "0";
637 if (auto num = dynamic_cast<NumberNode *>(n)) {
638 if (!num->isReal && !isRealNumber(num->value))
639 return num->value;
640 }
641 n->accept(*this);
642 if (evalStack.empty())
643 throw std::runtime_error("Expression produced no value");
644 return popValue();
645 }
646
647 void recordLocation(const std::string &var, ValueLocation loc) { valueLocations[var] = loc; }
648
649 void pushTri(const char *op, const std::string &a, const std::string &b) {
650 bool isFloatOp =
651 isFloatReg(a) || isFloatReg(b) ||
652 isRealNumber(a) || isRealNumber(b) ||
653 a.find("real_const_") != std::string::npos ||
654 b.find("real_const_") != std::string::npos;
655
656 std::string leftOp = a;
657 std::string rightOp = b;
658
659 bool mustCopy = isParmReg(leftOp);
660 if (isReg(leftOp) && !mustCopy) {
661 emit2(op, leftOp, rightOp);
662 pushValue(leftOp);
663 if (isReg(b) && !isParmReg(b))
664 freeReg(b);
665 if (rightOp != b)
666 freeReg(rightOp);
667 } else {
668 std::string result = isFloatOp ? allocFloatReg() : allocReg();
669 emit2("mov", result, leftOp);
670 emit2(op, result, rightOp);
671 pushValue(result);
672 if (isReg(b) && !isParmReg(b))
673 freeReg(b);
674 if (leftOp != a)
675 freeReg(leftOp);
676 if (rightOp != b)
677 freeReg(rightOp);
678 }
679 }
680
681 void pushCmpResult(const std::string &a, const std::string &b, const char *jop) {
682 std::string t = allocReg();
683 std::string L1 = newLabel("CMP_TRUE");
684 std::string L2 = newLabel("CMP_END");
685 emit2("cmp", a, b);
686 emit1(jop, L1);
687 emit2("mov", t, "0");
688 emit1("jmp", L2);
689 emitLabel(L1);
690 emit2("mov", t, "1");
691 emitLabel(L2);
692 pushValue(t);
693 if (a == b) {
694 if (isReg(a) && !isParmReg(a))
695 freeReg(a);
696 } else {
697 if (isReg(a) && !isParmReg(a))
698 freeReg(a);
699 if (isReg(b) && !isParmReg(b))
700 freeReg(b);
701 }
702 }
703
704 void pushFloatCmpResult(const std::string &aIn, const std::string &bIn, const char *jop) {
705 std::string a = aIn, b = bIn;
706 if (isRealNumber(a) && a.find("real_const_") != 0) {
707 std::string n = generateRealConstantName();
708 realConstants[n] = a;
709 usedRealConstants.insert(n);
710 a = n;
711 }
712 if (isRealNumber(b) && b.find("real_const_") != 0) {
713 std::string n = generateRealConstantName();
714 realConstants[n] = b;
715 usedRealConstants.insert(n);
716 b = n;
717 }
718 std::string t = allocReg();
719 std::string L1 = newLabel("CMP_TRUE");
720 std::string L2 = newLabel("CMP_END");
721 emit2("fcmp", a, b);
722 emit1(jop, L1);
723 emit2("mov", t, "0");
724 emit1("jmp", L2);
725 emitLabel(L1);
726 emit2("mov", t, "1");
727 emitLabel(L2);
728 pushValue(t);
729 if (isFloatReg(a) && a != aIn && !isParmReg(aIn))
730 freeFloatReg(a);
731 if (isFloatReg(b) && b != bIn && !isParmReg(bIn))
732 freeFloatReg(b);
733 }
734
735 void pushLogicalAnd(const std::string &a, const std::string &b) {
736 std::string t = allocReg();
737 std::string L0 = newLabel("AND_ZERO");
738 std::string L1 = newLabel("AND_END");
739 emit2("cmp", a, "0");
740 emit1("je", L0);
741 emit2("cmp", b, "0");
742 emit1("je", L0);
743 emit2("mov", t, "1");
744 emit1("jmp", L1);
745 emitLabel(L0);
746 emit2("mov", t, "0");
747 emitLabel(L1);
748 pushValue(t);
749 if (isReg(a) && !isParmReg(a))
750 freeReg(a);
751 if (isReg(b) && !isParmReg(b))
752 freeReg(b);
753 }
754
755 void pushLogicalOr(const std::string &a, const std::string &b) {
756 std::string t = allocReg();
757 std::string L1 = newLabel("OR_ONE");
758 std::string L2 = newLabel("OR_END");
759 emit2("cmp", a, "0");
760 emit1("jne", L1);
761 emit2("cmp", b, "0");
762 emit1("jne", L1);
763 emit2("mov", t, "0");
764 emit1("jmp", L2);
765 emitLabel(L1);
766 emit2("mov", t, "1");
767 emitLabel(L2);
768 pushValue(t);
769 if (isReg(a) && !isParmReg(a))
770 freeReg(a);
771 if (isReg(b) && !isParmReg(b))
772 freeReg(b);
773 }
774
775 bool isParmReg(const std::string &name) const {
776 for (size_t i = 1; i <= 6 && i < registers.size(); ++i)
777 if (registers[i] == name)
778 return true;
779 for (size_t i = 0; i < ptrRegisters.size(); ++i)
780 if (ptrRegisters[i] == name)
781 return true;
782 return false;
783 }
784
785 void allocateRecordFieldArrays(const std::string &recordVarName, const std::string &recordTypeName) {
786 auto recTypeIt = recordTypes.find(recordTypeName);
787 if (recTypeIt == recordTypes.end())
788 return;
789 auto &recInfo = recTypeIt->second;
790 std::string currentScope = getCurrentScopeName();
791
792 for (const auto &field : recInfo.fields) {
793 if (!field.isArray)
794 continue;
795
796 std::string fieldArrayName = recordVarName + "_" + field.name;
797
798 int slot = newSlotFor(fieldArrayName);
800 updateDataSectionInitialValue(slotVar(slot), "ptr", "null");
801
802 emit3("alloc",
803 slotVar(slot),
804 std::to_string(field.arrayInfo.elementSize),
805 std::to_string(field.arrayInfo.size));
806
807 std::string basePtr = ensurePtrBase(recordVarName);
808 emit4("store", slotVar(slot), basePtr, std::to_string(field.offset), "1");
809
810 if (currentScope.empty())
811 globalArrays.push_back(slotVar(slot));
812 else
813 functionScopedArrays[currentScope].push_back(slotVar(slot));
814 }
815 }
816
818 auto lc = [](std::string s) { std::transform(s.begin(), s.end(), s.begin(),
819 [](unsigned char c){ return std::tolower(c); }); return s; };
820 auto resolveTypeName = [&](std::string t) {
821 t = lc(t);
822 std::unordered_set<std::string> seen;
823 while (typeAliases.count(t) && !seen.count(t)) {
824 seen.insert(t);
825 t = lc(typeAliases.at(t));
826 }
827 return t;
828 };
829
830 ArrayInfo info{};
831
832 // Dynamic array: no bounds specified
833 if (!atn->lowerBound && !atn->upperBound) {
834 info.isDynamic = true;
835 info.lowerBound = 0;
836 info.upperBound = -1;
837 info.size = 0;
838 } else {
839 info.lowerBound = std::stoi(evaluateConstantExpression(atn->lowerBound.get()));
840 info.upperBound = std::stoi(evaluateConstantExpression(atn->upperBound.get()));
841 info.size = info.upperBound - info.lowerBound + 1;
842 }
843
844 if (auto *childArr = dynamic_cast<ArrayTypeNode *>(atn->elementType.get())) {
845 info.elementType = "array";
846 info.elementIsArray = true;
847 info.elementArray = std::make_unique<ArrayInfo>(buildArrayInfoFromNode(childArr));
848 info.elementSize = info.elementArray->size * info.elementArray->elementSize;
849 } else if (auto *childSimple = dynamic_cast<SimpleTypeNode *>(atn->elementType.get())) {
850 info.elementType = resolveTypeName(childSimple->typeName);
852 } else if (dynamic_cast<RecordTypeNode *>(atn->elementType.get())) {
853 info.elementType = "record";
854 info.elementSize = 8;
855 } else if (auto *pt = dynamic_cast<PointerTypeNode *>(atn->elementType.get())) {
856 info.elementType = "^" + pt->baseTypeName;
857 info.elementSize = 8;
858 } else {
859 info.elementType = "integer";
860 info.elementSize = 8;
861 }
862 return info;
863 }
864
865 int getArrayElementSize(const std::string &tIn) {
866 auto t = resolveTypeName(lc(tIn));
867 if (t == "integer" || t == "boolean")
868 return 8;
869 if (t == "real")
870 return 8;
871 if (t == "char")
872 return 8;
873 if (t == "string" || t == "ptr")
874 return 8;
875 auto it = recordTypes.find(t);
876 if (it != recordTypes.end())
877 return it->second.size;
878 return 8;
879 }
880
881 public:
882 friend class IOFunctionHandler;
884 friend class StdFunctionHandler;
885 friend class SDLFunctionHandler;
888
890
903 void registerExternalFunc(const std::string &funcName, const std::string &unitName) {
904 externalFuncs[funcName] = unitName;
905 importedUnitNames.insert(unitName);
906 }
907
909 [[nodiscard]] bool isImportedUnit(const std::string &name) const {
910 return importedUnitNames.count(name) > 0;
911 }
912
914 void registerImportedConst(const std::string &unitName, ConstDeclNode &node) {
915 importedUnitNames.insert(unitName);
916 for (const auto &assignment : node.assignments) {
917 if (auto *strNode = dynamic_cast<StringNode *>(assignment->value.get())) {
918 std::string sym = internString(strNode->value);
919 std::string qualifiedName = unitName + "." + assignment->identifier;
920 compileTimeConstants[qualifiedName] = sym;
921 compileTimeConstants[assignment->identifier] = sym;
922 continue;
923 }
924 try {
925 std::string literalValue = evaluateConstantExpression(assignment->value.get());
926 bool isFloat = isRealNumber(literalValue);
927 if (isFloat)
928 literalValue = ensureFloatConstSymbol(literalValue);
929 std::string qualifiedName = unitName + "." + assignment->identifier;
930 std::string val = isFloat ? realConstants[literalValue] : literalValue;
931 compileTimeConstants[qualifiedName] = val;
932 compileTimeConstants[assignment->identifier] = val;
933 } catch (...) {
934 // Non-constant expression; skip
935 }
936 }
937 }
938
940 void registerImportedVar(const std::string &unitName, VarDeclNode &node) {
941 importedUnitNames.insert(unitName);
942 for (const auto &varName : node.identifiers) {
943 std::string qualifiedName = unitName + "." + varName;
944 if (std::holds_alternative<std::unique_ptr<ASTNode>>(node.type)) {
945 auto &typeNode = std::get<std::unique_ptr<ASTNode>>(node.type);
946 if (auto *arrayTypeNode = dynamic_cast<ArrayTypeNode *>(typeNode.get())) {
947 ArrayInfo info = buildArrayInfoFromNode(arrayTypeNode);
948 importedArrayInfo[qualifiedName] = info;
949 importedVarTypes[qualifiedName] = VarType::PTR;
950 arrayInfo[qualifiedName] = std::move(info);
951 setVarType(qualifiedName, VarType::PTR);
952 continue;
953 }
954 }
955 std::string typeName;
956 if (std::holds_alternative<std::string>(node.type))
957 typeName = std::get<std::string>(node.type);
958 VarType vt = getTypeFromString(typeName);
959 importedVarTypes[qualifiedName] = vt;
960 setVarType(qualifiedName, vt);
961 }
962 }
963
966 virtual ~CodeGenVisitor() = default;
967
970 builtinRegistry.registerHandler(std::make_unique<IOFunctionHandler>());
971 builtinRegistry.registerHandler(std::make_unique<StdFunctionHandler>());
972 builtinRegistry.registerHandler(std::make_unique<SDLFunctionHandler>());
973 builtinRegistry.registerHandler(std::make_unique<StringFunctionHandler>());
974 builtinRegistry.registerHandler(std::make_unique<FileFunctionHandler>());
975 }
976
977 std::string name = "App";
978
980 void emitFree(const std::string &s) {
981 if (allocatedPtrs.erase(s)) {
982 emit("free " + s);
983 }
984 }
985
990 void generate(ASTNode *root) {
991 if (!root)
992 return;
993
994 instructions.clear();
995 usedStrings.clear();
996 prolog.clear();
997 deferredProcs.clear();
998 deferredFuncs.clear();
999 valueLocations.clear();
1000 objectDeps.clear();
1001 isUnit = false;
1002
1003 varTypes.clear();
1004 globalArrays.clear();
1005 auto savedConstants = compileTimeConstants;
1006 compileTimeConstants.clear();
1007 functionScopedArrays.clear();
1008 scopeStack.clear();
1009 scopeHierarchy.clear();
1010 scopeStack.push_back({"__global__"});
1011 scopeHierarchy.push_back("__global__");
1012 generatingDeferredCode = false;
1013 functionSetReturn = false;
1014 recordTypes.clear();
1015 varRecordType.clear();
1016 pointerBaseType.clear();
1017 typeAliases.clear();
1018 arrayInfo.clear();
1019
1020 // Restore imported unit variable metadata (registered before generate())
1021 arrayInfo.insert(importedArrayInfo.begin(), importedArrayInfo.end());
1022 varTypes.insert(importedVarTypes.begin(), importedVarTypes.end());
1023 compileTimeConstants.insert(savedConstants.begin(), savedConstants.end());
1024
1025 for (size_t i = 0; i < regInUse.size(); ++i)
1026 regInUse[i] = false;
1027 for (size_t i = 0; i < ptrRegInUse.size(); ++i)
1028 ptrRegInUse[i] = false;
1029 for (size_t i = 0; i < floatRegInUse.size(); ++i)
1030 floatRegInUse[i] = false;
1031
1032 if (auto prog = dynamic_cast<ProgramNode *>(root))
1033 name = prog->name;
1034 if (auto unit = dynamic_cast<UnitNode *>(root))
1035 name = unit->name;
1036
1037 root->accept(*this);
1038
1039 if (!isUnit) {
1040 for (const auto &arrayName : globalArrays)
1041 emit1("free", arrayName);
1042
1043 if (recordsToFreeInScope.count("")) {
1044 for (const auto &recordVar : recordsToFreeInScope[""]) {
1045 emit1("free", recordVar);
1046 }
1047 }
1048
1049 emit("done");
1050 }
1051
1053
1054 for (size_t i = 0; i < deferredProcs.size(); ++i) {
1055 auto pn = deferredProcs[i].first;
1056 auto oldHierarchy = scopeHierarchy;
1057 scopeHierarchy = deferredProcs[i].second;
1058
1059 std::vector<std::string> parentScope(scopeHierarchy.begin(), scopeHierarchy.end() - 1);
1060 std::string mangledName = mangleWithScope(pn->name, parentScope);
1061
1062 std::string scopeName = "PROC_" + mangledName;
1063
1064 emitLabel(funcLabel("function PROC_", mangledName));
1065 currentFunctionName = pn->name;
1066 currentFuncLocalSlots.clear();
1067 currentParamLocations.clear();
1068 currentParamTypes.clear();
1069
1070 if (!pn->parameters.empty()) {
1071 size_t intParamIndex = 1;
1072 size_t ptrParamIndex = 0;
1073 size_t floatParamIndex = 0;
1074
1075 for (auto &p_node : pn->parameters) {
1076 if (auto *param = dynamic_cast<ParameterNode *>(p_node.get())) {
1077
1078 const std::string normType = resolveTypeName(lc(param->type));
1079 const bool isArrayParam = (arrayInfo.find(normType) != arrayInfo.end());
1080
1081 VarType declType = getTypeFromString(param->type);
1082 std::unordered_map<std::string, bool> declaredFuncs;
1083
1084 for (const auto &id : param->identifiers) {
1085 VarType useType = declType;
1086 std::string incomingReg;
1087
1088 if (declType == VarType::DOUBLE) {
1089 if (floatParamIndex < floatRegisters.size())
1090 incomingReg = floatRegisters[floatParamIndex++];
1091 } else if (declType == VarType::PTR || declType == VarType::STRING ||
1092 declType == VarType::RECORD || isArrayParam) {
1093 useType = VarType::PTR;
1094 if (ptrParamIndex < ptrRegisters.size())
1095 incomingReg = ptrRegisters[ptrParamIndex++];
1096 } else {
1097 if (intParamIndex < registers.size())
1098 incomingReg = registers[intParamIndex++];
1099 }
1100
1101 std::string mangledId = mangleVariableName(id);
1102 int localSlot = newSlotFor(mangledId);
1103 setSlotType(localSlot, useType);
1104 setVarType(id, useType);
1105
1106 if (!incomingReg.empty())
1107 emit2("mov", slotVar(localSlot), incomingReg);
1108 currentFuncLocalSlots.push_back(slotVar(localSlot));
1109
1110 if (isArrayParam) {
1111 arrayInfo[mangledId] = arrayInfo.at(normType);
1112 }
1113
1114 currentParamTypes[id] = normType;
1115 }
1116 }
1117 }
1118 }
1119
1120 for (size_t r = 0; r < regInUse.size(); ++r)
1121 regInUse[r] = false;
1122 for (size_t r = 0; r < ptrRegInUse.size(); ++r)
1123 ptrRegInUse[r] = false;
1124 for (size_t r = 0; r < floatRegInUse.size(); ++r)
1125 floatRegInUse[r] = false;
1126 for (const auto &pair : currentParamLocations) {
1127 for (size_t reg_idx = 0; reg_idx < registers.size(); ++reg_idx) {
1128 if (registers[reg_idx] == pair.second) {
1129 regInUse[reg_idx] = true;
1130 break;
1131 }
1132 }
1133 }
1134
1135 if (pn->block)
1136 pn->block->accept(*this);
1137
1138 emitLabel("PROC_END_" + mangledName);
1139
1140 for (const auto &recVar : recordsToFreeInScope[scopeName]) {
1141 emit1("free", recVar);
1142 }
1143 auto temp1 = tempPtrByScope[scopeName];
1144 for (const auto &tp : temp1) {
1145 if (allocatedPtrs.count(tp) && !escapedTempPtrs.count(tp))
1146 emitFree(tp);
1147 }
1148 escapedTempPtrs.clear();
1149
1150 emit("ret");
1151 scopeHierarchy = oldHierarchy;
1152 }
1153
1154 for (size_t i = 0; i < deferredFuncs.size(); ++i) {
1155 auto fn = deferredFuncs[i].first;
1156 auto oldHierarchy = scopeHierarchy;
1157 scopeHierarchy = deferredFuncs[i].second;
1158
1159 std::vector<std::string> parentScope(scopeHierarchy.begin(), scopeHierarchy.end() - 1);
1160 std::string mangledName = mangleWithScope(fn->name, parentScope);
1161
1162 std::string scopeName = "FUNC_" + mangledName;
1163
1164 emitLabel(funcLabel("function FUNC_", mangledName));
1165 currentFunctionName = fn->name;
1167 currentFuncLocalSlots.clear();
1168 currentParamLocations.clear();
1169 currentParamTypes.clear();
1170 functionSetReturn = false;
1171
1172 if (!fn->parameters.empty()) {
1173 int intParamIndex = 1;
1174 int ptrParamIndex = 0;
1175 int floatParamIndex = 0;
1176 functionSetReturn = false;
1177
1178 for (auto &p_node : fn->parameters) {
1179 if (auto *param = dynamic_cast<ParameterNode *>(p_node.get())) {
1180 const std::string normType = resolveTypeName(lc(param->type));
1181 const bool isArrayParam = (arrayInfo.find(normType) != arrayInfo.end());
1182
1183 for (const auto &id : param->identifiers) {
1184 VarType paramType = getTypeFromString(param->type);
1185 std::string incomingReg;
1186
1187 if (paramType == VarType::STRING || paramType == VarType::RECORD || isArrayParam) {
1188 if (static_cast<size_t>(ptrParamIndex) < ptrRegisters.size())
1189 incomingReg = ptrRegisters[ptrParamIndex++];
1190 paramType = VarType::PTR;
1191 } else if (paramType == VarType::DOUBLE) {
1192 if (static_cast<size_t>(floatParamIndex) < floatRegisters.size())
1193 incomingReg = floatRegisters[floatParamIndex++];
1194 } else {
1195 if (static_cast<size_t>(intParamIndex) < registers.size())
1196 incomingReg = registers[intParamIndex++];
1197 }
1198
1199 if (!incomingReg.empty()) {
1200 std::string mangledId = mangleVariableName(id);
1201 int localSlot = newSlotFor(mangledId);
1202 setSlotType(localSlot, paramType);
1203 setVarType(id, paramType);
1204 emit2("mov", slotVar(localSlot), incomingReg);
1205 currentFuncLocalSlots.push_back(slotVar(localSlot));
1206
1207 if (isArrayParam) {
1208 arrayInfo[mangledId] = arrayInfo.at(normType);
1209 }
1210 }
1211 currentParamTypes[id] = normType;
1212 }
1213 }
1214 }
1215 }
1216
1217 for (size_t r = 0; r < regInUse.size(); ++r)
1218 regInUse[r] = false;
1219 for (size_t r = 0; r < ptrRegInUse.size(); ++r)
1220 ptrRegInUse[r] = false;
1221 for (size_t r = 0; r < floatRegInUse.size(); ++r)
1222 floatRegInUse[r] = false;
1223 for (const auto &pair : currentParamLocations) {
1224 for (size_t reg_idx = 0; reg_idx < registers.size(); ++reg_idx) {
1225 if (registers[reg_idx] == pair.second) {
1226 regInUse[reg_idx] = true;
1227 break;
1228 }
1229 }
1230 }
1231
1232 if (fn->block)
1233 fn->block->accept(*this);
1234
1235 emitLabel("FUNC_END_" + mangledName);
1236
1238 VarType rt = getVarType(fn->name);
1239 if (rt == VarType::STRING || rt == VarType::PTR || rt == VarType::RECORD)
1240 emit2("mov", "arg0", currentFunctionReturnSlot);
1241 else if (rt == VarType::DOUBLE)
1242 emit2("mov", "xmm0", currentFunctionReturnSlot);
1243 else
1244 emit2("mov", "rax", currentFunctionReturnSlot);
1245 } else if (!functionSetReturn) {
1246 VarType rt = getVarType(fn->name);
1247 if (rt == VarType::STRING || rt == VarType::PTR || rt == VarType::RECORD) {
1248 emit2("mov", "arg0", emptyString());
1249 } else if (rt == VarType::DOUBLE) {
1250 std::string zc = generateRealConstantName();
1251 realConstants[zc] = "0.0";
1252 usedRealConstants.insert(zc);
1253 emit2("mov", "xmm0", zc);
1254 } else {
1255 emit2("mov", "rax", "0");
1256 }
1257 }
1258
1259 for (const auto &recVar : recordsToFreeInScope[scopeName]) {
1260 emit1("free", recVar);
1261 }
1262
1263 auto temp1 = tempPtrByScope[scopeName];
1264 for (const auto &tp : temp1) {
1265 if (allocatedPtrs.count(tp) && !escapedTempPtrs.count(tp))
1266 emitFree(tp);
1267 }
1268 escapedTempPtrs.clear();
1269
1270 emit("ret");
1271 scopeHierarchy = oldHierarchy;
1272 }
1273
1274 generatingDeferredCode = false;
1275 currentFunctionName.clear();
1276 currentParamLocations.clear();
1277 }
1278
1280 void markAllocatedPtr(const std::string &p) { allocatedPtrs.insert(p); }
1281
1287 void emit_invoke(const std::string &funcName, const std::vector<std::string> &params) {
1288 std::string instruction = "invoke " + funcName;
1289 std::vector<std::string> tempRegs;
1290 for (const auto &p : params) {
1291 if (isIntLiteral(p) || isRealNumber(p)) {
1292 std::string r = isRealNumber(p) ? allocFloatReg() : allocReg();
1293 emit2("mov", r, p);
1294 instruction += ", " + r;
1295 tempRegs.push_back(r);
1296 } else {
1297 instruction += ", " + p;
1298 }
1299 }
1300 emit(instruction);
1301 for (const auto &r : tempRegs)
1302 freeReg(r);
1303 }
1304
1305 bool isIntLiteral(const std::string &s) const {
1306 if (s.empty())
1307 return false;
1308 size_t start = (s[0] == '-') ? 1 : 0;
1309 if (start >= s.size())
1310 return false;
1311 for (size_t i = start; i < s.size(); ++i)
1312 if (!std::isdigit(static_cast<unsigned char>(s[i])))
1313 return false;
1314 return true;
1315 }
1316
1324 void writeTo(std::ostream &out) const {
1325 out << (isUnit ? "object " : "program ") << name << " {\n";
1326
1327 // section object (unit/object dependencies)
1328 if (!objectDeps.empty()) {
1329 out << "\tsection object {\n\t\t";
1330 bool first = true;
1331 for (const auto &dep : objectDeps) {
1332 if (!first)
1333 out << ", ";
1334 out << dep;
1335 first = false;
1336 }
1337 out << "\n\t}\n";
1338 }
1339
1340 out << "\tsection module {\n ";
1341 bool first = true;
1342 for (const auto &mod : usedModules) {
1343 if (!first)
1344 out << ",\n";
1345 out << "\t\t" << mod;
1346 first = false;
1347 }
1348 out << "\n\t}\n";
1349 out << "\tsection data {\n";
1350
1351 for (const auto &reg : registers)
1352 out << "\t\tint " << reg << " = 0\n";
1353
1354 for (size_t i = 0; i < floatRegisters.size(); ++i) {
1355 out << "\t\tfloat " << floatRegisters[i] << " = 0.0\n";
1356 }
1357
1358 {
1359 std::set<std::string> temps;
1360 for (const auto &p : ptrRegisters)
1361 temps.insert(p);
1362 for (const auto &t : allTempPtrs)
1363 temps.insert(t);
1364 for (const auto &kv : tempPtrByScope)
1365 for (const auto &t : kv.second)
1366 temps.insert(t);
1367 for (const auto &tempPtr : temps)
1368 out << "\t\tptr " << tempPtr << " = null\n";
1369 }
1370
1371 for (const auto &constant : realConstants)
1372 out << "\t\tfloat " << constant.first << " = " << constant.second << "\n";
1373 if (needsEmptyString)
1374 out << "\t\tstring empty_str = \"\"\n";
1375 for (const auto &pair : stringLiterals)
1376 out << "\t\tstring " << pair.second << " = " << escapeStringForMxvm(pair.first) << "\n";
1377 if (usedStrings.count("fmt_int"))
1378 out << "\t\tstring fmt_int = \"%lld \"\n";
1379 if (usedStrings.count("fmt_str"))
1380 out << "\t\tstring fmt_str = \"%s \"\n";
1381 if (usedStrings.count("fmt_chr"))
1382 out << "\t\tstring fmt_chr = \"%c \"\n";
1383 if (usedStrings.count("fmt_float"))
1384 out << "\t\tstring fmt_float = \"%.6f \"\n";
1385 if (usedStrings.count("newline"))
1386 out << "\t\tstring newline = \"\\n\"\n";
1387 out << "\t\tstring input_buffer, 256\n";
1388 for (int i = 0; i < nextSlot; ++i) {
1389 if (!isRegisterSlot(i) && !isTempVar(slotVar(i)) && !isPtrReg(slotVar(i))) {
1390 auto it = slotToType.find(i);
1391 if (it != slotToType.end()) {
1392 std::string varName = slotVar(i);
1393 auto bufIt = bufferStringVars.find(varName);
1394 if (bufIt != bufferStringVars.end())
1395 out << "\t\tstring " << varName << ", " << bufIt->second << "\n";
1396 else {
1397 auto constIt = constInitialValues.find(varName);
1398 if (constIt != constInitialValues.end())
1399 out << "\t\t" << constIt->second.first << " " << varName << " = " << constIt->second.second << "\n";
1400 else {
1401 if (it->second == VarType::STRING)
1402 out << "\t\tstring " << varName << " = \"\"\n";
1403 else if (it->second == VarType::PTR)
1404 out << "\t\tptr " << varName << " = null\n";
1405 else if (it->second == VarType::CHAR)
1406 out << "\t\tint " << varName << " = 0\n";
1407 else if (it->second == VarType::DOUBLE)
1408 out << "\t\tfloat " << varName << " = 0.0\n";
1409 else
1410 out << "\t\tint " << varName << " = 0\n";
1411 }
1412 }
1413 } else
1414 out << "\t\tint " << slotVar(i) << " = 0\n";
1415 }
1416 }
1417 out << "\t}\n";
1418 out << "\tsection code {\n";
1419 if (!isUnit) {
1420 out << "\tstart:\n";
1421 // Call each dependency unit's UNIT_INIT to allocate their arrays
1422 for (const auto &dep : objectDeps) {
1423 out << "\t\tcall " << dep << ".PROC_UNIT_INIT\n";
1424 }
1425 for (auto &s : prolog)
1426 out << "\t\t" << s << "\n";
1427 }
1428 for (auto &s : instructions) {
1429 if (endsWithColon(s))
1430 out << "\t" << s << "\n";
1431 else
1432 out << "\t\t" << s << "\n";
1433 }
1434 out << "\t}\n";
1435 out << "}\n";
1436 }
1437
1441 void visit(ProgramNode &node) override;
1442 void visit(UnitNode &node) override;
1443 void visit(BlockNode &node) override;
1444 void visit(VarDeclNode &node) override;
1445 void visit(ProcCallNode &node) override;
1446 void visit(FuncCallNode &node) override;
1447 void visit(FuncDeclNode &node) override;
1448 void visit(ProcDeclNode &node) override;
1449 void visit(TypeAliasNode &node) override;
1450 void visit(ParameterNode &node) override;
1451 void visit(CompoundStmtNode &node) override;
1452 void visit(IfStmtNode &node) override;
1453 void visit(WhileStmtNode &node) override;
1454 void visit(ForStmtNode &node) override;
1455 void visit(BinaryOpNode &node) override;
1456 void visit(UnaryOpNode &node) override;
1457 void visit(VariableNode &node) override;
1458 void visit(NumberNode &node) override;
1459 void visit(StringNode &node) override;
1460 void visit(BooleanNode &node) override;
1461 void visit(EmptyStmtNode &node) override;
1462 void visit(ConstDeclNode &node) override;
1463 void visit(RepeatStmtNode &node) override;
1464 void visit(CaseStmtNode &node) override;
1465 void visit(ArrayTypeNode &node) override;
1466 void visit(ArrayDeclarationNode &node) override;
1467 void visit(FieldAccessNode &node) override;
1468 void visit(AssignmentNode &node) override;
1469 void visit(ArrayAssignmentNode &node) override;
1470 void visit(ArrayAccessNode &node) override;
1471 void visit(RecordTypeNode &node) override;
1472 void visit(RecordDeclarationNode &node) override;
1473 void visit(TypeDeclNode &node) override;
1474 void visit(SimpleTypeNode &node) override;
1475 void visit(ArrayTypeDeclarationNode &node) override;
1476 void visit(ExitNode &node) override;
1477 void visit(BreakNode &node) override;
1478 void visit(ContinueNode &node) override;
1479 void visit(NilNode &node) override;
1480 void visit(PointerTypeNode &node) override;
1481 void visit(PointerDerefNode &node) override;
1482 void visit(AddressOfNode &node) override;
1483 void visit(WithStmtNode &node) override;
1484 void visit(GotoStmtNode &node) override;
1485 void visit(LabelStmtNode &node) override;
1486 void visit(SetLiteralNode &node) override;
1487 void visit(SetTypeNode &node) override;
1488 void visit(EnumTypeDeclNode &node) override;
1490
1492 std::unordered_map<std::string, int> enumConstants;
1494 std::unordered_map<std::string, std::vector<std::string>> enumTypes;
1496 std::unordered_set<std::string> setVars;
1498 std::unordered_set<std::string> fileVars;
1500 std::unordered_map<std::string, std::string> fileVarNames;
1501
1502 std::string getTypeString(const VarDeclNode &node) {
1503 if (std::holds_alternative<std::string>(node.type))
1504 return std::get<std::string>(node.type);
1505 if (std::holds_alternative<std::unique_ptr<ASTNode>>(node.type)) {
1506 return "record";
1507 }
1508 return "";
1509 }
1510
1511 std::string storageSymbolFor(const std::string &mangled) {
1512 auto it = varSlot.find(mangled);
1513 if (it != varSlot.end())
1514 return slotVar(it->second);
1515 return mangled;
1516 }
1517 bool isRecordTypeName(const std::string &t) {
1518 auto base = resolveTypeName(lc(t));
1519 return recordTypes.find(base) != recordTypes.end();
1520 }
1521
1522 int getTypeSizeByName(const std::string &t) {
1523 auto base = resolveTypeName(lc(t));
1524 if (base == "integer" || base == "boolean")
1525 return 8;
1526 if (base == "real")
1527 return 8;
1528 if (base == "char")
1529 return 8;
1530 if (base == "string" || base == "ptr")
1531 return 8;
1532 auto it = recordTypes.find(base);
1533 if (it != recordTypes.end())
1534 return it->second.size;
1535 return 8;
1536 }
1537
1538 VarType getTypeFromString(const std::string &typeStr) {
1539 auto base = resolveTypeName(lc(typeStr));
1540 if (base == "integer" || base == "boolean")
1541 return VarType::INT;
1542 if (base == "real")
1543 return VarType::DOUBLE;
1544 if (base == "string")
1545 return VarType::PTR;
1546 if (base == "char")
1547 return VarType::CHAR;
1548 if (base == "pointer")
1549 return VarType::PTR;
1550 if (base == "file" || base == "text")
1551 return VarType::PTR;
1552 if (base.rfind("set of ", 0) == 0)
1553 return VarType::PTR;
1554 if (!base.empty() && base[0] == '^')
1555 return VarType::PTR;
1556 if (isRecordTypeName(base))
1557 return VarType::RECORD;
1558 if (arrayInfo.find(base) != arrayInfo.end())
1559 return VarType::PTR;
1560 return VarType::UNKNOWN;
1561 }
1562
1572 void emitArrayBoundsCheck(const std::string &idxReg, int lower, int upper) {
1573#ifdef MXVM_BOUNDS_CHECK
1574 std::string L_ok = newLabel("IDX_OK");
1575 std::string L_fail = newLabel("IDX_OOB");
1576 emit2("cmp", idxReg, std::to_string(lower));
1577 emit1("jl", L_fail);
1578 emit2("cmp", idxReg, std::to_string(upper));
1579 emit1("jg", L_fail);
1580 emit1("jmp", L_ok);
1581 emitLabel(L_fail);
1582 emit1("exit", "1");
1583 emitLabel(L_ok);
1584#endif
1585 }
1586
1596 void emitDynArrayBoundsCheck(const std::string &idxReg, const std::string &lenSym) {
1597#ifdef MXVM_BOUNDS_CHECK
1598 std::string L_ok = newLabel("IDX_OK");
1599 std::string L_fail = newLabel("IDX_OOB");
1600 emit2("cmp", idxReg, "0");
1601 emit1("jl", L_fail);
1602 std::string tmp = allocReg();
1603 emit2("mov", tmp, lenSym);
1604 emit2("cmp", idxReg, tmp);
1605 freeReg(tmp);
1606 emit1("jge", L_fail);
1607 emit1("jmp", L_ok);
1608 emitLabel(L_fail);
1609 emit1("exit", "1");
1610 emitLabel(L_ok);
1611#endif
1612 }
1613
1615 if (dynamic_cast<NilNode *>(node))
1616 return VarType::PTR;
1617 if (dynamic_cast<AddressOfNode *>(node))
1618 return VarType::PTR;
1619 if (dynamic_cast<NumberNode *>(node)) {
1620 auto n = static_cast<NumberNode *>(node);
1621 return n->isReal ? VarType::DOUBLE : VarType::INT;
1622 }
1623 if (dynamic_cast<StringNode *>(node)) {
1624 return VarType::STRING;
1625 }
1626 if (dynamic_cast<BooleanNode *>(node))
1627 return VarType::BOOL;
1628 if (auto derefNode = dynamic_cast<PointerDerefNode *>(node)) {
1629 if (auto varNode = dynamic_cast<VariableNode *>(derefNode->pointer.get())) {
1630 return getPointerDerefType(varNode->name);
1631 }
1632 return VarType::INT;
1633 }
1634 if (auto varNode = dynamic_cast<VariableNode *>(node))
1635 return getVarType(varNode->name);
1636 if (auto funcCall = dynamic_cast<FuncCallNode *>(node)) {
1637 std::string fnLower = lc(funcCall->name);
1638 auto handler = builtinRegistry.findHandler(fnLower);
1639 if (handler)
1640 return handler->getReturnType(fnLower);
1641 auto it = funcSignatures.find(funcCall->name);
1642 if (it != funcSignatures.end())
1643 return it->second.returnType;
1644 }
1645 if (auto fieldNode = dynamic_cast<FieldAccessNode *>(node)) {
1646 std::string recType = getVarRecordTypeNameFromExpr(fieldNode->recordExpr.get());
1647 recType = resolveTypeName(lc(recType));
1648 auto it = recordTypes.find(recType);
1649 if (it != recordTypes.end()) {
1650 auto jt = it->second.nameToIndex.find(lc(fieldNode->fieldName));
1651 if (jt != it->second.nameToIndex.end()) {
1652 const auto &f = it->second.fields[jt->second];
1653 if (f.isArray) {
1654 return VarType::PTR;
1655 }
1656 return getTypeFromString(f.typeName);
1657 }
1658 }
1659 }
1660 if (auto a = dynamic_cast<ArrayAccessNode *>(node)) {
1662 if (info) {
1663 if (info->elementIsArray)
1664 return VarType::PTR;
1665 const std::string &t = info->elementType;
1666 if (t == "integer" || t == "boolean")
1667 return VarType::INT;
1668 if (t == "real")
1669 return VarType::DOUBLE;
1670 if (t == "char")
1671 return VarType::CHAR;
1672 if (t == "string" || t == "ptr" ||
1673 t == "pointer" ||
1674 (!t.empty() && t[0] == '^'))
1675 return VarType::PTR;
1676 if (isRecordTypeName(t))
1677 return VarType::RECORD;
1678 }
1679 }
1680 if (auto b = dynamic_cast<BinaryOpNode *>(node)) {
1681 VarType lt = getExpressionType(b->left.get());
1682 VarType rt = getExpressionType(b->right.get());
1683 if (b->operator_ == BinaryOpNode::PLUS) {
1684 auto isStrLike = [](VarType v) { return v == VarType::STRING || v == VarType::PTR; };
1685 if (isStrLike(lt) || isStrLike(rt))
1686 return VarType::PTR;
1687 }
1688 if (lt == VarType::DOUBLE || rt == VarType::DOUBLE)
1689 return VarType::DOUBLE;
1690 return lt;
1691 }
1692 return VarType::INT;
1693 }
1694
1695 private:
1697 if (!node)
1698 return "";
1699 if (auto numNode = dynamic_cast<NumberNode *>(node)) {
1700 return numNode->value;
1701 }
1702 if (auto varNode = dynamic_cast<VariableNode *>(node)) {
1703 auto it = compileTimeConstants.find(findMangledName(varNode->name));
1704 if (it != compileTimeConstants.end())
1705 return it->second;
1706 it = compileTimeConstants.find(varNode->name);
1707 if (it != compileTimeConstants.end())
1708 return it->second;
1709 throw std::runtime_error("Cannot use non-constant variable '" + varNode->name + "' in a constant expression.");
1710 }
1711 if (auto binOp = dynamic_cast<BinaryOpNode *>(node)) {
1712 std::string L = evaluateConstantExpression(binOp->left.get());
1713 std::string R = evaluateConstantExpression(binOp->right.get());
1714 if (L.empty() || R.empty())
1715 throw std::runtime_error("Unsupported node type in constant expression.");
1716
1717 bool isFloatOp = isFloatLiteral(L) || isFloatLiteral(R) || binOp->operator_ == BinaryOpNode::DIVIDE;
1718
1719 auto toD = [&](const std::string &s) { return std::stod(s); };
1720 auto toI = [&](const std::string &s) {
1721 return isFloatLiteral(s) ? static_cast<long long>(std::stod(s)) : std::stoll(s);
1722 };
1723
1724 if (isFloatOp) {
1725 double result;
1726 switch (binOp->operator_) {
1727 case BinaryOpNode::PLUS:
1728 result = toD(L) + toD(R);
1729 break;
1731 result = toD(L) - toD(R);
1732 break;
1734 result = toD(L) * toD(R);
1735 break;
1737 if (toD(R) == 0.0)
1738 throw std::runtime_error("division by zero in constant expression");
1739 result = toD(L) / toD(R);
1740 break;
1741 default:
1742 throw std::runtime_error("Unsupported operator for floats in constant expression.");
1743 }
1744 return std::to_string(result);
1745 } else {
1746 long long result;
1747 switch (binOp->operator_) {
1748 case BinaryOpNode::PLUS:
1749 result = toI(L) + toI(R);
1750 break;
1752 result = toI(L) - toI(R);
1753 break;
1755 result = toI(L) * toI(R);
1756 break;
1757 case BinaryOpNode::DIV:
1758 if (toI(R) == 0)
1759 throw std::runtime_error("division by zero in constant expression");
1760 result = toI(L) / toI(R);
1761 break;
1762 case BinaryOpNode::MOD:
1763 if (toI(R) == 0)
1764 throw std::runtime_error("mod by zero in constant expression");
1765 result = toI(L) % toI(R);
1766 break;
1767 default:
1768 throw std::runtime_error("Unsupported operator for integers in constant expression.");
1769 }
1770 return std::to_string(result);
1771 }
1772 }
1773 if (auto un = dynamic_cast<UnaryOpNode *>(node)) {
1774 std::string v = evaluateConstantExpression(un->operand.get());
1775 if (v.empty())
1776 throw std::runtime_error("Unsupported node type in constant expression.");
1777 double dv = std::stod(v);
1778 switch (un->operator_) {
1779 case UnaryOpNode::MINUS:
1780 return std::to_string(-dv);
1781 case UnaryOpNode::PLUS:
1782 return v;
1783 default:
1784 throw std::runtime_error("Unsupported unary op in constant expression.");
1785 }
1786 }
1787 throw std::runtime_error("Unsupported node type in constant expression.");
1788 }
1789
1790 public:
1791 void updateDataSectionInitialValue(const std::string &varName, const std::string &type, const std::string &value) { constInitialValues[varName] = {type, value}; }
1792
1793 private:
1794 std::unordered_set<std::string> allocatedPtrs;
1795 std::unordered_set<std::string> escapedTempPtrs;
1796
1797 std::unordered_map<std::string, std::string> realConstants;
1798
1799 std::unordered_map<std::string, ArrayInfo> arrayInfo;
1800 std::unordered_map<std::string, int> dynArrayLenSlot;
1801
1802 std::string getTypeString(ASTNode *typeNode) {
1803 if (auto simpleType = dynamic_cast<SimpleTypeNode *>(typeNode)) {
1804 return simpleType->typeName;
1805 } else if (dynamic_cast<ArrayTypeNode *>(typeNode)) {
1806 return "array";
1807 } else if (dynamic_cast<RecordTypeNode *>(typeNode)) {
1808 return "record";
1809 }
1810 return "unknown";
1811 }
1812
1813 static inline std::string lc(std::string s) {
1814 std::transform(s.begin(), s.end(), s.begin(),
1815 [](unsigned char c) { return std::tolower(c); });
1816 return s;
1817 }
1818
1819 std::string resolveTypeName(std::string t) const {
1820 if (t.empty())
1821 return t;
1822 t = lc(t);
1823 std::unordered_set<std::string> seen;
1824 while (true) {
1825 if (!typeAliases.count(t))
1826 break;
1827 if (seen.count(t))
1828 break;
1829 seen.insert(t);
1830 t = lc(typeAliases.at(t));
1831 }
1832 return t;
1833 }
1834
1835 int getRecordTypeSize(const std::string &typeName) {
1836 auto it = recordTypes.find(typeName);
1837 if (it != recordTypes.end()) {
1838 return it->second.size;
1839 }
1840 return 8;
1841 }
1842
1843 std::string findMangledFuncName(const std::string &name, bool isProc) const {
1844 auto &lookupMap = isProc ? declaredProcs : declaredFuncs;
1845 for (int depth = (int)scopeHierarchy.size() - 1; depth >= 0; --depth) {
1846 std::vector<std::string> tempScope(scopeHierarchy.begin(), scopeHierarchy.begin() + depth + 1);
1847 std::string candidate = mangleWithScope(name, tempScope);
1848 if (lookupMap.count(candidate)) {
1849 return candidate;
1850 }
1851 }
1852 return name;
1853 }
1854
1855 std::string findMangledName(const std::string &name) const {
1856 if (scopeHierarchy.empty())
1857 return name;
1858 for (int depth = (int)scopeHierarchy.size() - 1; depth >= 0; --depth) {
1859 std::string candidate;
1860 if (depth == 0)
1861 candidate = name;
1862 else {
1863 for (int i = 1; i <= depth; ++i) {
1864 if (i > 1)
1865 candidate += "_";
1866 candidate += scopeHierarchy[i];
1867 }
1868 candidate += "_" + name;
1869 }
1870 auto it = varSlot.find(candidate);
1871 if (it != varSlot.end())
1872 return candidate;
1873 }
1874 return name;
1875 }
1876
1877 std::string findMangledArrayName(const std::string &name) const {
1878 if (scopeHierarchy.empty())
1879 return name;
1880 for (int depth = (int)scopeHierarchy.size() - 1; depth >= 1; --depth) {
1881 std::string candidate;
1882 for (int i = 1; i <= depth; ++i) {
1883 if (i > 1)
1884 candidate += "_";
1885 candidate += scopeHierarchy[i];
1886 }
1887 candidate += "_" + name;
1888 if (varSlot.count(candidate))
1889 return candidate;
1890 }
1891 return name;
1892 }
1893
1894 bool isIntegerLiteral(const std::string &s) const {
1895 if (s.empty())
1896 return false;
1897 size_t i = (s[0] == '+' || s[0] == '-') ? 1 : 0;
1898 if (i >= s.size())
1899 return false;
1900 for (; i < s.size(); ++i)
1901 if (!std::isdigit(static_cast<unsigned char>(s[i])))
1902 return false;
1903 return true;
1904 }
1905
1906 bool isFloatLiteral(const std::string &s) const {
1907 if (s.find_first_of(".eE") == std::string::npos)
1908 return false;
1909 char *end = nullptr;
1910 std::strtod(s.c_str(), &end);
1911 return end && *end == '\0';
1912 }
1913
1914 bool tryGetConstNumeric(const std::string &name, std::string &out) {
1915 auto itM = compileTimeConstants.find(findMangledName(name));
1916 if (itM != compileTimeConstants.end() && (isIntegerLiteral(itM->second) || isFloatLiteral(itM->second))) {
1917 out = itM->second;
1918 return true;
1919 }
1920 auto it = compileTimeConstants.find(name);
1921 if (it != compileTimeConstants.end() && (isIntegerLiteral(it->second) || isFloatLiteral(it->second))) {
1922 out = it->second;
1923 return true;
1924 }
1925 return false;
1926 }
1927
1928 std::string foldNumeric(ASTNode *n) {
1929 if (!n)
1930 return "";
1931 if (auto num = dynamic_cast<NumberNode *>(n))
1932 return num->value;
1933 if (auto var = dynamic_cast<VariableNode *>(n)) {
1934 std::string v;
1935 if (tryGetConstNumeric(var->name, v))
1936 return v;
1937 return "";
1938 }
1939 if (auto bin = dynamic_cast<BinaryOpNode *>(n)) {
1940 std::string L = foldNumeric(bin->left.get());
1941 std::string R = foldNumeric(bin->right.get());
1942 if (L.empty() || R.empty())
1943 return "";
1944 auto toD = [&](const std::string &s) -> double { return std::stod(s); };
1945 auto toI = [&](const std::string &s) -> long long {
1946 return isFloatLiteral(s) ? static_cast<long long>(std::stod(s)) : std::stoll(s);
1947 };
1948 switch (bin->operator_) {
1949 case BinaryOpNode::PLUS:
1950 return (isFloatLiteral(L) || isFloatLiteral(R)) ? std::to_string(toD(L) + toD(R)) : std::to_string(toI(L) + toI(R));
1952 return (isFloatLiteral(L) || isFloatLiteral(R)) ? std::to_string(toD(L) - toD(R)) : std::to_string(toI(L) - toI(R));
1954 return (isFloatLiteral(L) || isFloatLiteral(R)) ? std::to_string(toD(L) * toD(R)) : std::to_string(toI(L) * toI(R));
1955 case BinaryOpNode::DIVIDE: {
1956 double rv = toD(R);
1957 if (rv == 0.0)
1958 throw std::runtime_error("division by zero");
1959 return std::to_string(toD(L) / rv);
1960 }
1961 case BinaryOpNode::DIV: {
1962 long long ri = toI(R);
1963 if (ri == 0)
1964 throw std::runtime_error("division by zero");
1965 return std::to_string(toI(L) / ri);
1966 }
1967 case BinaryOpNode::MOD: {
1968 long long ri = toI(R);
1969 if (ri == 0)
1970 throw std::runtime_error("mod by zero");
1971 return std::to_string(toI(L) % ri);
1972 }
1973 default:
1974 return "";
1975 }
1976 }
1977 return "";
1978 }
1979
1982 std::string name;
1983 std::string typeName;
1985 int size;
1986 bool isArray = false;
1988
1989 RecordField() = default;
1990 RecordField(const RecordField &other) = default;
1991 RecordField(RecordField &&other) noexcept = default;
1992 RecordField &operator=(const RecordField &other) = default;
1993 RecordField &operator=(RecordField &&other) noexcept = default;
1994 };
1995
1997 int size = 0;
1998 std::vector<RecordField> fields;
1999 std::unordered_map<std::string, int> nameToIndex;
2000 };
2001 std::unordered_map<std::string, RecordTypeInfo> recordTypes;
2002 std::unordered_map<std::string, std::string> varRecordType;
2003 std::unordered_map<std::string, std::string> pointerBaseType;
2004
2005 std::pair<int, int> getRecordFieldOffsetAndSize(const std::string &recType, const std::string &field) {
2006 std::string normalizedRecType = resolveTypeName(lc(recType));
2007 std::string normalizedField = lc(field);
2008
2009 auto it = recordTypes.find(normalizedRecType);
2010 if (it == recordTypes.end())
2011 return {0, 8};
2012
2013 auto jt = it->second.nameToIndex.find(normalizedField);
2014 if (jt == it->second.nameToIndex.end())
2015 return {0, 8};
2016
2017 const auto &f = it->second.fields[jt->second];
2018 return {f.offset, f.size};
2019 }
2020
2022 auto lc = [](std::string s) {
2023 std::transform(s.begin(), s.end(), s.begin(),
2024 [](unsigned char c) { return std::tolower(c); });
2025 return s;
2026 };
2027 auto resolveAlias = [&](std::string t) {
2028 t = lc(t);
2029 std::unordered_set<std::string> seen;
2030 while (typeAliases.count(t) && !seen.count(t)) {
2031 seen.insert(t);
2032 t = lc(typeAliases.at(t));
2033 }
2034 return t;
2035 };
2036 if (auto *v = dynamic_cast<VariableNode *>(expr)) {
2037 if (auto it = currentParamTypes.find(v->name); it != currentParamTypes.end()) {
2038 auto resolved = resolveAlias(it->second);
2039 if (recordTypes.count(resolved))
2040 return resolved;
2041 }
2042 std::string mangled = findMangledName(v->name);
2043 if (auto it2 = varRecordType.find(mangled); it2 != varRecordType.end()) {
2044 auto resolved = resolveAlias(it2->second);
2045 if (recordTypes.count(resolved))
2046 return resolved;
2047 }
2048 if (auto it3 = varRecordType.find(v->name); it3 != varRecordType.end()) {
2049 auto resolved = resolveAlias(it3->second);
2050 if (recordTypes.count(resolved))
2051 return resolved;
2052 }
2053 return "";
2054 }
2055 if (auto *arrAccess = dynamic_cast<ArrayAccessNode *>(expr)) {
2056 ArrayInfo *info = getArrayInfoForArrayAccess(arrAccess);
2057 if (info) {
2058 auto elem = resolveAlias(info->elementType);
2059 if (recordTypes.count(elem))
2060 return elem;
2061 }
2062 return "";
2063 }
2064
2065 if (auto *fa = dynamic_cast<FieldAccessNode *>(expr)) {
2066 std::string owner = getVarRecordTypeNameFromExpr(fa->recordExpr.get());
2067 owner = resolveTypeName(lc(owner));
2068 if (owner.empty())
2069 return "";
2070
2071 auto rit = recordTypes.find(owner);
2072 if (rit == recordTypes.end())
2073 return "";
2074 auto idxIt = rit->second.nameToIndex.find(lc(fa->fieldName));
2075 if (idxIt == rit->second.nameToIndex.end())
2076 return "";
2077
2078 const auto &f = rit->second.fields[idxIt->second];
2079 std::string fieldType = resolveTypeName(lc(f.typeName));
2080 if (recordTypes.count(fieldType)) {
2081 return fieldType;
2082 }
2083 return "";
2084 }
2085
2086 return "";
2087 }
2088
2089 std::string getVarRecordTypeName(const std::string &varName) {
2090
2091 if (auto it = currentParamTypes.find(varName); it != currentParamTypes.end()) {
2092 std::string resolved = resolveTypeName(it->second);
2093 return resolved;
2094 }
2095
2096 std::string mangled = findMangledName(varName);
2097 if (auto it2 = varRecordType.find(mangled); it2 != varRecordType.end()) {
2098 std::string resolved = resolveTypeName(it2->second);
2099 return resolved;
2100 }
2101
2102 if (auto it3 = varRecordType.find(varName); it3 != varRecordType.end()) {
2103 std::string resolved = resolveTypeName(it3->second);
2104 return resolved;
2105 }
2106
2107 return "";
2108 }
2109
2110 bool isArrayTypeName(const std::string &t) const {
2111 auto base = resolveTypeName(lc(t));
2112 return arrayInfo.find(base) != arrayInfo.end();
2113 }
2114
2115 std::string getPointerBaseTypeName(const std::string &varName) const {
2116 std::string mangled = findMangledName(varName);
2117 auto it = pointerBaseType.find(mangled);
2118 if (it != pointerBaseType.end())
2119 return it->second;
2120 it = pointerBaseType.find(varName);
2121 if (it != pointerBaseType.end())
2122 return it->second;
2123 return "";
2124 }
2125
2126 int getPointerElementSize(const std::string &varName) {
2127 std::string base = getPointerBaseTypeName(varName);
2128 if (base.empty())
2129 return 8;
2130 base = resolveTypeName(lc(base));
2131 return getTypeSizeByName(base);
2132 }
2133
2134 VarType getPointerDerefType(const std::string &varName) {
2135 std::string base = getPointerBaseTypeName(varName);
2136 if (base.empty())
2137 return VarType::INT;
2138 return getTypeFromString(base);
2139 }
2140
2141 private:
2142 std::vector<std::string> loopEndLabels;
2143 std::vector<std::string> loopContinueLabels;
2144
2146 std::vector<std::unordered_map<std::string, std::string>> withFieldScopes;
2148 std::unordered_map<std::string, std::string> gotoLabels;
2149
2150 std::string getCurrentEndLabel() const {
2151 if (currentFunctionName.empty())
2152 return "";
2153
2154 std::string mangledName = findMangledFuncName(currentFunctionName, false);
2155 if (declaredFuncs.count(mangledName)) {
2156 return "FUNC_END_" + mangledName;
2157 }
2158
2159 mangledName = findMangledFuncName(currentFunctionName, true);
2160 if (declaredProcs.count(mangledName)) {
2161 return "PROC_END_" + mangledName;
2162 }
2163
2164 return "";
2165 }
2166
2167 static inline bool isPtrLike(VarType t) {
2168 return t == VarType::PTR || t == VarType::RECORD || t == VarType::STRING;
2169 }
2170
2171 std::string ensurePtrBase(const std::string &v) {
2172 auto isNamedPtr = [&](const std::string &s) -> bool {
2173 if (isPtrReg(s))
2174 return true;
2175 if (s.rfind("_tmpptr", 0) == 0)
2176 return true;
2177 auto it = varSlot.find(s);
2178 if (it != varSlot.end()) {
2179 auto jt = slotToType.find(it->second);
2180 if (jt != slotToType.end()) {
2181 auto t = jt->second;
2182 return t == VarType::PTR || t == VarType::RECORD || t == VarType::STRING;
2183 }
2184 }
2185 return false;
2186 };
2187 if (isNamedPtr(v))
2188 return v;
2189 std::string p = allocTempPtr();
2190 emit2("mov", p, v);
2191 return p;
2192 }
2193
2194 std::string getArrayNameFromBase(ASTNode *base) {
2195 if (auto var = dynamic_cast<VariableNode *>(base)) {
2196 return var->name;
2197 }
2198 if (auto field = dynamic_cast<FieldAccessNode *>(base)) {
2199 return getArrayNameFromBase(field->recordExpr.get()) + "." + field->fieldName;
2200 }
2201 if (auto arr = dynamic_cast<ArrayAccessNode *>(base)) {
2202 return getArrayNameFromBase(arr->base.get());
2203 }
2204
2205 throw std::runtime_error("Array base is not a simple variable or field");
2206 }
2207
2209 if (auto var = dynamic_cast<VariableNode *>(arr->base.get())) {
2210 std::string mangled = findMangledName(var->name);
2211 auto it = arrayInfo.find(mangled);
2212 if (it != arrayInfo.end())
2213 return &it->second;
2214 it = arrayInfo.find(var->name);
2215 if (it != arrayInfo.end())
2216 return &it->second;
2217 return nullptr;
2218 }
2219 if (auto field = dynamic_cast<FieldAccessNode *>(arr->base.get())) {
2220 // Cross-unit variable reference: UnitName.varName
2221 if (auto *baseVar = dynamic_cast<VariableNode *>(field->recordExpr.get())) {
2222 if (isImportedUnit(baseVar->name)) {
2223 std::string qualifiedName = baseVar->name + "." + field->fieldName;
2224 auto it = arrayInfo.find(qualifiedName);
2225 if (it != arrayInfo.end())
2226 return &it->second;
2227 return nullptr;
2228 }
2229 }
2230 // Record field array access
2231 std::string recTypeName = getVarRecordTypeNameFromExpr(field->recordExpr.get());
2232 recTypeName = resolveTypeName(lc(recTypeName));
2233 auto recTypeInfoIt = recordTypes.find(recTypeName);
2234 if (recTypeInfoIt != recordTypes.end()) {
2235 auto &recInfo = recTypeInfoIt->second;
2236 auto fieldIndexIt = recInfo.nameToIndex.find(lc(field->fieldName));
2237 if (fieldIndexIt != recInfo.nameToIndex.end()) {
2238 auto &f = recInfo.fields[fieldIndexIt->second];
2239 if (f.isArray)
2240 return const_cast<ArrayInfo *>(&f.arrayInfo);
2241 }
2242 }
2243 }
2244 if (auto inner = dynamic_cast<ArrayAccessNode *>(arr->base.get())) {
2245 ArrayInfo *baseInfo = getArrayInfoForArrayAccess(inner);
2246 if (baseInfo && baseInfo->elementIsArray) {
2247 return baseInfo->elementArray.get();
2248 }
2249 }
2250 return nullptr;
2251 }
2252 };
2253
2254 std::string mxvmOpt(const std::string &text);
2255
2256} // namespace pascal
2257#endif
Abstract base class for all AST nodes.
Definition ast.hpp:26
virtual void accept(ASTVisitor &visitor)=0
Accept an ASTVisitor (double-dispatch).
Abstract visitor interface for the AST (double-dispatch).
Definition ast.hpp:741
AST node for an address-of expression (@operand).
Definition ast.hpp:600
AST node for an array element access (arr[index]).
Definition ast.hpp:321
std::unique_ptr< ASTNode > base
array expression
Definition ast.hpp:323
AST node for assigning to an array element (arr[index] := value).
Definition ast.hpp:334
AST node for a named array variable declaration.
Definition ast.hpp:292
AST node for a named array type declaration (type Name = array[...]).
Definition ast.hpp:308
AST node for an array type (array[lower..upper] of elementType).
Definition ast.hpp:275
std::unique_ptr< ASTNode > lowerBound
lower index bound expression
Definition ast.hpp:278
std::unique_ptr< ASTNode > upperBound
upper index bound expression
Definition ast.hpp:279
std::unique_ptr< ASTNode > elementType
element type node
Definition ast.hpp:277
AST node for an assignment statement (variable := expression).
Definition ast.hpp:249
AST node for a binary operator expression.
Definition ast.hpp:438
AST node representing a block (declarations + compound statement).
Definition ast.hpp:79
AST node for a boolean literal (true / false).
Definition ast.hpp:549
AST node for a break statement.
Definition ast.hpp:65
Abstract handler for a group of built-in functions/procedures.
Definition icode.hpp:92
virtual VarType getReturnType(const std::string &funcName) const
Return the VarType produced by funcName.
Definition icode.hpp:102
virtual void generate(CodeGenVisitor &visitor, const std::string &funcName, const std::vector< std::unique_ptr< ASTNode > > &arguments)=0
Emit code for a procedure-style call (no return value used).
virtual bool canHandle(const std::string &funcName) const =0
Return true if this handler implements funcName.
virtual bool generateWithResult(CodeGenVisitor &visitor, const std::string &funcName, const std::vector< std::unique_ptr< ASTNode > > &arguments)
Emit code for a function-style call (result pushed on eval stack); return true if handled.
Definition icode.hpp:100
virtual ~BuiltinFunctionHandler()=default
Registry of BuiltinFunctionHandler instances.
Definition icode.hpp:111
BuiltinFunctionHandler * findHandler(const std::string &funcName)
Find the handler for a given function name.
Definition icode.hpp:122
std::vector< std::unique_ptr< BuiltinFunctionHandler > > handlers
registered handlers
Definition icode.hpp:113
void registerHandler(std::unique_ptr< BuiltinFunctionHandler > handler)
Register a new handler.
Definition icode.hpp:117
AST node for a case statement.
Definition ast.hpp:400
AST visitor that generates MXVM intermediate code.
Definition icode.hpp:178
std::unordered_map< std::string, std::vector< std::string > > tempPtrByScope
Definition icode.hpp:348
void freeReg(const std::string &reg)
Definition icode.hpp:439
std::unordered_map< std::string, std::vector< std::string > > enumTypes
Enum type name → ordered list of value names.
Definition icode.hpp:1494
std::unordered_map< std::string, std::string > realConstants
Definition icode.hpp:1797
ArrayInfo buildArrayInfoFromNode(ArrayTypeNode *atn)
Definition icode.hpp:817
std::string newLabel(const std::string &prefix="L")
Definition icode.hpp:473
bool isPtrReg(const std::string &name) const
Definition icode.hpp:456
std::unordered_set< std::string > setVars
Set of variable names that are set types (allocated as 256-byte bitsets).
Definition icode.hpp:1496
std::string findMangledFuncName(const std::string &name, bool isProc) const
Definition icode.hpp:1843
std::unordered_set< std::string > allocatedPtrs
Definition icode.hpp:1794
void visit(ProgramNode &node) override
void emitDynArrayBoundsCheck(const std::string &idxReg, const std::string &lenSym)
Emit bounds-check code for a dynamic array access.
Definition icode.hpp:1596
void setSlotType(int slot, VarType t)
Definition icode.hpp:306
int getPointerElementSize(const std::string &varName)
Definition icode.hpp:2126
std::vector< std::pair< FuncDeclNode *, std::vector< std::string > > > deferredFuncs
Definition icode.hpp:331
void initializeBuiltins()
Register all built-in function handlers (IO, Std, SDL, String).
Definition icode.hpp:969
std::unordered_map< std::string, std::string > varRecordType
Definition icode.hpp:2002
void pushValue(const std::string &v)
Definition icode.hpp:621
bool isIntegerLiteral(const std::string &s) const
Definition icode.hpp:1894
static bool isPtrLike(VarType t)
Definition icode.hpp:2167
bool isUnit
true when generating code for a unit (object output)
Definition icode.hpp:186
friend class StdFunctionHandler
Definition icode.hpp:884
BuiltinFunctionRegistry builtinRegistry
registry of built-in function handlers
Definition icode.hpp:889
std::unordered_map< std::string, VarType > importedVarTypes
var types for imported unit vars
Definition icode.hpp:190
bool isParmReg(const std::string &name) const
Definition icode.hpp:775
std::vector< std::string > objectDeps
unit/object dependencies for section object
Definition icode.hpp:185
friend class FileFunctionHandler
Definition icode.hpp:887
std::string allocPtrReg()
Definition icode.hpp:401
std::unordered_map< std::string, std::vector< std::string > > recordsToFreeInScope
Definition icode.hpp:347
std::string internString(const std::string &val)
Definition icode.hpp:578
bool isRealNumber(const std::string &s) const
Definition icode.hpp:210
void addTempPtr(const std::string &v)
Definition icode.hpp:353
void pushLogicalOr(const std::string &a, const std::string &b)
Definition icode.hpp:755
std::vector< std::string > scopeHierarchy
Definition icode.hpp:193
std::unordered_map< std::string, std::string > fileVarNames
Maps file variable name to companion filename variable name.
Definition icode.hpp:1500
void registerImportedVar(const std::string &unitName, VarDeclNode &node)
Register imported variable metadata from a dependency unit (no alloc emitted).
Definition icode.hpp:940
void emitLabel(const std::string &label)
Definition icode.hpp:528
std::unordered_map< std::string, std::string > typeAliases
Definition icode.hpp:200
friend class IOFunctionHandler
Definition icode.hpp:882
std::string allocFloatReg()
Definition icode.hpp:412
std::vector< std::string > evalStack
Definition icode.hpp:343
int getRecordTypeSize(const std::string &typeName)
Definition icode.hpp:1835
void updateDataSectionInitialValue(const std::string &varName, const std::string &type, const std::string &value)
Definition icode.hpp:1791
const std::vector< std::string > ptrRegisters
Definition icode.hpp:317
std::unordered_map< std::string, ArrayInfo > arrayInfo
Definition icode.hpp:1799
std::string funcLabel(const char *prefix, const std::string &name)
Definition icode.hpp:274
std::unordered_map< std::string, ValueLocation > valueLocations
Definition icode.hpp:366
bool isIntLiteral(const std::string &s) const
Definition icode.hpp:1305
bool isTempVar(const std::string &name) const
Definition icode.hpp:370
std::string allocReg()
Definition icode.hpp:389
bool tryGetConstNumeric(const std::string &name, std::string &out)
Definition icode.hpp:1914
VarType getPointerDerefType(const std::string &varName)
Definition icode.hpp:2134
void freeTempPtr(const std::string &ptrName)
Definition icode.hpp:477
std::string ensurePtrBase(const std::string &v)
Definition icode.hpp:2171
static std::string lc(std::string s)
Definition icode.hpp:1813
bool isRealConstSymbol(const std::string &s) const
Definition icode.hpp:288
ArrayInfo * getArrayInfoForArrayAccess(ArrayAccessNode *arr)
Definition icode.hpp:2208
std::unordered_map< std::string, VarType > varTypes
Definition icode.hpp:270
std::unordered_map< std::string, std::string > externalFuncs
maps function/proc name to source unit name
Definition icode.hpp:187
std::string getCurrentScopeName() const
Definition icode.hpp:245
void expandFloatRegisters()
Definition icode.hpp:425
std::string getPointerBaseTypeName(const std::string &varName) const
Definition icode.hpp:2115
void emit(const std::string &s)
Definition icode.hpp:525
void registerImportedConst(const std::string &unitName, ConstDeclNode &node)
Register imported constant values from a dependency unit.
Definition icode.hpp:914
std::unordered_map< std::string, ArrayInfo > importedArrayInfo
array metadata for imported unit vars
Definition icode.hpp:189
void emit2(const std::string &op, const std::string &a, const std::string &b)
Definition icode.hpp:530
std::string allocTempPtr(const std::string &forScope="")
Definition icode.hpp:502
void emit1(const std::string &op, const std::string &a)
Definition icode.hpp:529
void pushCmpResult(const std::string &a, const std::string &b, const char *jop)
Definition icode.hpp:681
std::unordered_map< std::string, RecordTypeInfo > recordTypes
Definition icode.hpp:2001
bool isFloatLiteral(const std::string &s) const
Definition icode.hpp:1906
bool isFloatReg(const std::string &name) const
Definition icode.hpp:455
VarType getExpressionType(ASTNode *node)
Definition icode.hpp:1614
std::string mangleVariableName(const std::string &varName) const
Definition icode.hpp:241
std::vector< std::string > floatRegisters
Definition icode.hpp:319
void writeTo(std::ostream &out) const
Write the complete MXVM program to a stream.
Definition icode.hpp:1324
void pushTri(const char *op, const std::string &a, const std::string &b)
Definition icode.hpp:649
std::string getVarRecordTypeNameFromExpr(ASTNode *expr)
Definition icode.hpp:2021
std::string foldNumeric(ASTNode *n)
Definition icode.hpp:1928
std::vector< std::unordered_map< std::string, std::string > > withFieldScopes
Stack of active with scopes: each entry maps unqualified field name -> record variable name.
Definition icode.hpp:2146
std::string getTypeString(ASTNode *typeNode)
Definition icode.hpp:1802
static bool endsWithColon(const std::string &s)
Definition icode.hpp:368
std::unordered_map< std::string, FuncInfo > funcSignatures
Definition icode.hpp:269
void freeFloatReg(const std::string &reg)
Definition icode.hpp:448
VarType getVarType(const std::string &name) const
Definition icode.hpp:276
std::vector< Scope > scopeStack
Definition icode.hpp:199
std::map< std::string, size_t > bufferStringVars
Definition icode.hpp:202
std::unordered_map< std::string, int > varSlot
Definition icode.hpp:340
std::vector< bool > ptrRegInUse
Definition icode.hpp:329
std::unordered_map< std::string, bool > declaredProcs
Definition icode.hpp:349
void pushLogicalAnd(const std::string &a, const std::string &b)
Definition icode.hpp:735
friend class SDLFunctionHandler
Definition icode.hpp:885
std::string currentFunctionName
Definition icode.hpp:332
std::string generateRealConstantName()
Definition icode.hpp:208
bool isTempPtr(const std::string &name) const
Definition icode.hpp:498
std::string findMangledArrayName(const std::string &name) const
Definition icode.hpp:1877
std::unordered_set< std::string > fileVars
Set of variable names that are file types (pointer to FILE).
Definition icode.hpp:1498
std::unordered_map< std::string, std::string > compileTimeConstants
Definition icode.hpp:180
std::map< std::string, std::pair< std::string, std::string > > constInitialValues
Definition icode.hpp:201
std::vector< bool > tempPtrInUse
Definition icode.hpp:206
friend class MathFunctionHandler
Definition icode.hpp:883
void emit3(const std::string &op, const std::string &a, const std::string &b, const std::string &c)
Definition icode.hpp:537
bool isRegisterSlot(int slot) const
Definition icode.hpp:369
int getArrayElementSize(const std::string &tIn)
Definition icode.hpp:865
void setVarType(const std::string &name, VarType t)
Definition icode.hpp:305
void releaseTempPtr(const std::string &ptrName)
Definition icode.hpp:489
bool isRecordTypeName(const std::string &t)
Definition icode.hpp:1517
std::vector< std::string > loopEndLabels
Definition icode.hpp:2142
void emitFree(const std::string &s)
Emit a free instruction for an allocated pointer.
Definition icode.hpp:980
std::vector< std::string > instructions
Definition icode.hpp:342
std::vector< bool > regInUse
Definition icode.hpp:328
void emit4(const std::string &op, const std::string &a, const std::string &b, const std::string &c, const std::string &d)
Definition icode.hpp:543
std::vector< std::string > currentFuncLocalSlots
Definition icode.hpp:334
virtual ~CodeGenVisitor()=default
std::string findMangledName(const std::string &name) const
Definition icode.hpp:1855
void emit_invoke(const std::string &funcName, const std::vector< std::string > &params)
Emit an invoke instruction calling a named function with parameters.
Definition icode.hpp:1287
std::unordered_map< int, std::string > slotToName
Definition icode.hpp:346
std::unordered_map< std::string, std::string > currentParamTypes
Definition icode.hpp:183
bool isStringVar(const std::string &name) const
Definition icode.hpp:307
std::unordered_map< std::string, std::vector< std::string > > functionScopedArrays
Definition icode.hpp:192
std::unordered_map< std::string, bool > declaredFuncs
Definition icode.hpp:350
std::string newTemp()
Definition icode.hpp:472
std::map< std::string, std::string > currentParamLocations
Definition icode.hpp:203
CodeGenVisitor()
Construct and initialise registers and float register pool.
std::pair< int, int > getRecordFieldOffsetAndSize(const std::string &recType, const std::string &field)
Definition icode.hpp:2005
std::string evaluateConstantExpression(ASTNode *node)
Definition icode.hpp:1696
void generate(ASTNode *root)
Generate MXVM code for the entire AST.
Definition icode.hpp:990
std::unordered_set< std::string > usedRealConstants
Definition icode.hpp:181
std::string mangleWithScope(const std::string &baseName, const std::vector< std::string > &scopePath) const
Definition icode.hpp:227
void pushFloatCmpResult(const std::string &aIn, const std::string &bIn, const char *jop)
Definition icode.hpp:704
std::vector< std::string > globalArrays
Definition icode.hpp:191
void allocateRecordFieldArrays(const std::string &recordVarName, const std::string &recordTypeName)
Definition icode.hpp:785
std::string emptyString()
Definition icode.hpp:311
std::vector< std::pair< std::string, std::string > > stringLiterals
Definition icode.hpp:344
std::unordered_set< std::string > usedStrings
Definition icode.hpp:345
std::string name
program name emitted in the output header
Definition icode.hpp:977
std::string escapeStringForMxvm(const std::string &raw) const
Definition icode.hpp:545
void freePtrReg(const std::string &reg)
Definition icode.hpp:431
std::string currentFunctionReturnSlot
slot variable that holds the return value until FUNC_END
Definition icode.hpp:333
std::string getTypeString(const VarDeclNode &node)
Definition icode.hpp:1502
std::string resolveTypeName(std::string t) const
Definition icode.hpp:1819
std::string eval(ASTNode *n)
Definition icode.hpp:634
std::unordered_map< std::string, int > dynArrayLenSlot
mangled array name → slot of companion length variable
Definition icode.hpp:1800
void registerExternalFunc(const std::string &funcName, const std::string &unitName)
Register a function or procedure as originating from an external unit.
Definition icode.hpp:903
int getTypeSizeByName(const std::string &t)
Definition icode.hpp:1522
std::vector< std::string > loopContinueLabels
Definition icode.hpp:2143
std::vector< bool > floatRegInUse
Definition icode.hpp:320
bool isReg(const std::string &name) const
Definition icode.hpp:457
int newSlotFor(const std::string &name)
Definition icode.hpp:461
std::string getCurrentEndLabel() const
Definition icode.hpp:2150
void markAllocatedPtr(const std::string &p)
Mark a pointer name as allocated (will be freed at scope end).
Definition icode.hpp:1280
std::unordered_map< std::string, int > enumConstants
Enum value → ordinal mapping (lowercased name → integer).
Definition icode.hpp:1492
std::string storageSymbolFor(const std::string &mangled)
Definition icode.hpp:1511
std::string popValue()
Definition icode.hpp:626
std::vector< std::string > registers
Definition icode.hpp:316
std::string slotVar(int slot) const
Definition icode.hpp:372
std::unordered_set< std::string > escapedTempPtrs
Definition icode.hpp:1795
std::string coerceToIntImmediate(const std::string &v)
Definition icode.hpp:290
void emitArrayBoundsCheck(const std::string &idxReg, int lower, int upper)
Emit bounds-check code for a static array access.
Definition icode.hpp:1572
std::unordered_map< int, VarType > slotToType
Definition icode.hpp:271
void initializeFloatRegisters()
Definition icode.hpp:323
bool isImportedUnit(const std::string &name) const
Check whether a name refers to an imported unit.
Definition icode.hpp:909
std::unordered_map< std::string, std::string > gotoLabels
Maps user-declared goto label numbers to generated MXVM labels.
Definition icode.hpp:2148
std::string getArrayNameFromBase(ASTNode *base)
Definition icode.hpp:2194
VarType getTypeFromString(const std::string &typeStr)
Definition icode.hpp:1538
friend class StringFunctionHandler
Definition icode.hpp:886
std::string ensureFloatConstSymbol(const std::string &value)
Definition icode.hpp:564
std::string getVarRecordTypeName(const std::string &varName)
Definition icode.hpp:2089
std::vector< std::string > prolog
Definition icode.hpp:341
std::vector< std::pair< ProcDeclNode *, std::vector< std::string > > > deferredProcs
Definition icode.hpp:204
std::string convertCharLiteral(const std::string &c)
Definition icode.hpp:587
std::vector< std::string > allTempPtrs
Definition icode.hpp:205
bool isArrayTypeName(const std::string &t) const
Definition icode.hpp:2110
std::unordered_map< std::string, std::string > pointerBaseType
Definition icode.hpp:2003
std::unordered_set< std::string > importedUnitNames
names of units imported via uses clause
Definition icode.hpp:188
std::set< std::string > usedModules
Definition icode.hpp:184
void recordLocation(const std::string &var, ValueLocation loc)
Definition icode.hpp:647
AST node for a compound statement (begin ... end).
Definition ast.hpp:237
AST node for constant declarations (const id = value).
Definition ast.hpp:174
std::vector< std::unique_ptr< ConstAssignment > > assignments
list of constant assignments
Definition ast.hpp:185
AST node for a continue statement.
Definition ast.hpp:55
AST node for an empty statement (no-op).
Definition ast.hpp:560
AST node for an enumerated type declaration (type Color = (Red, Green, Blue)).
Definition ast.hpp:627
AST node for an exit / halt statement.
Definition ast.hpp:44
AST node for accessing a record field (record.field).
Definition ast.hpp:726
Built-in handler for Pascal file I/O procedures (assign, reset, rewrite, append, close,...
Definition icode.hpp:163
VarType getReturnType(const std::string &funcName) const override
Return the VarType produced by funcName.
bool canHandle(const std::string &funcName) const override
Return true if this handler implements funcName.
bool generateWithResult(CodeGenVisitor &visitor, const std::string &funcName, const std::vector< std::unique_ptr< ASTNode > > &arguments) override
Emit code for a function-style call (result pushed on eval stack); return true if handled.
void generate(CodeGenVisitor &visitor, const std::string &funcName, const std::vector< std::unique_ptr< ASTNode > > &arguments) override
Emit code for a procedure-style call (no return value used).
AST node for a for loop (for var := start to/downto end do stmt).
Definition ast.hpp:379
AST node for a function call expression.
Definition ast.hpp:500
AST node for a function declaration.
Definition ast.hpp:194
AST node for a goto statement.
Definition ast.hpp:681
Built-in handler for I/O procedures (write, writeln, read, readln).
Definition icode.hpp:131
void generate(CodeGenVisitor &visitor, const std::string &funcName, const std::vector< std::unique_ptr< ASTNode > > &arguments) override
Emit code for a procedure-style call (no return value used).
Definition icode_imp.cpp:56
bool canHandle(const std::string &funcName) const override
Return true if this handler implements funcName.
Definition icode_imp.cpp:50
bool generateWithResult(CodeGenVisitor &visitor, const std::string &funcName, const std::vector< std::unique_ptr< ASTNode > > &arguments) override
Emit code for a function-style call (result pushed on eval stack); return true if handled.
AST node for an if-then-else statement.
Definition ast.hpp:350
AST node for a label definition (label: statement).
Definition ast.hpp:692
AST node for the nil literal.
Definition ast.hpp:569
AST node for an integer or real numeric literal.
Definition ast.hpp:524
bool isReal
true if real (floating-point) literal
Definition ast.hpp:528
AST node for a formal parameter list entry.
Definition ast.hpp:223
AST node for a pointer dereference (ptr^).
Definition ast.hpp:589
AST node for a pointer type declaration (^BaseType).
Definition ast.hpp:578
AST node for a procedure call statement.
Definition ast.hpp:425
AST node for a procedure declaration.
Definition ast.hpp:209
AST node for a complete program (program name + block).
Definition ast.hpp:98
AST node for a named record declaration.
Definition ast.hpp:658
AST node for a record type (record ... end).
Definition ast.hpp:646
AST node for a repeat..until loop.
Definition ast.hpp:611
Built-in handler for SDL2 bindings.
Definition icode.hpp:155
bool canHandle(const std::string &funcName) const override
Return true if this handler implements funcName.
bool generateWithResult(CodeGenVisitor &visitor, const std::string &funcName, const std::vector< std::unique_ptr< ASTNode > > &arguments) override
Emit code for a function-style call (result pushed on eval stack); return true if handled.
void generate(CodeGenVisitor &visitor, const std::string &funcName, const std::vector< std::unique_ptr< ASTNode > > &arguments) override
Emit code for a procedure-style call (no return value used).
AST node for a set literal [val1, val2, ...].
Definition ast.hpp:705
AST node for a set type declaration (set of <base type>).
Definition ast.hpp:716
AST node for a simple (named) type reference.
Definition ast.hpp:264
Built-in handler for standard library functions (chr, ord, inc, dec, etc.).
Definition icode.hpp:139
bool generateWithResult(CodeGenVisitor &visitor, const std::string &funcName, const std::vector< std::unique_ptr< ASTNode > > &arguments) override
Emit code for a function-style call (result pushed on eval stack); return true if handled.
bool canHandle(const std::string &funcName) const override
Return true if this handler implements funcName.
void generate(CodeGenVisitor &visitor, const std::string &funcName, const std::vector< std::unique_ptr< ASTNode > > &arguments) override
Emit code for a procedure-style call (no return value used).
Built-in handler for string library functions (length, copy, concat, etc.).
Definition icode.hpp:147
bool generateWithResult(CodeGenVisitor &visitor, const std::string &funcName, const std::vector< std::unique_ptr< ASTNode > > &arguments) override
Emit code for a function-style call (result pushed on eval stack); return true if handled.
bool canHandle(const std::string &funcName) const override
Return true if this handler implements funcName.
VarType getReturnType(const std::string &funcName) const override
Return the VarType produced by funcName.
void generate(CodeGenVisitor &visitor, const std::string &funcName, const std::vector< std::unique_ptr< ASTNode > > &arguments) override
Emit code for a procedure-style call (no return value used).
AST node for a string literal.
Definition ast.hpp:538
AST node for a type alias (type NewName = ExistingType).
Definition ast.hpp:161
AST node for a type declaration section.
Definition ast.hpp:149
AST node for a unary operator expression.
Definition ast.hpp:477
@ MINUS
unary -
Definition ast.hpp:482
@ PLUS
unary +
Definition ast.hpp:481
AST node for a Pascal unit (separately compiled module).
Definition ast.hpp:116
AST node for variable declarations (var id1, id2 : type).
Definition ast.hpp:132
std::vector< std::string > identifiers
declared variable names
Definition ast.hpp:134
std::variant< std::string, std::unique_ptr< ASTNode > > type
type (simple name or complex type node)
Definition ast.hpp:135
AST node for a variable reference.
Definition ast.hpp:513
AST node for a while loop (while cond do stmt).
Definition ast.hpp:366
AST node for a with statement (with record do statement).
Definition ast.hpp:668
Definition ast.cpp:9
std::string mxvmOpt(const std::string &text)
Definition icode.cpp:589
VarType
Enumeration of variable / expression value types.
Definition icode.hpp:72
@ CHAR
character value
Definition icode.hpp:76
@ ARRAY_INT
array of integers
Definition icode.hpp:81
@ INT
integer
Definition icode.hpp:73
@ STRING
string value
Definition icode.hpp:75
@ UNKNOWN
unresolved type
Definition icode.hpp:80
@ ARRAY_STRING
array of strings
Definition icode.hpp:83
@ RECORD
record (struct)
Definition icode.hpp:77
@ ARRAY_DOUBLE
array of doubles
Definition icode.hpp:82
@ BOOL
boolean
Definition icode.hpp:79
@ PTR
generic pointer
Definition icode.hpp:78
@ DOUBLE
real (floating-point)
Definition icode.hpp:74
AST node hierarchy for the Pascal-to-MXVM frontend parser.
Metadata describing a Pascal array's bounds, element type, and sizing.
Definition icode.hpp:29
std::unique_ptr< ArrayInfo > elementArray
nested array descriptor (when elementIsArray)
Definition icode.hpp:37
ArrayInfo & operator=(ArrayInfo &&other) noexcept=default
ArrayInfo & operator=(const ArrayInfo &other)
Definition icode.hpp:49
ArrayInfo()=default
bool isDynamic
true for dynamic arrays declared as array of <type> (no compile-time bounds)
Definition icode.hpp:36
std::string elementType
element type name (e.g. "integer", "real")
Definition icode.hpp:30
bool elementIsArray
true if element type is itself an array
Definition icode.hpp:35
int lowerBound
declared lower index bound
Definition icode.hpp:31
int size
number of elements
Definition icode.hpp:33
int elementSize
size (bytes) of each element
Definition icode.hpp:34
int upperBound
declared upper index bound
Definition icode.hpp:32
ArrayInfo(const ArrayInfo &other)
Definition icode.hpp:40
ArrayInfo(ArrayInfo &&other) noexcept=default
Describes a user-declared function's parameter and return types.
Definition icode.hpp:264
VarType returnType
return type
Definition icode.hpp:266
std::vector< VarType > paramTypes
parameter types in order
Definition icode.hpp:265
RecordField & operator=(RecordField &&other) noexcept=default
bool isArray
true if the field is an array
Definition icode.hpp:1986
std::string name
field name
Definition icode.hpp:1982
RecordField(const RecordField &other)=default
ArrayInfo arrayInfo
array descriptor (when isArray)
Definition icode.hpp:1987
int size
field size in bytes
Definition icode.hpp:1985
RecordField & operator=(const RecordField &other)=default
RecordField(RecordField &&other) noexcept=default
std::string typeName
field type name
Definition icode.hpp:1983
int offset
byte offset within the record
Definition icode.hpp:1984
Complete description of a record type.
Definition icode.hpp:1996
std::unordered_map< std::string, int > nameToIndex
field name -> index into fields
Definition icode.hpp:1999
int size
total record size in bytes
Definition icode.hpp:1997
std::vector< RecordField > fields
ordered field list
Definition icode.hpp:1998
std::vector< ProcDeclNode * > nestedProcs
Definition icode.hpp:196
std::vector< FuncDeclNode * > nestedFuncs
Definition icode.hpp:197
Describes where a value currently resides (register, memory, or immediate).
Definition icode.hpp:358
enum pascal::CodeGenVisitor::ValueLocation::Type type
std::string location
register name, slot name, or literal value
Definition icode.hpp:364
@ MEMORY
value is in a memory slot
Definition icode.hpp:361
@ IMMEDIATE
value is a compile-time constant
Definition icode.hpp:362
@ REGISTER
value is in a named register
Definition icode.hpp:360