aboutsummaryrefslogtreecommitdiff
path: root/lib/CodeGen/ExpressionGen.cc
diff options
context:
space:
mode:
author_ov4 <ov4@ov4.org>2026-10-16 09:42:45 +0000
committer_ov4 <ov4@ov4.org>2026-10-16 09:42:45 +0000
commit3dc3a15c86287b2788142b58e2512269cd5274c6 (patch)
tree1610b02343fd3e8b6b733c0cbfe327be6fef4394 /lib/CodeGen/ExpressionGen.cc
parentdb6ac29d0ac769ff780f3901ed987b97965f3f5e (diff)
refactor(codegen): split emission by responsibility
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Diffstat (limited to 'lib/CodeGen/ExpressionGen.cc')
-rw-r--r--lib/CodeGen/ExpressionGen.cc273
1 files changed, 273 insertions, 0 deletions
diff --git a/lib/CodeGen/ExpressionGen.cc b/lib/CodeGen/ExpressionGen.cc
new file mode 100644
index 0000000..0d69408
--- /dev/null
+++ b/lib/CodeGen/ExpressionGen.cc
@@ -0,0 +1,273 @@
+#include "cps/CodeGen.h"
+#include "cps/ArrayHandler.h"
+#include "cps/FileHandler.h"
+#include "cps/Lexer.h"
+#include "cps/TypeAST.h"
+#include "Builtins.h"
+
+using namespace llvm;
+using namespace cps;
+
+using namespace cps::detail;
+
+Value *CodeGen::emitExpr(ExprAST *Expr) {
+ if (auto *Cast = dynamic_cast<CastExprAST*>(Expr)) return emitCast(Cast);
+ if (!Expr) return nullptr;
+
+ if (auto *Num = dynamic_cast<IntegerExprAST*>(Expr)) {
+ return ConstantInt::get(*TheContext, APInt(64, Num->getVal(), true));
+ }
+
+ if (auto *Real = dynamic_cast<RealExprAST*>(Expr)) {
+ return ConstantFP::get(*TheContext, APFloat(Real->getVal()));
+ }
+
+ if (auto *Bool = dynamic_cast<BooleanExprAST*>(Expr)) {
+ return ConstantInt::get(*TheContext, APInt(1, Bool->getVal() ? 1 : 0));
+ }
+
+ if (auto *Chr = dynamic_cast<CharExprAST*>(Expr)) {
+ return ConstantInt::get(Type::getInt8Ty(*TheContext), static_cast<unsigned char>(Chr->getVal()));
+ }
+
+ if (auto *Str = dynamic_cast<StringExprAST*>(Expr)) {
+ return StrHandler->createLiteral(Str->getVal());
+ }
+
+ if (auto *Var = dynamic_cast<VariableExprAST*>(Expr)) {
+ const SymbolInfo *Info = getSymbolInfo(Var->getName());
+ if (!Info) {
+ // Enum values are bare identifiers; collisions with variables are
+ // rejected at declaration time, so the order here cannot shadow.
+ if (const EnumConstant *EC = Types->lookupEnumConstant(Var->getName())) {
+ return ConstantInt::get(*TheContext, APInt(64, static_cast<uint64_t>(EC->Ordinal), true));
+ }
+ reportError("Unknown variable name %s", Var->getName().c_str());
+ return nullptr;
+ }
+ if (Info->IsArray) {
+ return Info->Storage;
+ }
+
+ const TypeInfo *TypeInfo = resolveType(Info->TypeName);
+ if (!TypeInfo || !TypeInfo->LLVMType) {
+ reportError("Unknown type for variable %s", Var->getName().c_str());
+ return nullptr;
+ }
+
+ Value *Loaded = Builder->CreateLoad(TypeInfo->LLVMType, Info->Storage, Var->getName().c_str());
+ if (TypeInfo->isString()) {
+ Loaded = emitStringNullGuard(Loaded);
+ }
+ return Loaded;
+ }
+
+ if (auto *ArrAcc = dynamic_cast<ArrayAccessExprAST*>(Expr)) {
+ return Arrays->emitArrayAccess(ArrAcc, *this);
+ }
+
+ if (auto *Unary = dynamic_cast<UnaryExprAST*>(Expr)) {
+ if (Unary->getOp() == tok_not) {
+ const TypeInfo *Info = getExprTypeInfo(Unary->getOperand());
+ Value *Operand = Info && Info->isC()
+ ? convertNumericValue(emitExpr(Unary->getOperand()), Info, resolveType("BOOLEAN"))
+ : emitCoercedExpr(Unary->getOperand(), resolveType("BOOLEAN"));
+ if (Info && Info->Kind == TypeKind::CPointer) {
+ reportError("NOT does not support C pointer operands");
+ return nullptr;
+ }
+ if (!Operand) return nullptr;
+ return Builder->CreateNot(Operand, "nottmp");
+ }
+ if (Unary->getOp() == '-') {
+ const TypeInfo *Info = getExprTypeInfo(Unary->getOperand());
+ if (Info && Info->Kind == TypeKind::CScalar && !Info->isVoid()) {
+ const TypeInfo *Promoted = Types->promoteCScalar(Info);
+ Value *Operand = convertNumericValue(emitExpr(Unary->getOperand()), Info, Promoted);
+ if (!Operand) return nullptr;
+ return Promoted->isReal() ? Builder->CreateFNeg(Operand, "c_neg")
+ : Builder->CreateNeg(Operand, "c_neg");
+ }
+ if (!Info || Info->isC() || Info->isUserKind() || Info->isString() || Info->isVoid()) {
+ reportError("Unary '-' requires a supported numeric operand");
+ return nullptr;
+ }
+ Value *Operand = emitExpr(Unary->getOperand());
+ if (!Operand) return nullptr;
+ return ArithHandler->emitBinaryOp('-', ConstantInt::get(Type::getInt64Ty(*TheContext), 0),
+ Operand, 0);
+ }
+ return nullptr;
+ }
+
+ if (auto *Bin = dynamic_cast<BinaryExprAST*>(Expr)) {
+ // Whole arrays evaluate to raw data pointers and ^x to raw addresses;
+ // neither is a value any operator may consume.
+ if (isWholeArrayVar(Bin->getLHS()) || isWholeArrayVar(Bin->getRHS())) {
+ reportError("A whole array cannot be used as an operand; index it");
+ return nullptr;
+ }
+ if (dynamic_cast<AddrOfExprAST*>(Bin->getLHS()) ||
+ dynamic_cast<AddrOfExprAST*>(Bin->getRHS())) {
+ reportError("'^' (address-of) can only be used where a pointer type is expected");
+ return nullptr;
+ }
+
+ // Enum/Record/Pointer operands never reach ArithmeticHandler or the
+ // '&' path: their admissible operations are decided by name here.
+ const TypeInfo *LT = getExprTypeInfo(Bin->getLHS());
+ const TypeInfo *RT = getExprTypeInfo(Bin->getRHS());
+ if ((LT && LT->isC()) || (RT && RT->isC())) {
+ return emitCBinaryOp(Bin, LT, RT);
+ }
+ if ((LT && LT->isUserKind()) || (RT && RT->isUserKind())) {
+ return emitUserKindBinaryOp(Bin, LT, RT);
+ }
+
+ Value *L = emitExpr(Bin->getLHS());
+ Value *R = emitExpr(Bin->getRHS());
+ if (!L || !R) return nullptr;
+ if (Bin->getOp() == '&') {
+ L = coerceValueToType(L, resolveType("STRING"));
+ R = coerceValueToType(R, resolveType("STRING"));
+ if (!L || !R) return nullptr;
+ return StrHandler->emitConcat(L, R);
+ }
+ // STRING and explicit pointers share LLVM's ptr representation;
+ // string comparisons must dispatch on the pseudocode type.
+ switch (Bin->getOp()) {
+ case tok_eq: case tok_ne: case '<': case '>': case tok_le: case tok_ge:
+ if ((LT && LT->isString()) || (RT && RT->isString())) {
+ if (!(LT && LT->isString() && RT && RT->isString())) {
+ reportError("STRING comparison requires two STRING operands (line %d)", Bin->getLine());
+ return nullptr;
+ }
+ return StrHandler->emitCompare(Bin->getOp(), L, R);
+ }
+ break;
+ default:
+ break;
+ }
+ return ArithHandler->emitBinaryOp(Bin->getOp(), L, R, Bin->getLine());
+ }
+
+ 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") {
+ Value *Str = emitCoercedExpr(Call->getArgs()[0].get(), resolveType("STRING"));
+ if (!Str) return nullptr;
+ return StrHandler->emitLength(Str);
+ }
+ if (Name == "MID") {
+ Value *Str = emitCoercedExpr(Call->getArgs()[0].get(), resolveType("STRING"));
+ Value *Start = emitCoercedExpr(Call->getArgs()[1].get(), resolveType("INTEGER"));
+ Value *Len = emitCoercedExpr(Call->getArgs()[2].get(), resolveType("INTEGER"));
+ if (!Str || !Start || !Len) return nullptr;
+ return StrHandler->emitMid(Str, Start, Len);
+ }
+ if (Name == "RIGHT") {
+ Value *Str = emitCoercedExpr(Call->getArgs()[0].get(), resolveType("STRING"));
+ Value *Len = emitCoercedExpr(Call->getArgs()[1].get(), resolveType("INTEGER"));
+ if (!Str || !Len) return nullptr;
+ return StrHandler->emitRight(Str, Len);
+ }
+ if (Name == "LEFT") {
+ Value *Str = emitCoercedExpr(Call->getArgs()[0].get(), resolveType("STRING"));
+ Value *Len = emitCoercedExpr(Call->getArgs()[1].get(), resolveType("INTEGER"));
+ if (!Str || !Len) return nullptr;
+ return StrHandler->emitLeft(Str, Len);
+ }
+ if (Name == "LCASE") {
+ Value *Str = emitCoercedExpr(Call->getArgs()[0].get(), resolveType("STRING"));
+ if (!Str) return nullptr;
+ return StrHandler->emitLCase(Str);
+ }
+ if (Name == "UCASE") {
+ Value *Str = emitCoercedExpr(Call->getArgs()[0].get(), resolveType("STRING"));
+ if (!Str) return nullptr;
+ return StrHandler->emitUCase(Str);
+ }
+ if (Name == "ASC") {
+ const TypeInfo *ArgType = getExprTypeInfo(Call->getArgs()[0].get());
+ if (ArgType && (ArgType->isUserKind() || ArgType->isC())) {
+ reportError("ASC expects a CHAR or STRING (got %s)", ArgType->Name.c_str());
+ return nullptr;
+ }
+ Value *ArgVal = emitExpr(Call->getArgs()[0].get());
+ if (!ArgVal) return nullptr;
+ Value *CharVal = nullptr;
+ if (ArgType && ArgType->isChar()) {
+ CharVal = coerceValueToType(ArgVal, resolveType("CHAR"));
+ } else {
+ CharVal = Builder->CreateLoad(Type::getInt8Ty(*TheContext), ArgVal, "char_load");
+ }
+ Value *AscVal = Builder->CreateZExt(CharVal, Type::getInt32Ty(*TheContext), "asc_val");
+ return coerceValueToType(AscVal, resolveType("INTEGER"));
+ }
+ if (Name == "CHR") {
+ Value *IntVal = emitCoercedExpr(Call->getArgs()[0].get(), resolveType("INTEGER"));
+ if (!IntVal) return nullptr;
+ Value *CharVal = Builder->CreateTrunc(IntVal, Type::getInt8Ty(*TheContext), "chr_val");
+ return coerceValueToType(CharVal, resolveType("STRING"));
+ }
+ if (Name == "IS_NUM") {
+ Value *Str = emitCoercedExpr(Call->getArgs()[0].get(), resolveType("STRING"));
+ if (!Str) return nullptr;
+ return StrConvHandler->emitIsNum(Str);
+ }
+ if (Name == "NUM_TO_STR") {
+ const TypeInfo *ArgType = getExprTypeInfo(Call->getArgs()[0].get());
+ if (ArgType && (ArgType->isUserKind() || ArgType->isC())) {
+ reportError("NUM_TO_STR expects a number (got %s)", ArgType->Name.c_str());
+ return nullptr;
+ }
+ Value *NumV = emitExpr(Call->getArgs()[0].get());
+ if (!NumV) return nullptr;
+ bool IsReal = NumV->getType()->isDoubleTy();
+ if (NumV->getType()->isIntegerTy(8)) {
+ NumV = coerceValueToType(NumV, resolveType("INTEGER"));
+ }
+ return StrConvHandler->emitNumToStr(NumV, IsReal);
+ }
+ if (Name == "STR_TO_NUM") {
+ Value *Str = emitCoercedExpr(Call->getArgs()[0].get(), resolveType("STRING"));
+ if (!Str) return nullptr;
+ return StrConvHandler->emitStrToNum(Str, true);
+ }
+ if (Name == "EOF") {
+ if (isWholeArrayVar(Call->getArgs()[0].get())) {
+ reportError("Cannot use a whole array as the EOF filename");
+ return nullptr;
+ }
+ Value *FileName = emitCoercedExpr(Call->getArgs()[0].get(), resolveType("STRING"));
+ if (!FileName) return nullptr;
+ return Files->emitEof(FileName, Call->getLine());
+ }
+
+ std::vector<Value*> Args;
+ if (!marshalCallArgs(Name, Call->getArgs(), Call->getLine(), Args)) return nullptr;
+ return FuncGen->emitCallExpr(Call, Args);
+ }
+
+ if (auto *D = dynamic_cast<DesignatorExprAST*>(Expr)) {
+ LValueInfo LV;
+ if (!emitLValue(D, LV)) return nullptr;
+ return loadFromLValue(LV);
+ }
+
+ // ^x produces a raw address; it is only admissible where a pointer type
+ // is expected, which emitCoercedExpr enforces by pointee name.
+ if (auto *Addr = dynamic_cast<AddrOfExprAST*>(Expr)) {
+ LValueInfo LV;
+ if (!emitLValue(Addr->getTarget(), LV)) return nullptr;
+ return LV.Addr;
+ }
+
+ return nullptr;
+}