sim_lib_pattern/
match_form.rs1use 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#[derive(Clone, Copy)]
34pub struct MatchForm;
35
36impl MatchForm {
37 pub fn symbol() -> Symbol {
39 Symbol::new("match")
40 }
41}
42
43fn 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
66fn 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 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}