use crate::base::*; use crate::parser::*; use imbl::*; use std::cell::RefCell; use std::fmt; use std::rc::Rc; use struct_scalpel::Dissectible; #[derive(Clone, Debug)] pub struct Fn<'src> { pub name: &'src str, pub body: &'src Vec>, pub doc: &'src Option<&'src str>, } #[derive(Debug, Dissectible)] pub enum Value<'src> { Nil, Placeholder, Boolean(bool), Number(f64), Keyword(&'src str), // TODO: add a "runtime-generated" string type that wraps a Rust String // this is necessary for nice documentation and string interpolation InternedString(&'src str), AllocatedString(Rc), // on the heap for now Tuple(Rc>), Args(Rc>), // ref-counted, immutable, persistent List(Vector), // ref-counted, immutable, persistent // dicts may only use keywords as keys Dict(HashMap<&'src str, Self>), Box(&'src str, Rc>), Fn(Rc>), Base(Base<'src>), Recur(Vec), // Set(HashSet), // Sets are hard // Sets require Eq // Eq is not implemented on f64, because NaNs // We could use ordered_float::NotNan // Let's defer that // We're not really using sets in Ludus // Other things we're not implementing yet: // pkgs, nses, tests } impl<'src> Clone for Value<'src> { fn clone(&self) -> Value<'src> { match self { Value::Nil => Value::Nil, Value::Boolean(b) => Value::Boolean(*b), Value::InternedString(s) => Value::InternedString(s), Value::AllocatedString(s) => Value::AllocatedString(s.clone()), Value::Keyword(s) => Value::Keyword(s), Value::Number(n) => Value::Number(*n), Value::Tuple(t) => Value::Tuple(t.clone()), Value::Args(a) => Value::Args(a.clone()), Value::Fn(f) => Value::Fn(f.clone()), Value::List(l) => Value::List(l.clone()), Value::Dict(d) => Value::Dict(d.clone()), Value::Box(name, b) => Value::Box(name, b.clone()), Value::Placeholder => Value::Placeholder, Value::Base(b) => Value::Base(b.clone()), Value::Recur(..) => unreachable!(), } } } impl fmt::Display for Value<'_> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Value::Nil => write!(f, "nil"), Value::Boolean(b) => write!(f, "{}", b), Value::Number(n) => write!(f, "{}", n), Value::Keyword(k) => write!(f, ":{}", k), Value::InternedString(s) => write!(f, "\"{}\"", s), Value::AllocatedString(s) => write!(f, "\"{}\"", s), Value::Fn(fun) => write!(f, "fn {}", fun.name), Value::Tuple(t) | Value::Args(t) => write!( f, "({})", t.iter() .map(|x| x.to_string()) .collect::>() .join(", ") ), Value::List(l) => write!( f, "[{}]", l.iter() .map(|x| x.to_string()) .collect::>() .join(", ") ), Value::Dict(d) => write!( f, "#{{{}}}", d.iter() .map(|(k, v)| format!(":{k} {v}")) .collect::>() .join(", ") ), Value::Box(name, value) => { write!( f, "box {}: [{}]", name, &value.try_borrow().unwrap().to_string() ) } Value::Placeholder => write!(f, "_"), Value::Base(base) => { let name = match base { Base::Unary(name, _) => name, Base::Binary(name, _) => name, // Base::Ternary(name, _) => name, }; write!(f, "base fn {}", name) } Value::Recur(..) => unreachable!(), } } } impl Value<'_> { pub fn bool(&self) -> bool { matches!(self, Value::Nil | Value::Boolean(false)) } pub fn ludus_type(&self) -> Value { match self { 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!(), Value::Args(_) => unreachable!(), Value::Base(_) => Value::Keyword("fn"), Value::Recur(..) => unreachable!(), } } } impl<'src> PartialEq for Value<'src> { fn eq(&self, other: &Value<'src>) -> bool { match (self, other) { // value equality types (Value::Nil, Value::Nil) => true, (Value::Boolean(x), Value::Boolean(y)) => x == y, (Value::Number(x), Value::Number(y)) => x == y, (Value::InternedString(x), Value::InternedString(y)) => x == y, (Value::Keyword(x), Value::Keyword(y)) => x == y, (Value::Tuple(x), Value::Tuple(y)) => x == y, (Value::List(x), Value::List(y)) => x == y, (Value::Dict(x), Value::Dict(y)) => x == y, // reference equality types (Value::Fn(x), Value::Fn(y)) => Rc::>::as_ptr(x) == Rc::>::as_ptr(y), (Value::Box(_, x), Value::Box(_, y)) => { Rc::>>::as_ptr(x) == Rc::>>::as_ptr(y) } _ => false, } } } impl Eq for Value<'_> {} impl Value<'_> { pub fn interpolate(&self) -> String { match self { Value::Nil => String::new(), Value::Boolean(b) => format!("{b}"), Value::Number(n) => format!("{n}"), Value::Keyword(k) => format!(":{k}"), Value::AllocatedString(s) => format!("{s}"), Value::InternedString(s) => s.to_string(), Value::Box(_, x) => x.borrow().interpolate(), Value::Tuple(xs) => xs .iter() .map(|x| x.interpolate()) .collect::>() .join(", "), Value::List(xs) => xs .iter() .map(|x| x.interpolate()) .collect::>() .join(", "), Value::Dict(xs) => xs .iter() .map(|(k, v)| format!(":{} {}", k, v.interpolate())) .collect::>() .join(", "), Value::Fn(x) => format!("fn {}", x.name.to_string()), Value::Placeholder => unreachable!(), Value::Args(_) => unreachable!(), Value::Recur(_) => unreachable!(), Value::Base(_) => unreachable!(), } } }