Skip to main content

cljrs_runtime/interp/
macros.rs

1//! Macro expansion pipeline.
2
3use crate::builtins::form::{form_to_value, resolve_auto_forms};
4use cljrs_reader::Form;
5use cljrs_reader::form::FormKind;
6use cljrs_types::span::Span;
7use cljrs_value::{Symbol, Value};
8use std::sync::Arc;
9
10use crate::env::env::Env;
11use crate::env::error::{EvalError, EvalResult};
12
13/// Expand a form one step.  Returns the same form if it is not a macro call.
14pub fn macroexpand_1(form: &Form, env: &mut Env) -> EvalResult<Form> {
15    // Only expand list forms whose head is a macro symbol.
16    if let FormKind::List(parts) = &form.kind
17        && let Some(FormKind::Symbol(s)) = parts.first().map(|f| &f.kind)
18        && let Some(macro_fn) = resolve_macro(s, env)
19    {
20        // Resolve ::keywords using the caller's namespace before the macro sees
21        // them.  In Clojure, ::kw is resolved at READ time; since cljrs keeps
22        // AutoKeyword forms in the AST until eval, we resolve them here — the
23        // last point at which env.current_ns reflects the call site.
24        let resolved = resolve_auto_forms(form, env)?;
25        let parts = if let FormKind::List(p) = &resolved.kind {
26            p
27        } else {
28            unreachable!()
29        };
30
31        // Build &env value (local bindings as a map — empty at top level).
32        let env_val = {
33            let (names, vals) = env.all_local_bindings();
34            let mut m = cljrs_value::MapValue::empty();
35            for (name, val) in names.iter().zip(vals.iter()) {
36                m = m.assoc(Value::symbol(Symbol::simple(name.as_ref())), val.clone());
37            }
38            Value::Map(m)
39        };
40
41        // `&form` is the whole call as a list and the macro's arguments are
42        // that same list's tail, so convert each part once and share the
43        // values between the two rather than converting every argument
44        // subtree a second time. A macro body is often the largest thing in
45        // the call, and `macroexpand` runs this to a fixed point.
46        //
47        // A `#?@` splice among the parts is the one case where the two
48        // sequences genuinely differ: spliced into the list it contributes
49        // its elements to the enclosing sequence, while in an argument
50        // position it has no siblings and `form_to_value` rejects it. That
51        // case keeps the original two-pass conversion.
52        let spliced = parts
53            .iter()
54            .any(|f| matches!(f.kind, FormKind::ReaderCond { .. }));
55        let (form_val, arg_vals) = if spliced {
56            let mut arg_vals = Vec::with_capacity(parts.len() - 1);
57            for p in &parts[1..] {
58                arg_vals.push(form_to_value(p)?);
59            }
60            (form_to_value(&resolved)?, arg_vals)
61        } else {
62            let mut vals = Vec::with_capacity(parts.len());
63            for p in parts.iter() {
64                vals.push(form_to_value(p)?);
65            }
66            let form_val = crate::builtins::form::values_to_list(vals.iter().cloned());
67            let arg_vals = vals.split_off(1);
68            (form_val, arg_vals)
69        };
70
71        let mut args = Vec::with_capacity(arg_vals.len() + 2);
72        args.push(form_val);
73        args.push(env_val);
74        args.extend(arg_vals);
75        let expanded = crate::interp::apply::call_cljrs_fn(&macro_fn, &args, env)?;
76        let dummy = Span::new(Arc::new("<macro>".to_string()), 0, 0, 1, 1);
77        return value_to_form(&expanded, dummy);
78    }
79    Ok(form.clone())
80}
81
82/// Fully expand a form until the head is no longer a macro.
83pub fn macroexpand(form: &Form, env: &mut Env) -> EvalResult<Form> {
84    let mut current = form.clone();
85    loop {
86        // `macroexpand_1` returns an unchanged clone for non-macro forms.  Do
87        // not use structural equality to discover that case: IEEE NaN is not
88        // equal to itself, so a form containing `##NaN` would otherwise make
89        // this fixed-point loop run forever.
90        let is_macro_call = matches!(
91            &current.kind,
92            FormKind::List(parts)
93                if matches!(parts.first().map(|f| &f.kind), Some(FormKind::Symbol(s)) if resolve_macro(s, env).is_some())
94        );
95        if !is_macro_call {
96            return Ok(current);
97        }
98
99        let expanded = macroexpand_1(&current, env)?;
100        if expanded == current {
101            return Ok(current);
102        }
103        current = expanded;
104    }
105}
106
107/// Recursively macro-expand all forms in a tree.
108///
109/// First expands the top-level form, then walks into sub-forms.
110/// Special forms like `quote` are not walked into.
111pub fn macroexpand_all(form: &Form, env: &mut Env) -> EvalResult<Form> {
112    // First, expand the top level.
113    let expanded = macroexpand(form, env)?;
114
115    let span = expanded.span.clone();
116    let kind = match &expanded.kind {
117        FormKind::List(parts) if !parts.is_empty() => {
118            // Check if the head is a special form that shouldn't be walked.
119            let head_name = match &parts[0].kind {
120                FormKind::Symbol(s) => Some(s.as_str()),
121                _ => None,
122            };
123            match head_name {
124                // quote: don't expand inside quoted forms
125                Some("quote") => return Ok(expanded),
126                // fn*: expand body forms but not the param vector
127                Some("fn*") => {
128                    let mut new_parts = vec![parts[0].clone()];
129                    // fn* can have multiple arities: (fn* ([x] body) ([x y] body2))
130                    // or single arity: (fn* [x] body)
131                    if parts.len() > 1 {
132                        if let FormKind::Vector(_) = &parts[1].kind {
133                            // Single arity: (fn* [params] body...)
134                            new_parts.push(parts[1].clone()); // params
135                            for p in &parts[2..] {
136                                new_parts.push(macroexpand_all(p, env)?);
137                            }
138                        } else {
139                            // Multi-arity: (fn* ([params] body) ...)
140                            for arity in &parts[1..] {
141                                if let FormKind::List(arity_parts) = &arity.kind {
142                                    let mut new_arity = Vec::new();
143                                    if let Some(params) = arity_parts.first() {
144                                        new_arity.push(params.clone()); // param vector
145                                    }
146                                    for p in arity_parts.iter().skip(1) {
147                                        new_arity.push(macroexpand_all(p, env)?);
148                                    }
149                                    new_parts.push(Form::new(
150                                        FormKind::List(new_arity),
151                                        arity.span.clone(),
152                                    ));
153                                } else {
154                                    // Name or other token before arities
155                                    new_parts.push(arity.clone());
156                                }
157                            }
158                        }
159                    }
160                    FormKind::List(new_parts)
161                }
162                // let*, loop*: expand bindings values and body, but not binding names
163                Some("let*") | Some("loop*") => {
164                    let mut new_parts = vec![parts[0].clone()];
165                    if parts.len() > 1 {
166                        // Expand binding values (every other form in the vector)
167                        if let FormKind::Vector(bindings) = &parts[1].kind {
168                            let mut new_bindings = Vec::new();
169                            for (i, b) in bindings.iter().enumerate() {
170                                if i % 2 == 0 {
171                                    new_bindings.push(b.clone()); // binding name
172                                } else {
173                                    new_bindings.push(macroexpand_all(b, env)?);
174                                }
175                            }
176                            new_parts.push(Form::new(
177                                FormKind::Vector(new_bindings),
178                                parts[1].span.clone(),
179                            ));
180                        } else {
181                            new_parts.push(parts[1].clone());
182                        }
183                        for p in &parts[2..] {
184                            new_parts.push(macroexpand_all(p, env)?);
185                        }
186                    }
187                    FormKind::List(new_parts)
188                }
189                // catch/finally inside try: handled naturally by walking
190                _ => {
191                    // Generic: expand all sub-forms
192                    let new_parts = parts
193                        .iter()
194                        .map(|p| macroexpand_all(p, env))
195                        .collect::<EvalResult<Vec<_>>>()?;
196                    FormKind::List(new_parts)
197                }
198            }
199        }
200        FormKind::Vector(items) => {
201            let new_items = items
202                .iter()
203                .map(|i| macroexpand_all(i, env))
204                .collect::<EvalResult<Vec<_>>>()?;
205            FormKind::Vector(new_items)
206        }
207        FormKind::Map(items) => {
208            let new_items = items
209                .iter()
210                .map(|i| macroexpand_all(i, env))
211                .collect::<EvalResult<Vec<_>>>()?;
212            FormKind::Map(new_items)
213        }
214        FormKind::Set(items) => {
215            let new_items = items
216                .iter()
217                .map(|i| macroexpand_all(i, env))
218                .collect::<EvalResult<Vec<_>>>()?;
219            FormKind::Set(new_items)
220        }
221        // Atoms, keywords, strings, etc. — no sub-forms.
222        _ => return Ok(expanded),
223    };
224    Ok(Form::new(kind, span))
225}
226
227/// If `sym` resolves to a macro in the current env, return its CljxFn.
228fn resolve_macro(sym: &str, env: &Env) -> Option<cljrs_value::CljxFn> {
229    let parsed = Symbol::parse(sym);
230    let ns: Arc<str> = env.resolve_ns_or_current(parsed.namespace.as_deref());
231    let name = parsed.name.as_ref();
232
233    let v = env.globals.lookup_in_ns(&ns, name)?;
234    if let Value::Macro(f) = v {
235        Some(f.get().clone())
236    } else {
237        None
238    }
239}
240
241/// Convert a `Value` to a `Form` (inverse of `form_to_value`).
242///
243/// Used to convert a macro's output back to a Form for further evaluation.
244pub fn value_to_form(val: &Value, span: Span) -> EvalResult<Form> {
245    let kind = match val {
246        Value::Nil => FormKind::Nil,
247        Value::Bool(b) => FormKind::Bool(*b),
248        Value::Long(n) => FormKind::Int(*n),
249        Value::Double(f) => FormKind::Float(*f),
250        Value::Str(s) => FormKind::Str(s.get().clone()),
251        Value::Char(c) => FormKind::Char(*c),
252        Value::BigInt(b) => FormKind::BigInt(b.get().to_string()),
253        Value::BigDecimal(d) => FormKind::BigDecimal(d.get().to_string()),
254        Value::Ratio(r) => FormKind::Ratio(format!("{}/{}", r.get().numer(), r.get().denom())),
255
256        Value::Symbol(s) => FormKind::Symbol(s.get().full_name()),
257        Value::Keyword(k) => FormKind::Keyword(k.get().full_name()),
258
259        Value::List(l) => {
260            let items = l.get();
261            // Reconstruct reader special forms that were encoded as lists by form_to_value.
262            let head_sym = items.iter().next().and_then(|v| {
263                if let Value::Symbol(s) = v {
264                    Some(s.get().name.clone())
265                } else {
266                    None
267                }
268            });
269            match (head_sym.as_deref(), items.count()) {
270                (Some("syntax-quote"), 2) => {
271                    let inner = value_to_form(items.iter().nth(1).unwrap(), span.clone())?;
272                    FormKind::SyntaxQuote(Box::new(inner))
273                }
274                (Some("unquote"), 2) => {
275                    let inner = value_to_form(items.iter().nth(1).unwrap(), span.clone())?;
276                    FormKind::Unquote(Box::new(inner))
277                }
278                (Some("unquote-splicing"), 2) => {
279                    let inner = value_to_form(items.iter().nth(1).unwrap(), span.clone())?;
280                    FormKind::UnquoteSplice(Box::new(inner))
281                }
282                _ => {
283                    let forms: Vec<Form> = items
284                        .iter()
285                        .map(|v| value_to_form(v, span.clone()))
286                        .collect::<EvalResult<_>>()?;
287                    FormKind::List(forms)
288                }
289            }
290        }
291        Value::Vector(v) => {
292            let forms: Vec<Form> = v
293                .get()
294                .iter()
295                .map(|v| value_to_form(v, span.clone()))
296                .collect::<EvalResult<_>>()?;
297            FormKind::Vector(forms)
298        }
299        Value::Map(m) => {
300            let mut forms = Vec::new();
301            let mut err: Option<EvalError> = None;
302            let sc = span.clone();
303            m.for_each(|k, v| {
304                if err.is_none() {
305                    match (value_to_form(k, sc.clone()), value_to_form(v, sc.clone())) {
306                        (Ok(kf), Ok(vf)) => {
307                            forms.push(kf);
308                            forms.push(vf);
309                        }
310                        (Err(e), _) | (_, Err(e)) => err = Some(e),
311                    }
312                }
313            });
314            if let Some(e) = err {
315                return Err(e);
316            }
317            FormKind::Map(forms)
318        }
319        Value::Set(s) => {
320            let forms: Vec<Form> = s
321                .iter()
322                .map(|v| value_to_form(v, span.clone()))
323                .collect::<EvalResult<_>>()?;
324            FormKind::Set(forms)
325        }
326
327        // Lazy sequences and cons cells: materialize into a list form.
328        // This handles macro output like (cons 'do (map ...)).
329        Value::LazySeq(ls) => {
330            return value_to_form(&ls.get().realize(), span);
331        }
332        Value::Cons(c) => {
333            let mut items: Vec<Form> = Vec::new();
334            let mut cur = Value::Cons(c.clone());
335            loop {
336                match cur {
337                    Value::Cons(cell) => {
338                        items.push(value_to_form(&cell.get().head, span.clone())?);
339                        cur = cell.get().tail.clone();
340                    }
341                    Value::LazySeq(ls) => cur = ls.get().realize(),
342                    Value::List(l) => {
343                        for v in l.get().iter() {
344                            items.push(value_to_form(v, span.clone())?);
345                        }
346                        break;
347                    }
348                    Value::Nil => break,
349                    _ => break,
350                }
351            }
352            FormKind::List(items)
353        }
354
355        Value::Uuid(u) => {
356            let uuid_str = uuid::Uuid::from_u128(*u).to_string();
357            FormKind::TaggedLiteral(
358                "uuid".to_string(),
359                Box::new(Form::new(FormKind::Str(uuid_str), span.clone())),
360            )
361        }
362
363        // WithMeta: keep the annotation as a `^meta` wrapper so metadata a
364        // macro received (or attached) survives back into the AST.
365        //
366        // The annotation is *quoted*: it is already a value, and re-analysing
367        // it would resolve its contents as code — `{:tag 'String}` would look
368        // up `String`, and `{:arglists '([x])}` would look up `x`.
369        Value::WithMeta(inner, meta) => {
370            if matches!(**meta, Value::Nil) {
371                return value_to_form(inner, span);
372            }
373            let inner_form = value_to_form(inner, span.clone())?;
374            let meta_form = value_to_form(meta, span.clone())?;
375            let quoted_meta = Form::new(FormKind::Quote(Box::new(meta_form)), span.clone());
376            FormKind::Meta(Box::new(quoted_meta), Box::new(inner_form))
377        }
378
379        Value::Pattern(p) => FormKind::Regex(p.get().as_str().to_string()),
380
381        // Non-data types: wrap in a symbol placeholder (best effort).
382        other => FormKind::Symbol(format!("#<{}>", other.type_name())),
383    };
384    Ok(Form::new(kind, span))
385}