use std::collections::{HashMap, VecDeque};
use std::sync::Arc;
use tatara_lisp_eval::ffi::Arity;
use tatara_lisp_eval::{ErrorObj, Interpreter, Value};
use crate::Pid;
#[derive(Debug, Default)]
pub struct System {
boxes: HashMap<Pid, VecDeque<Value>>,
current: Pid,
pub undeliverable: Vec<(Pid, Value)>,
}
impl crate::Readiness for System {
fn is_ready(&self, pid: Pid) -> bool {
self.has_mail(pid)
}
fn any_pending(&self) -> bool {
self.any_mail()
}
fn set_current(&mut self, pid: Pid) {
self.current = pid;
}
fn on_restart(&mut self, pid: Pid) {
self.clear(pid);
}
}
impl System {
pub fn new() -> Self {
Self::default()
}
pub fn register(&mut self, pid: Pid) {
self.boxes.entry(pid).or_default();
}
pub fn current(&self) -> Pid {
self.current
}
pub fn send(&mut self, to: Pid, msg: Value) {
let msg = deep_copy(&msg);
match self.boxes.get_mut(&to) {
Some(q) => q.push_back(msg),
None => self.undeliverable.push((to, msg)),
}
}
pub fn has_mail(&self, pid: Pid) -> bool {
self.boxes.get(&pid).is_some_and(|q| !q.is_empty())
}
pub fn mail_count(&self, pid: Pid) -> usize {
self.boxes.get(&pid).map_or(0, VecDeque::len)
}
pub fn clear(&mut self, pid: Pid) {
if let Some(q) = self.boxes.get_mut(&pid) {
q.clear();
}
}
pub fn any_mail(&self) -> bool {
self.boxes.values().any(|q| !q.is_empty())
}
}
pub fn deep_copy(v: &Value) -> Value {
match v {
Value::Nil
| Value::Bool(_)
| Value::Int(_)
| Value::Float(_)
| Value::Str(_)
| Value::Symbol(_)
| Value::Keyword(_) => v.clone(),
Value::List(items) => Value::list(items.iter().map(deep_copy)),
Value::Map(m) => Value::Map(Arc::new(
m.iter().map(|(k, v)| (k.clone(), deep_copy(v))).collect(),
)),
Value::Error(e) => Value::Error(Arc::new(ErrorObj {
tag: e.tag.clone(),
message: e.message.clone(),
data: e
.data
.iter()
.map(|(k, v)| (deep_copy(k), deep_copy(v)))
.collect(),
})),
Value::Sexp(s, span) => Value::Sexp(s.clone(), *span),
_ => Value::Nil,
}
}
pub fn copy_data(v: &Value) -> Result<Value, &'static str> {
Ok(match v {
Value::Nil
| Value::Bool(_)
| Value::Int(_)
| Value::Float(_)
| Value::Str(_)
| Value::Symbol(_)
| Value::Keyword(_) => v.clone(),
Value::List(items) => {
Value::list(items.iter().map(copy_data).collect::<Result<Vec<_>, _>>()?)
}
Value::Map(m) => Value::Map(Arc::new(
m.iter()
.map(|(k, v)| copy_data(v).map(|v| (k.clone(), v)))
.collect::<Result<_, _>>()?,
)),
Value::Error(e) => Value::Error(Arc::new(ErrorObj {
tag: e.tag.clone(),
message: e.message.clone(),
data: e
.data
.iter()
.map(|(k, v)| Ok((copy_data(k)?, copy_data(v)?)))
.collect::<Result<_, &'static str>>()?,
})),
Value::Sexp(s, span) => Value::Sexp(s.clone(), *span),
other => return Err(kind_of(other)),
})
}
fn kind_of(v: &Value) -> &'static str {
match v {
Value::Closure(_) | Value::NativeFn(_) => "function",
Value::Foreign(any) if any.is::<tatara_lisp_eval::vm::run::CompiledClosure>() => "function",
Value::Foreign(_) => "host handle",
other => other.type_name(),
}
}
pub fn install_process_primitives(interp: &mut Interpreter<System>) {
interp.register_fn(
"send",
Arity::Exact(2),
|args: &[Value], sys: &mut System, span| {
let to = match &args[0] {
Value::Int(n) => Pid(*n as u64),
other => {
return Err(tatara_lisp_eval::EvalError::type_mismatch(
"pid (int)",
other.type_name(),
span,
)
.into())
}
};
sys.send(to, args[1].clone());
Ok(args[1].clone())
},
);
interp.register_awaitable_fn(
"receive",
Arity::Exact(0),
|_args: &[Value], sys: &System| sys.has_mail(sys.current()),
|_args: &[Value], sys: &mut System, _span| {
let me = sys.current();
Ok(sys
.boxes
.get_mut(&me)
.and_then(VecDeque::pop_front)
.unwrap_or(Value::Nil))
},
);
interp.register_fn(
"self",
Arity::Exact(0),
|_args: &[Value], sys: &mut System, _span| Ok(Value::Int(sys.current().0 as i64)),
);
}