Skip to main content

sim_shape/primitives/combinators/
capture.rs

1//! Capture combinator for binding matched shape inputs.
2
3use std::sync::Arc;
4
5use sim_kernel::{Cx, Expr, Result, ShapeRef, Symbol, Value, shape_is_subshape_of};
6
7use crate::base::{Shape, ShapeDoc, ShapeMatch};
8use crate::diagnostics::{binding_failure_diagnostic, expr_actual_label};
9
10/// A shape that wraps an inner shape and binds the match under a name.
11///
12/// When the inner shape accepts, the matched expression (and value, where the
13/// inner shape is not total) is recorded in the match captures under `name`,
14/// feeding shape-driven binding.
15pub struct CaptureShape {
16    name: Symbol,
17    inner: Arc<dyn Shape>,
18}
19
20impl CaptureShape {
21    /// Build a capture that binds the inner shape's match under `name`.
22    pub fn new(name: Symbol, inner: Arc<dyn Shape>) -> Self {
23        Self { name, inner }
24    }
25
26    /// The capture name bound on a successful match.
27    pub fn name(&self) -> &Symbol {
28        &self.name
29    }
30
31    /// The inner shape whose match is captured.
32    pub fn inner(&self) -> &Arc<dyn Shape> {
33        &self.inner
34    }
35}
36
37impl Shape for CaptureShape {
38    fn parents(&self, _cx: &mut Cx) -> Result<Vec<ShapeRef>> {
39        Ok(vec![crate::functions::shape_value(
40            Symbol::qualified("shape-capture-parent", self.name.to_string()),
41            self.inner.clone(),
42        )])
43    }
44
45    fn is_subshape_of(&self, cx: &mut Cx, parent: &dyn Shape) -> Result<Option<bool>> {
46        shape_is_subshape_of(cx, self.inner.as_ref(), parent).map(Some)
47    }
48
49    fn is_total(&self) -> bool {
50        self.inner.is_total()
51    }
52
53    fn is_effectful(&self) -> bool {
54        self.inner.is_effectful()
55    }
56
57    fn check_value(&self, cx: &mut Cx, value: Value) -> Result<ShapeMatch> {
58        let mut matched = self.inner.check_value(cx, value.clone())?;
59        if matched.accepted {
60            matched.captures.bind_value(self.name.clone(), value);
61            if !self.inner.is_total()
62                && let Ok(expr) = matched
63                    .captures
64                    .values()
65                    .last()
66                    .expect("just bound value capture")
67                    .1
68                    .object()
69                    .as_expr(cx)
70            {
71                matched.captures.bind_expr(self.name.clone(), expr);
72            }
73        } else {
74            matched.diagnostics.insert(
75                0,
76                binding_failure_diagnostic(&self.name, "captured value", "rejected value"),
77            );
78        }
79        Ok(matched)
80    }
81
82    fn check_expr(&self, cx: &mut Cx, expr: &Expr) -> Result<ShapeMatch> {
83        let mut matched = self.inner.check_expr(cx, expr)?;
84        if matched.accepted {
85            matched.captures.bind_expr(self.name.clone(), expr.clone());
86        } else {
87            matched.diagnostics.insert(
88                0,
89                binding_failure_diagnostic(
90                    &self.name,
91                    "captured expression",
92                    expr_actual_label(expr),
93                ),
94            );
95        }
96        Ok(matched)
97    }
98
99    fn describe(&self, cx: &mut Cx) -> Result<ShapeDoc> {
100        let inner = self.inner.describe(cx)?;
101        Ok(ShapeDoc::new(format!("capture {}", self.name)).with_detail(inner.name))
102    }
103}