use crate::value::*; use imbl::*; use std::rc::Rc; // use std::fmt; #[derive(Clone, Debug)] pub enum Base<'src> { Unary(fn(&Value<'src>) -> Value<'src>), Binary(fn(&Value<'src>, &Value<'src>) -> Value<'src>), Ternary(fn(&Value<'src>, &Value<'src>, &Value<'src>) -> Value<'src>), } pub fn eq<'src>(x: &Value<'src>, y: &Value<'src>) -> Value<'src> { Value::Boolean(x == y) } pub fn add<'src>(x: &Value<'src>, y: &Value<'src>) -> Value<'src> { match (x, y) { (Value::Number(x), Value::Number(y)) => Value::Number(x + y), _ => unreachable!("internal Ludus error"), } } pub fn sub<'src>(x: &Value<'src>, y: &Value<'src>) -> Value<'src> { match (x, y) { (Value::Number(x), Value::Number(y)) => Value::Number(x - y), _ => unreachable!("internal Ludus error"), } } pub fn unbox<'src>(x: &Value<'src>) -> Value<'src> { match x { Value::Box(_, cell) => cell.borrow().clone(), _ => unreachable!("internal Ludus error"), } } pub fn store<'src>(b: &Value<'src>, val: &Value<'src>) -> Value<'src> { if let Value::Box(_, cell) = b { cell.replace(val.clone()); val.clone() } else { unreachable!("internal Ludus error") } } // TODO: do better than returning just the docstr // name, patterns, AND docstring pub fn doc<'src>(f: &Value<'src>) -> Value<'src> { match f { Value::Fn(f) => { let Fn { name, doc, .. } = **f; if let Some(docstr) = doc { Value::AllocatedString(Rc::new(format!("{name}: {docstr}"))) } else { Value::InternedString(doc.unwrap_or("no documentation found")) } } _ => Value::InternedString("no documentation found"), } } pub fn and<'src>(x: &Value<'src>, y: &Value<'src>) -> Value<'src> { Value::Boolean(x.bool() && y.bool()) } pub fn assoc<'src>(dict: &Value<'src>, key: &Value<'src>, value: &Value<'src>) -> Value<'src> { match (dict, key) { (Value::Dict(d), Value::Keyword(k)) => Value::Dict(d.update(k, value.clone())), _ => unreachable!("internal Ludus error"), } } pub fn r#bool<'src>(x: &Value<'src>) -> Value<'src> { Value::Boolean(x.bool()) } pub fn chars<'src>(x: &Value<'src>) -> Value<'src> { match x { Value::InternedString(s) => { let chars = s.chars(); let mut charlist = vector![]; for char in chars { if char.is_ascii() { charlist.push_back(Value::AllocatedString(Rc::new(char.to_string()))) } else { return Value::Tuple(Rc::new(vec![ Value::Keyword("err"), Value::AllocatedString(Rc::new(format!( "{char} is not an ascii character" ))), ])); } } Value::Tuple(Rc::new(vec![Value::Keyword("ok"), Value::List(charlist)])) } Value::AllocatedString(s) => { let chars = s.chars(); let mut charlist = vector![]; for char in chars { if char.is_ascii() { charlist.push_back(Value::AllocatedString(Rc::new(char.to_string()))) } else { return Value::Tuple(Rc::new(vec![ Value::Keyword("err"), Value::AllocatedString(Rc::new(format!( "{char} is not an ascii character" ))), ])); } } Value::Tuple(Rc::new(vec![Value::Keyword("ok"), Value::List(charlist)])) } _ => unreachable!("internal Ludus error"), } } // TODO: figure out how to get to opportunistic mutation here pub fn concat<'src>(x: &Value<'src>, y: &Value<'src>) -> Value<'src> { match (x, y) { (Value::InternedString(x), Value::InternedString(y)) => { Value::AllocatedString(Rc::new(format!("{x}{y}"))) } (Value::AllocatedString(x), Value::AllocatedString(y)) => { Value::AllocatedString(Rc::new(format!("{x}{y}"))) } (Value::AllocatedString(x), Value::InternedString(y)) => { Value::AllocatedString(Rc::new(format!("{x}{y}"))) } (Value::InternedString(x), Value::AllocatedString(y)) => { Value::AllocatedString(Rc::new(format!("{x}{y}"))) } (Value::List(x), Value::List(y)) => { let mut newlist = x.clone(); newlist.append(y.clone()); Value::List(newlist) } _ => unreachable!("internal Ludus error"), } } pub fn base<'src>() -> Vec<(&'src str, Value<'src>)> { vec![ ("eq?", Value::Base(Base::Binary(eq))), ("add", Value::Base(Base::Binary(add))), ("sub", Value::Base(Base::Binary(sub))), ("unbox", Value::Base(Base::Unary(unbox))), ("store!", Value::Base(Base::Binary(store))), ("doc!", Value::Base(Base::Unary(doc))), ("and", Value::Base(Base::Binary(and))), ("assoc", Value::Base(Base::Ternary(assoc))), ("bool", Value::Base(Base::Unary(r#bool))), ("chars", Value::Base(Base::Unary(chars))), ("concat", Value::Base(Base::Binary(concat))), ] } // * [x] and (x, y) -> value // * [x] assoc (x, y) -> dict // * [ ] atan_2 (x) -> number // * [x] bool (x) -> bool // * [ ] ceil (x) -> number // * [x] 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 // * [ ] 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 // * [ ] 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 // * [ ] upcase (x) -> string // * [x] add (x, y) -> number // * [x] eq (x, y) -> bool // * [x] store! (x, y) -> value // * [x] unbox (x) -> value