From 680c059acbd6b45501d033b0141088bccac608f2 Mon Sep 17 00:00:00 2001 From: Tim Peters Date: Wed, 9 Mar 2022 22:06:40 +0100 Subject: [PATCH] Work on improving parser errors. Specifically, allow for multiple errors per parse. --- lox-compiler/src/bettercompiler/statements.rs | 1 + lox-syntax/src/ast.rs | 2 ++ lox-syntax/src/expr_parser.rs | 4 ++-- lox-syntax/src/lib.rs | 6 +++++- lox-syntax/src/parser.rs | 11 +++++++++++ lox-syntax/src/position.rs | 6 ++++++ lox-syntax/src/stmt_parser.rs | 9 +++++++-- test.lox | 12 +++--------- 8 files changed, 37 insertions(+), 14 deletions(-) diff --git a/lox-compiler/src/bettercompiler/statements.rs b/lox-compiler/src/bettercompiler/statements.rs index 8127a7f..90dd1b7 100644 --- a/lox-compiler/src/bettercompiler/statements.rs +++ b/lox-compiler/src/bettercompiler/statements.rs @@ -41,6 +41,7 @@ fn compile_stmt(compiler: &mut Compiler, stmt: &WithSpan) { compile_class(compiler, identifier.as_ref(), extends.as_ref(), stmts) } Stmt::Import(path, identifiers) => compile_import(compiler, path, identifiers.as_ref()), + Stmt::Error => (), } } diff --git a/lox-syntax/src/ast.rs b/lox-syntax/src/ast.rs index 5580459..fc980e0 100644 --- a/lox-syntax/src/ast.rs +++ b/lox-syntax/src/ast.rs @@ -45,6 +45,7 @@ pub enum Expr { Call(Box>, Vec>), Get(Box>, WithSpan), Set(Box>, WithSpan, Box>), + Error, } #[derive(Debug, PartialEq, Clone)] @@ -63,6 +64,7 @@ pub enum Stmt { Vec>, ), Import(WithSpan, Option>>), + Error, } pub type Ast = Vec>; diff --git a/lox-syntax/src/expr_parser.rs b/lox-syntax/src/expr_parser.rs index 4854daf..402448e 100644 --- a/lox-syntax/src/expr_parser.rs +++ b/lox-syntax/src/expr_parser.rs @@ -71,7 +71,7 @@ fn parse_infix(it: &mut Parser, left: WithSpan) -> Result, TokenKind::Dot => parse_get(it, left), _ => { it.error(&format!("Unexpected {}", it.peek_token().value), it.peek_token().span); - Err(()) + Ok(WithSpan::empty(Expr::Error)) }, } } @@ -90,7 +90,7 @@ fn parse_prefix(it: &mut Parser) -> Result, ()> { TokenKind::LeftParen => parse_grouping(it), _ => { it.error(&format!("Unexpected {}", it.peek_token().value), it.peek_token().span); - Err(()) + Ok(WithSpan::empty(Expr::Error)) }, } } diff --git a/lox-syntax/src/lib.rs b/lox-syntax/src/lib.rs index f3c5e57..d854f36 100644 --- a/lox-syntax/src/lib.rs +++ b/lox-syntax/src/lib.rs @@ -18,7 +18,11 @@ pub fn parse(code: &str) -> Result> { let tokens = tokenize_with_context(code); let mut parser = crate::parser::Parser::new(&tokens); match parse(&mut parser) { - Ok(ast) => Ok(ast), + Ok(ast) if parser.diagnostics().is_empty() => Ok(ast), + Ok(ast) => { + println!("Faulty AST: {:#?}", ast); + Err(parser.diagnostics().to_vec()) + }, Err(_) => Err(parser.diagnostics().to_vec()), } } diff --git a/lox-syntax/src/parser.rs b/lox-syntax/src/parser.rs index 6e2a918..4292963 100644 --- a/lox-syntax/src/parser.rs +++ b/lox-syntax/src/parser.rs @@ -65,6 +65,17 @@ impl<'a> Parser<'a> { } } + pub fn expect2(&mut self, expected: TokenKind) -> Option { + let token = self.peek_token(); + let kind = TokenKind::from(token); + if kind != expected { + self.error(&format!("Expected {} got {}", expected, token.value), token.span); + return None; + } + + Some(self.advance().span) + } + pub fn optionally(&mut self, expected: TokenKind) -> Result { let token = self.peek(); if TokenKind::from(token) == expected { diff --git a/lox-syntax/src/position.rs b/lox-syntax/src/position.rs index 8941b3f..76d41b0 100644 --- a/lox-syntax/src/position.rs +++ b/lox-syntax/src/position.rs @@ -32,6 +32,12 @@ impl Span { pub fn union_span(a: Self, b: Self) -> Self { use std::cmp; + if a == Span::empty() { + return b; + } else if b == Span::empty() { + return a; + } + Span { start: cmp::min(a.start, b.start), end: cmp::max(a.end, b.end), diff --git a/lox-syntax/src/stmt_parser.rs b/lox-syntax/src/stmt_parser.rs index fa41776..b40fc32 100644 --- a/lox-syntax/src/stmt_parser.rs +++ b/lox-syntax/src/stmt_parser.rs @@ -181,9 +181,14 @@ fn parse_return_statement(it: &mut Parser) -> Result, ()> { fn parse_expr_statement(it: &mut Parser) -> Result, ()> { let expr = parse_expr(it)?; - let end_span = it.expect(TokenKind::Semicolon)?; + let end_span = match it.expect2(TokenKind::Semicolon) { + Some(token) => token, + None => { + return Ok(WithSpan::empty(Stmt::Error)); + }, + }; - let span = Span::union(&expr, end_span); + let span = Span::union_span(expr.span, end_span); Ok(WithSpan::new(Stmt::Expression(Box::new(expr)), span)) } diff --git a/test.lox b/test.lox index de445be..2c3db6c 100644 --- a/test.lox +++ b/test.lox @@ -1,9 +1,3 @@ -{fun fib(n) { - if (n < 2) return n; - return fib(n - 2) + fib(n - 1); -} - -var start = clock(); -print fib(35) == 9227465; -print clock() - start; -} \ No newline at end of file +// 3 +*4; +// 3*; +print (3-); \ No newline at end of file