diff --git a/src/parser.rs b/src/parser.rs index e7c595f..83c0507 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -42,7 +42,7 @@ impl Parser { let mut errors = Vec::new(); while !self.at_end() { - match self.parse_var_decl_or_pass() { + match self.parse_decl() { Ok(s) => statements.push(s), Err(e) => { errors.push(e); @@ -61,17 +61,30 @@ impl Parser { /// Statement parsing impl Parser { - #[tracing::instrument(name = "decl", skip_all)] - fn parse_var_decl_or_pass(&mut self) -> Result> { - match self.pop_if_matches(MatchToken::Var) { - Some(var) => self.finish_var_decl(&var), - None => self.parse_fun_decl_or_pass(), + fn parse_decl(&mut self) -> Result> { + match self.peek().map(|t| t.kind) { + Some(TokenKind::Var) => self.parse_var_decl(), + Some(TokenKind::Fun) => self.parse_fun_decl(), + _ => self.parse_stmt(), } } - /// Parse everything after `var` in a `VarDecl` statement - fn finish_var_decl(&mut self, var_token: &Token) -> Result> { - debug_assert_eq!(var_token.kind, TokenKind::Var); + fn parse_stmt(&mut self) -> Result> { + match self.peek().map(|t| t.kind) { + Some(TokenKind::LeftBrace) => self.parse_block_stmt(), + Some(TokenKind::If) => self.parse_if_stmt(), + Some(TokenKind::While) => self.parse_while_stmt(), + Some(TokenKind::For) => self.parse_for_stmt(), + Some(TokenKind::Return) => self.parse_return_stmt(), + Some(TokenKind::Print) => self.parse_print_stmt(), + _ => self.parse_expr_stmt(), + } + } + + #[tracing::instrument(name = "var", skip_all)] + fn parse_var_decl(&mut self) -> Result> { + let var_token = self.expect_token(MatchToken::Var)?; + let name = self.expect_identifier()?; let initializer = self .pop_if_matches(MatchToken::Eq) @@ -79,30 +92,20 @@ impl Parser { .transpose()?; let semi = self.expect_semicolon()?; - Ok(Stmt::var_decl(var_token, name, initializer, &semi)) + Ok(Stmt::var_decl(&var_token, name, initializer, &semi)) } - #[tracing::instrument(skip_all)] - fn parse_fun_decl_or_pass(&mut self) -> Result> { - let Some(fun_token) = self.pop_if_matches(MatchToken::Fun) else { - return self.parse_block_or_pass(); - }; + #[tracing::instrument(name = "fun", skip_all)] + fn parse_fun_decl(&mut self) -> Result> { + let fun_token = self.expect_token(MatchToken::Fun)?; let name = self.expect_identifier()?; - // consume the opening `(` self.expect_token(MatchToken::LeftParen)?; - - // Parse the params list let params = self.parse_comma_separated_list(MatchToken::RightParen, Self::expect_identifier)?; - - // Consume the closing `)` self.expect_token(MatchToken::RightParen)?; - - // consume the opening `{` let open_brace = self.expect_token(MatchToken::LeftBrace)?; - let (body, close_brace) = self.parse_block_body().map_err(|e| match e { ParseBlockError::ReachedEnd { last_stmt_span } => { let end = last_stmt_span @@ -112,14 +115,13 @@ impl Parser { } ParseBlockError::ParserError(e) => e, })?; + Ok(Stmt::function(&fun_token, name, params, body, &close_brace)) } #[tracing::instrument(name = "block", skip_all)] - fn parse_block_or_pass(&mut self) -> Result> { - let Some(open_brace) = self.pop_if_matches(MatchToken::LeftBrace) else { - return self.parse_if_or_pass(); - }; + fn parse_block_stmt(&mut self) -> Result> { + let open_brace = self.expect_token(MatchToken::LeftBrace)?; match self.parse_block_body() { Ok((inner_stmts, close_brace)) => { @@ -136,50 +138,46 @@ impl Parser { } #[tracing::instrument(name = "if", skip_all)] - fn parse_if_or_pass(&mut self) -> Result> { - let Some(if_token) = self.pop_if_matches(MatchToken::If) else { - return self.parse_while_or_pass(); - }; + fn parse_if_stmt(&mut self) -> Result> { + let if_token = self.expect_token(MatchToken::If)?; self.expect_token(MatchToken::LeftParen)?; let condition = self.parse_expr()?; self.expect_token(MatchToken::RightParen)?; - let then = self.parse_block_or_pass()?; + let then = self.parse_stmt()?; let otherwise = self .pop_if_matches(MatchToken::Else) - .map(|_| self.parse_block_or_pass()) + .map(|_| self.parse_stmt()) .transpose()?; Ok(Stmt::if_then(&if_token, condition, then, otherwise)) } #[tracing::instrument(name = "while", skip_all)] - fn parse_while_or_pass(&mut self) -> Result> { - let Some(while_token) = self.pop_if_matches(MatchToken::While) else { - return self.parse_for_or_pass(); - }; + fn parse_while_stmt(&mut self) -> Result> { + let while_token = self.expect_token(MatchToken::While)?; self.expect_token(MatchToken::LeftParen)?; let condition = self.parse_expr()?; self.expect_token(MatchToken::RightParen)?; - let body = self.parse_block_or_pass()?; + let body = self.parse_stmt()?; Ok(Stmt::while_stmt(&while_token, condition, body)) } #[tracing::instrument(name = "for", skip_all)] - fn parse_for_or_pass(&mut self) -> Result> { - let Some(for_token) = self.pop_if_matches(MatchToken::For) else { - return self.parse_return_or_pass(); - }; + fn parse_for_stmt(&mut self) -> Result> { + let for_token = self.expect_token(MatchToken::For)?; let left_paren = self.expect_token(MatchToken::LeftParen)?; let initializer = if self.pop_if_matches(MatchToken::Semicolon).is_some() { None - } else if let Some(var) = self.pop_if_matches(MatchToken::Var) { + // } else if let Some(var) = self.pop_if_matches(MatchToken::Var) { + } else if self.peek().is_some_and(|t| t.kind == MatchToken::Var) { // requires and consumes a semicolon - Some(self.finish_var_decl(&var)?) + let var_decl = self.parse_var_decl()?; + Some(var_decl) } else { let expr = self.parse_expr()?; let semi = self.expect_semicolon()?; @@ -202,7 +200,7 @@ impl Parser { Some((expr, right_paren)) }; - let body = self.parse_block_or_pass()?; + let body = self.parse_stmt()?; // desugar to a while loop wrapped in a block Ok(Stmt::for_stmt( @@ -232,7 +230,7 @@ impl Parser { return Err(ParseBlockError::ReachedEnd { last_stmt_span }); } - let stmt = self.parse_var_decl_or_pass()?; + let stmt = self.parse_decl()?; inner_stmts.push(stmt); }; @@ -240,10 +238,8 @@ impl Parser { } #[tracing::instrument(name = "return", skip_all)] - fn parse_return_or_pass(&mut self) -> Result> { - let Some(return_token) = self.pop_if_matches(MatchToken::Return) else { - return self.parse_print_or_expr_stmt(); - }; + fn parse_return_stmt(&mut self) -> Result> { + let return_token = self.expect_token(MatchToken::Return)?; if let Some(semicolon) = self.pop_if_matches(MatchToken::Semicolon) { return Ok(Stmt::return_stmt(&return_token, None, &semicolon)); @@ -255,16 +251,18 @@ impl Parser { Ok(Stmt::return_stmt(&return_token, Some(value), &semicolon)) } - #[tracing::instrument(name = "stmt", skip_all)] - fn parse_print_or_expr_stmt(&mut self) -> Result> { - let print_token = self.pop_if_matches(MatchToken::Print); + #[tracing::instrument(name = "print", skip_all)] + fn parse_print_stmt(&mut self) -> Result> { + let print_token = self.expect_token(MatchToken::Print)?; let expr = self.parse_expr()?; - let semi = self.expect_semicolon()?; + let semicolon = self.expect_semicolon()?; + Ok(Stmt::print(&print_token, expr, &semicolon)) + } - match print_token { - Some(print_token) => Ok(Stmt::print(&print_token, expr, &semi)), - None => Ok(Stmt::expr(expr, &semi)), - } + fn parse_expr_stmt(&mut self) -> Result> { + let expr = self.parse_expr()?; + let semicolon = self.expect_semicolon()?; + Ok(Stmt::expr(expr, &semicolon)) } /// Parse items in a comma-separated list until the next token is not a comma