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/Parser | |
| 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/Parser')
| -rw-r--r-- | lib/Parser/Parser.cc | 18 | ||||
| -rw-r--r-- | lib/Parser/ParserControlFlow.cc | 86 | ||||
| -rw-r--r-- | lib/Parser/ParserExpression.cc | 4 | ||||
| -rw-r--r-- | lib/Parser/ParserFunction.cc | 3 |
4 files changed, 106 insertions, 5 deletions
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<std::unique_ptr<StmtAST>> 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<std::unique_ptr<StmtAST>> Parser::ParseBlock(std::initializer_list<int> Terminators) { +std::vector<std::unique_ptr<StmtAST>> Parser::ParseBlock(std::initializer_list<int> Terminators, bool CaseBody) { auto AtTerminator = [&] { for (int T : Terminators) { if (CurTok == T) return true; @@ -67,14 +71,16 @@ std::vector<std::unique_ptr<StmtAST>> Parser::ParseBlock(std::initializer_list<i }; ++BlockDepth; // 1 = top level (Parse() calls ParseBlock({})) + if (CaseBody) CaseBodyDepths.push_back(BlockDepth); std::vector<std::unique_ptr<StmtAST>> 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<StmtAST> Parser::ParseStatement() { } std::unique_ptr<StmtAST> 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<StmtAST> Parser::ParseForStmt() { return std::make_unique<ForStmtAST>(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<StmtAST> 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<CaseBranch> 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<CaseStmtAST>(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<ExprAST> 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<ExprAST> Parser::ParseIdentifierExpr() { // Postfix accessor chain after an identifier: '[' exprs ']' | '.' IDENT | '^' bool Parser::ParseDesignatorSuffix(std::vector<DesignatorAccess> &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<StmtAST> Parser::ParseReturnStmt() { getNextToken(); std::unique_ptr<ExprAST> 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) { |
