1use std::{any::Any, sync::Arc};
5
6use sim_kernel::{
7 Args, Callable, ClassRef, Cx, DefaultFactory, Error, Expr, Factory, NumberValue, Object,
8 ObjectEncode, Result, ShapeRef, Symbol, Value,
9};
10use sim_lib_numbers_cas::{CasExpr, cas_expr_to_surface_expr, free_vars, value_to_cas_expr};
11use sim_lib_numbers_cas_eval::eval_cas;
12use sim_shape::{AnyShape, ListShape, Shape, shape_value};
13
14use super::domain::{func_class_symbol, func_domain_symbol};
15use super::function::{child_env_with_args, vars_expr};
16
17mod ops;
18
19pub(crate) use ops::register_value_ops;
20
21pub type NativeFn = Arc<dyn Fn(&mut Cx, &[Value]) -> Result<Value> + Send + Sync>;
24
25#[derive(Clone, Default)]
27pub struct FuncMetadata {
28 pub source: Option<Symbol>,
31 pub differentiator_hint: Option<Symbol>,
34 pub payload: Option<Value>,
36}
37
38#[derive(Clone)]
39enum FuncBody {
40 Symbolic(CasExpr),
41 Native {
42 native: NativeFn,
43 symbolic_status: SymbolicStatus,
44 },
45 Dual {
46 cas: CasExpr,
47 native: NativeFn,
48 },
49}
50
51#[derive(Clone, Debug, PartialEq, Eq)]
54pub enum SymbolicStatus {
55 Available,
57 ProvidedByHint,
60 Lost {
62 reason: Symbol,
64 },
65}
66
67impl SymbolicStatus {
68 pub fn native_only() -> Self {
70 Self::Lost {
71 reason: Symbol::qualified("numbers/func", "native-only"),
72 }
73 }
74
75 pub fn mixed_native() -> Self {
78 Self::Lost {
79 reason: Symbol::qualified("numbers/func", "mixed-native"),
80 }
81 }
82
83 fn status_symbol(&self) -> Symbol {
84 match self {
85 Self::Available => Symbol::qualified("numbers/func", "available"),
86 Self::ProvidedByHint => Symbol::qualified("numbers/func", "provided-by-hint"),
87 Self::Lost { .. } => Symbol::qualified("numbers/func", "lost"),
88 }
89 }
90
91 fn reason(&self) -> Option<&Symbol> {
92 match self {
93 Self::Lost { reason } => Some(reason),
94 _ => None,
95 }
96 }
97}
98
99#[derive(Clone)]
102pub struct Func {
103 pub vars: Vec<Symbol>,
105 body: FuncBody,
106 pub metadata: FuncMetadata,
108}
109
110impl Func {
111 fn new(vars: Vec<Symbol>, body: FuncBody, metadata: FuncMetadata) -> Self {
112 Self {
113 vars,
114 body,
115 metadata,
116 }
117 }
118
119 pub fn symbolic(vars: Vec<Symbol>, body_cas: CasExpr) -> Self {
121 Self::symbolic_with(vars, body_cas, FuncMetadata::default())
122 }
123
124 pub fn symbolic_with(vars: Vec<Symbol>, body_cas: CasExpr, metadata: FuncMetadata) -> Self {
126 Self::new(vars, FuncBody::Symbolic(body_cas), metadata)
127 }
128
129 pub fn native(vars: Vec<Symbol>, body_native: NativeFn) -> Self {
147 Self::native_with(vars, body_native, FuncMetadata::default())
148 }
149
150 pub fn native_with(vars: Vec<Symbol>, body_native: NativeFn, metadata: FuncMetadata) -> Self {
152 let status = if metadata.differentiator_hint.is_some() {
153 SymbolicStatus::ProvidedByHint
154 } else {
155 SymbolicStatus::native_only()
156 };
157 Self::native_with_status(vars, body_native, metadata, status)
158 }
159
160 fn native_with_status(
161 vars: Vec<Symbol>,
162 body_native: NativeFn,
163 metadata: FuncMetadata,
164 symbolic_status: SymbolicStatus,
165 ) -> Self {
166 Self::new(
167 vars,
168 FuncBody::Native {
169 native: body_native,
170 symbolic_status,
171 },
172 metadata,
173 )
174 }
175
176 pub(crate) fn dual_with(
179 vars: Vec<Symbol>,
180 body_cas: CasExpr,
181 body_native: NativeFn,
182 metadata: FuncMetadata,
183 ) -> Self {
184 Self::new(
185 vars,
186 FuncBody::Dual {
187 cas: body_cas,
188 native: body_native,
189 },
190 metadata,
191 )
192 }
193
194 pub fn body_cas(&self) -> Option<&CasExpr> {
196 match &self.body {
197 FuncBody::Symbolic(body) | FuncBody::Dual { cas: body, .. } => Some(body),
198 FuncBody::Native { .. } => None,
199 }
200 }
201
202 fn body_native(&self) -> Option<&NativeFn> {
203 match &self.body {
204 FuncBody::Native { native: body, .. } | FuncBody::Dual { native: body, .. } => {
205 Some(body)
206 }
207 FuncBody::Symbolic(_) => None,
208 }
209 }
210
211 pub fn is_native(&self) -> bool {
213 self.body_native().is_some()
214 }
215
216 pub fn symbolic_status(&self) -> SymbolicStatus {
218 match &self.body {
219 FuncBody::Symbolic(_) | FuncBody::Dual { .. } => SymbolicStatus::Available,
220 FuncBody::Native {
221 symbolic_status, ..
222 } => symbolic_status.clone(),
223 }
224 }
225
226 fn native_extension_payload(&self) -> Expr {
227 let status = self.symbolic_status();
228 let mut fields = vec![(
229 Expr::Symbol(Symbol::new("symbolic-status")),
230 Expr::Symbol(status.status_symbol()),
231 )];
232 if let Some(reason) = status.reason() {
233 fields.push((
234 Expr::Symbol(Symbol::new("symbolic-loss-reason")),
235 Expr::Symbol(reason.clone()),
236 ));
237 }
238 if let Some(hint) = &self.metadata.differentiator_hint {
239 fields.push((
240 Expr::Symbol(Symbol::new("differentiator-hint")),
241 Expr::Symbol(hint.clone()),
242 ));
243 }
244 Expr::Map(fields)
245 }
246
247 fn invoke(&self, cx: &mut Cx, args: &[Value]) -> Result<Value> {
248 if args.len() != self.vars.len() {
249 return Err(Error::Eval(format!(
250 "function expected {} arguments but received {}",
251 self.vars.len(),
252 args.len()
253 )));
254 }
255 if let Some(body_native) = self.body_native() {
256 return body_native(cx, args);
257 }
258 let body_cas = self
259 .body_cas()
260 .expect("FuncBody always contains a symbolic or native body");
261 let env = child_env_with_args(cx.env(), &self.vars, args)?;
262 cx.with_env(env.clone(), |cx| eval_cas(cx, body_cas, &env))
263 }
264}
265
266impl Object for Func {
267 fn display(&self, cx: &mut Cx) -> Result<String> {
268 if let Some(body_cas) = self.body_cas() {
269 return Ok(format!(
270 "#<func {:?} -> {:?}>",
271 self.vars,
272 cas_expr_to_surface_expr(cx, body_cas)?
273 ));
274 }
275 Ok(format!("#<native-func {:?}>", self.vars))
276 }
277
278 fn as_any(&self) -> &dyn Any {
279 self
280 }
281}
282
283impl sim_kernel::ObjectCompat for Func {
284 fn class(&self, cx: &mut Cx) -> Result<ClassRef> {
285 if let Some(value) = cx.registry().class_by_symbol(&func_class_symbol()) {
286 return Ok(value.clone());
287 }
288 DefaultFactory.class_stub(
289 sim_kernel::CORE_NUMBER_CLASS_ID,
290 Symbol::qualified("core", "Number"),
291 )
292 }
293 fn as_expr(&self, cx: &mut Cx) -> Result<Expr> {
294 let Some(body_cas) = self.body_cas() else {
295 return Ok(Expr::Extension {
296 tag: func_class_symbol(),
297 payload: Box::new(self.native_extension_payload()),
298 });
299 };
300 Ok(Expr::Call {
301 operator: Box::new(Expr::Symbol(Symbol::new("fn"))),
302 args: vec![
303 vars_expr(&self.vars),
304 cas_expr_to_surface_expr(cx, body_cas)?,
305 ],
306 })
307 }
308 fn as_table(&self, cx: &mut Cx) -> Result<Value> {
309 let vars = cx.factory().list(
310 self.vars
311 .iter()
312 .cloned()
313 .map(|var| cx.factory().symbol(var))
314 .collect::<Result<Vec<_>>>()?,
315 )?;
316 let body_expr = self
317 .body_cas()
318 .map(|body| cas_expr_to_surface_expr(cx, body))
319 .transpose()?;
320 let body = match self.body_cas() {
321 Some(_) => cx
322 .factory()
323 .expr(body_expr.expect("body expr should exist when body_cas is present"))?,
324 None => cx.factory().nil()?,
325 };
326 let native = cx.factory().bool(self.is_native())?;
327 let symbolic_status = self.symbolic_status();
328 let mut fields = vec![
329 (Symbol::new("kind"), cx.factory().string("func".to_owned())?),
330 (Symbol::new("vars"), vars),
331 (Symbol::new("body"), body),
332 (Symbol::new("native"), native),
333 (
334 Symbol::new("symbolic-status"),
335 cx.factory().symbol(symbolic_status.status_symbol())?,
336 ),
337 ];
338 if let Some(reason) = symbolic_status.reason() {
339 fields.push((
340 Symbol::new("symbolic-loss-reason"),
341 cx.factory().symbol(reason.clone())?,
342 ));
343 }
344 cx.factory().table(fields)
345 }
346 fn as_callable(&self) -> Option<&dyn Callable> {
347 Some(self)
348 }
349 fn as_number_value(&self) -> Option<&dyn NumberValue> {
350 Some(self)
351 }
352 fn as_object_encoder(&self) -> Option<&dyn ObjectEncode> {
353 Some(self)
354 }
355}
356
357impl Callable for Func {
358 fn call(&self, cx: &mut Cx, args: Args) -> Result<Value> {
359 self.invoke(cx, args.values())
360 }
361
362 fn browse_args_shape(&self, _cx: &mut Cx) -> Result<Option<ShapeRef>> {
363 let items = self
364 .vars
365 .iter()
366 .map(|_| Arc::new(AnyShape) as Arc<dyn Shape>)
367 .collect();
368 Ok(Some(shape_value(
369 Symbol::qualified(func_class_symbol().to_string(), "args"),
370 Arc::new(ListShape::new(items)),
371 )))
372 }
373}
374
375impl NumberValue for Func {
376 fn number_domain(&self, _cx: &mut Cx) -> Result<Symbol> {
377 Ok(func_domain_symbol())
378 }
379}
380
381impl sim_citizen::Citizen for Func {
382 fn citizen_symbol() -> Symbol {
383 func_class_symbol()
384 }
385
386 fn citizen_version() -> u32 {
387 0
388 }
389
390 fn citizen_arity() -> usize {
391 2
392 }
393
394 fn citizen_fields() -> &'static [&'static str] {
395 &["vars", "body"]
396 }
397}
398
399pub fn build_func_value(cx: &mut Cx, func: Func) -> Result<Value> {
401 cx.factory().opaque(Arc::new(func))
402}
403
404pub(crate) fn build_constant_func_value(cx: &mut Cx, value: Value) -> Result<Value> {
405 let body = value_to_cas_expr(cx, value)?;
406 let vars = free_vars(&body);
407 build_func_value(cx, Func::symbolic(vars, body))
408}