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:
parent
00288b7b4d
commit
62fd586acb
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue
Block a user