use crate::eval::{err, RTError}; use crate::value::Value; type Result = std::result::Result; crate type NativeFunc = for<'a> fn(&'a [Value]) -> Result; crate fn prims() -> &'static [(&'static str, NativeFunc)] { &[ ("*", mul), ("+", add), ("-", sub), ("/", div), // TODO: Spec compiance ("=", equals), ("eq?", equals), ("eqv?", equals), ("equal?", equals), ("display", display), ("newline", newline), ("abs", abs), ("<", lt), (">", gt), ("<=", le), (">=", ge), ("_Z_debug", z_debug) ] } // TODO: DRY +-/* fn add(args: &[Value]) -> Result { args.iter() .map(Value::as_num) .try_fold(0.0, |a, b| Ok(a + b?)) .map(Value::Num) } fn mul(args: &[Value]) -> Result { args.iter() .map(Value::as_num) .try_fold(1.0, |a, b| Ok(a * b?)) .map(Value::Num) } fn div(args: &[Value]) -> Result { let init = args .get(0) .ok_or_else(|| RTError("`div` needs at least one argument".to_owned()))? .as_num()?; Ok(Value::Num(if args.len() == 1 { 1.0 / init } else { let rest = mul(&args[1..])?.as_num().unwrap(); init / rest })) } fn sub(args: &[Value]) -> Result { let init = args .get(0) .ok_or_else(|| RTError("`sub` needs at least one argument".to_owned()))? .as_num()?; Ok(Value::Num(if args.len() == 1 { -init } else { let rest = add(&args[1..])?.as_num().unwrap(); init - rest })) } fn equals(args: &[Value]) -> Result { Ok(Value::Bool(args.array_windows().all(|[l, r]| l == r))) } fn display(args: &[Value]) -> Result { let [arg] = args else {return err("To many args to `display`".to_owned())}; print!("{}", arg); Ok(Value::Trap) } fn newline(args: &[Value]) -> Result { if !args.is_empty() { return err("Newline takes no args".to_owned()); } println!(); Ok(Value::Trap) } fn abs(args: &[Value]) -> Result { let [v] = args else { return err("abs takes 1 arg".to_owned()) }; let ans = v.as_num()?.abs(); Ok(Value::Num(ans)) } crate fn compare_core( args: &[Value], f: fn(f64, f64) -> bool, name: &'static str, ) -> Result { for [l, r] in args.array_windows() { let (Value::Num(l), Value::Num(r)) = (l,r) else { return err(format!("`{}` args must be numbers", name)); }; // TODO: Ensure this is correct wrt NaN if !f(*l, *r) { return Ok(Value::Bool(false)); }; } Ok(Value::Bool(true)) } macro_rules! cmps { ($(($name:ident $op:tt))*) => { $( fn $name(args: &[Value]) -> Result { compare_core(args, |l, r| l $op r, stringify!($op)) } )* }; } cmps! { (lt <) (gt >) (le <=) (ge >=) } fn z_debug(args: &[Value]) -> Result { eprintln!("{:?}", args); Ok(Value::Trap) }