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
This commit is contained in:
Ishan Jain 2020-01-19 23:20:17 +05:30
parent 9763c5ca2e
commit 44506cf591
2 changed files with 254 additions and 8 deletions

View File

@ -32,6 +32,7 @@ pub enum Statement {
Let(LetStatement), Let(LetStatement),
Return(ReturnStatement), Return(ReturnStatement),
ExpressionStatement(ExpressionStatement), ExpressionStatement(ExpressionStatement),
BlockStatement(BlockStatement),
} }
impl<'a> Statement { impl<'a> Statement {
@ -46,12 +47,13 @@ impl<'a> Statement {
} }
} }
impl Display for Statement { impl ToString for Statement {
fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> { fn to_string(&self) -> String {
match self { match self {
Statement::Let(v) => write!(f, "{}", v.to_string()), Statement::Let(v) => v.to_string(),
Statement::Return(v) => write!(f, "{}", v.to_string()), Statement::Return(v) => v.to_string(),
Statement::ExpressionStatement(v) => write!(f, "{}", 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 { impl ToString for ExpressionStatement {
fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> { fn to_string(&self) -> String {
write!(f, "{}", self.expression.to_string()) String::from(self.expression.to_string())
} }
} }
@ -184,6 +186,7 @@ pub enum Expression {
PrefixExpression(PrefixExpression), PrefixExpression(PrefixExpression),
InfixExpression(InfixExpression), InfixExpression(InfixExpression),
BooleanExpression(BooleanExpression), BooleanExpression(BooleanExpression),
IfExpression(IfExpression),
// TODO: Temporary placeholder value. Should be removed once this section is done // TODO: Temporary placeholder value. Should be removed once this section is done
None, None,
} }
@ -241,6 +244,58 @@ impl Expression {
Some(Self::BooleanExpression(BooleanExpression::new(token.name))) Some(Self::BooleanExpression(BooleanExpression::new(token.name)))
} }
pub fn parse_grouped_expression(parser: &mut Parser, _token: Token) -> Option<Self> {
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<Self> {
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<Self> { pub fn parse_prefix_expression(parser: &mut Parser, ctoken: Token) -> Option<Self> {
let next_token = parser.lexer.next()?; let next_token = parser.lexer.next()?;
let right_expr = Expression::parse(parser, next_token.clone(), ExpressionPriority::Prefix)?; 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::PrefixExpression(v) => v.to_string(),
Expression::InfixExpression(v) => v.to_string(), Expression::InfixExpression(v) => v.to_string(),
Expression::BooleanExpression(v) => v.to_string(), Expression::BooleanExpression(v) => v.to_string(),
Expression::IfExpression(v) => v.to_string(),
Expression::None => "None".into(), Expression::None => "None".into(),
} }
) )
@ -405,6 +461,90 @@ impl Display for BooleanExpression {
} }
} }
#[derive(Debug, PartialEq)]
pub struct IfExpression {
token: TokenType,
condition: Box<Expression>,
consequence: BlockStatement,
alternative: Option<BlockStatement>,
}
impl IfExpression {
pub fn new(
token: TokenType,
condition: Expression,
consequence: BlockStatement,
alternative: Option<BlockStatement>,
) -> 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<Statement>,
}
impl BlockStatement {
pub fn new(token: Token, stmt: Vec<Statement>) -> Self {
Self {
token,
statements: stmt,
}
}
pub fn parse(parser: &mut Parser, ogtoken: Token) -> Option<Self> {
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)] #[cfg(test)]
mod tests { mod tests {
use crate::{ use crate::{

View File

@ -53,6 +53,8 @@ impl<'a> Parser<'a> {
parser.register_prefix(TokenType::Minus, Expression::parse_prefix_expression); parser.register_prefix(TokenType::Minus, Expression::parse_prefix_expression);
parser.register_prefix(TokenType::True, Expression::parse_boolean); parser.register_prefix(TokenType::True, Expression::parse_boolean);
parser.register_prefix(TokenType::False, 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::Plus, Expression::parse_infix_expression);
parser.register_infix(TokenType::Minus, 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 mut parser = Parser::new(lexer);
let program = parser.parse_program(); let program = parser.parse_program();
check_parser_errors(&parser); check_parser_errors(&parser);
assert_eq!(parser.errors.len(), 0);
assert!(program.is_some()); assert!(program.is_some());
let program = program.unwrap(); let program = program.unwrap();
assert_eq!(program.statements.len(), 3); assert_eq!(program.statements.len(), 3);
@ -219,6 +222,7 @@ mod tests {
let program = parser.parse_program(); let program = parser.parse_program();
check_parser_errors(&parser); check_parser_errors(&parser);
assert_eq!(parser.errors.len(), 0);
assert!(program.is_some()); assert!(program.is_some());
let program = program.unwrap(); let program = program.unwrap();
assert_eq!(program.statements.len(), 3); assert_eq!(program.statements.len(), 3);
@ -232,6 +236,7 @@ mod tests {
let program = parser.parse_program(); let program = parser.parse_program();
check_parser_errors(&parser); check_parser_errors(&parser);
assert_eq!(parser.errors.len(), 0);
assert!(program.is_some()); assert!(program.is_some());
let program = program.unwrap(); let program = program.unwrap();
assert_eq!(program.statements.len(), 1); assert_eq!(program.statements.len(), 1);
@ -250,6 +255,7 @@ mod tests {
let mut parser = Parser::new(lexer); let mut parser = Parser::new(lexer);
let program = parser.parse_program(); let program = parser.parse_program();
check_parser_errors(&parser); check_parser_errors(&parser);
assert_eq!(parser.errors.len(), 0);
assert!(program.is_some()); assert!(program.is_some());
assert_eq!( assert_eq!(
@ -340,6 +346,7 @@ mod tests {
let mut parser = Parser::new(lexer); let mut parser = Parser::new(lexer);
let program = parser.parse_program(); let program = parser.parse_program();
check_parser_errors(&parser); check_parser_errors(&parser);
assert_eq!(parser.errors.len(), 0);
assert!(program.is_some()); assert!(program.is_some());
assert_eq!(program.unwrap().statements, test.1); assert_eq!(program.unwrap().statements, test.1);
} }
@ -467,6 +474,7 @@ mod tests {
let mut parser = Parser::new(lexer); let mut parser = Parser::new(lexer);
let program = parser.parse_program(); let program = parser.parse_program();
check_parser_errors(&parser); check_parser_errors(&parser);
assert_eq!(parser.errors.len(), 0);
assert!(program.is_some()); assert!(program.is_some());
assert_eq!(program.unwrap().statements, test.1); assert_eq!(program.unwrap().statements, test.1);
} }
@ -498,6 +506,11 @@ mod tests {
("false", "false"), ("false", "false"),
("3 > 5 == false", "((3 > 5) == false)"), ("3 > 5 == false", "((3 > 5) == false)"),
("3 < 5 == true", "((3 < 5) == true)"), ("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() { for test in test_cases.iter() {
@ -505,6 +518,7 @@ mod tests {
let mut parser = Parser::new(lexer); let mut parser = Parser::new(lexer);
let program = parser.parse_program(); let program = parser.parse_program();
check_parser_errors(&parser); check_parser_errors(&parser);
assert_eq!(parser.errors.len(), 0);
assert!(program.is_some()); assert!(program.is_some());
assert_eq!(program.unwrap().to_string(), test.1); assert_eq!(program.unwrap().to_string(), test.1);
} }
@ -547,6 +561,98 @@ mod tests {
let mut parser = Parser::new(lexer); let mut parser = Parser::new(lexer);
let program = parser.parse_program(); let program = parser.parse_program();
check_parser_errors(&parser); 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!(program.is_some());
assert_eq!(program.unwrap(), test.1); assert_eq!(program.unwrap(), test.1);
} }