diff --git a/backend/src/evaluator/mod.rs b/backend/src/evaluator/mod.rs index 9d2e4a8..e424f45 100644 --- a/backend/src/evaluator/mod.rs +++ b/backend/src/evaluator/mod.rs @@ -266,106 +266,6 @@ fn eval_range( } } -fn eval_add(lval: &Eval, rval: &Eval) -> Result { - match (lval, rval) { - (Eval::Literal(a), Eval::Literal(b)) => { - if let Some(res) = eval_numeric_infix(a, b, |x, y| x + y) { - return Ok(Eval::Literal(res)); - } - - // Try string concatenation - if let (Literal::String(x), Literal::String(y)) = (a, b) { - let mut res = x.to_owned(); - res.push_str(y); - return Ok(Eval::Literal(Literal::String(res))); - } - - Err(LeadErr { - title: "Evaluation error.".into(), - desc: "Expected string or numeric types for ADD function.".into(), - code: LeadErrCode::Unsupported, - }) - } - _ => Err(LeadErr { - title: "Evaluation error.".into(), - desc: "Expected string or numeric types for ADD function.".into(), - code: LeadErrCode::Unsupported, - }), - } -} - -fn eval_sub(lval: &Eval, rval: &Eval) -> Result { - match (lval, rval) { - (Eval::Literal(a), Eval::Literal(b)) => { - if let Some(res) = eval_numeric_infix(a, b, |x, y| x - y) { - return Ok(Eval::Literal(res)); - } - - Err(LeadErr { - title: "Evaluation error.".into(), - desc: "Expected numeric types for SUB function.".into(), - code: LeadErrCode::Unsupported, - }) - } - _ => Err(LeadErr { - title: "Evaluation error.".into(), - desc: "Expected numeric types for SUB function.".into(), - code: LeadErrCode::Unsupported, - }), - } -} - -fn eval_mul(lval: &Eval, rval: &Eval) -> Result { - match (lval, rval) { - (Eval::Literal(a), Eval::Literal(b)) => { - if let Some(res) = eval_numeric_infix(a, b, |x, y| x * y) { - return Ok(Eval::Literal(res)); - } - - Err(LeadErr { - title: "Evaluation error.".into(), - desc: "Expected numeric types for MUL function.".into(), - code: LeadErrCode::Unsupported, - }) - } - _ => Err(LeadErr { - title: "Evaluation error.".into(), - desc: "Expected numeric types for MUL function.".into(), - code: LeadErrCode::Unsupported, - }), - } -} - -fn eval_div(lval: &Eval, rval: &Eval) -> Result { - match (lval, rval) { - (Eval::Literal(a), Eval::Literal(b)) => { - if let (Literal::Number(_), Literal::Number(y)) = (a, b) { - if *y == 0f64 { - return Err(LeadErr { - title: "Evaluation error.".into(), - desc: "Attempted to divide by zero.".into(), - code: LeadErrCode::DivZero, - }); - } - } - - if let Some(res) = eval_numeric_infix(a, b, |x, y| x / y) { - return Ok(Eval::Literal(res)); - } - - Err(LeadErr { - title: "Evaluation error.".into(), - desc: "Expected numeric types for DIV function.".into(), - code: LeadErrCode::Unsupported, - }) - } - _ => Err(LeadErr { - title: "Evaluation error.".into(), - desc: "Expected numeric types for DIV function.".into(), - code: LeadErrCode::Unsupported, - }), - } -} fn eval_pos(val: &Eval) -> Result { match val { diff --git a/backend/src/evaluator/numerics.rs b/backend/src/evaluator/numerics.rs index 7f5ded2..e1a8600 100644 --- a/backend/src/evaluator/numerics.rs +++ b/backend/src/evaluator/numerics.rs @@ -10,7 +10,47 @@ use crate::{ // -------------------------------------------------- // -fn eval_unary_numeric( +fn eval_const(args: &Vec, value: Eval, label: &str) -> Result { + if args.len() != 0 { + return Err(LeadErr { + title: "Evaluation error.".into(), + desc: format!("{label} function requires no arguments."), + code: LeadErrCode::Invalid, + }); + } + + Ok(value) +} + +macro_rules! const_func { + ($fn_name:ident, $value:expr, $label:expr) => { + pub fn $fn_name(args: &Vec) -> Result { + eval_const(args, $value, $label) + } + }; +} + +const_func!( + eval_pi, + Eval::Literal(Literal::Number(std::f64::consts::PI)), + "PI" +); + +const_func!( + eval_tau, + Eval::Literal(Literal::Number(std::f64::consts::TAU)), + "TAU" +); + +const_func!( + eval_sqrt2, + Eval::Literal(Literal::Number(std::f64::consts::SQRT_2)), + "SQRT2" +); + +// -------------------------------------------------- // + +fn eval_unary( args: &Vec, precs: &mut HashSet, grid: Option<&Grid>, @@ -39,69 +79,103 @@ fn eval_unary_numeric( } } -macro_rules! unary_numeric_func { +macro_rules! unary_func { ($fn_name:ident, $func:expr, $label:expr) => { pub fn $fn_name( args: &Vec, precs: &mut HashSet, grid: Option<&Grid>, ) -> Result { - eval_unary_numeric(args, precs, grid, $func, $label) + eval_unary(args, precs, grid, $func, $label) } }; } -unary_numeric_func!(eval_exp, |x| x.exp(), "EXP"); -unary_numeric_func!(eval_log, |x| x.ln(), "LOG"); -unary_numeric_func!(eval_sqrt, |x| x.sqrt(), "SQRT"); -unary_numeric_func!(eval_abs, |x| x.abs(), "ABS"); +unary_func!(eval_exp, |x| x.exp(), "EXP"); +unary_func!(eval_log, |x| x.ln(), "LOG"); +unary_func!(eval_sqrt, |x| x.sqrt(), "SQRT"); +unary_func!(eval_abs, |x| x.abs(), "ABS"); -unary_numeric_func!(eval_sin, |x| x.sin(), "SIN"); -unary_numeric_func!(eval_cos, |x| x.cos(), "COS"); -unary_numeric_func!(eval_tan, |x| x.tan(), "TAN"); +unary_func!(eval_sin, |x| x.sin(), "SIN"); +unary_func!(eval_cos, |x| x.cos(), "COS"); +unary_func!(eval_tan, |x| x.tan(), "TAN"); -unary_numeric_func!(eval_asin, |x| x.asin(), "ASIN"); -unary_numeric_func!(eval_acos, |x| x.acos(), "ACOS"); -unary_numeric_func!(eval_atan, |x| x.atan(), "ATAN"); +unary_func!(eval_asin, |x| x.asin(), "ASIN"); +unary_func!(eval_acos, |x| x.acos(), "ACOS"); +unary_func!(eval_atan, |x| x.atan(), "ATAN"); // -------------------------------------------------- // -fn eval_const(args: &Vec, value: Eval, label: &str) -> Result { - if args.len() != 0 { - return Err(LeadErr { +fn eval_infix( + lhs: &Eval, + rhs: &Eval, + func: fn(f64, f64) -> f64, + func_name: &str, +) -> Result { + let err = LeadErr { + title: "Evaluation error.".into(), + desc: format!("{func_name} function requires a numeric argument."), + code: LeadErrCode::TypeErr, + }; + + let l = match lhs.to_owned() { + Eval::Literal(Literal::Number(num)) => num, + Eval::CellRef { eval, .. } => match *eval { + Eval::Literal(Literal::Number(num)) => num, + _ => return Err(err), + }, + _ => return Err(err), + }; + + let r = match rhs.to_owned() { + Eval::Literal(Literal::Number(num)) => num, + Eval::CellRef { eval, .. } => match *eval { + Eval::Literal(Literal::Number(num)) => num, + _ => return Err(err), + }, + _ => return Err(err), + }; + + Ok(Eval::Literal(Literal::Number(func(l, r)))) +} + +macro_rules! infix { + ($fn_name:ident, $func:expr, $label:expr) => { + pub fn $fn_name(lhs: &Eval, rhs: &Eval) -> Result { + eval_infix(lhs, rhs, $func, $label) + } + }; +} + +// Can concat string as well +pub fn eval_add(lval: &Eval, rval: &Eval) -> Result { + match (lval, rval) { + (Eval::Literal(a), Eval::Literal(b)) => { + if let Ok(res) = eval_infix(lval, rval, |x, y| x + y, "ADD") { + return Ok(res); + } + + // Try string concatenation + if let (Literal::String(x), Literal::String(y)) = (a, b) { + let mut res = x.to_owned(); + res.push_str(y); + return Ok(Eval::Literal(Literal::String(res))); + } + + Err(LeadErr { + title: "Evaluation error.".into(), + desc: "Expected string or numeric types for ADD function.".into(), + code: LeadErrCode::Unsupported, + }) + } + _ => Err(LeadErr { title: "Evaluation error.".into(), - desc: format!("{label} function requires no arguments."), - code: LeadErrCode::Invalid, - }); + desc: "Expected string or numeric types for ADD function.".into(), + code: LeadErrCode::Unsupported, + }), } - - Ok(value) } -macro_rules! const_numeric_func { - ($fn_name:ident, $value:expr, $label:expr) => { - pub fn $fn_name(args: &Vec) -> Result { - eval_const(args, $value, $label) - } - }; -} - -const_numeric_func!( - eval_pi, - Eval::Literal(Literal::Number(std::f64::consts::PI)), - "PI" -); - -const_numeric_func!( - eval_tau, - Eval::Literal(Literal::Number(std::f64::consts::TAU)), - "TAU" -); - -const_numeric_func!( - eval_sqrt2, - Eval::Literal(Literal::Number(std::f64::consts::SQRT_2)), - "SQRT2" -); - -// -------------------------------------------------- // +infix!(eval_sub, |x, y| x - y, "SUB"); +infix!(eval_mul, |x, y| x * y, "MUL"); +infix!(eval_div, |x, y| x / y, "DIV");