18#include <unordered_map>
19#include <unordered_set>
22#define MXVM_BOUNDS_CHECK
45 elementArray = std::make_unique<ArrayInfo>(*other.elementArray);
96 virtual bool canHandle(
const std::string &funcName)
const = 0;
98 virtual void generate(
CodeGenVisitor &visitor,
const std::string &funcName,
const std::vector<std::unique_ptr<ASTNode>> &arguments) = 0;
113 std::vector<std::unique_ptr<BuiltinFunctionHandler>>
handlers;
124 if (h->canHandle(funcName))
133 bool canHandle(
const std::string &funcName)
const override;
134 void generate(
CodeGenVisitor &visitor,
const std::string &funcName,
const std::vector<std::unique_ptr<ASTNode>> &arguments)
override;
141 bool canHandle(
const std::string &funcName)
const override;
142 void generate(
CodeGenVisitor &visitor,
const std::string &funcName,
const std::vector<std::unique_ptr<ASTNode>> &arguments)
override;
148 bool canHandle(
const std::string &funcName)
const override;
149 void generate(
CodeGenVisitor &visitor,
const std::string &funcName,
const std::vector<std::unique_ptr<ASTNode>> &arguments)
override;
157 bool canHandle(
const std::string &funcName)
const override;
158 void generate(
CodeGenVisitor &visitor,
const std::string &funcName,
const std::vector<std::unique_ptr<ASTNode>> &arguments)
override;
165 bool canHandle(
const std::string &funcName)
const override;
166 void generate(
CodeGenVisitor &visitor,
const std::string &funcName,
const std::vector<std::unique_ptr<ASTNode>> &arguments)
override;
204 std::vector<std::pair<ProcDeclNode *, std::vector<std::string>>>
deferredProcs;
213 bool hasDot =
false, hasExp =
false;
217 if (c ==
'e' || c ==
'E')
220 if (!hasDot && !hasExp)
223 std::strtod(s.c_str(), &end);
224 return end && *end ==
'\0';
227 std::string
mangleWithScope(
const std::string &baseName,
const std::vector<std::string> &scopePath)
const {
228 if (scopePath.empty() || scopePath.size() <= 1) {
232 for (
size_t i = 1; i < scopePath.size(); ++i) {
253 if (df.first->name == currentScope) {
260 return (isFunc ?
"FUNC_" :
"PROC_") +
mangleWithScope(currentScope, parentScope);
274 std::string
funcLabel(
const char *prefix,
const std::string &
name) {
return std::string(prefix) +
name; }
278 auto slotIt =
varSlot.find(mangledName);
280 auto typeIt =
slotToType.find(slotIt->second);
282 return typeIt->second;
292 long long x =
static_cast<long long>(std::stod(v));
293 return std::to_string(x);
298 long long x =
static_cast<long long>(std::stod(it->second));
299 return std::to_string(x);
316 std::vector<std::string>
registers = {
"rax",
"rbx",
"rcx",
"rdx",
"rsi",
"rdi",
"r8",
"r9",
"r10",
"r11",
"r12",
"r13",
"r14",
"r15"};
318 "arg0",
"arg1",
"arg2",
"arg3",
"arg4",
"arg5",
"arg6",
"arg7",
"arg8",
"arg9"};
324 floatRegisters = {
"xmm0",
"xmm1",
"xmm2",
"xmm3",
"xmm4",
"xmm5",
"xmm6",
"xmm7"};
331 std::vector<std::pair<FuncDeclNode *, std::vector<std::string>>>
deferredFuncs;
368 static bool endsWithColon(
const std::string &s) {
return !s.empty() && s.back() ==
':'; }
376 if (slot < -1 && slot >= -(
int)
registers.size()) {
377 int regIndex = -2 - slot;
378 if (regIndex >= 0 && regIndex < (
int)
registers.size())
382 int regIndex = -100 - slot;
386 return "v" + std::to_string(slot);
390 for (
size_t i = 1; i <
regInUse.size(); ++i)
440 for (
size_t i = 0; i <
registers.size(); ++i)
467 if (!
name.empty() &&
name.find(
"__t") == std::string::npos)
473 std::string
newLabel(
const std::string &prefix =
"L") {
481 emit1(
"free", ptrName);
517 std::string tempName =
"_tmpptr" + std::to_string(
nextTemp++);
525 void emit(
const std::string &s) {
529 void emit1(
const std::string &op,
const std::string &a) {
emit(op +
" " + a); }
530 void emit2(
const std::string &op,
const std::string &a,
const std::string &b) {
535 emit(op +
" " + A +
", " + B);
537 void emit3(
const std::string &op,
const std::string &a,
const std::string &b,
const std::string &c) {
538 instructions.push_back(op +
" " + a +
", " + b +
", " + c);
543 void emit4(
const std::string &op,
const std::string &a,
const std::string &b,
const std::string &c,
const std::string &d) {
emit(op +
" " + a +
", " + b +
", " + c +
", " + d); }
546 std::ostringstream o;
567 if (value.rfind(
"real_const_", 0) == 0)
570 if (p.second == value)
588 if (c.length() == 3 && c[0] ==
'\'' && c[2] ==
'\'')
589 return std::to_string((
int)c[1]);
590 if (c.length() == 4 && c[0] ==
'\'' && c[1] ==
'\\' && c[3] ==
'\'') {
616 return std::to_string(v);
628 throw std::runtime_error(
"Evaluation stack underflow");
637 if (
auto num =
dynamic_cast<NumberNode *
>(n)) {
643 throw std::runtime_error(
"Expression produced no value");
649 void pushTri(
const char *op,
const std::string &a,
const std::string &b) {
653 a.find(
"real_const_") != std::string::npos ||
654 b.find(
"real_const_") != std::string::npos;
656 std::string leftOp = a;
657 std::string rightOp = b;
660 if (
isReg(leftOp) && !mustCopy) {
661 emit2(op, leftOp, rightOp);
669 emit2(
"mov", result, leftOp);
670 emit2(op, result, rightOp);
681 void pushCmpResult(
const std::string &a,
const std::string &b,
const char *jop) {
683 std::string L1 =
newLabel(
"CMP_TRUE");
684 std::string L2 =
newLabel(
"CMP_END");
687 emit2(
"mov", t,
"0");
690 emit2(
"mov", t,
"1");
705 std::string a = aIn, b = bIn;
719 std::string L1 =
newLabel(
"CMP_TRUE");
720 std::string L2 =
newLabel(
"CMP_END");
723 emit2(
"mov", t,
"0");
726 emit2(
"mov", t,
"1");
737 std::string L0 =
newLabel(
"AND_ZERO");
738 std::string L1 =
newLabel(
"AND_END");
739 emit2(
"cmp", a,
"0");
741 emit2(
"cmp", b,
"0");
743 emit2(
"mov", t,
"1");
746 emit2(
"mov", t,
"0");
757 std::string L1 =
newLabel(
"OR_ONE");
758 std::string L2 =
newLabel(
"OR_END");
759 emit2(
"cmp", a,
"0");
761 emit2(
"cmp", b,
"0");
763 emit2(
"mov", t,
"0");
766 emit2(
"mov", t,
"1");
776 for (
size_t i = 1; i <= 6 && i <
registers.size(); ++i)
789 auto &recInfo = recTypeIt->second;
792 for (
const auto &field : recInfo.fields) {
796 std::string fieldArrayName = recordVarName +
"_" + field.name;
804 std::to_string(field.arrayInfo.elementSize),
805 std::to_string(field.arrayInfo.size));
808 emit4(
"store",
slotVar(slot), basePtr, std::to_string(field.offset),
"1");
810 if (currentScope.empty())
818 auto lc = [](std::string s) { std::transform(s.begin(), s.end(), s.begin(),
819 [](
unsigned char c){ return std::tolower(c); });
return s; };
822 std::unordered_set<std::string> seen;
867 if (t ==
"integer" || t ==
"boolean")
873 if (t ==
"string" || t ==
"ptr")
877 return it->second.size;
917 if (
auto *strNode =
dynamic_cast<StringNode *
>(assignment->value.get())) {
919 std::string qualifiedName = unitName +
"." + assignment->identifier;
929 std::string qualifiedName = unitName +
"." + assignment->identifier;
930 std::string val = isFloat ?
realConstants[literalValue] : literalValue;
943 std::string qualifiedName = unitName +
"." + varName;
944 if (std::holds_alternative<std::unique_ptr<ASTNode>>(node.
type)) {
945 auto &typeNode = std::get<std::unique_ptr<ASTNode>>(node.
type);
946 if (
auto *arrayTypeNode =
dynamic_cast<ArrayTypeNode *
>(typeNode.get())) {
950 arrayInfo[qualifiedName] = std::move(info);
955 std::string typeName;
956 if (std::holds_alternative<std::string>(node.
type))
957 typeName = std::get<std::string>(node.
type);
970 builtinRegistry.registerHandler(std::make_unique<IOFunctionHandler>());
971 builtinRegistry.registerHandler(std::make_unique<StdFunctionHandler>());
972 builtinRegistry.registerHandler(std::make_unique<SDLFunctionHandler>());
973 builtinRegistry.registerHandler(std::make_unique<StringFunctionHandler>());
974 builtinRegistry.registerHandler(std::make_unique<FileFunctionHandler>());
1025 for (
size_t i = 0; i <
regInUse.size(); ++i)
1032 if (
auto prog =
dynamic_cast<ProgramNode *
>(root))
1034 if (
auto unit =
dynamic_cast<UnitNode *
>(root))
1041 emit1(
"free", arrayName);
1045 emit1(
"free", recordVar);
1062 std::string scopeName =
"PROC_" + mangledName;
1070 if (!pn->parameters.empty()) {
1071 size_t intParamIndex = 1;
1072 size_t ptrParamIndex = 0;
1073 size_t floatParamIndex = 0;
1075 for (
auto &p_node : pn->parameters) {
1076 if (
auto *param =
dynamic_cast<ParameterNode *
>(p_node.get())) {
1084 for (
const auto &
id : param->identifiers) {
1086 std::string incomingReg;
1098 incomingReg =
registers[intParamIndex++];
1106 if (!incomingReg.empty())
1120 for (
size_t r = 0; r <
regInUse.size(); ++r)
1127 for (
size_t reg_idx = 0; reg_idx <
registers.size(); ++reg_idx) {
1128 if (
registers[reg_idx] == pair.second) {
1136 pn->block->accept(*
this);
1141 emit1(
"free", recVar);
1144 for (
const auto &tp : temp1) {
1162 std::string scopeName =
"FUNC_" + mangledName;
1172 if (!fn->parameters.empty()) {
1173 int intParamIndex = 1;
1174 int ptrParamIndex = 0;
1175 int floatParamIndex = 0;
1178 for (
auto &p_node : fn->parameters) {
1179 if (
auto *param =
dynamic_cast<ParameterNode *
>(p_node.get())) {
1183 for (
const auto &
id : param->identifiers) {
1185 std::string incomingReg;
1188 if (
static_cast<size_t>(ptrParamIndex) <
ptrRegisters.size())
1192 if (
static_cast<size_t>(floatParamIndex) <
floatRegisters.size())
1195 if (
static_cast<size_t>(intParamIndex) <
registers.size())
1196 incomingReg =
registers[intParamIndex++];
1199 if (!incomingReg.empty()) {
1217 for (
size_t r = 0; r <
regInUse.size(); ++r)
1224 for (
size_t reg_idx = 0; reg_idx <
registers.size(); ++reg_idx) {
1225 if (
registers[reg_idx] == pair.second) {
1233 fn->block->accept(*
this);
1253 emit2(
"mov",
"xmm0", zc);
1255 emit2(
"mov",
"rax",
"0");
1260 emit1(
"free", recVar);
1264 for (
const auto &tp : temp1) {
1287 void emit_invoke(
const std::string &funcName,
const std::vector<std::string> ¶ms) {
1288 std::string instruction =
"invoke " + funcName;
1289 std::vector<std::string> tempRegs;
1290 for (
const auto &p : params) {
1294 instruction +=
", " + r;
1295 tempRegs.push_back(r);
1297 instruction +=
", " + p;
1301 for (
const auto &r : tempRegs)
1308 size_t start = (s[0] ==
'-') ? 1 : 0;
1309 if (start >= s.size())
1311 for (
size_t i = start; i < s.size(); ++i)
1312 if (!std::isdigit(
static_cast<unsigned char>(s[i])))
1325 out << (
isUnit ?
"object " :
"program ") <<
name <<
" {\n";
1329 out <<
"\tsection object {\n\t\t";
1340 out <<
"\tsection module {\n ";
1345 out <<
"\t\t" << mod;
1349 out <<
"\tsection data {\n";
1352 out <<
"\t\tint " << reg <<
" = 0\n";
1359 std::set<std::string> temps;
1365 for (
const auto &t : kv.second)
1367 for (
const auto &tempPtr : temps)
1368 out <<
"\t\tptr " << tempPtr <<
" = null\n";
1372 out <<
"\t\tfloat " << constant.first <<
" = " << constant.second <<
"\n";
1374 out <<
"\t\tstring empty_str = \"\"\n";
1378 out <<
"\t\tstring fmt_int = \"%lld \"\n";
1380 out <<
"\t\tstring fmt_str = \"%s \"\n";
1382 out <<
"\t\tstring fmt_chr = \"%c \"\n";
1384 out <<
"\t\tstring fmt_float = \"%.6f \"\n";
1386 out <<
"\t\tstring newline = \"\\n\"\n";
1387 out <<
"\t\tstring input_buffer, 256\n";
1388 for (
int i = 0; i <
nextSlot; ++i) {
1392 std::string varName =
slotVar(i);
1395 out <<
"\t\tstring " << varName <<
", " << bufIt->second <<
"\n";
1399 out <<
"\t\t" << constIt->second.first <<
" " << varName <<
" = " << constIt->second.second <<
"\n";
1402 out <<
"\t\tstring " << varName <<
" = \"\"\n";
1404 out <<
"\t\tptr " << varName <<
" = null\n";
1406 out <<
"\t\tint " << varName <<
" = 0\n";
1408 out <<
"\t\tfloat " << varName <<
" = 0.0\n";
1410 out <<
"\t\tint " << varName <<
" = 0\n";
1414 out <<
"\t\tint " <<
slotVar(i) <<
" = 0\n";
1418 out <<
"\tsection code {\n";
1420 out <<
"\tstart:\n";
1423 out <<
"\t\tcall " << dep <<
".PROC_UNIT_INIT\n";
1426 out <<
"\t\t" << s <<
"\n";
1430 out <<
"\t" << s <<
"\n";
1432 out <<
"\t\t" << s <<
"\n";
1494 std::unordered_map<std::string, std::vector<std::string>>
enumTypes;
1503 if (std::holds_alternative<std::string>(node.
type))
1504 return std::get<std::string>(node.
type);
1505 if (std::holds_alternative<std::unique_ptr<ASTNode>>(node.
type)) {
1512 auto it =
varSlot.find(mangled);
1524 if (base ==
"integer" || base ==
"boolean")
1530 if (base ==
"string" || base ==
"ptr")
1534 return it->second.size;
1540 if (base ==
"integer" || base ==
"boolean")
1544 if (base ==
"string")
1548 if (base ==
"pointer")
1550 if (base ==
"file" || base ==
"text")
1552 if (base.rfind(
"set of ", 0) == 0)
1554 if (!base.empty() && base[0] ==
'^')
1573#ifdef MXVM_BOUNDS_CHECK
1574 std::string L_ok =
newLabel(
"IDX_OK");
1575 std::string L_fail =
newLabel(
"IDX_OOB");
1576 emit2(
"cmp", idxReg, std::to_string(lower));
1577 emit1(
"jl", L_fail);
1578 emit2(
"cmp", idxReg, std::to_string(upper));
1579 emit1(
"jg", L_fail);
1597#ifdef MXVM_BOUNDS_CHECK
1598 std::string L_ok =
newLabel(
"IDX_OK");
1599 std::string L_fail =
newLabel(
"IDX_OOB");
1600 emit2(
"cmp", idxReg,
"0");
1601 emit1(
"jl", L_fail);
1603 emit2(
"mov", tmp, lenSym);
1604 emit2(
"cmp", idxReg, tmp);
1606 emit1(
"jge", L_fail);
1615 if (
dynamic_cast<NilNode *
>(node))
1629 if (
auto varNode =
dynamic_cast<VariableNode *
>(derefNode->pointer.get())) {
1636 if (
auto funcCall =
dynamic_cast<FuncCallNode *
>(node)) {
1637 std::string fnLower =
lc(funcCall->name);
1640 return handler->getReturnType(fnLower);
1643 return it->second.returnType;
1650 auto jt = it->second.nameToIndex.find(
lc(fieldNode->fieldName));
1651 if (jt != it->second.nameToIndex.end()) {
1652 const auto &f = it->second.fields[jt->second];
1666 if (t ==
"integer" || t ==
"boolean")
1672 if (t ==
"string" || t ==
"ptr" ||
1674 (!t.empty() && t[0] ==
'^'))
1685 if (isStrLike(lt) || isStrLike(rt))
1699 if (
auto numNode =
dynamic_cast<NumberNode *
>(node)) {
1700 return numNode->value;
1702 if (
auto varNode =
dynamic_cast<VariableNode *
>(node)) {
1709 throw std::runtime_error(
"Cannot use non-constant variable '" + varNode->name +
"' in a constant expression.");
1714 if (L.empty() || R.empty())
1715 throw std::runtime_error(
"Unsupported node type in constant expression.");
1719 auto toD = [&](
const std::string &s) {
return std::stod(s); };
1720 auto toI = [&](
const std::string &s) {
1721 return isFloatLiteral(s) ?
static_cast<long long>(std::stod(s)) : std::stoll(s);
1726 switch (binOp->operator_) {
1728 result = toD(L) + toD(R);
1731 result = toD(L) - toD(R);
1734 result = toD(L) * toD(R);
1738 throw std::runtime_error(
"division by zero in constant expression");
1739 result = toD(L) / toD(R);
1742 throw std::runtime_error(
"Unsupported operator for floats in constant expression.");
1744 return std::to_string(result);
1747 switch (binOp->operator_) {
1749 result = toI(L) + toI(R);
1752 result = toI(L) - toI(R);
1755 result = toI(L) * toI(R);
1759 throw std::runtime_error(
"division by zero in constant expression");
1760 result = toI(L) / toI(R);
1764 throw std::runtime_error(
"mod by zero in constant expression");
1765 result = toI(L) % toI(R);
1768 throw std::runtime_error(
"Unsupported operator for integers in constant expression.");
1770 return std::to_string(result);
1773 if (
auto un =
dynamic_cast<UnaryOpNode *
>(node)) {
1776 throw std::runtime_error(
"Unsupported node type in constant expression.");
1777 double dv = std::stod(v);
1778 switch (un->operator_) {
1780 return std::to_string(-dv);
1784 throw std::runtime_error(
"Unsupported unary op in constant expression.");
1787 throw std::runtime_error(
"Unsupported node type in constant expression.");
1803 if (
auto simpleType =
dynamic_cast<SimpleTypeNode *
>(typeNode)) {
1804 return simpleType->typeName;
1813 static inline std::string
lc(std::string s) {
1814 std::transform(s.begin(), s.end(), s.begin(),
1815 [](
unsigned char c) { return std::tolower(c); });
1823 std::unordered_set<std::string> seen;
1838 return it->second.size;
1845 for (
int depth = (
int)
scopeHierarchy.size() - 1; depth >= 0; --depth) {
1848 if (lookupMap.count(candidate)) {
1858 for (
int depth = (
int)
scopeHierarchy.size() - 1; depth >= 0; --depth) {
1859 std::string candidate;
1863 for (
int i = 1; i <= depth; ++i) {
1868 candidate +=
"_" +
name;
1870 auto it =
varSlot.find(candidate);
1880 for (
int depth = (
int)
scopeHierarchy.size() - 1; depth >= 1; --depth) {
1881 std::string candidate;
1882 for (
int i = 1; i <= depth; ++i) {
1887 candidate +=
"_" +
name;
1897 size_t i = (s[0] ==
'+' || s[0] ==
'-') ? 1 : 0;
1900 for (; i < s.size(); ++i)
1901 if (!std::isdigit(
static_cast<unsigned char>(s[i])))
1907 if (s.find_first_of(
".eE") == std::string::npos)
1909 char *end =
nullptr;
1910 std::strtod(s.c_str(), &end);
1911 return end && *end ==
'\0';
1931 if (
auto num =
dynamic_cast<NumberNode *
>(n))
1942 if (L.empty() || R.empty())
1944 auto toD = [&](
const std::string &s) ->
double {
return std::stod(s); };
1945 auto toI = [&](
const std::string &s) ->
long long {
1946 return isFloatLiteral(s) ?
static_cast<long long>(std::stod(s)) : std::stoll(s);
1948 switch (bin->operator_) {
1958 throw std::runtime_error(
"division by zero");
1959 return std::to_string(toD(L) / rv);
1962 long long ri = toI(R);
1964 throw std::runtime_error(
"division by zero");
1965 return std::to_string(toI(L) / ri);
1968 long long ri = toI(R);
1970 throw std::runtime_error(
"mod by zero");
1971 return std::to_string(toI(L) % ri);
2007 std::string normalizedField =
lc(field);
2013 auto jt = it->second.nameToIndex.find(normalizedField);
2014 if (jt == it->second.nameToIndex.end())
2017 const auto &f = it->second.fields[jt->second];
2018 return {f.offset, f.size};
2022 auto lc = [](std::string s) {
2023 std::transform(s.begin(), s.end(), s.begin(),
2024 [](
unsigned char c) { return std::tolower(c); });
2027 auto resolveAlias = [&](std::string t) {
2029 std::unordered_set<std::string> seen;
2038 auto resolved = resolveAlias(it->second);
2044 auto resolved = resolveAlias(it2->second);
2049 auto resolved = resolveAlias(it3->second);
2074 auto idxIt = rit->second.nameToIndex.find(
lc(fa->fieldName));
2075 if (idxIt == rit->second.nameToIndex.end())
2078 const auto &f = rit->second.fields[idxIt->second];
2156 return "FUNC_END_" + mangledName;
2161 return "PROC_END_" + mangledName;
2172 auto isNamedPtr = [&](
const std::string &s) ->
bool {
2175 if (s.rfind(
"_tmpptr", 0) == 0)
2181 auto t = jt->second;
2205 throw std::runtime_error(
"Array base is not a simple variable or field");
2221 if (
auto *baseVar =
dynamic_cast<VariableNode *
>(field->recordExpr.get())) {
2223 std::string qualifiedName = baseVar->name +
"." + field->fieldName;
2224 auto it =
arrayInfo.find(qualifiedName);
2233 auto recTypeInfoIt =
recordTypes.find(recTypeName);
2235 auto &recInfo = recTypeInfoIt->second;
2236 auto fieldIndexIt = recInfo.nameToIndex.find(
lc(field->fieldName));
2237 if (fieldIndexIt != recInfo.nameToIndex.end()) {
2238 auto &f = recInfo.fields[fieldIndexIt->second];
2240 return const_cast<ArrayInfo *
>(&f.arrayInfo);
2254 std::string
mxvmOpt(
const std::string &text);
Abstract base class for all AST nodes.
virtual void accept(ASTVisitor &visitor)=0
Accept an ASTVisitor (double-dispatch).
Abstract visitor interface for the AST (double-dispatch).
AST node for an address-of expression (@operand).
AST node for an array element access (arr[index]).
std::unique_ptr< ASTNode > base
array expression
AST node for assigning to an array element (arr[index] := value).
AST node for a named array variable declaration.
AST node for a named array type declaration (type Name = array[...]).
AST node for an array type (array[lower..upper] of elementType).
std::unique_ptr< ASTNode > lowerBound
lower index bound expression
std::unique_ptr< ASTNode > upperBound
upper index bound expression
std::unique_ptr< ASTNode > elementType
element type node
AST node for an assignment statement (variable := expression).
AST node for a binary operator expression.
AST node representing a block (declarations + compound statement).
AST node for a boolean literal (true / false).
AST node for a break statement.
Abstract handler for a group of built-in functions/procedures.
virtual VarType getReturnType(const std::string &funcName) const
Return the VarType produced by funcName.
virtual void generate(CodeGenVisitor &visitor, const std::string &funcName, const std::vector< std::unique_ptr< ASTNode > > &arguments)=0
Emit code for a procedure-style call (no return value used).
virtual bool canHandle(const std::string &funcName) const =0
Return true if this handler implements funcName.
virtual bool generateWithResult(CodeGenVisitor &visitor, const std::string &funcName, const std::vector< std::unique_ptr< ASTNode > > &arguments)
Emit code for a function-style call (result pushed on eval stack); return true if handled.
virtual ~BuiltinFunctionHandler()=default
Registry of BuiltinFunctionHandler instances.
BuiltinFunctionHandler * findHandler(const std::string &funcName)
Find the handler for a given function name.
std::vector< std::unique_ptr< BuiltinFunctionHandler > > handlers
registered handlers
void registerHandler(std::unique_ptr< BuiltinFunctionHandler > handler)
Register a new handler.
AST node for a case statement.
AST visitor that generates MXVM intermediate code.
std::unordered_map< std::string, std::vector< std::string > > tempPtrByScope
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")
bool isPtrReg(const std::string &name) const
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
static bool isPtrLike(VarType t)
bool isUnit
true when generating code for a unit (object output)
friend class StdFunctionHandler
BuiltinFunctionRegistry builtinRegistry
registry of built-in function handlers
std::unordered_map< std::string, VarType > importedVarTypes
var types for imported unit vars
bool isParmReg(const std::string &name) const
std::vector< std::string > objectDeps
unit/object dependencies for section object
friend class FileFunctionHandler
std::string allocPtrReg()
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 addTempPtr(const std::string &v)
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 registerImportedVar(const std::string &unitName, VarDeclNode &node)
Register imported variable metadata from a dependency unit (no alloc emitted).
void emitLabel(const std::string &label)
std::unordered_map< std::string, std::string > typeAliases
friend class IOFunctionHandler
std::string allocFloatReg()
std::vector< std::string > evalStack
int getRecordTypeSize(const std::string &typeName)
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
std::string funcLabel(const char *prefix, const std::string &name)
std::unordered_map< std::string, ValueLocation > valueLocations
bool isIntLiteral(const std::string &s) const
bool isTempVar(const std::string &name) const
bool tryGetConstNumeric(const std::string &name, std::string &out)
VarType getPointerDerefType(const std::string &varName)
void freeTempPtr(const std::string &ptrName)
std::string ensurePtrBase(const std::string &v)
static std::string lc(std::string s)
bool isRealConstSymbol(const std::string &s) const
ArrayInfo * getArrayInfoForArrayAccess(ArrayAccessNode *arr)
std::unordered_map< std::string, VarType > varTypes
std::unordered_map< std::string, std::string > externalFuncs
maps function/proc name to source unit name
std::string getCurrentScopeName() const
void expandFloatRegisters()
std::string getPointerBaseTypeName(const std::string &varName) const
void emit(const std::string &s)
void registerImportedConst(const std::string &unitName, ConstDeclNode &node)
Register imported constant values from a dependency unit.
std::unordered_map< std::string, ArrayInfo > importedArrayInfo
array metadata for imported unit vars
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
void writeTo(std::ostream &out) const
Write the complete MXVM program to a stream.
void pushTri(const char *op, const std::string &a, const std::string &b)
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::string getTypeString(ASTNode *typeNode)
static bool endsWithColon(const std::string &s)
std::unordered_map< std::string, FuncInfo > funcSignatures
void freeFloatReg(const std::string ®)
VarType getVarType(const std::string &name) const
std::vector< Scope > scopeStack
std::map< std::string, size_t > bufferStringVars
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)
friend class SDLFunctionHandler
std::string currentFunctionName
std::string generateRealConstantName()
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
std::map< std::string, std::pair< std::string, std::string > > constInitialValues
std::vector< bool > tempPtrInUse
friend class MathFunctionHandler
bool generatingDeferredCode
void emit3(const std::string &op, const std::string &a, const std::string &b, const std::string &c)
bool isRegisterSlot(int slot) const
int getArrayElementSize(const std::string &tIn)
void setVarType(const std::string &name, VarType t)
void releaseTempPtr(const std::string &ptrName)
bool isRecordTypeName(const std::string &t)
std::vector< std::string > loopEndLabels
void emitFree(const std::string &s)
Emit a free instruction for an allocated pointer.
std::vector< std::string > instructions
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
virtual ~CodeGenVisitor()=default
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< int, std::string > slotToName
std::unordered_map< std::string, std::string > currentParamTypes
bool isStringVar(const std::string &name) const
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)
void generate(ASTNode *root)
Generate MXVM code for the entire AST.
std::unordered_set< std::string > usedRealConstants
std::string mangleWithScope(const std::string &baseName, const std::vector< std::string > &scopePath) const
void pushFloatCmpResult(const std::string &aIn, const std::string &bIn, const char *jop)
std::vector< std::string > globalArrays
void allocateRecordFieldArrays(const std::string &recordVarName, const std::string &recordTypeName)
std::string emptyString()
std::vector< std::pair< std::string, std::string > > stringLiterals
std::unordered_set< std::string > usedStrings
std::string name
program name emitted in the output header
std::string escapeStringForMxvm(const std::string &raw) const
void freePtrReg(const std::string ®)
std::string currentFunctionReturnSlot
slot variable that holds the return value until FUNC_END
std::string getTypeString(const VarDeclNode &node)
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
void registerExternalFunc(const std::string &funcName, const std::string &unitName)
Register a function or procedure as originating from an external unit.
int getTypeSizeByName(const std::string &t)
std::vector< std::string > loopContinueLabels
std::vector< bool > floatRegInUse
bool isReg(const std::string &name) const
int newSlotFor(const std::string &name)
std::string getCurrentEndLabel() const
void markAllocatedPtr(const std::string &p)
Mark a pointer name as allocated (will be freed at scope end).
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.
std::unordered_map< int, VarType > slotToType
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)
friend class StringFunctionHandler
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::string convertCharLiteral(const std::string &c)
std::vector< std::string > allTempPtrs
bool isArrayTypeName(const std::string &t) const
std::unordered_map< std::string, std::string > pointerBaseType
std::unordered_set< std::string > importedUnitNames
names of units imported via uses clause
std::set< std::string > usedModules
void recordLocation(const std::string &var, ValueLocation loc)
AST node for a compound statement (begin ... end).
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)).
AST node for an exit / halt statement.
AST node for accessing a record field (record.field).
Built-in handler for Pascal file I/O procedures (assign, reset, rewrite, append, close,...
VarType getReturnType(const std::string &funcName) const override
Return the VarType produced by funcName.
bool canHandle(const std::string &funcName) const override
Return true if this handler implements funcName.
bool generateWithResult(CodeGenVisitor &visitor, const std::string &funcName, const std::vector< std::unique_ptr< ASTNode > > &arguments) override
Emit code for a function-style call (result pushed on eval stack); return true if handled.
void generate(CodeGenVisitor &visitor, const std::string &funcName, const std::vector< std::unique_ptr< ASTNode > > &arguments) override
Emit code for a procedure-style call (no return value used).
AST node for a for loop (for var := start to/downto end do stmt).
AST node for a function call expression.
AST node for a function declaration.
AST node for a goto statement.
Built-in handler for I/O procedures (write, writeln, read, readln).
void generate(CodeGenVisitor &visitor, const std::string &funcName, const std::vector< std::unique_ptr< ASTNode > > &arguments) override
Emit code for a procedure-style call (no return value used).
bool canHandle(const std::string &funcName) const override
Return true if this handler implements funcName.
bool generateWithResult(CodeGenVisitor &visitor, const std::string &funcName, const std::vector< std::unique_ptr< ASTNode > > &arguments) override
Emit code for a function-style call (result pushed on eval stack); return true if handled.
AST node for an if-then-else statement.
AST node for a label definition (label: statement).
AST node for the nil literal.
AST node for an integer or real numeric literal.
bool isReal
true if real (floating-point) literal
AST node for a formal parameter list entry.
AST node for a pointer dereference (ptr^).
AST node for a pointer type declaration (^BaseType).
AST node for a procedure call statement.
AST node for a procedure declaration.
AST node for a complete program (program name + block).
AST node for a named record declaration.
AST node for a record type (record ... end).
AST node for a repeat..until loop.
Built-in handler for SDL2 bindings.
bool canHandle(const std::string &funcName) const override
Return true if this handler implements funcName.
bool generateWithResult(CodeGenVisitor &visitor, const std::string &funcName, const std::vector< std::unique_ptr< ASTNode > > &arguments) override
Emit code for a function-style call (result pushed on eval stack); return true if handled.
void generate(CodeGenVisitor &visitor, const std::string &funcName, const std::vector< std::unique_ptr< ASTNode > > &arguments) override
Emit code for a procedure-style call (no return value used).
AST node for a set literal [val1, val2, ...].
AST node for a set type declaration (set of <base type>).
AST node for a simple (named) type reference.
Built-in handler for standard library functions (chr, ord, inc, dec, etc.).
bool generateWithResult(CodeGenVisitor &visitor, const std::string &funcName, const std::vector< std::unique_ptr< ASTNode > > &arguments) override
Emit code for a function-style call (result pushed on eval stack); return true if handled.
bool canHandle(const std::string &funcName) const override
Return true if this handler implements funcName.
void generate(CodeGenVisitor &visitor, const std::string &funcName, const std::vector< std::unique_ptr< ASTNode > > &arguments) override
Emit code for a procedure-style call (no return value used).
Built-in handler for string library functions (length, copy, concat, etc.).
bool generateWithResult(CodeGenVisitor &visitor, const std::string &funcName, const std::vector< std::unique_ptr< ASTNode > > &arguments) override
Emit code for a function-style call (result pushed on eval stack); return true if handled.
bool canHandle(const std::string &funcName) const override
Return true if this handler implements funcName.
VarType getReturnType(const std::string &funcName) const override
Return the VarType produced by funcName.
void generate(CodeGenVisitor &visitor, const std::string &funcName, const std::vector< std::unique_ptr< ASTNode > > &arguments) override
Emit code for a procedure-style call (no return value used).
AST node for a string literal.
AST node for a type alias (type NewName = ExistingType).
AST node for a type declaration section.
AST node for a unary operator expression.
AST node for a Pascal unit (separately compiled module).
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.
AST node for a while loop (while cond do stmt).
AST node for a with statement (with record do statement).
std::string mxvmOpt(const std::string &text)
VarType
Enumeration of variable / expression value types.
@ ARRAY_INT
array of integers
@ ARRAY_STRING
array of strings
@ ARRAY_DOUBLE
array of doubles
@ DOUBLE
real (floating-point)
AST node hierarchy for the Pascal-to-MXVM frontend parser.
Metadata describing a Pascal array's bounds, element type, and sizing.
std::unique_ptr< ArrayInfo > elementArray
nested array descriptor (when elementIsArray)
ArrayInfo & operator=(ArrayInfo &&other) noexcept=default
ArrayInfo & operator=(const ArrayInfo &other)
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
ArrayInfo(const ArrayInfo &other)
ArrayInfo(ArrayInfo &&other) noexcept=default
Describes a user-declared function's parameter and return types.
VarType returnType
return type
std::vector< VarType > paramTypes
parameter types in order
RecordField & operator=(RecordField &&other) noexcept=default
bool isArray
true if the field is an array
std::string name
field name
RecordField(const RecordField &other)=default
ArrayInfo arrayInfo
array descriptor (when isArray)
int size
field size in bytes
RecordField & operator=(const RecordField &other)=default
RecordField(RecordField &&other) noexcept=default
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
std::vector< ProcDeclNode * > nestedProcs
std::vector< FuncDeclNode * > nestedFuncs
Describes where a value currently resides (register, memory, or immediate).
enum pascal::CodeGenVisitor::ValueLocation::Type type
std::string location
register name, slot name, or literal value
@ MEMORY
value is in a memory slot
@ IMMEDIATE
value is a compile-time constant
@ REGISTER
value is in a named register