diff options
| author | _ov4 <ov4@ov4.org> | 2026-10-16 10:04:27 +0000 |
|---|---|---|
| committer | _ov4 <ov4@ov4.org> | 2026-10-16 10:04:27 +0000 |
| commit | 45c9d37eb27d204a6202b33cb37e890af0171357 (patch) | |
| tree | dba23170b16053c5e1ec4bf78efc2a533cb94a4e /lib/CodeGen | |
| parent | 522742e9df7fc5ac596ce4aa53d41b31e4f6a030 (diff) | |
feat(control-flow): implement ordered CASE OF matching
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Diffstat (limited to 'lib/CodeGen')
| -rw-r--r-- | lib/CodeGen/CTypeGen.cc | 20 | ||||
| -rw-r--r-- | lib/CodeGen/ControlFlowGen.cc | 58 | ||||
| -rw-r--r-- | lib/CodeGen/ExpressionGen.cc | 59 | ||||
| -rw-r--r-- | lib/CodeGen/StatementGen.cc | 4 | ||||
| -rw-r--r-- | lib/CodeGen/TypeCheckGen.cc | 15 |
5 files changed, 101 insertions, 55 deletions
diff --git a/lib/CodeGen/CTypeGen.cc b/lib/CodeGen/CTypeGen.cc index 22ecb9f..a20306e 100644 --- a/lib/CodeGen/CTypeGen.cc +++ b/lib/CodeGen/CTypeGen.cc @@ -78,10 +78,10 @@ Value *CodeGen::emitCast(CastExprAST *Cast) { return nullptr; } -Value *CodeGen::emitCBinaryOp(BinaryExprAST *Bin, const TypeInfo *Left, const TypeInfo *Right) { - int Op = Bin->getOp(); +Value *CodeGen::emitCBinaryOp(int Op, Value *L, const TypeInfo *Left, + Value *R, const TypeInfo *Right, int Line) { if (!Left || !Right || !Left->isC() || !Right->isC()) { - reportError("C and pseudocode operands require an explicit cast (line %d)", Bin->getLine()); + reportError("C and pseudocode operands require an explicit cast (line %d)", Line); return nullptr; } if (Left->isPointer() || Right->isPointer()) { @@ -90,9 +90,6 @@ Value *CodeGen::emitCBinaryOp(BinaryExprAST *Bin, const TypeInfo *Left, const Ty reportError("C pointers support only = and <> between the same C pointer type"); return nullptr; } - Value *L = emitExpr(Bin->getLHS()); - Value *R = emitExpr(Bin->getRHS()); - if (!L || !R) return nullptr; return Op == tok_eq ? Builder->CreateICmpEQ(L, R, "c_ptr_eq") : Builder->CreateICmpNE(L, R, "c_ptr_ne"); } @@ -102,12 +99,9 @@ Value *CodeGen::emitCBinaryOp(BinaryExprAST *Bin, const TypeInfo *Left, const Ty return nullptr; } if (Op == '&' || ((Op == tok_div || Op == tok_mod) && !Common->isIntegral())) { - reportError("Operator is not supported for these C operand types (line %d)", Bin->getLine()); + reportError("Operator is not supported for these C operand types (line %d)", Line); return nullptr; } - Value *L = emitExpr(Bin->getLHS()); - Value *R = emitExpr(Bin->getRHS()); - if (!L || !R) return nullptr; if (Op == tok_and || Op == tok_or) { L = convertNumericValue(L, Left, resolveType("BOOLEAN")); R = convertNumericValue(R, Right, resolveType("BOOLEAN")); @@ -119,7 +113,7 @@ Value *CodeGen::emitCBinaryOp(BinaryExprAST *Bin, const TypeInfo *Left, const Ty if (Op == '/') { L = convertNumericValue(L, Left, resolveType("REAL")); R = convertNumericValue(R, Right, resolveType("REAL")); - RuntimeChecker->emitDivZeroCheck(R, Bin->getLine()); + RuntimeChecker->emitDivZeroCheck(R, Line); return Builder->CreateFDiv(L, R, "c_real_div"); } L = convertNumericValue(L, Left, Common); @@ -142,7 +136,7 @@ Value *CodeGen::emitCBinaryOp(BinaryExprAST *Bin, const TypeInfo *Left, const Ty case '-': return Builder->CreateSub(L, R, "c_sub"); case '*': return Builder->CreateMul(L, R, "c_mul"); case tok_div: case tok_mod: - RuntimeChecker->emitDivZeroCheck(R, Bin->getLine()); + RuntimeChecker->emitDivZeroCheck(R, Line); if (Op == tok_div) return Common->CSigned ? Builder->CreateSDiv(L, R, "c_div") : Builder->CreateUDiv(L, R, "c_div"); @@ -160,7 +154,7 @@ Value *CodeGen::emitCBinaryOp(BinaryExprAST *Bin, const TypeInfo *Left, const Ty : Builder->CreateICmpUGE(L, R, "c_ge"); } } - reportError("Unsupported operator for C operands (line %d)", Bin->getLine()); + reportError("Unsupported operator for C operands (line %d)", Line); return nullptr; } diff --git a/lib/CodeGen/ControlFlowGen.cc b/lib/CodeGen/ControlFlowGen.cc index ebd5d3c..f9300dd 100644 --- a/lib/CodeGen/ControlFlowGen.cc +++ b/lib/CodeGen/ControlFlowGen.cc @@ -173,3 +173,61 @@ void CodeGen::emitForStmt(ForStmtAST *Stmt) { Builder->SetInsertPoint(AfterBB); } + +void CodeGen::emitCaseStmt(CaseStmtAST *Stmt) { + VariableExprAST Selector(Stmt->getSelector()); + if (isWholeArrayVar(&Selector)) { + reportError("CASE selector cannot be a whole array"); + return; + } + const TypeInfo *SelectorType = getExprTypeInfo(&Selector); + Value *Selected = emitExpr(&Selector); + if (!Selected || !SelectorType) return; + + auto *F = Builder->GetInsertBlock()->getParent(); + F->addFnAttr("no-jump-tables", "true"); + auto *After = BasicBlock::Create(*TheContext, "case_cont", F); + auto SavedArrays = Arrays->snapshotTable(); + for (const auto &Branch : Stmt->getBranches()) { + auto *Body = BasicBlock::Create(*TheContext, "case_body", F); + auto *Next = BasicBlock::Create(*TheContext, "case_next", F); + auto Compare = [&](int Op, ExprAST *Label) -> Value * { + if (isWholeArrayVar(Label)) { + reportError("CASE label cannot be a whole array"); + return nullptr; + } + const TypeInfo *LabelType = getExprTypeInfo(Label); + if (!LabelType) { + reportError("Cannot determine CASE label type at line %d", Branch.Line); + return nullptr; + } + Value *LabelValue = emitExpr(Label); + return emitBinaryOp(Op, Selected, SelectorType, LabelValue, LabelType, Branch.Line); + }; + if (!Branch.Lower) { + Builder->CreateBr(Body); + } else { + Value *Match = Compare(Branch.Upper ? tok_ge : tok_eq, Branch.Lower.get()); + if (!Match) { + // Finish the CFG even after a diagnostic; the driver discards it. + Builder->CreateBr(Next); + } else if (Branch.Upper) { + auto *Upper = BasicBlock::Create(*TheContext, "case_upper", F); + Builder->CreateCondBr(Match, Upper, Next); + Builder->SetInsertPoint(Upper); + Match = Compare(tok_le, Branch.Upper.get()); + if (Match) Builder->CreateCondBr(Match, Body, Next); + else Builder->CreateBr(Next); + } else { + Builder->CreateCondBr(Match, Body, Next); + } + } + Builder->SetInsertPoint(Body); + for (const auto &S : Branch.Body) emitStmt(S.get()); + if (!Builder->GetInsertBlock()->hasTerminator()) Builder->CreateBr(After); + Arrays->exchangeTable(SavedArrays); + Builder->SetInsertPoint(Next); + } + Builder->CreateBr(After); + Builder->SetInsertPoint(After); +} diff --git a/lib/CodeGen/ExpressionGen.cc b/lib/CodeGen/ExpressionGen.cc index 0d69408..9135d6c 100644 --- a/lib/CodeGen/ExpressionGen.cc +++ b/lib/CodeGen/ExpressionGen.cc @@ -117,38 +117,9 @@ Value *CodeGen::emitExpr(ExprAST *Expr) { // '&' 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()); + return emitBinaryOp(Bin->getOp(), L, LT, R, RT, Bin->getLine()); } if (auto *Call = dynamic_cast<CallExprAST*>(Expr)) { @@ -271,3 +242,31 @@ Value *CodeGen::emitExpr(ExprAST *Expr) { return nullptr; } + +// Ordinary binary expressions and CASE use the same typed operations. +Value *CodeGen::emitBinaryOp(int Op, Value *L, const TypeInfo *LT, + Value *R, const TypeInfo *RT, int Line) { + if (!L || !R) return nullptr; + if ((LT && LT->isC()) || (RT && RT->isC())) + return emitCBinaryOp(Op, L, LT, R, RT, Line); + if ((LT && LT->isUserKind()) || (RT && RT->isUserKind())) + return emitUserKindBinaryOp(Op, L, LT, R, RT, Line); + if (Op == '&') { + L = coerceValueToType(L, resolveType("STRING")); + R = coerceValueToType(R, resolveType("STRING")); + return L && R ? StrHandler->emitConcat(L, R) : nullptr; + } + switch (Op) { + 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)", Line); + return nullptr; + } + return StrHandler->emitCompare(Op, L, R); + } + break; + default: break; + } + return ArithHandler->emitBinaryOp(Op, L, R, Line); +} diff --git a/lib/CodeGen/StatementGen.cc b/lib/CodeGen/StatementGen.cc index f10d78b..8f8711b 100644 --- a/lib/CodeGen/StatementGen.cc +++ b/lib/CodeGen/StatementGen.cc @@ -248,6 +248,10 @@ void CodeGen::emitStmt(StmtAST *Stmt) { return; } + if (auto *Case = dynamic_cast<CaseStmtAST*>(Stmt)) { + emitCaseStmt(Case); + return; + } if (auto *IfStmt = dynamic_cast<IfStmtAST*>(Stmt)) { emitIfStmt(IfStmt); return; diff --git a/lib/CodeGen/TypeCheckGen.cc b/lib/CodeGen/TypeCheckGen.cc index a86fdb1..654afc8 100644 --- a/lib/CodeGen/TypeCheckGen.cc +++ b/lib/CodeGen/TypeCheckGen.cc @@ -121,8 +121,8 @@ Value *CodeGen::emitCoercedExpr(ExprAST *E, const TypeInfo *Target) { // Binary operators with at least one Enum/Record/Pointer operand. Decides // admissibility by type name and emits directly; ArithmeticHandler stays // ignorant of user kinds. -Value *CodeGen::emitUserKindBinaryOp(BinaryExprAST *Bin, const TypeInfo *LT, const TypeInfo *RT) { - int Op = Bin->getOp(); +Value *CodeGen::emitUserKindBinaryOp(int Op, Value *L, const TypeInfo *LT, + Value *R, const TypeInfo *RT, int Line) { const char *LName = LT ? LT->Name.c_str() : "?"; const char *RName = RT ? RT->Name.c_str() : "?"; @@ -135,9 +135,6 @@ Value *CodeGen::emitUserKindBinaryOp(BinaryExprAST *Bin, const TypeInfo *LT, con if ((LT && LT->isPointer()) || (RT && RT->isPointer())) { bool SameName = LT && RT && LT->isPointer() && RT->isPointer() && LT->Name == RT->Name; if (SameName && (Op == tok_eq || Op == tok_ne)) { - Value *L = emitExpr(Bin->getLHS()); - Value *R = emitExpr(Bin->getRHS()); - if (!L || !R) return nullptr; return Op == tok_eq ? Builder->CreateICmpEQ(L, R, "ptr_eq") : Builder->CreateICmpNE(L, R, "ptr_ne"); } @@ -152,9 +149,6 @@ Value *CodeGen::emitUserKindBinaryOp(BinaryExprAST *Bin, const TypeInfo *LT, con switch (Op) { case tok_eq: case tok_ne: case '<': case '>': case tok_le: case tok_ge: { if (!BothSameEnum) break; - Value *L = emitExpr(Bin->getLHS()); - Value *R = emitExpr(Bin->getRHS()); - if (!L || !R) return nullptr; switch (Op) { case tok_eq: return Builder->CreateICmpEQ(L, R, "enum_eq"); case tok_ne: return Builder->CreateICmpNE(L, R, "enum_ne"); @@ -170,13 +164,10 @@ Value *CodeGen::emitUserKindBinaryOp(BinaryExprAST *Bin, const TypeInfo *LT, con // The one legal arithmetic shape (guide ยง4.2): enum on the left, // INTEGER on the right, result checked against the enum's range. if (!(LT && LT->isEnum() && RT && RT->Kind == TypeKind::Integer)) break; - Value *L = emitExpr(Bin->getLHS()); - Value *R = emitExpr(Bin->getRHS()); - if (!L || !R) return nullptr; Value *Res = Op == '+' ? Builder->CreateAdd(L, R, "enum_step") : Builder->CreateSub(L, R, "enum_step"); RuntimeChecker->emitEnumRangeCheck(Res, LT->asEnum()->ValueNames.size(), - LT->Name, Bin->getLine()); + LT->Name, Line); return Res; } |
