1#![forbid(unsafe_code)]
2
3use 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
26pub 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)]
53pub 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
135pub struct F32NumbersLib;
153
154impl F32NumbersLib {
155 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}