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)]
|
#[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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user