// Solution to the #[derive(Clone)] problem: https://stackoverflow.com/questions/27883509/can-you-clone-a-closure use crate::value::*; use crate::vm::*; use std::rc::Rc; // use imbl::*; // use std::fmt; type LResult<'src> = Result, LudusError>; #[derive(Clone, Debug)] pub enum Base<'src> { Unary(&'src str, fn(&Value<'src>) -> LResult<'src>), Binary(&'src str, fn(&Value<'src>, &Value<'src>) -> LResult<'src>), // Ternary( // &'src str, // fn(&Value<'src>, &Value<'src>, &Value<'src>) -> LResult<'src>, // ), } // impl<'src> Base<'src> { // pub fn name(&self) -> &'src str { // match self { // Base::Unary(name, _) => name, // Base::Binary(name, _) => name, // Base::Ternary(name, _) => name, // } // } // } pub fn eq<'src>(x: &Value<'src>, y: &Value<'src>) -> LResult<'src> { Ok(Value::Boolean(x == y)) } pub fn add<'src>(x: &Value<'src>, y: &Value<'src>) -> LResult<'src> { match (x, y) { (Value::Number(x), Value::Number(y)) => Ok(Value::Number(x + y)), _ => Err(LudusError { msg: "add takes two numbers".to_string(), }), } } pub fn sub<'src>(x: &Value<'src>, y: &Value<'src>) -> LResult<'src> { match (x, y) { (Value::Number(x), Value::Number(y)) => Ok(Value::Number(x - y)), _ => Err(LudusError { msg: "sub takes two numbers".to_string(), }), } } pub fn unbox<'src>(x: &Value<'src>) -> LResult<'src> { match x { Value::Box(_, cell) => Ok(cell.borrow().clone()), _ => Err(LudusError { msg: "only boxes may be unboxed".to_string(), }), } } pub fn store<'src>(b: &Value<'src>, val: &Value<'src>) -> LResult<'src> { if let Value::Box(_, cell) = b { cell.replace(val.clone()); Ok(val.clone()) } else { Err(LudusError { msg: "only boxes may store values".to_string(), }) } } // TODO: do better than returning just the docstr // name, patterns, AND docstring pub fn doc<'src>(f: &Value<'src>) -> LResult<'src> { match f { Value::Fn(f) => { let Fn { name, doc, .. } = **f; if let Some(docstr) = doc { Ok(Value::AllocatedString(Rc::new(format!("{name}: {docstr}")))) } else { Ok(Value::InternedString( doc.unwrap_or("no documentation found"), )) } } _ => Ok(Value::InternedString("no documentation found")), } } pub fn base<'src>() -> Vec<(&'src str, Value<'src>)> { let mut base = vec![]; let eq = Base::Binary("eq", eq); base.push(("eq", Value::Base(eq))); let add = Base::Binary("add", add); base.push(("add", Value::Base(add))); base.push(("sub", Value::Base(Base::Binary("sub", sub)))); base.push(("unbox", Value::Base(Base::Unary("unbox", unbox)))); base.push(("store", Value::Base(Base::Binary("store", store)))); base.push(("doc", Value::Base(Base::Unary("doc", doc)))); base } // DONE add (x, y) -> number // and (x, y) -> value // assoc (x, y) -> dict // atan_2 (x) -> number // bool (x) -> bool // ceil (x) -> number // chars (x) -> list // concat (x, y) -> value // conj (x, y) -> list // cos (x) -> number // dec (x) -> number // disj (x) -> set // dissoc (x, y) -> dict // div (x, y) -> number // doc (x) -> string // downcase (x) -> string // DONE eq (x, y) -> bool // first (x) -> value // floor (x) -> number // get (x, y) -> value // gt (x, y) -> bool // gte! (x, y) -> bool // inc (x) -> number // last (x) -> value // lt (x) -> bool // lte (x) -> bool // mod (x, y) -> number // or (x, y) -> value // pi // print! (x) -> :ok // prn (x) -> value // push (x) -> list // random () -> number // range () -> list // rest (x) -> coll // round (x) -> number // show (x) -> string // sin (x) -> number // slice (x, y, z) -> list // split (x, y) -> list(string) // sqrt (x) -> number // DONE store! (x, y) -> value // str_slice (x, y, z) -> string // stringify (x) -> string // sub (x, y) -> number // tan (x) -> number // to_list (x) -> list // to_number (x) -> number // trim (x) -> string // triml (x) -> string // trimr (x) -> string // type (x) -> keyword // DONE unbox (x) -> value // upcase (x) -> string