use std::collections::HashMap;
use std::sync::Arc as Rc;
use crate::nan_value::{Arena, NanValue};
use crate::value::{RuntimeError, Value};
pub fn register(global: &mut HashMap<String, Value>) {
let mut members = HashMap::new();
for method in &[
"fromString",
"fromInt",
"toString",
"abs",
"floor",
"ceil",
"round",
"min",
"max",
"sin",
"cos",
"sqrt",
"pow",
"atan2",
"pi",
] {
members.insert(
method.to_string(),
Value::Builtin(format!("Float.{}", method)),
);
}
global.insert(
"Float".to_string(),
Value::Namespace {
name: "Float".to_string(),
members,
},
);
}
pub fn effects(_name: &str) -> &'static [&'static str] {
&[]
}
pub fn call(name: &str, args: &[Value]) -> Option<Result<Value, RuntimeError>> {
match name {
"Float.fromString" => Some(from_string(args)),
"Float.fromInt" => Some(from_int(args)),
"Float.toString" => Some(to_string(args)),
"Float.abs" => Some(abs(args)),
"Float.floor" => Some(floor(args)),
"Float.ceil" => Some(ceil(args)),
"Float.round" => Some(round(args)),
"Float.min" => Some(min(args)),
"Float.max" => Some(max(args)),
"Float.sin" => Some(sin(args)),
"Float.cos" => Some(cos(args)),
"Float.sqrt" => Some(sqrt(args)),
"Float.pow" => Some(pow(args)),
"Float.atan2" => Some(atan2(args)),
"Float.pi" => Some(pi(args)),
_ => None,
}
}
fn from_string(args: &[Value]) -> Result<Value, RuntimeError> {
let [val] = one_arg("Float.fromString", args)?;
let Value::Str(s) = val else {
return Err(RuntimeError::Error(
"Float.fromString: argument must be a String".to_string(),
));
};
match s.parse::<f64>() {
Ok(f) => Ok(Value::Ok(Box::new(Value::Float(f)))),
Err(_) => Ok(Value::Err(Box::new(Value::Str(format!(
"Cannot parse '{}' as Float",
s
))))),
}
}
fn from_int(args: &[Value]) -> Result<Value, RuntimeError> {
let [val] = one_arg("Float.fromInt", args)?;
let Value::Int(n) = val else {
return Err(RuntimeError::Error(
"Float.fromInt: argument must be an Int".to_string(),
));
};
Ok(Value::Float(*n as f64))
}
fn to_string(args: &[Value]) -> Result<Value, RuntimeError> {
let [val] = one_arg("Float.toString", args)?;
let Value::Float(f) = val else {
return Err(RuntimeError::Error(
"Float.toString: argument must be a Float".to_string(),
));
};
Ok(Value::Str(format!("{}", f)))
}
fn abs(args: &[Value]) -> Result<Value, RuntimeError> {
let [val] = one_arg("Float.abs", args)?;
let Value::Float(f) = val else {
return Err(RuntimeError::Error(
"Float.abs: argument must be a Float".to_string(),
));
};
Ok(Value::Float(f.abs()))
}
fn floor(args: &[Value]) -> Result<Value, RuntimeError> {
let [val] = one_arg("Float.floor", args)?;
let Value::Float(f) = val else {
return Err(RuntimeError::Error(
"Float.floor: argument must be a Float".to_string(),
));
};
Ok(Value::Int(f.floor() as i64))
}
fn ceil(args: &[Value]) -> Result<Value, RuntimeError> {
let [val] = one_arg("Float.ceil", args)?;
let Value::Float(f) = val else {
return Err(RuntimeError::Error(
"Float.ceil: argument must be a Float".to_string(),
));
};
Ok(Value::Int(f.ceil() as i64))
}
fn round(args: &[Value]) -> Result<Value, RuntimeError> {
let [val] = one_arg("Float.round", args)?;
let Value::Float(f) = val else {
return Err(RuntimeError::Error(
"Float.round: argument must be a Float".to_string(),
));
};
Ok(Value::Int(f.round() as i64))
}
fn min(args: &[Value]) -> Result<Value, RuntimeError> {
let [a, b] = two_args("Float.min", args)?;
let (Value::Float(x), Value::Float(y)) = (a, b) else {
return Err(RuntimeError::Error(
"Float.min: both arguments must be Float".to_string(),
));
};
Ok(Value::Float(f64::min(*x, *y)))
}
fn max(args: &[Value]) -> Result<Value, RuntimeError> {
let [a, b] = two_args("Float.max", args)?;
let (Value::Float(x), Value::Float(y)) = (a, b) else {
return Err(RuntimeError::Error(
"Float.max: both arguments must be Float".to_string(),
));
};
Ok(Value::Float(f64::max(*x, *y)))
}
fn sin(args: &[Value]) -> Result<Value, RuntimeError> {
let [val] = one_arg("Float.sin", args)?;
let Value::Float(f) = val else {
return Err(RuntimeError::Error(
"Float.sin: argument must be a Float".to_string(),
));
};
Ok(Value::Float(f.sin()))
}
fn cos(args: &[Value]) -> Result<Value, RuntimeError> {
let [val] = one_arg("Float.cos", args)?;
let Value::Float(f) = val else {
return Err(RuntimeError::Error(
"Float.cos: argument must be a Float".to_string(),
));
};
Ok(Value::Float(f.cos()))
}
fn sqrt(args: &[Value]) -> Result<Value, RuntimeError> {
let [val] = one_arg("Float.sqrt", args)?;
let Value::Float(f) = val else {
return Err(RuntimeError::Error(
"Float.sqrt: argument must be a Float".to_string(),
));
};
Ok(Value::Float(f.sqrt()))
}
fn pow(args: &[Value]) -> Result<Value, RuntimeError> {
let [a, b] = two_args("Float.pow", args)?;
let (Value::Float(base), Value::Float(exp)) = (a, b) else {
return Err(RuntimeError::Error(
"Float.pow: both arguments must be Float".to_string(),
));
};
Ok(Value::Float(base.powf(*exp)))
}
fn atan2(args: &[Value]) -> Result<Value, RuntimeError> {
let [a, b] = two_args("Float.atan2", args)?;
let (Value::Float(y), Value::Float(x)) = (a, b) else {
return Err(RuntimeError::Error(
"Float.atan2: both arguments must be Float".to_string(),
));
};
Ok(Value::Float(y.atan2(*x)))
}
fn pi(args: &[Value]) -> Result<Value, RuntimeError> {
if !args.is_empty() {
return Err(RuntimeError::Error(format!(
"Float.pi() takes 0 arguments, got {}",
args.len()
)));
}
Ok(Value::Float(std::f64::consts::PI))
}
fn one_arg<'a>(name: &str, args: &'a [Value]) -> Result<[&'a Value; 1], RuntimeError> {
if args.len() != 1 {
return Err(RuntimeError::Error(format!(
"{}() takes 1 argument, got {}",
name,
args.len()
)));
}
Ok([&args[0]])
}
fn two_args<'a>(name: &str, args: &'a [Value]) -> Result<[&'a Value; 2], RuntimeError> {
if args.len() != 2 {
return Err(RuntimeError::Error(format!(
"{}() takes 2 arguments, got {}",
name,
args.len()
)));
}
Ok([&args[0], &args[1]])
}
pub fn register_nv(global: &mut HashMap<String, NanValue>, arena: &mut Arena) {
let methods = &[
"fromString",
"fromInt",
"toString",
"abs",
"floor",
"ceil",
"round",
"min",
"max",
"sin",
"cos",
"sqrt",
"pow",
"atan2",
"pi",
];
let mut members: Vec<(Rc<str>, NanValue)> = Vec::with_capacity(methods.len());
for method in methods {
let idx = arena.push_builtin(&format!("Float.{}", method));
members.push((Rc::from(*method), NanValue::new_builtin(idx)));
}
let ns_idx = arena.push(crate::nan_value::ArenaEntry::Namespace {
name: Rc::from("Float"),
members,
});
global.insert("Float".to_string(), NanValue::new_namespace(ns_idx));
}
pub fn call_nv(
name: &str,
args: &[NanValue],
arena: &mut Arena,
) -> Option<Result<NanValue, RuntimeError>> {
match name {
"Float.fromString" => Some(from_string_nv(args, arena)),
"Float.fromInt" => Some(from_int_nv(args, arena)),
"Float.toString" => Some(to_string_nv(args, arena)),
"Float.abs" => Some(abs_nv(args, arena)),
"Float.floor" => Some(floor_nv(args, arena)),
"Float.ceil" => Some(ceil_nv(args, arena)),
"Float.round" => Some(round_nv(args, arena)),
"Float.min" => Some(min_nv(args, arena)),
"Float.max" => Some(max_nv(args, arena)),
"Float.sin" => Some(sin_nv(args, arena)),
"Float.cos" => Some(cos_nv(args, arena)),
"Float.sqrt" => Some(sqrt_nv(args, arena)),
"Float.pow" => Some(pow_nv(args, arena)),
"Float.atan2" => Some(atan2_nv(args, arena)),
"Float.pi" => Some(pi_nv(args)),
_ => None,
}
}
fn nv_check1(name: &str, args: &[NanValue]) -> Result<NanValue, RuntimeError> {
if args.len() != 1 {
return Err(RuntimeError::Error(format!(
"{}() takes 1 argument, got {}",
name,
args.len()
)));
}
Ok(args[0])
}
fn nv_check2(name: &str, args: &[NanValue]) -> Result<(NanValue, NanValue), RuntimeError> {
if args.len() != 2 {
return Err(RuntimeError::Error(format!(
"{}() takes 2 arguments, got {}",
name,
args.len()
)));
}
Ok((args[0], args[1]))
}
fn from_string_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
let v = nv_check1("Float.fromString", args)?;
if !v.is_string() {
return Err(RuntimeError::Error(
"Float.fromString: argument must be a String".to_string(),
));
}
let s = arena.get_string_value(v);
match s.parse::<f64>() {
Ok(f) => {
let inner = NanValue::new_float(f);
Ok(NanValue::new_ok_value(inner, arena))
}
Err(_) => {
let msg = format!("Cannot parse '{}' as Float", s);
let inner = NanValue::new_string_value(&msg, arena);
Ok(NanValue::new_err_value(inner, arena))
}
}
}
fn from_int_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
let v = nv_check1("Float.fromInt", args)?;
if !v.is_int() {
return Err(RuntimeError::Error(
"Float.fromInt: argument must be an Int".to_string(),
));
}
Ok(NanValue::new_float(v.as_int(arena) as f64))
}
fn to_string_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
let v = nv_check1("Float.toString", args)?;
if !v.is_float() {
return Err(RuntimeError::Error(
"Float.toString: argument must be a Float".to_string(),
));
}
let s = format!("{}", v.as_float());
Ok(NanValue::new_string_value(&s, arena))
}
fn abs_nv(args: &[NanValue], _arena: &mut Arena) -> Result<NanValue, RuntimeError> {
let v = nv_check1("Float.abs", args)?;
if !v.is_float() {
return Err(RuntimeError::Error(
"Float.abs: argument must be a Float".to_string(),
));
}
Ok(NanValue::new_float(v.as_float().abs()))
}
fn floor_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
let v = nv_check1("Float.floor", args)?;
if !v.is_float() {
return Err(RuntimeError::Error(
"Float.floor: argument must be a Float".to_string(),
));
}
Ok(NanValue::new_int(v.as_float().floor() as i64, arena))
}
fn ceil_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
let v = nv_check1("Float.ceil", args)?;
if !v.is_float() {
return Err(RuntimeError::Error(
"Float.ceil: argument must be a Float".to_string(),
));
}
Ok(NanValue::new_int(v.as_float().ceil() as i64, arena))
}
fn round_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
let v = nv_check1("Float.round", args)?;
if !v.is_float() {
return Err(RuntimeError::Error(
"Float.round: argument must be a Float".to_string(),
));
}
Ok(NanValue::new_int(v.as_float().round() as i64, arena))
}
fn min_nv(args: &[NanValue], _arena: &mut Arena) -> Result<NanValue, RuntimeError> {
let (a, b) = nv_check2("Float.min", args)?;
if !a.is_float() || !b.is_float() {
return Err(RuntimeError::Error(
"Float.min: both arguments must be Float".to_string(),
));
}
Ok(NanValue::new_float(f64::min(a.as_float(), b.as_float())))
}
fn max_nv(args: &[NanValue], _arena: &mut Arena) -> Result<NanValue, RuntimeError> {
let (a, b) = nv_check2("Float.max", args)?;
if !a.is_float() || !b.is_float() {
return Err(RuntimeError::Error(
"Float.max: both arguments must be Float".to_string(),
));
}
Ok(NanValue::new_float(f64::max(a.as_float(), b.as_float())))
}
fn sin_nv(args: &[NanValue], _arena: &mut Arena) -> Result<NanValue, RuntimeError> {
let v = nv_check1("Float.sin", args)?;
if !v.is_float() {
return Err(RuntimeError::Error(
"Float.sin: argument must be a Float".to_string(),
));
}
Ok(NanValue::new_float(v.as_float().sin()))
}
fn cos_nv(args: &[NanValue], _arena: &mut Arena) -> Result<NanValue, RuntimeError> {
let v = nv_check1("Float.cos", args)?;
if !v.is_float() {
return Err(RuntimeError::Error(
"Float.cos: argument must be a Float".to_string(),
));
}
Ok(NanValue::new_float(v.as_float().cos()))
}
fn sqrt_nv(args: &[NanValue], _arena: &mut Arena) -> Result<NanValue, RuntimeError> {
let v = nv_check1("Float.sqrt", args)?;
if !v.is_float() {
return Err(RuntimeError::Error(
"Float.sqrt: argument must be a Float".to_string(),
));
}
Ok(NanValue::new_float(v.as_float().sqrt()))
}
fn pow_nv(args: &[NanValue], _arena: &mut Arena) -> Result<NanValue, RuntimeError> {
let (a, b) = nv_check2("Float.pow", args)?;
if !a.is_float() || !b.is_float() {
return Err(RuntimeError::Error(
"Float.pow: both arguments must be Float".to_string(),
));
}
Ok(NanValue::new_float(a.as_float().powf(b.as_float())))
}
fn atan2_nv(args: &[NanValue], _arena: &mut Arena) -> Result<NanValue, RuntimeError> {
let (a, b) = nv_check2("Float.atan2", args)?;
if !a.is_float() || !b.is_float() {
return Err(RuntimeError::Error(
"Float.atan2: both arguments must be Float".to_string(),
));
}
Ok(NanValue::new_float(a.as_float().atan2(b.as_float())))
}
fn pi_nv(args: &[NanValue]) -> Result<NanValue, RuntimeError> {
if !args.is_empty() {
return Err(RuntimeError::Error(format!(
"Float.pi() takes 0 arguments, got {}",
args.len()
)));
}
Ok(NanValue::new_float(std::f64::consts::PI))
}