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)] #[cfg(test)]
mod tests { mod tests {
use crate::{ use crate::{
evaluator::{tree_walker::TreeWalker, Evaluator, Object}, evaluator::{tree_walker::TreeWalker, Evaluator, Object, FALSE, NULL, TRUE},
lexer::Lexer, lexer::Lexer,
parser::{ast::Node, Parser}, parser::{ast::Node, Parser},
}; };
@ -56,16 +56,16 @@ mod tests {
#[test] #[test]
fn eval_boolean_expression() { fn eval_boolean_expression() {
let test_cases = [ let test_cases = [
("true", Some(Object::Boolean(true))), ("true", Some(TRUE)),
("false", Some(Object::Boolean(false))), ("false", Some(FALSE)),
("3 < 4", Some(Object::Boolean(true))), ("3 < 4", Some(TRUE)),
("3 > 4", Some(Object::Boolean(false))), ("3 > 4", Some(FALSE)),
("3 == 4", Some(Object::Boolean(false))), ("3 == 4", Some(FALSE)),
("3 != 4", Some(Object::Boolean(true))), ("3 != 4", Some(TRUE)),
("(1 < 2 ) == true", Some(Object::Boolean(true))), ("(1 < 2 ) == true", Some(TRUE)),
("(1 < 2 ) == false", Some(Object::Boolean(false))), ("(1 < 2 ) == false", Some(FALSE)),
("(1 > 2 ) == true", Some(Object::Boolean(false))), ("(1 > 2 ) == true", Some(FALSE)),
("(1 > 2 ) == false", Some(Object::Boolean(true))), ("(1 > 2 ) == false", Some(TRUE)),
]; ];
for test in test_cases.iter() { for test in test_cases.iter() {
@ -83,12 +83,12 @@ mod tests {
#[test] #[test]
fn eval_bang_operator() { fn eval_bang_operator() {
let test_cases = [ let test_cases = [
("!5", Some(Object::Boolean(false))), ("!5", Some(FALSE)),
("!!true", Some(Object::Boolean(true))), ("!!true", Some(TRUE)),
("!!false", Some(Object::Boolean(false))), ("!!false", Some(FALSE)),
("!!5", Some(Object::Boolean(true))), ("!!5", Some(TRUE)),
("!true", Some(Object::Boolean(false))), ("!true", Some(FALSE)),
("!false", Some(Object::Boolean(true))), ("!false", Some(TRUE)),
]; ];
for test in test_cases.iter() { for test in test_cases.iter() {
let lexer = Lexer::new(test.0); let lexer = Lexer::new(test.0);
@ -132,4 +132,37 @@ mod tests {
assert_eq!(eval, test.1); 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, .. }) => { Statement::ExpressionStatement(ExpressionStatement { expression, .. }) => {
self.eval(Node::Expression(expression)) self.eval(Node::Expression(expression))
} }
Statement::BlockStatement(bs) => self.eval_statements(bs.statements),
_ => None, _ => None,
}, },
Node::Expression(expr) => match expr { Node::Expression(expr) => match expr {
@ -32,9 +33,19 @@ impl Evaluator for TreeWalker {
Expression::InfixExpression(ie) => { Expression::InfixExpression(ie) => {
let left = self.eval(Node::Expression(*ie.left))?; let left = self.eval(Node::Expression(*ie.left))?;
let right = self.eval(Node::Expression(*ie.right))?; let right = self.eval(Node::Expression(*ie.right))?;
self.eval_infix_expression(left, ie.operator, 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, _ => None,
}, },
} }
@ -99,4 +110,13 @@ impl TreeWalker {
_ => NULL, _ => 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)] #[derive(Debug, PartialEq)]
pub struct IfExpression { pub struct IfExpression {
condition: Box<Expression>, pub condition: Box<Expression>,
consequence: BlockStatement, pub consequence: BlockStatement,
alternative: Option<BlockStatement>, pub alternative: Option<BlockStatement>,
} }
impl IfExpression { impl IfExpression {
@ -465,7 +465,7 @@ impl Display for IfExpression {
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub struct BlockStatement { pub struct BlockStatement {
statements: Vec<Statement>, pub statements: Vec<Statement>,
} }
impl BlockStatement { impl BlockStatement {

View File

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