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),
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<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> {
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<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)]
mod tests {
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::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);
}