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 if let Expr::Extension { tag, payload } = expr
256 && *tag == Symbol::qualified("citizen", "read-construct")
257 && let Expr::Vector(items) = payload.as_ref()
258 && let Some(Expr::Symbol(class)) = items.first()
259 && class_symbol_matches(cx, class, &self.symbol)?
260 {
261 return Ok(ShapeMatch::accept(MatchScore::exact(25)));
262 }
263 match expr {
264 Expr::Symbol(symbol) if class_symbol_matches(cx, symbol, &self.symbol)? => {
265 Ok(ShapeMatch::accept(MatchScore::exact(20)))
266 }
267 _ => {
268 let object = match ObjectExpr::parse(expr) {
269 Some(object) => object,
270 None => {
271 return Ok(ShapeMatch::reject_with_diagnostic(
272 expected_shape_diagnostic(
273 format!("class {}", self.symbol),
274 expr_actual_label(expr),
275 ),
276 ));
277 }
278 };
279 if !class_symbol_matches(cx, &object.class, &self.symbol)? {
280 return Ok(ShapeMatch::reject_with_diagnostic(
281 expected_shape_diagnostic(
282 format!("class {}", self.symbol),
283 format!("class {}", object.class),
284 ),
285 ));
286 }
287 if let Some(class_value) = cx.registry().class_by_symbol(&self.symbol).cloned()
288 && let Some(class) = class_value.object().as_class()
289 {
290 let shape = class.instance_shape(cx)?;
291 if let Some(shape) = shape.object().as_shape() {
292 let Some(_guard) = DepthGuard::enter() else {
296 return Ok(ShapeMatch::reject_with_diagnostic(
297 expected_shape_diagnostic(
298 format!("class {} within shape recursion budget", self.symbol),
299 "shape recursion budget exceeded",
300 ),
301 ));
302 };
303 let matched = shape.check_expr(cx, expr)?;
304 if matched.accepted {
305 return Ok(ShapeMatch::accept(MatchScore::exact(30)));
306 }
307 return Ok(matched);
308 }
309 }
310 Ok(ShapeMatch::accept(MatchScore::exact(25)))
311 }
312 }
313 }
314
315 fn describe(&self, _cx: &mut Cx) -> Result<ShapeDoc> {
316 Ok(ShapeDoc::new(format!("class {}", self.symbol)))
317 }
318}
319
320pub struct ExactExprShape {
338 expected: Expr,
339}
340
341impl ExactExprShape {
342 pub fn new(expected: Expr) -> Self {
344 Self { expected }
345 }
346
347 pub fn expected(&self) -> &Expr {
349 &self.expected
350 }
351}
352
353impl Shape for ExactExprShape {
354 fn parents(&self, _cx: &mut Cx) -> Result<Vec<ShapeRef>> {
355 Ok(vec![shape_value(
356 Symbol::qualified("shape-exact-parent", expr_kind_of(&self.expected).name()),
357 std::sync::Arc::new(ExprKindShape::new(expr_kind_of(&self.expected))),
358 )])
359 }
360
361 fn is_subshape_of(&self, _cx: &mut Cx, parent: &dyn Shape) -> Result<Option<bool>> {
362 if let Some(parent) = parent.as_any().downcast_ref::<Self>() {
363 return Ok(Some(self.expected.canonical_eq(parent.expected())));
364 }
365 if let Some(parent) = parent.as_any().downcast_ref::<ExprKindShape>() {
366 return Ok(Some(parent.kind().matches(&self.expected)));
367 }
368 Ok(None)
369 }
370
371 fn check_value(&self, cx: &mut Cx, value: Value) -> Result<ShapeMatch> {
372 let expr = value.object().as_expr(cx)?;
373 self.check_expr(cx, &expr)
374 }
375
376 fn check_expr(&self, _cx: &mut Cx, expr: &Expr) -> Result<ShapeMatch> {
377 if self.expected.canonical_eq(expr) {
378 Ok(ShapeMatch::accept(MatchScore::exact(20)))
379 } else {
380 Ok(ShapeMatch::reject_with_diagnostic(
381 expected_shape_diagnostic("exact expression form", expr_actual_label(expr)),
382 ))
383 }
384 }
385
386 fn describe(&self, _cx: &mut Cx) -> Result<ShapeDoc> {
387 Ok(ShapeDoc::new("exact expr").with_detail(format!("{:?}", self.expected)))
388 }
389}
390
391fn class_matches(cx: &mut Cx, class: &dyn sim_kernel::Class, expected: &Symbol) -> Result<bool> {
392 if class.symbol() == *expected {
393 return Ok(true);
394 }
395 let Some(expected) = cx.registry().class_by_symbol(expected).cloned() else {
396 return Ok(false);
397 };
398 class_is_subclass_of_guarded(cx, class, expected)
399}
400
401fn class_symbol_matches(cx: &mut Cx, actual: &Symbol, expected: &Symbol) -> Result<bool> {
402 if actual == expected {
403 return Ok(true);
404 }
405 let Some(actual) = cx.registry().class_by_symbol(actual).cloned() else {
406 return Ok(false);
407 };
408 let Some(actual) = actual.object().as_class() else {
409 return Ok(false);
410 };
411 class_matches(cx, actual, expected)
412}
413
414fn expr_kind_of(expr: &Expr) -> ExprKind {
415 match expr {
416 Expr::Nil => ExprKind::Nil,
417 Expr::Bool(_) => ExprKind::Bool,
418 Expr::Number(_) => ExprKind::Number,
419 Expr::Symbol(_) => ExprKind::Symbol,
420 Expr::Local(_) => ExprKind::Symbol,
421 Expr::String(_) => ExprKind::String,
422 Expr::Bytes(_) => ExprKind::Bytes,
423 Expr::List(_) => ExprKind::List,
424 Expr::Vector(_) => ExprKind::Vector,
425 Expr::Map(_) => ExprKind::Map,
426 Expr::Set(_) => ExprKind::Set,
427 Expr::Call { .. } => ExprKind::Call,
428 Expr::Infix { .. } => ExprKind::Infix,
429 Expr::Prefix { .. } => ExprKind::Prefix,
430 Expr::Postfix { .. } => ExprKind::Postfix,
431 Expr::Block(_) => ExprKind::Block,
432 Expr::Quote { .. } => ExprKind::Quote,
433 Expr::Annotated { .. } => ExprKind::Annotated,
434 Expr::Extension { .. } => ExprKind::Extension,
435 }
436}