Skip to main content

sim/classes/
model.rs

1use std::sync::Arc;
2
3use sim_kernel::{
4    Args, Callable, Class, ClassId, ClassRef, Cx, DefaultFactory, Factory, Object, ObjectEncode,
5    ObjectEncoding, ReadConstructorRef, Result, ShapeRef, Symbol, TableRef, Value,
6};
7use sim_shape::{ObjectExpr, OneOfShape, Shape};
8
9use crate::{
10    functions::{FunctionObject, empty_member_table, empty_shape_ref},
11    shapes::shape_value,
12};
13
14/// Callable that reads one field out of an instance of a native class.
15#[derive(Clone)]
16pub struct MemberFunction {
17    /// Symbol of the class this member belongs to.
18    pub class_symbol: Symbol,
19    /// Qualified symbol under which the member accessor is registered.
20    pub symbol: Symbol,
21    /// Field name the accessor reads from an instance.
22    pub field: Symbol,
23}
24
25impl MemberFunction {
26    /// Creates a member accessor for `field` on the class named `class_symbol`.
27    pub fn new(class_symbol: Symbol, field: Symbol) -> Self {
28        Self {
29            symbol: Symbol::qualified(class_symbol.to_string(), field.name.clone()),
30            class_symbol,
31            field,
32        }
33    }
34}
35
36impl Object for MemberFunction {
37    fn display(&self, _cx: &mut Cx) -> Result<String> {
38        Ok(format!("#<member {} {}>", self.class_symbol, self.field))
39    }
40
41    fn as_any(&self) -> &dyn std::any::Any {
42        self
43    }
44}
45
46impl sim_kernel::ObjectCompat for MemberFunction {
47    fn class(&self, cx: &mut Cx) -> Result<ClassRef> {
48        if let Some(value) = cx
49            .registry()
50            .class_by_symbol(&Symbol::qualified("core", "Function"))
51        {
52            return Ok(value.clone());
53        }
54        cx.factory().class_stub(
55            sim_kernel::CORE_FUNCTION_CLASS_ID,
56            Symbol::qualified("core", "Function"),
57        )
58    }
59    fn as_expr(&self, _cx: &mut Cx) -> Result<sim_kernel::Expr> {
60        Ok(sim_kernel::Expr::Symbol(self.symbol.clone()))
61    }
62    fn as_callable(&self) -> Option<&dyn Callable> {
63        Some(self)
64    }
65}
66
67impl Callable for MemberFunction {
68    fn call(&self, cx: &mut Cx, args: Args) -> Result<Value> {
69        let args = args.into_vec();
70        let [instance] = args.as_slice() else {
71            return Err(sim_kernel::Error::Eval(format!(
72                "member {} expects exactly one instance argument",
73                self.symbol
74            )));
75        };
76
77        let expr = instance.object().as_expr(cx)?;
78        let entries = if let Some(object) = ObjectExpr::parse(&expr) {
79            object.fields
80        } else if let sim_kernel::Expr::Map(entries) = expr {
81            entries
82                .into_iter()
83                .filter_map(|(key, value)| match key {
84                    sim_kernel::Expr::Symbol(symbol) => Some((symbol, value)),
85                    _ => None,
86                })
87                .collect()
88        } else {
89            return Err(sim_kernel::Error::TypeMismatch {
90                expected: "map/object",
91                found: "non-map",
92            });
93        };
94
95        entries
96            .into_iter()
97            .find_map(|(key, value)| (key == self.field).then_some(value))
98            .map(|expr| cx.factory().expr(expr))
99            .transpose()?
100            .ok_or_else(|| sim_kernel::Error::UnknownSymbol {
101                symbol: self.field.clone(),
102            })
103    }
104}
105
106/// Host-defined class: a constructor, optional shapes, parents, and members
107/// implementing the kernel `Class` and `Callable` contracts.
108#[derive(Clone)]
109pub struct NativeClass {
110    /// Stable id assigned to the class.
111    pub id: ClassId,
112    /// Symbol the class is registered under.
113    pub symbol: Symbol,
114    /// Constructor invoked when the class is called.
115    pub constructor: FunctionObject,
116    /// Constructor used for read-construct literals, if distinct from the call
117    /// constructor.
118    pub read_constructor: Option<FunctionObject>,
119    /// Shape that instances of the class are expected to satisfy.
120    pub instance_shape: Option<Arc<dyn Shape>>,
121    /// Symbols of the class's parent classes.
122    pub parent_symbols: Vec<Symbol>,
123    /// Member accessors exposed by the class.
124    pub members: Vec<MemberFunction>,
125}
126
127impl NativeClass {
128    /// Creates a class with the given id, symbol, constructor, instance shape,
129    /// and member fields.
130    pub fn new(
131        id: ClassId,
132        symbol: Symbol,
133        constructor: FunctionObject,
134        instance_shape: Option<Arc<dyn Shape>>,
135        member_fields: Vec<Symbol>,
136    ) -> Self {
137        let members = member_fields
138            .into_iter()
139            .map(|field| MemberFunction::new(symbol.clone(), field))
140            .collect();
141        Self {
142            id,
143            symbol,
144            read_constructor: Some(constructor.clone()),
145            constructor,
146            instance_shape,
147            parent_symbols: Vec::new(),
148            members,
149        }
150    }
151
152    /// Sets the read-construct constructor and returns the updated class.
153    pub fn with_read_constructor(mut self, read_constructor: Option<FunctionObject>) -> Self {
154        self.read_constructor = read_constructor;
155        self
156    }
157
158    /// Sets the parent symbols and returns the updated class.
159    pub fn with_parents(mut self, parent_symbols: Vec<Symbol>) -> Self {
160        self.parent_symbols = parent_symbols;
161        self
162    }
163
164    /// Returns the class's call constructor.
165    pub fn constructor(&self) -> &FunctionObject {
166        &self.constructor
167    }
168
169    /// Iterates over the field names of the class's members.
170    pub fn member_names(&self) -> impl Iterator<Item = &Symbol> {
171        self.members.iter().map(|member| &member.field)
172    }
173
174    /// Returns the class's member accessors.
175    pub fn member_functions(&self) -> &[MemberFunction] {
176        &self.members
177    }
178
179    /// Looks up a member accessor by field name.
180    pub fn member_function(&self, field: &Symbol) -> Option<&MemberFunction> {
181        self.members.iter().find(|member| &member.field == field)
182    }
183
184    /// Builds the combined argument shape covering all constructor cases.
185    pub fn constructor_shape_arc(&self) -> Option<Arc<dyn Shape>> {
186        match self.constructor.cases.as_slice() {
187            [] => None,
188            [one] => Some(one.args.clone()),
189            many => Some(Arc::new(OneOfShape::new(
190                many.iter().map(|case| case.args.clone()).collect(),
191            ))),
192        }
193    }
194}
195
196impl Object for NativeClass {
197    fn display(&self, _cx: &mut Cx) -> Result<String> {
198        Ok(format!("#<class {}>", self.symbol))
199    }
200
201    fn as_any(&self) -> &dyn std::any::Any {
202        self
203    }
204}
205
206impl sim_kernel::ObjectCompat for NativeClass {
207    fn class(&self, cx: &mut Cx) -> Result<ClassRef> {
208        if let Some(value) = cx
209            .registry()
210            .class_by_symbol(&Symbol::qualified("core", "Class"))
211        {
212            return Ok(value.clone());
213        }
214        cx.factory().class_stub(
215            sim_kernel::CORE_CLASS_CLASS_ID,
216            Symbol::qualified("core", "Class"),
217        )
218    }
219    fn as_expr(&self, _cx: &mut Cx) -> Result<sim_kernel::Expr> {
220        Ok(sim_kernel::Expr::Symbol(self.symbol.clone()))
221    }
222    fn as_table(&self, cx: &mut Cx) -> Result<Value> {
223        let mut entries = vec![
224            (
225                Symbol::new("symbol"),
226                cx.factory().string(self.symbol.to_string())?,
227            ),
228            (
229                Symbol::new("constructor"),
230                cx.factory().string(self.constructor.symbol.to_string())?,
231            ),
232            (
233                Symbol::new("member-count"),
234                cx.factory().number_literal(
235                    Symbol::qualified("numbers", "f64"),
236                    self.members.len().to_string(),
237                )?,
238            ),
239        ];
240        if let Some(shape) = self.constructor_shape_arc() {
241            let doc = shape.describe(cx)?;
242            entries.push((
243                Symbol::new("constructor-shape"),
244                cx.factory().string(doc.name)?,
245            ));
246        }
247        if let Some(shape) = &self.instance_shape {
248            let doc = shape.describe(cx)?;
249            entries.push((
250                Symbol::new("instance-shape"),
251                cx.factory().string(doc.name)?,
252            ));
253        }
254        for member in &self.members {
255            entries.push((
256                Symbol::qualified("member", member.field.name.clone()),
257                cx.factory().string(member.symbol.to_string())?,
258            ));
259        }
260        cx.factory().table(entries)
261    }
262    fn as_callable(&self) -> Option<&dyn Callable> {
263        Some(self)
264    }
265    fn as_class(&self) -> Option<&dyn Class> {
266        Some(self)
267    }
268}
269
270impl Callable for NativeClass {
271    fn call(&self, cx: &mut Cx, args: Args) -> Result<Value> {
272        self.constructor.call(cx, args)
273    }
274
275    fn browse_args_shape(&self, cx: &mut Cx) -> Result<Option<ShapeRef>> {
276        shape_option(self.constructor_shape(cx)?)
277    }
278
279    fn browse_result_shape(&self, cx: &mut Cx) -> Result<Option<ShapeRef>> {
280        shape_option(self.instance_shape(cx)?)
281    }
282}
283
284fn shape_option(value: ShapeRef) -> Result<Option<ShapeRef>> {
285    if value.object().as_shape().is_some() {
286        Ok(Some(value))
287    } else {
288        Ok(None)
289    }
290}
291
292impl Class for NativeClass {
293    fn id(&self) -> ClassId {
294        self.id
295    }
296
297    fn symbol(&self) -> Symbol {
298        self.symbol.clone()
299    }
300
301    fn parents(&self, cx: &mut Cx) -> Result<Vec<ClassRef>> {
302        Ok(self
303            .parent_symbols
304            .iter()
305            .filter_map(|symbol| cx.registry().class_by_symbol(symbol).cloned())
306            .collect())
307    }
308
309    fn constructor_shape(&self, cx: &mut Cx) -> Result<ShapeRef> {
310        match self.constructor_shape_arc() {
311            Some(shape) => Ok(shape_value(
312                Symbol::qualified(self.symbol.to_string(), "constructor-shape"),
313                shape,
314            )),
315            None => empty_shape_ref(cx),
316        }
317    }
318
319    fn instance_shape(&self, cx: &mut Cx) -> Result<ShapeRef> {
320        match &self.instance_shape {
321            Some(shape) => Ok(shape_value(
322                Symbol::qualified(self.symbol.to_string(), "instance-shape"),
323                shape.clone(),
324            )),
325            None => empty_shape_ref(cx),
326        }
327    }
328
329    fn read_constructor(&self, _cx: &mut Cx) -> Result<Option<ReadConstructorRef>> {
330        Ok(self.read_constructor.as_ref().map(|read| {
331            DefaultFactory
332                .opaque(Arc::new(read.clone()))
333                .expect("read constructor should be boxable")
334        }))
335    }
336
337    fn members(&self, cx: &mut Cx) -> Result<TableRef> {
338        if self.members.is_empty() {
339            return empty_member_table(cx);
340        }
341        cx.factory().table(
342            self.members
343                .iter()
344                .map(|member| {
345                    (
346                        member.field.clone(),
347                        DefaultFactory
348                            .opaque(Arc::new(member.clone()))
349                            .expect("member function should be boxable"),
350                    )
351                })
352                .collect(),
353        )
354    }
355}
356
357/// Instance of a native class: its class symbol, the constructor arguments it
358/// was built from, and its field values.
359#[derive(Clone)]
360pub struct ClassInstance {
361    /// Symbol of the class this is an instance of.
362    pub class_symbol: Symbol,
363    /// Expressions the instance was constructed from, used for re-encoding.
364    pub constructor_args: Vec<sim_kernel::Expr>,
365    /// Field values held by the instance.
366    pub fields: Vec<(Symbol, Value)>,
367}
368
369impl ClassInstance {
370    /// Creates an instance from its class symbol, constructor arguments, and
371    /// field values.
372    pub fn new(
373        class_symbol: Symbol,
374        constructor_args: Vec<sim_kernel::Expr>,
375        fields: Vec<(Symbol, Value)>,
376    ) -> Self {
377        Self {
378            class_symbol,
379            constructor_args,
380            fields,
381        }
382    }
383}
384
385impl Object for ClassInstance {
386    fn display(&self, _cx: &mut Cx) -> Result<String> {
387        Ok(format!("#<instance {}>", self.class_symbol))
388    }
389
390    fn as_any(&self) -> &dyn std::any::Any {
391        self
392    }
393}
394
395impl sim_kernel::ObjectCompat for ClassInstance {
396    fn class(&self, cx: &mut Cx) -> Result<ClassRef> {
397        if let Some(value) = cx.registry().class_by_symbol(&self.class_symbol) {
398            return Ok(value.clone());
399        }
400        cx.factory()
401            .class_stub(ClassId(0), self.class_symbol.clone())
402    }
403    fn as_expr(&self, cx: &mut Cx) -> Result<sim_kernel::Expr> {
404        Ok(ObjectExpr {
405            class: self.class_symbol.clone(),
406            fields: self
407                .fields
408                .iter()
409                .map(|(key, value)| Ok((key.clone(), value.object().as_expr(cx)?)))
410                .collect::<Result<Vec<_>>>()?,
411        }
412        .to_expr())
413    }
414    fn as_table(&self, cx: &mut Cx) -> Result<Value> {
415        cx.factory().table(self.fields.clone())
416    }
417    fn as_object_encoder(&self) -> Option<&dyn ObjectEncode> {
418        Some(self)
419    }
420}
421
422impl ObjectEncode for ClassInstance {
423    fn object_encoding(&self, _cx: &mut Cx) -> Result<ObjectEncoding> {
424        Ok(ObjectEncoding::Constructor {
425            class: self.class_symbol.clone(),
426            args: self.constructor_args.clone(),
427        })
428    }
429}
430
431/// Borrows the constructor function of a native class.
432pub fn constructor_function(class: &NativeClass) -> &FunctionObject {
433    &class.constructor
434}