1use std::sync::Arc;
5
6use sim_kernel::{Cx, Error, Expr, Result, ShapeId, Symbol, Value};
7
8use crate::ExprKind;
9use crate::algebra::{
10 AndShape, NotShape, OrShape, RepeatShape, TableExtraPolicy, TableFieldSpec, TableShape,
11};
12use crate::base::{Shape, ShapeMatch};
13use crate::primitives::{
14 AnyShape, CaptureShape, ClassShape, ExprKindShape, FieldShape, FieldSpec, ListShape,
15 NumberValueShape,
16};
17
18pub fn parse_shape_expr(expr: &Expr) -> Result<Arc<dyn Shape>> {
43 let Some(_guard) = crate::recursion::DepthGuard::enter() else {
46 return Err(Error::Eval(
47 "shape grammar nesting exceeds the recursion budget".to_owned(),
48 ));
49 };
50 match expr {
51 Expr::Symbol(symbol) => Ok(match symbol.name.as_ref() {
52 "Any" if symbol.namespace.is_none() => Arc::new(AnyShape),
53 "Number" if symbol.namespace.is_none() => {
54 Arc::new(ExprKindShape::new(ExprKind::Number))
55 }
56 "Number" if symbol.namespace.as_deref() == Some("core") => Arc::new(NumberValueShape),
57 "String" if symbol.namespace.is_none() => {
58 Arc::new(ExprKindShape::new(ExprKind::String))
59 }
60 "Bool" if symbol.namespace.is_none() => Arc::new(ExprKindShape::new(ExprKind::Bool)),
61 "Symbol" if symbol.namespace.is_none() => {
62 Arc::new(ExprKindShape::new(ExprKind::Symbol))
63 }
64 "Map" if symbol.namespace.is_none() => Arc::new(ExprKindShape::new(ExprKind::Map)),
65 "List" if symbol.namespace.is_none() => Arc::new(ExprKindShape::new(ExprKind::List)),
66 "Nil" if symbol.namespace.is_none() => Arc::new(ExprKindShape::new(ExprKind::Nil)),
67 _ => Arc::new(ClassShape::new(symbol.clone())),
68 }),
69 Expr::List(items) => parse_shape_list(items),
70 other => Err(Error::Eval(format!(
71 "cannot build shape from expression kind {:?}",
72 other
73 ))),
74 }
75}
76
77fn parse_shape_list(items: &[Expr]) -> Result<Arc<dyn Shape>> {
78 let Some(Expr::Symbol(head)) = items.first() else {
79 return parse_tuple_shape(items);
80 };
81
82 match shape_form_name(head) {
83 Some("and" | "all") => Ok(Arc::new(AndShape::new(parse_shape_items(
84 shape_sequence_args(head, &items[1..]),
85 )?))),
86 Some("or" | "any") => Ok(Arc::new(OrShape::new(parse_shape_items(
87 shape_sequence_args(head, &items[1..]),
88 )?))),
89 Some("not" | "none") => {
90 expect_arity(head, items, 2)?;
91 Ok(Arc::new(NotShape::new(parse_shape_expr(&items[1])?)))
92 }
93 Some("capture") => parse_capture_shape(head, items),
94 Some("fields") => parse_fields_shape(&items[1..]),
95 Some("list") => Ok(Arc::new(ListShape::new(parse_shape_items(
96 shape_sequence_args(head, &items[1..]),
97 )?))),
98 Some("list-rest") => parse_list_rest_shape(head, items),
99 Some("repeat") => {
100 expect_arity(head, items, 2)?;
101 Ok(Arc::new(RepeatShape::new(parse_shape_expr(&items[1])?)))
102 }
103 Some("repeat-bounds") => parse_repeat_bounds_shape(head, items),
104 Some("table") => parse_single_table_shape(head, items),
105 Some("table-required" | "table-open") => {
106 parse_table_shape(&items[1..], TableExtraPolicy::Allow)
107 }
108 Some("table-closed") => parse_table_shape(&items[1..], TableExtraPolicy::Reject),
109 Some("without" | "difference") => parse_without_shape(head, items),
110 _ => parse_tuple_shape(items),
111 }
112}
113
114fn parse_tuple_shape(items: &[Expr]) -> Result<Arc<dyn Shape>> {
115 let items = items
116 .iter()
117 .map(parse_shape_expr)
118 .collect::<Result<Vec<_>>>()?;
119 Ok(Arc::new(ListShape::new(items)))
120}
121
122fn parse_shape_items(items: &[Expr]) -> Result<Vec<Arc<dyn Shape>>> {
123 items.iter().map(parse_shape_expr).collect()
124}
125
126fn shape_sequence_args<'a>(head: &Symbol, items: &'a [Expr]) -> &'a [Expr] {
127 if head.namespace.as_deref() == Some("shape")
128 && let [Expr::List(shapes)] = items
129 {
130 return shapes;
131 }
132 items
133}
134
135fn parse_capture_shape(head: &Symbol, items: &[Expr]) -> Result<Arc<dyn Shape>> {
136 expect_arity(head, items, 3)?;
137 let Expr::Symbol(name) = &items[1] else {
138 return Err(Error::Eval("capture name must be a symbol".to_owned()));
139 };
140 Ok(Arc::new(CaptureShape::new(
141 name.clone(),
142 parse_shape_expr(&items[2])?,
143 )))
144}
145
146fn parse_fields_shape(items: &[Expr]) -> Result<Arc<dyn Shape>> {
147 let specs = items
148 .iter()
149 .map(parse_field_spec_expr)
150 .collect::<Result<Vec<_>>>()?;
151 Ok(Arc::new(FieldShape::anonymous(specs)))
152}
153
154fn parse_list_rest_shape(head: &Symbol, items: &[Expr]) -> Result<Arc<dyn Shape>> {
155 expect_arity(head, items, 3)?;
156 let Expr::List(prefix) = &items[1] else {
157 return Err(Error::Eval(
158 "list-rest prefix must be a list of shapes".to_owned(),
159 ));
160 };
161 Ok(Arc::new(ListShape::with_rest(
162 parse_shape_items(prefix)?,
163 parse_shape_expr(&items[2])?,
164 )))
165}
166
167fn parse_repeat_bounds_shape(head: &Symbol, items: &[Expr]) -> Result<Arc<dyn Shape>> {
168 expect_arity(head, items, 4)?;
169 let min = parse_usize_expr(&items[2], "repeat-bounds min")?;
170 let max = parse_optional_usize_expr(&items[3], "repeat-bounds max")?;
171 if matches!(max, Some(max) if max < min) {
172 return Err(Error::Eval(
173 "repeat-bounds max must be greater than or equal to min".to_owned(),
174 ));
175 }
176 Ok(Arc::new(RepeatShape::with_bounds(
177 parse_shape_expr(&items[1])?,
178 min,
179 max,
180 )))
181}
182
183fn parse_table_shape(items: &[Expr], extra: TableExtraPolicy) -> Result<Arc<dyn Shape>> {
184 let fields = table_field_exprs(items)
185 .iter()
186 .map(parse_table_field_spec_expr)
187 .collect::<Result<Vec<_>>>()?;
188 Ok(Arc::new(TableShape::new(fields, extra)))
189}
190
191fn parse_single_table_shape(head: &Symbol, items: &[Expr]) -> Result<Arc<dyn Shape>> {
192 expect_arity(head, items, 3)?;
193 let Expr::Symbol(name) = &items[1] else {
194 return Err(Error::Eval("table key must be a symbol".to_owned()));
195 };
196 Ok(Arc::new(TableShape::single(
197 normalize_field_symbol(name),
198 parse_shape_expr(&items[2])?,
199 )))
200}
201
202fn table_field_exprs(items: &[Expr]) -> &[Expr] {
203 if let [Expr::List(fields)] = items
204 && (fields.is_empty() || fields.iter().all(is_table_field_expr))
205 {
206 return fields;
207 }
208 items
209}
210
211fn is_table_field_expr(expr: &Expr) -> bool {
212 matches!(expr, Expr::List(items) if matches!(items.as_slice(), [Expr::Symbol(_), _]))
213}
214
215fn parse_table_field_spec_expr(expr: &Expr) -> Result<TableFieldSpec> {
216 let Expr::List(items) = expr else {
217 return Err(Error::Eval("table field shape must be a list".to_owned()));
218 };
219 let [Expr::Symbol(name), shape] = items.as_slice() else {
220 return Err(Error::Eval(
221 "table field shape must be of the form (:field Shape)".to_owned(),
222 ));
223 };
224 Ok(TableFieldSpec {
225 key: normalize_field_symbol(name),
226 shape: parse_shape_expr(shape)?,
227 required: true,
228 })
229}
230
231fn parse_without_shape(head: &Symbol, items: &[Expr]) -> Result<Arc<dyn Shape>> {
232 expect_arity(head, items, 3)?;
233 let left = parse_shape_expr(&items[1])?;
234 let right = parse_shape_expr(&items[2])?;
235 let negated_right: Arc<dyn Shape> = Arc::new(NotShape::new(right));
236 Ok(Arc::new(AndShape::new(vec![left, negated_right])))
237}
238
239fn parse_optional_usize_expr(expr: &Expr, context: &str) -> Result<Option<usize>> {
240 if matches!(expr, Expr::Nil) {
241 Ok(None)
242 } else {
243 parse_usize_expr(expr, context).map(Some)
244 }
245}
246
247fn parse_usize_expr(expr: &Expr, context: &str) -> Result<usize> {
248 let Expr::Number(number) = expr else {
249 return Err(Error::Eval(format!("{context} expects a number")));
250 };
251 number
252 .canonical
253 .parse::<usize>()
254 .map_err(|_| Error::Eval(format!("{context} expects a non-negative integer")))
255}
256
257fn shape_form_name(symbol: &Symbol) -> Option<&str> {
258 if symbol.namespace.is_none() || symbol.namespace.as_deref() == Some("shape") {
259 Some(symbol.name.as_ref())
260 } else {
261 None
262 }
263}
264
265fn expect_arity(head: &Symbol, items: &[Expr], expected: usize) -> Result<()> {
266 if items.len() == expected {
267 Ok(())
268 } else {
269 Err(Error::Eval(format!(
270 "{head} expects {} argument(s), got {}",
271 expected - 1,
272 items.len().saturating_sub(1)
273 )))
274 }
275}
276
277fn parse_field_spec_expr(expr: &Expr) -> Result<FieldSpec> {
278 let Expr::List(items) = expr else {
279 return Err(Error::Eval("field shape must be a list".to_owned()));
280 };
281 let [Expr::Symbol(name), shape] = items.as_slice() else {
282 return Err(Error::Eval(
283 "field shape must be of the form (:field Shape)".to_owned(),
284 ));
285 };
286 Ok(FieldSpec::required(
287 normalize_field_symbol(name),
288 parse_shape_expr(shape)?,
289 ))
290}
291
292fn normalize_field_symbol(symbol: &Symbol) -> Symbol {
293 if symbol.namespace.is_none()
294 && let Some(stripped) = symbol.name.strip_prefix(':')
295 {
296 return Symbol::new(stripped.to_owned());
297 }
298 symbol.clone()
299}
300
301pub fn check_shape_on_expr(shape: &dyn Shape, cx: &mut Cx, expr: &Expr) -> Result<ShapeMatch> {
306 shape.check_expr(cx, expr)
307}
308
309pub fn check_shape_on_value(shape: &dyn Shape, cx: &mut Cx, value: Value) -> Result<ShapeMatch> {
314 shape.check_value(cx, value)
315}
316
317pub fn shape_error(expected: &dyn Shape, cx: &mut Cx, expr: &Expr) -> Result<Error> {
323 let matched = expected.check_expr(cx, expr)?;
324 if matched.accepted {
325 Err(Error::HostError(
326 "shape_error called for an accepted shape".to_owned(),
327 ))
328 } else {
329 Ok(Error::WrongShape {
330 expected: expected.id().unwrap_or(ShapeId(0)),
331 diagnostics: matched.diagnostics,
332 })
333 }
334}