Added Boolean Expression parser along with tests
This commit is contained in:
parent
2293c442eb
commit
9763c5ca2e
|
@ -65,6 +65,9 @@ pub struct LetStatement {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LetStatement {
|
impl LetStatement {
|
||||||
|
pub fn new(name: Identifier, value: Option<Expression>) -> Self {
|
||||||
|
Self { name, value }
|
||||||
|
}
|
||||||
// TODO: Implement code to parse let statement
|
// TODO: Implement code to parse let statement
|
||||||
pub fn parse(parser: &mut Parser) -> Option<Self> {
|
pub fn parse(parser: &mut Parser) -> Option<Self> {
|
||||||
let mut stmt = LetStatement {
|
let mut stmt = LetStatement {
|
||||||
|
@ -142,6 +145,9 @@ pub struct ExpressionStatement {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ExpressionStatement {
|
impl ExpressionStatement {
|
||||||
|
pub fn new(token: Token, expression: Expression) -> Self {
|
||||||
|
Self { token, expression }
|
||||||
|
}
|
||||||
fn parse(parser: &mut Parser, current_token: Token) -> Option<Self> {
|
fn parse(parser: &mut Parser, current_token: Token) -> Option<Self> {
|
||||||
let stmt = ExpressionStatement {
|
let stmt = ExpressionStatement {
|
||||||
token: current_token.clone(),
|
token: current_token.clone(),
|
||||||
|
@ -177,6 +183,7 @@ pub enum Expression {
|
||||||
IntegerLiteral(IntegerLiteral),
|
IntegerLiteral(IntegerLiteral),
|
||||||
PrefixExpression(PrefixExpression),
|
PrefixExpression(PrefixExpression),
|
||||||
InfixExpression(InfixExpression),
|
InfixExpression(InfixExpression),
|
||||||
|
BooleanExpression(BooleanExpression),
|
||||||
// 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,
|
||||||
}
|
}
|
||||||
|
@ -227,10 +234,13 @@ impl Expression {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Some(Self::IntegerLiteral(IntegerLiteral::new(TokenType::Int, n)))
|
Some(Self::IntegerLiteral(IntegerLiteral::new(TokenType::Int, n)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn parse_boolean(_parser: &mut Parser, token: Token) -> Option<Self> {
|
||||||
|
Some(Self::BooleanExpression(BooleanExpression::new(token.name)))
|
||||||
|
}
|
||||||
|
|
||||||
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)?;
|
||||||
|
@ -268,6 +278,7 @@ impl Display for Expression {
|
||||||
Expression::IntegerLiteral(v) => v.value.to_string(),
|
Expression::IntegerLiteral(v) => v.value.to_string(),
|
||||||
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::None => "None".into(),
|
Expression::None => "None".into(),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -373,6 +384,27 @@ impl Display for InfixExpression {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub struct BooleanExpression {
|
||||||
|
token: TokenType,
|
||||||
|
value: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BooleanExpression {
|
||||||
|
pub fn new(token: TokenType) -> Self {
|
||||||
|
BooleanExpression {
|
||||||
|
token: token,
|
||||||
|
value: token == TokenType::True,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for BooleanExpression {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> {
|
||||||
|
write!(f, "{}", self.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::{
|
use crate::{
|
||||||
|
|
|
@ -14,6 +14,8 @@ use {
|
||||||
type PrefixParseFn = fn(&mut Parser, token: Token) -> Option<Expression>;
|
type PrefixParseFn = fn(&mut Parser, token: Token) -> Option<Expression>;
|
||||||
type InfixParseFn = fn(&mut Parser, Token, Expression) -> Option<Expression>;
|
type InfixParseFn = fn(&mut Parser, Token, Expression) -> Option<Expression>;
|
||||||
|
|
||||||
|
//TODO: Add Parser tracing so we can easily figure out the call stack hierarchy
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref PRECEDENCE_MAP: HashMap<TokenType, ExpressionPriority> = {
|
static ref PRECEDENCE_MAP: HashMap<TokenType, ExpressionPriority> = {
|
||||||
let mut m = HashMap::new();
|
let mut m = HashMap::new();
|
||||||
|
@ -49,6 +51,8 @@ impl<'a> Parser<'a> {
|
||||||
parser.register_prefix(TokenType::Int, Expression::parse_integer_literal);
|
parser.register_prefix(TokenType::Int, Expression::parse_integer_literal);
|
||||||
parser.register_prefix(TokenType::Bang, Expression::parse_prefix_expression);
|
parser.register_prefix(TokenType::Bang, Expression::parse_prefix_expression);
|
||||||
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::False, Expression::parse_boolean);
|
||||||
|
|
||||||
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);
|
||||||
|
@ -293,6 +297,28 @@ mod tests {
|
||||||
)),
|
)),
|
||||||
})],
|
})],
|
||||||
),
|
),
|
||||||
|
(
|
||||||
|
"!true;",
|
||||||
|
vec![Statement::ExpressionStatement(ExpressionStatement {
|
||||||
|
token: Token::new(TokenType::Bang),
|
||||||
|
expression: Expression::PrefixExpression(PrefixExpression::new(
|
||||||
|
Token::new(TokenType::Bang),
|
||||||
|
"!",
|
||||||
|
Expression::BooleanExpression(BooleanExpression::new(TokenType::True)),
|
||||||
|
)),
|
||||||
|
})],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"!false;",
|
||||||
|
vec![Statement::ExpressionStatement(ExpressionStatement {
|
||||||
|
token: Token::new(TokenType::Bang),
|
||||||
|
expression: Expression::PrefixExpression(PrefixExpression::new(
|
||||||
|
Token::new(TokenType::Bang),
|
||||||
|
"!",
|
||||||
|
Expression::BooleanExpression(BooleanExpression::new(TokenType::False)),
|
||||||
|
)),
|
||||||
|
})],
|
||||||
|
),
|
||||||
// TODO: Add this test when we add function call parser
|
// TODO: Add this test when we add function call parser
|
||||||
// (
|
// (
|
||||||
// "!isGreaterThanZero( 2);",
|
// "!isGreaterThanZero( 2);",
|
||||||
|
@ -399,6 +425,42 @@ mod tests {
|
||||||
)),
|
)),
|
||||||
})],
|
})],
|
||||||
),
|
),
|
||||||
|
(
|
||||||
|
"true == true",
|
||||||
|
vec![Statement::ExpressionStatement(ExpressionStatement {
|
||||||
|
token: Token::new(TokenType::True),
|
||||||
|
expression: Expression::InfixExpression(InfixExpression::new(
|
||||||
|
Token::new(TokenType::Equals),
|
||||||
|
Expression::BooleanExpression(BooleanExpression::new(TokenType::True)),
|
||||||
|
"==",
|
||||||
|
Expression::BooleanExpression(BooleanExpression::new(TokenType::True)),
|
||||||
|
)),
|
||||||
|
})],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"true != false",
|
||||||
|
vec![Statement::ExpressionStatement(ExpressionStatement {
|
||||||
|
token: Token::new(TokenType::True),
|
||||||
|
expression: Expression::InfixExpression(InfixExpression::new(
|
||||||
|
Token::new(TokenType::NotEquals),
|
||||||
|
Expression::BooleanExpression(BooleanExpression::new(TokenType::True)),
|
||||||
|
"!=",
|
||||||
|
Expression::BooleanExpression(BooleanExpression::new(TokenType::False)),
|
||||||
|
)),
|
||||||
|
})],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"false == false",
|
||||||
|
vec![Statement::ExpressionStatement(ExpressionStatement {
|
||||||
|
token: Token::new(TokenType::False),
|
||||||
|
expression: Expression::InfixExpression(InfixExpression::new(
|
||||||
|
Token::new(TokenType::Equals),
|
||||||
|
Expression::BooleanExpression(BooleanExpression::new(TokenType::False)),
|
||||||
|
"==",
|
||||||
|
Expression::BooleanExpression(BooleanExpression::new(TokenType::False)),
|
||||||
|
)),
|
||||||
|
})],
|
||||||
|
),
|
||||||
];
|
];
|
||||||
for test in infix_tests.iter() {
|
for test in infix_tests.iter() {
|
||||||
let lexer = Lexer::new(test.0);
|
let lexer = Lexer::new(test.0);
|
||||||
|
@ -411,7 +473,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_operator_precedence_parsing() {
|
fn operator_precedence_parsing() {
|
||||||
let test_cases = [
|
let test_cases = [
|
||||||
("-a * b", "((-a) * b)"),
|
("-a * b", "((-a) * b)"),
|
||||||
("!-a", "(!(-a))"),
|
("!-a", "(!(-a))"),
|
||||||
|
@ -432,6 +494,10 @@ mod tests {
|
||||||
"3 + 4 * 5 == 3 * 1 + 4 * 5",
|
"3 + 4 * 5 == 3 * 1 + 4 * 5",
|
||||||
"((3 + (4 * 5)) == ((3 * 1) + (4 * 5)))",
|
"((3 + (4 * 5)) == ((3 * 1) + (4 * 5)))",
|
||||||
),
|
),
|
||||||
|
("true", "true"),
|
||||||
|
("false", "false"),
|
||||||
|
("3 > 5 == false", "((3 > 5) == false)"),
|
||||||
|
("3 < 5 == true", "((3 < 5) == true)"),
|
||||||
];
|
];
|
||||||
|
|
||||||
for test in test_cases.iter() {
|
for test in test_cases.iter() {
|
||||||
|
@ -443,4 +509,46 @@ mod tests {
|
||||||
assert_eq!(program.unwrap().to_string(), test.1);
|
assert_eq!(program.unwrap().to_string(), test.1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn boolean_expression() {
|
||||||
|
let test_cases = [
|
||||||
|
(
|
||||||
|
"true;",
|
||||||
|
Program {
|
||||||
|
statements: vec![Statement::ExpressionStatement(ExpressionStatement::new(
|
||||||
|
Token::new(TokenType::True),
|
||||||
|
Expression::BooleanExpression(BooleanExpression::new(TokenType::True)),
|
||||||
|
))],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"false;",
|
||||||
|
Program {
|
||||||
|
statements: vec![Statement::ExpressionStatement(ExpressionStatement::new(
|
||||||
|
Token::new(TokenType::False),
|
||||||
|
Expression::BooleanExpression(BooleanExpression::new(TokenType::False)),
|
||||||
|
))],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"let foobar = true;",
|
||||||
|
Program {
|
||||||
|
statements: vec![Statement::Let(LetStatement::new(
|
||||||
|
Identifier::new(TokenType::Let, "foobar"),
|
||||||
|
None, // TODO: fix this when we complete parsing of let statements
|
||||||
|
))],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
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!(program.is_some());
|
||||||
|
assert_eq!(program.unwrap(), test.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user