From 0321e9ba89a29886df1f72b3f4a727b4aae80bfe Mon Sep 17 00:00:00 2001 From: Devon Tingley Date: Mon, 6 Mar 2023 18:05:40 -0500 Subject: [PATCH] Chapter 2 (finished) --- src/lexer/tokens.rs | 4 ++++ src/parser/ast.rs | 17 ++++++++++++++- src/parser/mod.rs | 47 ++++++++++++++++++++++++++++++++++++++++ src/parser/precedence.rs | 4 +++- 4 files changed, 70 insertions(+), 2 deletions(-) diff --git a/src/lexer/tokens.rs b/src/lexer/tokens.rs index ce6dc34..598c64d 100644 --- a/src/lexer/tokens.rs +++ b/src/lexer/tokens.rs @@ -132,6 +132,8 @@ pub enum InfixOperator { GreaterThan, LessThanEqual, GreaterThanEqual, + + Call, } impl InfixOperator { @@ -157,6 +159,8 @@ impl TryFrom<&Token> for InfixOperator { Token::GreaterThan => Self::GreaterThan, Token::GreaterThanEqual => Self::GreaterThanEqual, + Token::LeftParenthesis => Self::Call, + _ => return Err(LexerError::InvalidToken), }; diff --git a/src/parser/ast.rs b/src/parser/ast.rs index 6cfc6c0..b92e82b 100644 --- a/src/parser/ast.rs +++ b/src/parser/ast.rs @@ -85,7 +85,12 @@ pub enum Expression { Function { parameters: Vec, - body: Box, + body: Box, // block statement + }, + + Call { + function: Box, // ident or function + args: Vec, }, } @@ -159,6 +164,16 @@ impl Display for Expression { Expression::Function { parameters, body } => { write!(f, "fn({}) {}", parameters.join(", "), body) } + + Expression::Call { function, args } => write!( + f, + "{}({})", + function, + args.iter() + .map(|expr| expr.to_string()) + .collect::>() + .join(", ") + ), } } } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 5fcea26..8e94fd1 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -161,6 +161,7 @@ fn parse_expression( None | Some(Token::RightParenthesis) | Some(Token::Semicolon) + | Some(Token::Comma) // Break used when parsing function arguments | Some(Token::LeftBrace) // Break used for if statements | Some(Token::RightBrace) => break, _ => (), @@ -315,6 +316,39 @@ fn parse_infix_operator( lhs: Expression, ) -> Result { let lhs = Box::new(lhs); + + // Call operators are parsed weirdly + if InfixOperator::Call == operator { + let mut args = Vec::new(); + loop { + match tokens.peek() { + Some(Token::Comma) => { + tokens.next(); + continue; + } + Some(Token::RightParenthesis) => { + tokens.next(); + break; + } + + // Parse expressions + Some(_) => { + let arg = parse_expression(tokens, Precedence::Lowest)?; + args.push(arg); + continue; + } + + // Unexpected EOF + None => return Err(ParserError::EOF), + }; + } + + return Ok(Expression::Call { + function: lhs, + args, + }); + }; + let rhs = Box::new(parse_expression(tokens, get_prescedence(&operator))?); let expr = match operator { @@ -331,6 +365,8 @@ fn parse_infix_operator( InfixOperator::LessThan => Expression::LessThan(lhs, rhs), InfixOperator::GreaterThanEqual => Expression::GreaterThanEqual(lhs, rhs), InfixOperator::LessThanEqual => Expression::LessThanEqual(lhs, rhs), + + InfixOperator::Call => panic!("unreachable"), }; Ok(expr) @@ -424,4 +460,15 @@ mod tests { let res = parse_expression(&mut tokens, Precedence::Lowest).unwrap(); assert_eq!(&res.to_string(), expected); } + + #[rstest] + #[case("add(2, 3)", "add(2, 3)")] + #[case("add(1 + 2, 3)", "add((1 + 2), 3)")] + #[case("add(x, y)", "add(x, y)")] + #[case("fn(x,y) { x + y }(1,2)", "fn(x, y) { (x + y) }(1, 2)")] + fn test_call_expression(#[case] input: &str, #[case] expected: &str) { + let mut tokens = lexer::tokenize(input).unwrap(); + let res = parse_expression(&mut tokens, Precedence::Lowest).unwrap(); + assert_eq!(&res.to_string(), expected); + } } diff --git a/src/parser/precedence.rs b/src/parser/precedence.rs index 202e872..c313c44 100644 --- a/src/parser/precedence.rs +++ b/src/parser/precedence.rs @@ -8,7 +8,7 @@ pub enum Precedence { Sum, Product, Prefix, - // Call, + Call, } pub(super) fn get_prescedence(tok: &InfixOperator) -> Precedence { @@ -26,5 +26,7 @@ pub(super) fn get_prescedence(tok: &InfixOperator) -> Precedence { InfixOperator::Asterisk => Precedence::Product, InfixOperator::ForwardSlash => Precedence::Product, + + InfixOperator::Call => Precedence::Call, } }