Skip to main content

sim_lib_numbers_core/
value_shape.rs

1//! The domain number-value shape and the number-domain browse table.
2
3use sim_kernel::{Cx, Expr, NumberValueRef, Result, Symbol, Value};
4use sim_shape::{MatchScore, Shape, ShapeDoc, ShapeMatch};
5
6/// The stable symbol for a domain's number-value shape.
7pub fn value_shape_symbol(domain: &Symbol) -> Symbol {
8    Symbol::qualified(domain.to_string(), "value-shape")
9}
10
11/// Inputs for the shared number-domain browse table.
12pub struct NumberDomainTableSpec<'a> {
13    domain: Symbol,
14    numeric_family: &'a str,
15    canonical_form: &'a str,
16    parse_priority: i32,
17    literal_class: Value,
18    instance_shape: Value,
19    value_shape: Value,
20}
21
22impl<'a> NumberDomainTableSpec<'a> {
23    /// Assemble the inputs for one domain's browse table from its identity,
24    /// parse priority, and the literal-class/instance-shape/value-shape values.
25    pub fn new(
26        domain: Symbol,
27        numeric_family: &'a str,
28        canonical_form: &'a str,
29        parse_priority: i32,
30        literal_class: Value,
31        instance_shape: Value,
32        value_shape: Value,
33    ) -> Self {
34        Self {
35            domain,
36            numeric_family,
37            canonical_form,
38            parse_priority,
39            literal_class,
40            instance_shape,
41            value_shape,
42        }
43    }
44}
45
46/// Build the shared number-domain browse table value.
47pub fn number_domain_table(cx: &mut Cx, spec: NumberDomainTableSpec<'_>) -> Result<Value> {
48    cx.factory().table(vec![
49        (Symbol::new("symbol"), cx.factory().symbol(spec.domain)?),
50        (
51            Symbol::new("kind"),
52            cx.factory().string("number-domain".to_owned())?,
53        ),
54        (
55            Symbol::new("numeric-family"),
56            cx.factory().string(spec.numeric_family.to_owned())?,
57        ),
58        (
59            Symbol::new("canonical-form"),
60            cx.factory().string(spec.canonical_form.to_owned())?,
61        ),
62        (
63            Symbol::new("parse-priority"),
64            cx.factory().string(spec.parse_priority.to_string())?,
65        ),
66        (Symbol::new("literal-class"), spec.literal_class),
67        (Symbol::new("instance-shape"), spec.instance_shape),
68        (Symbol::new("value-shape"), spec.value_shape),
69    ])
70}
71
72/// A shape matching number *values* (not just literals) in one domain.
73pub struct DomainNumberValueShape {
74    domain: Symbol,
75    name: &'static str,
76    details: Vec<&'static str>,
77}
78
79impl DomainNumberValueShape {
80    /// Build a value shape for `domain` with a browse `name` and detail lines.
81    pub fn new(
82        domain: Symbol,
83        name: &'static str,
84        details: impl IntoIterator<Item = &'static str>,
85    ) -> Self {
86        Self {
87            domain,
88            name,
89            details: details.into_iter().collect(),
90        }
91    }
92
93    fn matches_number(&self, number: &NumberValueRef) -> bool {
94        number.domain == self.domain
95    }
96}
97
98impl Shape for DomainNumberValueShape {
99    fn check_value(&self, cx: &mut Cx, value: Value) -> Result<ShapeMatch> {
100        match cx.number_value_ref(value)? {
101            Some(number) if self.matches_number(&number) => {
102                Ok(ShapeMatch::accept(MatchScore::exact(25)))
103            }
104            _ => Ok(ShapeMatch::reject(format!(
105                "expected number value in {}",
106                self.domain
107            ))),
108        }
109    }
110
111    fn check_expr(&self, _cx: &mut Cx, expr: &Expr) -> Result<ShapeMatch> {
112        match expr {
113            Expr::Number(number) if number.domain == self.domain => {
114                Ok(ShapeMatch::accept(MatchScore::exact(20)))
115            }
116            _ => Ok(ShapeMatch::reject(format!(
117                "expected number value in {}",
118                self.domain
119            ))),
120        }
121    }
122
123    fn describe(&self, _cx: &mut Cx) -> Result<ShapeDoc> {
124        let mut doc = ShapeDoc::new(self.name);
125        for detail in &self.details {
126            doc = doc.with_detail(*detail);
127        }
128        Ok(doc)
129    }
130}
131
132/// Assert the domain's value-shape symbol matches the shared helper (test aid).
133pub fn assert_value_shape_symbol(domain: Symbol, actual: Symbol) {
134    assert_eq!(actual, value_shape_symbol(&domain));
135}