aboutsummaryrefslogtreecommitdiff
path: root/lib/Parser
diff options
context:
space:
mode:
author_ov4 <ov4@ov4.org>2026-10-16 10:04:27 +0000
committer_ov4 <ov4@ov4.org>2026-10-16 10:04:27 +0000
commit45c9d37eb27d204a6202b33cb37e890af0171357 (patch)
treedba23170b16053c5e1ec4bf78efc2a533cb94a4e /lib/Parser
parent522742e9df7fc5ac596ce4aa53d41b31e4f6a030 (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.cc18
-rw-r--r--lib/Parser/ParserControlFlow.cc86
-rw-r--r--lib/Parser/ParserExpression.cc4
-rw-r--r--lib/Parser/ParserFunction.cc3
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) {