1use std::{cmp::Ordering, sync::Arc};
5
6use sim_kernel::{Cx, Error, Expr, Result, ShapeId, ShapeRef, Value, shape_is_subshape_of};
7
8use crate::base::{Bindings, MatchScore, Shape, ShapeDoc, ShapeMatch};
9use crate::diagnostics::{
10 binding_failure_diagnostic, expected_shape_diagnostic, expr_actual_label,
11};
12
13pub struct ListShape {
34 items: Vec<Arc<dyn Shape>>,
35 rest: Option<Arc<dyn Shape>>,
36}
37
38impl ListShape {
39 pub fn new(items: Vec<Arc<dyn Shape>>) -> Self {
41 Self { items, rest: None }
42 }
43
44 pub fn tuple(items: Vec<Arc<dyn Shape>>) -> Self {
46 Self::new(items)
47 }
48
49 pub fn with_rest(items: Vec<Arc<dyn Shape>>, rest: Arc<dyn Shape>) -> Self {
51 Self {
52 items,
53 rest: Some(rest),
54 }
55 }
56
57 pub fn variadic(prefix: Vec<Arc<dyn Shape>>, rest: Arc<dyn Shape>) -> Self {
59 Self::with_rest(prefix, rest)
60 }
61
62 pub fn items(&self) -> &[Arc<dyn Shape>] {
64 &self.items
65 }
66
67 pub fn rest(&self) -> Option<&Arc<dyn Shape>> {
69 self.rest.as_ref()
70 }
71
72 fn check_list_value(&self, cx: &mut Cx, head: Value) -> Result<ShapeMatch> {
73 let min_len = self.items.len();
74 {
75 let Some(list) = head.object().as_list() else {
76 return Err(Error::Eval("expected a list value".to_owned()));
77 };
78 let len_cmp = list.len_cmp(cx, min_len)?;
79 if len_cmp == Ordering::Less {
80 return Ok(ShapeMatch::reject_with_diagnostic(
81 expected_shape_diagnostic(
82 format!("at least {min_len} list items"),
83 "fewer list items",
84 ),
85 ));
86 }
87 if self.rest.is_none() && len_cmp != Ordering::Equal {
88 return Ok(ShapeMatch::reject_with_diagnostic(
89 expected_shape_diagnostic(format!("{min_len} list items"), "more list items"),
90 ));
91 }
92 }
93
94 let mut out = ShapeMatch::accept(MatchScore::exact(20));
95 let mut current = Some(head);
98
99 for shape in &self.items {
100 let Some(node_value) = current.clone() else {
101 return Ok(ShapeMatch::reject_with_diagnostic(
102 expected_shape_diagnostic(
103 format!("at least {min_len} list items"),
104 "fewer list items",
105 ),
106 ));
107 };
108 let Some(node) = node_value.object().as_list() else {
109 return Err(Error::Eval("list cdr did not yield a list".to_owned()));
110 };
111 let Some(item) = node.car(cx)? else {
112 return Ok(ShapeMatch::reject_with_diagnostic(
113 expected_shape_diagnostic(
114 format!("at least {min_len} list items"),
115 "fewer list items",
116 ),
117 ));
118 };
119 let next = node.cdr(cx)?;
120 let matched = shape.check_value(cx, item)?;
121 if !matched.accepted {
122 return Ok(matched);
123 }
124 out.captures.extend(matched.captures);
125 out.score += matched.score;
126 current = next;
127 }
128
129 let Some(rest) = &self.rest else {
130 return Ok(out);
131 };
132
133 while let Some(node_value) = current.clone() {
141 let Some(node) = node_value.object().as_list() else {
142 break;
143 };
144 if node.is_empty(cx)? {
145 break;
146 }
147 let Some(item) = node.car(cx)? else {
148 break;
149 };
150 let next = node.cdr(cx)?;
151 let matched = rest.check_value(cx, item)?;
152 if !matched.accepted {
153 return Ok(matched);
154 }
155 let bound_nothing =
156 matched.captures.values().is_empty() && matched.captures.exprs().is_empty();
157 out.captures.extend(matched.captures);
158 out.score += matched.score;
159 current = next;
160 if rest.is_total() && bound_nothing {
161 break;
162 }
163 }
164
165 Ok(out)
166 }
167}
168
169impl Shape for ListShape {
170 fn is_subshape_of(&self, cx: &mut Cx, parent: &dyn Shape) -> Result<Option<bool>> {
171 let Some(parent) = parent.as_any().downcast_ref::<Self>() else {
172 return Ok(None);
173 };
174 if self.items.len() != parent.items.len() {
181 return Ok(None);
182 }
183 match (&self.rest, &parent.rest) {
184 (None, None) => {}
185 (Some(child_rest), Some(parent_rest)) => {
186 if !shape_is_subshape_of(cx, child_rest.as_ref(), parent_rest.as_ref())? {
187 return Ok(None);
188 }
189 }
190 _ => return Ok(None),
191 }
192 for (child_item, parent_item) in self.items.iter().zip(parent.items.iter()) {
193 if !shape_is_subshape_of(cx, child_item.as_ref(), parent_item.as_ref())? {
194 return Ok(None);
195 }
196 }
197 Ok(Some(true))
198 }
199
200 fn check_value(&self, cx: &mut Cx, value: Value) -> Result<ShapeMatch> {
201 if value.object().as_list().is_none() {
202 let expr = value.object().as_expr(cx)?;
203 return self.check_expr(cx, &expr);
204 }
205 self.check_list_value(cx, value)
206 }
207
208 fn check_expr(&self, cx: &mut Cx, expr: &Expr) -> Result<ShapeMatch> {
209 let Expr::List(items) = expr else {
210 return Ok(ShapeMatch::reject_with_diagnostic(
211 expected_shape_diagnostic("list expression", expr_actual_label(expr)),
212 ));
213 };
214
215 if items.len() < self.items.len() {
216 return Ok(ShapeMatch::reject_with_diagnostic(
217 expected_shape_diagnostic(
218 format!("at least {} list items", self.items.len()),
219 format!("{} list items", items.len()),
220 ),
221 ));
222 }
223
224 if self.rest.is_none() && items.len() != self.items.len() {
225 return Ok(ShapeMatch::reject_with_diagnostic(
226 expected_shape_diagnostic(
227 format!("{} list items", self.items.len()),
228 format!("{} list items", items.len()),
229 ),
230 ));
231 }
232
233 let mut out = ShapeMatch::accept(MatchScore::exact(20));
234
235 for (shape, item) in self.items.iter().zip(items.iter()) {
236 let matched = shape.check_expr(cx, item)?;
237 if !matched.accepted {
238 return Ok(matched);
239 }
240 out.captures.extend(matched.captures);
241 out.score += matched.score;
242 }
243
244 if let Some(rest) = &self.rest {
245 for item in items.iter().skip(self.items.len()) {
246 let matched = rest.check_expr(cx, item)?;
247 if !matched.accepted {
248 return Ok(matched);
249 }
250 out.captures.extend(matched.captures);
251 out.score += matched.score;
252 }
253 }
254
255 Ok(out)
256 }
257
258 fn describe(&self, cx: &mut Cx) -> Result<ShapeDoc> {
259 let mut doc = ShapeDoc::new("list shape");
260 for item in &self.items {
261 doc = doc.with_detail(item.describe(cx)?.name);
262 }
263 if self.rest.is_some() {
264 doc = doc.with_detail("rest".to_owned());
265 }
266 Ok(doc)
267 }
268}
269
270pub struct CaptureShape {
276 name: sim_kernel::Symbol,
277 inner: Arc<dyn Shape>,
278}
279
280impl CaptureShape {
281 pub fn new(name: sim_kernel::Symbol, inner: Arc<dyn Shape>) -> Self {
283 Self { name, inner }
284 }
285
286 pub fn name(&self) -> &sim_kernel::Symbol {
288 &self.name
289 }
290
291 pub fn inner(&self) -> &Arc<dyn Shape> {
293 &self.inner
294 }
295}
296
297impl Shape for CaptureShape {
298 fn parents(&self, _cx: &mut Cx) -> Result<Vec<ShapeRef>> {
299 Ok(vec![crate::functions::shape_value(
300 sim_kernel::Symbol::qualified("shape-capture-parent", self.name.to_string()),
301 self.inner.clone(),
302 )])
303 }
304
305 fn is_subshape_of(&self, cx: &mut Cx, parent: &dyn Shape) -> Result<Option<bool>> {
306 shape_is_subshape_of(cx, self.inner.as_ref(), parent).map(Some)
307 }
308
309 fn is_total(&self) -> bool {
310 self.inner.is_total()
311 }
312
313 fn check_value(&self, cx: &mut Cx, value: Value) -> Result<ShapeMatch> {
314 let mut matched = self.inner.check_value(cx, value.clone())?;
315 if matched.accepted {
316 matched.captures.bind_value(self.name.clone(), value);
317 if !self.inner.is_total()
318 && let Ok(expr) = matched
319 .captures
320 .values()
321 .last()
322 .expect("just bound value capture")
323 .1
324 .object()
325 .as_expr(cx)
326 {
327 matched.captures.bind_expr(self.name.clone(), expr);
328 }
329 } else {
330 matched.diagnostics.insert(
331 0,
332 binding_failure_diagnostic(&self.name, "captured value", "rejected value"),
333 );
334 }
335 Ok(matched)
336 }
337
338 fn check_expr(&self, cx: &mut Cx, expr: &Expr) -> Result<ShapeMatch> {
339 let mut matched = self.inner.check_expr(cx, expr)?;
340 if matched.accepted {
341 matched.captures.bind_expr(self.name.clone(), expr.clone());
342 } else {
343 matched.diagnostics.insert(
344 0,
345 binding_failure_diagnostic(
346 &self.name,
347 "captured expression",
348 expr_actual_label(expr),
349 ),
350 );
351 }
352 Ok(matched)
353 }
354
355 fn describe(&self, cx: &mut Cx) -> Result<ShapeDoc> {
356 let inner = self.inner.describe(cx)?;
357 Ok(ShapeDoc::new(format!("capture {}", self.name)).with_detail(inner.name))
358 }
359}
360
361pub struct OneOfShape {
367 choices: Vec<Arc<dyn Shape>>,
368}
369
370impl OneOfShape {
371 pub fn new(choices: Vec<Arc<dyn Shape>>) -> Self {
373 Self { choices }
374 }
375
376 pub fn choices(&self) -> &[Arc<dyn Shape>] {
378 &self.choices
379 }
380}
381
382impl Shape for OneOfShape {
383 fn is_total(&self) -> bool {
384 self.choices.iter().any(|choice| choice.is_total())
385 }
386
387 fn is_subshape_of(&self, cx: &mut Cx, parent: &dyn Shape) -> Result<Option<bool>> {
388 for choice in &self.choices {
389 if !shape_is_subshape_of(cx, choice.as_ref(), parent)? {
390 return Ok(Some(false));
391 }
392 }
393 Ok(Some(true))
394 }
395
396 fn check_value(&self, cx: &mut Cx, value: Value) -> Result<ShapeMatch> {
397 let mut diagnostics = Vec::new();
398 for choice in &self.choices {
399 let matched = choice.check_value(cx, value.clone())?;
400 if matched.accepted {
401 return Ok(matched);
402 }
403 diagnostics.extend(matched.diagnostics);
404 }
405 Ok(ShapeMatch {
406 accepted: false,
407 captures: Bindings::new(),
408 score: MatchScore::reject(),
409 diagnostics,
410 })
411 }
412
413 fn check_expr(&self, cx: &mut Cx, expr: &Expr) -> Result<ShapeMatch> {
414 let mut diagnostics = Vec::new();
415 for choice in &self.choices {
416 let matched = choice.check_expr(cx, expr)?;
417 if matched.accepted {
418 return Ok(matched);
419 }
420 diagnostics.extend(matched.diagnostics);
421 }
422 Ok(ShapeMatch {
423 accepted: false,
424 captures: Bindings::new(),
425 score: MatchScore::reject(),
426 diagnostics,
427 })
428 }
429
430 fn describe(&self, cx: &mut Cx) -> Result<ShapeDoc> {
431 let mut doc = ShapeDoc::new("one-of shape");
432 for choice in &self.choices {
433 doc = doc.with_detail(choice.describe(cx)?.name);
434 }
435 Ok(doc)
436 }
437}
438
439pub trait ShapeExprParser: Send + Sync {
445 fn label(&self) -> &str;
447
448 fn parse_expr(&self, source: &str) -> Result<Expr>;
450}
451
452pub struct PrattShape {
459 parser: Arc<dyn ShapeExprParser>,
460 inner: Arc<dyn Shape>,
461}
462
463impl PrattShape {
464 pub fn new(parser: Arc<dyn ShapeExprParser>, inner: Arc<dyn Shape>) -> Self {
466 Self { parser, inner }
467 }
468
469 pub fn parser(&self) -> &Arc<dyn ShapeExprParser> {
471 &self.parser
472 }
473
474 pub fn inner(&self) -> &Arc<dyn Shape> {
476 &self.inner
477 }
478}
479
480impl Shape for PrattShape {
481 fn check_value(&self, cx: &mut Cx, value: Value) -> Result<ShapeMatch> {
482 let expr = value.object().as_expr(cx)?;
483 self.check_expr(cx, &expr)
484 }
485
486 fn check_expr(&self, cx: &mut Cx, expr: &Expr) -> Result<ShapeMatch> {
487 let source = match expr {
488 Expr::String(text) => text.as_str(),
489 _ => {
490 return Ok(ShapeMatch::reject(
491 "expected string expression for Pratt parse",
492 ));
493 }
494 };
495 let parsed = match self.parser.parse_expr(source) {
496 Ok(parsed) => parsed,
497 Err(Error::Eval(message)) => return Ok(ShapeMatch::reject(message)),
498 Err(other) => return Err(other),
499 };
500 self.inner.check_expr(cx, &parsed)
501 }
502
503 fn describe(&self, cx: &mut Cx) -> Result<ShapeDoc> {
504 let inner = self.inner.describe(cx)?;
505 Ok(ShapeDoc::new("pratt shape")
506 .with_detail(self.parser.label().to_owned())
507 .with_detail(inner.name))
508 }
509}
510
511pub struct EffectfulShape {
517 inner: Arc<dyn Shape>,
518}
519
520impl EffectfulShape {
521 pub fn new(inner: Arc<dyn Shape>) -> Self {
523 Self { inner }
524 }
525
526 pub fn inner(&self) -> &Arc<dyn Shape> {
528 &self.inner
529 }
530}
531
532impl Shape for EffectfulShape {
533 fn id(&self) -> Option<ShapeId> {
534 None
535 }
536
537 fn is_effectful(&self) -> bool {
538 true
539 }
540
541 fn is_total(&self) -> bool {
542 self.inner.is_total()
543 }
544
545 fn is_subshape_of(&self, cx: &mut Cx, parent: &dyn Shape) -> Result<Option<bool>> {
546 let Some(parent) = parent.as_any().downcast_ref::<Self>() else {
547 return Ok(Some(false));
548 };
549 shape_is_subshape_of(cx, self.inner.as_ref(), parent.inner.as_ref()).map(Some)
550 }
551
552 fn check_value(&self, cx: &mut Cx, value: Value) -> Result<ShapeMatch> {
553 self.inner.check_value(cx, value)
554 }
555
556 fn check_expr(&self, cx: &mut Cx, expr: &Expr) -> Result<ShapeMatch> {
557 self.inner.check_expr(cx, expr)
558 }
559
560 fn describe(&self, cx: &mut Cx) -> Result<ShapeDoc> {
561 let inner = self.inner.describe(cx)?;
562 Ok(
563 ShapeDoc::new(format!("effectful {}", inner.name)).with_detail(
564 "effectful parse-time validation requires a trusted parser position".to_owned(),
565 ),
566 )
567 }
568}