Skip to main content

sim_lib_pattern/
match_form.rs

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