MXVM 1.8.1
Virtual Machine, Compiler, and Pascal Frontend
Loading...
Searching...
No Matches
icode_visit.cpp
Go to the documentation of this file.
1
6#include "icode.hpp"
7
8namespace pascal {
9
16
18 name = node.name;
19 // Separate native modules from unit dependencies
20 static const std::unordered_set<std::string> nativeModules = {"io", "std", "string", "sdl", "strlib"};
21 for (const auto &mod : node.uses) {
22 if (mod == "strlib")
23 usedModules.insert("string");
24 else if (nativeModules.count(mod))
25 usedModules.insert(mod);
26 else
27 objectDeps.push_back(mod);
28 }
29 if (node.block) {
30 node.block->accept(*this);
31 }
32 }
33
35 name = node.name;
36 isUnit = true;
37 // Process uses clause
38 static const std::unordered_set<std::string> nativeModules = {"io", "std", "string", "sdl", "strlib"};
39 for (const auto &mod : node.uses) {
40 if (mod == "strlib")
41 usedModules.insert("string");
42 else if (nativeModules.count(mod))
43 usedModules.insert(mod);
44 else
45 objectDeps.push_back(mod);
46 }
47
48 // Emit UNIT_INIT function label — allocs from interface vars go here
49 emitLabel("function PROC_UNIT_INIT");
50
51 // Register interface declarations' signatures WITHOUT deferring code
52 // (interface declarations have null blocks — they are forward declarations)
53 for (auto &decl : node.interfaceDecls) {
54 if (!decl)
55 continue;
56 if (auto *funcDecl = dynamic_cast<FuncDeclNode *>(decl.get())) {
57 // Just register the signature info; don't call accept()
58 FuncInfo funcInfo;
59 funcInfo.returnType = getTypeFromString(funcDecl->returnType);
60 for (auto &p : funcDecl->parameters) {
61 if (auto pn = dynamic_cast<ParameterNode *>(p.get())) {
62 for (size_t i = 0; i < pn->identifiers.size(); ++i) {
63 funcInfo.paramTypes.push_back(getTypeFromString(pn->type));
64 }
65 }
66 }
67 funcSignatures[funcDecl->name] = funcInfo;
68 setVarType(funcDecl->name, funcInfo.returnType);
69 } else if (dynamic_cast<ConstDeclNode *>(decl.get()) ||
70 dynamic_cast<VarDeclNode *>(decl.get()) ||
71 dynamic_cast<TypeDeclNode *>(decl.get())) {
72 // Process interface const/var/type so they are available
73 // to the unit's own implementation code
74 decl->accept(*this);
75 }
76 // ProcDeclNode forward declarations don't need signature registration
77 // (they have no return type to track)
78 }
79
80 // Close UNIT_INIT function
81 emit("ret");
82
83 // Process implementation declarations (actual code)
84 for (auto &decl : node.implDecls) {
85 if (decl)
86 decl->accept(*this);
87 }
88 }
89
91 for (auto &decl : node.declarations) {
92 if (!decl)
93 continue;
94 if (dynamic_cast<TypeDeclNode *>(decl.get())) {
95 decl->accept(*this);
96 }
97 }
98
99 for (auto &decl : node.declarations) {
100 if (!decl)
101 continue;
102 if (dynamic_cast<TypeDeclNode *>(decl.get()))
103 continue;
104
106 if (dynamic_cast<ProcDeclNode *>(decl.get()) ||
107 dynamic_cast<FuncDeclNode *>(decl.get())) {
108 continue;
109 }
110 }
111 decl->accept(*this);
112 }
113
114 if (node.compoundStatement) {
115 node.compoundStatement->accept(*this);
116 }
117 }
118
120 for (const auto &varName : node.identifiers) {
121 std::string mangledName = mangleVariableName(varName);
122 std::string typeName;
123
124 if (std::holds_alternative<std::string>(node.type)) {
125 typeName = std::get<std::string>(node.type);
126 } else if (std::holds_alternative<std::unique_ptr<ASTNode>>(node.type)) {
127 auto &typeNode = std::get<std::unique_ptr<ASTNode>>(node.type);
128
129 if (auto *arrayTypeNode = dynamic_cast<ArrayTypeNode *>(typeNode.get())) {
130 ArrayInfo info = buildArrayInfoFromNode(arrayTypeNode);
131 arrayInfo[mangledName] = std::move(info);
132
133 setVarType(varName, VarType::PTR);
134 int slot = newSlotFor(mangledName);
136 varSlot[varName] = slot;
137 varSlot[mangledName] = slot;
138
139 updateDataSectionInitialValue(slotVar(slot), "ptr", "null");
140
141 if (arrayInfo[mangledName].isDynamic) {
142 // Dynamic array: no alloc at declaration, starts as null
143 // Create companion length variable
144 std::string lenName = mangledName + "_dynlen";
145 int lenSlot = newSlotFor(lenName);
146 setSlotType(lenSlot, VarType::INT);
147 varSlot[lenName] = lenSlot;
148 updateDataSectionInitialValue(slotVar(lenSlot), "int", "0");
149 dynArrayLenSlot[mangledName] = lenSlot;
150
151 std::string currentScope = getCurrentScopeName();
152 if (currentScope.empty())
153 globalArrays.push_back(slotVar(slot));
154 else
155 functionScopedArrays[currentScope].push_back(slotVar(slot));
156 } else {
157 emit3("alloc",
158 slotVar(slot),
159 std::to_string(arrayInfo[mangledName].elementSize),
160 std::to_string(arrayInfo[mangledName].size));
161
162 std::string currentScope = getCurrentScopeName();
163 if (currentScope.empty())
164 globalArrays.push_back(slotVar(slot));
165 else
166 functionScopedArrays[currentScope].push_back(slotVar(slot));
167 }
168 continue;
169 }
170
171 if (auto *ptrTypeNode = dynamic_cast<PointerTypeNode *>(typeNode.get())) {
172 setVarType(varName, VarType::PTR);
173 int slot = newSlotFor(mangledName);
175 varSlot[varName] = slot;
176 varSlot[mangledName] = slot;
177 pointerBaseType[mangledName] = ptrTypeNode->baseTypeName;
178 pointerBaseType[varName] = ptrTypeNode->baseTypeName;
179 updateDataSectionInitialValue(slotVar(slot), "ptr", "null");
180 continue;
181 }
182
183 if (dynamic_cast<SetTypeNode *>(typeNode.get())) {
184 setVarType(varName, VarType::PTR);
185 int slot = newSlotFor(mangledName);
187 varSlot[varName] = slot;
188 varSlot[mangledName] = slot;
189 setVars.insert(mangledName);
190 setVars.insert(varName);
191 updateDataSectionInitialValue(slotVar(slot), "ptr", "null");
192 // Allocate 256 bytes (one byte per ordinal 0..255)
193 emit3("alloc", slotVar(slot), "8", "256");
194 // Zero-fill the set
195 std::string loopIdx = allocReg();
196 std::string loopStart = newLabel("SET_INIT");
197 std::string loopEnd = newLabel("SET_INIT_END");
198 emit2("mov", loopIdx, "0");
199 emitLabel(loopStart);
200 emit2("cmp", loopIdx, "256");
201 emit1("jge", loopEnd);
202 emit4("store", "0", slotVar(slot), loopIdx, "8");
203 emit2("add", loopIdx, "1");
204 emit1("jmp", loopStart);
205 emitLabel(loopEnd);
206 freeReg(loopIdx);
207
208 std::string currentScope = getCurrentScopeName();
209 if (currentScope.empty())
210 globalArrays.push_back(slotVar(slot));
211 else
212 functionScopedArrays[currentScope].push_back(slotVar(slot));
213 continue;
214 }
215
216 if (auto *simpleTypeNode = dynamic_cast<SimpleTypeNode *>(typeNode.get())) {
217 typeName = simpleTypeNode->typeName;
218 } else if (dynamic_cast<RecordTypeNode *>(typeNode.get())) {
219 typeName = "record";
220 }
221 } else {
222 typeName = "unknown";
223 }
224
225 int slot = newSlotFor(mangledName);
226 varSlot[varName] = slot;
227 varSlot[mangledName] = slot;
228
229 std::string normalizedTypeName = resolveTypeName(lc(typeName));
230
231 // Handle set type aliases (e.g., type MySet = set of integer; var s: MySet;)
232 if (normalizedTypeName.rfind("set of ", 0) == 0) {
233 setVarType(varName, VarType::PTR);
235 setVars.insert(mangledName);
236 setVars.insert(varName);
237 updateDataSectionInitialValue(slotVar(slot), "ptr", "null");
238 emit3("alloc", slotVar(slot), "8", "256");
239 // Zero-fill the set
240 std::string loopIdx = allocReg();
241 std::string loopStart = newLabel("SET_INIT");
242 std::string loopEnd = newLabel("SET_INIT_END");
243 emit2("mov", loopIdx, "0");
244 emitLabel(loopStart);
245 emit2("cmp", loopIdx, "256");
246 emit1("jge", loopEnd);
247 emit4("store", "0", slotVar(slot), loopIdx, "8");
248 emit2("add", loopIdx, "1");
249 emit1("jmp", loopStart);
250 emitLabel(loopEnd);
251 freeReg(loopIdx);
252 std::string currentScope = getCurrentScopeName();
253 if (currentScope.empty())
254 globalArrays.push_back(slotVar(slot));
255 else
256 functionScopedArrays[currentScope].push_back(slotVar(slot));
257 continue;
258 }
259
260 // Handle file/text type variables
261 if (normalizedTypeName == "file" || normalizedTypeName == "text") {
262 setVarType(varName, VarType::PTR);
264 fileVars.insert(mangledName);
265 fileVars.insert(varName);
266 updateDataSectionInitialValue(slotVar(slot), "ptr", "null");
267 // Create companion filename variable
268 std::string fileNameVar = mangledName + "_filename";
269 int fnSlot = newSlotFor(fileNameVar);
270 setSlotType(fnSlot, VarType::PTR);
271 setVarType(fileNameVar, VarType::PTR);
272 varSlot[fileNameVar] = fnSlot;
273 fileVarNames[mangledName] = fileNameVar;
274 fileVarNames[varName] = fileNameVar;
275 continue;
276 }
277
278 auto recordTypeIt = recordTypes.find(normalizedTypeName);
279 if (recordTypeIt != recordTypes.end()) {
280 varRecordType[mangledName] = normalizedTypeName;
281 varRecordType[varName] = normalizedTypeName;
282
283 int recordSize = recordTypeIt->second.size;
284
285 setVarType(varName, VarType::RECORD);
287
288 updateDataSectionInitialValue(slotVar(slot), "ptr", "null");
289 emit3("alloc", slotVar(slot), std::to_string(recordSize), "1");
290
291 allocateRecordFieldArrays(mangledName, normalizedTypeName);
292
293 std::string currentScope = getCurrentScopeName();
294 recordsToFreeInScope[currentScope].push_back(mangledName);
295 continue;
296 }
297
298 auto aliasIt = arrayInfo.find(normalizedTypeName);
299 if (aliasIt != arrayInfo.end()) {
300 arrayInfo[mangledName] = aliasIt->second;
301
302 setVarType(varName, VarType::PTR);
304
305 updateDataSectionInitialValue(slotVar(slot), "ptr", "null");
306 emit3("alloc",
307 slotVar(slot),
308 std::to_string(arrayInfo[mangledName].elementSize),
309 std::to_string(arrayInfo[mangledName].size));
310
311 std::string currentScope = getCurrentScopeName();
312 if (currentScope.empty())
313 globalArrays.push_back(slotVar(slot));
314 else
315 functionScopedArrays[currentScope].push_back(slotVar(slot));
316 continue;
317 }
318
319 if (!normalizedTypeName.empty() && normalizedTypeName[0] == '^') {
320 std::string baseType = normalizedTypeName.substr(1);
321 setVarType(varName, VarType::PTR);
323 pointerBaseType[mangledName] = baseType;
324 pointerBaseType[varName] = baseType;
325 updateDataSectionInitialValue(slotVar(slot), "ptr", "null");
326 continue;
327 }
328
329 VarType vType = getTypeFromString(typeName);
330 setVarType(varName, vType);
331 setSlotType(slot, vType);
332
333 if (!currentFunctionName.empty())
334 currentFuncLocalSlots.push_back(slotVar(slot));
335
336 if (vType == VarType::PTR || vType == VarType::RECORD) {
337 updateDataSectionInitialValue(slotVar(slot), "ptr", "null");
338 } else if (vType == VarType::DOUBLE) {
339 updateDataSectionInitialValue(slotVar(slot), "float", "0.0");
340 } else {
341 updateDataSectionInitialValue(slotVar(slot), "int", "0");
342 }
343 }
344 }
345
347 std::string procLower = node.name;
348 std::transform(procLower.begin(), procLower.end(), procLower.begin(),
349 [](unsigned char c) { return std::tolower(c); });
350
351 if (procLower == "new") {
352 if (node.arguments.size() != 1)
353 throw std::runtime_error("new() requires exactly one pointer argument");
354
355 if (auto *fieldNode = dynamic_cast<FieldAccessNode *>(node.arguments[0].get())) {
356 std::string baseName;
357 std::string recType;
358 bool baseIsDirectPointer = false;
359
360 if (auto *v = dynamic_cast<VariableNode *>(fieldNode->recordExpr.get())) {
361 baseName = findMangledName(v->name);
362 recType = getVarRecordTypeName(v->name);
363 } else {
364 fieldNode->recordExpr->accept(*this);
365 baseName = popValue();
366 baseIsDirectPointer = true;
367 recType = getVarRecordTypeNameFromExpr(fieldNode->recordExpr.get());
368 }
369
370 recType = resolveTypeName(lc(recType));
371 auto recTypeIt = recordTypes.find(recType);
372 if (recTypeIt == recordTypes.end())
373 throw std::runtime_error("new(): unknown record type: " + recType);
374
375 const std::string fieldName = lc(fieldNode->fieldName);
376 auto it = recTypeIt->second.nameToIndex.find(fieldName);
377 if (it == recTypeIt->second.nameToIndex.end())
378 throw std::runtime_error("new(): field not found: " + fieldName);
379
380 const auto &fieldInfo = recTypeIt->second.fields[it->second];
381 const int byteOffset = fieldInfo.offset;
382
383 std::string fieldTypeName = fieldInfo.typeName;
384 int elemSize = 8;
385 if (!fieldTypeName.empty() && fieldTypeName[0] == '^') {
386 std::string baseType = resolveTypeName(lc(fieldTypeName.substr(1)));
387 elemSize = getTypeSizeByName(baseType);
388 if (isRecordTypeName(baseType)) {
389 auto rit = recordTypes.find(baseType);
390 if (rit != recordTypes.end())
391 elemSize = rit->second.size;
392 }
393 }
394
395 std::string tmp = allocTempPtr();
396 emit3("alloc", tmp, std::to_string(elemSize), "1");
397
398 std::string basePtr = baseIsDirectPointer ? baseName : ensurePtrBase(baseName);
399 emit4("store", tmp, basePtr, std::to_string(byteOffset), "1");
400
401 if (isReg(tmp) && !isParmReg(tmp))
402 freeReg(tmp);
403 if (baseIsDirectPointer && isReg(basePtr) && !isParmReg(basePtr))
404 freeReg(basePtr);
405 return;
406 }
407
408 if (auto *arrNode = dynamic_cast<ArrayAccessNode *>(node.arguments[0].get())) {
409 ArrayInfo *info = getArrayInfoForArrayAccess(arrNode);
410 if (!info)
411 throw std::runtime_error("new(): unknown array: " + getArrayNameFromBase(arrNode->base.get()));
412
413 int elemSize = 8;
414 std::string et = info->elementType;
415 if (!et.empty() && et[0] == '^') {
416 std::string baseType = resolveTypeName(lc(et.substr(1)));
417 elemSize = getTypeSizeByName(baseType);
418 if (isRecordTypeName(baseType)) {
419 auto rit = recordTypes.find(baseType);
420 if (rit != recordTypes.end())
421 elemSize = rit->second.size;
422 }
423 }
424
425 std::string idx = eval(arrNode->index.get());
426 if (getExpressionType(arrNode->index.get()) == VarType::DOUBLE) {
427 std::string intIdx = allocReg();
428 emit2("mov", intIdx, idx);
429 if (isReg(idx) && !isParmReg(idx))
430 freeReg(idx);
431 idx = intIdx;
432 }
433
434 std::string elemIndex;
435 if (isReg(idx) && !isParmReg(idx)) {
436 elemIndex = idx;
437 } else {
438 elemIndex = allocReg();
439 emit2("mov", elemIndex, idx);
440 }
441 if (info->lowerBound != 0)
442 emit2("sub", elemIndex, std::to_string(info->lowerBound));
443
444 std::string base;
445 if (auto var = dynamic_cast<VariableNode *>(arrNode->base.get())) {
446 std::string mangled = findMangledArrayName(var->name);
447 base = ensurePtrBase(storageSymbolFor(mangled));
448 } else {
449 arrNode->base->accept(*this);
450 base = popValue();
451 }
452
453 std::string tmp = allocTempPtr();
454 emit3("alloc", tmp, std::to_string(elemSize), "1");
455 emit4("store", tmp, base, elemIndex, std::to_string(info->elementSize));
456
457 if (isReg(tmp) && !isParmReg(tmp))
458 freeReg(tmp);
459 if (elemIndex != idx && isReg(idx) && !isParmReg(idx))
460 freeReg(idx);
461 freeReg(elemIndex);
462 return;
463 }
464
465 auto *varNode = dynamic_cast<VariableNode *>(node.arguments[0].get());
466 if (!varNode)
467 throw std::runtime_error("new() argument must be a variable, record field, or array element");
468
469 std::string mangled = findMangledName(varNode->name);
470 std::string sym = storageSymbolFor(mangled);
471 int elemSize = getPointerElementSize(varNode->name);
472
473 std::string baseType = getPointerBaseTypeName(varNode->name);
474 baseType = resolveTypeName(lc(baseType));
475 int allocCount = 1;
476 if (isRecordTypeName(baseType)) {
477 auto it = recordTypes.find(baseType);
478 if (it != recordTypes.end()) {
479 elemSize = it->second.size;
480 }
481 }
482
483 emit3("alloc", sym, std::to_string(elemSize), std::to_string(allocCount));
484 return;
485 }
486
487 if (procLower == "dispose") {
488 if (node.arguments.size() != 1)
489 throw std::runtime_error("dispose() requires exactly one pointer argument");
490
491 if (auto *fieldNode = dynamic_cast<FieldAccessNode *>(node.arguments[0].get())) {
492 std::string baseName;
493 std::string recType;
494 bool baseIsDirectPointer = false;
495
496 if (auto *v = dynamic_cast<VariableNode *>(fieldNode->recordExpr.get())) {
497 baseName = findMangledName(v->name);
498 recType = getVarRecordTypeName(v->name);
499 } else {
500 fieldNode->recordExpr->accept(*this);
501 baseName = popValue();
502 baseIsDirectPointer = true;
503 recType = getVarRecordTypeNameFromExpr(fieldNode->recordExpr.get());
504 }
505
506 recType = resolveTypeName(lc(recType));
507 auto recTypeIt = recordTypes.find(recType);
508 if (recTypeIt == recordTypes.end())
509 throw std::runtime_error("dispose(): unknown record type: " + recType);
510
511 const std::string fieldName = lc(fieldNode->fieldName);
512 auto it = recTypeIt->second.nameToIndex.find(fieldName);
513 if (it == recTypeIt->second.nameToIndex.end())
514 throw std::runtime_error("dispose(): field not found: " + fieldName);
515
516 const auto &fieldInfo = recTypeIt->second.fields[it->second];
517 const int byteOffset = fieldInfo.offset;
518
519 std::string basePtr = baseIsDirectPointer ? baseName : ensurePtrBase(baseName);
520 std::string tmp = allocTempPtr();
521 emit4("load", tmp, basePtr, std::to_string(byteOffset), "1");
522 emit1("free", tmp);
523
524 if (isReg(tmp) && !isParmReg(tmp))
525 freeReg(tmp);
526 if (baseIsDirectPointer && isReg(basePtr) && !isParmReg(basePtr))
527 freeReg(basePtr);
528 return;
529 }
530
531 if (auto *arrNode = dynamic_cast<ArrayAccessNode *>(node.arguments[0].get())) {
532 ArrayInfo *info = getArrayInfoForArrayAccess(arrNode);
533 if (!info)
534 throw std::runtime_error("dispose(): unknown array: " + getArrayNameFromBase(arrNode->base.get()));
535
536 std::string idx = eval(arrNode->index.get());
537 if (getExpressionType(arrNode->index.get()) == VarType::DOUBLE) {
538 std::string intIdx = allocReg();
539 emit2("mov", intIdx, idx);
540 if (isReg(idx) && !isParmReg(idx))
541 freeReg(idx);
542 idx = intIdx;
543 }
544
545 std::string elemIndex;
546 if (isReg(idx) && !isParmReg(idx)) {
547 elemIndex = idx;
548 } else {
549 elemIndex = allocReg();
550 emit2("mov", elemIndex, idx);
551 }
552 if (info->lowerBound != 0)
553 emit2("sub", elemIndex, std::to_string(info->lowerBound));
554
555 std::string base;
556 if (auto var = dynamic_cast<VariableNode *>(arrNode->base.get())) {
557 std::string mangled = findMangledArrayName(var->name);
558 base = ensurePtrBase(storageSymbolFor(mangled));
559 } else {
560 arrNode->base->accept(*this);
561 base = popValue();
562 }
563
564 std::string tmp = allocTempPtr();
565 emit4("load", tmp, base, elemIndex, std::to_string(info->elementSize));
566 emit1("free", tmp);
567
568 if (isReg(tmp) && !isParmReg(tmp))
569 freeReg(tmp);
570 if (elemIndex != idx && isReg(idx) && !isParmReg(idx))
571 freeReg(idx);
572 freeReg(elemIndex);
573 return;
574 }
575
576 auto *varNode = dynamic_cast<VariableNode *>(node.arguments[0].get());
577 if (!varNode)
578 throw std::runtime_error("dispose() argument must be a variable, record field, or array element");
579
580 std::string mangled = findMangledName(varNode->name);
581 std::string sym = storageSymbolFor(mangled);
582 emit1("free", sym);
583 return;
584 }
585
586 // SetLength(arr, newSize) — resize a dynamic array
587 if (procLower == "setlength" && node.arguments.size() == 2) {
588 auto *varNode = dynamic_cast<VariableNode *>(node.arguments[0].get());
589 if (!varNode)
590 throw std::runtime_error("SetLength: first argument must be a dynamic array variable");
591
592 std::string mangled = findMangledArrayName(varNode->name);
593 auto it = arrayInfo.find(mangled);
594 if (it == arrayInfo.end())
595 it = arrayInfo.find(varNode->name);
596 if (it == arrayInfo.end() || !it->second.isDynamic)
597 throw std::runtime_error("SetLength: " + varNode->name + " is not a dynamic array");
598
599 std::string arrMangled = it->first;
600 std::string sym = storageSymbolFor(arrMangled);
601
602 // Evaluate the new size
603 std::string newSize = eval(node.arguments[1].get());
604
605 // Emit realloc: realloc arrPtr, elementSize, newCount
606 emit3("realloc", sym, std::to_string(it->second.elementSize), newSize);
607
608 // Update companion length variable
609 auto lenIt = dynArrayLenSlot.find(arrMangled);
610 if (lenIt != dynArrayLenSlot.end()) {
611 emit2("mov", slotVar(lenIt->second), newSize);
612 }
613
614 if (isReg(newSize) && !isParmReg(newSize))
615 freeReg(newSize);
616 return;
617 }
618
619 auto handler = builtinRegistry.findHandler(procLower);
620 if (handler) {
621 handler->generate(*this, procLower, node.arguments);
622 return;
623 }
624
625 std::vector<std::string> evaluated_args;
626 std::vector<VarType> argTypes;
627
628 for (auto &arg : node.arguments) {
629 std::string argVal = eval(arg.get());
630 evaluated_args.push_back(argVal);
631 argTypes.push_back(getExpressionType(arg.get()));
632 }
633
634 // Build target register list
635 std::vector<std::string> targetRegs(evaluated_args.size());
636 size_t intRegIdx = 1;
637 size_t ptrRegIdx = 0;
638 size_t floatRegIdx = 0;
639
640 for (size_t i = 0; i < evaluated_args.size(); ++i) {
641 if (argTypes[i] == VarType::STRING || argTypes[i] == VarType::PTR || argTypes[i] == VarType::RECORD) {
642 if (ptrRegIdx < ptrRegisters.size())
643 targetRegs[i] = ptrRegisters[ptrRegIdx++];
644 } else if (argTypes[i] == VarType::DOUBLE) {
645 if (floatRegIdx < floatRegisters.size())
646 targetRegs[i] = floatRegisters[floatRegIdx++];
647 } else {
648 if (intRegIdx < registers.size())
649 targetRegs[i] = registers[intRegIdx++];
650 }
651 }
652
653 // Spill any evaluated arg that sits in another arg's target register
654 for (size_t i = 0; i < evaluated_args.size(); ++i) {
655 if (evaluated_args[i].empty() || targetRegs[i].empty())
656 continue;
657 if (evaluated_args[i] == targetRegs[i])
658 continue;
659 for (size_t j = 0; j < evaluated_args.size(); ++j) {
660 if (j == i)
661 continue;
662 if (evaluated_args[j] == targetRegs[i]) {
663 // arg j's value is in a register that arg i needs as target
664 std::string spill = allocReg();
665 emit2("mov", spill, evaluated_args[j]);
666 evaluated_args[j] = spill;
667 }
668 }
669 }
670
671 // Save caller's in-use registers that could be clobbered by this call
672 std::vector<std::string> savedRegs;
673 for (size_t i = 1; i < regInUse.size() && i < registers.size(); ++i) {
674 if (!regInUse[i]) continue;
675 bool isEvalArg = false;
676 for (const auto &ea : evaluated_args) {
677 if (ea == registers[i]) { isEvalArg = true; break; }
678 }
679 if (isEvalArg) continue;
680 savedRegs.push_back(registers[i]);
681 }
682 for (const auto &sr : savedRegs)
683 emit1("push", sr);
684
685 // Now move into target registers
686 {
687 auto eit = externalFuncs.find(node.name);
688 std::string objPrefix = (eit != externalFuncs.end()) ? eit->second + "." : "";
689 for (size_t i = 0; i < evaluated_args.size(); ++i) {
690 if (!targetRegs[i].empty())
691 emit2("mov", objPrefix + targetRegs[i], evaluated_args[i]);
692 }
693 }
694
695 std::string mangledName = findMangledFuncName(node.name, true);
696 {
697 std::string prefix = "PROC_";
698 if (funcSignatures.count(node.name))
699 prefix = "FUNC_";
700 std::string label = prefix + mangledName;
701 auto eit = externalFuncs.find(node.name);
702 if (eit != externalFuncs.end())
703 label = eit->second + "." + label;
704
705 if (!currentFuncLocalSlots.empty()) {
706 for (const auto &slot : currentFuncLocalSlots)
707 emit1("push", slot);
708 }
709
710 emit1("call", label);
711
712 if (!currentFuncLocalSlots.empty()) {
713 for (auto it2 = currentFuncLocalSlots.rbegin(); it2 != currentFuncLocalSlots.rend(); ++it2)
714 emit1("pop", *it2);
715 }
716 }
717
718 // Restore caller's saved registers
719 for (auto rit = savedRegs.rbegin(); rit != savedRegs.rend(); ++rit)
720 emit1("pop", *rit);
721
722 for (const auto &arg : evaluated_args)
723 if (isReg(arg) && !isParmReg(arg))
724 freeReg(arg);
725 }
726
728 // Handle array-specific Length, High, Low before string Length builtin
729 std::string fnLower = node.name;
730 std::transform(fnLower.begin(), fnLower.end(), fnLower.begin(),
731 [](unsigned char c) { return std::tolower(c); });
732 if ((fnLower == "length" || fnLower == "high" || fnLower == "low") && node.arguments.size() == 1) {
733 if (auto *varNode = dynamic_cast<VariableNode *>(node.arguments[0].get())) {
734 std::string mangled = findMangledArrayName(varNode->name);
735 auto it = arrayInfo.find(mangled);
736 if (it == arrayInfo.end())
737 it = arrayInfo.find(varNode->name);
738 if (it != arrayInfo.end()) {
739 if (fnLower == "low") {
740 // Dynamic arrays are always 0-based; static arrays use lowerBound
741 std::string r = allocReg();
742 emit2("mov", r, std::to_string(it->second.isDynamic ? 0 : it->second.lowerBound));
743 pushValue(r);
744 return;
745 }
746 if (it->second.isDynamic) {
747 // Runtime: read from companion length variable
748 std::string arrMangled = (it == arrayInfo.find(mangled)) ? mangled : varNode->name;
749 auto lenIt = dynArrayLenSlot.find(arrMangled);
750 if (lenIt == dynArrayLenSlot.end())
751 throw std::runtime_error("Length/High: dynamic array has no companion length variable: " + varNode->name);
752 std::string lenSym = slotVar(lenIt->second);
753 std::string r = allocReg();
754 emit2("mov", r, lenSym);
755 if (fnLower == "high") {
756 emit2("sub", r, "1");
757 }
758 pushValue(r);
759 return;
760 } else {
761 // Static array: compile-time constants
762 std::string r = allocReg();
763 if (fnLower == "length") {
764 emit2("mov", r, std::to_string(it->second.size));
765 } else { // high
766 emit2("mov", r, std::to_string(it->second.upperBound));
767 }
768 pushValue(r);
769 return;
770 }
771 }
772 }
773 }
774
775 auto handler = builtinRegistry.findHandler(fnLower);
776 if (handler) {
777 if (handler->generateWithResult(*this, fnLower, node.arguments))
778 return;
779 }
780
781 std::vector<std::string> evaluated_args;
782 std::vector<VarType> argTypes;
783
784 for (auto &arg : node.arguments) {
785 std::string argVal = eval(arg.get());
786 evaluated_args.push_back(argVal);
787 argTypes.push_back(getExpressionType(arg.get()));
788 }
789
790 // Build target register list
791 std::vector<std::string> targetRegs(evaluated_args.size());
792 size_t intRegIdx = 1;
793 size_t ptrRegIdx = 0;
794 size_t floatRegIdx = 0;
795
796 for (size_t i = 0; i < evaluated_args.size(); ++i) {
797 if (argTypes[i] == VarType::STRING || argTypes[i] == VarType::PTR || argTypes[i] == VarType::RECORD) {
798 if (ptrRegIdx < ptrRegisters.size())
799 targetRegs[i] = ptrRegisters[ptrRegIdx++];
800 } else if (argTypes[i] == VarType::DOUBLE) {
801 if (floatRegIdx < floatRegisters.size())
802 targetRegs[i] = floatRegisters[floatRegIdx++];
803 } else {
804 if (intRegIdx < registers.size())
805 targetRegs[i] = registers[intRegIdx++];
806 }
807 }
808
809 // Spill any evaluated arg that sits in another arg's target register
810 for (size_t i = 0; i < evaluated_args.size(); ++i) {
811 if (evaluated_args[i].empty() || targetRegs[i].empty())
812 continue;
813 if (evaluated_args[i] == targetRegs[i])
814 continue;
815 for (size_t j = 0; j < evaluated_args.size(); ++j) {
816 if (j == i)
817 continue;
818 if (evaluated_args[j] == targetRegs[i]) {
819 std::string spill = allocReg();
820 emit2("mov", spill, evaluated_args[j]);
821 evaluated_args[j] = spill;
822 }
823 }
824 }
825
826 // Save caller's in-use registers that could be clobbered by this call
827 std::vector<std::string> savedRegs;
828 for (size_t i = 1; i < regInUse.size() && i < registers.size(); ++i) {
829 if (!regInUse[i]) continue;
830 bool isEvalArg = false;
831 for (const auto &ea : evaluated_args) {
832 if (ea == registers[i]) { isEvalArg = true; break; }
833 }
834 if (isEvalArg) continue;
835 savedRegs.push_back(registers[i]);
836 }
837 for (const auto &sr : savedRegs)
838 emit1("push", sr);
839
840 // Now move into target registers
841 {
842 auto eit = externalFuncs.find(node.name);
843 std::string objPrefix = (eit != externalFuncs.end()) ? eit->second + "." : "";
844 for (size_t i = 0; i < evaluated_args.size(); ++i) {
845 if (!targetRegs[i].empty())
846 emit2("mov", objPrefix + targetRegs[i], evaluated_args[i]);
847 }
848 }
849
850 std::string mangledName = findMangledFuncName(node.name, false);
851 {
852 std::string label = "FUNC_" + mangledName;
853 auto eit = externalFuncs.find(node.name);
854 if (eit != externalFuncs.end())
855 label = eit->second + "." + label;
856
857 if (!currentFuncLocalSlots.empty()) {
858 for (const auto &slot : currentFuncLocalSlots)
859 emit1("push", slot);
860 }
861
862 emit1("call", label);
863
864 if (!currentFuncLocalSlots.empty()) {
865 for (auto it2 = currentFuncLocalSlots.rbegin(); it2 != currentFuncLocalSlots.rend(); ++it2)
866 emit1("pop", *it2);
867 }
868 }
869
870 // Restore caller's saved registers
871 for (auto rit = savedRegs.rbegin(); rit != savedRegs.rend(); ++rit)
872 emit1("pop", *rit);
873
874 auto it = funcSignatures.find(node.name);
875 VarType returnType = (it != funcSignatures.end()) ? it->second.returnType : VarType::INT;
876
877 std::string resultLocation;
878 {
879 auto eit = externalFuncs.find(node.name);
880 std::string objPrefix = (eit != externalFuncs.end()) ? eit->second + "." : "";
881 if (returnType == VarType::DOUBLE) {
882 resultLocation = allocFloatReg();
883 emit2("mov", resultLocation, objPrefix + "xmm0");
884 } else if (returnType == VarType::STRING || returnType == VarType::PTR || returnType == VarType::RECORD) {
885 resultLocation = allocTempPtr();
886 emit2("mov", resultLocation, objPrefix + "arg0");
887 } else {
888 resultLocation = allocReg();
889 emit2("mov", resultLocation, objPrefix + "rax");
890 }
891 }
892 pushValue(resultLocation);
893
894 for (const auto &arg : evaluated_args)
895 if (isReg(arg) && !isParmReg(arg))
896 freeReg(arg);
897 }
898
900 std::string mangledName = mangleVariableName(node.name);
901 if (declaredFuncs.count(mangledName)) {
902 return;
903 }
904 declaredFuncs[mangledName] = true;
905
906 FuncInfo funcInfo;
907 funcInfo.returnType = getTypeFromString(node.returnType);
908 for (auto &p : node.parameters) {
909 if (auto pn = dynamic_cast<ParameterNode *>(p.get())) {
910 for (size_t i = 0; i < pn->identifiers.size(); ++i) {
911 funcInfo.paramTypes.push_back(getTypeFromString(pn->type));
912 }
913 }
914 }
915 funcSignatures[node.name] = funcInfo;
916 setVarType(node.name, funcInfo.returnType);
917
918 // Only defer code generation if there's an actual body (not a forward declaration)
919 if (node.block) {
920 auto path = scopeHierarchy;
921 path.push_back(node.name);
922 deferredFuncs.push_back({&node, path});
923
924 scopeHierarchy.push_back(node.name);
925 if (auto blockNode = dynamic_cast<BlockNode *>(node.block.get())) {
926 for (auto &decl : blockNode->declarations) {
927 if (dynamic_cast<ProcDeclNode *>(decl.get()) || dynamic_cast<FuncDeclNode *>(decl.get())) {
928 decl->accept(*this);
929 }
930 }
931 }
932 scopeHierarchy.pop_back();
933 }
934 }
935
937 std::string mangledName = mangleVariableName(node.name);
938 if (declaredProcs.count(mangledName)) {
939 return;
940 }
941 declaredProcs[mangledName] = true;
942
943 // Only defer code generation if there's an actual body (not a forward declaration)
944 if (node.block) {
945 auto path = scopeHierarchy;
946 path.push_back(node.name);
947 deferredProcs.push_back({&node, path});
948
949 scopeHierarchy.push_back(node.name);
950 if (auto blockNode = dynamic_cast<BlockNode *>(node.block.get())) {
951 for (auto &decl : blockNode->declarations) {
952 if (dynamic_cast<ProcDeclNode *>(decl.get()) || dynamic_cast<FuncDeclNode *>(decl.get())) {
953 decl->accept(*this);
954 }
955 }
956 }
957 scopeHierarchy.pop_back();
958 }
959 }
960
962 auto lc = [](std::string s) { std::transform(s.begin(), s.end(), s.begin(),
963 [](unsigned char c){ return std::tolower(c); }); return s; };
964 typeAliases[lc(node.typeName)] = lc(node.baseType);
965 }
966
968 auto lc = [](std::string s) {
969 std::transform(s.begin(), s.end(), s.begin(),
970 [](unsigned char c) { return std::tolower(c); });
971 return s;
972 };
973 for (auto &idRaw : node.identifiers) {
974 std::string id = idRaw;
975 int slot = newSlotFor(id);
976 std::string t = lc(node.type);
977 if (t == "string") {
980 } else if (t == "integer" || t == "boolean") {
983 } else if (t == "real") {
986 } else if (!t.empty() && t[0] == '^') {
989 } else {
990 std::string rt = resolveTypeName(t);
991 if (recordTypes.count(rt)) {
994 varRecordType[id] = rt;
995 currentParamTypes[id] = rt;
996 }
997 }
998 }
999 }
1000
1011 for (auto &stmt : node.statements) {
1012 if (!stmt)
1013 continue;
1014
1015 // snapshot allocated temp ptrs before statement
1016 auto ptrsBefore = allocatedPtrs;
1017 auto escapedBefore = escapedTempPtrs;
1018
1019 if (auto v = dynamic_cast<VariableNode *>(stmt.get())) {
1020 std::string mangled = findMangledFuncName(v->name, true);
1021 if (declaredProcs.count(mangled)) {
1022 std::string prefix = "PROC_";
1023 if (funcSignatures.count(v->name))
1024 prefix = "FUNC_";
1025 std::string label = prefix + mangled;
1026 auto eit = externalFuncs.find(v->name);
1027 if (eit != externalFuncs.end())
1028 label = eit->second + "." + label;
1029 emit1("call", label);
1030 // free any temp ptrs allocated during this statement (not escaped)
1031 for (auto &p : allocatedPtrs) {
1032 if (!ptrsBefore.count(p) && !escapedTempPtrs.count(p))
1033 emit("free " + p);
1034 }
1035 allocatedPtrs = ptrsBefore;
1036 continue;
1037 }
1038 }
1039 stmt->accept(*this);
1040
1041 // free any temp ptrs allocated during this statement (not escaped)
1042 for (auto &p : allocatedPtrs) {
1043 if (!ptrsBefore.count(p) && !escapedTempPtrs.count(p))
1044 emit("free " + p);
1045 }
1046 allocatedPtrs = ptrsBefore;
1047 }
1048 }
1049
1051 std::string elseL = newLabel("ELSE");
1052 std::string endL = newLabel("ENDIF");
1053
1054 std::string condResult = eval(node.condition.get());
1055
1056 emit2("cmp", condResult, "0");
1057 emit1("je", elseL);
1058
1059 if (isReg(condResult) && !isParmReg(condResult)) {
1060 freeReg(condResult);
1061 }
1062 if (node.thenStatement) {
1063 node.thenStatement->accept(*this);
1064 }
1065 if (node.elseStatement) {
1066 emit1("jmp", endL);
1067 emitLabel(elseL);
1068 node.elseStatement->accept(*this);
1069 emitLabel(endL);
1070 } else {
1071 emitLabel(elseL);
1072 }
1073 }
1074
1076 std::string start = newLabel("WHILE");
1077 std::string end = newLabel("ENDWHILE");
1078 loopContinueLabels.push_back(start);
1079 loopEndLabels.push_back(end);
1080 emitLabel(start);
1081 std::string c = eval(node.condition.get());
1082 emit2("cmp", c, "0");
1083 emit1("je", end);
1084 if (isReg(c) && !isParmReg(c))
1085 freeReg(c);
1086 if (node.statement)
1087 node.statement->accept(*this);
1088 emit1("jmp", start);
1089 emitLabel(end);
1090 loopContinueLabels.pop_back();
1091 loopEndLabels.pop_back();
1092 }
1093
1095 std::string startVal = eval(node.startValue.get());
1096 std::string endVal = eval(node.endValue.get());
1097
1098 startVal = coerceToIntImmediate(startVal);
1099 endVal = coerceToIntImmediate(endVal);
1100
1101 std::string mangledLoopVar = mangleVariableName(node.variable);
1102 int slot = newSlotFor(mangledLoopVar);
1103 emit2("mov", slotVar(slot), startVal);
1104 if (isReg(startVal) && !isParmReg(startVal))
1105 freeReg(startVal);
1106
1107 std::string loopStartLabel = newLabel("FOR");
1108 std::string loopEndLabel = newLabel("ENDFOR");
1109 std::string continueLabel = newLabel("FOR_CONTINUE");
1110
1111 loopContinueLabels.push_back(continueLabel);
1112 loopEndLabels.push_back(loopEndLabel);
1113
1114 std::string endCmp = endVal;
1115
1116 emitLabel(loopStartLabel);
1117
1118 emit2("cmp", slotVar(slot), endCmp);
1119 if (node.isDownto)
1120 emit1("jl", loopEndLabel);
1121 else
1122 emit1("jg", loopEndLabel);
1123
1124 if (node.statement)
1125 node.statement->accept(*this);
1126 emitLabel(continueLabel);
1127 if (node.isDownto)
1128 emit2("sub", slotVar(slot), "1");
1129 else
1130 emit2("add", slotVar(slot), "1");
1131 emit1("jmp", loopStartLabel);
1132 emitLabel(loopEndLabel);
1133
1134 loopContinueLabels.pop_back();
1135 loopEndLabels.pop_back();
1136
1137 if (isReg(endCmp) && !isParmReg(endCmp))
1138 freeReg(endCmp);
1139 }
1140
1142 auto isStrLike = [&](VarType v) { return v == VarType::STRING || v == VarType::PTR; };
1143 VarType lt = getExpressionType(node.left.get());
1144 VarType rt = getExpressionType(node.right.get());
1145
1146 // Handle 'in' operator: expr in [val1, val2, ...] or expr in setVar
1147 if (node.operator_ == BinaryOpNode::IN) {
1148 auto *setNode = dynamic_cast<SetLiteralNode *>(node.right.get());
1149 if (setNode) {
1150 // Inline set literal: compare against each element
1151 std::string val = eval(node.left.get());
1152 std::string foundLabel = newLabel("IN_FOUND");
1153 std::string endLabel = newLabel("IN_END");
1154 std::string result = allocReg();
1155
1156 for (auto &elem : setNode->elements) {
1157 std::string elemVal = eval(elem.get());
1158 emit2("cmp", val, elemVal);
1159 emit1("je", foundLabel);
1160 if (isReg(elemVal) && !isParmReg(elemVal))
1161 freeReg(elemVal);
1162 }
1163 emit2("mov", result, "0");
1164 emit1("jmp", endLabel);
1165 emitLabel(foundLabel);
1166 emit2("mov", result, "1");
1167 emitLabel(endLabel);
1168
1169 if (isReg(val) && !isParmReg(val))
1170 freeReg(val);
1171 pushValue(result);
1172 return;
1173 }
1174 // Set variable: load byte at index
1175 std::string val = eval(node.left.get());
1176 std::string setVal = eval(node.right.get());
1177 std::string byte = allocReg();
1178 std::string result = allocReg();
1179 emit4("load", byte, setVal, val, "8");
1180 emit2("cmp", byte, "0");
1181 std::string notFoundLabel = newLabel("IN_NOT_FOUND");
1182 std::string endLabel = newLabel("IN_END");
1183 emit1("je", notFoundLabel);
1184 emit2("mov", result, "1");
1185 emit1("jmp", endLabel);
1186 emitLabel(notFoundLabel);
1187 emit2("mov", result, "0");
1188 emitLabel(endLabel);
1189 freeReg(byte);
1190 if (isReg(val) && !isParmReg(val))
1191 freeReg(val);
1192 if (isReg(setVal) && !isParmReg(setVal))
1193 freeReg(setVal);
1194 pushValue(result);
1195 return;
1196 }
1197
1198 // Set operations: + (union), - (difference), * (intersection)
1199 auto isSetExpr = [&](ASTNode *n) -> bool {
1200 if (dynamic_cast<SetLiteralNode *>(n))
1201 return true;
1202 if (auto *v = dynamic_cast<VariableNode *>(n)) {
1203 std::string mangled = findMangledName(v->name);
1204 return setVars.count(mangled) || setVars.count(v->name);
1205 }
1206 return false;
1207 };
1208 if ((node.operator_ == BinaryOpNode::PLUS ||
1211 (isSetExpr(node.left.get()) || isSetExpr(node.right.get()))) {
1212
1213 std::string leftSet = eval(node.left.get());
1214 std::string rightSet = eval(node.right.get());
1215
1216 // Allocate result set
1217 std::string resultSet = allocTempPtr();
1218 emit3("alloc", resultSet, "8", "256");
1219
1220 std::string idx = allocReg();
1221 std::string lb = allocReg();
1222 std::string rb = allocReg();
1223 std::string loopStart = newLabel("SET_OP");
1224 std::string loopEnd = newLabel("SET_OP_END");
1225 std::string storeBit = newLabel("SET_OP_STORE");
1226 std::string storeZero = newLabel("SET_OP_ZERO");
1227
1228 emit2("mov", idx, "0");
1229 emitLabel(loopStart);
1230 emit2("cmp", idx, "256");
1231 emit1("jge", loopEnd);
1232 emit4("load", lb, leftSet, idx, "8");
1233 emit4("load", rb, rightSet, idx, "8");
1234
1235 if (node.operator_ == BinaryOpNode::PLUS) {
1236 // Union: result = left OR right
1237 emit2("cmp", lb, "0");
1238 emit1("jne", storeBit);
1239 emit2("cmp", rb, "0");
1240 emit1("jne", storeBit);
1241 emit1("jmp", storeZero);
1242 } else if (node.operator_ == BinaryOpNode::MULTIPLY) {
1243 // Intersection: result = left AND right
1244 emit2("cmp", lb, "0");
1245 emit1("je", storeZero);
1246 emit2("cmp", rb, "0");
1247 emit1("je", storeZero);
1248 emit1("jmp", storeBit);
1249 } else {
1250 // Difference: result = left AND NOT right
1251 emit2("cmp", lb, "0");
1252 emit1("je", storeZero);
1253 emit2("cmp", rb, "0");
1254 emit1("jne", storeZero);
1255 emit1("jmp", storeBit);
1256 }
1257 emitLabel(storeBit);
1258 emit4("store", "1", resultSet, idx, "8");
1259 std::string nextLabel = newLabel("SET_OP_NEXT");
1260 emit1("jmp", nextLabel);
1261 emitLabel(storeZero);
1262 emit4("store", "0", resultSet, idx, "8");
1263 emitLabel(nextLabel);
1264 emit2("add", idx, "1");
1265 emit1("jmp", loopStart);
1266 emitLabel(loopEnd);
1267
1268 freeReg(idx);
1269 freeReg(lb);
1270 freeReg(rb);
1271 if (isReg(leftSet) && !isParmReg(leftSet))
1272 freeReg(leftSet);
1273 if (isReg(rightSet) && !isParmReg(rightSet))
1274 freeReg(rightSet);
1275 pushValue(resultSet);
1276 return;
1277 }
1278
1279 if (node.operator_ == BinaryOpNode::PLUS && (isStrLike(lt) || isStrLike(rt))) {
1280 usedModules.insert("string");
1281 std::string left = eval(node.left.get());
1282 std::string right = eval(node.right.get());
1283 std::string len1 = allocReg(), len2 = allocReg(), totalLen = allocReg();
1284 emit_invoke("strlen", {left});
1285 emit("return " + len1);
1286 emit_invoke("strlen", {right});
1287 emit("return " + len2);
1288 emit2("mov", totalLen, len1);
1289 emit2("add", totalLen, len2);
1290 emit2("add", totalLen, "1");
1291
1292 std::string result_str = allocTempPtr();
1293 emit3("alloc", result_str, "1", totalLen);
1294 emit_invoke("strncpy", {result_str, left, len1});
1295 emit_invoke("strncat", {result_str, right, len2});
1296
1297 pushValue(result_str);
1298 freeReg(len1);
1299 freeReg(len2);
1300 freeReg(totalLen);
1301 if (isReg(left) && !isParmReg(left))
1302 freeReg(left);
1303 if (isReg(right) && !isParmReg(right))
1304 freeReg(right);
1305 return;
1306 }
1307 try {
1308 std::string folded = foldNumeric(&node);
1309 if (!folded.empty()) {
1310 if (node.operator_ == BinaryOpNode::DIVIDE && isIntegerLiteral(folded))
1311 folded += ".0";
1312 if (isFloatLiteral(folded))
1314 else
1315 pushValue(folded);
1316 return;
1317 }
1318 } catch (...) {
1319 }
1320
1321 auto evalOperand = [&](ASTNode *n) -> std::string {
1322 if (auto var = dynamic_cast<VariableNode *>(n)) {
1323 std::string v;
1324 if (tryGetConstNumeric(var->name, v))
1325 return v;
1326 }
1327 return eval(n);
1328 };
1329
1330 std::string left = evalOperand(node.left.get());
1331 std::string right = evalOperand(node.right.get());
1332
1333 bool needsFloatOp = (lt == VarType::DOUBLE || rt == VarType::DOUBLE ||
1334 isFloatLiteral(left) || isFloatLiteral(right) ||
1336
1337 if (node.operator_ == BinaryOpNode::DIVIDE) {
1338 needsFloatOp = true;
1339 if (isIntegerLiteral(left) && !isFloatLiteral(left))
1340 left += ".0";
1341 if (isIntegerLiteral(right) && !isFloatLiteral(right))
1342 right += ".0";
1343 }
1344
1345 if (node.operator_ == BinaryOpNode::DIV && (isFloatLiteral(left) || isFloatLiteral(right))) {
1346 if (isFloatLiteral(left))
1347 left = std::to_string((long long)std::stod(left));
1348 if (isFloatLiteral(right))
1349 right = std::to_string((long long)std::stod(right));
1350 needsFloatOp = false;
1351 }
1352
1353 if (needsFloatOp && (node.operator_ == BinaryOpNode::PLUS ||
1357 std::string dst;
1358 if (isFloatReg(left) && !isParmReg(left)) {
1359 dst = left;
1360 } else {
1361 dst = allocFloatReg();
1362 emit2("mov", dst, left);
1363 }
1364
1365 emit2(node.operator_ == BinaryOpNode::DIVIDE ? "div" : node.operator_ == BinaryOpNode::MULTIPLY ? "mul"
1366 : node.operator_ == BinaryOpNode::MINUS ? "sub"
1367 : "add",
1368 dst, right);
1369
1370 if (isReg(right) && !isParmReg(right))
1371 freeReg(right);
1372 pushValue(dst);
1373 return;
1374 }
1375
1376 auto emitBinary = [&](const char *op) {
1377 std::string dst;
1378 bool leftIsUsableReg = isReg(left) && !isParmReg(left);
1379 if (leftIsUsableReg)
1380 dst = left;
1381 else {
1382 dst = allocReg();
1383 emit2("mov", dst, left);
1384 }
1385 emit2(op, dst, right);
1386 if (isReg(right) && !isParmReg(right))
1387 freeReg(right);
1388 pushValue(dst);
1389 };
1390
1391 switch (node.operator_) {
1392 case BinaryOpNode::PLUS:
1393 emitBinary("add");
1394 break;
1396 emitBinary("sub");
1397 break;
1399 emitBinary("mul");
1400 break;
1402 emitBinary("div");
1403 break;
1404 case BinaryOpNode::DIV:
1405 emitBinary("div");
1406 break;
1407 case BinaryOpNode::MOD:
1408 emitBinary("mod");
1409 break;
1410 case BinaryOpNode::AND:
1411 pushLogicalAnd(left, right);
1412 break;
1413 case BinaryOpNode::OR:
1414 pushLogicalOr(left, right);
1415 break;
1417 pushCmpResult(left, right, "je");
1418 break;
1420 pushCmpResult(left, right, "jne");
1421 break;
1422 case BinaryOpNode::LESS:
1423 pushCmpResult(left, right, "jl");
1424 break;
1426 pushCmpResult(left, right, "jle");
1427 break;
1429 pushCmpResult(left, right, "jg");
1430 break;
1432 pushCmpResult(left, right, "jge");
1433 break;
1434 default:
1435 throw std::runtime_error("Unsupported binary operator");
1436 }
1437 }
1438
1440 std::string v = eval(node.operand.get());
1441 switch (node.operator_) {
1442 case UnaryOpNode::MINUS: {
1443 std::string t = allocReg();
1444 emit2("mov", t, "0");
1445 emit2("sub", t, v);
1446 pushValue(t);
1447 break;
1448 }
1449 case UnaryOpNode::PLUS: {
1450 pushValue(v);
1451 break;
1452 }
1453 case UnaryOpNode::NOT: {
1454 emit("not " + v);
1455 pushValue(v);
1456 break;
1457 }
1458 }
1459 }
1460
1462 // 'result' inside a function refers to the return value
1463 if (lc(node.name) == "result" && !currentFunctionName.empty()) {
1464 if (!currentFunctionReturnSlot.empty()) {
1466 } else {
1468 int slot = newSlotFor("__funcret_" + currentFunctionName);
1469 if (rt == VarType::DOUBLE)
1471 else if (rt == VarType::STRING || rt == VarType::PTR || rt == VarType::RECORD)
1473 else
1477 }
1478 return;
1479 }
1480
1481 // Check with-scopes: if this name is a field in an active 'with' block,
1482 // treat it as recordVar.fieldName
1483 for (auto it = withFieldScopes.rbegin(); it != withFieldScopes.rend(); ++it) {
1484 auto fit = it->find(lc(node.name));
1485 if (fit != it->end()) {
1486 auto recExpr = std::make_unique<VariableNode>(fit->second);
1487 FieldAccessNode syntheticField(std::move(recExpr), node.name);
1488 visit(syntheticField);
1489 return;
1490 }
1491 }
1492
1493 if (auto pit = currentParamLocations.find(node.name); pit != currentParamLocations.end()) {
1494 pushValue(pit->second);
1495 return;
1496 }
1497
1498 std::string mangled = findMangledName(node.name);
1499
1500 if (auto ct = compileTimeConstants.find(mangled); ct != compileTimeConstants.end()) {
1501 pushValue(ct->second);
1502 return;
1503 }
1504 if (auto ct2 = compileTimeConstants.find(node.name); ct2 != compileTimeConstants.end()) {
1505 pushValue(ct2->second);
1506 return;
1507 }
1508 if (auto ct3 = compileTimeConstants.find(lc(node.name)); ct3 != compileTimeConstants.end()) {
1509 pushValue(ct3->second);
1510 return;
1511 }
1512
1513
1514 if (auto sit = varSlot.find(mangled); sit != varSlot.end()) {
1515 pushValue(slotVar(sit->second));
1516 return;
1517 }
1518
1519 // Zero-arg function call without parentheses
1520 if (externalFuncs.count(node.name) || funcSignatures.count(node.name)) {
1521 FuncCallNode syntheticCall(node.name, {});
1522 visit(syntheticCall);
1523 return;
1524 }
1525
1526 pushValue(mangled);
1527 }
1528
1530 if (node.isReal || isRealNumber(node.value)) {
1531 std::string reg = allocFloatReg();
1532 emit2("mov", reg, ensureFloatConstSymbol(node.value));
1533 pushValue(reg);
1534 } else {
1535 pushValue(node.value);
1536 }
1537 }
1538
1540 std::string sym = internString(node.value);
1541 pushValue(sym);
1542 }
1543
1545 pushValue(node.value ? "1" : "0");
1546 }
1547
1549 // No operation
1550 }
1551
1553 for (const auto &assignment : node.assignments) {
1554 if (auto strNode = dynamic_cast<StringNode *>(assignment->value.get())) {
1555 std::string sym = internString(strNode->value);
1556
1557 std::string mangledName = mangleVariableName(assignment->identifier);
1558 compileTimeConstants[mangledName] = mangledName;
1559 compileTimeConstants[assignment->identifier] = mangledName;
1560
1561 int slot = newSlotFor(mangledName);
1562 setVarType(assignment->identifier, VarType::PTR);
1564 updateDataSectionInitialValue(slotVar(slot), "ptr", "null");
1565 prolog.push_back("mov " + slotVar(slot) + ", " + sym);
1566 continue;
1567 }
1568
1569 try {
1570 std::string literalValue = evaluateConstantExpression(assignment->value.get());
1571 bool isFloat = isRealNumber(literalValue);
1572 if (isFloat)
1573 literalValue = ensureFloatConstSymbol(literalValue);
1574
1575 std::string varType = isFloat ? "float" : "int";
1576 std::string mangledName = mangleVariableName(assignment->identifier);
1577
1578 compileTimeConstants[mangledName] = (isFloat ? realConstants[literalValue] : literalValue);
1579 compileTimeConstants[assignment->identifier] = (isFloat ? realConstants[literalValue] : literalValue);
1580
1581 int slot = newSlotFor(mangledName);
1582 VarType vType = isFloat ? VarType::DOUBLE : VarType::INT;
1583 setVarType(assignment->identifier, vType);
1584 setSlotType(slot, vType);
1585
1587 isFloat ? realConstants[literalValue] : literalValue);
1588 } catch (const std::runtime_error &) {
1589 std::string valueReg = eval(assignment->value.get());
1590 int slot = newSlotFor(assignment->identifier);
1591 VarType exprType = getExpressionType(assignment->value.get());
1592 setVarType(assignment->identifier, exprType);
1593 setSlotType(slot, exprType);
1594 std::string varLocation = slotVar(slot);
1595 emit2("mov", varLocation, valueReg);
1596 if (isReg(valueReg) && !isParmReg(valueReg))
1597 freeReg(valueReg);
1598 }
1599 }
1600 }
1601
1603 std::string startLabel = newLabel("REPEAT");
1604 std::string endLabel = newLabel("UNTIL");
1605 std::string continueLabel = newLabel("REPEAT_CONTINUE");
1606
1607 loopContinueLabels.push_back(continueLabel);
1608 loopEndLabels.push_back(endLabel);
1609
1610 emitLabel(startLabel);
1611 for (auto &stmt : node.statements)
1612 if (stmt)
1613 stmt->accept(*this);
1614 emitLabel(continueLabel);
1615 std::string condResult = eval(node.condition.get());
1616 emit2("cmp", condResult, "0");
1617 emit1("je", startLabel);
1618 if (isReg(condResult) && !isParmReg(condResult))
1619 freeReg(condResult);
1620 emitLabel(endLabel);
1621 loopContinueLabels.pop_back();
1622 loopEndLabels.pop_back();
1623 }
1624
1626 VarType exprType = getExpressionType(node.expression.get());
1627 std::string switchExpr = eval(node.expression.get());
1628 std::string endLabel = newLabel("CASE_END");
1629 std::vector<std::string> branchLabels;
1630 for (size_t i = 0; i < node.branches.size(); i++)
1631 branchLabels.push_back(newLabel("CASE_" + std::to_string(i)));
1632 std::string elseLabel = newLabel("CASE_ELSE");
1633 for (size_t i = 0; i < node.branches.size(); i++) {
1634 auto &branch = node.branches[i];
1635 for (auto &value : branch->values) {
1636 std::string caseValue;
1637 if ((exprType == VarType::CHAR || exprType == VarType::INT) &&
1638 dynamic_cast<StringNode *>(value.get())) {
1639 auto *strNode = static_cast<StringNode *>(value.get());
1640 if (strNode->value.size() == 1) {
1641 caseValue = std::to_string((int)(unsigned char)strNode->value[0]);
1642 } else {
1643 caseValue = eval(value.get());
1644 }
1645 } else {
1646 caseValue = eval(value.get());
1647 }
1648 emit2("cmp", switchExpr, caseValue);
1649 emit1("je", branchLabels[i]);
1650 if (isReg(caseValue) && !isParmReg(caseValue))
1651 freeReg(caseValue);
1652 }
1653 }
1654 if (node.elseStatement)
1655 emit1("jmp", elseLabel);
1656 else
1657 emit1("jmp", endLabel);
1658 for (size_t i = 0; i < node.branches.size(); i++) {
1659 emitLabel(branchLabels[i]);
1660 if (node.branches[i]->statement)
1661 node.branches[i]->statement->accept(*this);
1662 emit1("jmp", endLabel);
1663 }
1664 if (node.elseStatement) {
1665 emitLabel(elseLabel);
1666 node.elseStatement->accept(*this);
1667 }
1668 emitLabel(endLabel);
1669 if (isReg(switchExpr) && !isParmReg(switchExpr))
1670 freeReg(switchExpr);
1671 }
1672
1674 // Typically handled within other nodes like VarDeclNode or TypeDeclNode
1675 }
1676
1678 std::string mangledName = findMangledName(node.name);
1679
1680 std::string elementType;
1681 if (auto simpleType = dynamic_cast<SimpleTypeNode *>(node.arrayType->elementType.get())) {
1682 elementType = simpleType->typeName;
1683 } else if (dynamic_cast<ArrayTypeNode *>(node.arrayType->elementType.get())) {
1684 elementType = "array";
1685 } else if (dynamic_cast<RecordTypeNode *>(node.arrayType->elementType.get())) {
1686 elementType = "record";
1687 } else if (auto *pt = dynamic_cast<PointerTypeNode *>(node.arrayType->elementType.get())) {
1688 elementType = "^" + pt->baseTypeName;
1689 } else {
1690 elementType = "integer";
1691 }
1692
1693 int lowerBound = std::stoi(evaluateConstantExpression(node.arrayType->lowerBound.get()));
1694 int upperBound = std::stoi(evaluateConstantExpression(node.arrayType->upperBound.get()));
1695 int size = upperBound - lowerBound + 1;
1696
1697 int elementSize = 8;
1698
1699 if (isRecordTypeName(elementType)) {
1700 auto it = recordTypes.find(elementType);
1701 if (it != recordTypes.end()) {
1702 elementSize = it->second.size;
1703 }
1704 } else {
1705 elementSize = getArrayElementSize(elementType);
1706 }
1707
1708 ArrayInfo info;
1709 info.elementType = elementType;
1710 info.lowerBound = lowerBound;
1711 info.upperBound = upperBound;
1712 info.size = size;
1713 info.elementSize = elementSize;
1714 mangledName = findMangledName(node.name);
1715 arrayInfo[mangledName] = std::move(info);
1716 int slot = newSlotFor(mangledName);
1717 varSlot[node.name] = slot;
1718 varSlot[mangledName] = slot;
1721 updateDataSectionInitialValue(slotVar(slot), "ptr", "null");
1722 emit3("alloc",
1723 slotVar(slot),
1724 std::to_string(elementSize),
1725 std::to_string(size));
1726 std::string currentScope = getCurrentScopeName();
1727 if (currentScope.empty())
1728 globalArrays.push_back(slotVar(slot));
1729 else
1730 functionScopedArrays[currentScope].push_back(slotVar(slot));
1731 }
1732
1734 // Cross-unit variable reference: UnitName.varName
1735 if (auto *v = dynamic_cast<VariableNode *>(node.recordExpr.get())) {
1736 if (isImportedUnit(v->name)) {
1737 std::string qualifiedName = v->name + "." + node.fieldName;
1738
1739 // Check for imported constant first
1740 auto ct = compileTimeConstants.find(qualifiedName);
1741 if (ct != compileTimeConstants.end()) {
1742 pushValue(ct->second);
1743 return;
1744 }
1745
1746 // Check for zero-arg function call: UnitName.FuncName
1747 auto eit = externalFuncs.find(node.fieldName);
1748 if (eit != externalFuncs.end()) {
1749 FuncCallNode syntheticCall(node.fieldName, {});
1750 visit(syntheticCall);
1751 return;
1752 }
1753
1754 VarType vt = getVarType(qualifiedName);
1755 std::string dst;
1756 if (vt == VarType::DOUBLE)
1757 dst = allocFloatReg();
1758 else if (vt == VarType::PTR || vt == VarType::STRING)
1759 dst = allocTempPtr();
1760 else
1761 dst = allocReg();
1762 emit2("mov", dst, qualifiedName);
1763 pushValue(dst);
1764 return;
1765 }
1766 }
1767
1768 std::string baseName;
1769 std::string recType;
1770 bool baseIsDirectPointer = false;
1771
1772 if (auto *v = dynamic_cast<VariableNode *>(node.recordExpr.get())) {
1773 baseName = findMangledName(v->name);
1774 recType = getVarRecordTypeName(v->name);
1775 } else if (dynamic_cast<ArrayAccessNode *>(node.recordExpr.get())) {
1776 node.recordExpr->accept(*this);
1777 baseName = popValue();
1778 baseIsDirectPointer = true;
1779 recType = getVarRecordTypeNameFromExpr(node.recordExpr.get());
1780 } else {
1781 node.recordExpr->accept(*this);
1782 baseName = popValue();
1783 baseIsDirectPointer = true;
1784 recType = getVarRecordTypeNameFromExpr(node.recordExpr.get());
1785 }
1786
1787 recType = resolveTypeName(lc(recType));
1788 if (recType.empty()) {
1789 throw std::runtime_error("Unknown record type: (empty) for field " + node.fieldName);
1790 }
1791
1792 auto recTypeIt = recordTypes.find(recType);
1793 if (recTypeIt == recordTypes.end()) {
1794 throw std::runtime_error("Unknown record type: " + recType + " for field " + node.fieldName);
1795 }
1796
1797 const std::string fieldName = lc(node.fieldName);
1798 auto &recInfo = recTypeIt->second;
1799 auto it = recInfo.nameToIndex.find(fieldName);
1800 if (it == recInfo.nameToIndex.end()) {
1801 throw std::runtime_error("Field not found in record: " + fieldName + " in type " + recType);
1802 }
1803
1804 const auto &fieldInfo = recInfo.fields[it->second];
1805 const int byteOffset = fieldInfo.offset;
1806
1807 std::string basePtr = baseIsDirectPointer ? baseName : ensurePtrBase(baseName);
1808
1809 if (fieldInfo.isArray || isRecordTypeName(fieldInfo.typeName)) {
1810 std::string p = allocTempPtr();
1811 emit2("mov", p, basePtr);
1812 emit2("add", p, std::to_string(byteOffset));
1813 pushValue(p);
1814 return;
1815 }
1816
1817 VarType fieldType = getTypeFromString(fieldInfo.typeName);
1818 std::string dst;
1819 if (fieldType == VarType::DOUBLE) {
1820 dst = allocFloatReg();
1821 } else if (fieldType == VarType::PTR || fieldType == VarType::STRING) {
1822 dst = allocTempPtr();
1823 } else {
1824 dst = allocReg();
1825 }
1826
1827 emit4("load", dst, basePtr, std::to_string(byteOffset), "1");
1828 pushValue(dst);
1829 }
1830
1832 // Intercept 'with' scope: if the LHS is a variable matching a record field, rewrite
1833 if (auto *varLHS = dynamic_cast<VariableNode *>(node.variable.get())) {
1834 for (auto it = withFieldScopes.rbegin(); it != withFieldScopes.rend(); ++it) {
1835 auto fit = it->find(lc(varLHS->name));
1836 if (fit != it->end()) {
1837 auto recExpr = std::make_unique<VariableNode>(fit->second);
1838 auto fieldAccess = std::make_unique<FieldAccessNode>(std::move(recExpr), varLHS->name);
1839 AssignmentNode syntheticAssign(std::move(fieldAccess), std::move(node.expression));
1840 visit(syntheticAssign);
1841 // Move expression back so the original node is not left in a moved-from state
1842 node.expression = std::move(syntheticAssign.expression);
1843 return;
1844 }
1845 }
1846 }
1847
1848 if (auto deref = dynamic_cast<PointerDerefNode *>(node.variable.get())) {
1849 std::string rhs = eval(node.expression.get());
1850 std::string ptrVal = eval(deref->pointer.get());
1851 std::string base = ensurePtrBase(ptrVal);
1852
1853 emit4("store", rhs, base, "0", "1");
1854
1855 if (isReg(rhs) && !isParmReg(rhs))
1856 freeReg(rhs);
1857 if (isReg(ptrVal) && !isParmReg(ptrVal) && ptrVal != base)
1858 freeReg(ptrVal);
1859 if (isReg(base) && !isParmReg(base))
1860 freeReg(base);
1861 return;
1862 }
1863
1864 if (auto arr = dynamic_cast<ArrayAccessNode *>(node.variable.get())) {
1866 if (!info)
1867 throw std::runtime_error("Unknown array: " + getArrayNameFromBase(arr->base.get()));
1868
1869 std::string rhs = eval(node.expression.get());
1870 std::string idx = eval(arr->index.get());
1871
1872 if (getExpressionType(arr->index.get()) == VarType::DOUBLE) {
1873 std::string intIdx = allocReg();
1874 emit2("mov", intIdx, idx);
1875 if (isReg(idx) && !isParmReg(idx))
1876 freeReg(idx);
1877 idx = intIdx;
1878 }
1879
1880#ifdef MXVM_BOUNDS_CHECK
1881 if (info->isDynamic) {
1882 std::string arrName = getArrayNameFromBase(arr->base.get());
1883 std::string mangled = findMangledArrayName(arrName);
1884 auto lenIt = dynArrayLenSlot.find(mangled);
1885 if (lenIt == dynArrayLenSlot.end())
1886 lenIt = dynArrayLenSlot.find(arrName);
1887 if (lenIt != dynArrayLenSlot.end())
1888 emitDynArrayBoundsCheck(idx, slotVar(lenIt->second));
1889 } else {
1890 emitArrayBoundsCheck(idx, info->lowerBound, info->upperBound);
1891 }
1892#endif
1893
1894 std::string elemIndex;
1895 if (isReg(idx) && !isParmReg(idx)) {
1896 elemIndex = idx;
1897 } else {
1898 elemIndex = allocReg();
1899 emit2("mov", elemIndex, idx);
1900 }
1901 if (info->lowerBound != 0)
1902 emit2("sub", elemIndex, std::to_string(info->lowerBound));
1903
1904 VarType elemType = VarType::INT;
1905 if (info->elementType == "real")
1906 elemType = VarType::DOUBLE;
1907 else if (info->elementType == "string" || info->elementType == "ptr" ||
1908 info->elementType == "pointer" ||
1909 (!info->elementType.empty() && info->elementType[0] == '^'))
1910 elemType = VarType::PTR;
1911
1912 VarType rhsType = getExpressionType(node.expression.get());
1913 if (elemType == VarType::DOUBLE && rhsType != VarType::DOUBLE && !isFloatReg(rhs)) {
1914 std::string f = allocFloatReg();
1915 emit2("mov", f, rhs);
1916 if (isReg(rhs) && !isParmReg(rhs))
1917 freeReg(rhs);
1918 rhs = f;
1919 } else if (elemType != VarType::DOUBLE && rhsType == VarType::DOUBLE && isFloatReg(rhs)) {
1920 std::string ir = allocReg();
1921 emit2("mov", ir, rhs);
1922 if (isReg(rhs) && !isParmReg(rhs))
1923 freeReg(rhs);
1924 rhs = ir;
1925 }
1926
1927 std::string base;
1928 if (auto var = dynamic_cast<VariableNode *>(arr->base.get())) {
1929 std::string mangled = findMangledArrayName(var->name);
1930 base = ensurePtrBase(storageSymbolFor(mangled));
1931 } else if (auto field = dynamic_cast<FieldAccessNode *>(arr->base.get())) {
1932 // Cross-unit array assignment: UnitName.arrayName[index] := value
1933 if (auto *baseVar = dynamic_cast<VariableNode *>(field->recordExpr.get());
1934 baseVar && isImportedUnit(baseVar->name)) {
1935 std::string qualifiedName = baseVar->name + "." + field->fieldName;
1936 base = ensurePtrBase(qualifiedName);
1937 } else {
1938 field->recordExpr->accept(*this);
1939 std::string recPtr = popValue();
1940 std::string recType = getVarRecordTypeNameFromExpr(field->recordExpr.get());
1941 auto ofs_sz = getRecordFieldOffsetAndSize(recType, field->fieldName);
1942 int fieldOffset = ofs_sz.first;
1943
1944 std::string arrayPtr = allocTempPtr();
1945 emit2("mov", arrayPtr, recPtr);
1946 emit2("add", arrayPtr, std::to_string(fieldOffset));
1947 base = arrayPtr;
1948 }
1949 } else if (dynamic_cast<ArrayAccessNode *>(arr->base.get())) {
1950 arr->base->accept(*this);
1951 base = popValue();
1952 base = ensurePtrBase(base);
1953 } else {
1954 throw std::runtime_error("Unsupported array base in assignment");
1955 }
1956
1957 emit4("store", rhs, base, elemIndex, std::to_string(info->elementSize));
1958
1959 if (isReg(rhs) && !isParmReg(rhs))
1960 freeReg(rhs);
1961 if (elemIndex != idx && isReg(idx) && !isParmReg(idx))
1962 freeReg(idx);
1963 freeReg(elemIndex);
1964 return;
1965 }
1966
1967 if (auto field = dynamic_cast<FieldAccessNode *>(node.variable.get())) {
1968 // Cross-unit simple variable assignment: UnitName.varName := value
1969 if (auto *v = dynamic_cast<VariableNode *>(field->recordExpr.get());
1970 v && isImportedUnit(v->name)) {
1971 std::string qualifiedName = v->name + "." + field->fieldName;
1972 std::string rhs = eval(node.expression.get());
1973 emit2("mov", qualifiedName, rhs);
1974 if (isReg(rhs) && !isParmReg(rhs))
1975 freeReg(rhs);
1976 return;
1977 }
1978
1979 std::string rhs = eval(node.expression.get());
1980
1981 std::string baseName;
1982 std::string recType;
1983 bool baseIsDirectPointer = false;
1984
1985 if (auto *v = dynamic_cast<VariableNode *>(field->recordExpr.get())) {
1986 baseName = findMangledName(v->name);
1987 recType = getVarRecordTypeName(v->name);
1988 } else {
1989 field->recordExpr->accept(*this);
1990 baseName = popValue();
1991 baseIsDirectPointer = true;
1992 recType = getVarRecordTypeNameFromExpr(field->recordExpr.get());
1993 }
1994
1995 recType = resolveTypeName(lc(recType));
1996 if (recType.empty()) {
1997 throw std::runtime_error("Unknown record type for field assignment: " + field->fieldName);
1998 }
1999
2000 auto recTypeIt = recordTypes.find(recType);
2001 if (recTypeIt == recordTypes.end()) {
2002 throw std::runtime_error("Unknown record type: " + recType + " for field " + field->fieldName);
2003 }
2004
2005 const std::string fieldName = lc(field->fieldName);
2006 auto &recInfo = recTypeIt->second;
2007 auto it = recInfo.nameToIndex.find(fieldName);
2008 if (it == recInfo.nameToIndex.end()) {
2009 throw std::runtime_error("Field not found in record: " + fieldName + " in type " + recType);
2010 }
2011
2012 const auto &fieldInfo = recInfo.fields[it->second];
2013 const int byteOffset = fieldInfo.offset;
2014
2015 std::string basePtr = baseIsDirectPointer ? baseName : ensurePtrBase(baseName);
2016
2017 // Convert between float and int if field type doesn't match rhs type.
2018 // When assigning a float expression (e.g. trunc() result in xmm reg) to an
2019 // integer record field, emit a mov to convert from float register to int
2020 // register first. Without this, the raw IEEE 754 double bit pattern would
2021 // be stored into the integer field, producing garbage values.
2022 VarType fieldType = getTypeFromString(fieldInfo.typeName);
2023 if (fieldType == VarType::INT && isFloatReg(rhs)) {
2024 std::string intReg = allocReg();
2025 emit2("mov", intReg, rhs);
2026 if (isReg(rhs) && !isParmReg(rhs))
2027 freeReg(rhs);
2028 rhs = intReg;
2029 } else if (fieldType == VarType::DOUBLE && !isFloatReg(rhs) && isReg(rhs)) {
2030 std::string fltReg = allocFloatReg();
2031 emit2("mov", fltReg, rhs);
2032 if (!isParmReg(rhs))
2033 freeReg(rhs);
2034 rhs = fltReg;
2035 }
2036
2037 emit4("store", rhs, basePtr, std::to_string(byteOffset), "1");
2038
2039 if (isReg(rhs) && !isParmReg(rhs))
2040 freeReg(rhs);
2041 if (baseIsDirectPointer && isReg(basePtr) && !isParmReg(basePtr))
2042 freeReg(basePtr);
2043
2044 return;
2045 }
2046
2047 auto varPtr = dynamic_cast<VariableNode *>(node.variable.get());
2048 if (!varPtr)
2049 return;
2050
2051 std::string varName = varPtr->name;
2052
2053 // Set assignment: copy 256 bytes from RHS set to LHS set
2054 if (setVars.count(lc(varName)) || setVars.count(lc(findMangledName(varName)))) {
2055 std::string rhs = eval(node.expression.get());
2056 std::string mangled = findMangledName(varName);
2057 std::string destBase;
2058 auto sit = varSlot.find(mangled);
2059 if (sit != varSlot.end())
2060 destBase = slotVar(sit->second);
2061 else
2062 destBase = mangled;
2063
2064 std::string srcBase = ensurePtrBase(rhs);
2065 std::string dstBase = ensurePtrBase(destBase);
2066 std::string idx = allocReg();
2067 std::string byteVal = allocReg();
2068 emit2("mov", idx, "0");
2069 std::string loopLbl = newLabel("set_copy");
2070 std::string endLbl = newLabel("set_copy_end");
2071 emitLabel(loopLbl);
2072 emit2("cmp", idx, "256");
2073 emit1("jge", endLbl);
2074 emit4("load", byteVal, srcBase, idx, "8");
2075 emit4("store", byteVal, dstBase, idx, "8");
2076 emit2("add", idx, "1");
2077 emit1("jmp", loopLbl);
2078 emitLabel(endLbl);
2079 freeReg(idx);
2080 freeReg(byteVal);
2081 if (isReg(rhs) && !isParmReg(rhs))
2082 freeReg(rhs);
2083 return;
2084 }
2085
2086 std::string rhs;
2087 VarType varType = getVarType(varName);
2088 if ((varType == VarType::CHAR || varType == VarType::INT)) {
2089 if (auto *strNode = dynamic_cast<StringNode *>(node.expression.get())) {
2090 if (strNode->value.size() == 1) {
2091 rhs = std::to_string((int)(unsigned char)strNode->value[0]);
2092 } else {
2093 rhs = eval(node.expression.get());
2094 }
2095 } else {
2096 rhs = eval(node.expression.get());
2097 }
2098 } else {
2099 rhs = eval(node.expression.get());
2100 }
2101
2102 auto it = currentParamLocations.find(varName);
2103 if (it != currentParamLocations.end()) {
2104 emit2("mov", it->second, rhs);
2105 if (isReg(rhs) && !isParmReg(rhs))
2106 freeReg(rhs);
2107 return;
2108 }
2109
2110 if (!currentFunctionName.empty() && (varName == currentFunctionName || varName == "result")) {
2112 if (rt == VarType::STRING || rt == VarType::PTR || rt == VarType::RECORD) {
2113 if (currentFunctionReturnSlot.empty()) {
2114 int slot = newSlotFor("__funcret_" + currentFunctionName);
2117 }
2118 emit2("mov", currentFunctionReturnSlot, rhs);
2119 } else if (rt == VarType::DOUBLE) {
2120 if (currentFunctionReturnSlot.empty()) {
2121 int slot = newSlotFor("__funcret_" + currentFunctionName);
2124 }
2125 emit2("mov", currentFunctionReturnSlot, rhs);
2126 } else {
2127 if (currentFunctionReturnSlot.empty()) {
2128 int slot = newSlotFor("__funcret_" + currentFunctionName);
2131 }
2132 emit2("mov", currentFunctionReturnSlot, rhs);
2133 }
2134 functionSetReturn = true;
2135 } else {
2136 std::string mangled = findMangledName(varName);
2137 auto it = varSlot.find(mangled);
2138 if (it != varSlot.end()) {
2139 emit2("mov", slotVar(it->second), rhs);
2140 recordLocation(varName, {ValueLocation::MEMORY, slotVar(it->second)});
2141 } else {
2142 emit2("mov", mangled, rhs);
2143 recordLocation(varName, {ValueLocation::MEMORY, mangled});
2144 }
2145 }
2146
2147 if (isTempPtr(rhs) && allocatedPtrs.count(rhs) && !isTempPtr(varName))
2148 escapedTempPtrs.insert(rhs);
2149
2150 if (isReg(rhs) && !isParmReg(rhs))
2151 freeReg(rhs);
2152 }
2153
2155 auto it = arrayInfo.find(node.arrayName);
2156 if (it == arrayInfo.end())
2157 throw std::runtime_error("Unknown array: " + node.arrayName);
2158 ArrayInfo &info = it->second;
2159
2160 std::string value = eval(node.value.get());
2161 std::string index = eval(node.index.get());
2162
2163 if (getExpressionType(node.index.get()) == VarType::DOUBLE) {
2164 std::string intIndex = allocReg();
2165 emit2("mov", intIndex, index);
2166 if (isReg(index) && !isParmReg(index))
2167 freeReg(index);
2168 index = intIndex;
2169 }
2170
2171#ifdef MXVM_BOUNDS_CHECK
2172 if (info.isDynamic) {
2173 auto lenIt = dynArrayLenSlot.find(node.arrayName);
2174 if (lenIt != dynArrayLenSlot.end())
2175 emitDynArrayBoundsCheck(index, slotVar(lenIt->second));
2176 } else {
2177 emitArrayBoundsCheck(index, info.lowerBound, info.upperBound);
2178 }
2179#endif
2180
2181 std::string elementIndex;
2182 if (isReg(index) && !isParmReg(index)) {
2183 elementIndex = index;
2184 } else {
2185 elementIndex = allocReg();
2186 emit2("mov", elementIndex, index);
2187 }
2188 if (info.lowerBound != 0)
2189 emit2("sub", elementIndex, std::to_string(info.lowerBound));
2190
2191 std::string base = storageSymbolFor(node.arrayName);
2192 base = ensurePtrBase(base);
2193
2194 VarType elemType = VarType::INT;
2195 if (info.elementType == "real")
2196 elemType = VarType::DOUBLE;
2197 else if (info.elementType == "string" || info.elementType == "ptr" ||
2198 info.elementType == "pointer" ||
2199 (!info.elementType.empty() && info.elementType[0] == '^'))
2200 elemType = VarType::PTR;
2201 else if (isRecordTypeName(info.elementType))
2202 elemType = VarType::RECORD;
2203
2204 VarType rhsType = getExpressionType(node.value.get());
2205 if (elemType == VarType::DOUBLE && rhsType != VarType::DOUBLE && !isFloatReg(value)) {
2206 std::string f = allocFloatReg();
2207 emit2("mov", f, value);
2208 if (isReg(value) && !isParmReg(value))
2209 freeReg(value);
2210 value = f;
2211 } else if (elemType != VarType::DOUBLE && rhsType == VarType::DOUBLE && isFloatReg(value)) {
2212 std::string i = allocReg();
2213 emit2("mov", i, value);
2214 if (isReg(value) && !isParmReg(value))
2215 freeReg(value);
2216 value = i;
2217 }
2218
2219 emit4("store", value, base, elementIndex, std::to_string(info.elementSize));
2220
2221 if (isReg(value) && !isParmReg(value))
2222 freeReg(value);
2223 if (elementIndex != index && isReg(index) && !isParmReg(index))
2224 freeReg(index);
2225 freeReg(elementIndex);
2226 }
2227
2230 if (!info) {
2231 std::string arrayName = getArrayNameFromBase(node.base.get());
2232 throw std::runtime_error("Unknown array: " + arrayName);
2233 }
2234
2235 std::string idx = eval(node.index.get());
2236
2237 if (getExpressionType(node.index.get()) == VarType::DOUBLE) {
2238 std::string intIdx = allocReg();
2239 emit2("mov", intIdx, idx);
2240 if (isReg(idx) && !isParmReg(idx))
2241 freeReg(idx);
2242 idx = intIdx;
2243 }
2244
2245 std::string elemIndex;
2246 if (isReg(idx) && !isParmReg(idx)) {
2247 elemIndex = idx;
2248 } else {
2249 elemIndex = allocReg();
2250 emit2("mov", elemIndex, idx);
2251 }
2252 if (info->lowerBound != 0)
2253 emit2("sub", elemIndex, std::to_string(info->lowerBound));
2254
2255 std::string base;
2256 if (auto var = dynamic_cast<VariableNode *>(node.base.get())) {
2257 std::string mangled = findMangledArrayName(var->name);
2258 base = ensurePtrBase(storageSymbolFor(mangled));
2259 } else if (auto field = dynamic_cast<FieldAccessNode *>(node.base.get())) {
2260 // Cross-unit array access: UnitName.arrayName[index]
2261 if (auto *baseVar = dynamic_cast<VariableNode *>(field->recordExpr.get());
2262 baseVar && isImportedUnit(baseVar->name)) {
2263 std::string qualifiedName = baseVar->name + "." + field->fieldName;
2264 base = ensurePtrBase(qualifiedName);
2265 } else {
2266 field->recordExpr->accept(*this);
2267 std::string recPtr = popValue();
2268 std::string recType = getVarRecordTypeNameFromExpr(field->recordExpr.get());
2269 auto ofs_sz = getRecordFieldOffsetAndSize(recType, field->fieldName);
2270 int fieldOffset = ofs_sz.first;
2271
2272 std::string arrayPtr = allocTempPtr();
2273 emit2("mov", arrayPtr, recPtr);
2274 emit2("add", arrayPtr, std::to_string(fieldOffset));
2275 base = arrayPtr;
2276 }
2277 } else if (dynamic_cast<ArrayAccessNode *>(node.base.get())) {
2278 node.base->accept(*this);
2279 base = popValue();
2280 } else {
2281 throw std::runtime_error("Unsupported array base in access");
2282 }
2283
2284 if (info->elementIsArray || isRecordTypeName(info->elementType)) {
2285 std::string offsetBytes = allocReg();
2286 emit2("mov", offsetBytes, elemIndex);
2287 emit2("mul", offsetBytes, std::to_string(info->elementSize));
2288
2289 std::string elemPtr = allocTempPtr();
2290 emit2("mov", elemPtr, base);
2291 emit2("add", elemPtr, offsetBytes);
2292 pushValue(elemPtr);
2293
2294 if (elemIndex != idx && isReg(idx) && !isParmReg(idx))
2295 freeReg(idx);
2296 freeReg(elemIndex);
2297 freeReg(offsetBytes);
2298 return;
2299 }
2300
2301 VarType elemType = getExpressionType(&node);
2302 std::string dst;
2303 if (elemType == VarType::DOUBLE)
2304 dst = allocFloatReg();
2305 else if (elemType == VarType::PTR ||
2306 elemType == VarType::STRING)
2307 dst = allocTempPtr();
2308 else
2309 dst = allocReg();
2310
2311 emit4("load", dst, base, elemIndex, std::to_string(info->elementSize));
2312 pushValue(dst);
2313
2314 if (elemIndex != idx && isReg(idx) && !isParmReg(idx))
2315 freeReg(idx);
2316 freeReg(elemIndex);
2317 }
2318
2320 // Typically handled within other nodes
2321 }
2322
2324 RecordTypeInfo info;
2325 int offset = 0;
2326
2327 auto lc = [](std::string s) {
2328 std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c) { return std::tolower(c); });
2329 return s;
2330 };
2331
2332 auto resolveTypeName = [&](std::string t) {
2333 t = lc(t);
2334 std::unordered_set<std::string> seen;
2335 while (typeAliases.count(t) && !seen.count(t)) {
2336 seen.insert(t);
2337 t = lc(typeAliases.at(t));
2338 }
2339 return t;
2340 };
2341
2342 for (auto &f : node.recordType->fields) {
2343 auto &fieldDecl = static_cast<VarDeclNode &>(*f);
2344 bool fieldIsArray = false;
2345 ArrayInfo arrInfo{};
2346 std::string fieldTypeName = "unknown";
2347
2348 if (std::holds_alternative<std::unique_ptr<ASTNode>>(fieldDecl.type)) {
2349 auto &typeNode = std::get<std::unique_ptr<ASTNode>>(fieldDecl.type);
2350 if (auto *at = dynamic_cast<ArrayTypeNode *>(typeNode.get())) {
2351 fieldIsArray = true;
2352 arrInfo = buildArrayInfoFromNode(at);
2353 fieldTypeName = "array";
2354 } else if (dynamic_cast<RecordTypeNode *>(typeNode.get())) {
2355 fieldTypeName = "record";
2356 } else if (auto *pt = dynamic_cast<PointerTypeNode *>(typeNode.get())) {
2357 fieldTypeName = "^" + lc(pt->baseTypeName);
2358 } else if (auto *st = dynamic_cast<SimpleTypeNode *>(typeNode.get())) {
2359 fieldTypeName = resolveTypeName(st->typeName);
2360 }
2361 } else {
2362 fieldTypeName = resolveTypeName(std::get<std::string>(fieldDecl.type));
2363 }
2364
2365 for (const auto &rawName : fieldDecl.identifiers) {
2366 RecordField rf;
2367 rf.name = lc(rawName);
2368 rf.offset = offset;
2369 rf.isArray = fieldIsArray;
2370
2371 if (fieldIsArray) {
2372 rf.arrayInfo = arrInfo;
2373 rf.typeName = "array";
2375 } else if (isRecordTypeName(fieldTypeName)) {
2376 rf.typeName = fieldTypeName;
2377 auto it = recordTypes.find(fieldTypeName);
2378 rf.size = (it != recordTypes.end()) ? it->second.size : 8;
2379 } else {
2380 rf.typeName = fieldTypeName;
2382 }
2383
2384 info.nameToIndex[rf.name] = (int)info.fields.size();
2385 info.fields.push_back(rf);
2386 offset += rf.size;
2387 }
2388 }
2389
2390 // Handle variant part (if present)
2391 if (!node.recordType->variantTagName.empty()) {
2392 // Add the tag field as a regular integer field
2393 RecordField tagField;
2394 tagField.name = lc(node.recordType->variantTagName);
2395 tagField.typeName = resolveTypeName(node.recordType->variantTagType);
2396 tagField.offset = offset;
2397 tagField.size = getTypeSizeByName(tagField.typeName);
2398 tagField.isArray = false;
2399 info.nameToIndex[tagField.name] = (int)info.fields.size();
2400 info.fields.push_back(tagField);
2401 offset += tagField.size;
2402
2403 // All variant arms share the same starting offset (union semantics)
2404 int variantStart = offset;
2405 int maxArmSize = 0;
2406
2407 for (auto &arm : node.recordType->variantArms) {
2408 int armOffset = variantStart;
2409 int armSize = 0;
2410
2411 for (auto &f : arm.fields) {
2412 auto &fieldDecl = static_cast<VarDeclNode &>(*f);
2413 bool fieldIsArray = false;
2414 ArrayInfo arrInfo{};
2415 std::string fieldTypeName = "unknown";
2416
2417 if (std::holds_alternative<std::unique_ptr<ASTNode>>(fieldDecl.type)) {
2418 auto &typeNode = std::get<std::unique_ptr<ASTNode>>(fieldDecl.type);
2419 if (auto *at = dynamic_cast<ArrayTypeNode *>(typeNode.get())) {
2420 fieldIsArray = true;
2421 arrInfo = buildArrayInfoFromNode(at);
2422 fieldTypeName = "array";
2423 } else if (dynamic_cast<RecordTypeNode *>(typeNode.get())) {
2424 fieldTypeName = "record";
2425 } else if (auto *pt = dynamic_cast<PointerTypeNode *>(typeNode.get())) {
2426 fieldTypeName = "^" + lc(pt->baseTypeName);
2427 } else if (auto *st = dynamic_cast<SimpleTypeNode *>(typeNode.get())) {
2428 fieldTypeName = resolveTypeName(st->typeName);
2429 }
2430 } else {
2431 fieldTypeName = resolveTypeName(std::get<std::string>(fieldDecl.type));
2432 }
2433
2434 for (const auto &rawName : fieldDecl.identifiers) {
2435 RecordField rf;
2436 rf.name = lc(rawName);
2437 rf.offset = armOffset;
2438 rf.isArray = fieldIsArray;
2439
2440 if (fieldIsArray) {
2441 rf.arrayInfo = arrInfo;
2442 rf.typeName = "array";
2444 } else if (isRecordTypeName(fieldTypeName)) {
2445 rf.typeName = fieldTypeName;
2446 auto it = recordTypes.find(fieldTypeName);
2447 rf.size = (it != recordTypes.end()) ? it->second.size : 8;
2448 } else {
2449 rf.typeName = fieldTypeName;
2451 }
2452
2453 info.nameToIndex[rf.name] = (int)info.fields.size();
2454 info.fields.push_back(rf);
2455 armOffset += rf.size;
2456 armSize += rf.size;
2457 }
2458 }
2459
2460 if (armSize > maxArmSize)
2461 maxArmSize = armSize;
2462 }
2463
2464 offset = variantStart + maxArmSize;
2465 }
2466
2467 info.size = offset;
2468 std::string recordName = lc(node.name);
2469 recordTypes[recordName] = info;
2470 }
2471
2473 for (auto &typeDecl : node.typeDeclarations) {
2474 typeDecl->accept(*this);
2475 }
2476 }
2477
2479 // Typically handled within other nodes
2480 }
2481
2483 std::string typeKey = resolveTypeName(lc(node.name));
2484 ArrayInfo info = buildArrayInfoFromNode(node.arrayType.get());
2485 arrayInfo[typeKey] = std::move(info);
2486 }
2487
2489 if (node.expr) {
2490 std::string retVal = eval(node.expr.get());
2492 if (!currentFunctionName.empty()) {
2494 }
2495
2496 if (rt == VarType::STRING || rt == VarType::PTR || rt == VarType::RECORD) {
2497 emit2("mov", "arg0", retVal);
2498 } else if (rt == VarType::DOUBLE) {
2499 emit2("mov", "xmm0", retVal);
2500 } else {
2501 emit2("mov", "rax", retVal);
2502 }
2503
2504 functionSetReturn = true;
2505 if (isReg(retVal) && !isParmReg(retVal))
2506 freeReg(retVal);
2507 }
2508 std::string endLabel = getCurrentEndLabel();
2509 if (!endLabel.empty()) {
2510 emit1("jmp", endLabel);
2511 } else {
2512 emit("ret");
2513 }
2514 }
2515
2517 if (!loopEndLabels.empty()) {
2518 emit1("jmp", loopEndLabels.back());
2519 }
2520 }
2521
2523 if (!loopContinueLabels.empty()) {
2524 emit1("jmp", loopContinueLabels.back());
2525 }
2526 }
2527
2529 pushValue("0");
2530 }
2531
2533 // Handled in VarDeclNode visitor
2534 }
2535
2537 // Evaluate the pointer expression
2538 std::string ptrVal = eval(node.pointer.get());
2539
2540 // Determine the pointed-to type
2541 VarType derefType = VarType::INT;
2542 if (auto varNode = dynamic_cast<VariableNode *>(node.pointer.get())) {
2543 derefType = getPointerDerefType(varNode->name);
2544 }
2545
2546 std::string base = ensurePtrBase(ptrVal);
2547
2548 if (derefType == VarType::DOUBLE) {
2549 std::string result = allocFloatReg();
2550 emit4("load", result, base, "0", "1");
2551 pushValue(result);
2552 } else if (derefType == VarType::PTR || derefType == VarType::STRING) {
2553 std::string result = allocTempPtr();
2554 emit4("load", result, base, "0", "1");
2555 pushValue(result);
2556 } else {
2557 std::string result = allocReg();
2558 emit4("load", result, base, "0", "1");
2559 pushValue(result);
2560 }
2561
2562 if (isReg(ptrVal) && !isParmReg(ptrVal) && ptrVal != base)
2563 freeReg(ptrVal);
2564 }
2565
2567 if (auto varNode = dynamic_cast<VariableNode *>(node.operand.get())) {
2568 std::string mangled = findMangledName(varNode->name);
2569 std::string result = allocTempPtr();
2570 emit2("lea", result, mangled);
2571 pushValue(result);
2572 } else {
2573 throw std::runtime_error("@ operator requires a variable operand");
2574 }
2575 }
2576
2578 std::string recType = getVarRecordTypeName(node.recordVar);
2579 recType = resolveTypeName(lc(recType));
2580 if (recType.empty()) {
2581 throw std::runtime_error("'with' variable '" + node.recordVar + "' is not a record type");
2582 }
2583 auto it = recordTypes.find(recType);
2584 if (it == recordTypes.end()) {
2585 throw std::runtime_error("Unknown record type '" + recType + "' in with statement");
2586 }
2587 std::unordered_map<std::string, std::string> fieldMap;
2588 for (const auto &[fieldName, idx] : it->second.nameToIndex) {
2589 fieldMap[fieldName] = node.recordVar;
2590 }
2591 withFieldScopes.push_back(std::move(fieldMap));
2592 if (node.statement)
2593 node.statement->accept(*this);
2594 withFieldScopes.pop_back();
2595 }
2596
2598 std::string lbl;
2599 auto it = gotoLabels.find(node.label);
2600 if (it != gotoLabels.end()) {
2601 lbl = it->second;
2602 } else {
2603 lbl = newLabel("GOTO_" + node.label);
2604 gotoLabels[node.label] = lbl;
2605 }
2606 emit1("jmp", lbl);
2607 }
2608
2610 std::string lbl;
2611 auto it = gotoLabels.find(node.label);
2612 if (it != gotoLabels.end()) {
2613 lbl = it->second;
2614 } else {
2615 lbl = newLabel("GOTO_" + node.label);
2616 gotoLabels[node.label] = lbl;
2617 }
2618 emitLabel(lbl);
2619 if (node.statement)
2620 node.statement->accept(*this);
2621 }
2622
2624 // Build a runtime set value: allocate 256 bytes, zero-fill, then set each element
2625 std::string setPtr = allocTempPtr();
2626 emit3("alloc", setPtr, "8", "256");
2627
2628 // Zero-fill
2629 std::string loopIdx = allocReg();
2630 std::string loopStart = newLabel("SET_LIT_INIT");
2631 std::string loopEnd = newLabel("SET_LIT_INIT_END");
2632 emit2("mov", loopIdx, "0");
2633 emitLabel(loopStart);
2634 emit2("cmp", loopIdx, "256");
2635 emit1("jge", loopEnd);
2636 emit4("store", "0", setPtr, loopIdx, "8");
2637 emit2("add", loopIdx, "1");
2638 emit1("jmp", loopStart);
2639 emitLabel(loopEnd);
2640 freeReg(loopIdx);
2641
2642 // Set each element
2643 for (auto &elem : node.elements) {
2644 std::string elemVal = eval(elem.get());
2645 emit4("store", "1", setPtr, elemVal, "8");
2646 if (isReg(elemVal) && !isParmReg(elemVal))
2647 freeReg(elemVal);
2648 }
2649 pushValue(setPtr);
2650 }
2651
2653 // Set type declarations handled via type aliases; nothing to emit here
2654 }
2655
2657 auto lc = [](std::string s) {
2658 std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c) { return std::tolower(c); });
2659 return s;
2660 };
2661 std::string typeName = lc(node.typeName);
2662 std::vector<std::string> valueNames;
2663 for (int i = 0; i < static_cast<int>(node.values.size()); ++i) {
2664 std::string valName = lc(node.values[i]);
2665 enumConstants[valName] = i;
2666 compileTimeConstants[valName] = std::to_string(i);
2667 valueNames.push_back(valName);
2668 }
2669 enumTypes[typeName] = std::move(valueNames);
2670 // Treat enum type as an alias for integer
2671 typeAliases[typeName] = "integer";
2672 }
2673
2674} // namespace pascal
Abstract base class for all AST nodes.
Definition ast.hpp:26
AST node for an address-of expression (@operand).
Definition ast.hpp:600
std::unique_ptr< ASTNode > operand
operand whose address is taken
Definition ast.hpp:602
AST node for an array element access (arr[index]).
Definition ast.hpp:321
std::unique_ptr< ASTNode > base
array expression
Definition ast.hpp:323
std::unique_ptr< ASTNode > index
index expression
Definition ast.hpp:324
AST node for assigning to an array element (arr[index] := value).
Definition ast.hpp:334
std::string arrayName
array variable name
Definition ast.hpp:336
std::unique_ptr< ASTNode > index
index expression
Definition ast.hpp:337
std::unique_ptr< ASTNode > value
value expression
Definition ast.hpp:338
AST node for a named array variable declaration.
Definition ast.hpp:292
std::string name
array variable name
Definition ast.hpp:294
std::unique_ptr< ArrayTypeNode > arrayType
array type (bounds + element)
Definition ast.hpp:295
AST node for a named array type declaration (type Name = array[...]).
Definition ast.hpp:308
std::string name
type name
Definition ast.hpp:310
std::unique_ptr< ArrayTypeNode > arrayType
the underlying array type
Definition ast.hpp:311
AST node for an array type (array[lower..upper] of elementType).
Definition ast.hpp:275
AST node for an assignment statement (variable := expression).
Definition ast.hpp:249
std::unique_ptr< ASTNode > variable
target of the assignment
Definition ast.hpp:251
std::unique_ptr< ASTNode > expression
value expression
Definition ast.hpp:252
AST node for a binary operator expression.
Definition ast.hpp:438
std::unique_ptr< ASTNode > left
left operand
Definition ast.hpp:459
OpType operator_
operator kind
Definition ast.hpp:460
std::unique_ptr< ASTNode > right
right operand
Definition ast.hpp:461
AST node representing a block (declarations + compound statement).
Definition ast.hpp:79
std::unique_ptr< CompoundStmtNode > compoundStatement
the begin..end compound statement
Definition ast.hpp:82
std::vector< std::unique_ptr< ASTNode > > declarations
variable / type / const / procedure / function declarations
Definition ast.hpp:81
AST node for a boolean literal (true / false).
Definition ast.hpp:549
bool value
the boolean value
Definition ast.hpp:551
AST node for a break statement.
Definition ast.hpp:65
AST node for a case statement.
Definition ast.hpp:400
std::unique_ptr< ASTNode > expression
selector expression
Definition ast.hpp:411
std::unique_ptr< ASTNode > elseStatement
optional else/otherwise branch
Definition ast.hpp:413
std::vector< std::unique_ptr< CaseBranch > > branches
case branches
Definition ast.hpp:412
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
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
bool isUnit
true when generating code for a unit (object output)
Definition icode.hpp:186
BuiltinFunctionRegistry builtinRegistry
registry of built-in function handlers
Definition icode.hpp:889
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
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 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 emitLabel(const std::string &label)
Definition icode.hpp:528
std::unordered_map< std::string, std::string > typeAliases
Definition icode.hpp:200
std::string allocFloatReg()
Definition icode.hpp:412
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 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
std::string ensurePtrBase(const std::string &v)
Definition icode.hpp:2171
static std::string lc(std::string s)
Definition icode.hpp:1813
ArrayInfo * getArrayInfoForArrayAccess(ArrayAccessNode *arr)
Definition icode.hpp:2208
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
std::string getPointerBaseTypeName(const std::string &varName) const
Definition icode.hpp:2115
void emit(const std::string &s)
Definition icode.hpp:525
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
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::unordered_map< std::string, FuncInfo > funcSignatures
Definition icode.hpp:269
VarType getVarType(const std::string &name) const
Definition icode.hpp:276
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
std::string currentFunctionName
Definition icode.hpp:332
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
void emit3(const std::string &op, const std::string &a, const std::string &b, const std::string &c)
Definition icode.hpp:537
int getArrayElementSize(const std::string &tIn)
Definition icode.hpp:865
void setVarType(const std::string &name, VarType t)
Definition icode.hpp:305
bool isRecordTypeName(const std::string &t)
Definition icode.hpp:1517
std::vector< std::string > loopEndLabels
Definition icode.hpp:2142
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
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< std::string, std::string > currentParamTypes
Definition icode.hpp:183
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::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
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 name
program name emitted in the output header
Definition icode.hpp:977
std::string currentFunctionReturnSlot
slot variable that holds the return value until FUNC_END
Definition icode.hpp:333
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
int getTypeSizeByName(const std::string &t)
Definition icode.hpp:1522
std::vector< std::string > loopContinueLabels
Definition icode.hpp:2143
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
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
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
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::unordered_map< std::string, std::string > pointerBaseType
Definition icode.hpp:2003
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
std::vector< std::unique_ptr< ASTNode > > statements
ordered list of statements
Definition ast.hpp:239
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
std::vector< std::string > values
ordered list of enum value identifiers
Definition ast.hpp:630
std::string typeName
the enum type name
Definition ast.hpp:629
AST node for an exit / halt statement.
Definition ast.hpp:44
std::unique_ptr< ASTNode > expr
optional exit-code expression
Definition ast.hpp:48
AST node for accessing a record field (record.field).
Definition ast.hpp:726
std::string fieldName
name of the accessed field
Definition ast.hpp:729
std::unique_ptr< ASTNode > recordExpr
expression yielding a record
Definition ast.hpp:728
AST node for a for loop (for var := start to/downto end do stmt).
Definition ast.hpp:379
std::string variable
loop variable name
Definition ast.hpp:381
bool isDownto
true if downto, false if to
Definition ast.hpp:384
std::unique_ptr< ASTNode > statement
loop body
Definition ast.hpp:385
std::unique_ptr< ASTNode > startValue
initial value expression
Definition ast.hpp:382
std::unique_ptr< ASTNode > endValue
final value expression
Definition ast.hpp:383
AST node for a function call expression.
Definition ast.hpp:500
std::vector< std::unique_ptr< ASTNode > > arguments
actual arguments
Definition ast.hpp:503
std::string name
function name
Definition ast.hpp:502
AST node for a function declaration.
Definition ast.hpp:194
std::string returnType
return type name
Definition ast.hpp:198
std::shared_ptr< ASTNode > block
function body block
Definition ast.hpp:199
std::vector< std::unique_ptr< ASTNode > > parameters
formal parameters
Definition ast.hpp:197
std::string name
function name
Definition ast.hpp:196
AST node for a goto statement.
Definition ast.hpp:681
std::string label
target label (numeric string)
Definition ast.hpp:683
AST node for an if-then-else statement.
Definition ast.hpp:350
std::unique_ptr< ASTNode > elseStatement
optional else branch
Definition ast.hpp:354
std::unique_ptr< ASTNode > thenStatement
then branch
Definition ast.hpp:353
std::unique_ptr< ASTNode > condition
boolean condition
Definition ast.hpp:352
AST node for a label definition (label: statement).
Definition ast.hpp:692
std::string label
the label (numeric string)
Definition ast.hpp:694
std::unique_ptr< ASTNode > statement
labeled statement
Definition ast.hpp:695
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
std::string value
textual representation of the number
Definition ast.hpp:526
AST node for a formal parameter list entry.
Definition ast.hpp:223
std::string type
parameter type name
Definition ast.hpp:226
std::vector< std::string > identifiers
parameter names
Definition ast.hpp:225
AST node for a pointer dereference (ptr^).
Definition ast.hpp:589
std::unique_ptr< ASTNode > pointer
expression yielding a pointer
Definition ast.hpp:591
AST node for a pointer type declaration (^BaseType).
Definition ast.hpp:578
AST node for a procedure call statement.
Definition ast.hpp:425
std::vector< std::unique_ptr< ASTNode > > arguments
actual arguments
Definition ast.hpp:428
std::string name
procedure name
Definition ast.hpp:427
AST node for a procedure declaration.
Definition ast.hpp:209
std::shared_ptr< ASTNode > block
procedure body block
Definition ast.hpp:213
std::string name
procedure name
Definition ast.hpp:211
AST node for a complete program (program name + block).
Definition ast.hpp:98
std::string name
program identifier
Definition ast.hpp:100
std::vector< std::string > uses
modules imported via 'uses' clause
Definition ast.hpp:102
std::unique_ptr< BlockNode > block
the program body
Definition ast.hpp:101
AST node for a named record declaration.
Definition ast.hpp:658
std::string name
record type name
Definition ast.hpp:660
std::unique_ptr< RecordTypeNode > recordType
the record body
Definition ast.hpp:661
AST node for a record type (record ... end).
Definition ast.hpp:646
AST node for a repeat..until loop.
Definition ast.hpp:611
std::vector< std::unique_ptr< ASTNode > > statements
loop body statements
Definition ast.hpp:613
std::unique_ptr< ASTNode > condition
termination condition
Definition ast.hpp:614
AST node for a set literal [val1, val2, ...].
Definition ast.hpp:705
std::vector< std::unique_ptr< ASTNode > > elements
set elements
Definition ast.hpp:707
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
AST node for a string literal.
Definition ast.hpp:538
std::string value
the string contents (without quotes)
Definition ast.hpp:540
AST node for a type alias (type NewName = ExistingType).
Definition ast.hpp:161
std::string typeName
the new type name
Definition ast.hpp:163
std::string baseType
the existing type being aliased
Definition ast.hpp:164
AST node for a type declaration section.
Definition ast.hpp:149
std::vector< std::unique_ptr< ASTNode > > typeDeclarations
list of individual type declarations
Definition ast.hpp:151
AST node for a unary operator expression.
Definition ast.hpp:477
Operator operator_
the unary operator
Definition ast.hpp:486
@ NOT
logical not
Definition ast.hpp:483
@ MINUS
unary -
Definition ast.hpp:482
@ PLUS
unary +
Definition ast.hpp:481
std::unique_ptr< ASTNode > operand
the operand expression
Definition ast.hpp:487
AST node for a Pascal unit (separately compiled module).
Definition ast.hpp:116
std::string name
unit identifier
Definition ast.hpp:118
std::vector< std::string > uses
modules/units imported via 'uses' clause
Definition ast.hpp:119
std::vector< std::unique_ptr< ASTNode > > implDecls
implementation declarations (proc/func/var/const/type)
Definition ast.hpp:121
std::vector< std::unique_ptr< ASTNode > > interfaceDecls
exported procedure/function forward declarations
Definition ast.hpp:120
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
std::string name
variable name
Definition ast.hpp:515
AST node for a while loop (while cond do stmt).
Definition ast.hpp:366
std::unique_ptr< ASTNode > statement
loop body
Definition ast.hpp:369
std::unique_ptr< ASTNode > condition
loop condition
Definition ast.hpp:368
AST node for a with statement (with record do statement).
Definition ast.hpp:668
std::unique_ptr< ASTNode > statement
body statement
Definition ast.hpp:671
std::string recordVar
record variable name
Definition ast.hpp:670
Definition ast.cpp:9
VarType
Enumeration of variable / expression value types.
Definition icode.hpp:72
@ CHAR
character value
Definition icode.hpp:76
@ INT
integer
Definition icode.hpp:73
@ STRING
string value
Definition icode.hpp:75
@ UNKNOWN
unresolved type
Definition icode.hpp:80
@ RECORD
record (struct)
Definition icode.hpp:77
@ PTR
generic pointer
Definition icode.hpp:78
@ DOUBLE
real (floating-point)
Definition icode.hpp:74
Pascal-to-MXVM code generator using the Visitor pattern over the Pascal AST.
Metadata describing a Pascal array's bounds, element type, and sizing.
Definition icode.hpp:29
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
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
A field within a record type definition.
Definition icode.hpp:1981
bool isArray
true if the field is an array
Definition icode.hpp:1986
std::string name
field name
Definition icode.hpp:1982
ArrayInfo arrayInfo
array descriptor (when isArray)
Definition icode.hpp:1987
int size
field size in bytes
Definition icode.hpp:1985
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
@ MEMORY
value is in a memory slot
Definition icode.hpp:361