1use crate::base::{ExprKind, MatchScore, Shape, ShapeDoc, ShapeMatch};
5use crate::diagnostics::{expected_shape_diagnostic, expr_actual_label};
6use crate::functions::shape_value;
7use crate::primitives::object::ObjectExpr;
8use crate::recursion::{DepthGuard, class_is_subclass_of_guarded, is_cyclic_parent_edge};
9use sim_kernel::{Cx, Expr, Result, ShapeRef, Symbol, Value};
10
11pub struct AnyShape;
29
30impl Shape for AnyShape {
31 fn symbol(&self) -> Option<Symbol> {
32 Some(Symbol::qualified("core", "Any"))
33 }
34
35 fn is_total(&self) -> bool {
36 true
37 }
38
39 fn check_value(&self, _cx: &mut Cx, _value: Value) -> Result<ShapeMatch> {
40 Ok(ShapeMatch::accept(MatchScore::exact(0)))
41 }
42
43 fn check_expr(&self, _cx: &mut Cx, _expr: &Expr) -> Result<ShapeMatch> {
44 Ok(ShapeMatch::accept(MatchScore::exact(0)))
45 }
46
47 fn describe(&self, _cx: &mut Cx) -> Result<ShapeDoc> {
48 Ok(ShapeDoc::new("Any"))
49 }
50}
51
52pub struct ExprKindShape {
57 kind: ExprKind,
58}
59
60impl ExprKindShape {
61 pub fn new(kind: ExprKind) -> Self {
63 Self { kind }
64 }
65
66 pub fn kind(&self) -> &ExprKind {
68 &self.kind
69 }
70}
71
72impl Shape for ExprKindShape {
73 fn parents(&self, cx: &mut Cx) -> Result<Vec<ShapeRef>> {
74 Ok(cx
75 .registry()
76 .shape_by_symbol(&Symbol::qualified("core", "Expr"))
77 .cloned()
78 .into_iter()
79 .collect())
80 }
81
82 fn is_subshape_of(&self, _cx: &mut Cx, parent: &dyn Shape) -> Result<Option<bool>> {
83 if let Some(parent) = parent.as_any().downcast_ref::<Self>() {
84 return Ok(Some(self.kind == parent.kind));
85 }
86 if parent.as_any().is::<ExactExprShape>()
87 || matches!(
88 parent.symbol(),
89 Some(symbol) if symbol == Symbol::qualified("core", "ExactExprShape")
90 )
91 {
92 return Ok(Some(false));
93 }
94 Ok(matches!(
95 parent.symbol(),
96 Some(symbol) if symbol == Symbol::qualified("core", "Expr")
97 )
98 .then_some(true))
99 }
100
101 fn check_value(&self, cx: &mut Cx, value: Value) -> Result<ShapeMatch> {
102 let expr = value.object().as_expr(cx)?;
103 self.check_expr(cx, &expr)
104 }
105
106 fn check_expr(&self, _cx: &mut Cx, expr: &Expr) -> Result<ShapeMatch> {
107 if self.kind.matches(expr) {
108 Ok(ShapeMatch::accept(MatchScore::exact(10)))
109 } else {
110 Ok(ShapeMatch::reject_with_diagnostic(
111 expected_shape_diagnostic(
112 format!("{} expression", self.kind.name()),
113 expr_actual_label(expr),
114 ),
115 ))
116 }
117 }
118
119 fn describe(&self, _cx: &mut Cx) -> Result<ShapeDoc> {
120 Ok(ShapeDoc::new(format!("expr-kind {}", self.kind.name())))
121 }
122}
123
124pub struct NumberValueShape;
130
131impl Shape for NumberValueShape {
132 fn symbol(&self) -> Option<Symbol> {
133 Some(Symbol::qualified("core", "Number"))
134 }
135
136 fn check_value(&self, cx: &mut Cx, value: Value) -> Result<ShapeMatch> {
137 if cx.number_value_ref(value)?.is_some() {
138 Ok(ShapeMatch::accept(MatchScore::exact(20)))
139 } else {
140 Ok(ShapeMatch::reject_with_diagnostic(
141 expected_shape_diagnostic("number value", "non-number value"),
142 ))
143 }
144 }
145
146 fn check_expr(&self, _cx: &mut Cx, expr: &Expr) -> Result<ShapeMatch> {
147 if matches!(expr, Expr::Number(_)) {
148 Ok(ShapeMatch::accept(MatchScore::exact(10)))
149 } else {
150 Ok(ShapeMatch::reject_with_diagnostic(
151 expected_shape_diagnostic("number expression", expr_actual_label(expr)),
152 ))
153 }
154 }
155
156 fn describe(&self, _cx: &mut Cx) -> Result<ShapeDoc> {
157 Ok(ShapeDoc::new("number value"))
158 }
159}
160
161pub struct ClassShape {
167 symbol: Symbol,
168}
169
170impl ClassShape {
171 pub fn new(symbol: Symbol) -> Self {
173 Self { symbol }
174 }
175
176 pub fn symbol(&self) -> &Symbol {
178 &self.symbol
179 }
180}
181
182impl Shape for ClassShape {
183 fn parents(&self, cx: &mut Cx) -> Result<Vec<ShapeRef>> {
184 let Some(class_value) = cx.registry().class_by_symbol(&self.symbol).cloned() else {
185 return Ok(Vec::new());
186 };
187 let Some(class) = class_value.object().as_class() else {
188 return Ok(Vec::new());
189 };
190 let child_id = class.id();
191 let parent_classes = class.parents(cx)?;
192 let mut out = Vec::new();
193 for parent in parent_classes {
194 let Some(parent_class) = parent.object().as_class() else {
195 continue;
196 };
197 if is_cyclic_parent_edge(cx, child_id, parent_class)? {
200 continue;
201 }
202 out.push(shape_value(
203 Symbol::qualified("shape-class-parent", parent_class.symbol().to_string()),
204 std::sync::Arc::new(ClassShape::new(parent_class.symbol())),
205 ));
206 }
207 Ok(out)
208 }
209
210 fn is_subshape_of(&self, cx: &mut Cx, parent: &dyn Shape) -> Result<Option<bool>> {
211 let Some(parent) = parent.as_any().downcast_ref::<Self>() else {
212 return Ok(None);
213 };
214 if self.symbol == parent.symbol {
215 return Ok(Some(true));
216 }
217 let Some(child_class) = cx.registry().class_by_symbol(&self.symbol).cloned() else {
218 return Ok(Some(false));
219 };
220 let Some(child_class) = child_class.object().as_class() else {
221 return Ok(Some(false));
222 };
223 let Some(parent_class) = cx.registry().class_by_symbol(&parent.symbol).cloned() else {
224 return Ok(Some(false));
225 };
226 class_is_subclass_of_guarded(cx, child_class, parent_class).map(Some)
227 }
228
229 fn check_value(&self, cx: &mut Cx, value: Value) -> Result<ShapeMatch> {
230 if let Some(class) = value.object().as_class()
231 && class_matches(cx, class, &self.symbol)?
232 {
233 return Ok(ShapeMatch::accept(MatchScore::exact(30)));
234 }
235
236 let class = value.object().class(cx)?;
237 let Some(class) = class.object().as_class() else {
238 return Ok(ShapeMatch::reject_with_diagnostic(
239 expected_shape_diagnostic("class-backed value", "value without class metadata"),
240 ));
241 };
242 if class_matches(cx, class, &self.symbol)? {
243 Ok(ShapeMatch::accept(MatchScore::exact(30)))
244 } else {
245 Ok(ShapeMatch::reject_with_diagnostic(
246 expected_shape_diagnostic(
247 format!("class {}", self.symbol),
248 format!("class {}", class.symbol()),
249 ),
250 ))
251 }
252 }
253
254 fn check_expr(&self, cx: &mut Cx, expr: &Expr) -> Result<ShapeMatch> {
255 match expr {
256 Expr::Symbol(symbol) if class_symbol_matches(cx, symbol, &self.symbol)? => {
257 Ok(ShapeMatch::accept(MatchScore::exact(20)))
258 }
259 _ => {
260 let object = match ObjectExpr::parse(expr) {
261 Some(object) => object,
262 None => {
263 return Ok(ShapeMatch::reject_with_diagnostic(
264 expected_shape_diagnostic(
265 format!("class {}", self.symbol),
266 expr_actual_label(expr),
267 ),
268 ));
269 }
270 };
271 if !class_symbol_matches(cx, &object.class, &self.symbol)? {
272 return Ok(ShapeMatch::reject_with_diagnostic(
273 expected_shape_diagnostic(
274 format!("class {}", self.symbol),
275 format!("class {}", object.class),
276 ),
277 ));
278 }
279 if let Some(class_value) = cx.registry().class_by_symbol(&self.symbol).cloned()
280 && let Some(class) = class_value.object().as_class()
281 {
282 let shape = class.instance_shape(cx)?;
283 if let Some(shape) = shape.object().as_shape() {
284 let Some(_guard) = DepthGuard::enter() else {
288 return Ok(ShapeMatch::reject_with_diagnostic(
289 expected_shape_diagnostic(
290 format!("class {} within shape recursion budget", self.symbol),
291 "shape recursion budget exceeded",
292 ),
293 ));
294 };
295 let matched = shape.check_expr(cx, expr)?;
296 if matched.accepted {
297 return Ok(ShapeMatch::accept(MatchScore::exact(30)));
298 }
299 return Ok(matched);
300 }
301 }
302 Ok(ShapeMatch::accept(MatchScore::exact(25)))
303 }
304 }
305 }
306
307 fn describe(&self, _cx: &mut Cx) -> Result<ShapeDoc> {
308 Ok(ShapeDoc::new(format!("class {}", self.symbol)))
309 }
310}
311
312pub struct ExactExprShape {
330 expected: Expr,
331}
332
333impl ExactExprShape {
334 pub fn new(expected: Expr) -> Self {
336 Self { expected }
337 }
338
339 pub fn expected(&self) -> &Expr {
341 &self.expected
342 }
343}
344
345impl Shape for ExactExprShape {
346 fn parents(&self, _cx: &mut Cx) -> Result<Vec<ShapeRef>> {
347 Ok(vec![shape_value(
348 Symbol::qualified("shape-exact-parent", expr_kind_of(&self.expected).name()),
349 std::sync::Arc::new(ExprKindShape::new(expr_kind_of(&self.expected))),
350 )])
351 }
352
353 fn is_subshape_of(&self, _cx: &mut Cx, parent: &dyn Shape) -> Result<Option<bool>> {
354 if let Some(parent) = parent.as_any().downcast_ref::<Self>() {
355 return Ok(Some(self.expected.canonical_eq(parent.expected())));
356 }
357 if let Some(parent) = parent.as_any().downcast_ref::<ExprKindShape>() {
358 return Ok(Some(parent.kind().matches(&self.expected)));
359 }
360 Ok(None)
361 }
362
363 fn check_value(&self, cx: &mut Cx, value: Value) -> Result<ShapeMatch> {
364 let expr = value.object().as_expr(cx)?;
365 self.check_expr(cx, &expr)
366 }
367
368 fn check_expr(&self, _cx: &mut Cx, expr: &Expr) -> Result<ShapeMatch> {
369 if self.expected.canonical_eq(expr) {
370 Ok(ShapeMatch::accept(MatchScore::exact(20)))
371 } else {
372 Ok(ShapeMatch::reject_with_diagnostic(
373 expected_shape_diagnostic("exact expression form", expr_actual_label(expr)),
374 ))
375 }
376 }
377
378 fn describe(&self, _cx: &mut Cx) -> Result<ShapeDoc> {
379 Ok(ShapeDoc::new("exact expr").with_detail(format!("{:?}", self.expected)))
380 }
381}
382
383fn class_matches(cx: &mut Cx, class: &dyn sim_kernel::Class, expected: &Symbol) -> Result<bool> {
384 if class.symbol() == *expected {
385 return Ok(true);
386 }
387 let Some(expected) = cx.registry().class_by_symbol(expected).cloned() else {
388 return Ok(false);
389 };
390 class_is_subclass_of_guarded(cx, class, expected)
391}
392
393fn class_symbol_matches(cx: &mut Cx, actual: &Symbol, expected: &Symbol) -> Result<bool> {
394 if actual == expected {
395 return Ok(true);
396 }
397 let Some(actual) = cx.registry().class_by_symbol(actual).cloned() else {
398 return Ok(false);
399 };
400 let Some(actual) = actual.object().as_class() else {
401 return Ok(false);
402 };
403 class_matches(cx, actual, expected)
404}
405
406fn expr_kind_of(expr: &Expr) -> ExprKind {
407 match expr {
408 Expr::Nil => ExprKind::Nil,
409 Expr::Bool(_) => ExprKind::Bool,
410 Expr::Number(_) => ExprKind::Number,
411 Expr::Symbol(_) => ExprKind::Symbol,
412 Expr::Local(_) => ExprKind::Symbol,
413 Expr::String(_) => ExprKind::String,
414 Expr::Bytes(_) => ExprKind::Bytes,
415 Expr::List(_) => ExprKind::List,
416 Expr::Vector(_) => ExprKind::Vector,
417 Expr::Map(_) => ExprKind::Map,
418 Expr::Set(_) => ExprKind::Set,
419 Expr::Call { .. } => ExprKind::Call,
420 Expr::Infix { .. } => ExprKind::Infix,
421 Expr::Prefix { .. } => ExprKind::Prefix,
422 Expr::Postfix { .. } => ExprKind::Postfix,
423 Expr::Block(_) => ExprKind::Block,
424 Expr::Quote { .. } => ExprKind::Quote,
425 Expr::Annotated { .. } => ExprKind::Annotated,
426 Expr::Extension { .. } => ExprKind::Extension,
427 }
428}