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