use std::collections::BTreeMap;
use std::fmt;
pub mod shared {
pub use std::sync::{Arc as Sh, Weak as ShWeak};
use std::any::Any;
pub struct Lock<T>(std::sync::Mutex<T>);
impl<T> Lock<T> {
pub fn new(v: T) -> Self {
Lock(std::sync::Mutex::new(v))
}
pub fn read(&self) -> impl std::ops::Deref<Target = T> + '_ {
self.0.lock().unwrap_or_else(|e| e.into_inner())
}
pub fn write(&self) -> impl std::ops::DerefMut<Target = T> + '_ {
self.0.lock().unwrap_or_else(|e| e.into_inner())
}
}
pub trait NativeBound: 'static + Send {}
impl<T: 'static + Send> NativeBound for T {}
pub trait HostBound: 'static + Send + Sync {}
impl<T: 'static + Send + Sync> HostBound for T {}
pub type AnyLock = Sh<std::sync::Mutex<dyn Any + Send>>;
pub fn new_any<T: NativeBound>(v: T) -> AnyLock {
Sh::new(std::sync::Mutex::new(v))
}
pub fn with_any_mut<R>(a: &AnyLock, f: impl FnOnce(&mut dyn Any) -> R) -> R {
f(&mut *a.lock().unwrap_or_else(|e| e.into_inner()))
}
pub fn any_ptr(a: &AnyLock) -> usize {
Sh::as_ptr(a) as *const () as usize
}
pub fn any_ptr_eq(a: &AnyLock, b: &AnyLock) -> bool {
any_ptr(a) == any_ptr(b)
}
}
use shared::{AnyLock, Lock, NativeBound, Sh};
pub type FList = imbl::Vector<Value>;
pub type FMap = imbl::OrdMap<String, Value>;
#[derive(Clone)]
pub enum Value {
Unit,
Bool(bool),
Int(i64),
Float(f64),
Str(Sh<str>),
List(Sh<FList>),
Tuple(Sh<FList>),
Record(FMap),
Variant(Sh<Variant>),
Closure(Sh<Closure>),
NativeFn(u32),
Atom(Sh<AtomCell>),
Cell(Sh<Lock<Value>>),
Native(NativeValue),
Range(i64, i64, bool),
}
pub struct Variant {
pub tag: String,
pub payload: VariantPayload,
}
#[derive(Clone)]
pub enum VariantPayload {
Unit,
Positional(Vec<Value>),
Named(BTreeMap<String, Value>),
}
pub struct Closure {
pub fn_id: u32,
pub upvals: Vec<Value>,
}
pub struct AtomCell {
pub id: u64,
pub value: Lock<Value>,
pub watchers: Lock<Vec<(String, Value)>>,
}
#[derive(Clone)]
pub struct NativeValue {
pub type_name: Sh<str>,
pub data: AnyLock,
}
impl NativeValue {
pub fn with_ref<T: 'static, R>(&self, f: impl FnOnce(&T) -> R) -> Option<R> {
shared::with_any_mut(&self.data, |any| any.downcast_ref::<T>().map(f))
}
pub fn with_mut<T: 'static, R>(&self, f: impl FnOnce(&mut T) -> R) -> Option<R> {
shared::with_any_mut(&self.data, |any| any.downcast_mut::<T>().map(f))
}
}
impl Value {
pub fn str(s: impl Into<String>) -> Value {
Value::Str(Sh::from(s.into().as_str()))
}
pub fn list(items: Vec<Value>) -> Value {
Value::List(Sh::new(items.into_iter().collect()))
}
pub fn tuple(items: Vec<Value>) -> Value {
Value::Tuple(Sh::new(items.into_iter().collect()))
}
pub fn list_v(items: FList) -> Value {
Value::List(Sh::new(items))
}
pub fn record(fields: BTreeMap<String, Value>) -> Value {
Value::Record(fields.into_iter().collect())
}
pub fn variant(tag: &str, payload: VariantPayload) -> Value {
Value::Variant(Sh::new(Variant {
tag: tag.to_string(),
payload,
}))
}
pub fn ok(v: Value) -> Value {
Value::variant("Ok", VariantPayload::Positional(vec![v]))
}
pub fn err(v: Value) -> Value {
Value::variant("Err", VariantPayload::Positional(vec![v]))
}
pub fn some(v: Value) -> Value {
Value::variant("Some", VariantPayload::Positional(vec![v]))
}
pub fn none() -> Value {
Value::variant("None", VariantPayload::Unit)
}
pub fn native<T: NativeBound>(type_name: &str, v: T) -> Value {
Value::Native(NativeValue {
type_name: Sh::from(type_name),
data: shared::new_any(v),
})
}
pub fn type_name(&self) -> String {
match self {
Value::Unit => "Unit".into(),
Value::Bool(_) => "Bool".into(),
Value::Int(_) => "Int".into(),
Value::Float(_) => "Float".into(),
Value::Str(_) => "Str".into(),
Value::List(_) => "List".into(),
Value::Tuple(_) => "Tuple".into(),
Value::Record(_) => "Record".into(),
Value::Variant(v) => format!("Variant({})", v.tag),
Value::Closure(_) => "Fn".into(),
Value::NativeFn(_) => "NativeFn".into(),
Value::Atom(_) => "Atom".into(),
Value::Cell(_) => "Cell".into(),
Value::Native(n) => n.type_name.to_string(),
Value::Range(..) => "Range".into(),
}
}
pub fn is_truthy_bool(&self) -> Option<bool> {
match self {
Value::Bool(b) => Some(*b),
_ => None,
}
}
}
impl PartialEq for Value {
fn eq(&self, other: &Self) -> bool {
use Value::*;
match (self, other) {
(Unit, Unit) => true,
(Bool(a), Bool(b)) => a == b,
(Int(a), Int(b)) => a == b,
(Float(a), Float(b)) => a == b,
(Int(a), Float(b)) | (Float(b), Int(a)) => (*a as f64) == *b,
(Str(a), Str(b)) => a == b,
(List(a), List(b)) | (Tuple(a), Tuple(b)) => a == b,
(Record(a), Record(b)) => a == b,
(Variant(a), Variant(b)) => {
a.tag == b.tag
&& match (&a.payload, &b.payload) {
(VariantPayload::Unit, VariantPayload::Unit) => true,
(VariantPayload::Positional(x), VariantPayload::Positional(y)) => x == y,
(VariantPayload::Named(x), VariantPayload::Named(y)) => x == y,
_ => false,
}
}
(Closure(a), Closure(b)) => Sh::ptr_eq(a, b),
(NativeFn(a), NativeFn(b)) => a == b,
(Atom(a), Atom(b)) => Sh::ptr_eq(a, b),
(Cell(a), Cell(b)) => Sh::ptr_eq(a, b),
(Native(a), Native(b)) => shared::any_ptr_eq(&a.data, &b.data),
(Range(a, b, c), Range(d, e, f)) => a == d && b == e && c == f,
_ => false,
}
}
}
impl fmt::Display for Value {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Value::Unit => write!(f, "()"),
Value::Bool(b) => write!(f, "{}", b),
Value::Int(i) => write!(f, "{}", i),
Value::Float(x) => {
if x.fract() == 0.0 && x.is_finite() {
write!(f, "{:.1}", x)
} else {
write!(f, "{}", x)
}
}
Value::Str(s) => write!(f, "{}", s),
Value::List(items) => {
write!(f, "[")?;
for (i, v) in items.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}", DebugStr(v))?;
}
write!(f, "]")
}
Value::Tuple(items) => {
write!(f, "(")?;
for (i, v) in items.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}", DebugStr(v))?;
}
write!(f, ")")
}
Value::Record(fields) => {
write!(f, "{{ ")?;
for (i, (k, v)) in fields.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}: {}", k, DebugStr(v))?;
}
write!(f, " }}")
}
Value::Variant(v) => match &v.payload {
VariantPayload::Unit => write!(f, "{}", v.tag),
VariantPayload::Positional(items) => {
write!(f, "{}(", v.tag)?;
for (i, x) in items.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}", DebugStr(x))?;
}
write!(f, ")")
}
VariantPayload::Named(fields) => {
write!(f, "{} {{ ", v.tag)?;
for (i, (k, x)) in fields.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}: {}", k, DebugStr(x))?;
}
write!(f, " }}")
}
},
Value::Closure(c) => write!(f, "<fn #{}>", c.fn_id),
Value::NativeFn(id) => write!(f, "<native fn #{}>", id),
Value::Atom(a) => write!(f, "atom({})", DebugStr(&a.value.read())),
Value::Cell(c) => write!(f, "{}", &*c.read()),
Value::Native(n) => write!(f, "<{}>", n.type_name),
Value::Range(a, b, inc) => write!(f, "{}{}{}", a, if *inc { "..=" } else { ".." }, b),
}
}
}
struct DebugStr<'a>(&'a Value);
impl fmt::Display for DebugStr<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.0 {
Value::Str(s) => write!(f, "\"{}\"", s),
v => write!(f, "{}", v),
}
}
}
impl fmt::Debug for Value {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", DebugStr(self))
}
}