This commit is contained in:
2025-09-15 15:54:23 +10:00
parent 80dbb09db0
commit 9df6b70b75
2 changed files with 122 additions and 148 deletions

View File

@@ -266,106 +266,6 @@ fn eval_range(
} }
} }
fn eval_add(lval: &Eval, rval: &Eval) -> Result<Eval, LeadErr> {
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<Eval, LeadErr> {
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<Eval, LeadErr> {
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<Eval, LeadErr> {
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<Eval, LeadErr> { fn eval_pos(val: &Eval) -> Result<Eval, LeadErr> {
match val { match val {

View File

@@ -10,7 +10,47 @@ use crate::{
// -------------------------------------------------- // // -------------------------------------------------- //
fn eval_unary_numeric( fn eval_const(args: &Vec<Expr>, value: Eval, label: &str) -> Result<Eval, LeadErr> {
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<Expr>) -> Result<Eval, LeadErr> {
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<Expr>, args: &Vec<Expr>,
precs: &mut HashSet<CellRef>, precs: &mut HashSet<CellRef>,
grid: Option<&Grid>, 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) => { ($fn_name:ident, $func:expr, $label:expr) => {
pub fn $fn_name( pub fn $fn_name(
args: &Vec<Expr>, args: &Vec<Expr>,
precs: &mut HashSet<CellRef>, precs: &mut HashSet<CellRef>,
grid: Option<&Grid>, grid: Option<&Grid>,
) -> Result<Eval, LeadErr> { ) -> Result<Eval, LeadErr> {
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_func!(eval_exp, |x| x.exp(), "EXP");
unary_numeric_func!(eval_log, |x| x.ln(), "LOG"); unary_func!(eval_log, |x| x.ln(), "LOG");
unary_numeric_func!(eval_sqrt, |x| x.sqrt(), "SQRT"); unary_func!(eval_sqrt, |x| x.sqrt(), "SQRT");
unary_numeric_func!(eval_abs, |x| x.abs(), "ABS"); unary_func!(eval_abs, |x| x.abs(), "ABS");
unary_numeric_func!(eval_sin, |x| x.sin(), "SIN"); unary_func!(eval_sin, |x| x.sin(), "SIN");
unary_numeric_func!(eval_cos, |x| x.cos(), "COS"); unary_func!(eval_cos, |x| x.cos(), "COS");
unary_numeric_func!(eval_tan, |x| x.tan(), "TAN"); unary_func!(eval_tan, |x| x.tan(), "TAN");
unary_numeric_func!(eval_asin, |x| x.asin(), "ASIN"); unary_func!(eval_asin, |x| x.asin(), "ASIN");
unary_numeric_func!(eval_acos, |x| x.acos(), "ACOS"); unary_func!(eval_acos, |x| x.acos(), "ACOS");
unary_numeric_func!(eval_atan, |x| x.atan(), "ATAN"); unary_func!(eval_atan, |x| x.atan(), "ATAN");
// -------------------------------------------------- // // -------------------------------------------------- //
fn eval_const(args: &Vec<Expr>, value: Eval, label: &str) -> Result<Eval, LeadErr> { fn eval_infix(
if args.len() != 0 { lhs: &Eval,
return Err(LeadErr { rhs: &Eval,
func: fn(f64, f64) -> f64,
func_name: &str,
) -> Result<Eval, LeadErr> {
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, LeadErr> {
eval_infix(lhs, rhs, $func, $label)
}
};
}
// Can concat string as well
pub fn eval_add(lval: &Eval, rval: &Eval) -> Result<Eval, LeadErr> {
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(), title: "Evaluation error.".into(),
desc: format!("{label} function requires no arguments."), desc: "Expected string or numeric types for ADD function.".into(),
code: LeadErrCode::Invalid, code: LeadErrCode::Unsupported,
}); }),
} }
Ok(value)
} }
macro_rules! const_numeric_func { infix!(eval_sub, |x, y| x - y, "SUB");
($fn_name:ident, $value:expr, $label:expr) => { infix!(eval_mul, |x, y| x * y, "MUL");
pub fn $fn_name(args: &Vec<Expr>) -> Result<Eval, LeadErr> { infix!(eval_div, |x, y| x / y, "DIV");
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"
);
// -------------------------------------------------- //