1use std::{
7 collections::{BTreeMap, BTreeSet},
8 sync::Arc,
9};
10
11use sim_kernel::{Cx, Expr, MatchScore, Result, ShapeBindings, ShapeMatch, Symbol};
12use sim_shape::{AnyShape, CaptureShape, ExactExprShape, ListShape, Shape, ShapeObject};
13
14use crate::model::OccursCheck;
15
16#[derive(Clone, Debug, Default, PartialEq, Eq)]
21pub struct LogicEnv {
22 captures: BTreeMap<Symbol, Expr>,
23 depth: usize,
24}
25
26impl LogicEnv {
27 pub fn new() -> Self {
29 Self::default()
30 }
31
32 pub fn with_depth(depth: usize) -> Self {
34 Self {
35 captures: BTreeMap::new(),
36 depth,
37 }
38 }
39
40 pub fn depth(&self) -> usize {
42 self.depth
43 }
44
45 pub fn set_depth(&mut self, depth: usize) {
47 self.depth = depth;
48 }
49
50 pub fn apply(&self, expr: &Expr) -> Expr {
53 match expr {
54 Expr::Local(var) => match self.captures.get(var) {
55 Some(bound) => self.apply(bound),
56 None => Expr::Local(var.clone()),
57 },
58 Expr::List(items) => Expr::List(items.iter().map(|item| self.apply(item)).collect()),
59 Expr::Vector(items) => {
60 Expr::Vector(items.iter().map(|item| self.apply(item)).collect())
61 }
62 Expr::Map(entries) => Expr::Map(
63 entries
64 .iter()
65 .map(|(key, value)| (self.apply(key), self.apply(value)))
66 .collect(),
67 ),
68 Expr::Set(items) => Expr::Set(items.iter().map(|item| self.apply(item)).collect()),
69 Expr::Call { operator, args } => Expr::Call {
70 operator: Box::new(self.apply(operator)),
71 args: args.iter().map(|arg| self.apply(arg)).collect(),
72 },
73 Expr::Infix {
74 operator,
75 left,
76 right,
77 } => Expr::Infix {
78 operator: operator.clone(),
79 left: Box::new(self.apply(left)),
80 right: Box::new(self.apply(right)),
81 },
82 Expr::Prefix { operator, arg } => Expr::Prefix {
83 operator: operator.clone(),
84 arg: Box::new(self.apply(arg)),
85 },
86 Expr::Postfix { operator, arg } => Expr::Postfix {
87 operator: operator.clone(),
88 arg: Box::new(self.apply(arg)),
89 },
90 Expr::Block(items) => Expr::Block(items.iter().map(|item| self.apply(item)).collect()),
91 Expr::Quote { mode, expr } => Expr::Quote {
92 mode: *mode,
93 expr: Box::new(self.apply(expr)),
94 },
95 Expr::Annotated { expr, annotations } => Expr::Annotated {
96 expr: Box::new(self.apply(expr)),
97 annotations: annotations
98 .iter()
99 .map(|(name, value)| (name.clone(), self.apply(value)))
100 .collect(),
101 },
102 Expr::Extension { tag, payload } => Expr::Extension {
103 tag: tag.clone(),
104 payload: Box::new(self.apply(payload)),
105 },
106 other => other.clone(),
107 }
108 }
109
110 pub fn get(&self, var: &Symbol) -> Option<&Expr> {
112 self.captures.get(var)
113 }
114
115 pub fn bind(&mut self, var: Symbol, value: Expr, occurs_check: OccursCheck) -> Result<()> {
120 if matches!(occurs_check, OccursCheck::Always) && occurs(var.clone(), &value, self) {
121 return Err(sim_kernel::Error::Eval(format!(
122 "occurs check failed for ?{}",
123 var.name
124 )));
125 }
126 self.captures.insert(var, value);
127 Ok(())
128 }
129
130 pub fn unify(
135 &mut self,
136 cx: &mut Cx,
137 left: &Expr,
138 right: &Expr,
139 occurs_check: OccursCheck,
140 ) -> Result<bool> {
141 let left = self.apply(left);
142 let right = self.apply(right);
143 if left.canonical_eq(&right) {
144 return Ok(true);
145 }
146
147 let left_match = self.shape_unify(cx, &left, &right, occurs_check)?;
148 let right_match = self.shape_unify(cx, &right, &left, occurs_check)?;
149 match (left_match, right_match) {
150 (ShapeUnify::Accepted(next), _) | (_, ShapeUnify::Accepted(next)) => {
151 *self = next;
152 Ok(true)
153 }
154 (ShapeUnify::Unsupported, _) | (_, ShapeUnify::Unsupported) => {
155 unify_ground(cx, self, &left, &right, occurs_check)
156 }
157 (ShapeUnify::Rejected, ShapeUnify::Rejected) => Ok(false),
158 }
159 }
160
161 fn shape_unify(
162 &self,
163 cx: &mut Cx,
164 pattern: &Expr,
165 subject: &Expr,
166 occurs_check: OccursCheck,
167 ) -> Result<ShapeUnify> {
168 let Some(shape) = shape_from_pattern(cx, pattern) else {
169 return Ok(ShapeUnify::Unsupported);
170 };
171 let matched = shape.check_expr(cx, subject)?;
172 if !matched.accepted {
173 return Ok(ShapeUnify::Rejected);
174 }
175 let mut next = self.clone();
176 if next.merge_shape_captures(cx, &matched.captures, occurs_check)? {
177 Ok(ShapeUnify::Accepted(next))
178 } else {
179 Ok(ShapeUnify::Rejected)
180 }
181 }
182
183 fn merge_shape_captures(
184 &mut self,
185 cx: &mut Cx,
186 captures: &ShapeBindings,
187 occurs_check: OccursCheck,
188 ) -> Result<bool> {
189 for (var, value) in captures.exprs() {
190 if !self.merge_shape_capture(cx, var.clone(), value.clone(), occurs_check)? {
191 return Ok(false);
192 }
193 }
194 Ok(true)
195 }
196
197 fn merge_shape_capture(
198 &mut self,
199 cx: &mut Cx,
200 var: Symbol,
201 value: Expr,
202 occurs_check: OccursCheck,
203 ) -> Result<bool> {
204 let value = self.apply(&value);
205 if let Some(bound) = self.captures.get(&var).cloned() {
206 let bound = self.apply(&bound);
207 return self.unify(cx, &bound, &value, occurs_check);
208 }
209 self.bind(var, value, occurs_check)?;
210 Ok(true)
211 }
212
213 pub fn free_vars(&self, expr: &Expr) -> Vec<Symbol> {
215 let mut vars = BTreeSet::new();
216 collect_vars(expr, &mut vars);
217 vars.into_iter().collect()
218 }
219
220 pub fn to_shape_bindings(&self, _cx: &mut Cx) -> Result<ShapeBindings> {
222 let mut bindings = ShapeBindings::new();
223 for (name, expr) in &self.captures {
224 bindings.bind_expr(name.clone(), self.apply(expr));
225 }
226 Ok(bindings)
227 }
228
229 pub fn as_shape_match(&self, cx: &mut Cx) -> Result<ShapeMatch> {
232 Ok(ShapeMatch {
233 accepted: true,
234 captures: self.to_shape_bindings(cx)?,
235 score: MatchScore::exact(100),
236 diagnostics: Vec::new(),
237 })
238 }
239}
240
241enum ShapeUnify {
242 Accepted(LogicEnv),
243 Rejected,
244 Unsupported,
245}
246
247fn shape_from_pattern(cx: &mut Cx, pattern: &Expr) -> Option<Arc<dyn Shape>> {
248 match pattern {
249 Expr::Local(var) => Some(Arc::new(CaptureShape::new(var.clone(), Arc::new(AnyShape)))),
250 Expr::List(items) => {
251 let item_shapes = items
252 .iter()
253 .map(|item| shape_from_pattern(cx, item))
254 .collect::<Option<Vec<_>>>()?;
255 Some(Arc::new(ListShape::new(item_shapes)))
256 }
257 Expr::Symbol(symbol) => resolve_shape_symbol(cx, symbol)
258 .or_else(|| Some(Arc::new(ExactExprShape::new(pattern.clone())))),
259 other if !contains_local(other) => Some(Arc::new(ExactExprShape::new(other.clone()))),
260 _ => None,
261 }
262}
263
264fn resolve_shape_symbol(cx: &mut Cx, symbol: &Symbol) -> Option<Arc<dyn Shape>> {
265 let value = cx.resolve_shape(symbol).ok()?;
266 let shape = value.object().downcast_ref::<ShapeObject>()?;
267 Some(Arc::clone(&shape.shape))
268}
269
270fn unify_ground(
271 cx: &mut Cx,
272 env: &mut LogicEnv,
273 left: &Expr,
274 right: &Expr,
275 occurs_check: OccursCheck,
276) -> Result<bool> {
277 match (left, right) {
278 (Expr::Nil, Expr::Nil)
279 | (Expr::Bool(_), Expr::Bool(_))
280 | (Expr::Number(_), Expr::Number(_))
281 | (Expr::Symbol(_), Expr::Symbol(_))
282 | (Expr::Local(_), Expr::Local(_))
283 | (Expr::String(_), Expr::String(_))
284 | (Expr::Bytes(_), Expr::Bytes(_)) => Ok(left.canonical_eq(right)),
285 (Expr::List(left_items), Expr::List(right_items))
286 | (Expr::Vector(left_items), Expr::Vector(right_items))
287 | (Expr::Set(left_items), Expr::Set(right_items))
288 | (Expr::Block(left_items), Expr::Block(right_items)) => {
289 unify_slices(cx, env, left_items, right_items, occurs_check)
290 }
291 (Expr::Map(left_entries), Expr::Map(right_entries)) => {
292 if left_entries.len() != right_entries.len() {
293 return Ok(false);
294 }
295 for ((left_key, left_value), (right_key, right_value)) in
296 left_entries.iter().zip(right_entries.iter())
297 {
298 if !env.unify(cx, left_key, right_key, occurs_check)? {
299 return Ok(false);
300 }
301 if !env.unify(cx, left_value, right_value, occurs_check)? {
302 return Ok(false);
303 }
304 }
305 Ok(true)
306 }
307 (
308 Expr::Call {
309 operator: left_op,
310 args: left_args,
311 },
312 Expr::Call {
313 operator: right_op,
314 args: right_args,
315 },
316 ) => {
317 if left_args.len() != right_args.len()
318 || !env.unify(cx, left_op, right_op, occurs_check)?
319 {
320 return Ok(false);
321 }
322 unify_slices(cx, env, left_args, right_args, occurs_check)
323 }
324 (
325 Expr::Quote {
326 mode: left_mode,
327 expr: left_expr,
328 },
329 Expr::Quote {
330 mode: right_mode,
331 expr: right_expr,
332 },
333 ) => {
334 if left_mode != right_mode {
335 return Ok(false);
336 }
337 env.unify(cx, left_expr, right_expr, occurs_check)
338 }
339 (
340 Expr::Annotated {
341 expr: left_expr,
342 annotations: left_annotations,
343 },
344 Expr::Annotated {
345 expr: right_expr,
346 annotations: right_annotations,
347 },
348 ) => {
349 if left_annotations.len() != right_annotations.len()
350 || !env.unify(cx, left_expr, right_expr, occurs_check)?
351 {
352 return Ok(false);
353 }
354 for ((left_name, left_value), (right_name, right_value)) in
355 left_annotations.iter().zip(right_annotations.iter())
356 {
357 if left_name != right_name
358 || !env.unify(cx, left_value, right_value, occurs_check)?
359 {
360 return Ok(false);
361 }
362 }
363 Ok(true)
364 }
365 (
366 Expr::Extension {
367 tag: left_tag,
368 payload: left_payload,
369 },
370 Expr::Extension {
371 tag: right_tag,
372 payload: right_payload,
373 },
374 ) => Ok(left_tag == right_tag && env.unify(cx, left_payload, right_payload, occurs_check)?),
375 (
376 Expr::Infix {
377 operator: left_op,
378 left: left_a,
379 right: left_b,
380 },
381 Expr::Infix {
382 operator: right_op,
383 left: right_a,
384 right: right_b,
385 },
386 ) => Ok(left_op == right_op
387 && env.unify(cx, left_a, right_a, occurs_check)?
388 && env.unify(cx, left_b, right_b, occurs_check)?),
389 (
390 Expr::Prefix {
391 operator: left_op,
392 arg: left_arg,
393 },
394 Expr::Prefix {
395 operator: right_op,
396 arg: right_arg,
397 },
398 )
399 | (
400 Expr::Postfix {
401 operator: left_op,
402 arg: left_arg,
403 },
404 Expr::Postfix {
405 operator: right_op,
406 arg: right_arg,
407 },
408 ) => Ok(left_op == right_op && env.unify(cx, left_arg, right_arg, occurs_check)?),
409 _ => Ok(false),
410 }
411}
412
413fn unify_slices(
414 cx: &mut Cx,
415 env: &mut LogicEnv,
416 left: &[Expr],
417 right: &[Expr],
418 occurs_check: OccursCheck,
419) -> Result<bool> {
420 if left.len() != right.len() {
421 return Ok(false);
422 }
423 for (left_item, right_item) in left.iter().zip(right.iter()) {
424 if !env.unify(cx, left_item, right_item, occurs_check)? {
425 return Ok(false);
426 }
427 }
428 Ok(true)
429}
430
431fn occurs(var: Symbol, expr: &Expr, env: &LogicEnv) -> bool {
432 match env.apply(expr) {
433 Expr::Local(candidate) => candidate == var,
434 Expr::List(items) | Expr::Vector(items) | Expr::Set(items) | Expr::Block(items) => {
435 items.iter().any(|item| occurs(var.clone(), item, env))
436 }
437 Expr::Map(entries) => entries
438 .iter()
439 .any(|(key, value)| occurs(var.clone(), key, env) || occurs(var.clone(), value, env)),
440 Expr::Call { operator, args } => {
441 occurs(var.clone(), &operator, env)
442 || args.iter().any(|arg| occurs(var.clone(), arg, env))
443 }
444 Expr::Infix { left, right, .. } => {
445 occurs(var.clone(), &left, env) || occurs(var, &right, env)
446 }
447 Expr::Prefix { arg, .. } | Expr::Postfix { arg, .. } => occurs(var, &arg, env),
448 Expr::Quote { expr, .. } => occurs(var, &expr, env),
449 Expr::Annotated { expr, annotations } => {
450 occurs(var.clone(), &expr, env)
451 || annotations
452 .iter()
453 .any(|(_, value)| occurs(var.clone(), value, env))
454 }
455 Expr::Extension { payload, .. } => occurs(var, &payload, env),
456 _ => false,
457 }
458}
459
460fn contains_local(expr: &Expr) -> bool {
461 match expr {
462 Expr::Local(_) => true,
463 Expr::List(items) | Expr::Vector(items) | Expr::Set(items) | Expr::Block(items) => {
464 items.iter().any(contains_local)
465 }
466 Expr::Map(entries) => entries
467 .iter()
468 .any(|(key, value)| contains_local(key) || contains_local(value)),
469 Expr::Call { operator, args } => {
470 contains_local(operator) || args.iter().any(contains_local)
471 }
472 Expr::Infix { left, right, .. } => contains_local(left) || contains_local(right),
473 Expr::Prefix { arg, .. } | Expr::Postfix { arg, .. } => contains_local(arg),
474 Expr::Quote { expr, .. } => contains_local(expr),
475 Expr::Annotated { expr, annotations } => {
476 contains_local(expr) || annotations.iter().any(|(_, value)| contains_local(value))
477 }
478 Expr::Extension { payload, .. } => contains_local(payload),
479 _ => false,
480 }
481}
482
483fn collect_vars(expr: &Expr, vars: &mut BTreeSet<Symbol>) {
484 match expr {
485 Expr::Local(var) => {
486 vars.insert(var.clone());
487 }
488 Expr::List(items) | Expr::Vector(items) | Expr::Set(items) | Expr::Block(items) => {
489 for item in items {
490 collect_vars(item, vars);
491 }
492 }
493 Expr::Map(entries) => {
494 for (key, value) in entries {
495 collect_vars(key, vars);
496 collect_vars(value, vars);
497 }
498 }
499 Expr::Call { operator, args } => {
500 collect_vars(operator, vars);
501 for arg in args {
502 collect_vars(arg, vars);
503 }
504 }
505 Expr::Infix { left, right, .. } => {
506 collect_vars(left, vars);
507 collect_vars(right, vars);
508 }
509 Expr::Prefix { arg, .. } | Expr::Postfix { arg, .. } => collect_vars(arg, vars),
510 Expr::Quote { expr, .. } => collect_vars(expr, vars),
511 Expr::Annotated { expr, annotations } => {
512 collect_vars(expr, vars);
513 for (_, value) in annotations {
514 collect_vars(value, vars);
515 }
516 }
517 Expr::Extension { payload, .. } => collect_vars(payload, vars),
518 _ => {}
519 }
520}