Skip to main content

sim_lib_pattern/
match_form.rs

1//! The `match` pattern special form.
2//!
3//! `match` is a COOKBOOK_7 Category B eval-policy organ. Like the other
4//! control/binding organs it is a special form -- a [`Callable`] overriding
5//! [`Callable::call_exprs`] so it receives its arguments UNEVALUATED. It
6//! evaluates the scrutinee once, then tries each clause's pattern in order via
7//! the kernel [`Shape`] match/binding protocol ([`match_value`]); the first arm
8//! whose pattern accepts the value has its captures installed into a fresh child
9//! [`Env`](sim_kernel::Env) and its body evaluated there.
10//!
11//! Supported patterns (compiled to kernel shapes):
12//! - `_`            -> [`AnyShape`] (wildcard, binds nothing),
13//! - a symbol `x`   -> [`CaptureShape`] over [`AnyShape`] (binds the value to `x`),
14//! - a literal      -> [`ExactExprShape`] (matches an equal number/string/bool/nil),
15//! - `[p ...]`      -> [`ListShape`] of the element patterns (list/vector destructure,
16//!   composing the element captures).
17//!
18//! Constructor/ADT patterns (`(Con a b)`) are a future extension -- the pattern
19//! crate carries the `AlgebraicDataType`/`VariantConstructor` machinery for them.
20
21use std::sync::Arc;
22
23use sim_kernel::{
24    Args, Callable, ClassRef, Cx, Error, Expr, Object, ObjectCompat, RawArgs, Result, Shape,
25    Symbol, Value,
26};
27use sim_shape::{AnyShape, CaptureShape, ExactExprShape, ListShape};
28
29use crate::matching::{MatchArm, match_value};
30
31/// The `match` special form: `(match scrutinee (pattern body...) ...)`.
32#[derive(Clone, Copy)]
33pub struct MatchForm;
34
35impl MatchForm {
36    /// The bare `match` symbol this form registers under.
37    pub fn symbol() -> Symbol {
38        Symbol::new("match")
39    }
40}
41
42/// Compile a pattern expression into a checking/binding kernel [`Shape`].
43fn compile_pattern(pattern: Expr) -> Result<Arc<dyn Shape>> {
44    match pattern {
45        Expr::Symbol(sym) if sym.namespace.is_none() && sym.name.as_ref() == "_" => {
46            Ok(Arc::new(AnyShape))
47        }
48        Expr::Symbol(sym) => Ok(Arc::new(CaptureShape::new(sym, Arc::new(AnyShape)))),
49        literal @ (Expr::Number(_) | Expr::String(_) | Expr::Bool(_) | Expr::Nil) => {
50            Ok(Arc::new(ExactExprShape::new(literal)))
51        }
52        Expr::List(items) | Expr::Vector(items) => {
53            let shapes = items
54                .into_iter()
55                .map(compile_pattern)
56                .collect::<Result<Vec<_>>>()?;
57            Ok(Arc::new(ListShape::new(shapes)))
58        }
59        other => Err(Error::Eval(format!(
60            "unsupported match pattern: {other:?} (patterns are `_`, a symbol, a literal, or a `[..]` list)"
61        ))),
62    }
63}
64
65/// Split a clause into its pattern and body expressions.
66fn clause_parts(clause: Expr) -> Result<(Expr, Vec<Expr>)> {
67    let items = match clause {
68        Expr::List(items) | Expr::Vector(items) => items,
69        Expr::Call { operator, args } => {
70            let mut items = vec![*operator];
71            items.extend(args);
72            items
73        }
74        other => {
75            return Err(Error::Eval(format!(
76                "match clause must be (pattern body...), got {other:?}"
77            )));
78        }
79    };
80    let mut items = items.into_iter();
81    let pattern = items
82        .next()
83        .ok_or_else(|| Error::Eval("match clause needs a pattern".to_owned()))?;
84    Ok((pattern, items.collect()))
85}
86
87impl Object for MatchForm {
88    fn display(&self, _cx: &mut Cx) -> Result<String> {
89        Ok("#<special-form match>".to_owned())
90    }
91
92    fn as_any(&self) -> &dyn std::any::Any {
93        self
94    }
95}
96
97impl ObjectCompat for MatchForm {
98    fn class(&self, cx: &mut Cx) -> Result<ClassRef> {
99        cx.resolve_class(&Symbol::qualified("core", "Function"))
100    }
101
102    fn as_callable(&self) -> Option<&dyn Callable> {
103        Some(self)
104    }
105}
106
107impl Callable for MatchForm {
108    fn call(&self, _cx: &mut Cx, _args: Args) -> Result<Value> {
109        Err(Error::Eval(
110            "match is a special form and cannot be applied to evaluated arguments".to_owned(),
111        ))
112    }
113
114    fn call_exprs(&self, cx: &mut Cx, args: RawArgs) -> Result<Value> {
115        let mut exprs = args.into_exprs().into_iter();
116        let Some(scrutinee) = exprs.next() else {
117            return Err(Error::Eval(
118                "match expects (match scrutinee (pattern body...) ...)".to_owned(),
119            ));
120        };
121
122        // Compile every clause's pattern into an arm, keeping its body alongside.
123        let mut arms = Vec::new();
124        let mut bodies = Vec::new();
125        for (index, clause) in exprs.enumerate() {
126            let (pattern, body) = clause_parts(clause)?;
127            arms.push(MatchArm::new(
128                Symbol::new(format!("arm-{index}")),
129                compile_pattern(pattern)?,
130            ));
131            bodies.push(body);
132        }
133
134        let value = cx.eval_expr(scrutinee)?;
135        let matched = match_value(cx, value, &arms)?;
136        let body = bodies
137            .into_iter()
138            .nth(matched.arm_index())
139            .expect("arm index within bodies");
140
141        let child = matched.captures().clone().into_child_env(cx)?;
142        cx.with_env(child, |cx| {
143            let mut last = cx.factory().nil()?;
144            for expr in body {
145                last = cx.eval_expr(expr)?;
146            }
147            Ok(last)
148        })
149    }
150}