use mumu::parser::interpreter::Interpreter;
use mumu::parser::types::{FunctionValue, Value};
use std::sync::{Arc, Mutex};
pub fn is_placeholder(v: &Value) -> bool {
match v {
Value::Placeholder => true,
Value::SingleString(s) if s == "_" => true,
Value::StrArray(ss) if ss.len() == 1 && ss[0] == "_" => true,
_ => false,
}
}
fn coerce_to_f64(val: Value, func_name: &str) -> Result<f64, String> {
match val {
Value::Int(i) => Ok(i as f64),
Value::Long(l) => Ok(l as f64),
Value::Float(ff) => Ok(ff),
_ => Err(format!("{} => argument must be numeric, got {:?}", func_name, val)),
}
}
fn coerce_to_float_or_float_array(v: Value) -> Result<Value, String> {
match v {
Value::Int(i) => Ok(Value::Float(i as f64)),
Value::Long(l) => Ok(Value::Float(l as f64)),
Value::Float(ff) => Ok(Value::Float(ff)),
Value::IntArray(xs) => {
let mut out = Vec::with_capacity(xs.len());
for &x in xs.iter() {
out.push(x as f64);
}
Ok(Value::FloatArray(out))
}
Value::FloatArray(ff) => Ok(Value::FloatArray(ff)),
_ => Err(format!("Cannot coerce {:?} to float or float array", v)),
}
}
pub fn math_plus_bridge(_interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, String> {
match args.len() {
0 => Ok(make_two_arg_partial(math_plus_finalize, None, None)),
1 => {
let a = &args[0];
if is_placeholder(a) {
Ok(make_two_arg_partial(math_plus_finalize, None, None))
} else {
Ok(make_two_arg_partial(math_plus_finalize, Some(a.clone()), None))
}
}
2 => {
let left_pl = is_placeholder(&args[0]);
let right_pl = is_placeholder(&args[1]);
if !left_pl && !right_pl {
math_plus_finalize(vec![args[0].clone(), args[1].clone()])
} else {
Ok(make_two_arg_partial(
math_plus_finalize,
if left_pl { None } else { Some(args[0].clone()) },
if right_pl { None } else { Some(args[1].clone()) },
))
}
}
n => Err(format!("math:plus => expected up to 2 arguments, got {}", n)),
}
}
fn math_plus_finalize(mut inputs: Vec<Value>) -> Result<Value, String> {
if inputs.len() != 2 {
return Err(format!("math:plus finalize => expected 2 arguments, got {}", inputs.len()));
}
let left = inputs.remove(0);
let right = inputs.remove(0);
match (left, right) {
(Value::Int(a), Value::Int(b)) => {
a.checked_add(b).map(Value::Int).ok_or_else(|| "Overflow in plus".to_string())
}
(Value::Int(a), Value::IntArray(xs)) => {
let mut out = Vec::with_capacity(xs.len());
for x in xs {
let sum = a.checked_add(x).ok_or_else(|| "Overflow in plus array".to_string())?;
out.push(sum);
}
Ok(Value::IntArray(out))
}
(Value::IntArray(xs), Value::Int(b)) => {
let mut out = Vec::with_capacity(xs.len());
for x in xs {
let sum = x.checked_add(b).ok_or_else(|| "Overflow in plus array".to_string())?;
out.push(sum);
}
Ok(Value::IntArray(out))
}
other => Err(format!("math:plus => unsupported combination: {:?}", other)),
}
}
pub fn math_subtract_bridge(_interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, String> {
match args.len() {
0 => Ok(make_two_arg_partial(math_subtract_finalize, None, None)),
1 => {
let a = &args[0];
if is_placeholder(a) {
Ok(make_two_arg_partial(math_subtract_finalize, None, None))
} else {
Ok(make_two_arg_partial(math_subtract_finalize, Some(a.clone()), None))
}
}
2 => {
let left_pl = is_placeholder(&args[0]);
let right_pl = is_placeholder(&args[1]);
if !left_pl && !right_pl {
math_subtract_finalize(vec![args[0].clone(), args[1].clone()])
} else {
Ok(make_two_arg_partial(
math_subtract_finalize,
if left_pl { None } else { Some(args[0].clone()) },
if right_pl { None } else { Some(args[1].clone()) },
))
}
}
n => Err(format!("math:subtract => expected up to 2 arguments, got {}", n)),
}
}
fn math_subtract_finalize(mut inputs: Vec<Value>) -> Result<Value, String> {
if inputs.len() != 2 {
return Err(format!("math:subtract finalize => expected 2 arguments, got {}", inputs.len()));
}
let left = inputs.remove(0);
let right = inputs.remove(0);
match (left, right) {
(Value::Int(a), Value::Int(b)) => {
a.checked_sub(b).map(Value::Int).ok_or_else(|| "Overflow in subtract".to_string())
}
(Value::IntArray(xs), Value::Int(i)) => {
let mut out = Vec::with_capacity(xs.len());
for x in xs {
let diff = x.checked_sub(i).ok_or_else(|| "Overflow in array subtract".to_string())?;
out.push(diff);
}
Ok(Value::IntArray(out))
}
(Value::Int(a), Value::IntArray(xs)) => {
let mut out = Vec::with_capacity(xs.len());
for x in xs {
let diff = a.checked_sub(x).ok_or_else(|| "Overflow in array subtract".to_string())?;
out.push(diff);
}
Ok(Value::IntArray(out))
}
other => Err(format!("math:subtract => unsupported combination: {:?}", other)),
}
}
pub fn math_multiply_bridge(_interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, String> {
match args.len() {
0 => Ok(make_two_arg_partial(math_multiply_finalize, None, None)),
1 => {
let a = &args[0];
if is_placeholder(a) {
Ok(make_two_arg_partial(math_multiply_finalize, None, None))
} else {
Ok(make_two_arg_partial(math_multiply_finalize, Some(a.clone()), None))
}
}
2 => {
let left_pl = is_placeholder(&args[0]);
let right_pl = is_placeholder(&args[1]);
if !left_pl && !right_pl {
math_multiply_finalize(vec![args[0].clone(), args[1].clone()])
} else {
Ok(make_two_arg_partial(
math_multiply_finalize,
if left_pl { None } else { Some(args[0].clone()) },
if right_pl { None } else { Some(args[1].clone()) },
))
}
}
n => Err(format!("math:multiply => expected up to 2 arguments, got {}", n)),
}
}
fn math_multiply_finalize(mut inputs: Vec<Value>) -> Result<Value, String> {
if inputs.len() != 2 {
return Err(format!("math:multiply finalize => expected 2 arguments, got {}", inputs.len()));
}
let left = inputs.remove(0);
let right = inputs.remove(0);
match (left, right) {
(Value::Int(a), Value::Int(b)) => {
a.checked_mul(b).map(Value::Int).ok_or_else(|| "Overflow in multiply".to_string())
}
(Value::IntArray(xs), Value::Int(i)) => {
let mut out = Vec::with_capacity(xs.len());
for x in xs {
let prod = x.checked_mul(i).ok_or_else(|| "Overflow in multiply array".to_string())?;
out.push(prod);
}
Ok(Value::IntArray(out))
}
(Value::Int(a), Value::IntArray(xs)) => {
let mut out = Vec::with_capacity(xs.len());
for x in xs {
let prod = a.checked_mul(x).ok_or_else(|| "Overflow in multiply array".to_string())?;
out.push(prod);
}
Ok(Value::IntArray(out))
}
other => Err(format!("math:multiply => unsupported combination: {:?}", other)),
}
}
pub fn math_divide_bridge(_interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, String> {
match args.len() {
0 => Ok(make_two_arg_partial(math_divide_finalize, None, None)),
1 => {
let a = &args[0];
if is_placeholder(a) {
Ok(make_two_arg_partial(math_divide_finalize, None, None))
} else {
Ok(make_two_arg_partial(math_divide_finalize, Some(a.clone()), None))
}
}
2 => {
let left_pl = is_placeholder(&args[0]);
let right_pl = is_placeholder(&args[1]);
if !left_pl && !right_pl {
math_divide_finalize(vec![args[0].clone(), args[1].clone()])
} else {
Ok(make_two_arg_partial(
math_divide_finalize,
if left_pl { None } else { Some(args[0].clone()) },
if right_pl { None } else { Some(args[1].clone()) },
))
}
}
n => Err(format!("math:divide => expected up to 2 arguments, got {}", n)),
}
}
fn math_divide_finalize(mut inputs: Vec<Value>) -> Result<Value, String> {
if inputs.len() != 2 {
return Err(format!("math:divide finalize => expected 2 arguments, got {}", inputs.len()));
}
let left = coerce_to_float_or_float_array(inputs.remove(0))?;
let right = coerce_to_float_or_float_array(inputs.remove(0))?;
apply_float_division(left, right)
}
fn apply_float_division(left: Value, right: Value) -> Result<Value, String> {
match (left, right) {
(Value::Float(a), Value::Float(b)) => {
if b == 0.0 {
Err("math:divide => divide by zero".to_string())
} else {
Ok(Value::Float(a / b))
}
}
(Value::FloatArray(xs), Value::Float(b)) => {
if b == 0.0 {
return Err("math:divide => array / 0.0 => error".to_string());
}
let mut out = Vec::with_capacity(xs.len());
for x in xs {
out.push(x / b);
}
Ok(Value::FloatArray(out))
}
(Value::Float(a), Value::FloatArray(ys)) => {
let mut out = Vec::with_capacity(ys.len());
for y in ys {
if y == 0.0 {
if a == 0.0 {
out.push(f64::NAN);
} else {
out.push(f64::INFINITY.copysign(a));
}
} else {
out.push(a / y);
}
}
Ok(Value::FloatArray(out))
}
(Value::FloatArray(xs), Value::FloatArray(ys)) => {
if xs.len() != ys.len() {
return Err("math:divide => float arrays with different lengths".to_string());
}
let mut out = Vec::with_capacity(xs.len());
for i in 0..xs.len() {
let y = ys[i];
if y == 0.0 {
if xs[i] == 0.0 {
out.push(f64::NAN);
} else {
out.push(f64::INFINITY.copysign(xs[i]));
}
} else {
out.push(xs[i] / y);
}
}
Ok(Value::FloatArray(out))
}
other => Err(format!("apply_float_division => unexpected: {:?}", other)),
}
}
pub fn make_two_arg_partial(
finalize_fn: fn(Vec<Value>) -> Result<Value, String>,
a_opt: Option<Value>,
b_opt: Option<Value>
) -> Value {
use FunctionValue::RustClosure;
let closure = move |_interp: &mut Interpreter, new_args: Vec<Value>| {
let mut aa = a_opt.clone();
let mut bb = b_opt.clone();
for arg in new_args {
if aa.is_none() {
if is_placeholder(&arg) {
} else {
aa = Some(arg);
}
continue;
}
if bb.is_none() {
if is_placeholder(&arg) {
} else {
bb = Some(arg);
}
continue;
}
return Err("Too many arguments for partial function".to_string());
}
if aa.is_some() && bb.is_some() {
let final_args = vec![aa.as_ref().unwrap().clone(), bb.as_ref().unwrap().clone()];
finalize_fn(final_args)
} else {
Ok(make_two_arg_partial(finalize_fn, aa, bb))
}
};
Value::Function(Box::new(RustClosure(
"math-2arg-partial".to_string(),
Arc::new(Mutex::new(closure)),
0,
)))
}
pub fn make_three_arg_partial(
finalize_fn: fn(Vec<Value>) -> Result<Value, String>,
a_opt: Option<Value>,
b_opt: Option<Value>,
c_opt: Option<Value>,
) -> Value {
use FunctionValue::RustClosure;
let closure = move |_interp: &mut Interpreter, new_args: Vec<Value>| {
let mut a = a_opt.clone();
let mut b = b_opt.clone();
let mut c = c_opt.clone();
for arg in new_args {
if a.is_none() {
if is_placeholder(&arg) { } else { a = Some(arg); }
continue;
}
if b.is_none() {
if is_placeholder(&arg) { } else { b = Some(arg); }
continue;
}
if c.is_none() {
if is_placeholder(&arg) { } else { c = Some(arg); }
continue;
}
return Err("Too many arguments for 3-arg partial function".to_string());
}
if a.is_some() && b.is_some() && c.is_some() {
let final_args = vec![
a.as_ref().unwrap().clone(),
b.as_ref().unwrap().clone(),
c.as_ref().unwrap().clone(),
];
finalize_fn(final_args)
} else {
Ok(make_three_arg_partial(finalize_fn, a, b, c))
}
};
Value::Function(Box::new(RustClosure(
"math-3arg-partial".to_string(),
Arc::new(Mutex::new(closure)),
0,
)))
}
pub fn math_atan2_bridge(_i: &mut Interpreter, args: Vec<Value>) -> Result<Value, String> {
match args.len() {
0 => Ok(make_two_arg_partial(atan2_finalize, None, None)),
1 => {
if is_placeholder(&args[0]) {
Ok(make_two_arg_partial(atan2_finalize, None, None))
} else {
Ok(make_two_arg_partial(atan2_finalize, Some(args[0].clone()), None))
}
}
2 => {
let lp = is_placeholder(&args[0]);
let rp = is_placeholder(&args[1]);
if !lp && !rp {
atan2_finalize(vec![args[0].clone(), args[1].clone()])
} else {
Ok(make_two_arg_partial(
atan2_finalize,
if lp { None } else { Some(args[0].clone()) },
if rp { None } else { Some(args[1].clone()) },
))
}
}
n => Err(format!("math:atan2 => up to 2 arguments, got {}", n)),
}
}
fn atan2_finalize(mut args: Vec<Value>) -> Result<Value, String> {
if args.len() != 2 {
return Err(format!("atan2_finalize => expected 2 args, got {}", args.len()));
}
let y = coerce_to_f64(args.remove(0), "math:atan2")?;
let x = coerce_to_f64(args.remove(0), "math:atan2")?;
Ok(Value::Float(y.atan2(x)))
}
pub fn math_imul_bridge(_i: &mut Interpreter, args: Vec<Value>) -> Result<Value, String> {
match args.len() {
0 => Ok(make_two_arg_partial(imul_finalize, None, None)),
1 => {
if is_placeholder(&args[0]) {
Ok(make_two_arg_partial(imul_finalize, None, None))
} else {
Ok(make_two_arg_partial(imul_finalize, Some(args[0].clone()), None))
}
}
2 => {
let lp = is_placeholder(&args[0]);
let rp = is_placeholder(&args[1]);
if !lp && !rp {
imul_finalize(vec![args[0].clone(), args[1].clone()])
} else {
Ok(make_two_arg_partial(
imul_finalize,
if lp { None } else { Some(args[0].clone()) },
if rp { None } else { Some(args[1].clone()) },
))
}
}
n => Err(format!("math:imul => up to 2 arguments, got {}", n)),
}
}
fn imul_finalize(mut args: Vec<Value>) -> Result<Value, String> {
if args.len() != 2 {
return Err(format!("imul_finalize => expected 2 args, got {}", args.len()));
}
let a = coerce_to_f64(args.remove(0), "math:imul")? as i32;
let b = coerce_to_f64(args.remove(0), "math:imul")? as i32;
Ok(Value::Int(a.wrapping_mul(b)))
}
#[derive(Clone)]
struct MultiPartialEnv {
collected: Vec<Value>,
finalize: fn(Vec<Value>) -> Result<Value, String>,
}
fn make_multi_arg_partial(
finalize_fn: fn(Vec<Value>) -> Result<Value, String>,
existing_args: Vec<Value>
) -> Value {
use FunctionValue::RustClosure;
let closure_env = Arc::new(Mutex::new(MultiPartialEnv {
collected: existing_args,
finalize: finalize_fn,
}));
Value::Function(Box::new(RustClosure(
"math-multiarg-partial".to_string(),
Arc::new(Mutex::new(move |_i: &mut Interpreter, new_args: Vec<Value>| {
let mut st = closure_env.lock().map_err(|_| "lock error".to_string())?;
let mut combined = st.collected.clone();
combined.extend(new_args.into_iter());
if combined.iter().any(is_placeholder) {
st.collected = combined;
let out = make_multi_arg_partial(st.finalize, st.collected.clone());
Ok(out)
} else {
let finalize_fn_ptr = st.finalize;
drop(st);
finalize_fn_ptr(combined)
}
})),
0,
)))
}
pub fn math_max_bridge(_i: &mut Interpreter, args: Vec<Value>) -> Result<Value, String> {
if args.is_empty() || args.iter().any(is_placeholder) {
Ok(make_multi_arg_partial(max_finalize, args))
} else {
max_finalize(args)
}
}
fn max_finalize(args: Vec<Value>) -> Result<Value, String> {
if args.is_empty() {
return Err("math:max => no arguments provided".to_string());
}
let vals = coerce_all_to_f64(&args, "math:max")?;
let mut m = std::f64::NEG_INFINITY;
for x in vals {
if x.is_nan() {
return Ok(Value::Float(std::f64::NAN));
}
if x > m {
m = x;
}
}
if m.is_finite() && m == m.trunc() && m >= (i32::MIN as f64) && m <= (i32::MAX as f64) {
Ok(Value::Int(m as i32))
} else {
Ok(Value::Float(m))
}
}
pub fn math_min_bridge(_i: &mut Interpreter, args: Vec<Value>) -> Result<Value, String> {
if args.is_empty() || args.iter().any(is_placeholder) {
Ok(make_multi_arg_partial(min_finalize, args))
} else {
min_finalize(args)
}
}
fn min_finalize(args: Vec<Value>) -> Result<Value, String> {
if args.is_empty() {
return Err("math:min => no arguments provided".to_string());
}
let vals = coerce_all_to_f64(&args, "math:min")?;
let mut m = std::f64::INFINITY;
for x in vals {
if x.is_nan() {
return Ok(Value::Float(std::f64::NAN));
}
if x < m {
m = x;
}
}
if m.is_finite() && m == m.trunc() && m >= (i32::MIN as f64) && m <= (i32::MAX as f64) {
Ok(Value::Int(m as i32))
} else {
Ok(Value::Float(m))
}
}
pub fn math_hypot_bridge(_i: &mut Interpreter, args: Vec<Value>) -> Result<Value, String> {
if args.is_empty() || args.iter().any(is_placeholder) {
Ok(make_multi_arg_partial(hypot_finalize, args))
} else {
hypot_finalize(args)
}
}
fn hypot_finalize(args: Vec<Value>) -> Result<Value, String> {
if args.is_empty() {
return Err("math:hypot => no arguments provided".to_string());
}
let vals = coerce_all_to_f64(&args, "math:hypot")?;
let mut sum_squares = 0.0;
for x in vals {
sum_squares += x * x;
if sum_squares.is_infinite() {
return Ok(Value::Float(std::f64::INFINITY));
}
if sum_squares.is_nan() {
return Ok(Value::Float(std::f64::NAN));
}
}
Ok(Value::Float(sum_squares.sqrt()))
}
fn coerce_all_to_f64(args: &[Value], func_name: &str) -> Result<Vec<f64>, String> {
if args.is_empty() {
return Err(format!("{} => at least 1 argument required", func_name));
}
let mut out = Vec::new();
for v in args {
match v {
Value::Int(i) => out.push(*i as f64),
Value::Long(l) => out.push(*l as f64),
Value::Float(ff) => out.push(*ff),
other => {
return Err(format!("{} => all arguments must be numeric, got {:?}", func_name, other));
}
}
}
Ok(out)
}
pub fn math_pow_bridge(_i: &mut Interpreter, args: Vec<Value>) -> Result<Value, String> {
match args.len() {
0 => Ok(make_two_arg_partial(pow_finalize, None, None)),
1 => {
let a = &args[0];
if is_placeholder(a) {
Ok(make_two_arg_partial(pow_finalize, None, None))
} else {
Ok(make_two_arg_partial(pow_finalize, Some(a.clone()), None))
}
}
2 => {
let lp = is_placeholder(&args[0]);
let rp = is_placeholder(&args[1]);
if !lp && !rp {
pow_finalize(vec![args[0].clone(), args[1].clone()])
} else {
Ok(make_two_arg_partial(
pow_finalize,
if lp { None } else { Some(args[0].clone()) },
if rp { None } else { Some(args[1].clone()) },
))
}
}
n => Err(format!("math:pow => up to 2 arguments, got {}", n)),
}
}
fn pow_finalize(mut args: Vec<Value>) -> Result<Value, String> {
if args.len() != 2 {
return Err(format!("pow_finalize => expected 2 args, got {}", args.len()));
}
let base = coerce_to_f64(args.remove(0), "math:pow")?;
let exp = coerce_to_f64(args.remove(0), "math:pow")?;
Ok(Value::Float(base.powf(exp)))
}
pub fn math_mod_bridge(_i: &mut Interpreter, args: Vec<Value>) -> Result<Value, String> {
match args.len() {
0 => Ok(make_two_arg_partial(mod_finalize, None, None)),
1 => {
let a = &args[0];
if is_placeholder(a) {
Ok(make_two_arg_partial(mod_finalize, None, None))
} else {
Ok(make_two_arg_partial(mod_finalize, Some(a.clone()), None))
}
}
2 => {
let lp = is_placeholder(&args[0]);
let rp = is_placeholder(&args[1]);
if !lp && !rp {
mod_finalize(vec![args[0].clone(), args[1].clone()])
} else {
Ok(make_two_arg_partial(
mod_finalize,
if lp { None } else { Some(args[0].clone()) },
if rp { None } else { Some(args[1].clone()) },
))
}
}
n => Err(format!("math:mod => up to 2 arguments, got {}", n)),
}
}
fn mod_finalize(args: Vec<Value>) -> Result<Value, String> {
if args.len() != 2 {
return Err(format!("mod_finalize => expected 2 args, got {}", args.len()));
}
let a = &args[0];
let b = &args[1];
match (a, b) {
(Value::Int(a), Value::Int(b)) => {
if *b == 0 {
Err("math:mod => divide by zero".to_string())
} else {
Ok(Value::Int(a % b))
}
}
_ => Err(format!("math:mod => only supports Int, got {:?} and {:?}", a, b)),
}
}