use crate::parser::Expression; use crate::parser::Operator; use crate::context::Context; use crate::parser::LineLocation; use super::operator::eval_operator; use super::function::eval_function; use super::EvalError; pub fn evaluate(t: &Expression, context: &mut Context) -> Result { // Keeps track of our position in the expression tree. // For example, the coordinates [0, 2, 1] are interpreted as follows: // Start at the root node, // then move to that node's 0th child, // then move to that node's 2nd child, // then move to that node's 1st child. // let mut coords: Vec = Vec::with_capacity(16); let mut root = t.clone(); // coords points to the *next* node we will move to. coords.push(0); // Repeats while we have coordinates to parse. // Exits when we finish parsing the root node. loop { // Current position in the tree let g = root.get_at_coords_mut( &coords[0 .. coords.len() - 1] ).unwrap(); // "Move up" step. // We move up if we're at a leaf or if we're out of children to move down to. if { g.is_quantity() || g.get_args().is_none() || (coords.len() != 0 && (*coords.last().unwrap() >= g.get_args().unwrap().len())) } { let new = match g { Expression::Quantity(_, _) => None, Expression::Constant(_, c) => { Some(evaluate(&c.value(), context).unwrap()) }, Expression::Variable(l, s) => { let v = context.get_variable(&s); if v.is_none() { return Err((*l, EvalError::Undefined(s.clone()))); } v }, Expression::Operator(_, Operator::Function(_), _) => { Some(eval_function(g)?) }, Expression::Operator(_, _, _) => { eval_operator(g, context)? }, }; if let Some(mut new) = new { if let Expression::Constant(_,_) = g { // Fix constant line location. // Constant expansion does not change the location of a value, // but other operations might. new.set_linelocation(&g.get_linelocation()); } *g = new; } // Move up the tree coords.pop(); if coords.len() != 0 { *coords.last_mut().unwrap() += 1; } else { break; } } else { // Move down the tree // Don't evaluate the first argument of a define. // This prevents variables from being expanded before a re-assignment. if let Expression::Operator(_, Operator::Define, _) = g { *coords.last_mut().unwrap() += 1; coords.push(0); continue; } coords.push(0); } } return Ok(root); }