1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
use crate::{error::CalcError, expr::val::Val, parser::fn_call::FnCall};
use crate::{expr::unit::Unit, expr::Expr, statement::Scope};
use std::ops::RangeInclusive;
type FunctionArgsRange = (RangeInclusive<usize>, fn(&[f64]) -> f64);
macro_rules! match_unary_fn {
($f:expr, $($name:ident),* $(,)?) => {
{
let res: Option<FunctionArgsRange> = match $f {
$(stringify!($name) => Some((1..=1, |x: &[f64]| f64::$name(x[0]))),)*
_ => None,
};
res
}
};
}
pub fn eval_fn_call(fc: &FnCall, scope: &Scope) -> Result<Val, CalcError> {
let e = |a: &Expr| a.eval(scope);
let name = fc.name.as_str();
let args_len = fc.args.len();
let fn_args_range = match_unary_fn!(
name, abs, acos, acosh, asin, asinh, atan, atanh, cbrt, ceil, cos, cosh, exp, floor, ln,
log10, log2, round, sin, sinh, sqrt, tan, tanh,
)
.or_else(|| match name {
"atan2" => Some((2..=2, |x: &[f64]| f64::atan2(x[0], x[1]))),
"min" => Some((1..=usize::MAX, |x: &[f64]| {
x.iter().cloned().reduce(f64::min).unwrap()
})),
"max" => Some((1..=usize::MAX, |x: &[f64]| {
x.iter().cloned().reduce(f64::max).unwrap()
})),
_ => None,
});
if let Some((args_range, calc)) = fn_args_range {
if args_range.contains(&args_len) {
let args: Result<Vec<f64>, CalcError> = fc
.args
.iter()
.map(|a| {
let a = e(&a)?;
if a.unit.desc.is_empty() {
Ok(a.num * a.unit.mult * 10f64.powi(a.unit.exp as i32))
} else {
Err(CalcError::UnitError(format!(
"Can't take {} of unit-ed value",
fc.name
)))
}
})
.collect();
let args = args?;
let res: Val = (calc(args.as_slice()), Unit::empty()).into();
Ok(res.clamp_num())
} else {
Err(CalcError::Other(format!(
"Incorrect number of arguments to function {}, expected {:?} but got {}",
name, args_range, args_len
)))
}
} else {
Err(CalcError::Other(format!("Unknown function {}", name)))
}
}