// TODO: This is all a mess. Almost certainly because right now, I don't know any better way to do this. // It's just constantly unwrapping enums from one place and rewrapping it to some other enum(or even the same enum) and returning it // The error handling story is pretty bad too use crate::{ evaluator::{Evaluator, Object, FALSE, NULL, TRUE}, lexer::TokenType, parser::ast::{BlockStatement, Expression, ExpressionStatement, Node, Program, Statement}, }; pub struct TreeWalker; impl TreeWalker { pub fn new() -> impl Evaluator { Self } } impl Evaluator for TreeWalker { fn eval(&self, node: Node) -> Option { match node { Node::Program(p) => self.eval_program(p), Node::Statement(stmt) => match stmt { Statement::ExpressionStatement(ExpressionStatement { expression, .. }) => { self.eval(Node::Expression(expression)) } Statement::BlockStatement(bs) => self.eval_block_statement(bs), Statement::Return(ret) => { let ret_val = self.eval(Node::Expression(ret.value?))?; Some(Object::ReturnValue(Box::new(ret_val))) } _ => None, }, Node::Expression(expr) => match expr { Expression::IntegerLiteral(il) => Some(Object::Integer(il.value)), Expression::BooleanExpression(b) => Some(Object::Boolean(b.value)), Expression::PrefixExpression(p) => { let expr = self.eval(Node::Expression(*p.right))?; self.eval_prefix_expression(p.operator, expr) } 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, }, } } } impl TreeWalker { fn eval_program(&self, prg: Program) -> Option { let mut out: Option = None; for stmt in prg.statements { out = self.eval(Node::Statement(stmt)); // No need to evaluate any more statements from a statements vector once we // get a return keyword. nothing after in the block matters. if let Some(out) = out.clone() { match out { Object::ReturnValue(v) => return Some(*v), Object::Error(_) => return Some(out), _ => {} } } } out } fn eval_block_statement(&self, bs: BlockStatement) -> Option { let mut out: Option = None; for stmt in bs.statements { out = self.eval(Node::Statement(stmt)); // TODO: Find a nicer way to do this. :( // The objective here is, // If we encounter a node of type ReturnValue, Don't unwrap it. Return it as is // So, It can evaluated again by the eval function. // This is helpful when we have a nested structure with multiple return statments. // something like, // if (true) { // if (true) { // return 10; // } // return 1; // } // This will return 1 if we unwrap return right here and return the value within. // But in reality that shouldn't happen. It should be returning 10 // So, We don't unwrap the ReturnValue node when we encounter 10. Just return it as is and it is later eval-ed // and the correct value is returned if let Some(out) = out.clone() { match out { Object::ReturnValue(v) => { return Some(Object::ReturnValue(v)); } Object::Error(_) => return Some(out), _ => {} } } } out } fn eval_prefix_expression(&self, operator: TokenType, expr: Object) -> Option { match operator { TokenType::Bang => Some(self.eval_bang_operator_expression(expr)), TokenType::Minus => Some(self.eval_minus_prefix_operator_expression(expr)), _ => Some(Object::Error(format!( "unknown operator: {}{}", operator, expr ))), } } fn eval_infix_expression( &self, left: Object, operator: TokenType, right: Object, ) -> Option { Some(match (left.clone(), right.clone()) { (Object::Integer(l), Object::Integer(r)) => match operator { TokenType::Plus => Object::Integer(l + r), TokenType::Minus => Object::Integer(l - r), TokenType::Asterisk => Object::Integer(l * r), TokenType::Slash => Object::Integer(l / r), TokenType::Equals => Object::Boolean(l == r), TokenType::NotEquals => Object::Boolean(l != r), TokenType::GreaterThan => Object::Boolean(l > r), TokenType::LessThan => Object::Boolean(l < r), _ => Object::Error(format!("unknown operator: {} {} {}", l, operator, r)), }, (o1, o2) if o1.to_string() != o2.to_string() => { Object::Error(format!("type mismatch: {} {} {}", o1, operator, o2)) } _ => match operator { TokenType::Equals => Object::Boolean(left == right), TokenType::NotEquals => Object::Boolean(left != right), _ => Object::Error(format!("unknown operator: {} {} {}", left, operator, right)), }, }) } fn eval_bang_operator_expression(&self, expr: Object) -> Object { match expr { TRUE => FALSE, FALSE => TRUE, NULL => TRUE, _ => FALSE, } } fn eval_minus_prefix_operator_expression(&self, expr: Object) -> Object { match expr { Object::Integer(v) => Object::Integer(-v), v => Object::Error(format!("unknown operator: -{}", v)), } } fn is_truthy(&self, obj: &Object) -> bool { match *obj { NULL => false, TRUE => true, FALSE => false, _ => true, } } }