math-mumu 0.2.0-rc.4

Math functions plugin for the MuMu / Lava language
Documentation
use mumu::parser::interpreter::Interpreter;
use mumu::parser::types::Value;

/// A small helper to unify single arguments into f64 for single-arg bridging.
fn coerce_single_to_f64(arg: Value, func_name: &str) -> Result<f64, String> {
    match arg {
        Value::Int(i) => Ok(i as f64),
        Value::Long(l) => Ok(l as f64),
        Value::Float(ff) => Ok(ff),
        other => Err(format!("{} => argument must be numeric, got {:?}", func_name, other)),
    }
}

/// math:abs(x)
pub fn math_abs_bridge(_i: &mut Interpreter, args: Vec<Value>) -> Result<Value, String> {
    if args.len() != 1 {
        return Err(format!("math:abs => expected 1 argument, got {}", args.len()));
    }
    let x = coerce_single_to_f64(args[0].clone(), "math:abs")?;
    Ok(Value::Float(x.abs()))
}

/// math:acos(x)
pub fn math_acos_bridge(_i: &mut Interpreter, args: Vec<Value>) -> Result<Value, String> {
    if args.len() != 1 {
        return Err(format!("math:acos => expected 1 argument, got {}", args.len()));
    }
    let x = coerce_single_to_f64(args[0].clone(), "math:acos")?;
    Ok(Value::Float(x.acos()))
}

/// math:acosh(x)
pub fn math_acosh_bridge(_i: &mut Interpreter, args: Vec<Value>) -> Result<Value, String> {
    if args.len() != 1 {
        return Err(format!("math:acosh => expected 1 argument, got {}", args.len()));
    }
    let x = coerce_single_to_f64(args[0].clone(), "math:acosh")?;
    Ok(Value::Float(x.acosh()))
}

/// math:asin(x)
pub fn math_asin_bridge(_i: &mut Interpreter, args: Vec<Value>) -> Result<Value, String> {
    if args.len() != 1 {
        return Err(format!("math:asin => expected 1 argument, got {}", args.len()));
    }
    let x = coerce_single_to_f64(args[0].clone(), "math:asin")?;
    Ok(Value::Float(x.asin()))
}

/// math:asinh(x)
pub fn math_asinh_bridge(_i: &mut Interpreter, args: Vec<Value>) -> Result<Value, String> {
    if args.len() != 1 {
        return Err(format!("math:asinh => expected 1 argument, got {}", args.len()));
    }
    let x = coerce_single_to_f64(args[0].clone(), "math:asinh")?;
    Ok(Value::Float(x.asinh()))
}

/// math:atan(x)
pub fn math_atan_bridge(_i: &mut Interpreter, args: Vec<Value>) -> Result<Value, String> {
    if args.len() != 1 {
        return Err(format!("math:atan => expected 1 argument, got {}", args.len()));
    }
    let x = coerce_single_to_f64(args[0].clone(), "math:atan")?;
    Ok(Value::Float(x.atan()))
}

/// math:atanh(x)
pub fn math_atanh_bridge(_i: &mut Interpreter, args: Vec<Value>) -> Result<Value, String> {
    if args.len() != 1 {
        return Err(format!("math:atanh => expected 1 argument, got {}", args.len()));
    }
    let x = coerce_single_to_f64(args[0].clone(), "math:atanh")?;
    Ok(Value::Float(x.atanh()))
}

/// math:cbrt(x)
pub fn math_cbrt_bridge(_i: &mut Interpreter, args: Vec<Value>) -> Result<Value, String> {
    if args.len() != 1 {
        return Err(format!("math:cbrt => expected 1 argument, got {}", args.len()));
    }
    let x = coerce_single_to_f64(args[0].clone(), "math:cbrt")?;
    Ok(Value::Float(x.cbrt()))
}

/// math:ceil(x)
pub fn math_ceil_bridge(_i: &mut Interpreter, args: Vec<Value>) -> Result<Value, String> {
    if args.len() != 1 {
        return Err(format!("math:ceil => expected 1 argument, got {}", args.len()));
    }
    let x = coerce_single_to_f64(args[0].clone(), "math:ceil")?;
    let c = x.ceil();
    if c.is_finite() && c == c.trunc() && c >= (i32::MIN as f64) && c <= (i32::MAX as f64) {
        Ok(Value::Int(c as i32))
    } else {
        Ok(Value::Float(c))
    }
}

/// math:clz32(x)
pub fn math_clz32_bridge(_i: &mut Interpreter, args: Vec<Value>) -> Result<Value, String> {
    if args.len() != 1 {
        return Err(format!("math:clz32 => expected 1 argument, got {}", args.len()));
    }
    let x = coerce_single_to_f64(args[0].clone(), "math:clz32")?;
    let as_u32 = x as u32;
    let result = as_u32.leading_zeros() as i32;
    Ok(Value::Int(result))
}

/// math:cos(x)
pub fn math_cos_bridge(_i: &mut Interpreter, args: Vec<Value>) -> Result<Value, String> {
    if args.len() != 1 {
        return Err(format!("math:cos => expected 1 argument, got {}", args.len()));
    }
    let x = coerce_single_to_f64(args[0].clone(), "math:cos")?;
    Ok(Value::Float(x.cos()))
}

/// math:cosh(x)
pub fn math_cosh_bridge(_i: &mut Interpreter, args: Vec<Value>) -> Result<Value, String> {
    if args.len() != 1 {
        return Err(format!("math:cosh => expected 1 argument, got {}", args.len()));
    }
    let x = coerce_single_to_f64(args[0].clone(), "math:cosh")?;
    Ok(Value::Float(x.cosh()))
}

/// math:exp(x)
pub fn math_exp_bridge(_i: &mut Interpreter, args: Vec<Value>) -> Result<Value, String> {
    if args.len() != 1 {
        return Err(format!("math:exp => expected 1 argument, got {}", args.len()));
    }
    let x = coerce_single_to_f64(args[0].clone(), "math:exp")?;
    Ok(Value::Float(x.exp()))
}

/// math:expm1(x)
pub fn math_expm1_bridge(_i: &mut Interpreter, args: Vec<Value>) -> Result<Value, String> {
    if args.len() != 1 {
        return Err(format!("math:expm1 => expected 1 argument, got {}", args.len()));
    }
    let x = coerce_single_to_f64(args[0].clone(), "math:expm1")?;
    Ok(Value::Float(x.exp_m1()))
}

/// math:floor(x)
pub fn math_floor_bridge(_i: &mut Interpreter, args: Vec<Value>) -> Result<Value, String> {
    if args.len() != 1 {
        return Err(format!("math:floor => expected 1 argument, got {}", args.len()));
    }
    let x = coerce_single_to_f64(args[0].clone(), "math:floor")?;
    let c = x.floor();
    if c.is_finite() && c == c.trunc() && c >= (i32::MIN as f64) && c <= (i32::MAX as f64) {
        Ok(Value::Int(c as i32))
    } else {
        Ok(Value::Float(c))
    }
}

/// math:fround(x)
pub fn math_fround_bridge(_i: &mut Interpreter, args: Vec<Value>) -> Result<Value, String> {
    if args.len() != 1 {
        return Err(format!("math:fround => expected 1 argument, got {}", args.len()));
    }
    let x = coerce_single_to_f64(args[0].clone(), "math:fround")?;
    let as_f32 = x as f32;
    Ok(Value::Float(as_f32 as f64))
}

/// math:log(x)
pub fn math_log_bridge(_i: &mut Interpreter, args: Vec<Value>) -> Result<Value, String> {
    if args.len() != 1 {
        return Err(format!("math:log => expected 1 argument, got {}", args.len()));
    }
    let x = coerce_single_to_f64(args[0].clone(), "math:log")?;
    Ok(Value::Float(x.ln()))
}

/// math:log10(x)
pub fn math_log10_bridge(_i: &mut Interpreter, args: Vec<Value>) -> Result<Value, String> {
    if args.len() != 1 {
        return Err(format!("math:log10 => expected 1 argument, got {}", args.len()));
    }
    let x = coerce_single_to_f64(args[0].clone(), "math:log10")?;
    Ok(Value::Float(x.log10()))
}

/// math:log1p(x)
pub fn math_log1p_bridge(_i: &mut Interpreter, args: Vec<Value>) -> Result<Value, String> {
    if args.len() != 1 {
        return Err(format!("math:log1p => expected 1 argument, got {}", args.len()));
    }
    let x = coerce_single_to_f64(args[0].clone(), "math:log1p")?;
    Ok(Value::Float(x.ln_1p()))
}

/// math:log2(x)
pub fn math_log2_bridge(_i: &mut Interpreter, args: Vec<Value>) -> Result<Value, String> {
    if args.len() != 1 {
        return Err(format!("math:log2 => expected 1 argument, got {}", args.len()));
    }
    let x = coerce_single_to_f64(args[0].clone(), "math:log2")?;
    Ok(Value::Float(x.log2()))
}

/// math:round(x)
pub fn math_round_bridge(_i: &mut Interpreter, args: Vec<Value>) -> Result<Value, String> {
    if args.len() != 1 {
        return Err(format!("math:round => expected 1 argument, got {}", args.len()));
    }
    let x = coerce_single_to_f64(args[0].clone(), "math:round")?;
    let r = x.round();
    if r.is_finite() && r == r.trunc() && r >= (i32::MIN as f64) && r <= (i32::MAX as f64) {
        Ok(Value::Int(r as i32))
    } else {
        Ok(Value::Float(r))
    }
}

/// math:sign(x)
pub fn math_sign_bridge(_i: &mut Interpreter, args: Vec<Value>) -> Result<Value, String> {
    if args.len() != 1 {
        return Err(format!("math:sign => expected 1 argument, got {}", args.len()));
    }
    let x = coerce_single_to_f64(args[0].clone(), "math:sign")?;
    let s = if x > 0.0 {
        1
    } else if x < 0.0 {
        -1
    } else {
        0
    };
    Ok(Value::Int(s))
}

/// math:sin(x)
pub fn math_sin_bridge(_i: &mut Interpreter, args: Vec<Value>) -> Result<Value, String> {
    if args.len() != 1 {
        return Err(format!("math:sin => expected 1 argument, got {}", args.len()));
    }
    let x = coerce_single_to_f64(args[0].clone(), "math:sin")?;
    Ok(Value::Float(x.sin()))
}

/// math:sinh(x)
pub fn math_sinh_bridge(_i: &mut Interpreter, args: Vec<Value>) -> Result<Value, String> {
    if args.len() != 1 {
        return Err(format!("math:sinh => expected 1 argument, got {}", args.len()));
    }
    let x = coerce_single_to_f64(args[0].clone(), "math:sinh")?;
    Ok(Value::Float(x.sinh()))
}

/// math:tan(x)
pub fn math_tan_bridge(_i: &mut Interpreter, args: Vec<Value>) -> Result<Value, String> {
    if args.len() != 1 {
        return Err(format!("math:tan => expected 1 argument, got {}", args.len()));
    }
    let x = coerce_single_to_f64(args[0].clone(), "math:tan")?;
    Ok(Value::Float(x.tan()))
}

/// math:tanh(x)
pub fn math_tanh_bridge(_i: &mut Interpreter, args: Vec<Value>) -> Result<Value, String> {
    if args.len() != 1 {
        return Err(format!("math:tanh => expected 1 argument, got {}", args.len()));
    }
    let x = coerce_single_to_f64(args[0].clone(), "math:tanh")?;
    Ok(Value::Float(x.tanh()))
}

/// math:trunc(x)
pub fn math_trunc_bridge(_i: &mut Interpreter, args: Vec<Value>) -> Result<Value, String> {
    if args.len() != 1 {
        return Err(format!("math:trunc => expected 1 argument, got {}", args.len()));
    }
    let x = coerce_single_to_f64(args[0].clone(), "math:trunc")?;
    let t = x.trunc();
    if t.is_finite() && t == t.trunc() && t >= (i32::MIN as f64) && t <= (i32::MAX as f64) {
        Ok(Value::Int(t as i32))
    } else {
        Ok(Value::Float(t))
    }
}