Skip to main content

sim_lib_numbers_core/
literal.rs

1//! The shared number-literal shape and class.
2//!
3//! Every scalar number domain (`numbers/i64`, `numbers/f64`, `numbers/bool`,
4//! `numbers/rational`, ...) had a byte-identical copy of these two types. This
5//! is their one home; a domain crate constructs them with its own domain symbol
6//! and instance-shape symbol.
7
8use std::sync::Arc;
9use std::sync::atomic::{AtomicU32, Ordering};
10
11use sim_kernel::{
12    Args, Callable, Class, ClassId, ClassRef, Cx, DefaultFactory, Error, Expr, Factory, Object,
13    ReadConstructorRef, Result, ShapeRef, Symbol, TableRef, Value,
14};
15use sim_shape::{MatchScore, Shape, ShapeDoc, ShapeMatch, shape_value};
16
17/// A shape matching number literals (`Expr::Number`) in one domain.
18pub struct NumberLiteralShape {
19    domain: Symbol,
20    name: &'static str,
21    details: Vec<&'static str>,
22}
23
24impl NumberLiteralShape {
25    /// Build a literal shape for `domain` with a browse `name` and detail lines.
26    pub fn new(
27        domain: Symbol,
28        name: &'static str,
29        details: impl IntoIterator<Item = &'static str>,
30    ) -> Self {
31        Self {
32            domain,
33            name,
34            details: details.into_iter().collect(),
35        }
36    }
37}
38
39impl Shape for NumberLiteralShape {
40    fn check_value(&self, cx: &mut Cx, value: Value) -> Result<ShapeMatch> {
41        let expr = value.object().as_expr(cx)?;
42        self.check_expr(cx, &expr)
43    }
44
45    fn check_expr(&self, _cx: &mut Cx, expr: &Expr) -> Result<ShapeMatch> {
46        match expr {
47            Expr::Number(number) if number.domain == self.domain => {
48                Ok(ShapeMatch::accept(MatchScore::exact(20)))
49            }
50            _ => Ok(ShapeMatch::reject(format!(
51                "expected number literal in {}",
52                self.domain
53            ))),
54        }
55    }
56
57    fn describe(&self, _cx: &mut Cx) -> Result<ShapeDoc> {
58        let mut doc = ShapeDoc::new(self.name);
59        for detail in &self.details {
60            doc = doc.with_detail(*detail);
61        }
62        Ok(doc)
63    }
64}
65
66/// The class object for a domain's number literals.
67pub struct NumberLiteralClass {
68    id: AtomicU32,
69    symbol: Symbol,
70    domain: Symbol,
71    numeric_family: &'static str,
72    canonical_form: &'static str,
73    instance_shape_symbol: Symbol,
74    instance_shape: Arc<dyn Shape>,
75}
76
77impl NumberLiteralClass {
78    /// Build the literal class for one domain from its identifying metadata and
79    /// the shape matching its instances.
80    pub fn new(
81        symbol: Symbol,
82        domain: Symbol,
83        numeric_family: &'static str,
84        canonical_form: &'static str,
85        instance_shape_symbol: Symbol,
86        instance_shape: Arc<dyn Shape>,
87    ) -> Self {
88        Self {
89            id: AtomicU32::new(0),
90            symbol,
91            domain,
92            numeric_family,
93            canonical_form,
94            instance_shape_symbol,
95            instance_shape,
96        }
97    }
98
99    /// Record the kernel-assigned [`ClassId`] after registration.
100    pub fn set_id(&self, id: ClassId) {
101        self.id.store(id.0, Ordering::Relaxed);
102    }
103}
104
105impl Object for NumberLiteralClass {
106    fn display(&self, _cx: &mut Cx) -> Result<String> {
107        Ok(format!("#<class {}>", self.symbol))
108    }
109
110    fn as_any(&self) -> &dyn std::any::Any {
111        self
112    }
113}
114
115impl sim_kernel::ObjectCompat for NumberLiteralClass {
116    fn class(&self, cx: &mut Cx) -> Result<ClassRef> {
117        if let Some(value) = cx
118            .registry()
119            .class_by_symbol(&Symbol::qualified("core", "Class"))
120        {
121            return Ok(value.clone());
122        }
123        DefaultFactory.class_stub(
124            sim_kernel::CORE_CLASS_CLASS_ID,
125            Symbol::qualified("core", "Class"),
126        )
127    }
128    fn as_expr(&self, _cx: &mut Cx) -> Result<Expr> {
129        Ok(Expr::Symbol(self.symbol.clone()))
130    }
131    fn as_table(&self, cx: &mut Cx) -> Result<Value> {
132        let instance_shape = shape_surface_or_symbol(cx, self.instance_shape_symbol.clone())?;
133        cx.factory().table(vec![
134            (
135                Symbol::new("symbol"),
136                cx.factory().symbol(self.symbol.clone())?,
137            ),
138            (
139                Symbol::new("domain"),
140                cx.factory().symbol(self.domain.clone())?,
141            ),
142            (
143                Symbol::new("numeric-family"),
144                cx.factory().string(self.numeric_family.to_owned())?,
145            ),
146            (
147                Symbol::new("canonical-form"),
148                cx.factory().string(self.canonical_form.to_owned())?,
149            ),
150            (Symbol::new("instance-shape"), instance_shape),
151        ])
152    }
153    fn as_callable(&self) -> Option<&dyn Callable> {
154        Some(self)
155    }
156    fn as_class(&self) -> Option<&dyn Class> {
157        Some(self)
158    }
159}
160
161impl Callable for NumberLiteralClass {
162    fn call(&self, _cx: &mut Cx, _args: Args) -> Result<Value> {
163        Err(Error::Eval(format!(
164            "class {} does not construct values directly; parse or compute a number literal instead",
165            self.symbol
166        )))
167    }
168}
169
170impl Class for NumberLiteralClass {
171    fn id(&self) -> ClassId {
172        ClassId(self.id.load(Ordering::Relaxed))
173    }
174
175    fn symbol(&self) -> Symbol {
176        self.symbol.clone()
177    }
178
179    fn constructor_shape(&self, cx: &mut Cx) -> Result<ShapeRef> {
180        cx.factory().nil()
181    }
182
183    fn instance_shape(&self, _cx: &mut Cx) -> Result<ShapeRef> {
184        Ok(shape_value(
185            self.instance_shape_symbol.clone(),
186            self.instance_shape.clone(),
187        ))
188    }
189
190    fn read_constructor(&self, _cx: &mut Cx) -> Result<Option<ReadConstructorRef>> {
191        Ok(None)
192    }
193
194    fn members(&self, cx: &mut Cx) -> Result<TableRef> {
195        cx.factory().table(Vec::new())
196    }
197}
198
199/// Resolve a class symbol to its registered surface value, or the bare symbol.
200pub fn class_surface_or_symbol(cx: &mut Cx, symbol: Symbol) -> Result<Value> {
201    Ok(cx
202        .registry()
203        .class_by_symbol(&symbol)
204        .cloned()
205        .unwrap_or(cx.factory().symbol(symbol)?))
206}
207
208/// Resolve a shape symbol to its registered surface value, or the bare symbol.
209pub fn shape_surface_or_symbol(cx: &mut Cx, symbol: Symbol) -> Result<Value> {
210    Ok(cx
211        .registry()
212        .shape_by_symbol(&symbol)
213        .cloned()
214        .unwrap_or(cx.factory().symbol(symbol)?))
215}