Skip to main content

sim/
shapes.rs

1use std::sync::Arc;
2
3use sim_kernel::{Cx, Error, Result, ShapeId, ShapeRef, Symbol, Value};
4use sim_shape::{Shape, ShapeDoc, ShapeMatch};
5
6/// Name of the core class under which shapes are registered.
7pub const CORE_SHAPE_CLASS: &str = "Shape";
8
9pub use sim_shape::{shape_value, shape_value_with_encoding};
10
11/// Shape wrapper that overrides only the documentation of an inner shape,
12/// delegating all matching and binding behavior unchanged.
13pub struct DocumentedShape {
14    name: String,
15    details: Vec<String>,
16    inner: Arc<dyn Shape>,
17}
18
19impl DocumentedShape {
20    /// Wraps `inner` with a display `name` and zero or more detail lines.
21    pub fn new(
22        name: impl Into<String>,
23        details: impl IntoIterator<Item = impl Into<String>>,
24        inner: Arc<dyn Shape>,
25    ) -> Self {
26        Self {
27            name: name.into(),
28            details: details.into_iter().map(Into::into).collect(),
29            inner,
30        }
31    }
32}
33
34impl Shape for DocumentedShape {
35    fn is_total(&self) -> bool {
36        self.inner.is_total()
37    }
38
39    fn parents(&self, cx: &mut Cx) -> Result<Vec<ShapeRef>> {
40        self.inner.parents(cx)
41    }
42
43    fn is_subshape_of(&self, cx: &mut Cx, parent: &dyn Shape) -> Result<Option<bool>> {
44        self.inner.is_subshape_of(cx, parent)
45    }
46
47    fn check_value(&self, cx: &mut Cx, value: Value) -> Result<sim_shape::ShapeMatch> {
48        self.inner.check_value(cx, value)
49    }
50
51    fn check_expr(&self, cx: &mut Cx, expr: &sim_kernel::Expr) -> Result<sim_shape::ShapeMatch> {
52        self.inner.check_expr(cx, expr)
53    }
54
55    fn describe(&self, _cx: &mut Cx) -> Result<ShapeDoc> {
56        let mut doc = ShapeDoc::new(self.name.clone());
57        for detail in &self.details {
58            doc = doc.with_detail(detail.clone());
59        }
60        Ok(doc)
61    }
62}
63
64/// Registers `shape` under `symbol` and returns its assigned shape id.
65pub fn shape_id(cx: &mut Cx, symbol: Symbol, shape: Arc<dyn Shape>) -> sim_kernel::ShapeId {
66    cx.registry_mut()
67        .register_shape_value(symbol.clone(), shape_value(symbol, shape))
68        .expect("shape registration should not duplicate symbols")
69}
70
71/// Borrows the shape backing a value, or errors if it is not a shape.
72pub fn value_as_shape(value: &Value) -> Result<&dyn Shape> {
73    value.object().as_shape().ok_or(Error::TypeMismatch {
74        expected: "shape",
75        found: "non-shape",
76    })
77}
78
79/// Borrows the shape behind a shape reference, or errors if it is not a shape.
80pub fn shape_ref_as_shape(shape: &ShapeRef) -> Result<&dyn Shape> {
81    value_as_shape(shape)
82}
83
84#[derive(Clone)]
85struct ValueBackedShape {
86    value: ShapeRef,
87}
88
89impl Shape for ValueBackedShape {
90    fn id(&self) -> Option<ShapeId> {
91        self.value.object().as_shape().and_then(|shape| shape.id())
92    }
93
94    fn symbol(&self) -> Option<Symbol> {
95        self.value
96            .object()
97            .as_shape()
98            .and_then(|shape| shape.symbol())
99    }
100
101    fn parents(&self, cx: &mut Cx) -> Result<Vec<ShapeRef>> {
102        shape_ref_as_shape(&self.value)?.parents(cx)
103    }
104
105    fn is_effectful(&self) -> bool {
106        self.value
107            .object()
108            .as_shape()
109            .map(Shape::is_effectful)
110            .unwrap_or(false)
111    }
112
113    fn is_total(&self) -> bool {
114        self.value
115            .object()
116            .as_shape()
117            .map(Shape::is_total)
118            .unwrap_or(false)
119    }
120
121    fn is_subshape_of(&self, cx: &mut Cx, parent: &dyn Shape) -> Result<Option<bool>> {
122        shape_ref_as_shape(&self.value)?.is_subshape_of(cx, parent)
123    }
124
125    fn check_value(&self, cx: &mut Cx, value: Value) -> Result<ShapeMatch> {
126        shape_ref_as_shape(&self.value)?.check_value(cx, value)
127    }
128
129    fn check_expr(&self, cx: &mut Cx, expr: &sim_kernel::Expr) -> Result<ShapeMatch> {
130        shape_ref_as_shape(&self.value)?.check_expr(cx, expr)
131    }
132
133    fn describe(&self, cx: &mut Cx) -> Result<ShapeDoc> {
134        shape_ref_as_shape(&self.value)?.describe(cx)
135    }
136}
137
138/// Wraps a shape reference in a reusable `Arc<dyn Shape>` handle.
139pub fn shape_ref_arc(shape: &ShapeRef) -> Result<Arc<dyn Shape>> {
140    let _ = shape_ref_as_shape(shape)?;
141    Ok(Arc::new(ValueBackedShape {
142        value: shape.clone(),
143    }))
144}
145
146/// Returns the shape id behind a shape reference, or `ShapeId(0)` if none.
147pub fn shape_ref_id(shape: &ShapeRef) -> ShapeId {
148    shape_ref_as_shape(shape)
149        .ok()
150        .and_then(|shape| shape.id())
151        .unwrap_or(ShapeId(0))
152}
153
154/// Checks a value against the shape behind a shape reference.
155pub fn check_shape_value(cx: &mut Cx, shape: &ShapeRef, value: Value) -> Result<ShapeMatch> {
156    shape_ref_as_shape(shape)?.check_value(cx, value)
157}
158
159/// Checks an expression against the shape behind a shape reference.
160pub fn check_shape_expr(
161    cx: &mut Cx,
162    shape: &ShapeRef,
163    expr: &sim_kernel::Expr,
164) -> Result<ShapeMatch> {
165    shape_ref_as_shape(shape)?.check_expr(cx, expr)
166}