aboutsummaryrefslogtreecommitdiff
path: root/lib/CodeGen
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
parentdb6ac29d0ac769ff780f3901ed987b97965f3f5e (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.h37
-rw-r--r--lib/CodeGen/CodeGen.cc1099
-rw-r--r--lib/CodeGen/ControlFlowGen.cc175
-rw-r--r--lib/CodeGen/ExpressionGen.cc273
-rw-r--r--lib/CodeGen/OutputGen.cc95
-rw-r--r--lib/CodeGen/StatementGen.cc282
-rw-r--r--lib/CodeGen/ValueTypeGen.cc274
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);
+}