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