aboutsummaryrefslogtreecommitdiff
path: root/lib/CodeGen/CodeGen.cc
diff options
context:
space:
mode:
author_ov4 <ov4@ov4.org>2026-07-26 12:04:56 +0000
committer_ov4 <ov4@ov4.org>2026-07-26 12:04:56 +0000
commitefc86b5c7625cf4c7074319f42f68807a25bde9d (patch)
tree0e2e1b5d9d336b1744602b3e47817f8a6b9c15f2 /lib/CodeGen/CodeGen.cc
parent4df7c22b99349dcf2175d69679eafa175d49aa9f (diff)
refactor: single builtin registry, retire keyword route for builtins
All 11 builtins now live in one static {Name, Arity, ReturnTypeName} table in CodeGen.cc: emitExpr checks arity against it (uniform "Error: X expects N args" diagnostics) and getExprTypeInfo reads return types from it, removing the duplicated hard-coded table. LENGTH/MID/RIGHT/LEFT/LCASE/UCASE lose their lexer tokens and ParseStringBuiltin; they parse as ordinary identifier calls exactly like ASC/CHR/IS_NUM/NUM_TO_STR/STR_TO_NUM already did. Behavior note: these six names are no longer reserved words. AGENTS docs updated for the new builtin story and the batch-1 error contract (exit codes, verifyModule, fixed descending FOR). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Diffstat (limited to 'lib/CodeGen/CodeGen.cc')
-rw-r--r--lib/CodeGen/CodeGen.cc60
1 files changed, 43 insertions, 17 deletions
diff --git a/lib/CodeGen/CodeGen.cc b/lib/CodeGen/CodeGen.cc
index 439e894..581e4a9 100644
--- a/lib/CodeGen/CodeGen.cc
+++ b/lib/CodeGen/CodeGen.cc
@@ -9,6 +9,40 @@
using namespace llvm;
using namespace cps;
+namespace {
+
+// Single source of truth for the built-in functions: name, arity and return
+// type. emitExpr checks arity against this table before dispatching, and
+// getExprTypeInfo reads the return type from it.
+struct BuiltinInfo {
+ const char *Name;
+ unsigned Arity;
+ const char *ReturnTypeName;
+};
+
+constexpr BuiltinInfo Builtins[] = {
+ {"LENGTH", 1, "INTEGER"},
+ {"MID", 3, "STRING"},
+ {"RIGHT", 2, "STRING"},
+ {"LEFT", 2, "STRING"},
+ {"LCASE", 1, "STRING"},
+ {"UCASE", 1, "STRING"},
+ {"ASC", 1, "INTEGER"},
+ {"CHR", 1, "STRING"},
+ {"IS_NUM", 1, "BOOLEAN"},
+ {"NUM_TO_STR", 1, "STRING"},
+ {"STR_TO_NUM", 1, "REAL"},
+};
+
+const BuiltinInfo *findBuiltin(const std::string &Name) {
+ for (const auto &B : Builtins) {
+ if (Name == B.Name) return &B;
+ }
+ return nullptr;
+}
+
+} // namespace
+
CodeGen::~CodeGen() = default;
void CodeGen::reportError(const char *Fmt, ...) {
@@ -174,12 +208,9 @@ const TypeInfo *CodeGen::getExprTypeInfo(ExprAST *Expr) const {
if (auto *Call = dynamic_cast<CallExprAST*>(Expr)) {
const std::string &Name = Call->getCallee();
- if (Name == "LENGTH" || Name == "ASC") return resolveType("INTEGER");
- if (Name == "MID" || Name == "RIGHT" || Name == "LEFT" ||
- Name == "LCASE" || Name == "UCASE" || Name == "CHR" ||
- Name == "NUM_TO_STR") return resolveType("STRING");
- if (Name == "IS_NUM") return resolveType("BOOLEAN");
- if (Name == "STR_TO_NUM") return resolveType("REAL");
+ if (const BuiltinInfo *B = findBuiltin(Name)) {
+ return resolveType(B->ReturnTypeName);
+ }
Function *CalleeF = TheModule->getFunction(Name);
if (!CalleeF) return resolveType("INTEGER");
@@ -474,12 +505,16 @@ Value *CodeGen::emitExpr(ExprAST *Expr) {
if (auto *Call = dynamic_cast<CallExprAST*>(Expr)) {
std::string Name = Call->getCallee();
+ if (const BuiltinInfo *B = findBuiltin(Name)) {
+ if (Call->getArgs().size() != B->Arity) {
+ reportError("%s expects %u arg%s", B->Name, B->Arity, B->Arity == 1 ? "" : "s");
+ return nullptr;
+ }
+ }
if (Name == "LENGTH") {
- if (Call->getArgs().size() != 1) { reportError("LENGTH expects 1 arg"); return nullptr; }
return StrHandler->emitLength(emitExpr(Call->getArgs()[0].get()));
}
if (Name == "MID") {
- if (Call->getArgs().size() != 3) { reportError("MID expects 3 args"); return nullptr; }
Value *Str = emitExpr(Call->getArgs()[0].get());
Value *Start = coerceValueToType(emitExpr(Call->getArgs()[1].get()), resolveType("INTEGER"));
Value *Len = coerceValueToType(emitExpr(Call->getArgs()[2].get()), resolveType("INTEGER"));
@@ -487,29 +522,24 @@ Value *CodeGen::emitExpr(ExprAST *Expr) {
return StrHandler->emitMid(Str, Start, Len);
}
if (Name == "RIGHT") {
- if (Call->getArgs().size() != 2) { reportError("RIGHT expects 2 args"); return nullptr; }
Value *Str = emitExpr(Call->getArgs()[0].get());
Value *Len = coerceValueToType(emitExpr(Call->getArgs()[1].get()), resolveType("INTEGER"));
if (!Str || !Len) return nullptr;
return StrHandler->emitRight(Str, Len);
}
if (Name == "LEFT") {
- if (Call->getArgs().size() != 2) { reportError("LEFT expects 2 args"); return nullptr; }
Value *Str = emitExpr(Call->getArgs()[0].get());
Value *Len = coerceValueToType(emitExpr(Call->getArgs()[1].get()), resolveType("INTEGER"));
if (!Str || !Len) return nullptr;
return StrHandler->emitLeft(Str, Len);
}
if (Name == "LCASE") {
- if (Call->getArgs().size() != 1) { reportError("LCASE expects 1 arg"); return nullptr; }
return StrHandler->emitLCase(emitExpr(Call->getArgs()[0].get()));
}
if (Name == "UCASE") {
- if (Call->getArgs().size() != 1) { reportError("UCASE expects 1 arg"); return nullptr; }
return StrHandler->emitUCase(emitExpr(Call->getArgs()[0].get()));
}
if (Name == "ASC") {
- if (Call->getArgs().size() != 1) { reportError("ASC expects 1 arg"); return nullptr; }
Value *ArgVal = emitExpr(Call->getArgs()[0].get());
const TypeInfo *ArgType = getExprTypeInfo(Call->getArgs()[0].get());
Value *CharVal = nullptr;
@@ -522,7 +552,6 @@ Value *CodeGen::emitExpr(ExprAST *Expr) {
return coerceValueToType(AscVal, resolveType("INTEGER"));
}
if (Name == "CHR") {
- if (Call->getArgs().size() != 1) { reportError("CHR expects 1 arg"); return nullptr; }
Value *IntVal = coerceValueToType(emitExpr(Call->getArgs()[0].get()), resolveType("INTEGER"));
Value *CharVal = ChrHandler->emitChr(IntVal);
Function *MallocF = TheModule->getFunction("malloc");
@@ -535,11 +564,9 @@ Value *CodeGen::emitExpr(ExprAST *Expr) {
return Mem;
}
if (Name == "IS_NUM") {
- if (Call->getArgs().size() != 1) { reportError("IS_NUM expects 1 arg"); return nullptr; }
return StrConvHandler->emitIsNum(emitExpr(Call->getArgs()[0].get()));
}
if (Name == "NUM_TO_STR") {
- if (Call->getArgs().size() != 1) { reportError("NUM_TO_STR expects 1 arg"); return nullptr; }
Value *NumV = emitExpr(Call->getArgs()[0].get());
bool IsReal = NumV->getType()->isDoubleTy();
if (NumV->getType()->isIntegerTy(8)) {
@@ -548,7 +575,6 @@ Value *CodeGen::emitExpr(ExprAST *Expr) {
return StrConvHandler->emitNumToStr(NumV, IsReal);
}
if (Name == "STR_TO_NUM") {
- if (Call->getArgs().size() != 1) { reportError("STR_TO_NUM expects 1 arg"); return nullptr; }
return StrConvHandler->emitStrToNum(emitExpr(Call->getArgs()[0].get()), true);
}