From fdef2814d0d7e65573d1b666ae9bc561fa35a9f9 Mon Sep 17 00:00:00 2001 From: _ov4 Date: Sun, 26 Jul 2026 12:11:00 +0000 Subject: refactor: extract ParseBlock/ParseExprList, resync-based error recovery Seven copy-pasted statement-block loops collapse into ParseBlock(terminators); five comma-separated expression-list loops collapse into ParseExprList(close, allowEmpty), which also fixes "arr[1 2] <- x" being silently accepted as arr[1,2] on the statement path. Error recovery now lives in one place: syncToStatementStart skips to the next statement-start/block-end token, so a bad statement no longer swallows the first token of the following good one. Drop the dead -1000/-1001 token check in ParsePrototypeArgs. Co-Authored-By: Claude Fable 5 --- lib/Parser/Parser.cc | 151 +++++++++++++++++++++---------------------- lib/Parser/ParserFunction.cc | 34 ++-------- 2 files changed, 80 insertions(+), 105 deletions(-) (limited to 'lib/Parser') diff --git a/lib/Parser/Parser.cc b/lib/Parser/Parser.cc index 320fdf4..77df9bb 100644 --- a/lib/Parser/Parser.cc +++ b/lib/Parser/Parser.cc @@ -57,43 +57,18 @@ std::unique_ptr Parser::ParseIdentifierExpr() { if (CurTok == '(') { getNextToken(); - std::vector> Args; - if (CurTok != ')') { - while (true) { - if (auto Arg = ParseExpression()) - Args.push_back(std::move(Arg)); - else - return nullptr; - - if (CurTok == ')') break; - - if (CurTok != ',') { - fprintf(stderr, "Error: Expected ')' or ',' in function call.\n"); - return nullptr; - } - getNextToken(); - } - } - getNextToken(); - return std::make_unique(IdName, std::move(Args)); + auto Args = ParseExprList(')', true); + if (!Args) return nullptr; + return std::make_unique(IdName, std::move(*Args)); } - + if (CurTok == '[') { getNextToken(); - std::vector> Indices; - while (true) { - auto Exp = ParseExpression(); - if (!Exp) return nullptr; - Indices.push_back(std::move(Exp)); - if (CurTok == ']') break; - if (CurTok == ',') { getNextToken(); continue; } - fprintf(stderr, "Error: Expected ',' or ']'\n"); - return nullptr; - } - getNextToken(); - return std::make_unique(IdName, std::move(Indices), Line); + auto Indices = ParseExprList(']', false); + if (!Indices) return nullptr; + return std::make_unique(IdName, std::move(*Indices), Line); } - + return std::make_unique(IdName); } @@ -224,15 +199,67 @@ std::unique_ptr Parser::ParseExpression() { } std::vector> Parser::Parse() { - std::vector> Statements; + return ParseBlock({}); +} + +// After a statement fails to parse, skip ahead to the next token that can +// start a statement or end a block, so one bad statement cannot swallow the +// start of the next good one. +void Parser::syncToStatementStart() { while (CurTok != tok_eof) { + switch (CurTok) { + case tok_declare: case tok_identifier: case tok_input: case tok_output: + 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: + case tok_next: case tok_endfunction: case tok_endprocedure: + return; + default: + getNextToken(); + } + } +} + +std::vector> Parser::ParseBlock(std::initializer_list Terminators) { + auto AtTerminator = [&] { + for (int T : Terminators) { + if (CurTok == T) return true; + } + return false; + }; + + std::vector> Body; + while (!AtTerminator() && CurTok != tok_eof) { if (auto Stmt = ParseStatement()) { - Statements.push_back(std::move(Stmt)); + Body.push_back(std::move(Stmt)); } else { - if (CurTok != tok_eof) getNextToken(); + syncToStatementStart(); } } - return Statements; + return Body; +} + +std::optional>> Parser::ParseExprList(char CloseDelim, bool AllowEmpty) { + std::vector> Elems; + if (AllowEmpty && CurTok == CloseDelim) { + getNextToken(); + return Elems; + } + + while (true) { + auto E = ParseExpression(); + if (!E) return std::nullopt; + Elems.push_back(std::move(E)); + + if (CurTok == CloseDelim) break; + if (CurTok != ',') { + fprintf(stderr, "Error: Expected ',' or '%c' at line %d\n", CloseDelim, Lex.getLine()); + return std::nullopt; + } + getNextToken(); + } + getNextToken(); + return Elems; } std::unique_ptr Parser::ParseDeclare() { @@ -318,21 +345,12 @@ std::unique_ptr Parser::ParseIfStmt() { } getNextToken(); - std::vector> ThenStmts; - while (CurTok != tok_else && CurTok != tok_endif && CurTok != tok_eof) { - auto Stmt = ParseStatement(); - if (Stmt) ThenStmts.push_back(std::move(Stmt)); - else if (CurTok != tok_eof && CurTok != tok_else && CurTok != tok_endif) getNextToken(); - } + std::vector> ThenStmts = ParseBlock({tok_else, tok_endif}); std::vector> ElseStmts; if (CurTok == tok_else) { getNextToken(); - while (CurTok != tok_endif && CurTok != tok_eof) { - auto Stmt = ParseStatement(); - if (Stmt) ElseStmts.push_back(std::move(Stmt)); - else if (CurTok != tok_eof && CurTok != tok_endif) getNextToken(); - } + ElseStmts = ParseBlock({tok_endif}); } if (CurTok != tok_endif) { @@ -355,12 +373,7 @@ std::unique_ptr Parser::ParseWhileStmt() { } getNextToken(); - std::vector> Body; - while (CurTok != tok_endwhile && CurTok != tok_eof) { - auto Stmt = ParseStatement(); - if (Stmt) Body.push_back(std::move(Stmt)); - else if (CurTok != tok_eof && CurTok != tok_endwhile) getNextToken(); - } + std::vector> Body = ParseBlock({tok_endwhile}); if (CurTok != tok_endwhile) { fprintf(stderr, "Error: expected ENDWHILE\n"); @@ -374,12 +387,7 @@ std::unique_ptr Parser::ParseWhileStmt() { std::unique_ptr Parser::ParseRepeatStmt() { getNextToken(); - std::vector> Body; - while (CurTok != tok_until && CurTok != tok_eof) { - auto Stmt = ParseStatement(); - if (Stmt) Body.push_back(std::move(Stmt)); - else if (CurTok != tok_eof && CurTok != tok_until) getNextToken(); - } + std::vector> Body = ParseBlock({tok_until}); if (CurTok != tok_until) { fprintf(stderr, "Error: expected UNTIL\n"); @@ -427,12 +435,7 @@ std::unique_ptr Parser::ParseForStmt() { if (!Step) return nullptr; } - std::vector> Body; - while (CurTok != tok_next && CurTok != tok_eof) { - auto Stmt = ParseStatement(); - if (Stmt) Body.push_back(std::move(Stmt)); - else if (CurTok != tok_eof && CurTok != tok_next) getNextToken(); - } + std::vector> Body = ParseBlock({tok_next}); if (CurTok != tok_next) { fprintf(stderr, "Error: expected NEXT\n"); @@ -472,16 +475,9 @@ std::unique_ptr Parser::ParseStatementImpl() { if (CurTok == '[') { getNextToken(); - std::vector> Indices; - while (true) { - auto Exp = ParseExpression(); - if (!Exp) return nullptr; - Indices.push_back(std::move(Exp)); - if (CurTok == ']') break; - if (CurTok == ',') getNextToken(); - } - getNextToken(); - + auto Indices = ParseExprList(']', false); + if (!Indices) return nullptr; + if (CurTok != tok_assign) { fprintf(stderr, "Error: Expected '<-' after array access in assignment\n"); return nullptr; @@ -489,7 +485,7 @@ std::unique_ptr Parser::ParseStatementImpl() { getNextToken(); auto Expr = ParseExpression(); if (!Expr) return nullptr; - return std::make_unique(Name, std::move(Indices), std::move(Expr), Line); + return std::make_unique(Name, std::move(*Indices), std::move(Expr), Line); } if (CurTok != tok_assign) { @@ -543,5 +539,6 @@ std::unique_ptr Parser::ParseStatementImpl() { } fprintf(stderr, "Error: Unexpected token at line %d when expecting a statement\n", Lex.getLine()); + getNextToken(); // guarantee progress so error recovery cannot loop forever return nullptr; } diff --git a/lib/Parser/ParserFunction.cc b/lib/Parser/ParserFunction.cc index 06348c4..6ce0b8e 100644 --- a/lib/Parser/ParserFunction.cc +++ b/lib/Parser/ParserFunction.cc @@ -11,8 +11,6 @@ std::optional>> Parser::P getNextToken(); while (CurTok != ')') { - if (CurTok == -1000 || CurTok == -1001) getNextToken(); - bool IsRef = false; if (CurTok == tok_byref) { IsRef = true; @@ -79,12 +77,7 @@ std::unique_ptr Parser::ParseFunction() { auto Proto = std::make_unique(Name, std::move(*Args), RetType); - std::vector> Body; - while (CurTok != tok_endfunction && CurTok != tok_eof) { - auto Stmt = ParseStatement(); - if (Stmt) Body.push_back(std::move(Stmt)); - else if (CurTok != tok_eof && CurTok != tok_endfunction) getNextToken(); - } + std::vector> Body = ParseBlock({tok_endfunction}); if (CurTok != tok_endfunction) { fprintf(stderr, "Error: expected ENDFUNCTION\n"); @@ -109,12 +102,7 @@ std::unique_ptr Parser::ParseProcedure() { if (!Args) return nullptr; auto Proto = std::make_unique(Name, std::move(*Args), "VOID"); - std::vector> Body; - while (CurTok != tok_endprocedure && CurTok != tok_eof) { - auto Stmt = ParseStatement(); - if (Stmt) Body.push_back(std::move(Stmt)); - else if (CurTok != tok_eof && CurTok != tok_endprocedure) getNextToken(); - } + std::vector> Body = ParseBlock({tok_endprocedure}); if (CurTok != tok_endprocedure) { fprintf(stderr, "Error: expected ENDPROCEDURE\n"); @@ -138,21 +126,11 @@ std::unique_ptr Parser::ParseCallStmt() { std::vector> Args; if (CurTok == '(') { getNextToken(); - while (CurTok != ')') { - auto Arg = ParseExpression(); - if (Arg) Args.push_back(std::move(Arg)); - else return nullptr; - - if (CurTok == ')') break; - if (CurTok != ',') { - fprintf(stderr, "Error: Expected ',' or ')' in call\n"); - return nullptr; - } - getNextToken(); - } - getNextToken(); + auto Parsed = ParseExprList(')', true); + if (!Parsed) return nullptr; + Args = std::move(*Parsed); } - + return std::make_unique(Callee, std::move(Args)); } -- cgit v1.3.1-4-g156e