sim_lib_expr_tree/
shape.rs1use std::sync::Arc;
2
3use sim_kernel::{Cx, Expr, MatchScore, Result, Shape, ShapeDoc, ShapeMatch, Symbol, Value};
4use sim_shape::shape_value;
5
6pub fn operation_args_shape_symbol(operation: &str) -> Symbol {
8 Symbol::qualified(format!("expr-tree/{operation}"), "Args")
9}
10
11pub 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}