Skip to main content

sim_lib_numbers_func/implementation/
domain.rs

1//! `Func` number-domain registration: the domain library, class symbols, and
2//! value-shape wiring that install the function domain into the runtime.
3
4use 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
20/// Returns the domain symbol that names the `Func` number domain (`numbers/func`).
21///
22/// # Examples
23///
24/// ```
25/// use sim_lib_numbers_func::func_domain_symbol;
26///
27/// assert_eq!(func_domain_symbol().to_string(), "numbers/func");
28/// ```
29pub fn func_domain_symbol() -> Symbol {
30    domains::func()
31}
32
33/// Returns the class symbol for the constructible `Func` value class (`numbers/Func`).
34///
35/// # Examples
36///
37/// ```
38/// use sim_lib_numbers_func::func_class_symbol;
39///
40/// assert_eq!(func_class_symbol().to_string(), "numbers/Func");
41/// ```
42pub 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
141/// Library that installs the `Func` number domain: its domain object, value
142/// class and shape, the `fn`/`call`/`grad` callables, and the promotion rules
143/// that lift scalar number values into constant functions.
144pub struct FuncNumbersLib;
145
146impl FuncNumbersLib {
147    /// Creates a new `FuncNumbersLib` ready to be loaded into a runtime.
148    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}