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)]
52pub 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
134pub struct F32NumbersLib;
152
153impl F32NumbersLib {
154 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}