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 &form value (the whole call as a list).
32        let form_val = form_to_value(&resolved)?;
33        // Build &env value (local bindings as a map — empty at top level).
34        let env_val = {
35            let (names, vals) = env.all_local_bindings();
36            let mut m = cljrs_value::MapValue::empty();
37            for (name, val) in names.iter().zip(vals.iter()) {
38                m = m.assoc(Value::symbol(Symbol::simple(name.as_ref())), val.clone());
39            }
40            Value::Map(m)
41        };
42        let mut args = vec![form_val, env_val];
43        for p in &parts[1..] {
44            args.push(form_to_value(p)?);
45        }
46        let expanded = crate::interp::apply::call_cljrs_fn(&macro_fn, &args, env)?;
47        let dummy = Span::new(Arc::new("<macro>".to_string()), 0, 0, 1, 1);
48        return value_to_form(&expanded, dummy);
49    }
50    Ok(form.clone())
51}
52
53/// Fully expand a form until the head is no longer a macro.
54pub fn macroexpand(form: &Form, env: &mut Env) -> EvalResult<Form> {
55    let mut current = form.clone();
56    loop {
57        // `macroexpand_1` returns an unchanged clone for non-macro forms.  Do
58        // not use structural equality to discover that case: IEEE NaN is not
59        // equal to itself, so a form containing `##NaN` would otherwise make
60        // this fixed-point loop run forever.
61        let is_macro_call = matches!(
62            &current.kind,
63            FormKind::List(parts)
64                if matches!(parts.first().map(|f| &f.kind), Some(FormKind::Symbol(s)) if resolve_macro(s, env).is_some())
65        );
66        if !is_macro_call {
67            return Ok(current);
68        }
69
70        let expanded = macroexpand_1(&current, env)?;
71        if expanded == current {
72            return Ok(current);
73        }
74        current = expanded;
75    }
76}
77
78/// Recursively macro-expand all forms in a tree.
79///
80/// First expands the top-level form, then walks into sub-forms.
81/// Special forms like `quote` are not walked into.
82pub fn macroexpand_all(form: &Form, env: &mut Env) -> EvalResult<Form> {
83    // First, expand the top level.
84    let expanded = macroexpand(form, env)?;
85
86    let span = expanded.span.clone();
87    let kind = match &expanded.kind {
88        FormKind::List(parts) if !parts.is_empty() => {
89            // Check if the head is a special form that shouldn't be walked.
90            let head_name = match &parts[0].kind {
91                FormKind::Symbol(s) => Some(s.as_str()),
92                _ => None,
93            };
94            match head_name {
95                // quote: don't expand inside quoted forms
96                Some("quote") => return Ok(expanded),
97                // fn*: expand body forms but not the param vector
98                Some("fn*") => {
99                    let mut new_parts = vec![parts[0].clone()];
100                    // fn* can have multiple arities: (fn* ([x] body) ([x y] body2))
101                    // or single arity: (fn* [x] body)
102                    if parts.len() > 1 {
103                        if let FormKind::Vector(_) = &parts[1].kind {
104                            // Single arity: (fn* [params] body...)
105                            new_parts.push(parts[1].clone()); // params
106                            for p in &parts[2..] {
107                                new_parts.push(macroexpand_all(p, env)?);
108                            }
109                        } else {
110                            // Multi-arity: (fn* ([params] body) ...)
111                            for arity in &parts[1..] {
112                                if let FormKind::List(arity_parts) = &arity.kind {
113                                    let mut new_arity = Vec::new();
114                                    if let Some(params) = arity_parts.first() {
115                                        new_arity.push(params.clone()); // param vector
116                                    }
117                                    for p in arity_parts.iter().skip(1) {
118                                        new_arity.push(macroexpand_all(p, env)?);
119                                    }
120                                    new_parts.push(Form::new(
121                                        FormKind::List(new_arity),
122                                        arity.span.clone(),
123                                    ));
124                                } else {
125                                    // Name or other token before arities
126                                    new_parts.push(arity.clone());
127                                }
128                            }
129                        }
130                    }
131                    FormKind::List(new_parts)
132                }
133                // let*, loop*: expand bindings values and body, but not binding names
134                Some("let*") | Some("loop*") => {
135                    let mut new_parts = vec![parts[0].clone()];
136                    if parts.len() > 1 {
137                        // Expand binding values (every other form in the vector)
138                        if let FormKind::Vector(bindings) = &parts[1].kind {
139                            let mut new_bindings = Vec::new();
140                            for (i, b) in bindings.iter().enumerate() {
141                                if i % 2 == 0 {
142                                    new_bindings.push(b.clone()); // binding name
143                                } else {
144                                    new_bindings.push(macroexpand_all(b, env)?);
145                                }
146                            }
147                            new_parts.push(Form::new(
148                                FormKind::Vector(new_bindings),
149                                parts[1].span.clone(),
150                            ));
151                        } else {
152                            new_parts.push(parts[1].clone());
153                        }
154                        for p in &parts[2..] {
155                            new_parts.push(macroexpand_all(p, env)?);
156                        }
157                    }
158                    FormKind::List(new_parts)
159                }
160                // catch/finally inside try: handled naturally by walking
161                _ => {
162                    // Generic: expand all sub-forms
163                    let new_parts = parts
164                        .iter()
165                        .map(|p| macroexpand_all(p, env))
166                        .collect::<EvalResult<Vec<_>>>()?;
167                    FormKind::List(new_parts)
168                }
169            }
170        }
171        FormKind::Vector(items) => {
172            let new_items = items
173                .iter()
174                .map(|i| macroexpand_all(i, env))
175                .collect::<EvalResult<Vec<_>>>()?;
176            FormKind::Vector(new_items)
177        }
178        FormKind::Map(items) => {
179            let new_items = items
180                .iter()
181                .map(|i| macroexpand_all(i, env))
182                .collect::<EvalResult<Vec<_>>>()?;
183            FormKind::Map(new_items)
184        }
185        FormKind::Set(items) => {
186            let new_items = items
187                .iter()
188                .map(|i| macroexpand_all(i, env))
189                .collect::<EvalResult<Vec<_>>>()?;
190            FormKind::Set(new_items)
191        }
192        // Atoms, keywords, strings, etc. — no sub-forms.
193        _ => return Ok(expanded),
194    };
195    Ok(Form::new(kind, span))
196}
197
198/// If `sym` resolves to a macro in the current env, return its CljxFn.
199fn resolve_macro(sym: &str, env: &Env) -> Option<cljrs_value::CljxFn> {
200    let parsed = Symbol::parse(sym);
201    // A namespace part may be an alias (`:require [... :as m]`), not a real
202    // namespace name — resolve it the same way `eval_symbol`/`(var ...)` do,
203    // falling back to the literal text only if it isn't a known alias.
204    let ns: Arc<str> = match parsed.namespace.as_deref() {
205        Some(ns_part) => env
206            .globals
207            .resolve_alias(&env.current_ns, ns_part)
208            .unwrap_or_else(|| Arc::from(ns_part)),
209        None => env.current_ns.clone(),
210    };
211    let name = parsed.name.as_ref();
212
213    let v = env.globals.lookup_in_ns(&ns, name)?;
214    if let Value::Macro(f) = v {
215        Some(f.get().clone())
216    } else {
217        None
218    }
219}
220
221/// Convert a `Value` to a `Form` (inverse of `form_to_value`).
222///
223/// Used to convert a macro's output back to a Form for further evaluation.
224pub fn value_to_form(val: &Value, span: Span) -> EvalResult<Form> {
225    let kind = match val {
226        Value::Nil => FormKind::Nil,
227        Value::Bool(b) => FormKind::Bool(*b),
228        Value::Long(n) => FormKind::Int(*n),
229        Value::Double(f) => FormKind::Float(*f),
230        Value::Str(s) => FormKind::Str(s.get().clone()),
231        Value::Char(c) => FormKind::Char(*c),
232        Value::BigInt(b) => FormKind::BigInt(b.get().to_string()),
233        Value::BigDecimal(d) => FormKind::BigDecimal(d.get().to_string()),
234        Value::Ratio(r) => FormKind::Ratio(format!("{}/{}", r.get().numer(), r.get().denom())),
235
236        Value::Symbol(s) => FormKind::Symbol(s.get().full_name()),
237        Value::Keyword(k) => FormKind::Keyword(k.get().full_name()),
238
239        Value::List(l) => {
240            let items = l.get();
241            // Reconstruct reader special forms that were encoded as lists by form_to_value.
242            let head_sym = items.iter().next().and_then(|v| {
243                if let Value::Symbol(s) = v {
244                    Some(s.get().name.clone())
245                } else {
246                    None
247                }
248            });
249            match (head_sym.as_deref(), items.count()) {
250                (Some("syntax-quote"), 2) => {
251                    let inner = value_to_form(items.iter().nth(1).unwrap(), span.clone())?;
252                    FormKind::SyntaxQuote(Box::new(inner))
253                }
254                (Some("unquote"), 2) => {
255                    let inner = value_to_form(items.iter().nth(1).unwrap(), span.clone())?;
256                    FormKind::Unquote(Box::new(inner))
257                }
258                (Some("unquote-splicing"), 2) => {
259                    let inner = value_to_form(items.iter().nth(1).unwrap(), span.clone())?;
260                    FormKind::UnquoteSplice(Box::new(inner))
261                }
262                _ => {
263                    let forms: Vec<Form> = items
264                        .iter()
265                        .map(|v| value_to_form(v, span.clone()))
266                        .collect::<EvalResult<_>>()?;
267                    FormKind::List(forms)
268                }
269            }
270        }
271        Value::Vector(v) => {
272            let forms: Vec<Form> = v
273                .get()
274                .iter()
275                .map(|v| value_to_form(v, span.clone()))
276                .collect::<EvalResult<_>>()?;
277            FormKind::Vector(forms)
278        }
279        Value::Map(m) => {
280            let mut forms = Vec::new();
281            let mut err: Option<EvalError> = None;
282            let sc = span.clone();
283            m.for_each(|k, v| {
284                if err.is_none() {
285                    match (value_to_form(k, sc.clone()), value_to_form(v, sc.clone())) {
286                        (Ok(kf), Ok(vf)) => {
287                            forms.push(kf);
288                            forms.push(vf);
289                        }
290                        (Err(e), _) | (_, Err(e)) => err = Some(e),
291                    }
292                }
293            });
294            if let Some(e) = err {
295                return Err(e);
296            }
297            FormKind::Map(forms)
298        }
299        Value::Set(s) => {
300            let forms: Vec<Form> = s
301                .iter()
302                .map(|v| value_to_form(v, span.clone()))
303                .collect::<EvalResult<_>>()?;
304            FormKind::Set(forms)
305        }
306
307        // Lazy sequences and cons cells: materialize into a list form.
308        // This handles macro output like (cons 'do (map ...)).
309        Value::LazySeq(ls) => {
310            return value_to_form(&ls.get().realize(), span);
311        }
312        Value::Cons(c) => {
313            let mut items: Vec<Form> = Vec::new();
314            let mut cur = Value::Cons(c.clone());
315            loop {
316                match cur {
317                    Value::Cons(cell) => {
318                        items.push(value_to_form(&cell.get().head, span.clone())?);
319                        cur = cell.get().tail.clone();
320                    }
321                    Value::LazySeq(ls) => cur = ls.get().realize(),
322                    Value::List(l) => {
323                        for v in l.get().iter() {
324                            items.push(value_to_form(v, span.clone())?);
325                        }
326                        break;
327                    }
328                    Value::Nil => break,
329                    _ => break,
330                }
331            }
332            FormKind::List(items)
333        }
334
335        Value::Uuid(u) => {
336            let uuid_str = uuid::Uuid::from_u128(*u).to_string();
337            FormKind::TaggedLiteral(
338                "uuid".to_string(),
339                Box::new(Form::new(FormKind::Str(uuid_str), span.clone())),
340            )
341        }
342
343        // WithMeta: strip metadata and convert the inner value.
344        Value::WithMeta(inner, _) => {
345            return value_to_form(inner, span);
346        }
347
348        Value::Pattern(p) => FormKind::Regex(p.get().as_str().to_string()),
349
350        // Non-data types: wrap in a symbol placeholder (best effort).
351        other => FormKind::Symbol(format!("#<{}>", other.type_name())),
352    };
353    Ok(Form::new(kind, span))
354}