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