diff --git a/src/ast.rs b/src/ast.rs index 3793a9e..28e73eb 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -109,6 +109,69 @@ impl Node for Prefix {} impl Expression for Prefix {} +#[derive(Debug)] +pub enum InfixOperator { + Plus, + Minus, + Asterisk, + Slash, + Lt, + Gt, + Eq, + NotEq, +} + +impl Display for InfixOperator { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use InfixOperator::*; + match self { + Plus => write!(f, "+")?, + Minus => write!(f, "-")?, + Asterisk => write!(f, "*")?, + Slash => write!(f, "/")?, + Lt => write!(f, "<")?, + Gt => write!(f, ">")?, + Eq => write!(f, "==")?, + NotEq => write!(f, "!=")?, + } + Ok(()) + } +} + +#[derive(Debug)] +pub struct InfixOperatorFromTokenTypeError { + token: TokenType, +} + +impl TryFrom for InfixOperator { + type Error = InfixOperatorFromTokenTypeError; + fn try_from(value: TokenType) -> Result { + use TokenType::*; + match value { + Plus => Ok(Self::Plus), + Minus => Ok(Self::Minus), + Asterisk => Ok(Self::Asterisk), + Slash => Ok(Self::Slash), + Lt => Ok(Self::Lt), + Gt => Ok(Self::Gt), + Eq => Ok(Self::Eq), + NotEq => Ok(Self::NotEq), + token => Err(Self::Error { token }), + } + } +} + +#[derive(Debug)] +pub struct Infix { + pub operator: InfixOperator, + pub left: Rc, + pub right: Rc, +} + +impl Node for Infix {} + +impl Expression for Infix {} + #[derive(Debug)] pub struct DummyExpression {} diff --git a/src/lexer.rs b/src/lexer.rs index 775c082..3a403b7 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -139,7 +139,7 @@ mod tests { use crate::token::Token; #[test] - fn test_next_token() { + fn next_token() { let input = "let five = 5;\ let ten = 10;\ \ diff --git a/src/parser.rs b/src/parser.rs index 08a406b..a6efb16 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -2,11 +2,11 @@ use std::{fmt, rc::Rc}; use crate::{ ast::{ - DummyExpression, Expression, ExpressionStatement, Identifier, IntegerLiteral, Let, Prefix, - PrefixOperator, Program, Return, Statement, + DummyExpression, Expression, ExpressionStatement, Identifier, Infix, InfixOperator, + IntegerLiteral, Let, Prefix, PrefixOperator, Program, Return, Statement, }, lexer::Lexer, - token::{Token, TokenType}, + token::{self, Token, TokenType}, }; pub type Result = std::result::Result; @@ -20,7 +20,8 @@ pub enum Error { expected: TokenType, actual: Option, }, - NoPrefixParseFnFound, + NoPrefixParseFnFound(TokenType), + NoInfixParseFnFound(TokenType), EmptyExprFollowingPrefixOp(PrefixOperator), } @@ -35,6 +36,24 @@ enum Precedence { Call, } +struct PrecedenceFromTokenTypeError { + value: TokenType, +} + +impl TryFrom for Precedence { + type Error = PrecedenceFromTokenTypeError; + fn try_from(value: TokenType) -> std::result::Result { + use TokenType::*; + match value { + Eq | NotEq => Ok(Self::Equals), + Lt | Gt => Ok(Self::LessGreater), + Plus | Minus => Ok(Self::Sum), + Asterisk | Slash => Ok(Self::Product), + value => Err(PrecedenceFromTokenTypeError { value }), + } + } +} + impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "parser error: ")?; @@ -48,7 +67,8 @@ impl fmt::Display for Error { None => "None".to_string(), }, ), - NoPrefixParseFnFound => write!(f, "no prefix parse function found"), + NoPrefixParseFnFound(t) => write!(f, "no prefix parse function found for `{t}`"), + NoInfixParseFnFound(t) => write!(f, "no infix parse function found for `{t}`"), EmptyExprFollowingPrefixOp(op) => write!(f, "empty expression following `{op}`"), }?; Ok(()) @@ -163,11 +183,26 @@ impl Parser { let prefix = if let Some(prefix) = self.prefix_parse_fn() { prefix } else { - self.errors.push(Error::NoPrefixParseFnFound); + let err = Error::NoPrefixParseFnFound(self.cur_token.clone().unwrap().token_type()); + self.errors.push(err); return None; }; - let left_exp = prefix(self); + let mut left_exp = prefix(self); + + while !self.peek_token_is(TokenType::Semicolon) && precedence < self.peek_precedence() { + let infix = if let Some(infix) = self.infix_parse_fn() { + infix + } else { + let err = Error::NoInfixParseFnFound(self.peek_token.clone().unwrap().token_type()); + self.errors.push(err); + return None; + }; + + self.next(); + + left_exp = infix(self, left_exp); + } Some(left_exp) } @@ -202,6 +237,26 @@ impl Parser { Rc::new(Prefix { operator, right }) } + fn parse_infix_expression(&mut self, left: Rc) -> Rc { + let token = if let Some(token) = &self.cur_token { + token + } else { + return Rc::new(DummyExpression {}); + }; + + let operator: InfixOperator = token.token_type().try_into().unwrap(); + let precedence = self.cur_precedence(); + + self.next(); + let right = self.parse_expression(precedence).unwrap(); + + Rc::new(Infix { + left, + operator, + right, + }) + } + fn expect_peek(&mut self, token_type: TokenType) -> bool { let peek_token = if let Some(token) = &self.peek_token { token @@ -267,6 +322,32 @@ impl Parser { cur_token.token_type() == token_type } + fn peek_precedence(&self) -> Precedence { + let token = if let Some(token) = &self.peek_token { + token + } else { + return Precedence::Lowest; + }; + if let Ok(p) = token.token_type().try_into() { + p + } else { + Precedence::Lowest + } + } + + fn cur_precedence(&self) -> Precedence { + let token = if let Some(token) = &self.cur_token { + token + } else { + return Precedence::Lowest; + }; + if let Ok(p) = token.token_type().try_into() { + p + } else { + Precedence::Lowest + } + } + fn prefix_parse_fn(&self) -> Option { let token = if let Some(token) = &self.cur_token { token @@ -298,10 +379,19 @@ impl Parser { } } - fn infix_parse_fn(token: &Token) -> InfixParseFn { + fn infix_parse_fn(&self) -> Option { + let token = if let Some(token) = &self.peek_token { + token + } else { + return None; + }; + use Token::*; match token { - _ => unimplemented!(), + Plus | Minus | Slash | Asterisk | Eq | NotEq | Lt | Gt => { + Some(Box::new(Self::parse_infix_expression)) + } + _ => None, } } } @@ -327,8 +417,8 @@ mod tests { use std::rc::Rc; use crate::{ - ast::{PrefixOperator, Statement}, - lexer::{self, Lexer}, + ast::{InfixOperator, PrefixOperator, Statement}, + lexer::Lexer, token::Token, }; @@ -465,6 +555,85 @@ mod tests { } } + #[test] + fn infix_expressions() { + struct Test { + input: String, + left_value: i64, + operator: InfixOperator, + right_value: i64, + } + + let tests = vec![ + Test { + input: "5 + 5".into(), + left_value: 5, + operator: InfixOperator::Plus, + right_value: 5, + }, + Test { + input: "5 - 5".into(), + left_value: 5, + operator: InfixOperator::Minus, + right_value: 5, + }, + Test { + input: "5 * 5".into(), + left_value: 5, + operator: InfixOperator::Asterisk, + right_value: 5, + }, + Test { + input: "5 / 5".into(), + left_value: 5, + operator: InfixOperator::Slash, + right_value: 5, + }, + Test { + input: "5 < 5".into(), + left_value: 5, + operator: InfixOperator::Lt, + right_value: 5, + }, + Test { + input: "5 > 5".into(), + left_value: 5, + operator: InfixOperator::Gt, + right_value: 5, + }, + Test { + input: "5 == 5".into(), + left_value: 5, + operator: InfixOperator::Eq, + right_value: 5, + }, + Test { + input: "5 != 5".into(), + left_value: 5, + operator: InfixOperator::NotEq, + right_value: 5, + }, + ]; + + for test in tests { + let l = Lexer::new(test.input); + + let mut parser = Parser::new(l); + + let program = parser.parse().unwrap(); + check_parser_errors(parser); + + assert_eq!(program.statements.len(), 1); + + test_infix_expression( + program.statements.first().unwrap().clone(), + test.left_value, + test.operator, + test.right_value, + ); + } + } + fn test_let_statement(stmt: Rc, name: &str) { assert_eq!( format!("{stmt:?}"), @@ -515,4 +684,16 @@ mod tests { format!("ExpressionStatement {{ token: {token:?}, expression: Prefix {{ operator: {operator:?}, right: IntegerLiteral {{ value: {num} }} }} }}"), ); } + + fn test_infix_expression( + stmt: Rc, + left: i64, + operator: InfixOperator, + right: i64, + ) { + assert_eq!( + format!("{stmt:?}"), + format!("ExpressionStatement {{ token: Int({left}), expression: Infix {{ operator: {operator:?}, left: IntegerLiteral {{ value: {left} }}, right: IntegerLiteral {{ value: {right} }} }} }}"), + ); + } }