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#[derive(Clone)]
16pub struct MemberFunction {
17 pub class_symbol: Symbol,
19 pub symbol: Symbol,
21 pub field: Symbol,
23}
24
25impl MemberFunction {
26 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#[derive(Clone)]
109pub struct NativeClass {
110 pub id: ClassId,
112 pub symbol: Symbol,
114 pub constructor: FunctionObject,
116 pub read_constructor: Option<FunctionObject>,
119 pub instance_shape: Option<Arc<dyn Shape>>,
121 pub parent_symbols: Vec<Symbol>,
123 pub members: Vec<MemberFunction>,
125}
126
127impl NativeClass {
128 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 pub fn with_read_constructor(mut self, read_constructor: Option<FunctionObject>) -> Self {
154 self.read_constructor = read_constructor;
155 self
156 }
157
158 pub fn with_parents(mut self, parent_symbols: Vec<Symbol>) -> Self {
160 self.parent_symbols = parent_symbols;
161 self
162 }
163
164 pub fn constructor(&self) -> &FunctionObject {
166 &self.constructor
167 }
168
169 pub fn member_names(&self) -> impl Iterator<Item = &Symbol> {
171 self.members.iter().map(|member| &member.field)
172 }
173
174 pub fn member_functions(&self) -> &[MemberFunction] {
176 &self.members
177 }
178
179 pub fn member_function(&self, field: &Symbol) -> Option<&MemberFunction> {
181 self.members.iter().find(|member| &member.field == field)
182 }
183
184 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#[derive(Clone)]
360pub struct ClassInstance {
361 pub class_symbol: Symbol,
363 pub constructor_args: Vec<sim_kernel::Expr>,
365 pub fields: Vec<(Symbol, Value)>,
367}
368
369impl ClassInstance {
370 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
431pub fn constructor_function(class: &NativeClass) -> &FunctionObject {
433 &class.constructor
434}