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