1use std::sync::Arc;
5
6use sim_kernel::{Cx, Error, Expr, Result, ShapeId, Symbol, Value};
7
8use crate::ExprKind;
9use crate::base::{Shape, ShapeMatch};
10use crate::primitives::{
11 AnyShape, CaptureShape, ClassShape, ExprKindShape, FieldShape, FieldSpec, ListShape,
12 NumberValueShape,
13};
14
15pub fn parse_shape_expr(expr: &Expr) -> Result<Arc<dyn Shape>> {
37 let Some(_guard) = crate::recursion::DepthGuard::enter() else {
40 return Err(Error::Eval(
41 "shape grammar nesting exceeds the recursion budget".to_owned(),
42 ));
43 };
44 match expr {
45 Expr::Symbol(symbol) => Ok(match symbol.name.as_ref() {
46 "Any" if symbol.namespace.is_none() => Arc::new(AnyShape),
47 "Number" if symbol.namespace.is_none() => {
48 Arc::new(ExprKindShape::new(ExprKind::Number))
49 }
50 "Number" if symbol.namespace.as_deref() == Some("core") => Arc::new(NumberValueShape),
51 "String" if symbol.namespace.is_none() => {
52 Arc::new(ExprKindShape::new(ExprKind::String))
53 }
54 "Bool" if symbol.namespace.is_none() => Arc::new(ExprKindShape::new(ExprKind::Bool)),
55 "Symbol" if symbol.namespace.is_none() => {
56 Arc::new(ExprKindShape::new(ExprKind::Symbol))
57 }
58 "Map" if symbol.namespace.is_none() => Arc::new(ExprKindShape::new(ExprKind::Map)),
59 "List" if symbol.namespace.is_none() => Arc::new(ExprKindShape::new(ExprKind::List)),
60 "Nil" if symbol.namespace.is_none() => Arc::new(ExprKindShape::new(ExprKind::Nil)),
61 _ => Arc::new(ClassShape::new(symbol.clone())),
62 }),
63 Expr::List(items) => parse_shape_list(items),
64 other => Err(Error::Eval(format!(
65 "cannot build shape from expression kind {:?}",
66 other
67 ))),
68 }
69}
70
71fn parse_shape_list(items: &[Expr]) -> Result<Arc<dyn Shape>> {
72 let Some(Expr::Symbol(head)) = items.first() else {
73 let items = items
74 .iter()
75 .map(parse_shape_expr)
76 .collect::<Result<Vec<_>>>()?;
77 return Ok(Arc::new(ListShape::new(items)));
78 };
79
80 if head.namespace.is_none() && head.name.as_ref() == "capture" && items.len() == 3 {
81 let Expr::Symbol(name) = &items[1] else {
82 return Err(Error::Eval("capture name must be a symbol".to_owned()));
83 };
84 return Ok(Arc::new(CaptureShape::new(
85 name.clone(),
86 parse_shape_expr(&items[2])?,
87 )));
88 }
89
90 if head.namespace.is_none() && head.name.as_ref() == "fields" {
91 let specs = items
92 .iter()
93 .skip(1)
94 .map(parse_field_spec_expr)
95 .collect::<Result<Vec<_>>>()?;
96 return Ok(Arc::new(FieldShape::anonymous(specs)));
97 }
98
99 let items = items
100 .iter()
101 .map(parse_shape_expr)
102 .collect::<Result<Vec<_>>>()?;
103 Ok(Arc::new(ListShape::new(items)))
104}
105
106fn parse_field_spec_expr(expr: &Expr) -> Result<FieldSpec> {
107 let Expr::List(items) = expr else {
108 return Err(Error::Eval("field shape must be a list".to_owned()));
109 };
110 let [Expr::Symbol(name), shape] = items.as_slice() else {
111 return Err(Error::Eval(
112 "field shape must be of the form (:field Shape)".to_owned(),
113 ));
114 };
115 Ok(FieldSpec::required(
116 normalize_field_symbol(name),
117 parse_shape_expr(shape)?,
118 ))
119}
120
121fn normalize_field_symbol(symbol: &Symbol) -> Symbol {
122 if symbol.namespace.is_none()
123 && let Some(stripped) = symbol.name.strip_prefix(':')
124 {
125 return Symbol::new(stripped.to_owned());
126 }
127 symbol.clone()
128}
129
130pub fn check_shape_on_expr(shape: &dyn Shape, cx: &mut Cx, expr: &Expr) -> Result<ShapeMatch> {
135 shape.check_expr(cx, expr)
136}
137
138pub fn check_shape_on_value(shape: &dyn Shape, cx: &mut Cx, value: Value) -> Result<ShapeMatch> {
143 shape.check_value(cx, value)
144}
145
146pub fn shape_error(expected: &dyn Shape, cx: &mut Cx, expr: &Expr) -> Result<Error> {
152 let matched = expected.check_expr(cx, expr)?;
153 if matched.accepted {
154 Err(Error::HostError(
155 "shape_error called for an accepted shape".to_owned(),
156 ))
157 } else {
158 Ok(Error::WrongShape {
159 expected: expected.id().unwrap_or(ShapeId(0)),
160 diagnostics: matched.diagnostics,
161 })
162 }
163}