diff --git a/src/evaluator/mod.rs b/src/evaluator/mod.rs index b1d08fd..24dabfc 100644 --- a/src/evaluator/mod.rs +++ b/src/evaluator/mod.rs @@ -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); + } + } } diff --git a/src/evaluator/tree_walker/mod.rs b/src/evaluator/tree_walker/mod.rs index 898b745..3250cf7 100644 --- a/src/evaluator/tree_walker/mod.rs +++ b/src/evaluator/tree_walker/mod.rs @@ -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, + } + } } diff --git a/src/parser/ast/mod.rs b/src/parser/ast/mod.rs index 9ec420f..df9128b 100644 --- a/src/parser/ast/mod.rs +++ b/src/parser/ast/mod.rs @@ -413,9 +413,9 @@ impl Display for BooleanExpression { #[derive(Debug, PartialEq)] pub struct IfExpression { - condition: Box, - consequence: BlockStatement, - alternative: Option, + pub condition: Box, + pub consequence: BlockStatement, + pub alternative: Option, } impl IfExpression { @@ -465,7 +465,7 @@ impl Display for IfExpression { #[derive(Debug, PartialEq)] pub struct BlockStatement { - statements: Vec, + pub statements: Vec, } impl BlockStatement { diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 047e012..148f3e8 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -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 {