From d631548d3f1aa78d1e235294b4014a48807019b5 Mon Sep 17 00:00:00 2001 From: Alex van de Sandt Date: Thu, 11 Jul 2024 22:09:26 -0400 Subject: [PATCH] Add a couple parser helpers --- src/match_token.rs | 69 +++++++++++++++++++++++++++- src/parser.rs | 109 ++++++++++++++++++--------------------------- 2 files changed, 111 insertions(+), 67 deletions(-) diff --git a/src/match_token.rs b/src/match_token.rs index 6523454..49e343d 100644 --- a/src/match_token.rs +++ b/src/match_token.rs @@ -1,27 +1,47 @@ use crate::token::TokenKind; -#[derive(Clone, Copy, Debug, PartialEq)] +#[derive(Clone, Copy, Debug, PartialEq, strum::Display)] +#[strum(serialize_all = "snake_case")] #[allow(dead_code)] pub enum MatchToken { + #[strum(serialize = "(")] LeftParen, + #[strum(serialize = ")")] RightParen, + #[strum(serialize = "{")] LeftBrace, + #[strum(serialize = "}")] RightBrace, + #[strum(serialize = ",")] Comma, + #[strum(serialize = ".")] Dot, + #[strum(serialize = "-")] Minus, + #[strum(serialize = "+")] Plus, + #[strum(serialize = ";")] Semicolon, + #[strum(serialize = "/")] Slash, + #[strum(serialize = "*")] Star, + #[strum(serialize = "!")] Bang, + #[strum(serialize = "!=")] BangEq, + #[strum(serialize = "=")] Eq, + #[strum(serialize = "==")] EqEq, + #[strum(serialize = ">")] Greater, + #[strum(serialize = ">=")] GreaterEq, + #[strum(serialize = "<")] Less, + #[strum(serialize = "<=")] LessEq, Ident, @@ -46,6 +66,53 @@ pub enum MatchToken { While, } +/* +impl From for TokenKind { + fn from(value: MatchToken) -> Self { + match value { + MatchToken::LeftParen => Self::LeftParen, + MatchToken::RightParen => Self::RightParen, + MatchToken::LeftBrace => Self::LeftBrace, + MatchToken::RightBrace => Self::RightBrace, + MatchToken::Comma => Self::Comma, + MatchToken::Dot => Self::Dot, + MatchToken::Minus => Self::Minus, + MatchToken::Plus => Self::Plus, + MatchToken::Semicolon => Self::Semicolon, + MatchToken::Slash => Self::Slash, + MatchToken::Star => Self::Star, + MatchToken::Bang => Self::Bang, + MatchToken::BangEq => Self::BangEq, + MatchToken::Eq => Self::Eq, + MatchToken::EqEq => Self::EqEq, + MatchToken::Greater => Self::Greater, + MatchToken::GreaterEq => Self::GreaterEq, + MatchToken::Less => Self::Less, + MatchToken::LessEq => Self::LessEq, + MatchToken::Ident => Self::Ident, + MatchToken::StringLit => Self::StringLit, + MatchToken::NumberLit => Self::NumberLit, + MatchToken::And => Self::And, + MatchToken::Class => Self::Class, + MatchToken::Else => Self::Else, + MatchToken::False => Self::False, + MatchToken::Fun => Self::Fun, + MatchToken::For => Self::For, + MatchToken::If => Self::If, + MatchToken::Nil => Self::Nil, + MatchToken::Or => Self::Or, + MatchToken::Print => Self::Print, + MatchToken::Return => Self::Return, + MatchToken::Super => Self::Super, + MatchToken::This => Self::This, + MatchToken::True => Self::True, + MatchToken::Var => Self::Var, + MatchToken::While => Self::While, + } + } +} + */ + impl PartialEq for MatchToken { fn eq(&self, other: &TokenKind) -> bool { matches!( diff --git a/src/parser.rs b/src/parser.rs index e96f3f9..e7c595f 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -72,17 +72,13 @@ impl Parser { /// 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); - let name = self.expect_identifier()?; - let initializer = self .pop_if_matches(MatchToken::Eq) .map(|_| self.parse_expr()) .transpose()?; + let semi = self.expect_semicolon()?; - let semi = self - .pop_if_matches(MatchToken::Semicolon) - .ok_or_else(|| ParserError::missing_semicolon(self.previous()))?; Ok(Stmt::var_decl(var_token, name, initializer, &semi)) } @@ -95,23 +91,17 @@ impl Parser { let name = self.expect_identifier()?; // consume the opening `(` - self.pop_if_matches(MatchToken::LeftParen).ok_or_else(|| { - ParserError::expected_token(TokenKind::LeftParen, self.peek().as_ref()) - })?; + 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.pop_if_matches(MatchToken::RightParen).ok_or_else(|| { - ParserError::expected_token(TokenKind::RightParen, self.peek().as_ref()) - })?; + self.expect_token(MatchToken::RightParen)?; // consume the opening `{` - let open_brace = self.pop_if_matches(MatchToken::LeftBrace).ok_or_else(|| { - ParserError::expected_token(TokenKind::LeftBrace, self.peek().as_ref()) - })?; + 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 } => { @@ -151,18 +141,10 @@ impl Parser { return self.parse_while_or_pass(); }; - self.pop_if_matches(MatchToken::LeftParen).ok_or_else(|| { - ParserError::expected_token(TokenKind::LeftParen, self.peek().as_ref()) - })?; - + self.expect_token(MatchToken::LeftParen)?; let condition = self.parse_expr()?; - - self.pop_if_matches(MatchToken::RightParen).ok_or_else(|| { - ParserError::expected_token(TokenKind::RightParen, self.peek().as_ref()) - })?; - + self.expect_token(MatchToken::RightParen)?; let then = self.parse_block_or_pass()?; - let otherwise = self .pop_if_matches(MatchToken::Else) .map(|_| self.parse_block_or_pass()) @@ -177,16 +159,9 @@ impl Parser { return self.parse_for_or_pass(); }; - self.pop_if_matches(MatchToken::LeftParen).ok_or_else(|| { - ParserError::expected_token(TokenKind::LeftParen, self.peek().as_ref()) - })?; - + self.expect_token(MatchToken::LeftParen)?; let condition = self.parse_expr()?; - - self.pop_if_matches(MatchToken::RightParen).ok_or_else(|| { - ParserError::expected_token(TokenKind::RightParen, self.peek().as_ref()) - })?; - + self.expect_token(MatchToken::RightParen)?; let body = self.parse_block_or_pass()?; Ok(Stmt::while_stmt(&while_token, condition, body)) @@ -198,9 +173,7 @@ impl Parser { return self.parse_return_or_pass(); }; - let left_paren = self.pop_if_matches(MatchToken::LeftParen).ok_or_else(|| { - ParserError::expected_token(TokenKind::LeftParen, self.peek().as_ref()) - })?; + let left_paren = self.expect_token(MatchToken::LeftParen)?; let initializer = if self.pop_if_matches(MatchToken::Semicolon).is_some() { None @@ -209,9 +182,7 @@ impl Parser { Some(self.finish_var_decl(&var)?) } else { let expr = self.parse_expr()?; - let semi = self - .pop_if_matches(MatchToken::Semicolon) - .ok_or_else(|| ParserError::missing_semicolon(self.previous()))?; + let semi = self.expect_semicolon()?; Some(Stmt::expr(expr, &semi)) }; @@ -219,8 +190,7 @@ impl Parser { None } else { let expr = self.parse_expr()?; - self.pop_if_matches(MatchToken::Semicolon) - .ok_or_else(|| ParserError::missing_semicolon(self.previous()))?; + self.expect_semicolon()?; Some(expr) }; @@ -228,9 +198,7 @@ impl Parser { None } else { let expr = self.parse_expr()?; - let right_paren = self.pop_if_matches(MatchToken::RightParen).ok_or_else(|| { - ParserError::expected_token(TokenKind::RightParen, self.peek().as_ref()) - })?; + let right_paren = self.expect_token(MatchToken::RightParen)?; Some((expr, right_paren)) }; @@ -282,9 +250,7 @@ impl Parser { }; let value = self.parse_expr()?; - let semicolon = self - .pop_if_matches(MatchToken::Semicolon) - .ok_or_else(|| ParserError::missing_semicolon(self.previous()))?; + let semicolon = self.expect_semicolon()?; Ok(Stmt::return_stmt(&return_token, Some(value), &semicolon)) } @@ -293,9 +259,7 @@ impl Parser { fn parse_print_or_expr_stmt(&mut self) -> Result> { let print_token = self.pop_if_matches(MatchToken::Print); let expr = self.parse_expr()?; - let semi = self - .pop_if_matches(MatchToken::Semicolon) - .ok_or_else(|| ParserError::missing_semicolon(self.previous()))?; + let semi = self.expect_semicolon()?; match print_token { Some(print_token) => Ok(Stmt::print(&print_token, expr, &semi)), @@ -303,18 +267,6 @@ impl Parser { } } - fn expect_identifier(&mut self) -> Result { - self.pop_if_map(|t| match t.kind { - TokenKind::Ident { name } => Some(name), - _ => None, - }) - .ok_or_else(|| { - self.peek() - .map(ParserError::expected_identifier) - .unwrap_or(ParserError::UnexpectedEndOfInput) - }) - } - /// Parse items in a comma-separated list until the next token is not a comma fn parse_comma_separated_list( &mut self, @@ -544,6 +496,31 @@ impl Parser { } } +/// Shorthand expectations +impl Parser { + fn expect_token(&mut self, kind: MatchToken) -> Result { + self.pop_if_matches(kind) + .ok_or_else(|| ParserError::expected_token(kind, self.peek().as_ref())) + } + + fn expect_semicolon(&mut self) -> Result { + self.pop_if_matches(MatchToken::Semicolon) + .ok_or_else(|| ParserError::missing_semicolon(self.previous())) + } + + fn expect_identifier(&mut self) -> Result { + self.pop_if_map(|t| match t.kind { + TokenKind::Ident { name } => Some(name), + _ => None, + }) + .ok_or_else(|| { + self.peek() + .map(ParserError::expected_identifier) + .unwrap_or(ParserError::UnexpectedEndOfInput) + }) + } +} + /// Parsing primitives impl Parser { #[tracing::instrument(skip_all)] @@ -640,7 +617,7 @@ pub enum ParserError { #[error("Missing a `{kind}`")] ExpectedToken { - kind: TokenKind, + kind: MatchToken, actual: Option, #[label("expected a `{kind}` here, but got a `{}`", actual.as_ref().unwrap())] span: Option, @@ -709,7 +686,7 @@ impl ParserError { } } - fn expected_token(kind: TokenKind, actual: Option<&Token>) -> Self { + fn expected_token(kind: MatchToken, actual: Option<&Token>) -> Self { Self::ExpectedToken { kind, actual: actual.map(|t| t.kind.clone()), @@ -927,14 +904,14 @@ mod tests { assert_matches!( parse_stmt_to_err("if true print 1;"), ParserError::ExpectedToken { - kind: TokenKind::LeftParen, + kind: MatchToken::LeftParen, .. } ); assert_matches!( parse_stmt_to_err("if (true print 1;"), ParserError::ExpectedToken { - kind: TokenKind::RightParen, + kind: MatchToken::RightParen, .. } ); -- 2.51.2