From 45c9d37eb27d204a6202b33cb37e890af0171357 Mon Sep 17 00:00:00 2001 From: _ov4 Date: Fri, 16 Oct 2026 10:04:27 +0000 Subject: feat(control-flow): implement ordered CASE OF matching Co-Authored-By: Claude Fable 5.1 --- lib/CodeGen/CTypeGen.cc | 20 ++++------ lib/CodeGen/ControlFlowGen.cc | 58 +++++++++++++++++++++++++++ lib/CodeGen/ExpressionGen.cc | 59 ++++++++++++++-------------- lib/CodeGen/StatementGen.cc | 4 ++ lib/CodeGen/TypeCheckGen.cc | 15 ++----- lib/Lexer/Lexer.cc | 46 +++++++++++++++++++++- lib/Parser/Parser.cc | 18 +++++++-- lib/Parser/ParserControlFlow.cc | 86 +++++++++++++++++++++++++++++++++++++++++ lib/Parser/ParserExpression.cc | 4 +- lib/Parser/ParserFunction.cc | 3 +- 10 files changed, 251 insertions(+), 62 deletions(-) (limited to 'lib') 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(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(Stmt)) { + emitCaseStmt(Case); + return; + } if (auto *IfStmt = dynamic_cast(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; } diff --git a/lib/Lexer/Lexer.cc b/lib/Lexer/Lexer.cc index 31bafe1..fcc9a40 100644 --- a/lib/Lexer/Lexer.cc +++ b/lib/Lexer/Lexer.cc @@ -7,12 +7,14 @@ using namespace cps; -int Lexer::gettok() { +int Lexer::readToken() { while (isspace(LastChar)) { if (LastChar == '\n') CurrentLine++; LastChar = Input.get(); } + TokenLine = CurrentLine; + if (LastChar == '"') { StringVal.clear(); LastChar = Input.get(); @@ -86,6 +88,10 @@ int Lexer::gettok() { if (IdentifierStr == "INPUT") return tok_input; if (IdentifierStr == "OUTPUT") return tok_output; + if (IdentifierStr == "CASE") return tok_case; + if (IdentifierStr == "ENDCASE") return tok_endcase; + if (IdentifierStr == "OTHERWISE") return tok_otherwise; + if (IdentifierStr == "IF") return tok_if; if (IdentifierStr == "THEN") return tok_then; if (IdentifierStr == "ELSE") return tok_else; @@ -225,7 +231,7 @@ int Lexer::gettok() { } while (LastChar != EOF && LastChar != '\n' && LastChar != '\r'); if (LastChar != EOF) - return gettok(); + return readToken(); } else { LastChar = NextChar; return '/'; @@ -239,3 +245,39 @@ int Lexer::gettok() { LastChar = Input.get(); return ThisChar; } + +Lexer::TokenData Lexer::captureToken(int Kind) const { + return {Kind, TokenLine, IdentifierStr, StringVal, NumVal, RealVal, CharVal}; +} + +void Lexer::restoreToken(const TokenData &Token) { + TokenLine = Token.Line; + IdentifierStr = Token.Identifier; + StringVal = Token.String; + NumVal = Token.Integer; + RealVal = Token.Real; + CharVal = Token.Character; +} + +int Lexer::gettok() { + if (Lookahead.empty()) return readToken(); + auto Token = std::move(Lookahead.front()); + Lookahead.pop_front(); + restoreToken(Token); + return Token.Kind; +} + +int Lexer::peekToken(unsigned Offset) { + auto Current = captureToken(0); + while (Lookahead.size() <= Offset) { + int Kind = readToken(); + Lookahead.push_back(captureToken(Kind)); + } + restoreToken(Current); + return Lookahead[Offset].Kind; +} + +int Lexer::peekLine(unsigned Offset) { + peekToken(Offset); + return Lookahead[Offset].Line; +} diff --git a/lib/Parser/Parser.cc b/lib/Parser/Parser.cc index 3e8a096..58422fc 100644 --- a/lib/Parser/Parser.cc +++ b/lib/Parser/Parser.cc @@ -29,7 +29,10 @@ Parser::Parser(Lexer &L) : Lex(L) { } int Parser::getNextToken() { - return CurTok = Lex.gettok(); + int PreviousLine = CurTok == tok_eof ? 0 : Lex.getLine(); + CurTok = Lex.gettok(); + StartsLine = Lex.getLine() != PreviousLine; + return CurTok; } std::vector> Parser::Parse() { @@ -43,6 +46,7 @@ void Parser::syncToStatementStart() { while (CurTok != tok_eof) { switch (CurTok) { case tok_declare: case tok_identifier: case tok_input: case tok_output: + case tok_case: case tok_endcase: case tok_otherwise: case tok_if: case tok_while: case tok_repeat: case tok_for: case tok_function: case tok_procedure: case tok_call: case tok_return: case tok_else: case tok_endif: case tok_endwhile: case tok_until: @@ -58,7 +62,7 @@ void Parser::syncToStatementStart() { } } -std::vector> Parser::ParseBlock(std::initializer_list Terminators) { +std::vector> Parser::ParseBlock(std::initializer_list Terminators, bool CaseBody) { auto AtTerminator = [&] { for (int T : Terminators) { if (CurTok == T) return true; @@ -67,14 +71,16 @@ std::vector> Parser::ParseBlock(std::initializer_list> Body; - while (!AtTerminator() && CurTok != tok_eof) { + while (!AtTerminator() && CurTok != tok_eof && !(CaseBody && isCaseLabel())) { if (auto Stmt = ParseStatement()) { Body.push_back(std::move(Stmt)); } else { syncToStatementStart(); } } + if (CaseBody) CaseBodyDepths.pop_back(); --BlockDepth; return Body; } @@ -86,6 +92,12 @@ std::unique_ptr Parser::ParseStatement() { } std::unique_ptr Parser::ParseStatementImpl() { + if (CurTok == tok_case) return ParseCaseStmt(); + if (CurTok == tok_endcase || CurTok == tok_otherwise) { + fprintf(stderr, "error: Unexpected CASE terminator at line %d\n", Lex.getLine()); + getNextToken(); + return nullptr; + } if (CurTok == tok_declare) { return ParseDeclare(); } diff --git a/lib/Parser/ParserControlFlow.cc b/lib/Parser/ParserControlFlow.cc index cb32466..d01e561 100644 --- a/lib/Parser/ParserControlFlow.cc +++ b/lib/Parser/ParserControlFlow.cc @@ -144,3 +144,89 @@ std::unique_ptr Parser::ParseForStmt() { return std::make_unique(VarName, std::move(Start), std::move(End), std::move(Step), std::move(Body)); } + +// A label begins a source line and ends in ':' on that line. Lookahead +// preserves token payloads; assignments and statement keywords are not labels. +bool Parser::isCaseLabel() { + if (!StartsLine) return false; + int Line = Lex.getLine(); + int Depth = 0; + for (unsigned I = 0;; ++I) { + int T = I == 0 ? CurTok : Lex.peekToken(I - 1); + int TokenLine = I == 0 ? Line : Lex.peekLine(I - 1); + if (TokenLine != Line) return false; + if (T == tok_colon) return Depth == 0; + switch (T) { + case '(': case '[': ++Depth; break; + case ')': case ']': if (--Depth < 0) return false; break; + case tok_identifier: case tok_number_int: case tok_number_real: + case tok_string_literal: case tok_char_literal: case tok_true: case tok_false: + case tok_integer_kw: case tok_real_kw: case tok_boolean_kw: + case tok_string_kw: case tok_char_kw: case tok_c_int: case tok_c_char: + case tok_c_string: case tok_c_double: + case tok_to: case tok_otherwise: case tok_and: case tok_or: case tok_not: + case tok_eq: case tok_ne: case tok_le: case tok_ge: case tok_div: case tok_mod: + case '+': case '-': case '*': case '/': case '&': case '^': + case '<': case '>': case '.': case ',': break; + default: return false; + } + } +} + +bool Parser::atCaseBoundary() { + return !ParsingCaseLabel && !CaseBodyDepths.empty() && + CaseBodyDepths.back() == BlockDepth && isCaseLabel(); +} + +std::unique_ptr Parser::ParseCaseStmt() { + getNextToken(); + if (CurTok != tok_of) { + fprintf(stderr, "error: Expected OF after CASE at line %d\n", Lex.getLine()); + return nullptr; + } + getNextToken(); + if (CurTok != tok_identifier) { + fprintf(stderr, "error: Expected identifier after CASE OF at line %d\n", Lex.getLine()); + return nullptr; + } + std::string Selector = Lex.IdentifierStr; + getNextToken(); + std::vector Branches; + bool SeenOtherwise = false; + while (CurTok != tok_endcase && CurTok != tok_eof) { + if (SeenOtherwise || !isCaseLabel()) { + fprintf(stderr, "error: Expected CASE label on a new line (OTHERWISE must be last) at line %d\n", Lex.getLine()); + return nullptr; + } + CaseBranch Branch; + Branch.Line = Lex.getLine(); + if (CurTok == tok_otherwise) { + SeenOtherwise = true; + getNextToken(); + } else { + bool Saved = ParsingCaseLabel; + ParsingCaseLabel = true; + Branch.Lower = ParseExpression(); + if (Branch.Lower && CurTok == tok_to) { + getNextToken(); + Branch.Upper = ParseExpression(); + if (!Branch.Upper) Branch.Lower.reset(); + } + ParsingCaseLabel = Saved; + if (!Branch.Lower) return nullptr; + } + if (CurTok != tok_colon) { + fprintf(stderr, "error: Expected ':' after CASE label at line %d\n", Lex.getLine()); + return nullptr; + } + getNextToken(); + Branch.Body = ParseBlock({tok_endcase}, true); + Branches.push_back(std::move(Branch)); + } + if (CurTok != tok_endcase || Branches.empty()) { + fprintf(stderr, "error: Expected nonempty CASE and ENDCASE at line %d\n", Lex.getLine()); + return nullptr; + } + getNextToken(); + return std::make_unique(std::move(Selector), std::move(Branches)); +} diff --git a/lib/Parser/ParserExpression.cc b/lib/Parser/ParserExpression.cc index 3e2665f..d32c407 100644 --- a/lib/Parser/ParserExpression.cc +++ b/lib/Parser/ParserExpression.cc @@ -5,6 +5,7 @@ using namespace cps; int Parser::GetTokPrecedence() { + if (atCaseBoundary()) return -1; if (BinopPrecedence.find(CurTok) == BinopPrecedence.end()) return -1; int TokPrec = BinopPrecedence[CurTok]; if (TokPrec <= 0) return -1; @@ -28,7 +29,7 @@ std::unique_ptr Parser::ParseIdentifierExpr() { int Line = Lex.getLine(); getNextToken(); - if (CurTok == '(') { + if (CurTok == '(' && !atCaseBoundary()) { getNextToken(); auto Args = ParseExprList(')', true); if (!Args) return nullptr; @@ -54,6 +55,7 @@ std::unique_ptr Parser::ParseIdentifierExpr() { // Postfix accessor chain after an identifier: '[' exprs ']' | '.' IDENT | '^' bool Parser::ParseDesignatorSuffix(std::vector &Out) { while (true) { + if (atCaseBoundary()) return true; if (CurTok == '[') { getNextToken(); auto Indices = ParseExprList(']', false); diff --git a/lib/Parser/ParserFunction.cc b/lib/Parser/ParserFunction.cc index 25375f2..c47485e 100644 --- a/lib/Parser/ParserFunction.cc +++ b/lib/Parser/ParserFunction.cc @@ -222,7 +222,8 @@ std::unique_ptr Parser::ParseReturnStmt() { getNextToken(); std::unique_ptr Expr = nullptr; // A bare RETURN is recognized right before any block terminator. - if (CurTok != tok_endif && CurTok != tok_else && CurTok != tok_endfunction && + if (!atCaseBoundary() && CurTok != tok_endcase && CurTok != tok_otherwise && + CurTok != tok_endif && CurTok != tok_else && CurTok != tok_endfunction && CurTok != tok_endprocedure && CurTok != tok_endwhile && CurTok != tok_until && CurTok != tok_next && CurTok != tok_catch && CurTok != tok_endtry && CurTok != tok_eof) { -- cgit v1.3.1-4-g156e