sim_lib_numbers_func/implementation/
domain.rs1use std::sync::Arc;
5
6use sim_kernel::{
7 AbiVersion, ClassId, DefaultFactory, Dependency, Export, Expr, Factory, Lib, LibManifest,
8 LibTarget, Linker, NumberDomain, NumberLiteral, Object, Result, Symbol, Value,
9 ValuePromotionRule, Version,
10};
11use sim_lib_numbers_cas::CasExpr;
12use sim_lib_numbers_core::{DomainNumberValueShape, domains};
13use sim_shape::shape_value;
14
15use super::function::{
16 CallFunction, FnBuilder, GradFunction, build_func_class, call_symbol, fn_symbol, grad_symbol,
17};
18use super::value::{Func, FuncMetadata, build_constant_func_value, build_func_value};
19
20pub fn func_domain_symbol() -> Symbol {
30 domains::func()
31}
32
33pub fn func_class_symbol() -> Symbol {
43 domains::domain("Func")
44}
45
46pub fn value_shape_symbol() -> Symbol {
47 sim_lib_numbers_core::value_shape_symbol(&func_domain_symbol())
48}
49
50#[sim_citizen_derive::non_citizen(
51 reason = "numbers/func number-domain marker; reconstruct by loading the function number lib",
52 kind = "marker"
53)]
54pub struct FuncNumberDomain;
55
56impl NumberDomain for FuncNumberDomain {
57 fn symbol(&self) -> Symbol {
58 func_domain_symbol()
59 }
60
61 fn parse_priority(&self) -> i32 {
62 -100
63 }
64
65 fn parse_literal(&self, _cx: &mut sim_kernel::Cx, _text: &str) -> Result<Option<Value>> {
66 Ok(None)
67 }
68
69 fn encode_literal(
70 &self,
71 _cx: &mut sim_kernel::Cx,
72 _value: Value,
73 ) -> Result<Option<NumberLiteral>> {
74 Ok(None)
75 }
76}
77
78impl Object for FuncNumberDomain {
79 fn display(&self, _cx: &mut sim_kernel::Cx) -> Result<String> {
80 Ok("#<number-domain numbers/func>".to_owned())
81 }
82
83 fn as_any(&self) -> &dyn std::any::Any {
84 self
85 }
86}
87
88impl sim_kernel::ObjectCompat for FuncNumberDomain {
89 fn class(&self, cx: &mut sim_kernel::Cx) -> Result<sim_kernel::ClassRef> {
90 sim_lib_numbers_core::number_domain_class_stub(cx)
91 }
92 fn as_expr(&self, _cx: &mut sim_kernel::Cx) -> Result<Expr> {
93 Ok(Expr::Symbol(func_domain_symbol()))
94 }
95 fn as_table(&self, cx: &mut sim_kernel::Cx) -> Result<Value> {
96 let value_shape = cx
97 .registry()
98 .shape_by_symbol(&value_shape_symbol())
99 .cloned()
100 .unwrap_or(cx.factory().symbol(value_shape_symbol())?);
101 let func_class = cx
102 .registry()
103 .class_by_symbol(&func_class_symbol())
104 .cloned()
105 .unwrap_or(cx.factory().symbol(func_class_symbol())?);
106 cx.factory().table(vec![
107 (
108 Symbol::new("symbol"),
109 cx.factory().symbol(func_domain_symbol())?,
110 ),
111 (
112 Symbol::new("kind"),
113 cx.factory().string("number-domain".to_owned())?,
114 ),
115 (
116 Symbol::new("numeric-family"),
117 cx.factory().string("function".to_owned())?,
118 ),
119 (
120 Symbol::new("canonical-form"),
121 cx.factory()
122 .string("callable symbolic function".to_owned())?,
123 ),
124 (
125 Symbol::new("parse-priority"),
126 cx.factory().string("-100".to_owned())?,
127 ),
128 (Symbol::new("constructor-class"), func_class),
129 (Symbol::new("value-shape"), value_shape),
130 (Symbol::new("builder"), cx.factory().symbol(fn_symbol())?),
131 (Symbol::new("call"), cx.factory().symbol(call_symbol())?),
132 (Symbol::new("grad"), cx.factory().symbol(grad_symbol())?),
133 ])
134 }
135 fn as_number_domain(&self) -> Option<&dyn NumberDomain> {
136 Some(self)
137 }
138}
139
140pub struct FuncNumbersLib;
144
145impl FuncNumbersLib {
146 pub fn new() -> Self {
148 Self
149 }
150}
151
152impl Default for FuncNumbersLib {
153 fn default() -> Self {
154 Self::new()
155 }
156}
157
158impl Lib for FuncNumbersLib {
159 fn manifest(&self) -> LibManifest {
160 LibManifest {
161 id: func_domain_symbol(),
162 version: Version(env!("CARGO_PKG_VERSION").to_owned()),
163 abi: AbiVersion { major: 0, minor: 1 },
164 target: LibTarget::HostRegistered,
165 requires: Vec::<Dependency>::new(),
166 capabilities: Vec::new(),
167 exports: vec![
168 Export::NumberDomain {
169 symbol: func_domain_symbol(),
170 number_domain_id: None,
171 },
172 Export::Class {
173 symbol: func_class_symbol(),
174 class_id: None,
175 },
176 Export::Shape {
177 symbol: value_shape_symbol(),
178 shape_id: None,
179 },
180 Export::Function {
181 symbol: fn_symbol(),
182 function_id: None,
183 },
184 Export::Function {
185 symbol: call_symbol(),
186 function_id: None,
187 },
188 Export::Function {
189 symbol: grad_symbol(),
190 function_id: None,
191 },
192 ],
193 }
194 }
195
196 fn load(&self, _cx: &mut sim_kernel::LoadCx, linker: &mut Linker<'_>) -> Result<()> {
197 let value_shape = Arc::new(DomainNumberValueShape::new(
198 func_domain_symbol(),
199 "FuncValue",
200 [
201 "callable number value in the numbers/func domain",
202 "accepts any NumberValue where domain == numbers/func",
203 ],
204 ));
205
206 linker.number_domain_value(
207 func_domain_symbol(),
208 DefaultFactory
209 .opaque(Arc::new(FuncNumberDomain))
210 .expect("number domain should be boxable"),
211 )?;
212 register_func_value_class(linker)?;
213 linker.shape_value(
214 value_shape_symbol(),
215 shape_value(value_shape_symbol(), value_shape),
216 )?;
217 for (symbol, value) in [
218 (
219 fn_symbol(),
220 DefaultFactory
221 .opaque(Arc::new(FnBuilder))
222 .expect("fn builder should be boxable"),
223 ),
224 (
225 call_symbol(),
226 DefaultFactory
227 .opaque(Arc::new(CallFunction))
228 .expect("call helper should be boxable"),
229 ),
230 (
231 grad_symbol(),
232 DefaultFactory
233 .opaque(Arc::new(GradFunction))
234 .expect("grad helper should be boxable"),
235 ),
236 ] {
237 linker.function_value(symbol, value)?;
238 }
239 for from_domain in promoted_domains() {
240 linker.value_promotion_rule(ValuePromotionRule {
241 from_domain,
242 to_domain: func_domain_symbol(),
243 cost: 1,
244 convert: promote_value_to_func,
245 });
246 }
247 super::value::register_value_ops(linker);
248 Ok(())
249 }
250}
251
252fn register_func_value_class(linker: &mut Linker<'_>) -> Result<ClassId> {
253 let func_class = build_func_class();
254 let class_id = linker.class_value(
255 func_class_symbol(),
256 DefaultFactory
257 .opaque(func_class.clone())
258 .expect("function class should be boxable"),
259 )?;
260 func_class.set_id(class_id);
261 Ok(class_id)
262}
263
264fn install_func_value_citizen(linker: &mut Linker<'_>) -> Result<()> {
265 register_func_value_class(linker).map(|_| ())
266}
267
268fn conformance_func_value_citizen(cx: &mut sim_kernel::Cx) -> Result<()> {
269 let var = Symbol::new("x");
270 let value = build_func_value(
271 cx,
272 Func::new(
273 vec![var.clone()],
274 Some(CasExpr::Var(var)),
275 None,
276 FuncMetadata::default(),
277 ),
278 )?;
279 sim_citizen::check_value_fixture(cx, value)
280}
281
282sim_citizen::inventory::submit! {
283 sim_citizen::CitizenInfo {
284 symbol: "numbers/Func",
285 version: 0,
286 crate_name: env!("CARGO_PKG_NAME"),
287 arity: 2,
288 install: install_func_value_citizen,
289 conformance: conformance_func_value_citizen,
290 }
291}
292
293fn promoted_domains() -> Vec<Symbol> {
294 vec![
295 domains::bool(),
296 domains::f32(),
297 domains::f64(),
298 domains::i64(),
299 domains::bigint(),
300 domains::rational(),
301 domains::complex(),
302 domains::cas(),
303 domains::continued_fraction(),
304 ]
305}
306
307fn promote_value_to_func(cx: &mut sim_kernel::Cx, value: Value) -> Result<Value> {
308 build_constant_func_value(cx, value)
309}