1use std::sync::Arc;
5
6mod demand;
7mod select;
8mod shape_object;
9
10#[cfg(test)]
11mod tests;
12
13use sim_kernel::{
14 Args, Callable, ClassRef, Cx, Demand, FunctionId, Object, PreparedArgs, RawArgs,
15 ReadConstructor, Result, ShapeId, ShapeRef, Symbol, Value,
16};
17
18use crate::base::{Bindings, Shape, ShapeMatch};
19use crate::primitives::OneOfShape;
20pub use shape_object::{ShapeObject, shape_value, shape_value_with_encoding};
21
22pub type NativeFunctionImpl = fn(&mut Cx, &PreparedArgs, Bindings) -> Result<Value>;
27
28#[derive(Clone)]
31pub struct FunctionCase {
32 pub id: sim_kernel::CaseId,
34 pub name: Symbol,
36 pub args: Arc<dyn Shape>,
38 pub result: Option<Arc<dyn Shape>>,
40 pub demand: Vec<sim_kernel::Demand>,
42 pub priority: i32,
44 pub implementation: NativeFunctionImpl,
46}
47
48#[derive(Clone)]
51pub struct FunctionObject {
52 pub id: FunctionId,
54 pub symbol: Symbol,
56 pub cases: Vec<FunctionCase>,
58}
59
60#[derive(Clone)]
62pub struct SelectedCase<'a> {
63 pub case: &'a FunctionCase,
65 pub match_result: ShapeMatch,
67}
68
69impl FunctionObject {
70 pub fn new(id: FunctionId, symbol: Symbol, cases: Vec<FunctionCase>) -> Self {
72 Self { id, symbol, cases }
73 }
74
75 pub fn combined_args_shape(&self) -> Option<Arc<dyn Shape>> {
78 match self.cases.as_slice() {
79 [] => None,
80 [one] => Some(one.args.clone()),
81 many => Some(Arc::new(OneOfShape::new(
82 many.iter().map(|case| case.args.clone()).collect(),
83 ))),
84 }
85 }
86
87 pub fn combined_result_shape(&self) -> Option<Arc<dyn Shape>> {
91 let shapes = self
92 .cases
93 .iter()
94 .map(|case| case.result.clone())
95 .collect::<Option<Vec<_>>>()?;
96 match shapes.as_slice() {
97 [] => None,
98 [one] => Some(one.clone()),
99 many => Some(Arc::new(OneOfShape::new(many.to_vec()))),
100 }
101 }
102
103 pub fn declared_demand(&self, index: usize) -> Option<Demand> {
108 let mut declared = None;
109 for case in &self.cases {
110 let case_demand = case.demand.get(index).copied().unwrap_or(Demand::Value);
111 match declared {
112 None => declared = Some(case_demand),
113 Some(existing) if existing == case_demand => {}
114 Some(_) => return Some(Demand::Value),
115 }
116 }
117 declared
118 }
119
120 pub fn declared_demands(&self) -> Vec<Demand> {
123 let max_len = self
124 .cases
125 .iter()
126 .map(|case| case.demand.len())
127 .max()
128 .unwrap_or(0);
129 (0..max_len)
130 .map(|index| self.declared_demand(index).unwrap_or(Demand::Value))
131 .collect()
132 }
133}
134
135impl Object for FunctionObject {
136 fn display(&self, _cx: &mut Cx) -> Result<String> {
137 Ok(format!("#<function {}>", self.symbol))
138 }
139
140 fn as_any(&self) -> &dyn std::any::Any {
141 self
142 }
143}
144
145impl sim_kernel::ObjectCompat for FunctionObject {
146 fn class(&self, cx: &mut Cx) -> Result<ClassRef> {
147 if let Some(value) = cx
148 .registry()
149 .class_by_symbol(&Symbol::qualified("core", "Function"))
150 {
151 return Ok(value.clone());
152 }
153 cx.factory().class_stub(
154 sim_kernel::CORE_FUNCTION_CLASS_ID,
155 Symbol::qualified("core", "Function"),
156 )
157 }
158 fn as_table(&self, cx: &mut Cx) -> Result<Value> {
159 let mut entries = vec![
160 (
161 Symbol::new("symbol"),
162 cx.factory().string(self.symbol.to_string())?,
163 ),
164 (
165 Symbol::new("case-count"),
166 cx.factory().number_literal(
167 Symbol::qualified("numbers", "f64"),
168 self.cases.len().to_string(),
169 )?,
170 ),
171 ];
172 for (index, case) in self.cases.iter().enumerate() {
173 entries.push((
174 Symbol::qualified("case", case.name.name.clone()),
175 cx.factory().string(case.name.to_string())?,
176 ));
177 let args_doc = case.args.describe(cx)?;
178 entries.push((
179 Symbol::qualified("case-args", index.to_string()),
180 cx.factory().string(args_doc.name)?,
181 ));
182 if let Some(result) = &case.result {
183 let result_doc = result.describe(cx)?;
184 entries.push((
185 Symbol::qualified("case-result", index.to_string()),
186 cx.factory().string(result_doc.name)?,
187 ));
188 }
189 if !case.demand.is_empty() {
190 entries.push((
191 Symbol::qualified("case-demand", index.to_string()),
192 cx.factory().list(
193 case.demand
194 .iter()
195 .map(|demand| {
196 let name = match demand {
197 Demand::Never => "never",
198 Demand::Bool => "bool",
199 Demand::Value => "value",
200 Demand::Expr => "expr",
201 Demand::Class(_) => "class",
202 Demand::Shape(_) => "shape",
203 };
204 cx.factory().symbol(Symbol::new(name))
205 })
206 .collect::<Result<Vec<_>>>()?,
207 )?,
208 ));
209 }
210 }
211 cx.factory().table(entries)
212 }
213 fn as_callable(&self) -> Option<&dyn Callable> {
214 Some(self)
215 }
216 fn as_read_constructor(&self) -> Option<&dyn ReadConstructor> {
217 Some(self)
218 }
219}
220
221impl Callable for FunctionObject {
222 fn call(&self, cx: &mut Cx, args: Args) -> Result<Value> {
223 let prepared = PreparedArgs::new(args.into_vec());
224 let selected = self.select_case(cx, &prepared)?;
225 let prepared = refine_prepared_args(cx, &prepared, selected.case)?;
226 let bindings = selected.match_result.captures;
227 let env = bindings.clone().into_child_env(cx)?;
228 let result = cx.with_env(env, |cx| {
229 (selected.case.implementation)(cx, &prepared, bindings)
230 })?;
231
232 if let Some(shape) = &selected.case.result {
233 let matched = shape.check_value(cx, result.clone())?;
234 if !matched.accepted {
235 return Err(sim_kernel::Error::WrongShape {
236 expected: shape.id().unwrap_or(ShapeId(0)),
237 diagnostics: matched.diagnostics,
238 });
239 }
240 }
241
242 Ok(result)
243 }
244
245 fn browse_args_shape(&self, _cx: &mut Cx) -> Result<Option<ShapeRef>> {
246 Ok(self
247 .combined_args_shape()
248 .map(|shape| shape_value(Symbol::qualified(self.symbol.to_string(), "args"), shape)))
249 }
250
251 fn browse_result_shape(&self, _cx: &mut Cx) -> Result<Option<ShapeRef>> {
252 Ok(self
253 .combined_result_shape()
254 .map(|shape| shape_value(Symbol::qualified(self.symbol.to_string(), "result"), shape)))
255 }
256
257 fn call_exprs(&self, cx: &mut Cx, args: RawArgs) -> Result<Value> {
258 self.call_exprs_with_demands(cx, args)
259 }
260}
261
262fn refine_prepared_args(
263 cx: &mut Cx,
264 prepared: &PreparedArgs,
265 case: &FunctionCase,
266) -> Result<PreparedArgs> {
267 let mut values = Vec::with_capacity(prepared.len());
268 for index in 0..prepared.len() {
269 let value = prepared
270 .get(index)
271 .cloned()
272 .ok_or_else(|| sim_kernel::Error::Eval(format!("missing prepared arg {index}")))?;
273 let demand = case.demand.get(index).copied().unwrap_or(Demand::Value);
274 values.push(force_for_case_demand(cx, value, demand)?);
275 }
276 Ok(PreparedArgs::new(values))
277}
278
279fn force_for_case_demand(cx: &mut Cx, value: Value, demand: Demand) -> Result<Value> {
280 match demand {
281 Demand::Shape(shape_id) => {
282 let value = cx.force(value, Demand::Value)?;
283 let shape_value = cx
284 .registry()
285 .shape_value(shape_id)
286 .cloned()
287 .ok_or_else(|| sim_kernel::Error::WrongShape {
288 expected: shape_id,
289 diagnostics: Vec::new(),
290 })?;
291 let shape = shape_value
292 .object()
293 .as_shape()
294 .ok_or(sim_kernel::Error::TypeMismatch {
295 expected: "shape object",
296 found: "non-shape object",
297 })?;
298 let matched = shape.check_value(cx, value.clone())?;
299 if matched.accepted {
300 Ok(value)
301 } else {
302 Err(sim_kernel::Error::WrongShape {
303 expected: shape_id,
304 diagnostics: matched.diagnostics,
305 })
306 }
307 }
308 other => cx.force(value, other),
309 }
310}
311
312impl ReadConstructor for FunctionObject {
313 fn symbol(&self) -> Symbol {
314 self.symbol.clone()
315 }
316
317 fn args_shape(&self, cx: &mut Cx) -> Result<ShapeRef> {
318 match self.combined_args_shape() {
319 Some(shape) => Ok(shape_value(
320 Symbol::qualified(self.symbol.to_string(), "args-shape"),
321 shape,
322 )),
323 None => cx.factory().nil(),
324 }
325 }
326
327 fn construct_read(&self, cx: &mut Cx, args: Vec<Value>) -> Result<Value> {
328 self.call(cx, Args::new(args))
329 }
330}
331
332pub fn overload(cx: &mut Cx, functions: Vec<FunctionObject>) -> Result<FunctionObject> {
337 let mut cases = Vec::new();
338 let mut names = Vec::new();
339
340 for function in functions {
341 names.push(function.symbol.to_string());
342 cases.extend(function.cases);
343 }
344
345 let symbol = Symbol::new(format!("overload:{}", names.join("+")));
346 Ok(FunctionObject {
347 id: cx.registry_mut().fresh_function_id(),
348 symbol,
349 cases,
350 })
351}
352
353pub fn function_cases(function: &FunctionObject) -> &[FunctionCase] {
355 &function.cases
356}
357
358pub fn case_shape(case: &FunctionCase) -> &dyn Shape {
360 case.args.as_ref()
361}
362
363pub fn case_result_shape(case: &FunctionCase) -> Option<&dyn Shape> {
365 case.result.as_deref()
366}