diff --git a/fine/src/parser.rs b/fine/src/parser.rs index 0c4a8800..9d05d3b5 100644 --- a/fine/src/parser.rs +++ b/fine/src/parser.rs @@ -395,6 +395,15 @@ impl<'a> CParser<'a> { // eprintln!("{}: {}: {}", self.current.start, self.current, msg); // } + fn at_any(&self, kinds: &[TokenKind]) -> bool { + for kind in kinds { + if self.at(*kind) { + return true; + } + } + return false; + } + fn at(&self, kind: TokenKind) -> bool { self.peek() == kind } @@ -586,6 +595,8 @@ fn field_decl(p: &mut CParser) { p.end(m, TreeKind::FieldDecl); } +const PARAM_LIST_RECOVERY: &[TokenKind] = &[TokenKind::Arrow, TokenKind::LeftBrace, TokenKind::Fun]; + fn param_list(p: &mut CParser) { let m = p.start(); @@ -594,7 +605,10 @@ fn param_list(p: &mut CParser) { if p.at(TokenKind::Identifier) { parameter(p); } else { - break; + if p.at_any(PARAM_LIST_RECOVERY) { + break; + } + p.advance_with_error("expected parameter"); } } p.expect(TokenKind::RightParen, "expect ')' to end a parameter list"); diff --git a/fine/tests/errors/resilience/README.md b/fine/tests/errors/resilience/README.md new file mode 100644 index 00000000..ed743d3d --- /dev/null +++ b/fine/tests/errors/resilience/README.md @@ -0,0 +1,4 @@ +These tests all produce errors in their parse, but the point is that +the trees are kinda as best as we can get. + +See e.g. https://matklad.github.io/2023/05/21/resilient-ll-parsing-tutorial.html diff --git a/fine/tests/errors/resilience/function_extra_comma.fine b/fine/tests/errors/resilience/function_extra_comma.fine new file mode 100644 index 00000000..a8d571b9 --- /dev/null +++ b/fine/tests/errors/resilience/function_extra_comma.fine @@ -0,0 +1,53 @@ +fun f1(x: f64, + +fun f2(x: f64,, z: f64) {} + +fun f3() {} + +// @concrete: +// | File +// | FunctionDecl +// | Fun:'"fun"' +// | Identifier:'"f1"' +// | ParamList +// | LeftParen:'"("' +// | Parameter +// | Identifier:'"x"' +// | Colon:'":"' +// | TypeExpression +// | Identifier:'"f64"' +// | Comma:'","' +// | Error:'"Error at 'fun': expect ')' to end a parameter list"' +// | FunctionDecl +// | Fun:'"fun"' +// | Identifier:'"f2"' +// | ParamList +// | LeftParen:'"("' +// | Parameter +// | Identifier:'"x"' +// | Colon:'":"' +// | TypeExpression +// | Identifier:'"f64"' +// | Comma:'","' +// | Error +// | Error:'"Error at ',': expected parameter"' +// | Comma:'","' +// | Parameter +// | Identifier:'"z"' +// | Colon:'":"' +// | TypeExpression +// | Identifier:'"f64"' +// | RightParen:'")"' +// | Block +// | LeftBrace:'"{"' +// | RightBrace:'"}"' +// | FunctionDecl +// | Fun:'"fun"' +// | Identifier:'"f3"' +// | ParamList +// | LeftParen:'"("' +// | RightParen:'")"' +// | Block +// | LeftBrace:'"{"' +// | RightBrace:'"}"' +// diff --git a/fine/tests/errors/resilience/incomplete_function.fine b/fine/tests/errors/resilience/incomplete_function.fine new file mode 100644 index 00000000..2a627c13 --- /dev/null +++ b/fine/tests/errors/resilience/incomplete_function.fine @@ -0,0 +1,57 @@ +fun fib_rec(f1: f64, + +fun fib(n: f64) -> f64 { + fib_rec(1, 1, n) +} + +// @concrete: +// | File +// | FunctionDecl +// | Fun:'"fun"' +// | Identifier:'"fib_rec"' +// | ParamList +// | LeftParen:'"("' +// | Parameter +// | Identifier:'"f1"' +// | Colon:'":"' +// | TypeExpression +// | Identifier:'"f64"' +// | Comma:'","' +// | Error:'"Error at 'fun': expect ')' to end a parameter list"' +// | FunctionDecl +// | Fun:'"fun"' +// | Identifier:'"fib"' +// | ParamList +// | LeftParen:'"("' +// | Parameter +// | Identifier:'"n"' +// | Colon:'":"' +// | TypeExpression +// | Identifier:'"f64"' +// | RightParen:'")"' +// | ReturnType +// | Arrow:'"->"' +// | TypeExpression +// | Identifier:'"f64"' +// | Block +// | LeftBrace:'"{"' +// | ExpressionStatement +// | CallExpression +// | Identifier +// | Identifier:'"fib_rec"' +// | ArgumentList +// | LeftParen:'"("' +// | Argument +// | LiteralExpression +// | Number:'"1"' +// | Comma:'","' +// | Argument +// | LiteralExpression +// | Number:'"1"' +// | Comma:'","' +// | Argument +// | Identifier +// | Identifier:'"n"' +// | RightParen:'")"' +// | RightBrace:'"}"' +// diff --git a/fine/tests/errors/unbalanced_right_brace.fine b/fine/tests/errors/resilience/unbalanced_right_brace.fine similarity index 100% rename from fine/tests/errors/unbalanced_right_brace.fine rename to fine/tests/errors/resilience/unbalanced_right_brace.fine