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:
parent
9763c5ca2e
commit
44506cf591
|
@ -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::{
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user