From 44506cf591ad4b5aaf708e3a3dc7969ddf9959f1 Mon Sep 17 00:00:00 2001 From: ishanjain28 Date: Sun, 19 Jan 2020 23:20:17 +0530 Subject: [PATCH] Added If and IfElse parser 1. Added If and IfElse expression parsers. 2. Also, Added asserts to ensure there are 0 errors in parser object --- src/parser/ast/mod.rs | 156 +++++++++++++++++++++++++++++++++++++++--- src/parser/mod.rs | 106 ++++++++++++++++++++++++++++ 2 files changed, 254 insertions(+), 8 deletions(-) diff --git a/src/parser/ast/mod.rs b/src/parser/ast/mod.rs index 3bb2232..a5181a0 100644 --- a/src/parser/ast/mod.rs +++ b/src/parser/ast/mod.rs @@ -32,6 +32,7 @@ pub enum Statement { Let(LetStatement), Return(ReturnStatement), ExpressionStatement(ExpressionStatement), + BlockStatement(BlockStatement), } impl<'a> Statement { @@ -46,12 +47,13 @@ impl<'a> Statement { } } -impl Display for Statement { - fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> { +impl ToString for Statement { + fn to_string(&self) -> String { match self { - Statement::Let(v) => write!(f, "{}", v.to_string()), - Statement::Return(v) => write!(f, "{}", v.to_string()), - Statement::ExpressionStatement(v) => write!(f, "{}", v.to_string()), + Statement::Let(v) => v.to_string(), + Statement::Return(v) => v.to_string(), + Statement::ExpressionStatement(v) => v.to_string(), + Statement::BlockStatement(v) => v.to_string(), } } } @@ -160,9 +162,9 @@ impl ExpressionStatement { } } -impl Display for ExpressionStatement { - fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> { - write!(f, "{}", self.expression.to_string()) +impl ToString for ExpressionStatement { + fn to_string(&self) -> String { + String::from(self.expression.to_string()) } } @@ -184,6 +186,7 @@ pub enum Expression { PrefixExpression(PrefixExpression), InfixExpression(InfixExpression), BooleanExpression(BooleanExpression), + IfExpression(IfExpression), // TODO: Temporary placeholder value. Should be removed once this section is done None, } @@ -241,6 +244,58 @@ impl Expression { Some(Self::BooleanExpression(BooleanExpression::new(token.name))) } + pub fn parse_grouped_expression(parser: &mut Parser, _token: Token) -> Option { + let next_token = parser.lexer.next()?; + let expr = Expression::parse(parser, next_token, ExpressionPriority::Lowest); + + if parser.expect_peek(TokenType::RParen).is_none() { + None + } else { + expr + } + } + + pub fn parse_if_expression(parser: &mut Parser, ctoken: Token) -> Option { + if parser.expect_peek(TokenType::LParen).is_none() { + return None; + } + let next_token = parser.lexer.next()?; + let condition = Expression::parse(parser, next_token.clone(), ExpressionPriority::Lowest)?; + + if parser.expect_peek(TokenType::RParen).is_none() { + return None; + } + if parser.expect_peek(TokenType::LBrace).is_none() { + return None; + } + + let consequence = BlockStatement::parse(parser, next_token)?; + + if parser.peek_token_is(TokenType::Else) { + let token = parser.lexer.next()?; + + if parser.expect_peek(TokenType::LBrace).is_none() { + return None; + } + + let alternative = BlockStatement::parse(parser, token); + + Some(Expression::IfExpression(IfExpression::new( + ctoken.name, + condition, + consequence, + alternative, + ))) + } else { + Some(Expression::IfExpression(IfExpression::new( + ctoken.name, + condition, + consequence, + None, + ))) + } + } + pub fn parse_prefix_expression(parser: &mut Parser, ctoken: Token) -> Option { let next_token = parser.lexer.next()?; let right_expr = Expression::parse(parser, next_token.clone(), ExpressionPriority::Prefix)?; @@ -279,6 +334,7 @@ impl Display for Expression { Expression::PrefixExpression(v) => v.to_string(), Expression::InfixExpression(v) => v.to_string(), Expression::BooleanExpression(v) => v.to_string(), + Expression::IfExpression(v) => v.to_string(), Expression::None => "None".into(), } ) @@ -405,6 +461,90 @@ impl Display for BooleanExpression { } } +#[derive(Debug, PartialEq)] +pub struct IfExpression { + token: TokenType, + condition: Box, + consequence: BlockStatement, + alternative: Option, +} + +impl IfExpression { + pub fn new( + token: TokenType, + condition: Expression, + consequence: BlockStatement, + alternative: Option, + ) -> Self { + Self { + token, + condition: Box::new(condition), + consequence, + alternative, + } + } +} + +impl ToString for IfExpression { + fn to_string(&self) -> String { + let mut out = format!( + "if {} {{ {} }} ", + self.condition.to_string(), + self.consequence.to_string() + ); + if let Some(alternative) = &self.alternative { + out += &format!("else {{ {} }}", alternative.to_string()); + } + out + } +} + +#[derive(Debug, PartialEq)] +pub struct BlockStatement { + token: Token, + statements: Vec, +} + +impl BlockStatement { + pub fn new(token: Token, stmt: Vec) -> Self { + Self { + token, + statements: stmt, + } + } + + pub fn parse(parser: &mut Parser, ogtoken: Token) -> Option { + let mut stmts = vec![]; + + let mut ctoken = parser.lexer.next(); + + while let Some(token) = ctoken { + if token.name == TokenType::RBrace { + break; + } + + let stmt = Statement::parse(parser, token); + if stmt.is_some() { + stmts.push(stmt.unwrap()); + } + ctoken = parser.lexer.next(); + } + + Some(BlockStatement::new(ogtoken, stmts)) + } +} + +impl ToString for BlockStatement { + fn to_string(&self) -> String { + let mut out = String::new(); + + for stmt in &self.statements { + out.push_str(&stmt.to_string()); + } + out + } +} + #[cfg(test)] mod tests { use crate::{ diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 7f53f5d..b061e8c 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -53,6 +53,8 @@ impl<'a> Parser<'a> { parser.register_prefix(TokenType::Minus, Expression::parse_prefix_expression); parser.register_prefix(TokenType::True, Expression::parse_boolean); parser.register_prefix(TokenType::False, Expression::parse_boolean); + parser.register_prefix(TokenType::LParen, Expression::parse_grouped_expression); + parser.register_prefix(TokenType::If, Expression::parse_if_expression); parser.register_infix(TokenType::Plus, Expression::parse_infix_expression); parser.register_infix(TokenType::Minus, Expression::parse_infix_expression); @@ -182,6 +184,7 @@ mod tests { let mut parser = Parser::new(lexer); let program = parser.parse_program(); check_parser_errors(&parser); + assert_eq!(parser.errors.len(), 0); assert!(program.is_some()); let program = program.unwrap(); assert_eq!(program.statements.len(), 3); @@ -219,6 +222,7 @@ mod tests { let program = parser.parse_program(); check_parser_errors(&parser); + assert_eq!(parser.errors.len(), 0); assert!(program.is_some()); let program = program.unwrap(); assert_eq!(program.statements.len(), 3); @@ -232,6 +236,7 @@ mod tests { let program = parser.parse_program(); check_parser_errors(&parser); + assert_eq!(parser.errors.len(), 0); assert!(program.is_some()); let program = program.unwrap(); assert_eq!(program.statements.len(), 1); @@ -250,6 +255,7 @@ mod tests { let mut parser = Parser::new(lexer); let program = parser.parse_program(); check_parser_errors(&parser); + assert_eq!(parser.errors.len(), 0); assert!(program.is_some()); assert_eq!( @@ -340,6 +346,7 @@ mod tests { let mut parser = Parser::new(lexer); let program = parser.parse_program(); check_parser_errors(&parser); + assert_eq!(parser.errors.len(), 0); assert!(program.is_some()); assert_eq!(program.unwrap().statements, test.1); } @@ -467,6 +474,7 @@ mod tests { let mut parser = Parser::new(lexer); let program = parser.parse_program(); check_parser_errors(&parser); + assert_eq!(parser.errors.len(), 0); assert!(program.is_some()); assert_eq!(program.unwrap().statements, test.1); } @@ -498,6 +506,11 @@ mod tests { ("false", "false"), ("3 > 5 == false", "((3 > 5) == false)"), ("3 < 5 == true", "((3 < 5) == true)"), + ("1 + (2 + 3) + 4", "((1 + (2 + 3)) + 4)"), + ("(5 + 5) * 2", "((5 + 5) * 2)"), + ("2 / (5 + 5)", "(2 / (5 + 5))"), + ("-(5 + 5)", "(-(5 + 5))"), + ("!(true == true)", "(!(true == true))"), ]; for test in test_cases.iter() { @@ -505,6 +518,7 @@ mod tests { let mut parser = Parser::new(lexer); let program = parser.parse_program(); check_parser_errors(&parser); + assert_eq!(parser.errors.len(), 0); assert!(program.is_some()); assert_eq!(program.unwrap().to_string(), test.1); } @@ -547,6 +561,98 @@ mod tests { let mut parser = Parser::new(lexer); let program = parser.parse_program(); check_parser_errors(&parser); + assert_eq!(parser.errors.len(), 0); + assert!(program.is_some()); + assert_eq!(program.unwrap(), test.1); + } + } + #[test] + fn if_expression() { + let test_cases = [( + "if (x > y) { x };", + Program { + statements: vec![Statement::ExpressionStatement(ExpressionStatement::new( + Token::new(TokenType::If), + Expression::IfExpression(IfExpression::new( + TokenType::If, + Expression::InfixExpression(InfixExpression::new( + Token::new(TokenType::GreaterThan), + Expression::Identifier(Identifier::new(TokenType::Ident, "x")), + ">", + Expression::Identifier(Identifier::new(TokenType::Ident, "y")), + )), + BlockStatement::new( + Token::with_value(TokenType::Ident, "x"), + vec![Statement::ExpressionStatement(ExpressionStatement { + token: Token::with_value(TokenType::Ident, "x"), + expression: Expression::Identifier(Identifier::new( + TokenType::Ident, + "x", + )), + })], + ), + None, + )), + ))], + }, + )]; + + for test in test_cases.iter() { + let lexer = Lexer::new(test.0); + let mut parser = Parser::new(lexer); + let program = parser.parse_program(); + check_parser_errors(&parser); + assert_eq!(parser.errors.len(), 0); + assert!(program.is_some()); + assert_eq!(program.unwrap(), test.1); + } + } + #[test] + fn if_else_expression() { + let test_cases = [( + "if (x > y) { x } else { y };", + Program { + statements: vec![Statement::ExpressionStatement(ExpressionStatement::new( + Token::new(TokenType::If), + Expression::IfExpression(IfExpression::new( + TokenType::If, + Expression::InfixExpression(InfixExpression::new( + Token::new(TokenType::GreaterThan), + Expression::Identifier(Identifier::new(TokenType::Ident, "x")), + ">", + Expression::Identifier(Identifier::new(TokenType::Ident, "y")), + )), + BlockStatement::new( + Token::with_value(TokenType::Ident, "x"), + vec![Statement::ExpressionStatement(ExpressionStatement { + token: Token::with_value(TokenType::Ident, "x"), + expression: Expression::Identifier(Identifier::new( + TokenType::Ident, + "x", + )), + })], + ), + Some(BlockStatement::new( + Token::new(TokenType::Else), + vec![Statement::ExpressionStatement(ExpressionStatement { + token: Token::with_value(TokenType::Ident, "y"), + expression: Expression::Identifier(Identifier::new( + TokenType::Ident, + "y", + )), + })], + )), + )), + ))], + }, + )]; + + for test in test_cases.iter() { + let lexer = Lexer::new(test.0); + let mut parser = Parser::new(lexer); + let program = parser.parse_program(); + check_parser_errors(&parser); + assert_eq!(parser.errors.len(), 0); assert!(program.is_some()); assert_eq!(program.unwrap(), test.1); }