Skip to main content

sim_lib_lang_islisp/
generic.rs

1use std::{collections::BTreeMap, sync::Arc};
2
3use sim_kernel::{Cx, Expr, Object, ObjectCompat, Result, Shape, Symbol, Value};
4use sim_lib_dispatch::{
5    DispatchMethod, GenericFunction, MethodBody, MethodRole, MethodSpecificity,
6};
7
8#[sim_citizen_derive::non_citizen(
9    reason = "dynamic ISLISP instance shell; canonical data is the class symbol and slot table",
10    kind = "marker",
11    descriptor = "islisp/Object"
12)]
13/// Runtime shell for an ISLISP instance: a class symbol plus a slot table.
14///
15/// A kernel [`Object`] that renders to the shared [`Expr`] graph; the canonical
16/// data is the class symbol and slots, not this Rust struct.
17#[derive(Clone, Debug)]
18pub struct IslispObject {
19    class: Symbol,
20    slots: BTreeMap<Symbol, Value>,
21}
22
23impl IslispObject {
24    /// Builds an instance from its class symbol and slot table.
25    pub fn new(class: Symbol, slots: BTreeMap<Symbol, Value>) -> Self {
26        Self { class, slots }
27    }
28
29    /// Returns the class symbol this instance was created against.
30    pub fn class(&self) -> &Symbol {
31        &self.class
32    }
33
34    /// Returns the instance slot table keyed by slot symbol.
35    pub fn slots(&self) -> &BTreeMap<Symbol, Value> {
36        &self.slots
37    }
38}
39
40impl Object for IslispObject {
41    fn display(&self, _cx: &mut Cx) -> Result<String> {
42        Ok(format!("#<islisp-object {}>", self.class))
43    }
44
45    fn as_any(&self) -> &dyn std::any::Any {
46        self
47    }
48}
49
50impl ObjectCompat for IslispObject {
51    fn as_expr(&self, cx: &mut Cx) -> Result<Expr> {
52        let slots = self
53            .slots
54            .iter()
55            .map(|(slot, value)| Ok((Expr::Symbol(slot.clone()), value.object().as_expr(cx)?)))
56            .collect::<Result<Vec<_>>>()?;
57        Ok(Expr::Map(vec![
58            (
59                Expr::Symbol(Symbol::new("class")),
60                Expr::Symbol(self.class.clone()),
61            ),
62            (Expr::Symbol(Symbol::new("slots")), Expr::Map(slots)),
63        ]))
64    }
65
66    fn truth(&self, _cx: &mut Cx) -> Result<bool> {
67        Ok(true)
68    }
69}
70
71/// Wraps an [`IslispObject`] as an opaque kernel [`Value`].
72///
73/// Allocates the instance through the context factory so it participates in the
74/// runtime like any other object value.
75pub fn islisp_object_value(
76    cx: &mut Cx,
77    class: Symbol,
78    slots: BTreeMap<Symbol, Value>,
79) -> Result<Value> {
80    cx.factory()
81        .opaque(Arc::new(IslispObject::new(class, slots)))
82}
83
84/// ISLISP generic function backed by the shared dispatch organ.
85///
86/// Thin profile wrapper over [`GenericFunction`]: it adds the ISLISP surface
87/// shape, while method selection and shape-based dispatch remain dispatch-organ
88/// behavior rather than kernel contract.
89pub struct IslispGeneric {
90    generic: GenericFunction,
91}
92
93impl IslispGeneric {
94    /// Creates an empty generic function with the given name.
95    pub fn new(name: Symbol) -> Self {
96        Self {
97            generic: GenericFunction::new(name),
98        }
99    }
100
101    /// Returns the generic function's name symbol.
102    pub fn name(&self) -> &Symbol {
103        self.generic.name()
104    }
105
106    /// Attaches a primary method keyed by its identifier and parameter shapes.
107    pub fn add_primary_method(
108        &mut self,
109        id: Symbol,
110        parameter_shapes: Vec<Arc<dyn Shape>>,
111        body: MethodBody,
112    ) -> Result<()> {
113        self.generic.add_method(DispatchMethod::new(
114            id,
115            MethodRole::Primary,
116            parameter_shapes,
117            body,
118        ))
119    }
120
121    /// Selects the most specific primary method for the given arguments.
122    pub fn select_primary(&self, cx: &mut Cx, args: &[Value]) -> Result<MethodSpecificity> {
123        self.generic.select_primary(cx, args)
124    }
125
126    /// Returns the applicable primary methods ordered from most to least specific.
127    pub fn dispatch_order(&self, cx: &mut Cx, args: &[Value]) -> Result<Vec<Symbol>> {
128        self.generic.dispatch_order(cx, args)
129    }
130
131    /// Dispatches the generic on the given arguments and returns the result.
132    pub fn call(&self, cx: &mut Cx, args: &[Value]) -> Result<Value> {
133        self.generic.call(cx, args)
134    }
135}