Skip to main content

sim_shape/primitives/combinators/
parser.rs

1//! Parser-backed shape combinator.
2
3use std::sync::Arc;
4
5use sim_kernel::{Cx, Error, Expr, Result, Value};
6
7use crate::base::{Shape, ShapeDoc, ShapeMatch};
8
9/// A parser that turns shape source text into an [`Expr`] for [`PrattShape`].
10///
11/// Implementations plug a concrete grammar (the codec layer, not the kernel)
12/// into shape matching; `parse_expr` should report a parse failure as
13/// `Error::Eval` so the shape rejects rather than aborts.
14pub trait ShapeExprParser: Send + Sync {
15    /// A short human-readable name for this parser, shown in shape descriptions.
16    fn label(&self) -> &str;
17
18    /// Whether parsing may run effects; `false` by default.
19    fn is_effectful(&self) -> bool {
20        false
21    }
22
23    /// Parse source text into an expression to match against the inner shape.
24    fn parse_expr(&self, source: &str) -> Result<Expr>;
25}
26
27/// A shape that parses a string expression with a [`ShapeExprParser`] before
28/// matching.
29///
30/// Accepts only `String` expressions: the text is parsed and the resulting
31/// expression is checked against the inner shape. Non-string inputs and parse
32/// failures reject.
33pub struct PrattShape {
34    parser: Arc<dyn ShapeExprParser>,
35    inner: Arc<dyn Shape>,
36}
37
38impl PrattShape {
39    /// Build a shape that parses string input with `parser`, then checks `inner`.
40    pub fn new(parser: Arc<dyn ShapeExprParser>, inner: Arc<dyn Shape>) -> Self {
41        Self { parser, inner }
42    }
43
44    /// The parser applied to string input.
45    pub fn parser(&self) -> &Arc<dyn ShapeExprParser> {
46        &self.parser
47    }
48
49    /// The inner shape checked against the parsed expression.
50    pub fn inner(&self) -> &Arc<dyn Shape> {
51        &self.inner
52    }
53}
54
55impl Shape for PrattShape {
56    fn is_effectful(&self) -> bool {
57        self.parser.is_effectful() || self.inner.is_effectful()
58    }
59
60    fn check_value(&self, cx: &mut Cx, value: Value) -> Result<ShapeMatch> {
61        let expr = value.object().as_expr(cx)?;
62        self.check_expr(cx, &expr)
63    }
64
65    fn check_expr(&self, cx: &mut Cx, expr: &Expr) -> Result<ShapeMatch> {
66        let source = match expr {
67            Expr::String(text) => text.as_str(),
68            _ => {
69                return Ok(ShapeMatch::reject(
70                    "expected string expression for Pratt parse",
71                ));
72            }
73        };
74        let parsed = match self.parser.parse_expr(source) {
75            Ok(parsed) => parsed,
76            Err(Error::Eval(message)) => return Ok(ShapeMatch::reject(message)),
77            Err(other) => return Err(other),
78        };
79        self.inner.check_expr(cx, &parsed)
80    }
81
82    fn describe(&self, cx: &mut Cx) -> Result<ShapeDoc> {
83        let inner = self.inner.describe(cx)?;
84        Ok(ShapeDoc::new("pratt shape")
85            .with_detail(self.parser.label().to_owned())
86            .with_detail(inner.name))
87    }
88}