20 static const std::unordered_set<std::string> nativeModules = {
"io",
"std",
"string",
"sdl",
"strlib"};
21 for (
const auto &mod : node.
uses) {
24 else if (nativeModules.count(mod))
30 node.
block->accept(*
this);
38 static const std::unordered_set<std::string> nativeModules = {
"io",
"std",
"string",
"sdl",
"strlib"};
39 for (
const auto &mod : node.
uses) {
42 else if (nativeModules.count(mod))
56 if (
auto *funcDecl =
dynamic_cast<FuncDeclNode *
>(decl.get())) {
60 for (
auto &p : funcDecl->parameters) {
62 for (
size_t i = 0; i < pn->identifiers.size(); ++i) {
122 std::string typeName;
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);
129 if (
auto *arrayTypeNode =
dynamic_cast<ArrayTypeNode *
>(typeNode.get())) {
131 arrayInfo[mangledName] = std::move(info);
144 std::string lenName = mangledName +
"_dynlen";
152 if (currentScope.empty())
159 std::to_string(
arrayInfo[mangledName].elementSize),
160 std::to_string(
arrayInfo[mangledName].size));
163 if (currentScope.empty())
171 if (
auto *ptrTypeNode =
dynamic_cast<PointerTypeNode *
>(typeNode.get())) {
196 std::string loopStart =
newLabel(
"SET_INIT");
197 std::string loopEnd =
newLabel(
"SET_INIT_END");
198 emit2(
"mov", loopIdx,
"0");
200 emit2(
"cmp", loopIdx,
"256");
201 emit1(
"jge", loopEnd);
203 emit2(
"add", loopIdx,
"1");
204 emit1(
"jmp", loopStart);
209 if (currentScope.empty())
216 if (
auto *simpleTypeNode =
dynamic_cast<SimpleTypeNode *
>(typeNode.get())) {
217 typeName = simpleTypeNode->typeName;
222 typeName =
"unknown";
232 if (normalizedTypeName.rfind(
"set of ", 0) == 0) {
241 std::string loopStart =
newLabel(
"SET_INIT");
242 std::string loopEnd =
newLabel(
"SET_INIT_END");
243 emit2(
"mov", loopIdx,
"0");
245 emit2(
"cmp", loopIdx,
"256");
246 emit1(
"jge", loopEnd);
248 emit2(
"add", loopIdx,
"1");
249 emit1(
"jmp", loopStart);
253 if (currentScope.empty())
261 if (normalizedTypeName ==
"file" || normalizedTypeName ==
"text") {
268 std::string fileNameVar = mangledName +
"_filename";
278 auto recordTypeIt =
recordTypes.find(normalizedTypeName);
283 int recordSize = recordTypeIt->second.size;
289 emit3(
"alloc",
slotVar(slot), std::to_string(recordSize),
"1");
298 auto aliasIt =
arrayInfo.find(normalizedTypeName);
300 arrayInfo[mangledName] = aliasIt->second;
308 std::to_string(
arrayInfo[mangledName].elementSize),
309 std::to_string(
arrayInfo[mangledName].size));
312 if (currentScope.empty())
319 if (!normalizedTypeName.empty() && normalizedTypeName[0] ==
'^') {
320 std::string baseType = normalizedTypeName.substr(1);
347 std::string procLower = node.
name;
348 std::transform(procLower.begin(), procLower.end(), procLower.begin(),
349 [](
unsigned char c) { return std::tolower(c); });
351 if (procLower ==
"new") {
353 throw std::runtime_error(
"new() requires exactly one pointer argument");
356 std::string baseName;
358 bool baseIsDirectPointer =
false;
360 if (
auto *v =
dynamic_cast<VariableNode *
>(fieldNode->recordExpr.get())) {
364 fieldNode->recordExpr->accept(*
this);
366 baseIsDirectPointer =
true;
373 throw std::runtime_error(
"new(): unknown record type: " + recType);
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);
380 const auto &fieldInfo = recTypeIt->second.fields[it->second];
381 const int byteOffset = fieldInfo.offset;
383 std::string fieldTypeName = fieldInfo.typeName;
385 if (!fieldTypeName.empty() && fieldTypeName[0] ==
'^') {
391 elemSize = rit->second.size;
396 emit3(
"alloc", tmp, std::to_string(elemSize),
"1");
398 std::string basePtr = baseIsDirectPointer ? baseName :
ensurePtrBase(baseName);
399 emit4(
"store", tmp, basePtr, std::to_string(byteOffset),
"1");
415 if (!et.empty() && et[0] ==
'^') {
421 elemSize = rit->second.size;
425 std::string idx =
eval(arrNode->index.get());
428 emit2(
"mov", intIdx, idx);
434 std::string elemIndex;
439 emit2(
"mov", elemIndex, idx);
445 if (
auto var =
dynamic_cast<VariableNode *
>(arrNode->base.get())) {
449 arrNode->base->accept(*
this);
454 emit3(
"alloc", tmp, std::to_string(elemSize),
"1");
467 throw std::runtime_error(
"new() argument must be a variable, record field, or array element");
479 elemSize = it->second.size;
483 emit3(
"alloc", sym, std::to_string(elemSize), std::to_string(allocCount));
487 if (procLower ==
"dispose") {
489 throw std::runtime_error(
"dispose() requires exactly one pointer argument");
492 std::string baseName;
494 bool baseIsDirectPointer =
false;
496 if (
auto *v =
dynamic_cast<VariableNode *
>(fieldNode->recordExpr.get())) {
500 fieldNode->recordExpr->accept(*
this);
502 baseIsDirectPointer =
true;
509 throw std::runtime_error(
"dispose(): unknown record type: " + recType);
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);
516 const auto &fieldInfo = recTypeIt->second.fields[it->second];
517 const int byteOffset = fieldInfo.offset;
519 std::string basePtr = baseIsDirectPointer ? baseName :
ensurePtrBase(baseName);
521 emit4(
"load", tmp, basePtr, std::to_string(byteOffset),
"1");
534 throw std::runtime_error(
"dispose(): unknown array: " +
getArrayNameFromBase(arrNode->base.get()));
536 std::string idx =
eval(arrNode->index.get());
539 emit2(
"mov", intIdx, idx);
545 std::string elemIndex;
550 emit2(
"mov", elemIndex, idx);
556 if (
auto var =
dynamic_cast<VariableNode *
>(arrNode->base.get())) {
560 arrNode->base->accept(*
this);
578 throw std::runtime_error(
"dispose() argument must be a variable, record field, or array element");
587 if (procLower ==
"setlength" && node.
arguments.size() == 2) {
590 throw std::runtime_error(
"SetLength: first argument must be a dynamic array variable");
596 if (it ==
arrayInfo.end() || !it->second.isDynamic)
597 throw std::runtime_error(
"SetLength: " + varNode->name +
" is not a dynamic array");
599 std::string arrMangled = it->first;
606 emit3(
"realloc", sym, std::to_string(it->second.elementSize), newSize);
621 handler->generate(*
this, procLower, node.
arguments);
625 std::vector<std::string> evaluated_args;
626 std::vector<VarType> argTypes;
629 std::string argVal =
eval(arg.get());
630 evaluated_args.push_back(argVal);
635 std::vector<std::string> targetRegs(evaluated_args.size());
636 size_t intRegIdx = 1;
637 size_t ptrRegIdx = 0;
638 size_t floatRegIdx = 0;
640 for (
size_t i = 0; i < evaluated_args.size(); ++i) {
654 for (
size_t i = 0; i < evaluated_args.size(); ++i) {
655 if (evaluated_args[i].empty() || targetRegs[i].empty())
657 if (evaluated_args[i] == targetRegs[i])
659 for (
size_t j = 0; j < evaluated_args.size(); ++j) {
662 if (evaluated_args[j] == targetRegs[i]) {
665 emit2(
"mov", spill, evaluated_args[j]);
666 evaluated_args[j] = spill;
672 std::vector<std::string> savedRegs;
675 bool isEvalArg =
false;
676 for (
const auto &ea : evaluated_args) {
677 if (ea ==
registers[i]) { isEvalArg =
true;
break; }
679 if (isEvalArg)
continue;
682 for (
const auto &sr : savedRegs)
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]);
697 std::string prefix =
"PROC_";
700 std::string label = prefix + mangledName;
703 label = eit->second +
"." + label;
710 emit1(
"call", label);
719 for (
auto rit = savedRegs.rbegin(); rit != savedRegs.rend(); ++rit)
722 for (
const auto &arg : evaluated_args)
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) {
739 if (fnLower ==
"low") {
742 emit2(
"mov", r, std::to_string(it->second.isDynamic ? 0 : it->second.lowerBound));
746 if (it->second.isDynamic) {
748 std::string arrMangled = (it ==
arrayInfo.find(mangled)) ? mangled : varNode->name;
751 throw std::runtime_error(
"Length/High: dynamic array has no companion length variable: " + varNode->name);
752 std::string lenSym =
slotVar(lenIt->second);
754 emit2(
"mov", r, lenSym);
755 if (fnLower ==
"high") {
756 emit2(
"sub", r,
"1");
763 if (fnLower ==
"length") {
764 emit2(
"mov", r, std::to_string(it->second.size));
766 emit2(
"mov", r, std::to_string(it->second.upperBound));
777 if (handler->generateWithResult(*
this, fnLower, node.
arguments))
781 std::vector<std::string> evaluated_args;
782 std::vector<VarType> argTypes;
785 std::string argVal =
eval(arg.get());
786 evaluated_args.push_back(argVal);
791 std::vector<std::string> targetRegs(evaluated_args.size());
792 size_t intRegIdx = 1;
793 size_t ptrRegIdx = 0;
794 size_t floatRegIdx = 0;
796 for (
size_t i = 0; i < evaluated_args.size(); ++i) {
810 for (
size_t i = 0; i < evaluated_args.size(); ++i) {
811 if (evaluated_args[i].empty() || targetRegs[i].empty())
813 if (evaluated_args[i] == targetRegs[i])
815 for (
size_t j = 0; j < evaluated_args.size(); ++j) {
818 if (evaluated_args[j] == targetRegs[i]) {
820 emit2(
"mov", spill, evaluated_args[j]);
821 evaluated_args[j] = spill;
827 std::vector<std::string> savedRegs;
830 bool isEvalArg =
false;
831 for (
const auto &ea : evaluated_args) {
832 if (ea ==
registers[i]) { isEvalArg =
true;
break; }
834 if (isEvalArg)
continue;
837 for (
const auto &sr : savedRegs)
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]);
852 std::string label =
"FUNC_" + mangledName;
855 label = eit->second +
"." + label;
862 emit1(
"call", label);
871 for (
auto rit = savedRegs.rbegin(); rit != savedRegs.rend(); ++rit)
877 std::string resultLocation;
880 std::string objPrefix = (eit !=
externalFuncs.end()) ? eit->second +
"." :
"";
883 emit2(
"mov", resultLocation, objPrefix +
"xmm0");
886 emit2(
"mov", resultLocation, objPrefix +
"arg0");
889 emit2(
"mov", resultLocation, objPrefix +
"rax");
894 for (
const auto &arg : evaluated_args)
910 for (
size_t i = 0; i < pn->identifiers.size(); ++i) {
921 path.push_back(node.
name);
925 if (
auto blockNode =
dynamic_cast<BlockNode *
>(node.
block.get())) {
926 for (
auto &decl : blockNode->declarations) {
946 path.push_back(node.
name);
950 if (
auto blockNode =
dynamic_cast<BlockNode *
>(node.
block.get())) {
951 for (
auto &decl : blockNode->declarations) {
962 auto lc = [](std::string s) { std::transform(s.begin(), s.end(), s.begin(),
963 [](
unsigned char c){ return std::tolower(c); });
return s; };
968 auto lc = [](std::string s) {
969 std::transform(s.begin(), s.end(), s.begin(),
970 [](
unsigned char c) { return std::tolower(c); });
974 std::string
id = idRaw;
976 std::string t =
lc(node.
type);
980 }
else if (t ==
"integer" || t ==
"boolean") {
983 }
else if (t ==
"real") {
986 }
else if (!t.empty() && t[0] ==
'^') {
1019 if (
auto v =
dynamic_cast<VariableNode *
>(stmt.get())) {
1022 std::string prefix =
"PROC_";
1025 std::string label = prefix + mangled;
1028 label = eit->second +
"." + label;
1029 emit1(
"call", label);
1039 stmt->accept(*
this);
1051 std::string elseL =
newLabel(
"ELSE");
1052 std::string endL =
newLabel(
"ENDIF");
1056 emit2(
"cmp", condResult,
"0");
1076 std::string start =
newLabel(
"WHILE");
1077 std::string end =
newLabel(
"ENDWHILE");
1082 emit2(
"cmp", c,
"0");
1088 emit1(
"jmp", start);
1107 std::string loopStartLabel =
newLabel(
"FOR");
1108 std::string loopEndLabel =
newLabel(
"ENDFOR");
1109 std::string continueLabel =
newLabel(
"FOR_CONTINUE");
1114 std::string endCmp = endVal;
1120 emit1(
"jl", loopEndLabel);
1122 emit1(
"jg", loopEndLabel);
1131 emit1(
"jmp", loopStartLabel);
1151 std::string val =
eval(node.
left.get());
1152 std::string foundLabel =
newLabel(
"IN_FOUND");
1153 std::string endLabel =
newLabel(
"IN_END");
1156 for (
auto &elem : setNode->elements) {
1157 std::string elemVal =
eval(elem.get());
1158 emit2(
"cmp", val, elemVal);
1159 emit1(
"je", foundLabel);
1163 emit2(
"mov", result,
"0");
1164 emit1(
"jmp", endLabel);
1166 emit2(
"mov", result,
"1");
1175 std::string val =
eval(node.
left.get());
1176 std::string setVal =
eval(node.
right.get());
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);
1187 emit2(
"mov", result,
"0");
1199 auto isSetExpr = [&](
ASTNode *n) ->
bool {
1211 (isSetExpr(node.
left.get()) || isSetExpr(node.
right.get()))) {
1213 std::string leftSet =
eval(node.
left.get());
1214 std::string rightSet =
eval(node.
right.get());
1218 emit3(
"alloc", resultSet,
"8",
"256");
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");
1228 emit2(
"mov", idx,
"0");
1230 emit2(
"cmp", idx,
"256");
1231 emit1(
"jge", loopEnd);
1232 emit4(
"load", lb, leftSet, idx,
"8");
1233 emit4(
"load", rb, rightSet, idx,
"8");
1237 emit2(
"cmp", lb,
"0");
1238 emit1(
"jne", storeBit);
1239 emit2(
"cmp", rb,
"0");
1240 emit1(
"jne", storeBit);
1241 emit1(
"jmp", storeZero);
1244 emit2(
"cmp", lb,
"0");
1245 emit1(
"je", storeZero);
1246 emit2(
"cmp", rb,
"0");
1247 emit1(
"je", storeZero);
1248 emit1(
"jmp", storeBit);
1251 emit2(
"cmp", lb,
"0");
1252 emit1(
"je", storeZero);
1253 emit2(
"cmp", rb,
"0");
1254 emit1(
"jne", storeZero);
1255 emit1(
"jmp", storeBit);
1258 emit4(
"store",
"1", resultSet, idx,
"8");
1259 std::string nextLabel =
newLabel(
"SET_OP_NEXT");
1260 emit1(
"jmp", nextLabel);
1262 emit4(
"store",
"0", resultSet, idx,
"8");
1264 emit2(
"add", idx,
"1");
1265 emit1(
"jmp", loopStart);
1281 std::string left =
eval(node.
left.get());
1282 std::string right =
eval(node.
right.get());
1285 emit(
"return " + len1);
1287 emit(
"return " + len2);
1288 emit2(
"mov", totalLen, len1);
1289 emit2(
"add", totalLen, len2);
1290 emit2(
"add", totalLen,
"1");
1293 emit3(
"alloc", result_str,
"1", totalLen);
1295 emit_invoke(
"strncat", {result_str, right, len2});
1309 if (!folded.empty()) {
1321 auto evalOperand = [&](
ASTNode *n) -> std::string {
1330 std::string left = evalOperand(node.
left.get());
1331 std::string right = evalOperand(node.
right.get());
1338 needsFloatOp =
true;
1347 left = std::to_string((
long long)std::stod(left));
1349 right = std::to_string((
long long)std::stod(right));
1350 needsFloatOp =
false;
1362 emit2(
"mov", dst, left);
1376 auto emitBinary = [&](
const char *op) {
1379 if (leftIsUsableReg)
1383 emit2(
"mov", dst, left);
1385 emit2(op, dst, right);
1435 throw std::runtime_error(
"Unsupported binary operator");
1444 emit2(
"mov", t,
"0");
1484 auto fit = it->find(
lc(node.
name));
1485 if (fit != it->end()) {
1486 auto recExpr = std::make_unique<VariableNode>(fit->second);
1488 visit(syntheticField);
1522 visit(syntheticCall);
1554 if (
auto strNode =
dynamic_cast<StringNode *
>(assignment->value.get())) {
1575 std::string varType = isFloat ?
"float" :
"int";
1588 }
catch (
const std::runtime_error &) {
1589 std::string valueReg =
eval(assignment->value.get());
1590 int slot =
newSlotFor(assignment->identifier);
1592 setVarType(assignment->identifier, exprType);
1594 std::string varLocation =
slotVar(slot);
1595 emit2(
"mov", varLocation, valueReg);
1603 std::string startLabel =
newLabel(
"REPEAT");
1604 std::string endLabel =
newLabel(
"UNTIL");
1605 std::string continueLabel =
newLabel(
"REPEAT_CONTINUE");
1613 stmt->accept(*
this);
1616 emit2(
"cmp", condResult,
"0");
1617 emit1(
"je", startLabel);
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++) {
1635 for (
auto &value : branch->values) {
1636 std::string caseValue;
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]);
1643 caseValue =
eval(value.get());
1646 caseValue =
eval(value.get());
1648 emit2(
"cmp", switchExpr, caseValue);
1649 emit1(
"je", branchLabels[i]);
1655 emit1(
"jmp", elseLabel);
1657 emit1(
"jmp", endLabel);
1658 for (
size_t i = 0; i < node.
branches.size(); i++) {
1661 node.
branches[i]->statement->accept(*
this);
1662 emit1(
"jmp", endLabel);
1680 std::string elementType;
1682 elementType = simpleType->typeName;
1684 elementType =
"array";
1686 elementType =
"record";
1688 elementType =
"^" + pt->baseTypeName;
1690 elementType =
"integer";
1695 int size = upperBound - lowerBound + 1;
1697 int elementSize = 8;
1702 elementSize = it->second.size;
1715 arrayInfo[mangledName] = std::move(info);
1724 std::to_string(elementSize),
1725 std::to_string(size));
1727 if (currentScope.empty())
1737 std::string qualifiedName = v->name +
"." + node.
fieldName;
1750 visit(syntheticCall);
1762 emit2(
"mov", dst, qualifiedName);
1768 std::string baseName;
1769 std::string recType;
1770 bool baseIsDirectPointer =
false;
1778 baseIsDirectPointer =
true;
1783 baseIsDirectPointer =
true;
1788 if (recType.empty()) {
1789 throw std::runtime_error(
"Unknown record type: (empty) for field " + node.
fieldName);
1794 throw std::runtime_error(
"Unknown record type: " + recType +
" for field " + 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);
1804 const auto &fieldInfo = recInfo.fields[it->second];
1805 const int byteOffset = fieldInfo.offset;
1807 std::string basePtr = baseIsDirectPointer ? baseName :
ensurePtrBase(baseName);
1811 emit2(
"mov", p, basePtr);
1812 emit2(
"add", p, std::to_string(byteOffset));
1827 emit4(
"load", dst, basePtr, std::to_string(byteOffset),
"1");
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);
1840 visit(syntheticAssign);
1850 std::string ptrVal =
eval(deref->pointer.get());
1853 emit4(
"store", rhs, base,
"0",
"1");
1870 std::string idx =
eval(arr->index.get());
1874 emit2(
"mov", intIdx, idx);
1880#ifdef MXVM_BOUNDS_CHECK
1894 std::string elemIndex;
1899 emit2(
"mov", elemIndex, idx);
1915 emit2(
"mov", f, rhs);
1921 emit2(
"mov", ir, rhs);
1928 if (
auto var =
dynamic_cast<VariableNode *
>(arr->base.get())) {
1931 }
else if (
auto field =
dynamic_cast<FieldAccessNode *
>(arr->base.get())) {
1933 if (
auto *baseVar =
dynamic_cast<VariableNode *
>(field->recordExpr.get());
1935 std::string qualifiedName = baseVar->name +
"." + field->fieldName;
1938 field->recordExpr->accept(*
this);
1942 int fieldOffset = ofs_sz.first;
1945 emit2(
"mov", arrayPtr, recPtr);
1946 emit2(
"add", arrayPtr, std::to_string(fieldOffset));
1950 arr->base->accept(*
this);
1954 throw std::runtime_error(
"Unsupported array base in assignment");
1969 if (
auto *v =
dynamic_cast<VariableNode *
>(field->recordExpr.get());
1971 std::string qualifiedName = v->name +
"." + field->fieldName;
1973 emit2(
"mov", qualifiedName, rhs);
1981 std::string baseName;
1982 std::string recType;
1983 bool baseIsDirectPointer =
false;
1985 if (
auto *v =
dynamic_cast<VariableNode *
>(field->recordExpr.get())) {
1989 field->recordExpr->accept(*
this);
1991 baseIsDirectPointer =
true;
1996 if (recType.empty()) {
1997 throw std::runtime_error(
"Unknown record type for field assignment: " + field->fieldName);
2002 throw std::runtime_error(
"Unknown record type: " + recType +
" for field " + field->fieldName);
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);
2012 const auto &fieldInfo = recInfo.fields[it->second];
2013 const int byteOffset = fieldInfo.offset;
2015 std::string basePtr = baseIsDirectPointer ? baseName :
ensurePtrBase(baseName);
2025 emit2(
"mov", intReg, rhs);
2031 emit2(
"mov", fltReg, rhs);
2037 emit4(
"store", rhs, basePtr, std::to_string(byteOffset),
"1");
2041 if (baseIsDirectPointer &&
isReg(basePtr) && !
isParmReg(basePtr))
2051 std::string varName = varPtr->
name;
2057 std::string destBase;
2058 auto sit =
varSlot.find(mangled);
2060 destBase =
slotVar(sit->second);
2068 emit2(
"mov", idx,
"0");
2069 std::string loopLbl =
newLabel(
"set_copy");
2070 std::string endLbl =
newLabel(
"set_copy_end");
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);
2090 if (strNode->value.size() == 1) {
2091 rhs = std::to_string((
int)(
unsigned char)strNode->value[0]);
2104 emit2(
"mov", it->second, rhs);
2137 auto it =
varSlot.find(mangled);
2142 emit2(
"mov", mangled, rhs);
2157 throw std::runtime_error(
"Unknown array: " + node.
arrayName);
2160 std::string value =
eval(node.
value.get());
2161 std::string index =
eval(node.
index.get());
2165 emit2(
"mov", intIndex, index);
2171#ifdef MXVM_BOUNDS_CHECK
2181 std::string elementIndex;
2183 elementIndex = index;
2186 emit2(
"mov", elementIndex, index);
2207 emit2(
"mov", f, value);
2213 emit2(
"mov", i, value);
2219 emit4(
"store", value, base, elementIndex, std::to_string(info.
elementSize));
2232 throw std::runtime_error(
"Unknown array: " + arrayName);
2235 std::string idx =
eval(node.
index.get());
2239 emit2(
"mov", intIdx, idx);
2245 std::string elemIndex;
2250 emit2(
"mov", elemIndex, idx);
2261 if (
auto *baseVar =
dynamic_cast<VariableNode *
>(field->recordExpr.get());
2263 std::string qualifiedName = baseVar->name +
"." + field->fieldName;
2266 field->recordExpr->accept(*
this);
2270 int fieldOffset = ofs_sz.first;
2273 emit2(
"mov", arrayPtr, recPtr);
2274 emit2(
"add", arrayPtr, std::to_string(fieldOffset));
2278 node.
base->accept(*
this);
2281 throw std::runtime_error(
"Unsupported array base in access");
2285 std::string offsetBytes =
allocReg();
2286 emit2(
"mov", offsetBytes, elemIndex);
2290 emit2(
"mov", elemPtr, base);
2291 emit2(
"add", elemPtr, offsetBytes);
2327 auto lc = [](std::string s) {
2328 std::transform(s.begin(), s.end(), s.begin(), [](
unsigned char c) { return std::tolower(c); });
2334 std::unordered_set<std::string> seen;
2344 bool fieldIsArray =
false;
2346 std::string fieldTypeName =
"unknown";
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;
2353 fieldTypeName =
"array";
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())) {
2362 fieldTypeName =
resolveTypeName(std::get<std::string>(fieldDecl.type));
2365 for (
const auto &rawName : fieldDecl.identifiers) {
2385 info.
fields.push_back(rf);
2391 if (!node.
recordType->variantTagName.empty()) {
2396 tagField.
offset = offset;
2400 info.
fields.push_back(tagField);
2401 offset += tagField.
size;
2404 int variantStart = offset;
2407 for (
auto &arm : node.
recordType->variantArms) {
2408 int armOffset = variantStart;
2411 for (
auto &f : arm.fields) {
2413 bool fieldIsArray =
false;
2415 std::string fieldTypeName =
"unknown";
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;
2422 fieldTypeName =
"array";
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())) {
2431 fieldTypeName =
resolveTypeName(std::get<std::string>(fieldDecl.type));
2434 for (
const auto &rawName : fieldDecl.identifiers) {
2454 info.
fields.push_back(rf);
2455 armOffset += rf.
size;
2460 if (armSize > maxArmSize)
2461 maxArmSize = armSize;
2464 offset = variantStart + maxArmSize;
2468 std::string recordName =
lc(node.
name);
2474 typeDecl->accept(*
this);
2490 std::string retVal =
eval(node.
expr.get());
2497 emit2(
"mov",
"arg0", retVal);
2499 emit2(
"mov",
"xmm0", retVal);
2501 emit2(
"mov",
"rax", retVal);
2509 if (!endLabel.empty()) {
2510 emit1(
"jmp", endLabel);
2550 emit4(
"load", result, base,
"0",
"1");
2554 emit4(
"load", result, base,
"0",
"1");
2558 emit4(
"load", result, base,
"0",
"1");
2570 emit2(
"lea", result, mangled);
2573 throw std::runtime_error(
"@ operator requires a variable operand");
2580 if (recType.empty()) {
2581 throw std::runtime_error(
"'with' variable '" + node.
recordVar +
"' is not a record type");
2585 throw std::runtime_error(
"Unknown record type '" + recType +
"' in with statement");
2587 std::unordered_map<std::string, std::string> fieldMap;
2588 for (
const auto &[fieldName, idx] : it->second.nameToIndex) {
2626 emit3(
"alloc", setPtr,
"8",
"256");
2630 std::string loopStart =
newLabel(
"SET_LIT_INIT");
2631 std::string loopEnd =
newLabel(
"SET_LIT_INIT_END");
2632 emit2(
"mov", loopIdx,
"0");
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);
2644 std::string elemVal =
eval(elem.get());
2645 emit4(
"store",
"1", setPtr, elemVal,
"8");
2657 auto lc = [](std::string s) {
2658 std::transform(s.begin(), s.end(), s.begin(), [](
unsigned char c) { return std::tolower(c); });
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]);
2667 valueNames.push_back(valName);
2669 enumTypes[typeName] = std::move(valueNames);
Abstract base class for all AST nodes.
AST node for an address-of expression (@operand).
std::unique_ptr< ASTNode > operand
operand whose address is taken
AST node for an array element access (arr[index]).
std::unique_ptr< ASTNode > base
array expression
std::unique_ptr< ASTNode > index
index expression
AST node for assigning to an array element (arr[index] := value).
std::string arrayName
array variable name
std::unique_ptr< ASTNode > index
index expression
std::unique_ptr< ASTNode > value
value expression
AST node for a named array variable declaration.
std::string name
array variable name
std::unique_ptr< ArrayTypeNode > arrayType
array type (bounds + element)
AST node for a named array type declaration (type Name = array[...]).
std::string name
type name
std::unique_ptr< ArrayTypeNode > arrayType
the underlying array type
AST node for an array type (array[lower..upper] of elementType).
AST node for an assignment statement (variable := expression).
std::unique_ptr< ASTNode > variable
target of the assignment
std::unique_ptr< ASTNode > expression
value expression
AST node for a binary operator expression.
std::unique_ptr< ASTNode > left
left operand
OpType operator_
operator kind
std::unique_ptr< ASTNode > right
right operand
AST node representing a block (declarations + compound statement).
std::unique_ptr< CompoundStmtNode > compoundStatement
the begin..end compound statement
std::vector< std::unique_ptr< ASTNode > > declarations
variable / type / const / procedure / function declarations
AST node for a boolean literal (true / false).
bool value
the boolean value
AST node for a break statement.
AST node for a case statement.
std::unique_ptr< ASTNode > expression
selector expression
std::unique_ptr< ASTNode > elseStatement
optional else/otherwise branch
std::vector< std::unique_ptr< CaseBranch > > branches
case branches
void freeReg(const std::string ®)
std::unordered_map< std::string, std::vector< std::string > > enumTypes
Enum type name → ordered list of value names.
std::unordered_map< std::string, std::string > realConstants
ArrayInfo buildArrayInfoFromNode(ArrayTypeNode *atn)
std::string newLabel(const std::string &prefix="L")
std::unordered_set< std::string > setVars
Set of variable names that are set types (allocated as 256-byte bitsets).
std::string findMangledFuncName(const std::string &name, bool isProc) const
std::unordered_set< std::string > allocatedPtrs
void visit(ProgramNode &node) override
void emitDynArrayBoundsCheck(const std::string &idxReg, const std::string &lenSym)
Emit bounds-check code for a dynamic array access.
void setSlotType(int slot, VarType t)
int getPointerElementSize(const std::string &varName)
std::vector< std::pair< FuncDeclNode *, std::vector< std::string > > > deferredFuncs
void initializeBuiltins()
Register all built-in function handlers (IO, Std, SDL, String).
std::unordered_map< std::string, std::string > varRecordType
void pushValue(const std::string &v)
bool isIntegerLiteral(const std::string &s) const
bool isUnit
true when generating code for a unit (object output)
BuiltinFunctionRegistry builtinRegistry
registry of built-in function handlers
bool isParmReg(const std::string &name) const
std::vector< std::string > objectDeps
unit/object dependencies for section object
std::unordered_map< std::string, std::vector< std::string > > recordsToFreeInScope
std::string internString(const std::string &val)
bool isRealNumber(const std::string &s) const
void pushLogicalOr(const std::string &a, const std::string &b)
std::vector< std::string > scopeHierarchy
std::unordered_map< std::string, std::string > fileVarNames
Maps file variable name to companion filename variable name.
void emitLabel(const std::string &label)
std::unordered_map< std::string, std::string > typeAliases
std::string allocFloatReg()
void updateDataSectionInitialValue(const std::string &varName, const std::string &type, const std::string &value)
const std::vector< std::string > ptrRegisters
std::unordered_map< std::string, ArrayInfo > arrayInfo
bool tryGetConstNumeric(const std::string &name, std::string &out)
VarType getPointerDerefType(const std::string &varName)
std::string ensurePtrBase(const std::string &v)
static std::string lc(std::string s)
ArrayInfo * getArrayInfoForArrayAccess(ArrayAccessNode *arr)
std::unordered_map< std::string, std::string > externalFuncs
maps function/proc name to source unit name
std::string getCurrentScopeName() const
std::string getPointerBaseTypeName(const std::string &varName) const
void emit(const std::string &s)
void emit2(const std::string &op, const std::string &a, const std::string &b)
std::string allocTempPtr(const std::string &forScope="")
void emit1(const std::string &op, const std::string &a)
void pushCmpResult(const std::string &a, const std::string &b, const char *jop)
std::unordered_map< std::string, RecordTypeInfo > recordTypes
bool isFloatLiteral(const std::string &s) const
bool isFloatReg(const std::string &name) const
VarType getExpressionType(ASTNode *node)
std::string mangleVariableName(const std::string &varName) const
std::vector< std::string > floatRegisters
std::string getVarRecordTypeNameFromExpr(ASTNode *expr)
std::string foldNumeric(ASTNode *n)
std::vector< std::unordered_map< std::string, std::string > > withFieldScopes
Stack of active with scopes: each entry maps unqualified field name -> record variable name.
std::unordered_map< std::string, FuncInfo > funcSignatures
VarType getVarType(const std::string &name) const
std::unordered_map< std::string, int > varSlot
std::vector< bool > ptrRegInUse
std::unordered_map< std::string, bool > declaredProcs
void pushLogicalAnd(const std::string &a, const std::string &b)
std::string currentFunctionName
bool isTempPtr(const std::string &name) const
std::string findMangledArrayName(const std::string &name) const
std::unordered_set< std::string > fileVars
Set of variable names that are file types (pointer to FILE).
std::unordered_map< std::string, std::string > compileTimeConstants
bool generatingDeferredCode
void emit3(const std::string &op, const std::string &a, const std::string &b, const std::string &c)
int getArrayElementSize(const std::string &tIn)
void setVarType(const std::string &name, VarType t)
bool isRecordTypeName(const std::string &t)
std::vector< std::string > loopEndLabels
std::vector< bool > regInUse
void emit4(const std::string &op, const std::string &a, const std::string &b, const std::string &c, const std::string &d)
std::vector< std::string > currentFuncLocalSlots
std::string findMangledName(const std::string &name) const
void emit_invoke(const std::string &funcName, const std::vector< std::string > ¶ms)
Emit an invoke instruction calling a named function with parameters.
std::unordered_map< std::string, std::string > currentParamTypes
std::unordered_map< std::string, std::vector< std::string > > functionScopedArrays
std::unordered_map< std::string, bool > declaredFuncs
std::map< std::string, std::string > currentParamLocations
CodeGenVisitor()
Construct and initialise registers and float register pool.
std::pair< int, int > getRecordFieldOffsetAndSize(const std::string &recType, const std::string &field)
std::string evaluateConstantExpression(ASTNode *node)
std::vector< std::string > globalArrays
void allocateRecordFieldArrays(const std::string &recordVarName, const std::string &recordTypeName)
std::string name
program name emitted in the output header
std::string currentFunctionReturnSlot
slot variable that holds the return value until FUNC_END
std::string resolveTypeName(std::string t) const
std::string eval(ASTNode *n)
std::unordered_map< std::string, int > dynArrayLenSlot
mangled array name → slot of companion length variable
int getTypeSizeByName(const std::string &t)
std::vector< std::string > loopContinueLabels
bool isReg(const std::string &name) const
int newSlotFor(const std::string &name)
std::string getCurrentEndLabel() const
std::unordered_map< std::string, int > enumConstants
Enum value → ordinal mapping (lowercased name → integer).
std::string storageSymbolFor(const std::string &mangled)
std::vector< std::string > registers
std::string slotVar(int slot) const
std::unordered_set< std::string > escapedTempPtrs
std::string coerceToIntImmediate(const std::string &v)
void emitArrayBoundsCheck(const std::string &idxReg, int lower, int upper)
Emit bounds-check code for a static array access.
void initializeFloatRegisters()
bool isImportedUnit(const std::string &name) const
Check whether a name refers to an imported unit.
std::unordered_map< std::string, std::string > gotoLabels
Maps user-declared goto label numbers to generated MXVM labels.
std::string getArrayNameFromBase(ASTNode *base)
VarType getTypeFromString(const std::string &typeStr)
std::string ensureFloatConstSymbol(const std::string &value)
std::string getVarRecordTypeName(const std::string &varName)
std::vector< std::string > prolog
std::vector< std::pair< ProcDeclNode *, std::vector< std::string > > > deferredProcs
std::unordered_map< std::string, std::string > pointerBaseType
std::set< std::string > usedModules
void recordLocation(const std::string &var, ValueLocation loc)
AST node for a compound statement (begin ... end).
std::vector< std::unique_ptr< ASTNode > > statements
ordered list of statements
AST node for constant declarations (const id = value).
std::vector< std::unique_ptr< ConstAssignment > > assignments
list of constant assignments
AST node for a continue statement.
AST node for an empty statement (no-op).
AST node for an enumerated type declaration (type Color = (Red, Green, Blue)).
std::vector< std::string > values
ordered list of enum value identifiers
std::string typeName
the enum type name
AST node for an exit / halt statement.
std::unique_ptr< ASTNode > expr
optional exit-code expression
AST node for accessing a record field (record.field).
std::string fieldName
name of the accessed field
std::unique_ptr< ASTNode > recordExpr
expression yielding a record
AST node for a for loop (for var := start to/downto end do stmt).
std::string variable
loop variable name
bool isDownto
true if downto, false if to
std::unique_ptr< ASTNode > statement
loop body
std::unique_ptr< ASTNode > startValue
initial value expression
std::unique_ptr< ASTNode > endValue
final value expression
AST node for a function call expression.
std::vector< std::unique_ptr< ASTNode > > arguments
actual arguments
std::string name
function name
AST node for a function declaration.
std::string returnType
return type name
std::shared_ptr< ASTNode > block
function body block
std::vector< std::unique_ptr< ASTNode > > parameters
formal parameters
std::string name
function name
AST node for a goto statement.
std::string label
target label (numeric string)
AST node for an if-then-else statement.
std::unique_ptr< ASTNode > elseStatement
optional else branch
std::unique_ptr< ASTNode > thenStatement
then branch
std::unique_ptr< ASTNode > condition
boolean condition
AST node for a label definition (label: statement).
std::string label
the label (numeric string)
std::unique_ptr< ASTNode > statement
labeled statement
AST node for the nil literal.
AST node for an integer or real numeric literal.
bool isReal
true if real (floating-point) literal
std::string value
textual representation of the number
AST node for a formal parameter list entry.
std::string type
parameter type name
std::vector< std::string > identifiers
parameter names
AST node for a pointer dereference (ptr^).
std::unique_ptr< ASTNode > pointer
expression yielding a pointer
AST node for a pointer type declaration (^BaseType).
AST node for a procedure call statement.
std::vector< std::unique_ptr< ASTNode > > arguments
actual arguments
std::string name
procedure name
AST node for a procedure declaration.
std::shared_ptr< ASTNode > block
procedure body block
std::string name
procedure name
AST node for a complete program (program name + block).
std::string name
program identifier
std::vector< std::string > uses
modules imported via 'uses' clause
std::unique_ptr< BlockNode > block
the program body
AST node for a named record declaration.
std::string name
record type name
std::unique_ptr< RecordTypeNode > recordType
the record body
AST node for a record type (record ... end).
AST node for a repeat..until loop.
std::vector< std::unique_ptr< ASTNode > > statements
loop body statements
std::unique_ptr< ASTNode > condition
termination condition
AST node for a set literal [val1, val2, ...].
std::vector< std::unique_ptr< ASTNode > > elements
set elements
AST node for a set type declaration (set of <base type>).
AST node for a simple (named) type reference.
AST node for a string literal.
std::string value
the string contents (without quotes)
AST node for a type alias (type NewName = ExistingType).
std::string typeName
the new type name
std::string baseType
the existing type being aliased
AST node for a type declaration section.
std::vector< std::unique_ptr< ASTNode > > typeDeclarations
list of individual type declarations
AST node for a unary operator expression.
Operator operator_
the unary operator
std::unique_ptr< ASTNode > operand
the operand expression
AST node for a Pascal unit (separately compiled module).
std::string name
unit identifier
std::vector< std::string > uses
modules/units imported via 'uses' clause
std::vector< std::unique_ptr< ASTNode > > implDecls
implementation declarations (proc/func/var/const/type)
std::vector< std::unique_ptr< ASTNode > > interfaceDecls
exported procedure/function forward declarations
AST node for variable declarations (var id1, id2 : type).
std::vector< std::string > identifiers
declared variable names
std::variant< std::string, std::unique_ptr< ASTNode > > type
type (simple name or complex type node)
AST node for a variable reference.
std::string name
variable name
AST node for a while loop (while cond do stmt).
std::unique_ptr< ASTNode > statement
loop body
std::unique_ptr< ASTNode > condition
loop condition
AST node for a with statement (with record do statement).
std::unique_ptr< ASTNode > statement
body statement
std::string recordVar
record variable name
VarType
Enumeration of variable / expression value types.
@ DOUBLE
real (floating-point)
Pascal-to-MXVM code generator using the Visitor pattern over the Pascal AST.
Metadata describing a Pascal array's bounds, element type, and sizing.
bool isDynamic
true for dynamic arrays declared as array of <type> (no compile-time bounds)
std::string elementType
element type name (e.g. "integer", "real")
bool elementIsArray
true if element type is itself an array
int lowerBound
declared lower index bound
int size
number of elements
int elementSize
size (bytes) of each element
int upperBound
declared upper index bound
Describes a user-declared function's parameter and return types.
VarType returnType
return type
std::vector< VarType > paramTypes
parameter types in order
A field within a record type definition.
bool isArray
true if the field is an array
std::string name
field name
ArrayInfo arrayInfo
array descriptor (when isArray)
int size
field size in bytes
std::string typeName
field type name
int offset
byte offset within the record
Complete description of a record type.
std::unordered_map< std::string, int > nameToIndex
field name -> index into fields
int size
total record size in bytes
std::vector< RecordField > fields
ordered field list
@ MEMORY
value is in a memory slot