use std::{collections::BTreeMap, sync::Arc};
use sim_kernel::{Cx, Expr, Object, ObjectCompat, Result, Shape, Symbol, Value};
use sim_lib_dispatch::{
DispatchMethod, GenericFunction, MethodBody, MethodRole, MethodSpecificity,
};
#[sim_citizen_derive::non_citizen(
reason = "dynamic ISLISP instance shell; canonical data is the class symbol and slot table",
kind = "marker"
)]
#[derive(Clone, Debug)]
pub struct IslispObject {
class: Symbol,
slots: BTreeMap<Symbol, Value>,
}
impl IslispObject {
pub fn new(class: Symbol, slots: BTreeMap<Symbol, Value>) -> Self {
Self { class, slots }
}
pub fn class(&self) -> &Symbol {
&self.class
}
pub fn slots(&self) -> &BTreeMap<Symbol, Value> {
&self.slots
}
}
impl Object for IslispObject {
fn display(&self, _cx: &mut Cx) -> Result<String> {
Ok(format!("#<islisp-object {}>", self.class))
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
impl ObjectCompat for IslispObject {
fn as_expr(&self, cx: &mut Cx) -> Result<Expr> {
let slots = self
.slots
.iter()
.map(|(slot, value)| Ok((Expr::Symbol(slot.clone()), value.object().as_expr(cx)?)))
.collect::<Result<Vec<_>>>()?;
Ok(Expr::Map(vec![
(
Expr::Symbol(Symbol::new("class")),
Expr::Symbol(self.class.clone()),
),
(Expr::Symbol(Symbol::new("slots")), Expr::Map(slots)),
]))
}
fn truth(&self, _cx: &mut Cx) -> Result<bool> {
Ok(true)
}
}
pub fn islisp_object_value(
cx: &mut Cx,
class: Symbol,
slots: BTreeMap<Symbol, Value>,
) -> Result<Value> {
cx.factory()
.opaque(Arc::new(IslispObject::new(class, slots)))
}
pub struct IslispGeneric {
generic: GenericFunction,
}
impl IslispGeneric {
pub fn new(name: Symbol) -> Self {
Self {
generic: GenericFunction::new(name),
}
}
pub fn name(&self) -> &Symbol {
self.generic.name()
}
pub fn add_primary_method(
&mut self,
id: Symbol,
parameter_shapes: Vec<Arc<dyn Shape>>,
body: MethodBody,
) -> Result<()> {
self.generic.add_method(DispatchMethod::new(
id,
MethodRole::Primary,
parameter_shapes,
body,
))
}
pub fn select_primary(&self, cx: &mut Cx, args: &[Value]) -> Result<MethodSpecificity> {
self.generic.select_primary(cx, args)
}
pub fn dispatch_order(&self, cx: &mut Cx, args: &[Value]) -> Result<Vec<Symbol>> {
self.generic.dispatch_order(cx, args)
}
pub fn call(&self, cx: &mut Cx, args: &[Value]) -> Result<Value> {
self.generic.call(cx, args)
}
}