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