use crate::value::*; use imbl::*; use std::rc::Rc; #[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 append<'src>(x: &Value<'src>, y: &Value<'src>) -> Value<'src> { match x { Value::List(list) => { let mut newlist = list.clone(); newlist.push_back(y.clone()); Value::List(newlist) } _ => unreachable!("internal Ludus error"), } } pub fn dec<'src>(x: &Value<'src>) -> Value<'src> { match x { Value::Number(n) => Value::Number(n - 1.0), _ => unreachable!("internal Ludus error"), } } pub fn inc<'src>(x: &Value<'src>) -> Value<'src> { match x { Value::Number(n) => Value::Number(n + 1.0), _ => unreachable!("internal Ludus error"), } } pub fn div<'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 mult<'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 dissoc<'src>(dict: &Value<'src>, key: &Value<'src>) -> Value<'src> { match (dict, key) { (Value::Dict(dict), Value::Keyword(key)) => { let mut new = dict.clone(); new.remove(key); Value::Dict(new) } _ => unreachable!("internal Ludus error"), } } pub fn first<'src>(ordered: &Value<'src>) -> Value<'src> { match ordered { Value::List(list) => match list.front() { Some(n) => n.clone(), None => Value::Nil, }, Value::Tuple(tuple) => match tuple.first() { Some(n) => n.clone(), None => Value::Nil, }, _ => unreachable!("internal Ludus error"), } } // TODO: figure out how to handle negative numbers // the cast from f64 to usize discards sign info pub fn at<'src>(ordered: &Value<'src>, i: &Value<'src>) -> Value<'src> { match (ordered, i) { (Value::List(list), Value::Number(n)) => { let i = *n as usize; match list.get(i) { Some(n) => n.clone(), None => Value::Nil, } } (Value::Tuple(tuple), Value::Number(n)) => { let i = *n as usize; match tuple.get(i) { Some(n) => n.clone(), None => Value::Nil, } } _ => unreachable!("internal Ludus error"), } } pub fn get<'src>(dict: &Value<'src>, key: &Value<'src>) -> Value<'src> { match (dict, key) { (Value::Dict(dict), Value::Keyword(key)) => match dict.get(key) { Some(x) => x.clone(), None => Value::Nil, }, _ => unreachable!("internal Ludus error"), } } pub fn last<'src>(ordered: &Value<'src>) -> Value<'src> { match ordered { Value::List(list) => match list.last() { Some(x) => x.clone(), None => Value::Nil, }, Value::Tuple(tuple) => match tuple.last() { Some(x) => x.clone(), None => Value::Nil, }, _ => unreachable!("internal Ludus error"), } } pub fn or<'src>(x: &Value<'src>, y: &Value<'src>) -> Value<'src> { Value::Boolean(x.bool() || y.bool()) } pub fn print<'src>(x: &Value<'src>) -> Value<'src> { println!("{}", x); Value::Keyword("ok") } pub fn show<'src>(x: &Value<'src>) -> Value<'src> { Value::AllocatedString(Rc::new(format!("{x}"))) } pub fn rest<'src>(ordered: &Value<'src>) -> Value<'src> { match ordered { Value::List(list) => Value::List(list.clone().split_at(1).1), Value::Tuple(tuple) => Value::List(Vector::from_iter(tuple.iter().next().cloned())), _ => unreachable!("internal Ludus error"), } } pub fn count<'src>(coll: &Value<'src>) -> Value<'src> { match coll { Value::Dict(d) => Value::Number(d.len() as f64), Value::List(l) => Value::Number(l.len() as f64), Value::Tuple(t) => Value::Number(t.len() as f64), Value::AllocatedString(s) => Value::Number(s.len() as f64), Value::InternedString(s) => Value::Number(s.len() as f64), _ => unreachable!("internal Ludus error"), } } pub fn range<'src>(start: &Value<'src>, end: &Value<'src>) -> Value<'src> { match (start, end) { (Value::Number(start), Value::Number(end)) => { let start = *start as isize; let end = *end as isize; let mut range = Vector::new(); for n in start..end { range.push_back(Value::Number(n as f64)) } Value::List(range) } _ => unreachable!("internal Ludus error"), } } pub fn slice<'src>(ordered: &Value<'src>, start: &Value<'src>, end: &Value<'src>) -> Value<'src> { match (ordered, start, end) { (Value::List(list), Value::Number(start), Value::Number(end)) => { let mut newlist = list.clone(); let start = std::cmp::max(*start as usize, 0); let end = std::cmp::min(*end as usize, list.len()); Value::List(newlist.slice(start..end)) } // TODO: figure out something better to do than return an empty string on a bad slice (Value::AllocatedString(string), Value::Number(start), Value::Number(end)) => { let start = std::cmp::max(*start as usize, 0); let end = std::cmp::min(*end as usize, string.len()); Value::AllocatedString(Rc::new( string .clone() .as_str() .get(start..end) .unwrap_or("") .to_string(), )) } (Value::InternedString(string), Value::Number(start), Value::Number(end)) => { let start = std::cmp::max(*start as usize, 0); let end = std::cmp::min(*end as usize, string.len()); Value::AllocatedString(Rc::new(string.get(start..end).unwrap_or("").to_string())) } _ => unreachable!("internal Ludus error"), } } pub fn list<'src>(x: &Value<'src>) -> Value<'src> { match x { Value::List(_) => x.clone(), Value::Tuple(t) => Value::List(Vector::from_iter(t.iter().cloned())), Value::Dict(d) => { let kvs = d.iter(); let mut list = vector![]; for (key, value) in kvs { let kv = Value::Tuple(Rc::new(vec![Value::Keyword(key), value.clone()])); list.push_back(kv); } Value::List(list) } _ => Value::List(vector![x.clone()]), } } pub fn number<'src>(x: &Value<'src>) -> Value<'src> { match x { Value::InternedString(string) => match string.parse::() { Ok(n) => Value::Tuple(Rc::new(vec![Value::Keyword("ok"), Value::Number(n)])), Err(_) => Value::Tuple(Rc::new(vec![ Value::Keyword("err"), Value::AllocatedString(Rc::new(format!("could not parse `{string}` as a number"))), ])), }, Value::AllocatedString(string) => match string.parse::() { Ok(n) => Value::Tuple(Rc::new(vec![Value::Keyword("ok"), Value::Number(n)])), Err(_) => Value::Tuple(Rc::new(vec![ Value::Keyword("err"), Value::AllocatedString(Rc::new(format!("could not parse `{string}` as a number"))), ])), }, _ => unreachable!("internal Ludus error"), } } pub fn r#type<'src>(x: &Value<'src>) -> Value<'src> { match x { Value::Nil => Value::Keyword("nil"), Value::Number(_) => Value::Keyword("number"), Value::Boolean(_) => Value::Keyword("boolean"), Value::Keyword(_) => Value::Keyword("keyword"), Value::Tuple(_) => Value::Keyword("tuple"), Value::InternedString(_) => Value::Keyword("string"), Value::AllocatedString(_) => Value::Keyword("string"), Value::List(_) => Value::Keyword("list"), Value::Dict(_) => Value::Keyword("dict"), Value::Fn(_) => Value::Keyword("fn"), Value::Box(_, _) => Value::Keyword("box"), Value::Placeholder => unreachable!("internal Ludus error"), Value::Args(_) => unreachable!("internal Ludus error"), Value::Base(_) => Value::Keyword("fn"), Value::Recur(..) => 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))), ("append", Value::Base(Base::Binary(append))), ("dec", Value::Base(Base::Unary(dec))), ("inc", Value::Base(Base::Unary(inc))), ("div", Value::Base(Base::Binary(div))), ("mult", Value::Base(Base::Binary(mult))), ("dissoc", Value::Base(Base::Binary(dissoc))), ("first", Value::Base(Base::Unary(first))), ("at", Value::Base(Base::Binary(at))), ("get", Value::Base(Base::Binary(get))), ("last", Value::Base(Base::Unary(last))), ("or", Value::Base(Base::Binary(or))), ("print!", Value::Base(Base::Unary(print))), ("show", Value::Base(Base::Unary(show))), ("rest", Value::Base(Base::Unary(rest))), ("count", Value::Base(Base::Unary(count))), ("range", Value::Base(Base::Binary(range))), ("slice", Value::Base(Base::Ternary(slice))), ("list", Value::Base(Base::Unary(list))), ("number", Value::Base(Base::Unary(number))), ("type", Value::Base(Base::Unary(r#type))), ] } // * [ ] atan_2 (x) -> number // * [ ] ceil (x) -> number // * [ ] cos (x) -> number // * [x] count (x) -> number // * [~] disj (x) -> set // * [ ] downcase (x) -> string // * [ ] floor (x) -> number // * [ ] gt (x, y) -> bool // * [ ] gte! (x, y) -> bool // * [ ] lt (x) -> bool // * [ ] lte (x) -> bool // * [ ] mod (x, y) -> number // * [ ] pi // * [ ] random () -> number // * [x] range () -> list // * [ ] round (x) -> number // * [ ] sin (x) -> number // * [x] slice (x, y, z) -> list // * [ ] split (x, y) -> list(string) // * [ ] sqrt (x) -> number // * [x] ~str_slice (x, y, z) -> string~ // * [x] ~stringify (x) -> string~ // * [ ] tan (x) -> number // * [x] 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] and (x, y) -> value // * [x] append (x, y) -> list // * [x] assoc (x, y) -> dict // * [x] bool (x) -> bool // * [x] chars (x) -> list // * [x] concat (x, y) -> value // * [x] dec (x) -> number // * [x] dissoc (x, y) -> dict // * [x] div (x, y) -> number // * [x] doc (x) -> string // * [x] eq (x, y) -> bool // * [x] first (x) -> value // * [x] get (x, y) -> value // * [x] inc (x) -> number // * [x] last (x) -> value // * [x] or (x, y) -> value // * [x] print! (x) -> :ok // * [x] rest (x) -> coll // * [x] show (x) -> string // * [x] store! (x, y) -> value // * [x] sub (x, y) -> number // * [x] unbox (x) -> value // * [x] ~prn (x) -> value~ // * [x] ~push (x) -> list~