diff options
| author | _ov4 <ov4@ov4.org> | 2026-10-16 09:42:45 +0000 |
|---|---|---|
| committer | _ov4 <ov4@ov4.org> | 2026-10-16 09:42:45 +0000 |
| commit | 3dc3a15c86287b2788142b58e2512269cd5274c6 (patch) | |
| tree | 1610b02343fd3e8b6b733c0cbfe327be6fef4394 /lib/CodeGen | |
| parent | db6ac29d0ac769ff780f3901ed987b97965f3f5e (diff) | |
refactor(codegen): split emission by responsibility
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Diffstat (limited to 'lib/CodeGen')
| -rw-r--r-- | lib/CodeGen/Builtins.h | 37 | ||||
| -rw-r--r-- | lib/CodeGen/CodeGen.cc | 1099 | ||||
| -rw-r--r-- | lib/CodeGen/ControlFlowGen.cc | 175 | ||||
| -rw-r--r-- | lib/CodeGen/ExpressionGen.cc | 273 | ||||
| -rw-r--r-- | lib/CodeGen/OutputGen.cc | 95 | ||||
| -rw-r--r-- | lib/CodeGen/StatementGen.cc | 282 | ||||
| -rw-r--r-- | lib/CodeGen/ValueTypeGen.cc | 274 |
7 files changed, 1138 insertions, 1097 deletions
diff --git a/lib/CodeGen/Builtins.h b/lib/CodeGen/Builtins.h new file mode 100644 index 0000000..34e36a9 --- /dev/null +++ b/lib/CodeGen/Builtins.h @@ -0,0 +1,37 @@ +#pragma once +#include <string> + +namespace cps::detail { + +// 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; +}; + +inline 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"}, + {"EOF", 1, "BOOLEAN"}, +}; + +inline const BuiltinInfo *findBuiltin(const std::string &Name) { + for (const auto &B : Builtins) { + if (Name == B.Name) return &B; + } + return nullptr; +} + +} // namespace cps::detail diff --git a/lib/CodeGen/CodeGen.cc b/lib/CodeGen/CodeGen.cc index 7e463db..6c6f682 100644 --- a/lib/CodeGen/CodeGen.cc +++ b/lib/CodeGen/CodeGen.cc @@ -1,10 +1,8 @@ #include "cps/CodeGen.h" #include "cps/ArrayHandler.h" -#include "cps/FileAST.h" #include "cps/FileHandler.h" #include "cps/ExceptionHandler.h" -#include "cps/Lexer.h" -#include "cps/TypeAST.h" +#include "Builtins.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/Verifier.h" #include <cstdarg> @@ -13,40 +11,7 @@ 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"}, - {"EOF", 1, "BOOLEAN"}, -}; - -const BuiltinInfo *findBuiltin(const std::string &Name) { - for (const auto &B : Builtins) { - if (Name == B.Name) return &B; - } - return nullptr; -} - -} // namespace +using namespace cps::detail; CodeGen::~CodeGen() = default; @@ -177,362 +142,6 @@ const TypeInfo *CodeGen::resolveType(const std::string &TypeName) const { return Types->resolve(TypeName); } -const TypeInfo *CodeGen::getExprTypeInfo(ExprAST *Expr) const { - if (!Expr) return nullptr; - - if (dynamic_cast<IntegerExprAST*>(Expr)) return resolveType("INTEGER"); - if (dynamic_cast<RealExprAST*>(Expr)) return resolveType("REAL"); - if (dynamic_cast<BooleanExprAST*>(Expr)) return resolveType("BOOLEAN"); - if (dynamic_cast<CharExprAST*>(Expr)) return resolveType("CHAR"); - if (dynamic_cast<StringExprAST*>(Expr)) return resolveType("STRING"); - if (auto *Cast = dynamic_cast<CastExprAST*>(Expr)) - return resolveType(Cast->getTypeName()); - - if (auto *Var = dynamic_cast<VariableExprAST*>(Expr)) { - const SymbolInfo *Info = getSymbolInfo(Var->getName()); - if (Info) return resolveType(Info->TypeName); - if (const EnumConstant *EC = Types->lookupEnumConstant(Var->getName())) { - return resolveType(EC->TypeName); - } - return nullptr; - } - - if (auto *Arr = dynamic_cast<ArrayAccessExprAST*>(Expr)) { - const SymbolInfo *Info = getSymbolInfo(Arr->getName()); - return Info ? resolveType(Info->TypeName) : nullptr; - } - - if (auto *D = dynamic_cast<DesignatorExprAST*>(Expr)) { - const SymbolInfo *Info = getSymbolInfo(D->getBaseName()); - if (!Info) return nullptr; - std::string TypeName = Info->TypeName; - size_t Start = 0; - if (Info->IsArray) { - // SymbolInfo already holds the ELEMENT type name for arrays. - if (D->getAccesses().empty() || D->getAccesses()[0].Kind != AccessKind::Index) - return nullptr; - Start = 1; - } - for (size_t I = Start; I < D->getAccesses().size(); ++I) { - const TypeInfo *Cur = resolveType(TypeName); - if (!Cur) return nullptr; - const DesignatorAccess &A = D->getAccesses()[I]; - if (A.Kind == AccessKind::Field) { - const RecordPayload *RP = Cur->asRecord(); - const RecordFieldInfo *FI = RP ? RP->findField(A.FieldName) : nullptr; - if (!FI) return nullptr; - if (!FI->IsPublic) return nullptr; - TypeName = FI->TypeName; - } else if (A.Kind == AccessKind::Deref) { - const PointerPayload *PP = Cur->asPointer(); - if (!PP) return nullptr; - TypeName = PP->PointeeTypeName; - } else { - return nullptr; // indexing a non-array - } - } - return resolveType(TypeName); - } - - // ^x has no pseudocode type of its own; only the pointer admission in - // emitCoercedExpr may consume it. - if (dynamic_cast<AddrOfExprAST*>(Expr)) return nullptr; - - if (auto *Unary = dynamic_cast<UnaryExprAST*>(Expr)) { - if (Unary->getOp() == tok_not) { - return resolveType("BOOLEAN"); - } - const TypeInfo *Operand = getExprTypeInfo(Unary->getOperand()); - if (Operand && Operand->isC()) return Types->promoteCScalar(Operand); - if (Operand && !Operand->isC() && !Operand->isUserKind()) - return Operand->isReal() ? resolveType("REAL") : resolveType("INTEGER"); - return nullptr; - } - - if (auto *Bin = dynamic_cast<BinaryExprAST*>(Expr)) { - switch (Bin->getOp()) { - case tok_eq: - case tok_ne: - case '<': - case '>': - case tok_le: - case tok_ge: - case tok_and: - case tok_or: - return resolveType("BOOLEAN"); - case '&': - return resolveType("STRING"); - case '/': - return resolveType("REAL"); - default: - break; - } - - const TypeInfo *L = getExprTypeInfo(Bin->getLHS()); - const TypeInfo *R = getExprTypeInfo(Bin->getRHS()); - if ((L && L->isC()) || (R && R->isC())) return Types->commonCScalar(L, R); - // Enum arithmetic: E +/- INTEGER (enum on the left) yields the enum; - // every other user-kind combination has no type (emission rejects it). - if ((L && L->isUserKind()) || (R && R->isUserKind())) { - if (L && L->isEnum() && R && R->Kind == TypeKind::Integer && - (Bin->getOp() == '+' || Bin->getOp() == '-')) { - return L; - } - return nullptr; - } - if ((L && L->Kind == TypeKind::Real) || (R && R->Kind == TypeKind::Real)) { - return resolveType("REAL"); - } - return resolveType("INTEGER"); - } - - if (auto *Call = dynamic_cast<CallExprAST*>(Expr)) { - const std::string &Name = Call->getCallee(); - if (const BuiltinInfo *B = findBuiltin(Name)) { - return resolveType(B->ReturnTypeName); - } - if (const FuncSig *Sig = FuncGen->getSignature(Name)) { - return resolveType(Sig->ReturnTypeName); - } - - Function *CalleeF = TheModule->getFunction(Name); - if (!CalleeF) return resolveType("INTEGER"); - - Type *RetTy = CalleeF->getReturnType(); - if (RetTy->isVoidTy()) return resolveType("VOID"); - if (RetTy->isDoubleTy()) return resolveType("REAL"); - if (RetTy->isIntegerTy(1)) return resolveType("BOOLEAN"); - if (RetTy->isIntegerTy(8)) return resolveType("CHAR"); - if (RetTy->isPointerTy()) return resolveType("STRING"); - return resolveType("INTEGER"); - } - - return nullptr; -} - -Value *CodeGen::coerceValueToType(Value *Val, const TypeInfo *TargetInfo) { - if (!Val || !TargetInfo || !TargetInfo->LLVMType) return nullptr; - - if (Val->getType() == TargetInfo->LLVMType) { - return Val; - } - - switch (TargetInfo->Kind) { - case TypeKind::Real: - if (Val->getType()->isDoubleTy()) return Val; - if (Val->getType()->isIntegerTy(1)) - return Builder->CreateUIToFP(Val, Type::getDoubleTy(*TheContext), "bool_to_real"); - if (Val->getType()->isIntegerTy(8)) - return Builder->CreateUIToFP(Val, Type::getDoubleTy(*TheContext), "char_to_real"); - if (Val->getType()->isIntegerTy()) - return Builder->CreateSIToFP(Val, Type::getDoubleTy(*TheContext), "int_to_real"); - break; - - case TypeKind::Integer: - if (Val->getType()->isIntegerTy(64)) return Val; - if (Val->getType()->isDoubleTy()) - return Builder->CreateFPToSI(Val, Type::getInt64Ty(*TheContext), "real_to_int"); - if (Val->getType()->isIntegerTy(1)) - return Builder->CreateZExt(Val, Type::getInt64Ty(*TheContext), "bool_to_int"); - if (Val->getType()->isIntegerTy(8)) - return Builder->CreateZExt(Val, Type::getInt64Ty(*TheContext), "char_to_int"); - if (Val->getType()->isIntegerTy()) - return Builder->CreateSExtOrTrunc(Val, Type::getInt64Ty(*TheContext), "int_cast"); - break; - - case TypeKind::Boolean: - if (Val->getType()->isIntegerTy(1)) return Val; - if (Val->getType()->isIntegerTy()) - return Builder->CreateICmpNE(Val, Constant::getNullValue(Val->getType()), "to_bool"); - if (Val->getType()->isDoubleTy()) - return Builder->CreateFCmpONE(Val, ConstantFP::get(*TheContext, APFloat(0.0)), "to_bool"); - if (Val->getType()->isPointerTy()) - return Builder->CreateICmpNE(Val, - ConstantPointerNull::get(cast<PointerType>(Val->getType())), - "ptr_to_bool"); - break; - - case TypeKind::Char: - if (Val->getType()->isIntegerTy(8)) return Val; - if (Val->getType()->isIntegerTy(1)) - return Builder->CreateZExt(Val, Type::getInt8Ty(*TheContext), "bool_to_char"); - if (Val->getType()->isDoubleTy()) - return Builder->CreateFPToUI(Val, Type::getInt8Ty(*TheContext), "real_to_char"); - if (Val->getType()->isIntegerTy()) - return Builder->CreateTruncOrBitCast(Val, Type::getInt8Ty(*TheContext), "int_to_char"); - break; - - case TypeKind::String: - if (Val->getType()->isPointerTy()) return Val; - if (Val->getType()->isIntegerTy(1)) { - return Builder->CreateSelect(Val, TrueStr, FalseStr, "bool_to_str"); - } - if (Val->getType()->isDoubleTy()) { - return StrConvHandler->emitNumToStr(Val, true); - } - if (Val->getType()->isIntegerTy(8)) { - Value *Mem = Builder->CreateCall(MallocFunc, - ConstantInt::get(*TheContext, APInt(64, 2)), - "char_str"); - Builder->CreateStore(Val, Mem); - Value *NullPtr = Builder->CreateInBoundsGEP(Type::getInt8Ty(*TheContext), - Mem, - ConstantInt::get(*TheContext, APInt(64, 1))); - Builder->CreateStore(ConstantInt::get(Type::getInt8Ty(*TheContext), 0), NullPtr); - return Mem; - } - if (Val->getType()->isIntegerTy()) { - Value *AsInt = coerceValueToType(Val, resolveType("INTEGER")); - return AsInt ? StrConvHandler->emitNumToStr(AsInt, false) : nullptr; - } - break; - - case TypeKind::Void: - return nullptr; - - // Enum/Record/Pointer identity lives in the type NAME; at this level - // an enum is a plain i64 and a pointer a plain ptr, so nothing can be - // checked here. emitCoercedExpr is the only legitimate door. - case TypeKind::Enum: - case TypeKind::Record: - case TypeKind::Pointer: - case TypeKind::CScalar: - case TypeKind::CPointer: - reportError("Internal: a %s value reached representation coercion without a name-level check", - TargetInfo->Name.c_str()); - return nullptr; - } - - reportError("Cannot coerce value of LLVM type to target type %s", - TargetInfo->Name.c_str()); - return nullptr; -} - -void CodeGen::emitDeclareStmt(DeclareStmtAST *Stmt) { - const TypeInfo *Info = resolveType(Stmt->getType()); - if (!Info || !Info->LLVMType || Info->isVoid()) { - reportError("Unknown type %s", Stmt->getType().c_str()); - return; - } - if (Types->lookupEnumConstant(Stmt->getName())) { - reportError("'%s' is an enum value and cannot be declared as a variable", - Stmt->getName().c_str()); - return; - } - if (Types->resolve(Stmt->getName())) { - reportError("'%s' is a type name and cannot be declared as a variable", - Stmt->getName().c_str()); - return; - } - - Function *TheFunction = Builder->GetInsertBlock()->getParent(); - AllocaInst *Alloca = CreateEntryBlockAlloca(TheFunction, Info->LLVMType, Stmt->getName()); - - Value *InitVal = nullptr; - if (Info->isString()) { - InitVal = EmptyStringStr; - } else { - InitVal = Types->getZeroValue(Info->Name); - } - - if (InitVal) { - Builder->CreateStore(InitVal, Alloca); - } - - registerSymbol(Stmt->getName(), Alloca, Info->Name, false); -} - -Value *CodeGen::emitStringNullGuard(Value *StrVal) { - if (!StrVal || !StrVal->getType()->isPointerTy()) return StrVal; - Value *IsNull = Builder->CreateICmpEQ(StrVal, - ConstantPointerNull::get(cast<PointerType>(StrVal->getType())), - "str_is_null"); - return Builder->CreateSelect(IsNull, EmptyStringStr, StrVal, "safe_str"); -} - -void CodeGen::emitOutputValue(Value *Val, const TypeInfo *Info, bool AppendNewline) { - if (!Val || !Info) return; - - std::vector<Value*> Args; - Value *Fmt = nullptr; - - // Exhaustive over TypeKind (no default): the compiler warns when a new - // kind is added without deciding its OUTPUT story. - switch (Info->Kind) { - case TypeKind::Real: - Fmt = AppendNewline - ? PrintfFloatFormatStr - : Builder->CreateGlobalString("%f", "fmt_flt_inline", 0, TheModule.get()); - Args.push_back(Fmt); - Args.push_back(Val); - break; - - case TypeKind::Boolean: { - Value *BoolVal = coerceValueToType(Val, resolveType("BOOLEAN")); - Fmt = AppendNewline - ? PrintfStringFormatStr - : Builder->CreateGlobalString("%s", "fmt_str_inline", 0, TheModule.get()); - Args.push_back(Fmt); - Args.push_back(Builder->CreateSelect(BoolVal, TrueStr, FalseStr)); - break; - } - - case TypeKind::String: { - Value *StrVal = emitStringNullGuard(Val); - Fmt = AppendNewline - ? PrintfStringFormatStr - : Builder->CreateGlobalString("%s", "fmt_str_inline", 0, TheModule.get()); - Args.push_back(Fmt); - Args.push_back(StrVal); - break; - } - - case TypeKind::Char: { - Value *CharVal = coerceValueToType(Val, resolveType("CHAR")); - Value *Promoted = Builder->CreateZExt(CharVal, Type::getInt32Ty(*TheContext), "char_for_printf"); - Fmt = AppendNewline - ? PrintfCharFormatStr - : Builder->CreateGlobalString("%c", "fmt_chr_inline", 0, TheModule.get()); - Args.push_back(Fmt); - Args.push_back(Promoted); - break; - } - - case TypeKind::Integer: - case TypeKind::Enum: { // an enum prints its ordinal (user decision) - Value *IntVal = Info->isEnum() ? Val : coerceValueToType(Val, resolveType("INTEGER")); - Fmt = AppendNewline - ? PrintfFormatStr - : Builder->CreateGlobalString("%lld", "fmt_int_inline", 0, TheModule.get()); - Args.push_back(Fmt); - Args.push_back(IntVal); - break; - } - - case TypeKind::Record: - reportError("Cannot OUTPUT a value of record type %s; output its fields individually", - Info->Name.c_str()); - return; - - case TypeKind::Pointer: - reportError("Cannot OUTPUT a value of pointer type %s", Info->Name.c_str()); - return; - - case TypeKind::CScalar: - emitCOutput(Val, Info, AppendNewline); - return; - case TypeKind::CPointer: - reportError("OUTPUT is not supported for type %s", Info->Name.c_str()); - return; - - case TypeKind::Void: - reportError("Cannot OUTPUT a VOID value"); - return; - } - - Builder->CreateCall(PrintfFunc, Args); -} - void CodeGen::compile(const std::vector<std::unique_ptr<StmtAST>> &Statements) { FunctionType *FT = FunctionType::get(Type::getInt32Ty(*TheContext), false); Function *F = Function::Create(FT, Function::ExternalLinkage, "main", TheModule.get()); @@ -581,268 +190,6 @@ void CodeGen::print(raw_ostream &Output) { TheModule->print(Output, nullptr); } -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; -} - // Collects the arguments for a call to a user FUNCTION/PROCEDURE, dispatching // on the *declared* signature: BYREF parameters take the variable's alloca, // BYVAL parameters are evaluated and coerced to the declared parameter type. @@ -912,445 +259,3 @@ bool CodeGen::marshalCallArgs(const std::string &Callee, } return true; } - -void CodeGen::emitTryStmt(TryStmtAST *Stmt) { - auto *F = Builder->GetInsertBlock()->getParent(); - auto *CatchBB = BasicBlock::Create(*TheContext, "catch", F); - auto *AfterBB = BasicBlock::Create(*TheContext, "try_cont", F); - auto SavedArrays = Arrays->snapshotTable(); - - Exceptions->pushHandler(CatchBB); - for (const auto &S : Stmt->getBody()) emitStmt(S.get()); - auto *Exception = Exceptions->popHandler(); - if (!Builder->GetInsertBlock()->hasTerminator()) Builder->CreateBr(AfterBB); - Arrays->exchangeTable(SavedArrays); - - Builder->SetInsertPoint(CatchBB); - Exceptions->beginCatch(Exception); - for (const auto &S : Stmt->getCatchBody()) emitStmt(S.get()); - Exceptions->endCatch(); - if (!Builder->GetInsertBlock()->hasTerminator()) Builder->CreateBr(AfterBB); - Arrays->exchangeTable(std::move(SavedArrays)); - Builder->SetInsertPoint(AfterBB); -} - -void CodeGen::emitIfStmt(IfStmtAST *Stmt) { - Value *CondV = emitCoercedExpr(Stmt->getCond(), resolveType("BOOLEAN")); - if (!CondV) return; - - Function *TheFunction = Builder->GetInsertBlock()->getParent(); - BasicBlock *ThenBB = BasicBlock::Create(*TheContext, "then", TheFunction); - BasicBlock *ElseBB = BasicBlock::Create(*TheContext, "else", TheFunction); - BasicBlock *MergeBB = BasicBlock::Create(*TheContext, "ifcont", TheFunction); - - Builder->CreateCondBr(CondV, ThenBB, ElseBB); - - // Arrays are block-scoped: metadata SSA values and the buffer setup live - // inside the branch, so a declaration must not survive it. - auto SavedArrays = Arrays->snapshotTable(); - Builder->SetInsertPoint(ThenBB); - for (const auto &S : Stmt->getThenStmts()) emitStmt(S.get()); - if (!Builder->GetInsertBlock()->hasTerminator()) Builder->CreateBr(MergeBB); - Arrays->exchangeTable(SavedArrays); - - Builder->SetInsertPoint(ElseBB); - for (const auto &S : Stmt->getElseStmts()) emitStmt(S.get()); - if (!Builder->GetInsertBlock()->hasTerminator()) Builder->CreateBr(MergeBB); - Arrays->exchangeTable(std::move(SavedArrays)); - - Builder->SetInsertPoint(MergeBB); -} - -void CodeGen::emitWhileStmt(WhileStmtAST *Stmt) { - Function *TheFunction = Builder->GetInsertBlock()->getParent(); - - BasicBlock *CondBB = BasicBlock::Create(*TheContext, "whilecond", TheFunction); - BasicBlock *LoopBB = BasicBlock::Create(*TheContext, "whileloop", TheFunction); - BasicBlock *AfterBB = BasicBlock::Create(*TheContext, "whilecont", TheFunction); - - Builder->CreateBr(CondBB); - - Builder->SetInsertPoint(CondBB); - Value *CondV = emitCoercedExpr(Stmt->getCond(), resolveType("BOOLEAN")); - if (!CondV) return; - - Builder->CreateCondBr(CondV, LoopBB, AfterBB); - - // Arrays declared in the body are block-scoped (see emitIfStmt). - auto SavedArrays = Arrays->snapshotTable(); - Builder->SetInsertPoint(LoopBB); - for (const auto &S : Stmt->getBody()) emitStmt(S.get()); - Arrays->exchangeTable(std::move(SavedArrays)); - - if (!Builder->GetInsertBlock()->hasTerminator()) - Builder->CreateBr(CondBB); - - Builder->SetInsertPoint(AfterBB); -} - -void CodeGen::emitRepeatStmt(RepeatStmtAST *Stmt) { - Function *TheFunction = Builder->GetInsertBlock()->getParent(); - - BasicBlock *LoopBB = BasicBlock::Create(*TheContext, "repeatloop", TheFunction); - BasicBlock *CondBB = BasicBlock::Create(*TheContext, "repeatcond", TheFunction); - BasicBlock *AfterBB = BasicBlock::Create(*TheContext, "repeatcont", TheFunction); - - Builder->CreateBr(LoopBB); - - // Arrays declared in the body are block-scoped (see emitIfStmt). - auto SavedArrays = Arrays->snapshotTable(); - Builder->SetInsertPoint(LoopBB); - for (const auto &S : Stmt->getBody()) emitStmt(S.get()); - Arrays->exchangeTable(std::move(SavedArrays)); - if (!Builder->GetInsertBlock()->hasTerminator()) - Builder->CreateBr(CondBB); - - Builder->SetInsertPoint(CondBB); - Value *CondV = emitCoercedExpr(Stmt->getCond(), resolveType("BOOLEAN")); - if (!CondV) return; - - Builder->CreateCondBr(CondV, AfterBB, LoopBB); - - Builder->SetInsertPoint(AfterBB); -} - -void CodeGen::emitForStmt(ForStmtAST *Stmt) { - Function *TheFunction = Builder->GetInsertBlock()->getParent(); - std::string VarName = Stmt->getVarName(); - - const SymbolInfo *Symbol = getSymbolInfo(VarName); - if (!Symbol) { - reportError("Unknown variable in FOR loop %s", VarName.c_str()); - return; - } - if (Symbol->TypeName != "INTEGER") { - reportError("FOR loop variable %s must be INTEGER", VarName.c_str()); - return; - } - - Value *StartVal = emitCoercedExpr(Stmt->getStart(), resolveType("INTEGER")); - if (!StartVal) return; - - Value *Alloca = Symbol->Storage; - Builder->CreateStore(StartVal, Alloca); - - // STEP is evaluated exactly once, before the loop. - Value *StepVal = nullptr; - if (Stmt->getStep()) { - StepVal = emitCoercedExpr(Stmt->getStep(), resolveType("INTEGER")); - if (!StepVal) return; - } else { - StepVal = ConstantInt::get(*TheContext, APInt(64, 1)); - } - - BasicBlock *CondBB = BasicBlock::Create(*TheContext, "forcond", TheFunction); - BasicBlock *LoopBB = BasicBlock::Create(*TheContext, "forloop", TheFunction); - BasicBlock *IncBB = BasicBlock::Create(*TheContext, "forinc", TheFunction); - BasicBlock *AfterBB = BasicBlock::Create(*TheContext, "forcont", TheFunction); - - Builder->CreateBr(CondBB); - Builder->SetInsertPoint(CondBB); - - Value *CurVar = Builder->CreateLoad(Type::getInt64Ty(*TheContext), Alloca, VarName.c_str()); - Value *EndVal = emitCoercedExpr(Stmt->getEnd(), resolveType("INTEGER")); - if (!EndVal) return; - - // The loop direction depends on the STEP value at run time. - Value *StepNonNeg = Builder->CreateICmpSGE(StepVal, - ConstantInt::get(*TheContext, APInt(64, 0)), - "step_nonneg"); - Value *CondUp = Builder->CreateICmpSLE(CurVar, EndVal, "forcond_le"); - Value *CondDown = Builder->CreateICmpSGE(CurVar, EndVal, "forcond_ge"); - Value *CondV = Builder->CreateSelect(StepNonNeg, CondUp, CondDown, "forcond"); - - Builder->CreateCondBr(CondV, LoopBB, AfterBB); - - // Arrays declared in the body are block-scoped (see emitIfStmt). - auto SavedArrays = Arrays->snapshotTable(); - Builder->SetInsertPoint(LoopBB); - for (const auto &S : Stmt->getBody()) emitStmt(S.get()); - Arrays->exchangeTable(std::move(SavedArrays)); - if (!Builder->GetInsertBlock()->hasTerminator()) - Builder->CreateBr(IncBB); - - Builder->SetInsertPoint(IncBB); - Value *CurValForInc = Builder->CreateLoad(Type::getInt64Ty(*TheContext), Alloca, VarName.c_str()); - Value *NextVal = Builder->CreateAdd(CurValForInc, StepVal, "nextval"); - Builder->CreateStore(NextVal, Alloca); - Builder->CreateBr(CondBB); - - Builder->SetInsertPoint(AfterBB); -} - -void CodeGen::emitStmt(StmtAST *Stmt) { - if (!Stmt) return; - - if (auto *FuncDef = dynamic_cast<FunctionDefAST*>(Stmt)) { - BasicBlock *SavedBlock = Builder->GetInsertBlock(); - // Function bodies get a fresh array scope (metadata SSA values belong - // to the emitting function), mirroring FunctionGen's Symbols handling. - auto SavedArrays = Arrays->exchangeTable({}); - - FuncGen->emitFunctionDef(FuncDef, [this](StmtAST *S) { - this->emitStmt(S); - }); - - Arrays->exchangeTable(std::move(SavedArrays)); - if (SavedBlock) Builder->SetInsertPoint(SavedBlock); - return; - } - - // Keep diagnosing statements after RETURN/THROW without appending code - // to a terminated block or making those statements reachable. - if (Builder->GetInsertBlock()->hasTerminator()) { - auto *F = Builder->GetInsertBlock()->getParent(); - Builder->SetInsertPoint(BasicBlock::Create(*TheContext, "unreachable_code", F)); - } - - if (auto *Try = dynamic_cast<TryStmtAST*>(Stmt)) { - emitTryStmt(Try); - return; - } - if (dynamic_cast<ThrowStmtAST*>(Stmt)) { - Exceptions->emitThrow(); - return; - } - if (auto *ArrDecl = dynamic_cast<ArrayDeclareStmtAST*>(Stmt)) { - Arrays->emitArrayDeclare(ArrDecl, *this); - return; - } - if (auto *ArrAssign = dynamic_cast<ArrayAssignStmtAST*>(Stmt)) { - Arrays->emitArrayAssign(ArrAssign, *this); - return; - } - - if (auto *Call = dynamic_cast<CallStmtAST*>(Stmt)) { - std::vector<Value*> Args; - if (!marshalCallArgs(Call->getCallee(), Call->getArgs(), Call->getLine(), Args)) return; - FuncGen->emitCallStmt(Call, Args); - return; - } - - if (auto *Ret = dynamic_cast<ReturnStmtAST*>(Stmt)) { - Function *F = Builder->GetInsertBlock()->getParent(); - const FuncSig *Sig = FuncGen->getSignature(F->getName().str()); - // Pseudocode cannot name a function "main" (reserved runtime symbol), - // so no signature means we are emitting the top level. - if (!Sig) { - reportError("RETURN is only allowed inside a FUNCTION or PROCEDURE"); - return; - } - if (F->getReturnType()->isVoidTy() && Ret->getRetVal()) { - reportError("RETURN with a value is only allowed in a FUNCTION"); - return; - } - if (!F->getReturnType()->isVoidTy() && !Ret->getRetVal()) { - reportError("RETURN in a FUNCTION requires a value"); - return; - } - Value *RetVal = nullptr; - if (Ret->getRetVal()) { - RetVal = emitCoercedExpr(Ret->getRetVal(), resolveType(Sig->ReturnTypeName)); - if (!RetVal) return; - } - FuncGen->emitReturn(Ret, RetVal); - return; - } - - if (auto *Decl = dynamic_cast<DeclareStmtAST*>(Stmt)) { - emitDeclareStmt(Decl); - return; - } - - if (auto *Assign = dynamic_cast<AssignStmtAST*>(Stmt)) { - const SymbolInfo *Info = getSymbolInfo(Assign->getName()); - if (!Info) { - if (Types->lookupEnumConstant(Assign->getName())) { - reportError("Enum value %s is not an assignable location", Assign->getName().c_str()); - } else { - reportError("Unknown variable name %s", Assign->getName().c_str()); - } - return; - } - if (Info->IsArray) { - reportError("Cannot assign array %s without indices", Assign->getName().c_str()); - return; - } - - Value *Val = emitCoercedExpr(Assign->getExpr(), resolveType(Info->TypeName)); - if (!Val) return; - - Builder->CreateStore(Val, Info->Storage); - return; - } - - if (auto *In = dynamic_cast<InputStmtAST*>(Stmt)) { - const SymbolInfo *Info = getSymbolInfo(In->getName()); - if (!Info) { - reportError("Unknown variable name %s", In->getName().c_str()); - return; - } - if (Info->IsArray) { - reportError("INPUT for entire array %s is not supported", In->getName().c_str()); - return; - } - - const TypeInfo *TypeInfo = resolveType(Info->TypeName); - if (!TypeInfo) { - reportError("Unknown type for INPUT %s", In->getName().c_str()); - return; - } - - std::vector<Value*> Args; - if (TypeInfo->isC()) { - emitCInput(Info, TypeInfo); - } else if (TypeInfo->isReal()) { - Args.push_back(ScanfFloatFormatStr); - Args.push_back(Info->Storage); - Builder->CreateCall(ScanfFunc, Args); - } else if (TypeInfo->isBoolean()) { - AllocaInst *TempInt = CreateEntryBlockAlloca(Builder->GetInsertBlock()->getParent(), "tmp_bool_input"); - Args.push_back(ScanfFormatStr); - Args.push_back(TempInt); - Builder->CreateCall(ScanfFunc, Args); - - Value *Val = Builder->CreateLoad(Type::getInt64Ty(*TheContext), TempInt); - Value *BoolVal = Builder->CreateICmpNE(Val, ConstantInt::get(*TheContext, APInt(64, 0)), "bool_cast"); - Builder->CreateStore(BoolVal, Info->Storage); - } else if (TypeInfo->isString()) { - Value *Mem = Builder->CreateCall(MallocFunc, ConstantInt::get(*TheContext, APInt(64, 1024)), "input_str"); - Args.push_back(ScanfStringFormatStr); - Args.push_back(Mem); - Builder->CreateCall(ScanfFunc, Args); - Builder->CreateStore(Mem, Info->Storage); - } else if (TypeInfo->isChar()) { - Value *CharFmt = Builder->CreateGlobalString(" %c", "fmt_in_char", 0, TheModule.get()); - Args.push_back(CharFmt); - Args.push_back(Info->Storage); - Builder->CreateCall(ScanfFunc, Args); - } else if (TypeInfo->Kind == TypeKind::Integer) { - Args.push_back(ScanfFormatStr); - Args.push_back(Info->Storage); - Builder->CreateCall(ScanfFunc, Args); - } else { - // Enum/Record/Pointer and any future kind: scanf'ing raw bytes - // into typed storage would corrupt it — reject explicitly. - reportError("INPUT is not supported for a value of type %s", TypeInfo->Name.c_str()); - } - return; - } - - if (auto *Out = dynamic_cast<OutputStmtAST*>(Stmt)) { - const auto &Exprs = Out->getExprs(); - for (size_t I = 0; I < Exprs.size(); ++I) { - ExprAST *Expr = Exprs[I].get(); - if (Arrays->tryEmitArrayOutput(Expr, *this)) continue; - - Value *Val = emitExpr(Expr); - const TypeInfo *TypeInfo = getExprTypeInfo(Expr); - if (!TypeInfo) { - reportError("Cannot infer the type of the OUTPUT expression"); - continue; - } - if (TypeInfo->isVoid()) { - reportError("Cannot OUTPUT the result of a PROCEDURE call"); - continue; - } - emitOutputValue(Val, TypeInfo, I + 1 == Exprs.size()); - } - return; - } - - if (auto *Open = dynamic_cast<OpenFileStmtAST*>(Stmt)) { - if (isWholeArrayVar(Open->getFileName())) { - reportError("Cannot use a whole array as the OPENFILE filename"); - return; - } - Value *Name = emitCoercedExpr(Open->getFileName(), resolveType("STRING")); - if (!Name) return; - Files->emitOpen(Name, Open->getMode(), Open->getLine()); - return; - } - - if (auto *Read = dynamic_cast<ReadFileStmtAST*>(Stmt)) { - const SymbolInfo *Info = getSymbolInfo(Read->getVarName()); - if (!Info) { - reportError("Unknown variable name %s", Read->getVarName().c_str()); - return; - } - if (Info->IsArray) { - reportError("READFILE into entire array %s is not supported", Read->getVarName().c_str()); - return; - } - if (Info->TypeName != "STRING") { - reportError("READFILE variable %s must be of type STRING (got %s)", - Read->getVarName().c_str(), Info->TypeName.c_str()); - return; - } - if (isWholeArrayVar(Read->getFileName())) { - reportError("Cannot use a whole array as the READFILE filename"); - return; - } - Value *Name = emitCoercedExpr(Read->getFileName(), resolveType("STRING")); - if (!Name) return; - Value *LineStr = Files->emitRead(Name, Read->getLine()); - if (!LineStr) return; - Builder->CreateStore(LineStr, Info->Storage); - return; - } - - if (auto *Write = dynamic_cast<WriteFileStmtAST*>(Stmt)) { - if (isWholeArrayVar(Write->getFileName()) || isWholeArrayVar(Write->getData())) { - reportError("Cannot use a whole array in WRITEFILE"); - return; - } - Value *Name = emitCoercedExpr(Write->getFileName(), resolveType("STRING")); - Value *Data = emitCoercedExpr(Write->getData(), resolveType("STRING")); - if (!Name || !Data) return; - Files->emitWrite(Name, Data, Write->getLine()); - return; - } - - if (auto *Close = dynamic_cast<CloseFileStmtAST*>(Stmt)) { - if (isWholeArrayVar(Close->getFileName())) { - reportError("Cannot use a whole array as the CLOSEFILE filename"); - return; - } - Value *Name = emitCoercedExpr(Close->getFileName(), resolveType("STRING")); - if (!Name) return; - Files->emitClose(Name, Close->getLine()); - return; - } - - if (auto *IfStmt = dynamic_cast<IfStmtAST*>(Stmt)) { - emitIfStmt(IfStmt); - return; - } - if (auto *WhileStmt = dynamic_cast<WhileStmtAST*>(Stmt)) { - emitWhileStmt(WhileStmt); - return; - } - if (auto *RepeatStmt = dynamic_cast<RepeatStmtAST*>(Stmt)) { - emitRepeatStmt(RepeatStmt); - return; - } - if (auto *ForStmt = dynamic_cast<ForStmtAST*>(Stmt)) { - emitForStmt(ForStmt); - return; - } - - // TYPE declarations are registered by compile()'s pre-pass (the parser - // guarantees they are top-level); nothing is emitted here. - if (dynamic_cast<EnumTypeDeclAST*>(Stmt) || dynamic_cast<PointerTypeDeclAST*>(Stmt) || - dynamic_cast<RecordTypeDeclAST*>(Stmt) || dynamic_cast<CTypeDeclAST*>(Stmt)) { - return; - } - if (auto *DA = dynamic_cast<DesignatorAssignStmtAST*>(Stmt)) { - LValueInfo LV; - if (!emitLValue(DA->getTarget(), LV)) return; - Value *Val = emitCoercedExpr(DA->getExpr(), resolveType(LV.TypeName)); - if (!Val) return; - Builder->CreateStore(Val, LV.Addr); - return; - } -} diff --git a/lib/CodeGen/ControlFlowGen.cc b/lib/CodeGen/ControlFlowGen.cc new file mode 100644 index 0000000..ebd5d3c --- /dev/null +++ b/lib/CodeGen/ControlFlowGen.cc @@ -0,0 +1,175 @@ +#include "cps/CodeGen.h" +#include "cps/ArrayHandler.h" +#include "cps/ExceptionHandler.h" + +using namespace llvm; +using namespace cps; + +void CodeGen::emitTryStmt(TryStmtAST *Stmt) { + auto *F = Builder->GetInsertBlock()->getParent(); + auto *CatchBB = BasicBlock::Create(*TheContext, "catch", F); + auto *AfterBB = BasicBlock::Create(*TheContext, "try_cont", F); + auto SavedArrays = Arrays->snapshotTable(); + + Exceptions->pushHandler(CatchBB); + for (const auto &S : Stmt->getBody()) emitStmt(S.get()); + auto *Exception = Exceptions->popHandler(); + if (!Builder->GetInsertBlock()->hasTerminator()) Builder->CreateBr(AfterBB); + Arrays->exchangeTable(SavedArrays); + + Builder->SetInsertPoint(CatchBB); + Exceptions->beginCatch(Exception); + for (const auto &S : Stmt->getCatchBody()) emitStmt(S.get()); + Exceptions->endCatch(); + if (!Builder->GetInsertBlock()->hasTerminator()) Builder->CreateBr(AfterBB); + Arrays->exchangeTable(std::move(SavedArrays)); + Builder->SetInsertPoint(AfterBB); +} + +void CodeGen::emitIfStmt(IfStmtAST *Stmt) { + Value *CondV = emitCoercedExpr(Stmt->getCond(), resolveType("BOOLEAN")); + if (!CondV) return; + + Function *TheFunction = Builder->GetInsertBlock()->getParent(); + BasicBlock *ThenBB = BasicBlock::Create(*TheContext, "then", TheFunction); + BasicBlock *ElseBB = BasicBlock::Create(*TheContext, "else", TheFunction); + BasicBlock *MergeBB = BasicBlock::Create(*TheContext, "ifcont", TheFunction); + + Builder->CreateCondBr(CondV, ThenBB, ElseBB); + + // Arrays are block-scoped: metadata SSA values and the buffer setup live + // inside the branch, so a declaration must not survive it. + auto SavedArrays = Arrays->snapshotTable(); + Builder->SetInsertPoint(ThenBB); + for (const auto &S : Stmt->getThenStmts()) emitStmt(S.get()); + if (!Builder->GetInsertBlock()->hasTerminator()) Builder->CreateBr(MergeBB); + Arrays->exchangeTable(SavedArrays); + + Builder->SetInsertPoint(ElseBB); + for (const auto &S : Stmt->getElseStmts()) emitStmt(S.get()); + if (!Builder->GetInsertBlock()->hasTerminator()) Builder->CreateBr(MergeBB); + Arrays->exchangeTable(std::move(SavedArrays)); + + Builder->SetInsertPoint(MergeBB); +} + +void CodeGen::emitWhileStmt(WhileStmtAST *Stmt) { + Function *TheFunction = Builder->GetInsertBlock()->getParent(); + + BasicBlock *CondBB = BasicBlock::Create(*TheContext, "whilecond", TheFunction); + BasicBlock *LoopBB = BasicBlock::Create(*TheContext, "whileloop", TheFunction); + BasicBlock *AfterBB = BasicBlock::Create(*TheContext, "whilecont", TheFunction); + + Builder->CreateBr(CondBB); + + Builder->SetInsertPoint(CondBB); + Value *CondV = emitCoercedExpr(Stmt->getCond(), resolveType("BOOLEAN")); + if (!CondV) return; + + Builder->CreateCondBr(CondV, LoopBB, AfterBB); + + // Arrays declared in the body are block-scoped (see emitIfStmt). + auto SavedArrays = Arrays->snapshotTable(); + Builder->SetInsertPoint(LoopBB); + for (const auto &S : Stmt->getBody()) emitStmt(S.get()); + Arrays->exchangeTable(std::move(SavedArrays)); + + if (!Builder->GetInsertBlock()->hasTerminator()) + Builder->CreateBr(CondBB); + + Builder->SetInsertPoint(AfterBB); +} + +void CodeGen::emitRepeatStmt(RepeatStmtAST *Stmt) { + Function *TheFunction = Builder->GetInsertBlock()->getParent(); + + BasicBlock *LoopBB = BasicBlock::Create(*TheContext, "repeatloop", TheFunction); + BasicBlock *CondBB = BasicBlock::Create(*TheContext, "repeatcond", TheFunction); + BasicBlock *AfterBB = BasicBlock::Create(*TheContext, "repeatcont", TheFunction); + + Builder->CreateBr(LoopBB); + + // Arrays declared in the body are block-scoped (see emitIfStmt). + auto SavedArrays = Arrays->snapshotTable(); + Builder->SetInsertPoint(LoopBB); + for (const auto &S : Stmt->getBody()) emitStmt(S.get()); + Arrays->exchangeTable(std::move(SavedArrays)); + if (!Builder->GetInsertBlock()->hasTerminator()) + Builder->CreateBr(CondBB); + + Builder->SetInsertPoint(CondBB); + Value *CondV = emitCoercedExpr(Stmt->getCond(), resolveType("BOOLEAN")); + if (!CondV) return; + + Builder->CreateCondBr(CondV, AfterBB, LoopBB); + + Builder->SetInsertPoint(AfterBB); +} + +void CodeGen::emitForStmt(ForStmtAST *Stmt) { + Function *TheFunction = Builder->GetInsertBlock()->getParent(); + std::string VarName = Stmt->getVarName(); + + const SymbolInfo *Symbol = getSymbolInfo(VarName); + if (!Symbol) { + reportError("Unknown variable in FOR loop %s", VarName.c_str()); + return; + } + if (Symbol->TypeName != "INTEGER") { + reportError("FOR loop variable %s must be INTEGER", VarName.c_str()); + return; + } + + Value *StartVal = emitCoercedExpr(Stmt->getStart(), resolveType("INTEGER")); + if (!StartVal) return; + + Value *Alloca = Symbol->Storage; + Builder->CreateStore(StartVal, Alloca); + + // STEP is evaluated exactly once, before the loop. + Value *StepVal = nullptr; + if (Stmt->getStep()) { + StepVal = emitCoercedExpr(Stmt->getStep(), resolveType("INTEGER")); + if (!StepVal) return; + } else { + StepVal = ConstantInt::get(*TheContext, APInt(64, 1)); + } + + BasicBlock *CondBB = BasicBlock::Create(*TheContext, "forcond", TheFunction); + BasicBlock *LoopBB = BasicBlock::Create(*TheContext, "forloop", TheFunction); + BasicBlock *IncBB = BasicBlock::Create(*TheContext, "forinc", TheFunction); + BasicBlock *AfterBB = BasicBlock::Create(*TheContext, "forcont", TheFunction); + + Builder->CreateBr(CondBB); + Builder->SetInsertPoint(CondBB); + + Value *CurVar = Builder->CreateLoad(Type::getInt64Ty(*TheContext), Alloca, VarName.c_str()); + Value *EndVal = emitCoercedExpr(Stmt->getEnd(), resolveType("INTEGER")); + if (!EndVal) return; + + // The loop direction depends on the STEP value at run time. + Value *StepNonNeg = Builder->CreateICmpSGE(StepVal, + ConstantInt::get(*TheContext, APInt(64, 0)), + "step_nonneg"); + Value *CondUp = Builder->CreateICmpSLE(CurVar, EndVal, "forcond_le"); + Value *CondDown = Builder->CreateICmpSGE(CurVar, EndVal, "forcond_ge"); + Value *CondV = Builder->CreateSelect(StepNonNeg, CondUp, CondDown, "forcond"); + + Builder->CreateCondBr(CondV, LoopBB, AfterBB); + + // Arrays declared in the body are block-scoped (see emitIfStmt). + auto SavedArrays = Arrays->snapshotTable(); + Builder->SetInsertPoint(LoopBB); + for (const auto &S : Stmt->getBody()) emitStmt(S.get()); + Arrays->exchangeTable(std::move(SavedArrays)); + if (!Builder->GetInsertBlock()->hasTerminator()) + Builder->CreateBr(IncBB); + + Builder->SetInsertPoint(IncBB); + Value *CurValForInc = Builder->CreateLoad(Type::getInt64Ty(*TheContext), Alloca, VarName.c_str()); + Value *NextVal = Builder->CreateAdd(CurValForInc, StepVal, "nextval"); + Builder->CreateStore(NextVal, Alloca); + Builder->CreateBr(CondBB); + + Builder->SetInsertPoint(AfterBB); +} 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; +} diff --git a/lib/CodeGen/OutputGen.cc b/lib/CodeGen/OutputGen.cc new file mode 100644 index 0000000..c4ceb79 --- /dev/null +++ b/lib/CodeGen/OutputGen.cc @@ -0,0 +1,95 @@ +#include "cps/CodeGen.h" + +using namespace llvm; +using namespace cps; + +Value *CodeGen::emitStringNullGuard(Value *StrVal) { + if (!StrVal || !StrVal->getType()->isPointerTy()) return StrVal; + Value *IsNull = Builder->CreateICmpEQ(StrVal, + ConstantPointerNull::get(cast<PointerType>(StrVal->getType())), + "str_is_null"); + return Builder->CreateSelect(IsNull, EmptyStringStr, StrVal, "safe_str"); +} + +void CodeGen::emitOutputValue(Value *Val, const TypeInfo *Info, bool AppendNewline) { + if (!Val || !Info) return; + + std::vector<Value*> Args; + Value *Fmt = nullptr; + + // Exhaustive over TypeKind (no default): the compiler warns when a new + // kind is added without deciding its OUTPUT story. + switch (Info->Kind) { + case TypeKind::Real: + Fmt = AppendNewline + ? PrintfFloatFormatStr + : Builder->CreateGlobalString("%f", "fmt_flt_inline", 0, TheModule.get()); + Args.push_back(Fmt); + Args.push_back(Val); + break; + + case TypeKind::Boolean: { + Value *BoolVal = coerceValueToType(Val, resolveType("BOOLEAN")); + Fmt = AppendNewline + ? PrintfStringFormatStr + : Builder->CreateGlobalString("%s", "fmt_str_inline", 0, TheModule.get()); + Args.push_back(Fmt); + Args.push_back(Builder->CreateSelect(BoolVal, TrueStr, FalseStr)); + break; + } + + case TypeKind::String: { + Value *StrVal = emitStringNullGuard(Val); + Fmt = AppendNewline + ? PrintfStringFormatStr + : Builder->CreateGlobalString("%s", "fmt_str_inline", 0, TheModule.get()); + Args.push_back(Fmt); + Args.push_back(StrVal); + break; + } + + case TypeKind::Char: { + Value *CharVal = coerceValueToType(Val, resolveType("CHAR")); + Value *Promoted = Builder->CreateZExt(CharVal, Type::getInt32Ty(*TheContext), "char_for_printf"); + Fmt = AppendNewline + ? PrintfCharFormatStr + : Builder->CreateGlobalString("%c", "fmt_chr_inline", 0, TheModule.get()); + Args.push_back(Fmt); + Args.push_back(Promoted); + break; + } + + case TypeKind::Integer: + case TypeKind::Enum: { // an enum prints its ordinal (user decision) + Value *IntVal = Info->isEnum() ? Val : coerceValueToType(Val, resolveType("INTEGER")); + Fmt = AppendNewline + ? PrintfFormatStr + : Builder->CreateGlobalString("%lld", "fmt_int_inline", 0, TheModule.get()); + Args.push_back(Fmt); + Args.push_back(IntVal); + break; + } + + case TypeKind::Record: + reportError("Cannot OUTPUT a value of record type %s; output its fields individually", + Info->Name.c_str()); + return; + + case TypeKind::Pointer: + reportError("Cannot OUTPUT a value of pointer type %s", Info->Name.c_str()); + return; + + case TypeKind::CScalar: + emitCOutput(Val, Info, AppendNewline); + return; + case TypeKind::CPointer: + reportError("OUTPUT is not supported for type %s", Info->Name.c_str()); + return; + + case TypeKind::Void: + reportError("Cannot OUTPUT a VOID value"); + return; + } + + Builder->CreateCall(PrintfFunc, Args); +} diff --git a/lib/CodeGen/StatementGen.cc b/lib/CodeGen/StatementGen.cc new file mode 100644 index 0000000..f10d78b --- /dev/null +++ b/lib/CodeGen/StatementGen.cc @@ -0,0 +1,282 @@ +#include "cps/CodeGen.h" +#include "cps/ArrayHandler.h" +#include "cps/FileAST.h" +#include "cps/FileHandler.h" +#include "cps/ExceptionHandler.h" +#include "cps/TypeAST.h" + +using namespace llvm; +using namespace cps; + +void CodeGen::emitStmt(StmtAST *Stmt) { + if (!Stmt) return; + + if (auto *FuncDef = dynamic_cast<FunctionDefAST*>(Stmt)) { + BasicBlock *SavedBlock = Builder->GetInsertBlock(); + // Function bodies get a fresh array scope (metadata SSA values belong + // to the emitting function), mirroring FunctionGen's Symbols handling. + auto SavedArrays = Arrays->exchangeTable({}); + + FuncGen->emitFunctionDef(FuncDef, [this](StmtAST *S) { + this->emitStmt(S); + }); + + Arrays->exchangeTable(std::move(SavedArrays)); + if (SavedBlock) Builder->SetInsertPoint(SavedBlock); + return; + } + + // Keep diagnosing statements after RETURN/THROW without appending code + // to a terminated block or making those statements reachable. + if (Builder->GetInsertBlock()->hasTerminator()) { + auto *F = Builder->GetInsertBlock()->getParent(); + Builder->SetInsertPoint(BasicBlock::Create(*TheContext, "unreachable_code", F)); + } + + if (auto *Try = dynamic_cast<TryStmtAST*>(Stmt)) { + emitTryStmt(Try); + return; + } + if (dynamic_cast<ThrowStmtAST*>(Stmt)) { + Exceptions->emitThrow(); + return; + } + if (auto *ArrDecl = dynamic_cast<ArrayDeclareStmtAST*>(Stmt)) { + Arrays->emitArrayDeclare(ArrDecl, *this); + return; + } + if (auto *ArrAssign = dynamic_cast<ArrayAssignStmtAST*>(Stmt)) { + Arrays->emitArrayAssign(ArrAssign, *this); + return; + } + + if (auto *Call = dynamic_cast<CallStmtAST*>(Stmt)) { + std::vector<Value*> Args; + if (!marshalCallArgs(Call->getCallee(), Call->getArgs(), Call->getLine(), Args)) return; + FuncGen->emitCallStmt(Call, Args); + return; + } + + if (auto *Ret = dynamic_cast<ReturnStmtAST*>(Stmt)) { + Function *F = Builder->GetInsertBlock()->getParent(); + const FuncSig *Sig = FuncGen->getSignature(F->getName().str()); + // Pseudocode cannot name a function "main" (reserved runtime symbol), + // so no signature means we are emitting the top level. + if (!Sig) { + reportError("RETURN is only allowed inside a FUNCTION or PROCEDURE"); + return; + } + if (F->getReturnType()->isVoidTy() && Ret->getRetVal()) { + reportError("RETURN with a value is only allowed in a FUNCTION"); + return; + } + if (!F->getReturnType()->isVoidTy() && !Ret->getRetVal()) { + reportError("RETURN in a FUNCTION requires a value"); + return; + } + Value *RetVal = nullptr; + if (Ret->getRetVal()) { + RetVal = emitCoercedExpr(Ret->getRetVal(), resolveType(Sig->ReturnTypeName)); + if (!RetVal) return; + } + FuncGen->emitReturn(Ret, RetVal); + return; + } + + if (auto *Decl = dynamic_cast<DeclareStmtAST*>(Stmt)) { + emitDeclareStmt(Decl); + return; + } + + if (auto *Assign = dynamic_cast<AssignStmtAST*>(Stmt)) { + const SymbolInfo *Info = getSymbolInfo(Assign->getName()); + if (!Info) { + if (Types->lookupEnumConstant(Assign->getName())) { + reportError("Enum value %s is not an assignable location", Assign->getName().c_str()); + } else { + reportError("Unknown variable name %s", Assign->getName().c_str()); + } + return; + } + if (Info->IsArray) { + reportError("Cannot assign array %s without indices", Assign->getName().c_str()); + return; + } + + Value *Val = emitCoercedExpr(Assign->getExpr(), resolveType(Info->TypeName)); + if (!Val) return; + + Builder->CreateStore(Val, Info->Storage); + return; + } + + if (auto *In = dynamic_cast<InputStmtAST*>(Stmt)) { + const SymbolInfo *Info = getSymbolInfo(In->getName()); + if (!Info) { + reportError("Unknown variable name %s", In->getName().c_str()); + return; + } + if (Info->IsArray) { + reportError("INPUT for entire array %s is not supported", In->getName().c_str()); + return; + } + + const TypeInfo *TypeInfo = resolveType(Info->TypeName); + if (!TypeInfo) { + reportError("Unknown type for INPUT %s", In->getName().c_str()); + return; + } + + std::vector<Value*> Args; + if (TypeInfo->isC()) { + emitCInput(Info, TypeInfo); + } else if (TypeInfo->isReal()) { + Args.push_back(ScanfFloatFormatStr); + Args.push_back(Info->Storage); + Builder->CreateCall(ScanfFunc, Args); + } else if (TypeInfo->isBoolean()) { + AllocaInst *TempInt = CreateEntryBlockAlloca(Builder->GetInsertBlock()->getParent(), "tmp_bool_input"); + Args.push_back(ScanfFormatStr); + Args.push_back(TempInt); + Builder->CreateCall(ScanfFunc, Args); + + Value *Val = Builder->CreateLoad(Type::getInt64Ty(*TheContext), TempInt); + Value *BoolVal = Builder->CreateICmpNE(Val, ConstantInt::get(*TheContext, APInt(64, 0)), "bool_cast"); + Builder->CreateStore(BoolVal, Info->Storage); + } else if (TypeInfo->isString()) { + Value *Mem = Builder->CreateCall(MallocFunc, ConstantInt::get(*TheContext, APInt(64, 1024)), "input_str"); + Args.push_back(ScanfStringFormatStr); + Args.push_back(Mem); + Builder->CreateCall(ScanfFunc, Args); + Builder->CreateStore(Mem, Info->Storage); + } else if (TypeInfo->isChar()) { + Value *CharFmt = Builder->CreateGlobalString(" %c", "fmt_in_char", 0, TheModule.get()); + Args.push_back(CharFmt); + Args.push_back(Info->Storage); + Builder->CreateCall(ScanfFunc, Args); + } else if (TypeInfo->Kind == TypeKind::Integer) { + Args.push_back(ScanfFormatStr); + Args.push_back(Info->Storage); + Builder->CreateCall(ScanfFunc, Args); + } else { + // Enum/Record/Pointer and any future kind: scanf'ing raw bytes + // into typed storage would corrupt it — reject explicitly. + reportError("INPUT is not supported for a value of type %s", TypeInfo->Name.c_str()); + } + return; + } + + if (auto *Out = dynamic_cast<OutputStmtAST*>(Stmt)) { + const auto &Exprs = Out->getExprs(); + for (size_t I = 0; I < Exprs.size(); ++I) { + ExprAST *Expr = Exprs[I].get(); + if (Arrays->tryEmitArrayOutput(Expr, *this)) continue; + + Value *Val = emitExpr(Expr); + const TypeInfo *TypeInfo = getExprTypeInfo(Expr); + if (!TypeInfo) { + reportError("Cannot infer the type of the OUTPUT expression"); + continue; + } + if (TypeInfo->isVoid()) { + reportError("Cannot OUTPUT the result of a PROCEDURE call"); + continue; + } + emitOutputValue(Val, TypeInfo, I + 1 == Exprs.size()); + } + return; + } + + if (auto *Open = dynamic_cast<OpenFileStmtAST*>(Stmt)) { + if (isWholeArrayVar(Open->getFileName())) { + reportError("Cannot use a whole array as the OPENFILE filename"); + return; + } + Value *Name = emitCoercedExpr(Open->getFileName(), resolveType("STRING")); + if (!Name) return; + Files->emitOpen(Name, Open->getMode(), Open->getLine()); + return; + } + + if (auto *Read = dynamic_cast<ReadFileStmtAST*>(Stmt)) { + const SymbolInfo *Info = getSymbolInfo(Read->getVarName()); + if (!Info) { + reportError("Unknown variable name %s", Read->getVarName().c_str()); + return; + } + if (Info->IsArray) { + reportError("READFILE into entire array %s is not supported", Read->getVarName().c_str()); + return; + } + if (Info->TypeName != "STRING") { + reportError("READFILE variable %s must be of type STRING (got %s)", + Read->getVarName().c_str(), Info->TypeName.c_str()); + return; + } + if (isWholeArrayVar(Read->getFileName())) { + reportError("Cannot use a whole array as the READFILE filename"); + return; + } + Value *Name = emitCoercedExpr(Read->getFileName(), resolveType("STRING")); + if (!Name) return; + Value *LineStr = Files->emitRead(Name, Read->getLine()); + if (!LineStr) return; + Builder->CreateStore(LineStr, Info->Storage); + return; + } + + if (auto *Write = dynamic_cast<WriteFileStmtAST*>(Stmt)) { + if (isWholeArrayVar(Write->getFileName()) || isWholeArrayVar(Write->getData())) { + reportError("Cannot use a whole array in WRITEFILE"); + return; + } + Value *Name = emitCoercedExpr(Write->getFileName(), resolveType("STRING")); + Value *Data = emitCoercedExpr(Write->getData(), resolveType("STRING")); + if (!Name || !Data) return; + Files->emitWrite(Name, Data, Write->getLine()); + return; + } + + if (auto *Close = dynamic_cast<CloseFileStmtAST*>(Stmt)) { + if (isWholeArrayVar(Close->getFileName())) { + reportError("Cannot use a whole array as the CLOSEFILE filename"); + return; + } + Value *Name = emitCoercedExpr(Close->getFileName(), resolveType("STRING")); + if (!Name) return; + Files->emitClose(Name, Close->getLine()); + return; + } + + if (auto *IfStmt = dynamic_cast<IfStmtAST*>(Stmt)) { + emitIfStmt(IfStmt); + return; + } + if (auto *WhileStmt = dynamic_cast<WhileStmtAST*>(Stmt)) { + emitWhileStmt(WhileStmt); + return; + } + if (auto *RepeatStmt = dynamic_cast<RepeatStmtAST*>(Stmt)) { + emitRepeatStmt(RepeatStmt); + return; + } + if (auto *ForStmt = dynamic_cast<ForStmtAST*>(Stmt)) { + emitForStmt(ForStmt); + return; + } + + // TYPE declarations are registered by compile()'s pre-pass (the parser + // guarantees they are top-level); nothing is emitted here. + if (dynamic_cast<EnumTypeDeclAST*>(Stmt) || dynamic_cast<PointerTypeDeclAST*>(Stmt) || + dynamic_cast<RecordTypeDeclAST*>(Stmt) || dynamic_cast<CTypeDeclAST*>(Stmt)) { + return; + } + if (auto *DA = dynamic_cast<DesignatorAssignStmtAST*>(Stmt)) { + LValueInfo LV; + if (!emitLValue(DA->getTarget(), LV)) return; + Value *Val = emitCoercedExpr(DA->getExpr(), resolveType(LV.TypeName)); + if (!Val) return; + Builder->CreateStore(Val, LV.Addr); + return; + } +} diff --git a/lib/CodeGen/ValueTypeGen.cc b/lib/CodeGen/ValueTypeGen.cc new file mode 100644 index 0000000..70def81 --- /dev/null +++ b/lib/CodeGen/ValueTypeGen.cc @@ -0,0 +1,274 @@ +#include "cps/CodeGen.h" +#include "cps/Lexer.h" +#include "cps/TypeAST.h" +#include "Builtins.h" + +using namespace llvm; +using namespace cps; + +using namespace cps::detail; + +const TypeInfo *CodeGen::getExprTypeInfo(ExprAST *Expr) const { + if (!Expr) return nullptr; + + if (dynamic_cast<IntegerExprAST*>(Expr)) return resolveType("INTEGER"); + if (dynamic_cast<RealExprAST*>(Expr)) return resolveType("REAL"); + if (dynamic_cast<BooleanExprAST*>(Expr)) return resolveType("BOOLEAN"); + if (dynamic_cast<CharExprAST*>(Expr)) return resolveType("CHAR"); + if (dynamic_cast<StringExprAST*>(Expr)) return resolveType("STRING"); + if (auto *Cast = dynamic_cast<CastExprAST*>(Expr)) + return resolveType(Cast->getTypeName()); + + if (auto *Var = dynamic_cast<VariableExprAST*>(Expr)) { + const SymbolInfo *Info = getSymbolInfo(Var->getName()); + if (Info) return resolveType(Info->TypeName); + if (const EnumConstant *EC = Types->lookupEnumConstant(Var->getName())) { + return resolveType(EC->TypeName); + } + return nullptr; + } + + if (auto *Arr = dynamic_cast<ArrayAccessExprAST*>(Expr)) { + const SymbolInfo *Info = getSymbolInfo(Arr->getName()); + return Info ? resolveType(Info->TypeName) : nullptr; + } + + if (auto *D = dynamic_cast<DesignatorExprAST*>(Expr)) { + const SymbolInfo *Info = getSymbolInfo(D->getBaseName()); + if (!Info) return nullptr; + std::string TypeName = Info->TypeName; + size_t Start = 0; + if (Info->IsArray) { + // SymbolInfo already holds the ELEMENT type name for arrays. + if (D->getAccesses().empty() || D->getAccesses()[0].Kind != AccessKind::Index) + return nullptr; + Start = 1; + } + for (size_t I = Start; I < D->getAccesses().size(); ++I) { + const TypeInfo *Cur = resolveType(TypeName); + if (!Cur) return nullptr; + const DesignatorAccess &A = D->getAccesses()[I]; + if (A.Kind == AccessKind::Field) { + const RecordPayload *RP = Cur->asRecord(); + const RecordFieldInfo *FI = RP ? RP->findField(A.FieldName) : nullptr; + if (!FI) return nullptr; + if (!FI->IsPublic) return nullptr; + TypeName = FI->TypeName; + } else if (A.Kind == AccessKind::Deref) { + const PointerPayload *PP = Cur->asPointer(); + if (!PP) return nullptr; + TypeName = PP->PointeeTypeName; + } else { + return nullptr; // indexing a non-array + } + } + return resolveType(TypeName); + } + + // ^x has no pseudocode type of its own; only the pointer admission in + // emitCoercedExpr may consume it. + if (dynamic_cast<AddrOfExprAST*>(Expr)) return nullptr; + + if (auto *Unary = dynamic_cast<UnaryExprAST*>(Expr)) { + if (Unary->getOp() == tok_not) { + return resolveType("BOOLEAN"); + } + const TypeInfo *Operand = getExprTypeInfo(Unary->getOperand()); + if (Operand && Operand->isC()) return Types->promoteCScalar(Operand); + if (Operand && !Operand->isC() && !Operand->isUserKind()) + return Operand->isReal() ? resolveType("REAL") : resolveType("INTEGER"); + return nullptr; + } + + if (auto *Bin = dynamic_cast<BinaryExprAST*>(Expr)) { + switch (Bin->getOp()) { + case tok_eq: + case tok_ne: + case '<': + case '>': + case tok_le: + case tok_ge: + case tok_and: + case tok_or: + return resolveType("BOOLEAN"); + case '&': + return resolveType("STRING"); + case '/': + return resolveType("REAL"); + default: + break; + } + + const TypeInfo *L = getExprTypeInfo(Bin->getLHS()); + const TypeInfo *R = getExprTypeInfo(Bin->getRHS()); + if ((L && L->isC()) || (R && R->isC())) return Types->commonCScalar(L, R); + // Enum arithmetic: E +/- INTEGER (enum on the left) yields the enum; + // every other user-kind combination has no type (emission rejects it). + if ((L && L->isUserKind()) || (R && R->isUserKind())) { + if (L && L->isEnum() && R && R->Kind == TypeKind::Integer && + (Bin->getOp() == '+' || Bin->getOp() == '-')) { + return L; + } + return nullptr; + } + if ((L && L->Kind == TypeKind::Real) || (R && R->Kind == TypeKind::Real)) { + return resolveType("REAL"); + } + return resolveType("INTEGER"); + } + + if (auto *Call = dynamic_cast<CallExprAST*>(Expr)) { + const std::string &Name = Call->getCallee(); + if (const BuiltinInfo *B = findBuiltin(Name)) { + return resolveType(B->ReturnTypeName); + } + if (const FuncSig *Sig = FuncGen->getSignature(Name)) { + return resolveType(Sig->ReturnTypeName); + } + + Function *CalleeF = TheModule->getFunction(Name); + if (!CalleeF) return resolveType("INTEGER"); + + Type *RetTy = CalleeF->getReturnType(); + if (RetTy->isVoidTy()) return resolveType("VOID"); + if (RetTy->isDoubleTy()) return resolveType("REAL"); + if (RetTy->isIntegerTy(1)) return resolveType("BOOLEAN"); + if (RetTy->isIntegerTy(8)) return resolveType("CHAR"); + if (RetTy->isPointerTy()) return resolveType("STRING"); + return resolveType("INTEGER"); + } + + return nullptr; +} + +Value *CodeGen::coerceValueToType(Value *Val, const TypeInfo *TargetInfo) { + if (!Val || !TargetInfo || !TargetInfo->LLVMType) return nullptr; + + if (Val->getType() == TargetInfo->LLVMType) { + return Val; + } + + switch (TargetInfo->Kind) { + case TypeKind::Real: + if (Val->getType()->isDoubleTy()) return Val; + if (Val->getType()->isIntegerTy(1)) + return Builder->CreateUIToFP(Val, Type::getDoubleTy(*TheContext), "bool_to_real"); + if (Val->getType()->isIntegerTy(8)) + return Builder->CreateUIToFP(Val, Type::getDoubleTy(*TheContext), "char_to_real"); + if (Val->getType()->isIntegerTy()) + return Builder->CreateSIToFP(Val, Type::getDoubleTy(*TheContext), "int_to_real"); + break; + + case TypeKind::Integer: + if (Val->getType()->isIntegerTy(64)) return Val; + if (Val->getType()->isDoubleTy()) + return Builder->CreateFPToSI(Val, Type::getInt64Ty(*TheContext), "real_to_int"); + if (Val->getType()->isIntegerTy(1)) + return Builder->CreateZExt(Val, Type::getInt64Ty(*TheContext), "bool_to_int"); + if (Val->getType()->isIntegerTy(8)) + return Builder->CreateZExt(Val, Type::getInt64Ty(*TheContext), "char_to_int"); + if (Val->getType()->isIntegerTy()) + return Builder->CreateSExtOrTrunc(Val, Type::getInt64Ty(*TheContext), "int_cast"); + break; + + case TypeKind::Boolean: + if (Val->getType()->isIntegerTy(1)) return Val; + if (Val->getType()->isIntegerTy()) + return Builder->CreateICmpNE(Val, Constant::getNullValue(Val->getType()), "to_bool"); + if (Val->getType()->isDoubleTy()) + return Builder->CreateFCmpONE(Val, ConstantFP::get(*TheContext, APFloat(0.0)), "to_bool"); + if (Val->getType()->isPointerTy()) + return Builder->CreateICmpNE(Val, + ConstantPointerNull::get(cast<PointerType>(Val->getType())), + "ptr_to_bool"); + break; + + case TypeKind::Char: + if (Val->getType()->isIntegerTy(8)) return Val; + if (Val->getType()->isIntegerTy(1)) + return Builder->CreateZExt(Val, Type::getInt8Ty(*TheContext), "bool_to_char"); + if (Val->getType()->isDoubleTy()) + return Builder->CreateFPToUI(Val, Type::getInt8Ty(*TheContext), "real_to_char"); + if (Val->getType()->isIntegerTy()) + return Builder->CreateTruncOrBitCast(Val, Type::getInt8Ty(*TheContext), "int_to_char"); + break; + + case TypeKind::String: + if (Val->getType()->isPointerTy()) return Val; + if (Val->getType()->isIntegerTy(1)) { + return Builder->CreateSelect(Val, TrueStr, FalseStr, "bool_to_str"); + } + if (Val->getType()->isDoubleTy()) { + return StrConvHandler->emitNumToStr(Val, true); + } + if (Val->getType()->isIntegerTy(8)) { + Value *Mem = Builder->CreateCall(MallocFunc, + ConstantInt::get(*TheContext, APInt(64, 2)), + "char_str"); + Builder->CreateStore(Val, Mem); + Value *NullPtr = Builder->CreateInBoundsGEP(Type::getInt8Ty(*TheContext), + Mem, + ConstantInt::get(*TheContext, APInt(64, 1))); + Builder->CreateStore(ConstantInt::get(Type::getInt8Ty(*TheContext), 0), NullPtr); + return Mem; + } + if (Val->getType()->isIntegerTy()) { + Value *AsInt = coerceValueToType(Val, resolveType("INTEGER")); + return AsInt ? StrConvHandler->emitNumToStr(AsInt, false) : nullptr; + } + break; + + case TypeKind::Void: + return nullptr; + + // Enum/Record/Pointer identity lives in the type NAME; at this level + // an enum is a plain i64 and a pointer a plain ptr, so nothing can be + // checked here. emitCoercedExpr is the only legitimate door. + case TypeKind::Enum: + case TypeKind::Record: + case TypeKind::Pointer: + case TypeKind::CScalar: + case TypeKind::CPointer: + reportError("Internal: a %s value reached representation coercion without a name-level check", + TargetInfo->Name.c_str()); + return nullptr; + } + + reportError("Cannot coerce value of LLVM type to target type %s", + TargetInfo->Name.c_str()); + return nullptr; +} + +void CodeGen::emitDeclareStmt(DeclareStmtAST *Stmt) { + const TypeInfo *Info = resolveType(Stmt->getType()); + if (!Info || !Info->LLVMType || Info->isVoid()) { + reportError("Unknown type %s", Stmt->getType().c_str()); + return; + } + if (Types->lookupEnumConstant(Stmt->getName())) { + reportError("'%s' is an enum value and cannot be declared as a variable", + Stmt->getName().c_str()); + return; + } + if (Types->resolve(Stmt->getName())) { + reportError("'%s' is a type name and cannot be declared as a variable", + Stmt->getName().c_str()); + return; + } + + Function *TheFunction = Builder->GetInsertBlock()->getParent(); + AllocaInst *Alloca = CreateEntryBlockAlloca(TheFunction, Info->LLVMType, Stmt->getName()); + + Value *InitVal = nullptr; + if (Info->isString()) { + InitVal = EmptyStringStr; + } else { + InitVal = Types->getZeroValue(Info->Name); + } + + if (InitVal) { + Builder->CreateStore(InitVal, Alloca); + } + + registerSymbol(Stmt->getName(), Alloca, Info->Name, false); +} |
