Added If/Else Evaluator

1. Added If Else evaluator
2. Added tests to support expression evaluator
3. Fixed issues reported by clippy
This commit is contained in:
Ishan Jain 2020-01-21 23:51:09 +05:30
parent 00288b7b4d
commit 62fd586acb
4 changed files with 76 additions and 25 deletions

View File

@ -29,7 +29,7 @@ impl Object {
#[cfg(test)]
mod tests {
use crate::{
evaluator::{tree_walker::TreeWalker, Evaluator, Object},
evaluator::{tree_walker::TreeWalker, Evaluator, Object, FALSE, NULL, TRUE},
lexer::Lexer,
parser::{ast::Node, Parser},
};
@ -56,16 +56,16 @@ mod tests {
#[test]
fn eval_boolean_expression() {
let test_cases = [
("true", Some(Object::Boolean(true))),
("false", Some(Object::Boolean(false))),
("3 < 4", Some(Object::Boolean(true))),
("3 > 4", Some(Object::Boolean(false))),
("3 == 4", Some(Object::Boolean(false))),
("3 != 4", Some(Object::Boolean(true))),
("(1 < 2 ) == true", Some(Object::Boolean(true))),
("(1 < 2 ) == false", Some(Object::Boolean(false))),
("(1 > 2 ) == true", Some(Object::Boolean(false))),
("(1 > 2 ) == false", Some(Object::Boolean(true))),
("true", Some(TRUE)),
("false", Some(FALSE)),
("3 < 4", Some(TRUE)),
("3 > 4", Some(FALSE)),
("3 == 4", Some(FALSE)),
("3 != 4", Some(TRUE)),
("(1 < 2 ) == true", Some(TRUE)),
("(1 < 2 ) == false", Some(FALSE)),
("(1 > 2 ) == true", Some(FALSE)),
("(1 > 2 ) == false", Some(TRUE)),
];
for test in test_cases.iter() {
@ -83,12 +83,12 @@ mod tests {
#[test]
fn eval_bang_operator() {
let test_cases = [
("!5", Some(Object::Boolean(false))),
("!!true", Some(Object::Boolean(true))),
("!!false", Some(Object::Boolean(false))),
("!!5", Some(Object::Boolean(true))),
("!true", Some(Object::Boolean(false))),
("!false", Some(Object::Boolean(true))),
("!5", Some(FALSE)),
("!!true", Some(TRUE)),
("!!false", Some(FALSE)),
("!!5", Some(TRUE)),
("!true", Some(FALSE)),
("!false", Some(TRUE)),
];
for test in test_cases.iter() {
let lexer = Lexer::new(test.0);
@ -132,4 +132,37 @@ mod tests {
assert_eq!(eval, test.1);
}
}
#[test]
fn eval_if_else_expression() {
let test_cases = [
// (
// "if (x > 10) {
// puts(\"everything okay\");
// } else {
// puts(\"x is too low!\");
// potato();
// }",
// Object::Null,
// ),
("if(true) {10}", Some(Object::Integer(10))),
("if(false) {10}", Some(NULL)),
("if (1) {10}", Some(Object::Integer(10))),
("if(1 < 2) {10}", Some(Object::Integer(10))),
("if (1 > 2) {10}", Some(NULL)),
("if(1 > 2) {10} else {20}", Some(Object::Integer(20))),
("if (1 < 2) {10} else {20}", Some(Object::Integer(10))),
];
for test in test_cases.iter() {
let lexer = Lexer::new(test.0);
let mut parser = Parser::new(lexer);
let program = parser.parse_program();
assert!(program.is_some());
let program = program.unwrap();
let evaluator = TreeWalker::new();
let eval = evaluator.eval(Node::Program(program));
assert_eq!(eval, test.1);
}
}
}

View File

@ -20,6 +20,7 @@ impl Evaluator for TreeWalker {
Statement::ExpressionStatement(ExpressionStatement { expression, .. }) => {
self.eval(Node::Expression(expression))
}
Statement::BlockStatement(bs) => self.eval_statements(bs.statements),
_ => None,
},
Node::Expression(expr) => match expr {
@ -32,9 +33,19 @@ impl Evaluator for TreeWalker {
Expression::InfixExpression(ie) => {
let left = self.eval(Node::Expression(*ie.left))?;
let right = self.eval(Node::Expression(*ie.right))?;
self.eval_infix_expression(left, ie.operator, right)
}
Expression::IfExpression(ie) => {
let condition = self.eval(Node::Expression(*ie.condition))?;
if self.is_truthy(&condition) {
self.eval(Node::Statement(Statement::BlockStatement(ie.consequence)))
} else if let Some(alternative) = ie.alternative {
self.eval(Node::Statement(Statement::BlockStatement(alternative)))
} else {
Some(NULL)
}
}
_ => None,
},
}
@ -99,4 +110,13 @@ impl TreeWalker {
_ => NULL,
}
}
fn is_truthy(&self, obj: &Object) -> bool {
match *obj {
NULL => false,
TRUE => true,
FALSE => false,
_ => true,
}
}
}

View File

@ -413,9 +413,9 @@ impl Display for BooleanExpression {
#[derive(Debug, PartialEq)]
pub struct IfExpression {
condition: Box<Expression>,
consequence: BlockStatement,
alternative: Option<BlockStatement>,
pub condition: Box<Expression>,
pub consequence: BlockStatement,
pub alternative: Option<BlockStatement>,
}
impl IfExpression {
@ -465,7 +465,7 @@ impl Display for IfExpression {
#[derive(Debug, PartialEq)]
pub struct BlockStatement {
statements: Vec<Statement>,
pub statements: Vec<Statement>,
}
impl BlockStatement {

View File

@ -171,9 +171,7 @@ mod tests {
};
fn check_parser_errors(p: &Parser) {
if p.errors.is_empty() {
return;
} else {
if !p.errors.is_empty() {
let mut out = String::new();
out.push_str(&format!("parser has {} errors\n", p.errors.len()));
for error in &p.errors {