Skip to main content

sim_shape/primitives/combinators/
one_of.rs

1//! Alternation combinator over multiple candidate shapes.
2
3use std::sync::Arc;
4
5use sim_kernel::{Cx, Expr, Result, Value, shape_is_subshape_of};
6
7use crate::base::{Bindings, MatchScore, Shape, ShapeDoc, ShapeMatch};
8
9/// A shape that matches if any one of its choice shapes matches.
10///
11/// Choices are tried in order and the first accepting match wins; if none
12/// accept, the rejection collects every choice's diagnostics. Total if any
13/// choice is total.
14pub struct OneOfShape {
15    choices: Vec<Arc<dyn Shape>>,
16}
17
18impl OneOfShape {
19    /// Build an alternation over the given choice shapes.
20    pub fn new(choices: Vec<Arc<dyn Shape>>) -> Self {
21        Self { choices }
22    }
23
24    /// The choice shapes tried in order.
25    pub fn choices(&self) -> &[Arc<dyn Shape>] {
26        &self.choices
27    }
28}
29
30impl Shape for OneOfShape {
31    fn is_effectful(&self) -> bool {
32        self.choices.iter().any(|choice| choice.is_effectful())
33    }
34
35    fn is_total(&self) -> bool {
36        self.choices.iter().any(|choice| choice.is_total())
37    }
38
39    fn is_subshape_of(&self, cx: &mut Cx, parent: &dyn Shape) -> Result<Option<bool>> {
40        for choice in &self.choices {
41            if !shape_is_subshape_of(cx, choice.as_ref(), parent)? {
42                return Ok(Some(false));
43            }
44        }
45        Ok(Some(true))
46    }
47
48    fn check_value(&self, cx: &mut Cx, value: Value) -> Result<ShapeMatch> {
49        let mut diagnostics = Vec::new();
50        for choice in &self.choices {
51            let matched = choice.check_value(cx, value.clone())?;
52            if matched.accepted {
53                return Ok(matched);
54            }
55            diagnostics.extend(matched.diagnostics);
56        }
57        Ok(ShapeMatch {
58            accepted: false,
59            captures: Bindings::new(),
60            score: MatchScore::reject(),
61            diagnostics,
62        })
63    }
64
65    fn check_expr(&self, cx: &mut Cx, expr: &Expr) -> Result<ShapeMatch> {
66        let mut diagnostics = Vec::new();
67        for choice in &self.choices {
68            let matched = choice.check_expr(cx, expr)?;
69            if matched.accepted {
70                return Ok(matched);
71            }
72            diagnostics.extend(matched.diagnostics);
73        }
74        Ok(ShapeMatch {
75            accepted: false,
76            captures: Bindings::new(),
77            score: MatchScore::reject(),
78            diagnostics,
79        })
80    }
81
82    fn describe(&self, cx: &mut Cx) -> Result<ShapeDoc> {
83        let mut doc = ShapeDoc::new("one-of shape");
84        for choice in &self.choices {
85            doc = doc.with_detail(choice.describe(cx)?.name);
86        }
87        Ok(doc)
88    }
89}