Skip to main content

sim_lib_expr_tree/
shape.rs

1use std::sync::Arc;
2
3use sim_kernel::{Cx, Expr, MatchScore, Result, Shape, ShapeDoc, ShapeMatch, Symbol, Value};
4use sim_shape::shape_value;
5
6/// Symbol for one operation's argument-list Shape.
7pub fn operation_args_shape_symbol(operation: &str) -> Symbol {
8    Symbol::qualified(format!("expr-tree/{operation}"), "Args")
9}
10
11/// Symbol for one operation's result Shape.
12pub fn operation_result_shape_symbol(operation: &str) -> Symbol {
13    Symbol::qualified(format!("expr-tree/{operation}"), "Result")
14}
15
16pub(crate) fn argument_shape(
17    operation: &'static str,
18    min_args: usize,
19    max_args: usize,
20    detail: &'static str,
21) -> Value {
22    let symbol = operation_args_shape_symbol(operation);
23    shape_value(
24        symbol.clone(),
25        Arc::new(OperationArgsShape {
26            symbol,
27            operation,
28            min_args,
29            max_args,
30            detail,
31        }),
32    )
33}
34
35pub(crate) fn result_shape(operation: &'static str, detail: &'static str) -> Value {
36    let symbol = operation_result_shape_symbol(operation);
37    shape_value(
38        symbol.clone(),
39        Arc::new(OperationResultShape {
40            symbol,
41            operation,
42            detail,
43        }),
44    )
45}
46
47struct OperationArgsShape {
48    symbol: Symbol,
49    operation: &'static str,
50    min_args: usize,
51    max_args: usize,
52    detail: &'static str,
53}
54
55impl Shape for OperationArgsShape {
56    fn symbol(&self) -> Option<Symbol> {
57        Some(self.symbol.clone())
58    }
59
60    fn check_value(&self, cx: &mut Cx, value: Value) -> Result<ShapeMatch> {
61        let expression = value.object().as_expr(cx)?;
62        self.check_expr(cx, &expression)
63    }
64
65    fn check_expr(&self, _cx: &mut Cx, expr: &Expr) -> Result<ShapeMatch> {
66        let Expr::List(items) = expr else {
67            return Ok(ShapeMatch::reject(format!(
68                "expr-tree/{} arguments must be a list",
69                self.operation
70            )));
71        };
72        if (self.min_args..=self.max_args).contains(&items.len()) {
73            Ok(ShapeMatch::accept(MatchScore::exact(100)))
74        } else {
75            Ok(ShapeMatch::reject(format!(
76                "expr-tree/{} expects {}..={} arguments, found {}",
77                self.operation,
78                self.min_args,
79                self.max_args,
80                items.len()
81            )))
82        }
83    }
84
85    fn describe(&self, _cx: &mut Cx) -> Result<ShapeDoc> {
86        Ok(
87            ShapeDoc::new(format!("expr-tree/{} argument list", self.operation))
88                .with_detail(self.detail)
89                .with_detail(format!(
90                    "bounded arity {}..={}",
91                    self.min_args, self.max_args
92                )),
93        )
94    }
95}
96
97struct OperationResultShape {
98    symbol: Symbol,
99    operation: &'static str,
100    detail: &'static str,
101}
102
103impl Shape for OperationResultShape {
104    fn symbol(&self) -> Option<Symbol> {
105        Some(self.symbol.clone())
106    }
107
108    fn is_total(&self) -> bool {
109        true
110    }
111
112    fn check_value(&self, _cx: &mut Cx, _value: Value) -> Result<ShapeMatch> {
113        Ok(ShapeMatch::accept(MatchScore::exact(1)))
114    }
115
116    fn check_expr(&self, _cx: &mut Cx, _expr: &Expr) -> Result<ShapeMatch> {
117        Ok(ShapeMatch::accept(MatchScore::exact(1)))
118    }
119
120    fn describe(&self, _cx: &mut Cx) -> Result<ShapeDoc> {
121        Ok(
122            ShapeDoc::new(format!("expr-tree/{} result", self.operation))
123                .with_detail(self.detail)
124                .with_detail("ordinary bounded SIM value or opaque live handle"),
125        )
126    }
127}