1use std::cmp::Ordering;
5use std::sync::Arc;
6
7mod shape_object;
8
9use sim_kernel::{
10 Args, Callable, ClassRef, Cx, Demand, FunctionId, Object, PreparedArgs, RawArgs,
11 ReadConstructor, Result, ShapeId, ShapeRef, Symbol, Value, shape_is_subshape_of,
12};
13
14use crate::base::{Bindings, Shape, ShapeMatch};
15use crate::diagnostics::{callable_mismatch_diagnostic, overload_selection_diagnostic};
16use crate::primitives::OneOfShape;
17pub use shape_object::{ShapeObject, shape_value, shape_value_with_encoding};
18
19pub type NativeFunctionImpl = fn(&mut Cx, &PreparedArgs, Bindings) -> Result<Value>;
24
25#[derive(Clone)]
28pub struct FunctionCase {
29 pub id: sim_kernel::CaseId,
31 pub name: Symbol,
33 pub args: Arc<dyn Shape>,
35 pub result: Option<Arc<dyn Shape>>,
37 pub demand: Vec<sim_kernel::Demand>,
39 pub priority: i32,
41 pub implementation: NativeFunctionImpl,
43}
44
45#[derive(Clone)]
48pub struct FunctionObject {
49 pub id: FunctionId,
51 pub symbol: Symbol,
53 pub cases: Vec<FunctionCase>,
55}
56
57#[derive(Clone)]
59pub struct SelectedCase<'a> {
60 pub case: &'a FunctionCase,
62 pub match_result: ShapeMatch,
64}
65
66impl FunctionObject {
67 pub fn new(id: FunctionId, symbol: Symbol, cases: Vec<FunctionCase>) -> Self {
69 Self { id, symbol, cases }
70 }
71
72 pub fn select_case<'a>(
86 &'a self,
87 cx: &mut Cx,
88 prepared: &PreparedArgs,
89 ) -> Result<SelectedCase<'a>> {
90 let mut matches = Vec::new();
91 let mut diagnostics = Vec::new();
92 let args = cx.new_list(prepared.values().to_vec())?;
93
94 for case in &self.cases {
95 let matched = case.args.check_value(cx, args.clone())?;
96 if matched.accepted {
97 matches.push(SelectedCase {
98 case,
99 match_result: matched,
100 });
101 } else {
102 diagnostics.extend(matched.diagnostics);
103 }
104 }
105
106 if matches.is_empty() {
107 diagnostics.insert(
108 0,
109 overload_selection_diagnostic(&self.symbol, "no matching overload case"),
110 );
111 diagnostics.push(callable_mismatch_diagnostic(
112 &self.symbol,
113 "an applicable case",
114 "rejected arguments",
115 ));
116 return Err(sim_kernel::Error::NoMatchingOverload {
117 function: self.id,
118 diagnostics,
119 });
120 }
121
122 matches.sort_by(|left, right| {
126 right
127 .case
128 .priority
129 .cmp(&left.case.priority)
130 .then_with(|| right.match_result.score.cmp(&left.match_result.score))
131 });
132
133 let mut best = 0usize;
134 for index in 1..matches.len() {
135 if self
136 .compare_cases(cx, &matches[index], &matches[best])?
137 .is_gt()
138 {
139 best = index;
140 }
141 }
142
143 let mut candidates = vec![matches[best].case.id];
147 for index in 0..matches.len() {
148 if index != best
149 && self
150 .compare_cases(cx, &matches[best], &matches[index])?
151 .is_eq()
152 {
153 candidates.push(matches[index].case.id);
154 }
155 }
156 if candidates.len() > 1 {
157 cx.push_diagnostic(overload_selection_diagnostic(
158 &self.symbol,
159 "ambiguous top-ranked overload cases",
160 ));
161 return Err(sim_kernel::Error::AmbiguousOverload {
162 function: self.id,
163 candidates,
164 });
165 }
166
167 Ok(matches[best].clone())
168 }
169
170 fn compare_cases(
178 &self,
179 cx: &mut Cx,
180 a: &SelectedCase<'_>,
181 b: &SelectedCase<'_>,
182 ) -> Result<Ordering> {
183 match a.case.priority.cmp(&b.case.priority) {
184 Ordering::Equal => {}
185 other => return Ok(other),
186 }
187
188 let a_subshape = shape_is_subshape_of(cx, a.case.args.as_ref(), b.case.args.as_ref())?;
189 let b_subshape = shape_is_subshape_of(cx, b.case.args.as_ref(), a.case.args.as_ref())?;
190 match (a_subshape, b_subshape) {
191 (true, false) => return Ok(Ordering::Greater),
192 (false, true) => return Ok(Ordering::Less),
193 _ => {}
194 }
195
196 Ok(a.match_result.score.cmp(&b.match_result.score))
197 }
198
199 pub fn combined_args_shape(&self) -> Option<Arc<dyn Shape>> {
202 match self.cases.as_slice() {
203 [] => None,
204 [one] => Some(one.args.clone()),
205 many => Some(Arc::new(OneOfShape::new(
206 many.iter().map(|case| case.args.clone()).collect(),
207 ))),
208 }
209 }
210
211 pub fn combined_result_shape(&self) -> Option<Arc<dyn Shape>> {
215 let shapes = self
216 .cases
217 .iter()
218 .map(|case| case.result.clone())
219 .collect::<Option<Vec<_>>>()?;
220 match shapes.as_slice() {
221 [] => None,
222 [one] => Some(one.clone()),
223 many => Some(Arc::new(OneOfShape::new(many.to_vec()))),
224 }
225 }
226
227 pub fn declared_demand(&self, index: usize) -> Option<Demand> {
232 let mut declared = None;
233 for case in &self.cases {
234 let case_demand = case.demand.get(index).copied().unwrap_or(Demand::Value);
235 match declared {
236 None => declared = Some(case_demand),
237 Some(existing) if existing == case_demand => {}
238 Some(_) => return Some(Demand::Value),
239 }
240 }
241 declared
242 }
243
244 pub fn declared_demands(&self) -> Vec<Demand> {
247 let max_len = self
248 .cases
249 .iter()
250 .map(|case| case.demand.len())
251 .max()
252 .unwrap_or(0);
253 (0..max_len)
254 .map(|index| self.declared_demand(index).unwrap_or(Demand::Value))
255 .collect()
256 }
257}
258
259impl Object for FunctionObject {
260 fn display(&self, _cx: &mut Cx) -> Result<String> {
261 Ok(format!("#<function {}>", self.symbol))
262 }
263
264 fn as_any(&self) -> &dyn std::any::Any {
265 self
266 }
267}
268
269impl sim_kernel::ObjectCompat for FunctionObject {
270 fn class(&self, cx: &mut Cx) -> Result<ClassRef> {
271 if let Some(value) = cx
272 .registry()
273 .class_by_symbol(&Symbol::qualified("core", "Function"))
274 {
275 return Ok(value.clone());
276 }
277 cx.factory().class_stub(
278 sim_kernel::CORE_FUNCTION_CLASS_ID,
279 Symbol::qualified("core", "Function"),
280 )
281 }
282 fn as_table(&self, cx: &mut Cx) -> Result<Value> {
283 let mut entries = vec![
284 (
285 Symbol::new("symbol"),
286 cx.factory().string(self.symbol.to_string())?,
287 ),
288 (
289 Symbol::new("case-count"),
290 cx.factory().number_literal(
291 Symbol::qualified("numbers", "f64"),
292 self.cases.len().to_string(),
293 )?,
294 ),
295 ];
296 for (index, case) in self.cases.iter().enumerate() {
297 entries.push((
298 Symbol::qualified("case", case.name.name.clone()),
299 cx.factory().string(case.name.to_string())?,
300 ));
301 let args_doc = case.args.describe(cx)?;
302 entries.push((
303 Symbol::qualified("case-args", index.to_string()),
304 cx.factory().string(args_doc.name)?,
305 ));
306 if let Some(result) = &case.result {
307 let result_doc = result.describe(cx)?;
308 entries.push((
309 Symbol::qualified("case-result", index.to_string()),
310 cx.factory().string(result_doc.name)?,
311 ));
312 }
313 if !case.demand.is_empty() {
314 entries.push((
315 Symbol::qualified("case-demand", index.to_string()),
316 cx.factory().list(
317 case.demand
318 .iter()
319 .map(|demand| {
320 let name = match demand {
321 Demand::Never => "never",
322 Demand::Bool => "bool",
323 Demand::Value => "value",
324 Demand::Expr => "expr",
325 Demand::Class(_) => "class",
326 Demand::Shape(_) => "shape",
327 };
328 cx.factory().symbol(Symbol::new(name))
329 })
330 .collect::<Result<Vec<_>>>()?,
331 )?,
332 ));
333 }
334 }
335 cx.factory().table(entries)
336 }
337 fn as_callable(&self) -> Option<&dyn Callable> {
338 Some(self)
339 }
340 fn as_read_constructor(&self) -> Option<&dyn ReadConstructor> {
341 Some(self)
342 }
343}
344
345impl Callable for FunctionObject {
346 fn call(&self, cx: &mut Cx, args: Args) -> Result<Value> {
347 let prepared = PreparedArgs::new(args.into_vec());
348 let selected = self.select_case(cx, &prepared)?;
349 let prepared = refine_prepared_args(cx, &prepared, selected.case)?;
350 let bindings = selected.match_result.captures;
351 let env = bindings.clone().into_child_env(cx)?;
352 let result = cx.with_env(env, |cx| {
353 (selected.case.implementation)(cx, &prepared, bindings)
354 })?;
355
356 if let Some(shape) = &selected.case.result {
357 let matched = shape.check_value(cx, result.clone())?;
358 if !matched.accepted {
359 return Err(sim_kernel::Error::WrongShape {
360 expected: shape.id().unwrap_or(ShapeId(0)),
361 diagnostics: matched.diagnostics,
362 });
363 }
364 }
365
366 Ok(result)
367 }
368
369 fn browse_args_shape(&self, _cx: &mut Cx) -> Result<Option<ShapeRef>> {
370 Ok(self
371 .combined_args_shape()
372 .map(|shape| shape_value(Symbol::qualified(self.symbol.to_string(), "args"), shape)))
373 }
374
375 fn browse_result_shape(&self, _cx: &mut Cx) -> Result<Option<ShapeRef>> {
376 Ok(self
377 .combined_result_shape()
378 .map(|shape| shape_value(Symbol::qualified(self.symbol.to_string(), "result"), shape)))
379 }
380
381 fn call_exprs(&self, cx: &mut Cx, args: RawArgs) -> Result<Value> {
382 let prepared =
383 cx.eval_policy_ref()
384 .prepare_call_args(cx, args, &self.declared_demands())?;
385 let selected = self.select_case(cx, &prepared)?;
386 let prepared = refine_prepared_args(cx, &prepared, selected.case)?;
387 let bindings = selected.match_result.captures;
388 let env = bindings.clone().into_child_env(cx)?;
389 let result = cx.with_env(env, |cx| {
390 (selected.case.implementation)(cx, &prepared, bindings)
391 })?;
392
393 if let Some(shape) = &selected.case.result {
394 let matched = shape.check_value(cx, result.clone())?;
395 if !matched.accepted {
396 return Err(sim_kernel::Error::WrongShape {
397 expected: shape.id().unwrap_or(ShapeId(0)),
398 diagnostics: matched.diagnostics,
399 });
400 }
401 }
402
403 Ok(result)
404 }
405}
406
407fn refine_prepared_args(
408 cx: &mut Cx,
409 prepared: &PreparedArgs,
410 case: &FunctionCase,
411) -> Result<PreparedArgs> {
412 let mut values = Vec::with_capacity(prepared.len());
413 for index in 0..prepared.len() {
414 let value = prepared
415 .get(index)
416 .cloned()
417 .ok_or_else(|| sim_kernel::Error::Eval(format!("missing prepared arg {index}")))?;
418 let demand = case.demand.get(index).copied().unwrap_or(Demand::Value);
419 values.push(force_for_case_demand(cx, value, demand)?);
420 }
421 Ok(PreparedArgs::new(values))
422}
423
424fn force_for_case_demand(cx: &mut Cx, value: Value, demand: Demand) -> Result<Value> {
425 match demand {
426 Demand::Shape(shape_id) => {
427 let value = cx.force(value, Demand::Value)?;
428 let shape_value = cx
429 .registry()
430 .shape_value(shape_id)
431 .cloned()
432 .ok_or_else(|| sim_kernel::Error::WrongShape {
433 expected: shape_id,
434 diagnostics: Vec::new(),
435 })?;
436 let shape = shape_value
437 .object()
438 .as_shape()
439 .ok_or(sim_kernel::Error::TypeMismatch {
440 expected: "shape object",
441 found: "non-shape object",
442 })?;
443 let matched = shape.check_value(cx, value.clone())?;
444 if matched.accepted {
445 Ok(value)
446 } else {
447 Err(sim_kernel::Error::WrongShape {
448 expected: shape_id,
449 diagnostics: matched.diagnostics,
450 })
451 }
452 }
453 other => cx.force(value, other),
454 }
455}
456
457impl ReadConstructor for FunctionObject {
458 fn symbol(&self) -> Symbol {
459 self.symbol.clone()
460 }
461
462 fn args_shape(&self, cx: &mut Cx) -> Result<ShapeRef> {
463 match self.combined_args_shape() {
464 Some(shape) => Ok(shape_value(
465 Symbol::qualified(self.symbol.to_string(), "args-shape"),
466 shape,
467 )),
468 None => cx.factory().nil(),
469 }
470 }
471
472 fn construct_read(&self, cx: &mut Cx, args: Vec<Value>) -> Result<Value> {
473 self.call(cx, Args::new(args))
474 }
475}
476
477pub fn overload(cx: &mut Cx, functions: Vec<FunctionObject>) -> Result<FunctionObject> {
482 let mut cases = Vec::new();
483 let mut names = Vec::new();
484
485 for function in functions {
486 names.push(function.symbol.to_string());
487 cases.extend(function.cases);
488 }
489
490 let symbol = Symbol::new(format!("overload:{}", names.join("+")));
491 Ok(FunctionObject {
492 id: cx.registry_mut().fresh_function_id(),
493 symbol,
494 cases,
495 })
496}
497
498pub fn function_cases(function: &FunctionObject) -> &[FunctionCase] {
500 &function.cases
501}
502
503pub fn case_shape(case: &FunctionCase) -> &dyn Shape {
505 case.args.as_ref()
506}
507
508pub fn case_result_shape(case: &FunctionCase) -> Option<&dyn Shape> {
510 case.result.as_deref()
511}