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