Skip to main content

sim_lib_numbers_float/
implementation.rs

1#![forbid(unsafe_code)]
2
3//! The `numbers/f32` library: its domain object, literal and value shapes, and
4//! the `Lib` that installs the f32 ops and the promotion into `f64`.
5
6use std::sync::Arc;
7
8use sim_kernel::{
9    AbiVersion, DefaultFactory, Dependency, Export, Expr, Factory, Lib, LibManifest, LibTarget,
10    Linker, NumberDomain, NumberLiteral, Object, PromotionRule, Result, Symbol, Value,
11    ValuePromotionRule, Version,
12};
13use sim_lib_numbers_core::{
14    DomainNumberValueShape, NumberDomainTableSpec, NumberLiteralClass, NumberLiteralShape,
15    ScalarBinaryOp, ScalarOps, ScalarReductionOp, ScalarUnaryOp, class_surface_or_symbol, domains,
16    install_scalar_ops, number_domain_table, shape_surface_or_symbol,
17};
18use sim_shape::shape_value;
19
20use crate::literal::value_instance_shape_symbol;
21use crate::ops::{
22    F32RuleFn, ValueRuleFn, canonical_f32, f32_add_rule, f32_div_rule, f32_mul_rule, f32_neg_rule,
23    f32_product_rule, f32_sub_rule, f32_sum_rule,
24};
25
26/// The `numbers/f32` domain symbol shared by this crate's literals, values,
27/// and ops.
28pub fn number_domain() -> Symbol {
29    domains::f32()
30}
31
32fn literal_class_symbol() -> Symbol {
33    domains::literal_class("f32")
34}
35
36pub(crate) fn literal_instance_shape_symbol() -> Symbol {
37    Symbol::qualified(literal_class_symbol().to_string(), "instance-shape")
38}
39
40fn value_shape_symbol() -> Symbol {
41    value_instance_shape_symbol()
42}
43
44pub(crate) fn f64_domain() -> Symbol {
45    domains::f64()
46}
47
48#[sim_citizen_derive::non_citizen(
49    reason = "numbers/f32 number-domain marker; reconstruct by loading the float number lib",
50    kind = "marker",
51    descriptor = "numbers/f32"
52)]
53/// The single-precision 32-bit floating-point number domain: parses decimal
54/// literals and declares the widening promotion edge into [`f64`](domains::f64).
55pub struct F32NumberDomain;
56
57impl NumberDomain for F32NumberDomain {
58    fn symbol(&self) -> Symbol {
59        number_domain()
60    }
61
62    fn parse_priority(&self) -> i32 {
63        -1
64    }
65
66    fn parse_literal(&self, cx: &mut sim_kernel::Cx, text: &str) -> Result<Option<Value>> {
67        if text.parse::<f32>().is_err() {
68            return Ok(None);
69        }
70        cx.factory()
71            .number_literal(self.symbol(), canonical_f32(text))
72            .map(Some)
73    }
74
75    fn encode_literal(
76        &self,
77        cx: &mut sim_kernel::Cx,
78        value: Value,
79    ) -> Result<Option<NumberLiteral>> {
80        match value.object().as_expr(cx)? {
81            Expr::Number(number) if number.domain == self.symbol() => Ok(Some(number)),
82            _ => Ok(None),
83        }
84    }
85
86    fn promotions(&self) -> Vec<PromotionRule> {
87        vec![PromotionRule {
88            from_domain: number_domain(),
89            to_domain: f64_domain(),
90            cost: 1,
91            convert: crate::ops::promote_f32_to_f64,
92        }]
93    }
94}
95
96impl Object for F32NumberDomain {
97    fn display(&self, _cx: &mut sim_kernel::Cx) -> Result<String> {
98        Ok("#<number-domain numbers/f32>".to_owned())
99    }
100
101    fn as_any(&self) -> &dyn std::any::Any {
102        self
103    }
104}
105
106impl sim_kernel::ObjectCompat for F32NumberDomain {
107    fn class(&self, cx: &mut sim_kernel::Cx) -> Result<sim_kernel::ClassRef> {
108        sim_lib_numbers_core::number_domain_class_stub(cx)
109    }
110    fn as_expr(&self, _cx: &mut sim_kernel::Cx) -> Result<Expr> {
111        Ok(Expr::Symbol(number_domain()))
112    }
113    fn as_table(&self, cx: &mut sim_kernel::Cx) -> Result<Value> {
114        let literal_class = class_surface_or_symbol(cx, literal_class_symbol())?;
115        let instance_shape = shape_surface_or_symbol(cx, literal_instance_shape_symbol())?;
116        let value_shape = shape_surface_or_symbol(cx, value_shape_symbol())?;
117        number_domain_table(
118            cx,
119            NumberDomainTableSpec::new(
120                number_domain(),
121                "real",
122                "f32",
123                -1,
124                literal_class,
125                instance_shape,
126                value_shape,
127            ),
128        )
129    }
130    fn as_number_domain(&self) -> Option<&dyn NumberDomain> {
131        Some(self)
132    }
133}
134
135/// The library that installs the `numbers/f32` domain: its literal class and
136/// shapes, value shape, scalar ops, and promotion rules.
137///
138/// # Examples
139///
140/// ```
141/// use std::sync::Arc;
142/// use sim_kernel::{Cx, DefaultFactory, NoopEvalPolicy};
143/// use sim_lib_numbers_float::{F32NumbersLib, number_domain};
144///
145/// let mut cx = Cx::new(Arc::new(NoopEvalPolicy), Arc::new(DefaultFactory));
146/// cx.load_lib(&F32NumbersLib::new()).unwrap();
147///
148/// let value = cx.factory().number_literal(number_domain(), "1.5".to_owned()).unwrap();
149/// let number = cx.number_value_ref(value).unwrap().unwrap();
150/// assert_eq!(number.domain, number_domain());
151/// ```
152pub struct F32NumbersLib;
153
154impl F32NumbersLib {
155    /// Construct the f32 library installer.
156    pub fn new() -> Self {
157        Self
158    }
159}
160
161impl Default for F32NumbersLib {
162    fn default() -> Self {
163        Self::new()
164    }
165}
166
167impl Lib for F32NumbersLib {
168    fn manifest(&self) -> LibManifest {
169        LibManifest {
170            id: number_domain(),
171            version: Version(env!("CARGO_PKG_VERSION").to_owned()),
172            abi: AbiVersion { major: 0, minor: 1 },
173            target: LibTarget::HostRegistered,
174            requires: Vec::<Dependency>::new(),
175            capabilities: Vec::new(),
176            exports: vec![
177                Export::NumberDomain {
178                    symbol: number_domain(),
179                    number_domain_id: None,
180                },
181                Export::Class {
182                    symbol: literal_class_symbol(),
183                    class_id: None,
184                },
185                Export::Shape {
186                    symbol: literal_instance_shape_symbol(),
187                    shape_id: None,
188                },
189                Export::Shape {
190                    symbol: value_shape_symbol(),
191                    shape_id: None,
192                },
193            ],
194        }
195    }
196
197    fn load(&self, _cx: &mut sim_kernel::LoadCx, linker: &mut Linker<'_>) -> Result<()> {
198        let instance_shape = Arc::new(NumberLiteralShape::new(
199            number_domain(),
200            "F32Literal",
201            [
202                "number literal in the numbers/f32 domain",
203                "matches Expr::Number where domain == numbers/f32",
204            ],
205        ));
206        let literal_class = Arc::new(NumberLiteralClass::new(
207            literal_class_symbol(),
208            number_domain(),
209            "real",
210            "f32",
211            literal_instance_shape_symbol(),
212            instance_shape.clone(),
213        ));
214        let value_shape = Arc::new(DomainNumberValueShape::new(
215            number_domain(),
216            "F32Value",
217            [
218                "number value in the numbers/f32 domain",
219                "accepts any NumberValue where domain == numbers/f32",
220            ],
221        ));
222        linker.number_domain_value(
223            number_domain(),
224            DefaultFactory
225                .opaque(Arc::new(F32NumberDomain))
226                .expect("number domain should be boxable"),
227        )?;
228        let class_id = linker.class_value(
229            literal_class_symbol(),
230            DefaultFactory
231                .opaque(literal_class.clone())
232                .expect("number literal class should be boxable"),
233        )?;
234        literal_class.set_id(class_id);
235        linker.shape_value(
236            literal_instance_shape_symbol(),
237            shape_value(literal_instance_shape_symbol(), instance_shape),
238        )?;
239        linker.shape_value(
240            value_shape_symbol(),
241            shape_value(value_shape_symbol(), value_shape),
242        )?;
243        for rule in F32NumberDomain.promotions() {
244            linker.promotion_rule(rule.clone());
245            linker.value_promotion_rule(ValuePromotionRule {
246                from_domain: rule.from_domain,
247                to_domain: rule.to_domain,
248                cost: rule.cost,
249                convert: crate::ops::promote_f32_value_to_f64,
250            });
251        }
252        let binary = [
253            (
254                Symbol::qualified("math", "add"),
255                f32_add_rule as F32RuleFn,
256                crate::ops::f32_add_value_rule as ValueRuleFn,
257            ),
258            (
259                Symbol::qualified("math", "sub"),
260                f32_sub_rule,
261                crate::ops::f32_sub_value_rule,
262            ),
263            (
264                Symbol::qualified("math", "mul"),
265                f32_mul_rule,
266                crate::ops::f32_mul_value_rule,
267            ),
268            (
269                Symbol::qualified("math", "div"),
270                f32_div_rule,
271                crate::ops::f32_div_value_rule,
272            ),
273        ]
274        .into_iter()
275        .map(|(operator, literal_apply, value_apply)| ScalarBinaryOp {
276            operator,
277            literal_cost: 0,
278            literal_apply,
279            value_cost: 1,
280            value_apply,
281        })
282        .collect();
283        let ops = ScalarOps {
284            domain: number_domain(),
285            binary,
286            unary: vec![ScalarUnaryOp {
287                operator: Symbol::qualified("math", "neg"),
288                literal_cost: 0,
289                literal_apply: f32_neg_rule,
290                value_cost: 1,
291                value_apply: crate::ops::f32_neg_value_rule,
292            }],
293            reduction: vec![
294                ScalarReductionOp {
295                    operator: Symbol::qualified("math", "sum"),
296                    literal_cost: 0,
297                    literal_apply: f32_sum_rule,
298                    value_cost: 1,
299                    value_apply: crate::ops::f32_sum_value_rule,
300                },
301                ScalarReductionOp {
302                    operator: Symbol::qualified("math", "product"),
303                    literal_cost: 0,
304                    literal_apply: f32_product_rule,
305                    value_cost: 1,
306                    value_apply: crate::ops::f32_product_value_rule,
307                },
308            ],
309        };
310        install_scalar_ops(linker, &ops);
311        Ok(())
312    }
313}