Skip to main content

tatara_lisp_eval/
eval.rs

1//! Core evaluator.
2//!
3//! Threads a mutable `Env` and the immutable `FnRegistry<H>` through
4//! recursive eval. Special forms are dispatched by head symbol before
5//! function application. Closures capture a snapshot of the current env
6//! at lambda creation; native functions live in the registry and are
7//! referred to in values by name.
8
9use std::sync::Arc;
10
11use tatara_lisp::{
12    Atom, MacroDef, MacroParams, Span, Spanned, SpannedExpander, SpannedForm,
13};
14
15use crate::code::{spanned_to_value, value_to_spanned};
16use crate::env::Env;
17use crate::error::{EvalError, Result};
18use crate::ffi::{
19    Arity, Caller, FnEntry, FnImpl, FnRegistry, FromValue, HigherOrderCallable, IntoValue,
20    NativeCallable,
21};
22use crate::module::{Loader, Module, ModuleError, ModuleRegistry, NoLoader};
23use crate::special::SpecialForm;
24use crate::value::{Closure, ErrorObj, NativeFn, Value};
25
26/// An embedded tatara-lisp evaluator, parameterized over the host context
27/// `H` that registered functions read/write.
28pub struct Interpreter<H> {
29    pub(crate) registry: FnRegistry<H>,
30    pub(crate) globals: Env,
31    /// Span-preserving macro expander. Top-level `defmacro`,
32    /// `defpoint-template`, and `defcheck` forms register here; macro calls
33    /// in subsequent forms are rewritten before evaluation. Persisted across
34    /// `eval_program` calls so REPL sessions accumulate macros naturally.
35    pub(crate) expander: SpannedExpander,
36    /// Module table — populated as `(require ...)` loads files. Shared
37    /// across all `Interpreter`s that share a registry (cloning an
38    /// `Interpreter` for sub-eval reuses the same registry).
39    pub(crate) modules: ModuleRegistry,
40    /// Source loader for `(require ...)`. Embedders inject filesystem
41    /// access here; the default `NoLoader` rejects every require.
42    pub(crate) loader: Arc<dyn Loader>,
43    /// Path of the module currently being evaluated. `(provide ...)`
44    /// adds names to whichever module owns this path. Top-level eval
45    /// (not inside any `(require)`) uses an empty path which means
46    /// "no current module" — `provide` errors there.
47    pub(crate) current_module: Option<Arc<str>>,
48}
49
50impl<H: 'static> Interpreter<H> {
51    pub fn new() -> Self {
52        Self {
53            registry: FnRegistry::new(),
54            globals: Env::new(),
55            expander: SpannedExpander::new(),
56            modules: ModuleRegistry::new(),
57            loader: Arc::new(NoLoader),
58            current_module: None,
59        }
60    }
61
62    /// Replace the source loader. Required for `(require ...)` to do
63    /// anything useful — the default `NoLoader` rejects every require.
64    pub fn set_loader(&mut self, loader: Arc<dyn Loader>) {
65        self.loader = loader;
66    }
67
68    /// Borrow the module registry. Useful for tests + inspection.
69    pub fn modules(&self) -> &ModuleRegistry {
70        &self.modules
71    }
72
73    /// Register a native Rust function, exposing it to Lisp code under
74    /// `name`. Re-registering the same name overwrites the prior entry
75    /// (last-write-wins) and leaves the global binding intact.
76    pub fn register_fn<F>(&mut self, name: impl Into<Arc<str>>, arity: Arity, callable: F)
77    where
78        F: NativeCallable<H>,
79    {
80        let name = name.into();
81        self.registry.insert(FnEntry {
82            name: name.clone(),
83            arity,
84            callable: FnImpl::Native(Arc::new(callable)),
85        });
86        self.globals.define(
87            name.clone(),
88            Value::NativeFn(Arc::new(NativeFn { name, arity })),
89        );
90    }
91
92    /// Register a higher-order Rust primitive — receives a `Caller` so it
93    /// can invoke `Value::Closure` / `Value::NativeFn` arguments back into
94    /// the eval loop. Used for `map`, `filter`, `fold`, `apply`,
95    /// `for-each`, etc. Same overwrite semantics as `register_fn`.
96    pub fn register_higher_order_fn<F>(
97        &mut self,
98        name: impl Into<Arc<str>>,
99        arity: Arity,
100        callable: F,
101    ) where
102        F: HigherOrderCallable<H>,
103    {
104        let name = name.into();
105        self.registry.insert(FnEntry {
106            name: name.clone(),
107            arity,
108            callable: FnImpl::Higher(Arc::new(callable)),
109        });
110        self.globals.define(
111            name.clone(),
112            Value::NativeFn(Arc::new(NativeFn { name, arity })),
113        );
114    }
115
116    /// Evaluate a single already-read spanned form in this interpreter's
117    /// global environment. Macro expansion runs first if any macros are
118    /// registered. Bare `eval_spanned` does NOT register top-level
119    /// `defmacro` — `eval_top_form` is the entry point for that.
120    pub fn eval_spanned(&mut self, form: &Spanned, host: &mut H) -> Result<Value> {
121        let expanded = self.fully_expand(form, host)?;
122        eval_in(
123            &mut self.globals,
124            &self.registry,
125            &self.expander,
126            &expanded,
127            host,
128        )
129    }
130
131    /// Evaluate a slice of forms in order, returning the last result.
132    ///
133    /// Top-level `defmacro` / `defpoint-template` / `defcheck` forms register
134    /// into the persistent expander and yield `Value::Nil`. All other forms
135    /// are fully expanded (recursively rewriting macro calls anywhere
136    /// in the form tree, with each macro body run through the live
137    /// evaluator at expansion time) before being evaluated. This is the
138    /// canonical entry point for running a tatara-lisp program — REPL,
139    /// embedded host, batch script.
140    ///
141    /// Empty input returns `Value::Nil`.
142    pub fn eval_program(&mut self, forms: &[Spanned], host: &mut H) -> Result<Value> {
143        let mut last = Value::Nil;
144        for form in forms {
145            last = self.eval_top_form(form, host)?;
146        }
147        Ok(last)
148    }
149
150    /// Evaluate one top-level form: register macros, handle module-
151    /// system forms (`provide` / `require`), expand, then eval.
152    /// Public so embedders that drive the read-eval loop themselves
153    /// (REPL, hot-reload watchers) can preserve top-level semantics
154    /// without re-implementing the registration handshake.
155    pub fn eval_top_form(&mut self, form: &Spanned, host: &mut H) -> Result<Value> {
156        if self.expander.try_register_macro(form)? {
157            return Ok(Value::Nil);
158        }
159        // Handle module-system forms BEFORE general expansion. They
160        // need `&mut self` access (loader, module registry, current
161        // module) which the generic eval dispatch can't carry.
162        if let Some(head) = head_symbol(form) {
163            match head {
164                "provide" => return self.eval_provide(form, host),
165                "require" => return self.eval_require(form, host),
166                _ => {}
167            }
168        }
169        let expanded = self.fully_expand(form, host)?;
170        eval_in(
171            &mut self.globals,
172            &self.registry,
173            &self.expander,
174            &expanded,
175            host,
176        )
177    }
178
179    /// Top-level `(provide name1 name2 ...)`. Adds each name to the
180    /// current module's export set. Errors if not currently inside a
181    /// module load (i.e. running at the embedder's top level).
182    fn eval_provide(&mut self, form: &Spanned, _host: &mut H) -> Result<Value> {
183        let items = form.as_list().unwrap_or(&[]);
184        let span = form.span;
185        let Some(current) = self.current_module.clone() else {
186            return Err(EvalError::bad_form(
187                "provide",
188                "`provide` only valid at module top level — embedder evaluating top-level code has no current module",
189                span,
190            ));
191        };
192        // Collect names to export.
193        let mut names: Vec<Arc<str>> = Vec::with_capacity(items.len().saturating_sub(1));
194        for item in &items[1..] {
195            let name = item.as_symbol().ok_or_else(|| {
196                EvalError::bad_form(
197                    "provide",
198                    "expected symbol — every arg must name a binding to export",
199                    item.span,
200                )
201            })?;
202            names.push(Arc::<str>::from(name));
203        }
204        // Append to the partially-loaded module's export set. The
205        // module is ALWAYS in the registry's "loading" stack at this
206        // point — loaded into the table on finish_load. We append
207        // exports via a dedicated registry method.
208        {
209            let mut g = self.modules.inner_lock();
210            // The currently-loading module's exports are tracked in a
211            // side staging map keyed by path; finalize_load merges
212            // the staging into the Module before promoting.
213            g.exports_staging
214                .entry(current.to_string())
215                .or_default()
216                .extend(names.iter().cloned());
217        }
218        Ok(Value::Nil)
219    }
220
221    /// Top-level `(require "path" ...)`. Loads the file via the
222    /// configured loader, evaluates its contents in a fresh module
223    /// context, then imports its exports into the calling env.
224    ///
225    /// Forms supported:
226    ///   (require "path")              ; alias = path; binds path/name
227    ///   (require "path" :as alias)    ; binds alias/name
228    ///   (require "path" :refer (...)) ; binds bare names; alias also bound
229    fn eval_require(&mut self, form: &Spanned, host: &mut H) -> Result<Value> {
230        let items = form.as_list().unwrap_or(&[]);
231        let span = form.span;
232        if items.len() < 2 {
233            return Err(EvalError::bad_form(
234                "require",
235                "expected (require \"path\" [:as alias] [:refer (...)])",
236                span,
237            ));
238        }
239        let path: Arc<str> = match items[1].as_string() {
240            Some(s) => Arc::from(s),
241            None => {
242                return Err(EvalError::bad_form(
243                    "require",
244                    "first arg must be a string path",
245                    items[1].span,
246                ))
247            }
248        };
249
250        // Parse optional :as alias / :refer (names) trailing kwargs.
251        let mut alias: Option<Arc<str>> = None;
252        let mut refer: Option<Vec<Arc<str>>> = None;
253        let mut i = 2usize;
254        while i < items.len() {
255            let kw = items[i].as_keyword().ok_or_else(|| {
256                EvalError::bad_form(
257                    "require",
258                    "expected keyword (:as / :refer) after path",
259                    items[i].span,
260                )
261            })?;
262            let val = items.get(i + 1).ok_or_else(|| {
263                EvalError::bad_form("require", "keyword without value", items[i].span)
264            })?;
265            match kw {
266                "as" => {
267                    alias = Some(Arc::from(val.as_symbol().ok_or_else(|| {
268                        EvalError::bad_form("require", ":as needs a symbol alias", val.span)
269                    })?));
270                }
271                "refer" => {
272                    let names_list = val.as_list().ok_or_else(|| {
273                        EvalError::bad_form(
274                            "require",
275                            ":refer needs a parenthesized list of symbols",
276                            val.span,
277                        )
278                    })?;
279                    let mut names = Vec::with_capacity(names_list.len());
280                    for n in names_list {
281                        names.push(Arc::<str>::from(n.as_symbol().ok_or_else(|| {
282                            EvalError::bad_form(
283                                "require",
284                                ":refer list must contain symbols only",
285                                n.span,
286                            )
287                        })?));
288                    }
289                    refer = Some(names);
290                }
291                other => {
292                    return Err(EvalError::bad_form(
293                        "require",
294                        format!("unknown require option :{other}"),
295                        items[i].span,
296                    ));
297                }
298            }
299            i += 2;
300        }
301
302        // Load + evaluate the module if it's not already cached.
303        if !self.modules.has(&path) {
304            self.load_module(&path, span, host)?;
305        }
306        let module = self
307            .modules
308            .get(&path)
309            .ok_or_else(|| EvalError::native_fn("require", "module disappeared after load", span))?;
310
311        // Import bindings into the calling env.
312        let chosen_alias = alias.unwrap_or_else(|| path.clone());
313        for name in &module.exports {
314            let value = module
315                .bindings
316                .get(name)
317                .cloned()
318                .unwrap_or(Value::Nil);
319            let qualified: Arc<str> = Arc::from(format!("{chosen_alias}/{name}"));
320            self.globals.define(qualified, value);
321        }
322        if let Some(names) = refer {
323            for name in names {
324                if let Some(value) = module.bindings.get(&name) {
325                    if module.exports.contains(&name) {
326                        self.globals.define(name.clone(), value.clone());
327                    } else {
328                        return Err(EvalError::User {
329                            value: error_value("not-exported", &format!(
330                                "{path} does not export {name}"
331                            )),
332                            at: span,
333                        });
334                    }
335                } else {
336                    return Err(EvalError::User {
337                        value: error_value("not-defined", &format!(
338                            "{path} does not define {name}"
339                        )),
340                        at: span,
341                    });
342                }
343            }
344        }
345        Ok(Value::Nil)
346    }
347
348    /// Drive the load of a single module: read source via loader,
349    /// register on the load stack (cycle detect), evaluate every form
350    /// against a fresh global env owned by THIS interpreter (so the
351    /// module sees the same primitives + macros), capture the bindings
352    /// that ended up in `globals` after eval, and finalize.
353    fn load_module(&mut self, path: &str, span: Span, host: &mut H) -> Result<()> {
354        // Cycle detect.
355        self.modules
356            .begin_load(path)
357            .map_err(|e| module_error_to_eval(e, span))?;
358
359        // Read source.
360        let source = match self.loader.load(path) {
361            Ok(s) => s,
362            Err(e) => {
363                self.modules.abort_load(path);
364                return Err(module_error_to_eval(e, span));
365            }
366        };
367
368        // Parse.
369        let forms = match tatara_lisp::read_spanned(&source) {
370            Ok(f) => f,
371            Err(e) => {
372                self.modules.abort_load(path);
373                return Err(EvalError::Reader(e));
374            }
375        };
376
377        // Save + swap module-context state. We isolate the module's
378        // bindings by snapshotting the globals env, evaluating into a
379        // FRESH env that inherits the host primitives, then restoring.
380        let saved_globals = std::mem::replace(&mut self.globals, Env::new());
381        // Re-install primitives into the fresh env: every NativeFn
382        // binding from the saved env is copied (the registry behind
383        // them is unchanged).
384        for (name, value) in saved_globals.iter_top_level() {
385            // Only carry NativeFn / Closure bindings forward — these
386            // are the primitive surface. The module's user-defined
387            // values get isolated.
388            if matches!(value, Value::NativeFn(_) | Value::Closure(_)) {
389                self.globals.define(name.clone(), value.clone());
390            }
391        }
392        let saved_current = self.current_module.replace(Arc::from(path));
393
394        // Evaluate every form. On error, restore + propagate.
395        let mut eval_err: Option<EvalError> = None;
396        for f in &forms {
397            // Re-enter eval_top_form so nested defmacro / require
398            // works recursively. (defmacro inside a module is fine;
399            // require chains are how libraries depend on each other.)
400            if let Err(e) = self.eval_top_form(f, host) {
401                eval_err = Some(e);
402                break;
403            }
404        }
405
406        // Snapshot module's bindings + exports BEFORE restoring globals.
407        let module_globals = std::mem::replace(&mut self.globals, saved_globals);
408        self.current_module = saved_current;
409
410        if let Some(e) = eval_err {
411            self.modules.abort_load(path);
412            return Err(e);
413        }
414
415        // Build the Module from the captured env's top-level bindings
416        // + the staged export set.
417        let mut module = Module::new(path);
418        for (name, value) in module_globals.iter_top_level() {
419            // Skip primitives that we re-inherited. We want only the
420            // module's OWN definitions.
421            if !matches!(value, Value::NativeFn(_)) {
422                module.define(name.clone(), value.clone());
423            }
424        }
425        // Apply staged exports.
426        let staged = {
427            let mut g = self.modules.inner_lock();
428            g.exports_staging
429                .remove(path)
430                .unwrap_or_default()
431        };
432        for n in staged {
433            module.add_export(n);
434        }
435        self.modules.finish_load(module);
436        Ok(())
437    }
438
439    /// Fully expand a form: walk the tree; whenever the head of a list
440    /// is a registered macro, evaluate the macro body (a regular Lisp
441    /// program) at expansion time, convert the resulting Value back to
442    /// a Spanned tree, and recurse — the expansion may itself contain
443    /// further macro calls.
444    ///
445    /// This is the CL/Racket macro model: the macro body has full access
446    /// to every primitive and library function, can compute over its
447    /// argument source forms (which arrive as Lisp data structures —
448    /// lists of symbols, etc.), and produces code as data.
449    pub fn fully_expand(&mut self, form: &Spanned, host: &mut H) -> Result<Spanned> {
450        // Fast path: no macros registered — nothing to expand.
451        if self.expander.is_empty() {
452            return Ok(form.clone());
453        }
454        self.expand_recursive(form, host)
455    }
456
457    fn expand_recursive(&mut self, form: &Spanned, host: &mut H) -> Result<Spanned> {
458        match &form.form {
459            SpannedForm::List(items) if !items.is_empty() => {
460                if let Some(head) = items[0].as_symbol() {
461                    if self.expander.has(head) {
462                        // Macro call. Expand by running the body, then
463                        // recurse on the result (it may itself be a
464                        // macro call or contain nested macro calls).
465                        let expanded =
466                            self.expand_macro_call(head, &items[1..], form.span, host)?;
467                        return self.expand_recursive(&expanded, host);
468                    }
469                }
470                // Not a macro call — recurse into children to catch
471                // nested macros.
472                let mut out = Vec::with_capacity(items.len());
473                for child in items {
474                    out.push(self.expand_recursive(child, host)?);
475                }
476                Ok(Spanned::new(form.span, SpannedForm::List(out)))
477            }
478            SpannedForm::Quote(_) => {
479                // Inside a `'expr`, expr is data — don't expand inside.
480                Ok(form.clone())
481            }
482            SpannedForm::Quasiquote(inner) => {
483                // Inside a `\`expr`, only unquoted subforms get expanded.
484                Ok(Spanned::new(
485                    form.span,
486                    SpannedForm::Quasiquote(Box::new(self.expand_inside_quasiquote(inner, host)?)),
487                ))
488            }
489            // Atoms, Nil, bare Unquote/UnquoteSplice — pass through.
490            _ => Ok(form.clone()),
491        }
492    }
493
494    fn expand_inside_quasiquote(&mut self, form: &Spanned, host: &mut H) -> Result<Spanned> {
495        match &form.form {
496            SpannedForm::Unquote(inner) => Ok(Spanned::new(
497                form.span,
498                SpannedForm::Unquote(Box::new(self.expand_recursive(inner, host)?)),
499            )),
500            SpannedForm::UnquoteSplice(inner) => Ok(Spanned::new(
501                form.span,
502                SpannedForm::UnquoteSplice(Box::new(self.expand_recursive(inner, host)?)),
503            )),
504            SpannedForm::List(items) => {
505                let mut out = Vec::with_capacity(items.len());
506                for item in items {
507                    out.push(self.expand_inside_quasiquote(item, host)?);
508                }
509                Ok(Spanned::new(form.span, SpannedForm::List(out)))
510            }
511            _ => Ok(form.clone()),
512        }
513    }
514
515    /// Expand a single macro call: bind macro params to lowered Value
516    /// representations of the source-form args, evaluate the body in
517    /// the live interpreter, and lift the result Value back to Spanned.
518    fn expand_macro_call(
519        &mut self,
520        macro_name: &str,
521        args: &[Spanned],
522        call_span: Span,
523        host: &mut H,
524    ) -> Result<Spanned> {
525        // Take a clone of the def — we'll use it without holding the
526        // expander borrow across an eval call.
527        let def: MacroDef = self
528            .expander
529            .get_macro(macro_name)
530            .cloned()
531            .ok_or_else(|| {
532                EvalError::native_fn(
533                    Arc::<str>::from(macro_name),
534                    "macro disappeared during expansion",
535                    call_span,
536                )
537            })?;
538
539        // Lift the body Sexp (which has no spans) to a Spanned tree
540        // stamped with the call site. Errors inside the body will
541        // appear at the macro call site — the right behavior for
542        // user-facing diagnostics.
543        let body_spanned = Spanned::from_sexp_at(&def.body, call_span);
544
545        // Expand any macros INSIDE the body before evaluation. This is
546        // what lets a macro use other macros (`dolist`, `when-let`,
547        // helper macros from stdlib) in its expansion logic. Without
548        // this pass, the body's eval would hit those forms as plain
549        // function calls and fail.
550        let body_expanded = self.fully_expand(&body_spanned, host)?;
551
552        // Build the macro-time environment: capture globals, push a
553        // frame for the macro params.
554        // SEALED: the macro body reads every global and stdlib function,
555        // and `define`s freely in its own frame — but a `set!` walking
556        // outward into the interpreter's globals is refused rather than
557        // silently mutating compile-time state. Without this, expansion is
558        // not deterministic and no expansion memo is sound.
559        let mut macro_env = self.globals.sealed_below_top();
560        bind_macro_args(&mut macro_env, &def.name, &def.params, args, call_span)?;
561
562        // Evaluate the body in the macro env using the live interpreter
563        // — every primitive, every library fn is in scope.
564        let result = eval_in(
565            &mut macro_env,
566            &self.registry,
567            &self.expander,
568            &body_expanded,
569            host,
570        )?;
571
572        // Convert the resulting Value back to a Spanned form. Anything
573        // that can't be lifted (closure, native fn, foreign) is a user
574        // error in the macro.
575        value_to_spanned(&result, call_span).map_err(|reason| {
576            EvalError::native_fn(
577                Arc::<str>::from(format!("macro {macro_name}")),
578                reason,
579                call_span,
580            )
581        })
582    }
583
584    /// Borrow the macro expander. Embedders may register macros directly
585    /// (e.g. preloaded standard library) without reading them from source.
586    pub fn expander(&self) -> &SpannedExpander {
587        &self.expander
588    }
589
590    /// Mutable access to the expander — for preloading macros via
591    /// `try_register_macro` from a separately-read form list, or clearing
592    /// the registry.
593    pub fn expander_mut(&mut self) -> &mut SpannedExpander {
594        &mut self.expander
595    }
596
597    /// Look up a symbol in the global env.
598    pub fn lookup_global(&self, name: &str) -> Option<Value> {
599        self.globals.lookup(name)
600    }
601
602    /// Bind a value in the global env.
603    pub fn define_global(&mut self, name: impl Into<Arc<str>>, value: Value) {
604        self.globals.define(name, value);
605    }
606
607    /// Borrow the globals env. Used by the VM to snapshot at closure
608    /// creation time.
609    pub fn globals_snapshot(&self) -> &Env {
610        &self.globals
611    }
612
613    /// External entry point: apply a callable `Value` (closure or
614    /// native fn) with `args`. Wraps the internal `apply_external` so
615    /// the VM can dispatch to the tree-walker for non-VM callables.
616    pub fn apply_external_value(
617        &mut self,
618        callee: &Value,
619        args: Vec<Value>,
620        host: &mut H,
621        call_span: Span,
622    ) -> Result<Value> {
623        apply_external(callee, args, call_span, &self.registry, &self.expander, host)
624    }
625
626    /// Compile + execute a parsed program through the bytecode VM.
627    /// Top-level `defmacro` forms register into the persistent
628    /// expander (same as `eval_program`); every other form is
629    /// macro-expanded in place, then a fresh `Chunk` is compiled and
630    /// run. This is the opt-in fast path; `eval_program` remains the
631    /// authoritative tree-walker. Returns the value of the last form.
632    pub fn eval_program_vm(&mut self, forms: &[Spanned], host: &mut H) -> Result<Value> {
633        let mut expanded: Vec<Spanned> = Vec::with_capacity(forms.len());
634        for form in forms {
635            if self.expander.try_register_macro(form)? {
636                continue;
637            }
638            expanded.push(self.fully_expand(form, host)?);
639        }
640        let chunk = crate::vm::compile_program(&expanded).map_err(|e| match e {
641            crate::vm::CompileError::Bad { at, message } => {
642                EvalError::bad_form(Arc::<str>::from("vm:compile"), message, at)
643            }
644        })?;
645        let mut vm = crate::vm::Vm::new();
646        vm.run(&chunk, self, host).map_err(|e| match e {
647            crate::vm::VmError::Eval(inner) => inner,
648            other => EvalError::native_fn(Arc::<str>::from("vm"), format!("{other}"), Span::synthetic()),
649        })
650    }
651
652    // ── Typed registration helpers ──────────────────────────────────
653
654    /// Register a 0-arity native fn with typed return value.
655    pub fn register_typed0<R, F>(&mut self, name: impl Into<Arc<str>>, f: F)
656    where
657        R: IntoValue + 'static,
658        F: Fn(&mut H) -> Result<R> + Send + Sync + 'static,
659    {
660        self.register_fn(
661            name,
662            Arity::Exact(0),
663            move |_args: &[Value], host: &mut H, _sp| f(host).map(IntoValue::into_value),
664        );
665    }
666
667    /// Register a 1-arity native fn with typed arg + return.
668    pub fn register_typed1<A, R, F>(&mut self, name: impl Into<Arc<str>>, f: F)
669    where
670        A: FromValue + 'static,
671        R: IntoValue + 'static,
672        F: Fn(&mut H, A) -> Result<R> + Send + Sync + 'static,
673    {
674        self.register_fn(
675            name,
676            Arity::Exact(1),
677            move |args: &[Value], host: &mut H, sp| {
678                let a = A::from_value(&args[0], sp)?;
679                f(host, a).map(IntoValue::into_value)
680            },
681        );
682    }
683
684    /// Register a 2-arity native fn with typed args + return.
685    pub fn register_typed2<A, B, R, F>(&mut self, name: impl Into<Arc<str>>, f: F)
686    where
687        A: FromValue + 'static,
688        B: FromValue + 'static,
689        R: IntoValue + 'static,
690        F: Fn(&mut H, A, B) -> Result<R> + Send + Sync + 'static,
691    {
692        self.register_fn(
693            name,
694            Arity::Exact(2),
695            move |args: &[Value], host: &mut H, sp| {
696                let a = A::from_value(&args[0], sp)?;
697                let b = B::from_value(&args[1], sp)?;
698                f(host, a, b).map(IntoValue::into_value)
699            },
700        );
701    }
702
703    /// Register a 3-arity native fn with typed args + return.
704    pub fn register_typed3<A, B, C, R, F>(&mut self, name: impl Into<Arc<str>>, f: F)
705    where
706        A: FromValue + 'static,
707        B: FromValue + 'static,
708        C: FromValue + 'static,
709        R: IntoValue + 'static,
710        F: Fn(&mut H, A, B, C) -> Result<R> + Send + Sync + 'static,
711    {
712        self.register_fn(
713            name,
714            Arity::Exact(3),
715            move |args: &[Value], host: &mut H, sp| {
716                let a = A::from_value(&args[0], sp)?;
717                let b = B::from_value(&args[1], sp)?;
718                let c = C::from_value(&args[2], sp)?;
719                f(host, a, b, c).map(IntoValue::into_value)
720            },
721        );
722    }
723
724    /// Register a 4-arity native fn with typed args + return.
725    pub fn register_typed4<A, B, C, D, R, F>(&mut self, name: impl Into<Arc<str>>, f: F)
726    where
727        A: FromValue + 'static,
728        B: FromValue + 'static,
729        C: FromValue + 'static,
730        D: FromValue + 'static,
731        R: IntoValue + 'static,
732        F: Fn(&mut H, A, B, C, D) -> Result<R> + Send + Sync + 'static,
733    {
734        self.register_fn(
735            name,
736            Arity::Exact(4),
737            move |args: &[Value], host: &mut H, sp| {
738                let a = A::from_value(&args[0], sp)?;
739                let b = B::from_value(&args[1], sp)?;
740                let c = C::from_value(&args[2], sp)?;
741                let d = D::from_value(&args[3], sp)?;
742                f(host, a, b, c, d).map(IntoValue::into_value)
743            },
744        );
745    }
746}
747
748impl<H: 'static> Default for Interpreter<H> {
749    fn default() -> Self {
750        Self::new()
751    }
752}
753
754// ── Core recursive evaluator ──────────────────────────────────────────
755
756/// Evaluate `form` against `env`, resolving native fns via `registry`.
757/// Mutates `env` for `define` / `set!` / body frame push+pop.
758pub(crate) fn eval_in<H: 'static>(
759    env: &mut Env,
760    registry: &FnRegistry<H>,
761    expander: &SpannedExpander,
762    form: &Spanned,
763    host: &mut H,
764) -> Result<Value> {
765    match &form.form {
766        SpannedForm::Nil => Ok(Value::Nil),
767        SpannedForm::Atom(a) => eval_atom(a, form.span, env),
768        SpannedForm::Quote(inner) => Ok(quoted_value(inner)),
769        SpannedForm::Quasiquote(inner) => quasiquote_eval(inner, env, registry, expander, host),
770        SpannedForm::Unquote(_) | SpannedForm::UnquoteSplice(_) => Err(EvalError::bad_form(
771            "unquote",
772            "unquote outside of quasiquote",
773            form.span,
774        )),
775        SpannedForm::List(items) => {
776            if items.is_empty() {
777                return Ok(Value::Nil);
778            }
779            // Head may be a special-form keyword, a symbol that resolves
780            // to a callable, or an arbitrary expression that evaluates
781            // to a callable.
782            if let Some(head_sym) = items[0].as_symbol() {
783                if let Some(sf) = SpecialForm::from_symbol(head_sym) {
784                    return eval_special(sf, items, form.span, env, registry, expander, host);
785                }
786            }
787            eval_application(items, form.span, env, registry, expander, host)
788        }
789    }
790}
791
792fn eval_atom(a: &Atom, span: Span, env: &Env) -> Result<Value> {
793    match a {
794        Atom::Symbol(name) => env
795            .lookup(name)
796            .ok_or_else(|| EvalError::unbound(name.as_str(), span)),
797        Atom::Keyword(s) => Ok(Value::Keyword(crate::interner::intern(s.as_str()))),
798        Atom::Str(s) => Ok(Value::Str(Arc::from(s.as_str()))),
799        Atom::Int(n) => Ok(Value::Int(*n)),
800        Atom::Float(n) => Ok(Value::Float(*n)),
801        Atom::Bool(b) => Ok(Value::Bool(*b)),
802    }
803}
804
805/// `'x` (Quote node from the reader) — yields the runtime value of x
806/// without evaluation. Symbol → Value::Symbol; list → Value::List of
807/// lowered children. Same semantics as the explicit `(quote x)`.
808fn quoted_value(inner: &Spanned) -> Value {
809    crate::code::spanned_to_value(inner)
810}
811
812/// Evaluate a quasiquoted form — unlike `quote`, `,expr` inside the form
813/// is evaluated and substituted, and `,@expr` splices the evaluated list
814/// into the enclosing list. Atoms lower to their runtime `Value`
815/// equivalents (Symbol → Value::Symbol, etc.). Nested quasiquote is not
816/// supported in v1 — it is returned as an opaque `Value::Sexp` literal.
817fn quasiquote_eval<H: 'static>(
818    form: &Spanned,
819    env: &mut Env,
820    registry: &FnRegistry<H>,
821    expander: &SpannedExpander,
822    host: &mut H,
823) -> Result<Value> {
824    match &form.form {
825        SpannedForm::Unquote(inner) => eval_in(env, registry, expander, inner, host),
826        SpannedForm::UnquoteSplice(_) => Err(EvalError::bad_form(
827            "unquote-splice",
828            "`,@` only valid directly inside a list",
829            form.span,
830        )),
831        SpannedForm::List(items) => {
832            let mut out: Vec<Value> = Vec::with_capacity(items.len());
833            for item in items {
834                if let SpannedForm::UnquoteSplice(inner) = &item.form {
835                    let v = eval_in(env, registry, expander, inner, host)?;
836                    match v {
837                        Value::List(xs) => out.extend(xs.iter().cloned()),
838                        Value::Nil => {}
839                        other => {
840                            return Err(EvalError::type_mismatch(
841                                "list",
842                                other.type_name(),
843                                item.span,
844                            ))
845                        }
846                    }
847                } else {
848                    out.push(quasiquote_eval(item, env, registry, expander, host)?);
849                }
850            }
851            if out.is_empty() {
852                Ok(Value::Nil)
853            } else {
854                Ok(Value::list(out))
855            }
856        }
857        SpannedForm::Nil => Ok(Value::Nil),
858        SpannedForm::Atom(a) => Ok(match a {
859            Atom::Symbol(s) => Value::Symbol(crate::interner::intern(s.as_str())),
860            Atom::Keyword(s) => Value::Keyword(crate::interner::intern(s.as_str())),
861            Atom::Str(s) => Value::Str(Arc::from(s.as_str())),
862            Atom::Int(n) => Value::Int(*n),
863            Atom::Float(n) => Value::Float(*n),
864            Atom::Bool(b) => Value::Bool(*b),
865        }),
866        // Inside quasiquote, an inner `quote` is preserved structurally —
867        // we treat it as an opaque literal subtree so downstream consumers
868        // can see it as a source form if they care.
869        SpannedForm::Quote(_) | SpannedForm::Quasiquote(_) => {
870            Ok(Value::Sexp(form.to_sexp(), form.span))
871        }
872    }
873}
874
875// ── Function application ──────────────────────────────────────────────
876
877fn eval_application<H: 'static>(
878    items: &[Spanned],
879    call_span: Span,
880    env: &mut Env,
881    registry: &FnRegistry<H>,
882    expander: &SpannedExpander,
883    host: &mut H,
884) -> Result<Value> {
885    let head_val = eval_in(env, registry, expander, &items[0], host)?;
886    let mut args: Vec<Value> = Vec::with_capacity(items.len().saturating_sub(1));
887    for arg_form in &items[1..] {
888        args.push(eval_in(env, registry, expander, arg_form, host)?);
889    }
890    apply(&head_val, args, call_span, registry, expander, host)
891}
892
893fn apply<H: 'static>(
894    callee: &Value,
895    args: Vec<Value>,
896    call_span: Span,
897    registry: &FnRegistry<H>,
898    expander: &SpannedExpander,
899    host: &mut H,
900) -> Result<Value> {
901    match callee {
902        Value::NativeFn(nfn) => {
903            if nfn.arity.check(args.len()).is_err() {
904                return Err(EvalError::ArityMismatch {
905                    fn_name: nfn.name.clone(),
906                    expected: nfn.arity,
907                    got: args.len(),
908                    at: call_span,
909                });
910            }
911            let entry = registry.lookup(&nfn.name).ok_or_else(|| {
912                EvalError::native_fn(
913                    nfn.name.clone(),
914                    format!("native fn {} is not registered", nfn.name),
915                    call_span,
916                )
917            })?;
918            match &entry.callable {
919                FnImpl::Native(f) => f.call(&args, host, call_span),
920                FnImpl::Higher(f) => {
921                    let caller = Caller { registry, expander };
922                    f.call(&args, host, &caller, call_span)
923                }
924            }
925        }
926        Value::Closure(c) => call_closure(c.clone(), args, call_span, registry, expander, host),
927        // VM-compiled closure flowing into a tree-walker apply path
928        // (typically because a native HoF captured the closure as an
929        // arg). Lift to a tree-walker-shaped Closure and dispatch.
930        // See `CompiledClosure::lift_to_closure` for trade-offs.
931        Value::Foreign(any) => {
932            if let Some(cc) = any
933                .clone()
934                .downcast::<crate::vm::run::CompiledClosure>()
935                .ok()
936            {
937                let lifted = cc.lift_to_closure();
938                return call_closure(lifted, args, call_span, registry, expander, host);
939            }
940            Err(EvalError::NotCallable {
941                value_kind: callee.type_name(),
942                at: call_span,
943            })
944        }
945        other => Err(EvalError::NotCallable {
946            value_kind: other.type_name(),
947            at: call_span,
948        }),
949    }
950}
951
952// ── Tail-call optimization ────────────────────────────────────────
953//
954// Tatara-lisp guarantees TCO in the sense Scheme R7RS requires: a
955// procedure call in tail position never grows the stack. This is
956// implemented as a trampoline driven from `call_closure`.
957//
958// "Tail position" is the structural notion: the form whose value
959// becomes the value of the surrounding form. The tail positions
960// supported here:
961//
962//   * `if` — both branches
963//   * `cond` / `when` / `unless` — last form of the matching body
964//   * `begin` / `let` / `let*` / `letrec` — last form of the body
965//   * `and` / `or` — last form when prior forms didn't short-circuit
966//   * Lambda body — last form
967//
968// `eval_in_tail` mirrors `eval_in` but, for closure-application forms
969// in tail position, returns `TailResult::Resume(closure, args)` rather
970// than calling `apply`. The outer trampoline in `call_closure` then
971// rebinds and loops without consuming a stack frame.
972
973/// Result of tail-position evaluation.
974enum TailResult {
975    /// Evaluation completed; here is the value.
976    Done(Value),
977    /// A tail call to a closure that the trampoline should re-enter
978    /// rather than recursing into. Carries the closure to invoke,
979    /// the already-evaluated arguments, and the call site span for
980    /// arity-error attribution.
981    Resume(Arc<Closure>, Vec<Value>, Span),
982}
983
984/// Tail-position evaluation. Same semantics as `eval_in` for forms
985/// that don't yield a closure tail call, but defers closure tail calls
986/// to the trampoline.
987fn eval_in_tail<H: 'static>(
988    env: &mut Env,
989    registry: &FnRegistry<H>,
990    expander: &SpannedExpander,
991    form: &Spanned,
992    host: &mut H,
993) -> Result<TailResult> {
994    match &form.form {
995        SpannedForm::List(items) if !items.is_empty() => {
996            // Special-form check first.
997            if let Some(head_sym) = items[0].as_symbol() {
998                if let Some(sf) = SpecialForm::from_symbol(head_sym) {
999                    return eval_special_tail(sf, items, form.span, env, registry, expander, host);
1000                }
1001            }
1002            // Function application: evaluate head + args, then either
1003            // resume (closure) or apply (everything else).
1004            let head_val = eval_in(env, registry, expander, &items[0], host)?;
1005            let mut args: Vec<Value> = Vec::with_capacity(items.len().saturating_sub(1));
1006            for arg_form in &items[1..] {
1007                args.push(eval_in(env, registry, expander, arg_form, host)?);
1008            }
1009            match head_val {
1010                Value::Closure(c) => Ok(TailResult::Resume(c, args, form.span)),
1011                _ => apply(&head_val, args, form.span, registry, expander, host)
1012                    .map(TailResult::Done),
1013            }
1014        }
1015        // Atoms, Quote, Nil — no tail context to exploit; just compute.
1016        _ => eval_in(env, registry, expander, form, host).map(TailResult::Done),
1017    }
1018}
1019
1020fn eval_special_tail<H: 'static>(
1021    sf: SpecialForm,
1022    items: &[Spanned],
1023    call_span: Span,
1024    env: &mut Env,
1025    registry: &FnRegistry<H>,
1026    expander: &SpannedExpander,
1027    host: &mut H,
1028) -> Result<TailResult> {
1029    match sf {
1030        SpecialForm::If => {
1031            if items.len() < 3 || items.len() > 4 {
1032                return eval_special(sf, items, call_span, env, registry, expander, host)
1033                    .map(TailResult::Done);
1034            }
1035            let c = eval_in(env, registry, expander, &items[1], host)?;
1036            if c.is_truthy() {
1037                eval_in_tail(env, registry, expander, &items[2], host)
1038            } else if items.len() == 4 {
1039                eval_in_tail(env, registry, expander, &items[3], host)
1040            } else {
1041                Ok(TailResult::Done(Value::Nil))
1042            }
1043        }
1044        SpecialForm::Begin => {
1045            let body = &items[1..];
1046            if body.is_empty() {
1047                return Ok(TailResult::Done(Value::Nil));
1048            }
1049            for form in &body[..body.len() - 1] {
1050                eval_in(env, registry, expander, form, host)?;
1051            }
1052            eval_in_tail(env, registry, expander, body.last().unwrap(), host)
1053        }
1054        SpecialForm::When | SpecialForm::Unless => {
1055            if items.len() < 2 {
1056                return eval_special(sf, items, call_span, env, registry, expander, host)
1057                    .map(TailResult::Done);
1058            }
1059            let invert = matches!(sf, SpecialForm::Unless);
1060            let cond = eval_in(env, registry, expander, &items[1], host)?;
1061            let run = cond.is_truthy() ^ invert;
1062            if !run {
1063                return Ok(TailResult::Done(Value::Nil));
1064            }
1065            let body = &items[2..];
1066            if body.is_empty() {
1067                return Ok(TailResult::Done(Value::Nil));
1068            }
1069            for form in &body[..body.len() - 1] {
1070                eval_in(env, registry, expander, form, host)?;
1071            }
1072            eval_in_tail(env, registry, expander, body.last().unwrap(), host)
1073        }
1074        SpecialForm::Cond => {
1075            for clause in &items[1..] {
1076                let Some(clause_list) = clause.as_list() else {
1077                    return eval_special(sf, items, call_span, env, registry, expander, host)
1078                        .map(TailResult::Done);
1079                };
1080                if clause_list.is_empty() {
1081                    return eval_special(sf, items, call_span, env, registry, expander, host)
1082                        .map(TailResult::Done);
1083                }
1084                let is_else = clause_list[0].as_symbol() == Some("else");
1085                let cond_matches = if is_else {
1086                    true
1087                } else {
1088                    eval_in(env, registry, expander, &clause_list[0], host)?.is_truthy()
1089                };
1090                if cond_matches {
1091                    let body = &clause_list[1..];
1092                    if body.is_empty() {
1093                        return Ok(TailResult::Done(Value::Nil));
1094                    }
1095                    for form in &body[..body.len() - 1] {
1096                        eval_in(env, registry, expander, form, host)?;
1097                    }
1098                    return eval_in_tail(env, registry, expander, body.last().unwrap(), host);
1099                }
1100            }
1101            Ok(TailResult::Done(Value::Nil))
1102        }
1103        SpecialForm::Let | SpecialForm::LetStar | SpecialForm::LetRec => {
1104            eval_let_family_tail(sf, items, call_span, env, registry, expander, host)
1105        }
1106        SpecialForm::And => {
1107            let exprs = &items[1..];
1108            if exprs.is_empty() {
1109                return Ok(TailResult::Done(Value::Bool(true)));
1110            }
1111            // All but last: short-circuit.
1112            for e in &exprs[..exprs.len() - 1] {
1113                let v = eval_in(env, registry, expander, e, host)?;
1114                if !v.is_truthy() {
1115                    return Ok(TailResult::Done(v));
1116                }
1117            }
1118            // Last in tail position.
1119            eval_in_tail(env, registry, expander, exprs.last().unwrap(), host)
1120        }
1121        SpecialForm::Or => {
1122            let exprs = &items[1..];
1123            if exprs.is_empty() {
1124                return Ok(TailResult::Done(Value::Bool(false)));
1125            }
1126            for e in &exprs[..exprs.len() - 1] {
1127                let v = eval_in(env, registry, expander, e, host)?;
1128                if v.is_truthy() {
1129                    return Ok(TailResult::Done(v));
1130                }
1131            }
1132            eval_in_tail(env, registry, expander, exprs.last().unwrap(), host)
1133        }
1134        SpecialForm::Try => {
1135            // try/catch is delicate to TCO — preserving the catch
1136            // handler context across a tail call would require unwinding
1137            // through Resume. Punt: always run try in non-tail position.
1138            // Tail position inside the catch handler is fine; the body
1139            // simply doesn't trampoline a tail call past the try frame.
1140            sf_try(items, call_span, env, registry, expander, host).map(TailResult::Done)
1141        }
1142        SpecialForm::MacroexpandOne => {
1143            sf_macroexpand(items, call_span, env, registry, expander, host, false)
1144                .map(TailResult::Done)
1145        }
1146        SpecialForm::MacroexpandAll => {
1147            sf_macroexpand(items, call_span, env, registry, expander, host, true)
1148                .map(TailResult::Done)
1149        }
1150        SpecialForm::Delay => sf_delay(items, call_span, env).map(TailResult::Done),
1151        SpecialForm::Eval => {
1152            sf_eval(items, call_span, env, registry, expander, host).map(TailResult::Done)
1153        }
1154        // Non-tail forms: just evaluate normally.
1155        _ => {
1156            eval_special(sf, items, call_span, env, registry, expander, host).map(TailResult::Done)
1157        }
1158    }
1159}
1160
1161/// Tail-aware evaluator for `let` / `let*` / `letrec`. Mirrors the
1162/// non-tail versions in `sf_let` / `sf_let_star` / `sf_letrec` but uses
1163/// `eval_in_tail` for the body's last form.
1164fn eval_let_family_tail<H: 'static>(
1165    sf: SpecialForm,
1166    items: &[Spanned],
1167    call_span: Span,
1168    env: &mut Env,
1169    registry: &FnRegistry<H>,
1170    expander: &SpannedExpander,
1171    host: &mut H,
1172) -> Result<TailResult> {
1173    if items.len() < 3 {
1174        return Err(EvalError::bad_form(
1175            match sf {
1176                SpecialForm::Let => "let",
1177                SpecialForm::LetStar => "let*",
1178                SpecialForm::LetRec => "letrec",
1179                _ => "let-family",
1180            },
1181            "expected ((name expr)...) body...",
1182            call_span,
1183        ));
1184    }
1185    let bindings = parse_binding_list(
1186        &items[1],
1187        match sf {
1188            SpecialForm::Let => "let",
1189            SpecialForm::LetStar => "let*",
1190            SpecialForm::LetRec => "letrec",
1191            _ => "let-family",
1192        },
1193    )?;
1194
1195    match sf {
1196        SpecialForm::Let => {
1197            let mut values = Vec::with_capacity(bindings.len());
1198            for (_, expr) in &bindings {
1199                values.push(eval_in(env, registry, expander, expr, host)?);
1200            }
1201            env.push();
1202            for ((name, _), val) in bindings.into_iter().zip(values) {
1203                env.define(name, val);
1204            }
1205        }
1206        SpecialForm::LetStar => {
1207            env.push();
1208            for (name, expr) in bindings {
1209                let v = eval_in(env, registry, expander, expr, host)?;
1210                env.define(name, v);
1211            }
1212        }
1213        SpecialForm::LetRec => {
1214            env.push();
1215            for (name, _) in &bindings {
1216                env.define(name.clone(), Value::Nil);
1217            }
1218            for (name, expr) in &bindings {
1219                let v = eval_in(env, registry, expander, expr, host)?;
1220                env.define(name.clone(), v);
1221            }
1222        }
1223        _ => unreachable!(),
1224    }
1225
1226    let body = &items[2..];
1227    let result = if body.is_empty() {
1228        Ok(TailResult::Done(Value::Nil))
1229    } else {
1230        for form in &body[..body.len() - 1] {
1231            if let Err(e) = eval_in(env, registry, expander, form, host) {
1232                env.pop();
1233                return Err(e);
1234            }
1235        }
1236        eval_in_tail(env, registry, expander, body.last().unwrap(), host)
1237    };
1238    env.pop();
1239    result
1240}
1241
1242/// External entry point for `Caller::apply_value` — the higher-order
1243/// primitive needs to invoke a callable Value back into the eval loop.
1244/// This is the same `apply` function above; it is exposed `pub(crate)`
1245/// at function visibility so the FFI module can reach it without
1246/// publishing the rest of the eval internals.
1247pub(crate) fn apply_external<H: 'static>(
1248    callee: &Value,
1249    args: Vec<Value>,
1250    call_span: Span,
1251    registry: &FnRegistry<H>,
1252    expander: &SpannedExpander,
1253    host: &mut H,
1254) -> Result<Value> {
1255    apply(callee, args, call_span, registry, expander, host)
1256}
1257
1258/// Bind macro parameters onto the macro-time env.
1259///
1260/// The positional binding itself is NOT restated here: it runs the one
1261/// shared `MacroParams::bind_carrier` over the `Spanned` carrier — the same
1262/// loop the plain and span-preserving expanders use — and this function only
1263/// lowers the resulting per-index values Spanned→Value and defines them.
1264/// Before that lift this was a third copy of the loop, and the only one of
1265/// the three that knew nothing about `&optional`.
1266fn bind_macro_args(
1267    env: &mut Env,
1268    macro_name: &str,
1269    params: &MacroParams,
1270    args: &[Spanned],
1271    call_span: Span,
1272) -> Result<()> {
1273    let bound = params
1274        .bind_carrier(macro_name, args, call_span)
1275        .map_err(|e| {
1276            EvalError::native_fn(
1277                Arc::<str>::from(format!("macro {macro_name}")),
1278                e.to_string(),
1279                call_span,
1280            )
1281        })?;
1282    for (name, value) in params.names().into_iter().zip(bound.iter()) {
1283        env.define(Arc::<str>::from(name), spanned_to_value(value));
1284    }
1285    Ok(())
1286}
1287
1288/// Apply a closure to arguments. Implements TCO: if the body's last
1289/// form is a tail call to another closure, the trampoline reuses the
1290/// stack frame instead of recursing. Self-recursion and mutual
1291/// recursion both bottom out into a loop.
1292fn call_closure<H: 'static>(
1293    closure: Arc<Closure>,
1294    args: Vec<Value>,
1295    call_span: Span,
1296    registry: &FnRegistry<H>,
1297    expander: &SpannedExpander,
1298    host: &mut H,
1299) -> Result<Value> {
1300    let mut current = closure;
1301    let mut current_args = args;
1302    let mut current_span = call_span;
1303    loop {
1304        // Arity check.
1305        let required = current.params.len();
1306        let has_rest = current.rest.is_some();
1307        if !has_rest && current_args.len() != required {
1308            return Err(EvalError::ArityMismatch {
1309                fn_name: Arc::from("<closure>"),
1310                expected: Arity::Exact(required),
1311                got: current_args.len(),
1312                at: current_span,
1313            });
1314        }
1315        if has_rest && current_args.len() < required {
1316            return Err(EvalError::ArityMismatch {
1317                fn_name: Arc::from("<closure>"),
1318                expected: Arity::AtLeast(required),
1319                got: current_args.len(),
1320                at: current_span,
1321            });
1322        }
1323
1324        // Build the body env: capture closure's lexical scope, push frame,
1325        // bind params + rest.
1326        let mut env = current.captured_env.clone();
1327        env.push();
1328        for (param, arg) in current.params.iter().zip(current_args.iter()) {
1329            env.define(param.clone(), arg.clone());
1330        }
1331        if let Some(rest_name) = &current.rest {
1332            let rest_args: Vec<Value> = current_args.iter().skip(required).cloned().collect();
1333            env.define(rest_name.clone(), Value::list(rest_args));
1334        }
1335
1336        // Body: evaluate all but the last normally, then the last in
1337        // tail position so a tail call can be trampolined.
1338        let body = &current.body;
1339        if body.is_empty() {
1340            return Ok(Value::Nil);
1341        }
1342        for body_form in &body[..body.len() - 1] {
1343            eval_in(&mut env, registry, expander, body_form, host)?;
1344        }
1345        match eval_in_tail(&mut env, registry, expander, body.last().unwrap(), host)? {
1346            TailResult::Done(v) => return Ok(v),
1347            TailResult::Resume(next, next_args, next_span) => {
1348                // Tail call: replace state and loop. Drop env (frame
1349                // popped on next iteration's fresh env).
1350                current = next;
1351                current_args = next_args;
1352                current_span = next_span;
1353            }
1354        }
1355    }
1356}
1357
1358// ── Special forms ─────────────────────────────────────────────────────
1359
1360fn eval_special<H: 'static>(
1361    sf: SpecialForm,
1362    items: &[Spanned],
1363    call_span: Span,
1364    env: &mut Env,
1365    registry: &FnRegistry<H>,
1366    expander: &SpannedExpander,
1367    host: &mut H,
1368) -> Result<Value> {
1369    match sf {
1370        SpecialForm::Quote => sf_quote(items, call_span),
1371        SpecialForm::Quasiquote => {
1372            if items.len() != 2 {
1373                return Err(EvalError::bad_form(
1374                    "quasiquote",
1375                    format!("expected 1 arg, got {}", items.len() - 1),
1376                    call_span,
1377                ));
1378            }
1379            quasiquote_eval(&items[1], env, registry, expander, host)
1380        }
1381        SpecialForm::If => sf_if(items, call_span, env, registry, expander, host),
1382        SpecialForm::Cond => sf_cond(items, call_span, env, registry, expander, host),
1383        SpecialForm::When => sf_when_unless(items, call_span, env, registry, expander, host, false),
1384        SpecialForm::Unless => {
1385            sf_when_unless(items, call_span, env, registry, expander, host, true)
1386        }
1387        SpecialForm::Let => sf_let(items, call_span, env, registry, expander, host),
1388        SpecialForm::LetStar => sf_let_star(items, call_span, env, registry, expander, host),
1389        SpecialForm::LetRec => sf_letrec(items, call_span, env, registry, expander, host),
1390        SpecialForm::Lambda => sf_lambda(items, call_span, env),
1391        SpecialForm::Define => sf_define(items, call_span, env, registry, expander, host),
1392        SpecialForm::Set => sf_set(items, call_span, env, registry, expander, host),
1393        SpecialForm::Begin => sf_begin(&items[1..], env, registry, expander, host),
1394        SpecialForm::And => sf_and(&items[1..], env, registry, expander, host),
1395        SpecialForm::Or => sf_or(&items[1..], env, registry, expander, host),
1396        SpecialForm::Not => sf_not(items, call_span, env, registry, expander, host),
1397        SpecialForm::Try => sf_try(items, call_span, env, registry, expander, host),
1398        SpecialForm::MacroexpandOne => {
1399            sf_macroexpand(items, call_span, env, registry, expander, host, false)
1400        }
1401        SpecialForm::MacroexpandAll => {
1402            sf_macroexpand(items, call_span, env, registry, expander, host, true)
1403        }
1404        SpecialForm::Delay => sf_delay(items, call_span, env),
1405        SpecialForm::Eval => sf_eval(items, call_span, env, registry, expander, host),
1406        SpecialForm::Provide | SpecialForm::Require => Err(EvalError::bad_form(
1407            if matches!(sf, SpecialForm::Provide) { "provide" } else { "require" },
1408            "module-system forms are only valid at top level — wrap your call in (eval (quote ...)) if you really need it dynamic",
1409            call_span,
1410        )),
1411    }
1412}
1413
1414/// Extract the head-symbol of a list form, or `None` if `form` isn't a
1415/// list whose head is a symbol. Used by the top-level dispatcher to
1416/// recognize module-system forms before macroexpansion.
1417fn head_symbol(form: &Spanned) -> Option<&str> {
1418    let SpannedForm::List(items) = &form.form else {
1419        return None;
1420    };
1421    items.first().and_then(Spanned::as_symbol)
1422}
1423
1424/// Build a `Value::Error` with the given tag + message.
1425fn error_value(tag: &str, message: &str) -> Value {
1426    Value::Error(Arc::new(ErrorObj {
1427        tag: Arc::from(tag),
1428        message: Arc::from(message),
1429        data: Vec::new(),
1430    }))
1431}
1432
1433/// Convert a `ModuleError` to the `EvalError::User` carrying a
1434/// `Value::Error`. This way module-system failures can be `(catch ...)`-ed
1435/// like any other thrown error.
1436fn module_error_to_eval(e: ModuleError, span: Span) -> EvalError {
1437    let (tag, message) = match &e {
1438        ModuleError::NotFound(_) => ("module-not-found", e.to_string()),
1439        ModuleError::Circular { .. } => ("circular-require", e.to_string()),
1440        ModuleError::NotExported(_, _) => ("not-exported", e.to_string()),
1441    };
1442    EvalError::User {
1443        value: error_value(tag, &message),
1444        at: span,
1445    }
1446}
1447
1448fn sf_quote(items: &[Spanned], span: Span) -> Result<Value> {
1449    if items.len() != 2 {
1450        return Err(EvalError::bad_form(
1451            "quote",
1452            format!("expected 1 arg, got {}", items.len() - 1),
1453            span,
1454        ));
1455    }
1456    // Scheme / Clojure semantics: (quote x) returns the runtime
1457    // structural value of x. A bare symbol becomes Value::Symbol; a
1458    // list becomes Value::List of recursively-lowered items; etc.
1459    // This is what makes (car '(a b c)) return the symbol `a` —
1460    // exactly what users expect from a Lisp.
1461    Ok(crate::code::spanned_to_value(&items[1]))
1462}
1463
1464fn sf_if<H: 'static>(
1465    items: &[Spanned],
1466    span: Span,
1467    env: &mut Env,
1468    registry: &FnRegistry<H>,
1469    expander: &SpannedExpander,
1470    host: &mut H,
1471) -> Result<Value> {
1472    if items.len() < 3 || items.len() > 4 {
1473        return Err(EvalError::bad_form(
1474            "if",
1475            format!("expected (if c t [e]), got {} subforms", items.len()),
1476            span,
1477        ));
1478    }
1479    let c = eval_in(env, registry, expander, &items[1], host)?;
1480    if c.is_truthy() {
1481        eval_in(env, registry, expander, &items[2], host)
1482    } else if items.len() == 4 {
1483        eval_in(env, registry, expander, &items[3], host)
1484    } else {
1485        Ok(Value::Nil)
1486    }
1487}
1488
1489fn sf_cond<H: 'static>(
1490    items: &[Spanned],
1491    span: Span,
1492    env: &mut Env,
1493    registry: &FnRegistry<H>,
1494    expander: &SpannedExpander,
1495    host: &mut H,
1496) -> Result<Value> {
1497    for clause in &items[1..] {
1498        let Some(clause_list) = clause.as_list() else {
1499            return Err(EvalError::bad_form(
1500                "cond",
1501                "clause must be a list",
1502                clause.span,
1503            ));
1504        };
1505        if clause_list.is_empty() {
1506            return Err(EvalError::bad_form("cond", "empty clause", clause.span));
1507        }
1508        let is_else = clause_list[0].as_symbol() == Some("else");
1509        let cond_matches = if is_else {
1510            true
1511        } else {
1512            let v = eval_in(env, registry, expander, &clause_list[0], host)?;
1513            v.is_truthy()
1514        };
1515        if cond_matches {
1516            let mut last = Value::Nil;
1517            for expr in &clause_list[1..] {
1518                last = eval_in(env, registry, expander, expr, host)?;
1519            }
1520            return Ok(last);
1521        }
1522    }
1523    // No clause matched.
1524    let _ = span;
1525    Ok(Value::Nil)
1526}
1527
1528fn sf_when_unless<H: 'static>(
1529    items: &[Spanned],
1530    span: Span,
1531    env: &mut Env,
1532    registry: &FnRegistry<H>,
1533    expander: &SpannedExpander,
1534    host: &mut H,
1535    invert: bool,
1536) -> Result<Value> {
1537    if items.len() < 2 {
1538        return Err(EvalError::bad_form(
1539            if invert { "unless" } else { "when" },
1540            "need a test",
1541            span,
1542        ));
1543    }
1544    let cond = eval_in(env, registry, expander, &items[1], host)?;
1545    let run = cond.is_truthy() ^ invert;
1546    if run {
1547        let mut last = Value::Nil;
1548        for expr in &items[2..] {
1549            last = eval_in(env, registry, expander, expr, host)?;
1550        }
1551        Ok(last)
1552    } else {
1553        Ok(Value::Nil)
1554    }
1555}
1556
1557/// Parse a `((name expr) ...)` binding list into `[(name, &expr_spanned)]`.
1558fn parse_binding_list<'a>(
1559    list: &'a Spanned,
1560    form_name: &'static str,
1561) -> Result<Vec<(Arc<str>, &'a Spanned)>> {
1562    let bindings = list
1563        .as_list()
1564        .ok_or_else(|| EvalError::bad_form(form_name, "bindings must be a list", list.span))?;
1565    let mut out = Vec::with_capacity(bindings.len());
1566    for binding in bindings {
1567        let pair = binding.as_list().ok_or_else(|| {
1568            EvalError::bad_form(form_name, "each binding must be (name expr)", binding.span)
1569        })?;
1570        if pair.len() != 2 {
1571            return Err(EvalError::bad_form(
1572                form_name,
1573                "binding must be exactly (name expr)",
1574                binding.span,
1575            ));
1576        }
1577        let name = pair[0].as_symbol().ok_or_else(|| {
1578            EvalError::bad_form(form_name, "binding name must be a symbol", pair[0].span)
1579        })?;
1580        out.push((Arc::<str>::from(name), &pair[1]));
1581    }
1582    Ok(out)
1583}
1584
1585fn sf_let<H: 'static>(
1586    items: &[Spanned],
1587    span: Span,
1588    env: &mut Env,
1589    registry: &FnRegistry<H>,
1590    expander: &SpannedExpander,
1591    host: &mut H,
1592) -> Result<Value> {
1593    if items.len() < 3 {
1594        return Err(EvalError::bad_form(
1595            "let",
1596            "expected (let ((name expr)...) body...)",
1597            span,
1598        ));
1599    }
1600    let bindings = parse_binding_list(&items[1], "let")?;
1601    // Parallel semantics: evaluate all RHS in the *outer* env, then
1602    // extend with new frame.
1603    let mut values = Vec::with_capacity(bindings.len());
1604    for (_, expr) in &bindings {
1605        values.push(eval_in(env, registry, expander, expr, host)?);
1606    }
1607    env.push();
1608    for ((name, _), val) in bindings.into_iter().zip(values) {
1609        env.define(name, val);
1610    }
1611    let result = eval_body(&items[2..], env, registry, expander, host);
1612    env.pop();
1613    result
1614}
1615
1616fn sf_let_star<H: 'static>(
1617    items: &[Spanned],
1618    span: Span,
1619    env: &mut Env,
1620    registry: &FnRegistry<H>,
1621    expander: &SpannedExpander,
1622    host: &mut H,
1623) -> Result<Value> {
1624    if items.len() < 3 {
1625        return Err(EvalError::bad_form(
1626            "let*",
1627            "expected (let* ((name expr)...) body...)",
1628            span,
1629        ));
1630    }
1631    let bindings = parse_binding_list(&items[1], "let*")?;
1632    env.push();
1633    for (name, expr) in bindings {
1634        let v = eval_in(env, registry, expander, expr, host)?;
1635        env.define(name, v);
1636    }
1637    let result = eval_body(&items[2..], env, registry, expander, host);
1638    env.pop();
1639    result
1640}
1641
1642fn sf_letrec<H: 'static>(
1643    items: &[Spanned],
1644    span: Span,
1645    env: &mut Env,
1646    registry: &FnRegistry<H>,
1647    expander: &SpannedExpander,
1648    host: &mut H,
1649) -> Result<Value> {
1650    if items.len() < 3 {
1651        return Err(EvalError::bad_form(
1652            "letrec",
1653            "expected (letrec ((name expr)...) body...)",
1654            span,
1655        ));
1656    }
1657    let bindings = parse_binding_list(&items[1], "letrec")?;
1658    env.push();
1659    // Pre-bind each name to Nil so RHS can self-reference (and cross-
1660    // reference). Then eval each RHS in order and rebind.
1661    for (name, _) in &bindings {
1662        env.define(name.clone(), Value::Nil);
1663    }
1664    for (name, expr) in &bindings {
1665        let v = eval_in(env, registry, expander, expr, host)?;
1666        env.define(name.clone(), v);
1667    }
1668    let result = eval_body(&items[2..], env, registry, expander, host);
1669    env.pop();
1670    result
1671}
1672
1673fn eval_body<H: 'static>(
1674    body: &[Spanned],
1675    env: &mut Env,
1676    registry: &FnRegistry<H>,
1677    expander: &SpannedExpander,
1678    host: &mut H,
1679) -> Result<Value> {
1680    let mut last = Value::Nil;
1681    for form in body {
1682        last = eval_in(env, registry, expander, form, host)?;
1683    }
1684    Ok(last)
1685}
1686
1687fn sf_lambda(items: &[Spanned], span: Span, env: &Env) -> Result<Value> {
1688    if items.len() < 3 {
1689        return Err(EvalError::bad_form(
1690            "lambda",
1691            "expected (lambda (params...) body...)",
1692            span,
1693        ));
1694    }
1695    // Empty `()` source parses as Nil, not List([]); accept both as
1696    // "no parameters". Anything else must be a List.
1697    let param_list: &[Spanned] = match &items[1].form {
1698        SpannedForm::Nil => &[],
1699        SpannedForm::List(xs) => xs.as_slice(),
1700        _ => {
1701            return Err(EvalError::bad_form(
1702                "lambda",
1703                "params must be a list",
1704                items[1].span,
1705            ))
1706        }
1707    };
1708    let (params, rest) = parse_lambda_params(param_list, items[1].span)?;
1709    let body = items[2..].to_vec();
1710    Ok(Value::Closure(Arc::new(Closure {
1711        params,
1712        rest,
1713        body,
1714        captured_env: env.clone(),
1715        source: span,
1716    })))
1717}
1718
1719fn parse_lambda_params(list: &[Spanned], span: Span) -> Result<(Vec<Arc<str>>, Option<Arc<str>>)> {
1720    let mut params = Vec::new();
1721    let mut rest = None;
1722    let mut i = 0;
1723    while i < list.len() {
1724        let s = list[i]
1725            .as_symbol()
1726            .ok_or_else(|| EvalError::bad_form("lambda", "param must be a symbol", list[i].span))?;
1727        if s == "&rest" {
1728            let name = list
1729                .get(i + 1)
1730                .and_then(Spanned::as_symbol)
1731                .ok_or_else(|| EvalError::bad_form("lambda", "&rest needs a name", span))?;
1732            rest = Some(Arc::<str>::from(name));
1733            if i + 2 != list.len() {
1734                return Err(EvalError::bad_form(
1735                    "lambda",
1736                    "&rest must be the last param",
1737                    span,
1738                ));
1739            }
1740            break;
1741        }
1742        params.push(Arc::<str>::from(s));
1743        i += 1;
1744    }
1745    Ok((params, rest))
1746}
1747
1748/// `(define name expr)` or `(define (name params...) body...)`
1749fn sf_define<H: 'static>(
1750    items: &[Spanned],
1751    span: Span,
1752    env: &mut Env,
1753    registry: &FnRegistry<H>,
1754    expander: &SpannedExpander,
1755    host: &mut H,
1756) -> Result<Value> {
1757    if items.len() < 3 {
1758        return Err(EvalError::bad_form(
1759            "define",
1760            "expected (define name expr) or (define (name args) body)",
1761            span,
1762        ));
1763    }
1764    match &items[1].form {
1765        SpannedForm::Atom(Atom::Symbol(name)) => {
1766            let v = eval_in(env, registry, expander, &items[2], host)?;
1767            env.define(Arc::<str>::from(name.as_str()), v);
1768            Ok(Value::Nil)
1769        }
1770        SpannedForm::List(head_list) => {
1771            if head_list.is_empty() {
1772                return Err(EvalError::bad_form(
1773                    "define",
1774                    "empty (name args) list",
1775                    items[1].span,
1776                ));
1777            }
1778            let name = head_list[0].as_symbol().ok_or_else(|| {
1779                EvalError::bad_form(
1780                    "define",
1781                    "first item in (name args) must be a symbol",
1782                    head_list[0].span,
1783                )
1784            })?;
1785            let (params, rest) = parse_lambda_params(&head_list[1..], items[1].span)?;
1786            let body = items[2..].to_vec();
1787            let closure = Arc::new(Closure {
1788                params,
1789                rest,
1790                body,
1791                captured_env: env.clone(),
1792                source: span,
1793            });
1794            env.define(Arc::<str>::from(name), Value::Closure(closure));
1795            Ok(Value::Nil)
1796        }
1797        _ => Err(EvalError::bad_form(
1798            "define",
1799            "second form must be a symbol or (name args) list",
1800            items[1].span,
1801        )),
1802    }
1803}
1804
1805fn sf_set<H: 'static>(
1806    items: &[Spanned],
1807    span: Span,
1808    env: &mut Env,
1809    registry: &FnRegistry<H>,
1810    expander: &SpannedExpander,
1811    host: &mut H,
1812) -> Result<Value> {
1813    if items.len() != 3 {
1814        return Err(EvalError::bad_form(
1815            "set!",
1816            "expected (set! name expr)",
1817            span,
1818        ));
1819    }
1820    let name = items[1]
1821        .as_symbol()
1822        .ok_or_else(|| EvalError::bad_form("set!", "first arg must be a symbol", items[1].span))?;
1823    let v = eval_in(env, registry, expander, &items[2], host)?;
1824    if env.set(name, v) {
1825        Ok(Value::Nil)
1826    } else if env.is_sealed_binding(name) {
1827        // Distinguishes a sealed write from an unbound name. A macro body
1828        // reaching outward to mutate the interpreter's globals lands here.
1829        Err(EvalError::bad_form(
1830            "set!",
1831            format!(
1832                "cannot `set!` {name:?} from a macro body — it is bound outside \
1833                 the expansion and sealed. Macro expansion must be deterministic, \
1834                 so compile-time state cannot outlive the expansion. Use a local \
1835                 binding, or return the value in the expansion."
1836            ),
1837            items[1].span,
1838        ))
1839    } else {
1840        Err(EvalError::unbound(name, items[1].span))
1841    }
1842}
1843
1844fn sf_begin<H: 'static>(
1845    body: &[Spanned],
1846    env: &mut Env,
1847    registry: &FnRegistry<H>,
1848    expander: &SpannedExpander,
1849    host: &mut H,
1850) -> Result<Value> {
1851    eval_body(body, env, registry, expander, host)
1852}
1853
1854fn sf_and<H: 'static>(
1855    exprs: &[Spanned],
1856    env: &mut Env,
1857    registry: &FnRegistry<H>,
1858    expander: &SpannedExpander,
1859    host: &mut H,
1860) -> Result<Value> {
1861    let mut last = Value::Bool(true);
1862    for e in exprs {
1863        last = eval_in(env, registry, expander, e, host)?;
1864        if !last.is_truthy() {
1865            return Ok(last);
1866        }
1867    }
1868    Ok(last)
1869}
1870
1871fn sf_or<H: 'static>(
1872    exprs: &[Spanned],
1873    env: &mut Env,
1874    registry: &FnRegistry<H>,
1875    expander: &SpannedExpander,
1876    host: &mut H,
1877) -> Result<Value> {
1878    let mut last = Value::Bool(false);
1879    for e in exprs {
1880        last = eval_in(env, registry, expander, e, host)?;
1881        if last.is_truthy() {
1882            return Ok(last);
1883        }
1884    }
1885    Ok(last)
1886}
1887
1888fn sf_not<H: 'static>(
1889    items: &[Spanned],
1890    span: Span,
1891    env: &mut Env,
1892    registry: &FnRegistry<H>,
1893    expander: &SpannedExpander,
1894    host: &mut H,
1895) -> Result<Value> {
1896    if items.len() != 2 {
1897        return Err(EvalError::bad_form("not", "expected (not x)", span));
1898    }
1899    let v = eval_in(env, registry, expander, &items[1], host)?;
1900    Ok(Value::Bool(!v.is_truthy()))
1901}
1902
1903/// `(try body... (catch (binding) handler...))` — evaluate body
1904/// sequentially. If any form raises an `EvalError::User` (Lisp
1905/// `(throw ...)`), bind the thrown Value to `binding` and run handler.
1906/// Other Rust-side errors (type mismatch, arity, etc.) are converted
1907/// to a `Value::Error` with tag `:runtime` so handlers can also
1908/// recover from them.
1909///
1910/// Form layout:
1911/// ```text
1912///   (try
1913///     body-expr
1914///     ...
1915///     (catch (e) handler-body...))
1916/// ```
1917/// The catch clause MUST be the last form. There can only be one
1918/// catch clause. Body forms before it are evaluated in order; the
1919/// last body form's value (or the handler's value, if caught) is
1920/// returned.
1921fn sf_try<H: 'static>(
1922    items: &[Spanned],
1923    span: Span,
1924    env: &mut Env,
1925    registry: &FnRegistry<H>,
1926    expander: &SpannedExpander,
1927    host: &mut H,
1928) -> Result<Value> {
1929    if items.len() < 3 {
1930        return Err(EvalError::bad_form(
1931            "try",
1932            "expected (try body... (catch (e) handler...))",
1933            span,
1934        ));
1935    }
1936    // The last form must be a catch clause.
1937    let catch_form = items.last().unwrap();
1938    let catch_list = catch_form.as_list().ok_or_else(|| {
1939        EvalError::bad_form(
1940            "try",
1941            "last form must be (catch (binding) handler...)",
1942            catch_form.span,
1943        )
1944    })?;
1945    if catch_list.is_empty() || catch_list[0].as_symbol() != Some("catch") {
1946        return Err(EvalError::bad_form(
1947            "try",
1948            "last form must be a (catch ...) clause",
1949            catch_form.span,
1950        ));
1951    }
1952    if catch_list.len() < 3 {
1953        return Err(EvalError::bad_form(
1954            "catch",
1955            "expected (catch (binding) handler...)",
1956            catch_form.span,
1957        ));
1958    }
1959    let binding_list = catch_list[1].as_list().ok_or_else(|| {
1960        EvalError::bad_form(
1961            "catch",
1962            "binding must be a 1-element list (e)",
1963            catch_list[1].span,
1964        )
1965    })?;
1966    if binding_list.len() != 1 {
1967        return Err(EvalError::bad_form(
1968            "catch",
1969            "binding must bind exactly one symbol",
1970            catch_list[1].span,
1971        ));
1972    }
1973    let binding_name = binding_list[0].as_symbol().ok_or_else(|| {
1974        EvalError::bad_form("catch", "binding must be a symbol", binding_list[0].span)
1975    })?;
1976
1977    let body = &items[1..items.len() - 1];
1978    let mut last = Value::Nil;
1979    for form in body {
1980        match eval_in(env, registry, expander, form, host) {
1981            Ok(v) => {
1982                last = v;
1983            }
1984            Err(EvalError::User { value, .. }) => {
1985                return run_catch_handler(
1986                    binding_name,
1987                    value,
1988                    &catch_list[2..],
1989                    env,
1990                    registry,
1991                    expander,
1992                    host,
1993                );
1994            }
1995            Err(other) => {
1996                // Convert any other runtime error into a Value::Error
1997                // so catch can still observe it. Tag :runtime
1998                // distinguishes from user-thrown errors.
1999                let value = rust_err_to_value_error(&other);
2000                return run_catch_handler(
2001                    binding_name,
2002                    value,
2003                    &catch_list[2..],
2004                    env,
2005                    registry,
2006                    expander,
2007                    host,
2008                );
2009            }
2010        }
2011    }
2012    Ok(last)
2013}
2014
2015fn run_catch_handler<H: 'static>(
2016    binding_name: &str,
2017    error_value: Value,
2018    handler_body: &[Spanned],
2019    env: &mut Env,
2020    registry: &FnRegistry<H>,
2021    expander: &SpannedExpander,
2022    host: &mut H,
2023) -> Result<Value> {
2024    env.push();
2025    env.define(Arc::<str>::from(binding_name), error_value);
2026    let mut last = Value::Nil;
2027    for form in handler_body {
2028        match eval_in(env, registry, expander, form, host) {
2029            Ok(v) => last = v,
2030            Err(e) => {
2031                env.pop();
2032                return Err(e);
2033            }
2034        }
2035    }
2036    env.pop();
2037    Ok(last)
2038}
2039
2040/// `(eval form)` — evaluate the runtime Value `form` as code. The
2041/// argument is itself evaluated first to obtain the form (typically
2042/// a quoted list). The form is then lifted to Spanned, fully expanded
2043/// (in case it contains macro calls), and evaluated in the current
2044/// env. Returns the result.
2045///
2046/// Unlocks runtime metaprogramming: `(eval (read-string source))` is
2047/// the canonical "compile + run from string" pattern.
2048fn sf_eval<H: 'static>(
2049    items: &[Spanned],
2050    call_span: Span,
2051    env: &mut Env,
2052    registry: &FnRegistry<H>,
2053    expander: &SpannedExpander,
2054    host: &mut H,
2055) -> Result<Value> {
2056    if items.len() != 2 {
2057        return Err(EvalError::bad_form(
2058            "eval",
2059            "expected (eval form)",
2060            call_span,
2061        ));
2062    }
2063    let form_value = eval_in(env, registry, expander, &items[1], host)?;
2064    let form_spanned = crate::code::value_to_spanned(&form_value, call_span)
2065        .map_err(|reason| EvalError::native_fn(Arc::<str>::from("eval"), reason, call_span))?;
2066    let expanded = fully_expand_with(&form_spanned, registry, expander, env, host)?;
2067    eval_in(env, registry, expander, &expanded, host)
2068}
2069
2070/// `(delay expr)` — wrap `expr` in a `Value::Promise` whose first
2071/// `force` evaluates the body once and caches. The body becomes the
2072/// closure body of a 0-arity lambda capturing the current env, then
2073/// stored as the promise's pending state.
2074fn sf_delay(items: &[Spanned], call_span: Span, env: &Env) -> Result<Value> {
2075    if items.len() != 2 {
2076        return Err(EvalError::bad_form(
2077            "delay",
2078            "expected (delay expr)",
2079            call_span,
2080        ));
2081    }
2082    let body = vec![items[1].clone()];
2083    let thunk = Arc::new(Closure {
2084        params: Vec::new(),
2085        rest: None,
2086        body,
2087        captured_env: env.clone(),
2088        source: call_span,
2089    });
2090    Ok(Value::Promise(Arc::new(std::sync::Mutex::new(
2091        crate::value::PromiseState::Pending(thunk),
2092    ))))
2093}
2094
2095/// `(macroexpand-1 form)` and `(macroexpand form)` — return the
2096/// expansion of `form` as a Value. `form` is evaluated to obtain a
2097/// source-form Value (typically a quoted list); we lift it back to a
2098/// Spanned, run one (macroexpand-1) or full (macroexpand) expansion,
2099/// then convert the result Value back.
2100///
2101/// Useful for debugging macros — see exactly what the expander
2102/// produces given a sample input.
2103fn sf_macroexpand<H: 'static>(
2104    items: &[Spanned],
2105    call_span: Span,
2106    env: &mut Env,
2107    registry: &FnRegistry<H>,
2108    expander: &SpannedExpander,
2109    host: &mut H,
2110    fully: bool,
2111) -> Result<Value> {
2112    if items.len() != 2 {
2113        return Err(EvalError::bad_form(
2114            if fully {
2115                "macroexpand"
2116            } else {
2117                "macroexpand-1"
2118            },
2119            "expected (macroexpand[-1] form)",
2120            call_span,
2121        ));
2122    }
2123    // Evaluate the argument to obtain a source-form Value.
2124    let form_value = eval_in(env, registry, expander, &items[1], host)?;
2125    // Lift to Spanned so the expander can walk it.
2126    let form_spanned = crate::code::value_to_spanned(&form_value, call_span).map_err(|reason| {
2127        EvalError::native_fn(
2128            Arc::<str>::from(if fully {
2129                "macroexpand"
2130            } else {
2131                "macroexpand-1"
2132            }),
2133            reason,
2134            call_span,
2135        )
2136    })?;
2137
2138    // Build a fresh interpreter-style call into the same expander/registry.
2139    // We can't recursively call self.fully_expand or self.expand_macro_call
2140    // here because we don't have &mut Interpreter. Instead, we do the
2141    // single-step or recursive expansion ourselves via the same
2142    // primitives that the Interpreter uses.
2143    let expanded = if fully {
2144        fully_expand_with(&form_spanned, registry, expander, env, host)?
2145    } else {
2146        macroexpand_one(&form_spanned, registry, expander, env, host)?
2147    };
2148
2149    Ok(crate::code::spanned_to_value(&expanded))
2150}
2151
2152/// Free-function variant of `Interpreter::expand_macro_call`. Takes the
2153/// state pieces explicitly so it can be called from a special form
2154/// (where we don't have `&mut Interpreter` available).
2155fn expand_one_macro_call<H: 'static>(
2156    macro_name: &str,
2157    args: &[Spanned],
2158    call_span: Span,
2159    registry: &FnRegistry<H>,
2160    expander: &SpannedExpander,
2161    parent_env: &Env,
2162    host: &mut H,
2163) -> Result<Spanned> {
2164    let def: MacroDef = expander.get_macro(macro_name).cloned().ok_or_else(|| {
2165        EvalError::native_fn(
2166            Arc::<str>::from(macro_name),
2167            "macro disappeared during expansion",
2168            call_span,
2169        )
2170    })?;
2171    let body_spanned = Spanned::from_sexp_at(&def.body, call_span);
2172    // First expand any macros inside the body itself.
2173    let body_expanded = fully_expand_with(&body_spanned, registry, expander, parent_env, host)?;
2174
2175    let mut macro_env = parent_env.clone();
2176    macro_env.push();
2177    bind_macro_args(&mut macro_env, &def.name, &def.params, args, call_span)?;
2178    let result = eval_in(&mut macro_env, registry, expander, &body_expanded, host)?;
2179
2180    crate::code::value_to_spanned(&result, call_span).map_err(|reason| {
2181        EvalError::native_fn(
2182            Arc::<str>::from(format!("macro {macro_name}")),
2183            reason,
2184            call_span,
2185        )
2186    })
2187}
2188
2189/// Free-function variant of `Interpreter::fully_expand`. Recursively
2190/// expands every macro call in the form tree, terminating at fixed
2191/// point.
2192fn fully_expand_with<H: 'static>(
2193    form: &Spanned,
2194    registry: &FnRegistry<H>,
2195    expander: &SpannedExpander,
2196    parent_env: &Env,
2197    host: &mut H,
2198) -> Result<Spanned> {
2199    if expander.is_empty() {
2200        return Ok(form.clone());
2201    }
2202    expand_recursive_with(form, registry, expander, parent_env, host)
2203}
2204
2205fn expand_recursive_with<H: 'static>(
2206    form: &Spanned,
2207    registry: &FnRegistry<H>,
2208    expander: &SpannedExpander,
2209    parent_env: &Env,
2210    host: &mut H,
2211) -> Result<Spanned> {
2212    match &form.form {
2213        SpannedForm::List(items) if !items.is_empty() => {
2214            if let Some(head) = items[0].as_symbol() {
2215                if expander.has(head) {
2216                    let expanded = expand_one_macro_call(
2217                        head,
2218                        &items[1..],
2219                        form.span,
2220                        registry,
2221                        expander,
2222                        parent_env,
2223                        host,
2224                    )?;
2225                    return expand_recursive_with(&expanded, registry, expander, parent_env, host);
2226                }
2227            }
2228            let mut out = Vec::with_capacity(items.len());
2229            for child in items {
2230                out.push(expand_recursive_with(
2231                    child, registry, expander, parent_env, host,
2232                )?);
2233            }
2234            Ok(Spanned::new(form.span, SpannedForm::List(out)))
2235        }
2236        SpannedForm::Quote(_) => Ok(form.clone()),
2237        SpannedForm::Quasiquote(inner) => Ok(Spanned::new(
2238            form.span,
2239            SpannedForm::Quasiquote(Box::new(expand_inside_quasiquote_with(
2240                inner, registry, expander, parent_env, host,
2241            )?)),
2242        )),
2243        _ => Ok(form.clone()),
2244    }
2245}
2246
2247fn expand_inside_quasiquote_with<H: 'static>(
2248    form: &Spanned,
2249    registry: &FnRegistry<H>,
2250    expander: &SpannedExpander,
2251    parent_env: &Env,
2252    host: &mut H,
2253) -> Result<Spanned> {
2254    match &form.form {
2255        SpannedForm::Unquote(inner) => Ok(Spanned::new(
2256            form.span,
2257            SpannedForm::Unquote(Box::new(expand_recursive_with(
2258                inner, registry, expander, parent_env, host,
2259            )?)),
2260        )),
2261        SpannedForm::UnquoteSplice(inner) => Ok(Spanned::new(
2262            form.span,
2263            SpannedForm::UnquoteSplice(Box::new(expand_recursive_with(
2264                inner, registry, expander, parent_env, host,
2265            )?)),
2266        )),
2267        SpannedForm::List(items) => {
2268            let mut out = Vec::with_capacity(items.len());
2269            for item in items {
2270                out.push(expand_inside_quasiquote_with(
2271                    item, registry, expander, parent_env, host,
2272                )?);
2273            }
2274            Ok(Spanned::new(form.span, SpannedForm::List(out)))
2275        }
2276        _ => Ok(form.clone()),
2277    }
2278}
2279
2280/// One-step macroexpansion: expand ONLY the head call if it's a macro;
2281/// otherwise return form unchanged. Children are NOT expanded.
2282fn macroexpand_one<H: 'static>(
2283    form: &Spanned,
2284    registry: &FnRegistry<H>,
2285    expander: &SpannedExpander,
2286    parent_env: &Env,
2287    host: &mut H,
2288) -> Result<Spanned> {
2289    if let SpannedForm::List(items) = &form.form {
2290        if let Some(head) = items.first().and_then(Spanned::as_symbol) {
2291            if expander.has(head) {
2292                return expand_one_macro_call(
2293                    head,
2294                    &items[1..],
2295                    form.span,
2296                    registry,
2297                    expander,
2298                    parent_env,
2299                    host,
2300                );
2301            }
2302        }
2303    }
2304    Ok(form.clone())
2305}
2306
2307/// Convert a Rust-side `EvalError` into a `Value::Error` so a `(catch)`
2308/// handler can observe runtime errors uniformly with user-thrown ones.
2309fn rust_err_to_value_error(err: &EvalError) -> Value {
2310    use crate::value::ErrorObj;
2311    let tag: Arc<str> = match err {
2312        EvalError::UnboundSymbol { .. } => Arc::from("unbound-symbol"),
2313        EvalError::ArityMismatch { .. } => Arc::from("arity-mismatch"),
2314        EvalError::TypeMismatch { .. } => Arc::from("type-mismatch"),
2315        EvalError::DivisionByZero { .. } => Arc::from("division-by-zero"),
2316        EvalError::NotCallable { .. } => Arc::from("not-callable"),
2317        EvalError::BadSpecialForm { .. } => Arc::from("bad-special-form"),
2318        EvalError::NativeFn { .. } => Arc::from("native-fn"),
2319        EvalError::Reader(_) => Arc::from("reader"),
2320        EvalError::Halted => Arc::from("halted"),
2321        EvalError::NotImplemented(_) => Arc::from("not-implemented"),
2322        EvalError::User { .. } => Arc::from("user"),
2323    };
2324    let message: Arc<str> = Arc::from(err.short_message());
2325    Value::Error(Arc::new(ErrorObj {
2326        tag,
2327        message,
2328        data: Vec::new(),
2329    }))
2330}
2331
2332#[cfg(test)]
2333mod tests {
2334    use super::*;
2335    use crate::primitive::install_primitives;
2336    use tatara_lisp::read_spanned;
2337
2338    struct NoHost;
2339
2340    fn eval_ok(src: &str) -> Value {
2341        let forms = read_spanned(src).unwrap();
2342        let mut i: Interpreter<NoHost> = Interpreter::new();
2343        install_primitives(&mut i);
2344        let mut host = NoHost;
2345        i.eval_program(&forms, &mut host).unwrap()
2346    }
2347
2348    fn eval_err(src: &str) -> EvalError {
2349        let forms = read_spanned(src).unwrap();
2350        let mut i: Interpreter<NoHost> = Interpreter::new();
2351        install_primitives(&mut i);
2352        let mut host = NoHost;
2353        i.eval_program(&forms, &mut host).unwrap_err()
2354    }
2355
2356    // ── Literals + symbol lookup ──────────────────────────────────
2357
2358    #[test]
2359    fn literal_int() {
2360        assert!(matches!(eval_ok("42"), Value::Int(42)));
2361    }
2362
2363    #[test]
2364    fn unbound_symbol_errors() {
2365        let e = eval_err("no-such-var");
2366        assert!(matches!(e, EvalError::UnboundSymbol { .. }));
2367    }
2368
2369    #[test]
2370    fn quote_returns_runtime_list_of_symbols() {
2371        // Scheme/Clojure semantics: '(a b c) yields a runtime list of
2372        // three symbols, not a wrapped source-form Sexp.
2373        let v = eval_ok("'(a b c)");
2374        match v {
2375            Value::List(xs) => {
2376                assert_eq!(xs.len(), 3);
2377                assert!(matches!(&xs[0], Value::Symbol(s) if s.as_ref() == "a"));
2378                assert!(matches!(&xs[1], Value::Symbol(s) if s.as_ref() == "b"));
2379                assert!(matches!(&xs[2], Value::Symbol(s) if s.as_ref() == "c"));
2380            }
2381            other => panic!("{other:?}"),
2382        }
2383    }
2384
2385    // ── Arithmetic via primitives ─────────────────────────────────
2386
2387    #[test]
2388    fn add_ints() {
2389        assert!(matches!(eval_ok("(+ 1 2 3)"), Value::Int(6)));
2390    }
2391
2392    #[test]
2393    fn sub_divides_float() {
2394        match eval_ok("(- 10 3)") {
2395            Value::Int(7) => {}
2396            other => panic!("{other:?}"),
2397        }
2398    }
2399
2400    #[test]
2401    fn division_by_zero_errors() {
2402        assert!(matches!(
2403            eval_err("(/ 1 0)"),
2404            EvalError::DivisionByZero { .. }
2405        ));
2406    }
2407
2408    // ── Conditionals ──────────────────────────────────────────────
2409
2410    #[test]
2411    fn if_truthy_branch() {
2412        assert!(matches!(eval_ok("(if #t 1 2)"), Value::Int(1)));
2413    }
2414
2415    #[test]
2416    fn if_falsy_branch() {
2417        assert!(matches!(eval_ok("(if #f 1 2)"), Value::Int(2)));
2418    }
2419
2420    #[test]
2421    fn if_no_else_returns_nil() {
2422        assert!(matches!(eval_ok("(if #f 1)"), Value::Nil));
2423    }
2424
2425    #[test]
2426    fn cond_picks_first_match() {
2427        assert!(matches!(
2428            eval_ok("(cond (#f 1) (#t 2) (else 3))"),
2429            Value::Int(2)
2430        ));
2431    }
2432
2433    #[test]
2434    fn cond_falls_through_to_else() {
2435        assert!(matches!(
2436            eval_ok("(cond (#f 1) (#f 2) (else 3))"),
2437            Value::Int(3)
2438        ));
2439    }
2440
2441    #[test]
2442    fn when_runs_body_if_true() {
2443        assert!(matches!(eval_ok("(when #t 99)"), Value::Int(99)));
2444        assert!(matches!(eval_ok("(when #f 99)"), Value::Nil));
2445    }
2446
2447    // ── Let forms ─────────────────────────────────────────────────
2448
2449    #[test]
2450    fn let_binds_and_evaluates_body() {
2451        assert!(matches!(
2452            eval_ok("(let ((x 10) (y 20)) (+ x y))"),
2453            Value::Int(30)
2454        ));
2455    }
2456
2457    #[test]
2458    fn let_star_sequential_bindings() {
2459        assert!(matches!(
2460            eval_ok("(let* ((x 5) (y (+ x 1))) (+ x y))"),
2461            Value::Int(11)
2462        ));
2463    }
2464
2465    #[test]
2466    fn letrec_mutual_recursion() {
2467        let v = eval_ok(
2468            "(letrec ((even? (lambda (n) (if (= n 0) #t (odd? (- n 1)))))
2469                      (odd?  (lambda (n) (if (= n 0) #f (even? (- n 1))))))
2470               (even? 10))",
2471        );
2472        assert!(matches!(v, Value::Bool(true)));
2473    }
2474
2475    // ── Lambda + closure ──────────────────────────────────────────
2476
2477    #[test]
2478    fn lambda_applies() {
2479        assert!(matches!(
2480            eval_ok("((lambda (x y) (+ x y)) 3 4)"),
2481            Value::Int(7)
2482        ));
2483    }
2484
2485    #[test]
2486    fn lambda_closes_over_env() {
2487        assert!(matches!(
2488            eval_ok("(let ((n 10)) ((lambda (x) (+ x n)) 5))"),
2489            Value::Int(15)
2490        ));
2491    }
2492
2493    #[test]
2494    fn closure_captures_by_value_at_creation() {
2495        // make-adder style — the returned closure should capture n=5 even
2496        // though the outer let scope has exited.
2497        let v = eval_ok(
2498            "(define make-adder (lambda (n) (lambda (x) (+ x n))))
2499             (define add5 (make-adder 5))
2500             (add5 10)",
2501        );
2502        assert!(matches!(v, Value::Int(15)));
2503    }
2504
2505    #[test]
2506    fn rest_args_collect_into_list() {
2507        let v = eval_ok("((lambda (x &rest rs) (length rs)) 1 2 3 4 5)");
2508        assert!(matches!(v, Value::Int(4)));
2509    }
2510
2511    #[test]
2512    fn closure_arity_mismatch() {
2513        let e = eval_err("((lambda (x y) (+ x y)) 1)");
2514        assert!(matches!(e, EvalError::ArityMismatch { .. }));
2515    }
2516
2517    // ── Define + set! ─────────────────────────────────────────────
2518
2519    #[test]
2520    fn define_then_use() {
2521        assert!(matches!(eval_ok("(define x 42) x"), Value::Int(42)));
2522    }
2523
2524    #[test]
2525    fn define_function_shorthand() {
2526        assert!(matches!(
2527            eval_ok("(define (sq x) (* x x)) (sq 6)"),
2528            Value::Int(36)
2529        ));
2530    }
2531
2532    #[test]
2533    fn set_mutates_existing() {
2534        assert!(matches!(
2535            eval_ok("(define x 1) (set! x 99) x"),
2536            Value::Int(99)
2537        ));
2538    }
2539
2540    #[test]
2541    fn set_unbound_errors() {
2542        let e = eval_err("(set! nope 1)");
2543        assert!(matches!(e, EvalError::UnboundSymbol { .. }));
2544    }
2545
2546    // ── begin / and / or / not ────────────────────────────────────
2547
2548    #[test]
2549    fn begin_returns_last() {
2550        assert!(matches!(eval_ok("(begin 1 2 3)"), Value::Int(3)));
2551    }
2552
2553    #[test]
2554    fn and_short_circuits() {
2555        assert!(matches!(eval_ok("(and 1 #f 2)"), Value::Bool(false)));
2556        assert!(matches!(eval_ok("(and 1 2 3)"), Value::Int(3)));
2557        assert!(matches!(eval_ok("(and)"), Value::Bool(true)));
2558    }
2559
2560    #[test]
2561    fn or_short_circuits() {
2562        assert!(matches!(eval_ok("(or #f #f 7)"), Value::Int(7)));
2563        assert!(matches!(eval_ok("(or #f #f)"), Value::Bool(false)));
2564        assert!(matches!(eval_ok("(or)"), Value::Bool(false)));
2565    }
2566
2567    #[test]
2568    fn not_inverts() {
2569        assert!(matches!(eval_ok("(not #t)"), Value::Bool(false)));
2570        assert!(matches!(eval_ok("(not #f)"), Value::Bool(true)));
2571        assert!(matches!(eval_ok("(not 42)"), Value::Bool(false)));
2572    }
2573
2574    // ── Recursion ─────────────────────────────────────────────────
2575
2576    #[test]
2577    fn recursive_factorial() {
2578        let v = eval_ok(
2579            "(define (fact n)
2580               (if (= n 0) 1 (* n (fact (- n 1)))))
2581             (fact 6)",
2582        );
2583        assert!(matches!(v, Value::Int(720)));
2584    }
2585
2586    #[test]
2587    fn recursive_length() {
2588        let v = eval_ok(
2589            "(define (len xs)
2590               (if (null? xs) 0 (+ 1 (len (cdr xs)))))
2591             (len (list 1 2 3 4 5))",
2592        );
2593        assert!(matches!(v, Value::Int(5)));
2594    }
2595
2596    // ── Host context reachable via register_fn ────────────────────
2597
2598    // ── Quasiquote ────────────────────────────────────────────────
2599
2600    #[test]
2601    fn quasiquote_plain_list_is_runtime_list() {
2602        let v = eval_ok("`(a b c)");
2603        match v {
2604            Value::List(xs) => {
2605                assert_eq!(xs.len(), 3);
2606                assert!(matches!(&xs[0], Value::Symbol(s) if s.as_ref() == "a"));
2607                assert!(matches!(&xs[1], Value::Symbol(s) if s.as_ref() == "b"));
2608                assert!(matches!(&xs[2], Value::Symbol(s) if s.as_ref() == "c"));
2609            }
2610            other => panic!("{other:?}"),
2611        }
2612    }
2613
2614    #[test]
2615    fn quasiquote_unquote_substitutes_evaluated_value() {
2616        let v = eval_ok("(let ((x 42)) `(a ,x c))");
2617        match v {
2618            Value::List(xs) => {
2619                assert_eq!(xs.len(), 3);
2620                assert!(matches!(&xs[1], Value::Int(42)));
2621            }
2622            other => panic!("{other:?}"),
2623        }
2624    }
2625
2626    #[test]
2627    fn quasiquote_unquote_arbitrary_expr() {
2628        let v = eval_ok("`(x ,(+ 1 2 3) y)");
2629        match v {
2630            Value::List(xs) => {
2631                assert!(matches!(&xs[1], Value::Int(6)));
2632            }
2633            other => panic!("{other:?}"),
2634        }
2635    }
2636
2637    #[test]
2638    fn quasiquote_splice_inlines_list() {
2639        let v = eval_ok("`(a ,@(list 1 2 3) b)");
2640        match v {
2641            Value::List(xs) => {
2642                assert_eq!(xs.len(), 5);
2643                assert!(matches!(&xs[0], Value::Symbol(s) if s.as_ref() == "a"));
2644                assert!(matches!(&xs[1], Value::Int(1)));
2645                assert!(matches!(&xs[2], Value::Int(2)));
2646                assert!(matches!(&xs[3], Value::Int(3)));
2647                assert!(matches!(&xs[4], Value::Symbol(s) if s.as_ref() == "b"));
2648            }
2649            other => panic!("{other:?}"),
2650        }
2651    }
2652
2653    #[test]
2654    fn quasiquote_splice_empty_list_splices_nothing() {
2655        let v = eval_ok("`(a ,@(list) b)");
2656        match v {
2657            Value::List(xs) => {
2658                assert_eq!(xs.len(), 2);
2659                assert!(matches!(&xs[0], Value::Symbol(s) if s.as_ref() == "a"));
2660                assert!(matches!(&xs[1], Value::Symbol(s) if s.as_ref() == "b"));
2661            }
2662            other => panic!("{other:?}"),
2663        }
2664    }
2665
2666    #[test]
2667    fn quasiquote_splice_non_list_errors() {
2668        let e = eval_err("`(a ,@42)");
2669        assert!(matches!(e, EvalError::TypeMismatch { .. }));
2670    }
2671
2672    #[test]
2673    fn quasiquote_atom_yields_atom_value() {
2674        assert!(matches!(eval_ok("`foo"), Value::Symbol(s) if s.as_ref() == "foo"));
2675        assert!(matches!(eval_ok("`42"), Value::Int(42)));
2676    }
2677
2678    #[test]
2679    fn quasiquote_with_nested_list_and_unquote() {
2680        // `(foo (bar ,x) baz) where x=99 → (foo (bar 99) baz)
2681        let v = eval_ok("(let ((x 99)) `(foo (bar ,x) baz))");
2682        match v {
2683            Value::List(xs) => {
2684                assert_eq!(xs.len(), 3);
2685                match &xs[1] {
2686                    Value::List(inner) => {
2687                        assert!(matches!(&inner[1], Value::Int(99)));
2688                    }
2689                    other => panic!("{other:?}"),
2690                }
2691            }
2692            other => panic!("{other:?}"),
2693        }
2694    }
2695
2696    #[test]
2697    fn quasiquote_symbol_keyword_distinction_preserved() {
2698        let v = eval_ok("`(:key val)");
2699        match v {
2700            Value::List(xs) => {
2701                assert!(matches!(&xs[0], Value::Keyword(s) if s.as_ref() == "key"));
2702                assert!(matches!(&xs[1], Value::Symbol(s) if s.as_ref() == "val"));
2703            }
2704            other => panic!("{other:?}"),
2705        }
2706    }
2707
2708    #[test]
2709    fn bare_unquote_outside_quasiquote_errors() {
2710        let e = eval_err(",x");
2711        assert!(matches!(e, EvalError::BadSpecialForm { .. }));
2712    }
2713
2714    // ── Host context reachable via register_fn ────────────────────
2715
2716    #[test]
2717    fn native_fn_reads_host_state() {
2718        struct Counter {
2719            n: i64,
2720        }
2721        let forms = read_spanned("(bump) (bump) (bump) (cur)").unwrap();
2722        let mut i: Interpreter<Counter> = Interpreter::new();
2723        install_primitives(&mut i);
2724        i.register_fn(
2725            "bump",
2726            Arity::Exact(0),
2727            |_args: &[Value], host: &mut Counter, _span| {
2728                host.n += 1;
2729                Ok(Value::Int(host.n))
2730            },
2731        );
2732        i.register_fn(
2733            "cur",
2734            Arity::Exact(0),
2735            |_args: &[Value], host: &mut Counter, _span| Ok(Value::Int(host.n)),
2736        );
2737        let mut host = Counter { n: 0 };
2738        let v = i.eval_program(&forms, &mut host).unwrap();
2739        assert!(matches!(v, Value::Int(3)));
2740    }
2741
2742    // ── Typed FFI registration ────────────────────────────────────
2743
2744    struct Ctx {
2745        records: Vec<(String, i64)>,
2746    }
2747
2748    #[test]
2749    fn register_typed1_marshals_string_arg() {
2750        let mut i: Interpreter<Ctx> = Interpreter::new();
2751        install_primitives(&mut i);
2752        i.register_typed1("greet", |_h: &mut Ctx, name: String| -> Result<String> {
2753            Ok(format!("hello {name}"))
2754        });
2755        let forms = read_spanned(r#"(greet "luis")"#).unwrap();
2756        let mut h = Ctx { records: vec![] };
2757        let v = i.eval_program(&forms, &mut h).unwrap();
2758        match v {
2759            Value::Str(s) => assert_eq!(&*s, "hello luis"),
2760            other => panic!("{other:?}"),
2761        }
2762    }
2763
2764    #[test]
2765    fn register_typed2_marshals_host_state_mutation() {
2766        let mut i: Interpreter<Ctx> = Interpreter::new();
2767        install_primitives(&mut i);
2768        i.register_typed2(
2769            "record",
2770            |h: &mut Ctx, name: String, n: i64| -> Result<()> {
2771                h.records.push((name, n));
2772                Ok(())
2773            },
2774        );
2775        let forms = read_spanned(r#"(record "a" 1) (record "b" 2)"#).unwrap();
2776        let mut h = Ctx { records: vec![] };
2777        let _ = i.eval_program(&forms, &mut h).unwrap();
2778        assert_eq!(h.records.len(), 2);
2779        assert_eq!(h.records[0], ("a".to_string(), 1));
2780        assert_eq!(h.records[1], ("b".to_string(), 2));
2781    }
2782
2783    #[test]
2784    fn register_typed_arg_type_mismatch_surfaces_at_call_site() {
2785        let mut i: Interpreter<Ctx> = Interpreter::new();
2786        install_primitives(&mut i);
2787        i.register_typed1("needs-int", |_h: &mut Ctx, n: i64| -> Result<i64> {
2788            Ok(n + 1)
2789        });
2790        let forms = read_spanned(r#"(needs-int "not-a-number")"#).unwrap();
2791        let mut h = Ctx { records: vec![] };
2792        let err = i.eval_program(&forms, &mut h).unwrap_err();
2793        assert!(matches!(
2794            err,
2795            EvalError::TypeMismatch {
2796                expected: "integer",
2797                ..
2798            }
2799        ));
2800    }
2801
2802    #[test]
2803    fn register_typed3_three_args() {
2804        let mut i: Interpreter<Ctx> = Interpreter::new();
2805        install_primitives(&mut i);
2806        i.register_typed3(
2807            "triple-sum",
2808            |_h: &mut Ctx, a: i64, b: i64, c: i64| -> Result<i64> { Ok(a + b + c) },
2809        );
2810        let forms = read_spanned("(triple-sum 10 20 30)").unwrap();
2811        let mut h = Ctx { records: vec![] };
2812        let v = i.eval_program(&forms, &mut h).unwrap();
2813        assert!(matches!(v, Value::Int(60)));
2814    }
2815
2816    // ── User macros via defmacro ──────────────────────────────────
2817
2818    #[test]
2819    fn user_macro_expands_and_evaluates() {
2820        let v = eval_ok(
2821            "(defmacro twice (x) `(* ,x 2))
2822             (twice 21)",
2823        );
2824        assert!(matches!(v, Value::Int(42)));
2825    }
2826
2827    #[test]
2828    fn user_macro_definition_returns_nil() {
2829        let v = eval_ok("(defmacro inc (x) `(+ ,x 1))");
2830        assert!(matches!(v, Value::Nil));
2831    }
2832
2833    #[test]
2834    fn user_macro_inside_define_body_expands() {
2835        // (define (f n) (inc n)) — the (inc n) call is rewritten to (+ n 1)
2836        // before define captures the body.
2837        let v = eval_ok(
2838            "(defmacro inc (x) `(+ ,x 1))
2839             (define (f n) (inc n))
2840             (f 41)",
2841        );
2842        assert!(matches!(v, Value::Int(42)));
2843    }
2844
2845    #[test]
2846    fn user_macro_with_rest_args_splices() {
2847        let v = eval_ok(
2848            "(defmacro sum-all (&rest xs) `(+ ,@xs))
2849             (sum-all 1 2 3 4 5)",
2850        );
2851        assert!(matches!(v, Value::Int(15)));
2852    }
2853
2854    #[test]
2855    fn nested_user_macros_compose() {
2856        let v = eval_ok(
2857            "(defmacro twice (x) `(* ,x 2))
2858             (defmacro quad (x) `(twice (twice ,x)))
2859             (quad 5)",
2860        );
2861        assert!(matches!(v, Value::Int(20)));
2862    }
2863
2864    #[test]
2865    fn user_macro_can_expand_to_special_form() {
2866        // Macros can expand into special forms — `if`, `let`, `lambda`,
2867        // `define` are all reachable as expansion targets.
2868        let v = eval_ok(
2869            "(defmacro guard (test then) `(if ,test ,then 0))
2870             (guard #t 99)",
2871        );
2872        assert!(matches!(v, Value::Int(99)));
2873    }
2874
2875    #[test]
2876    fn user_macro_redefined_replaces_prior_template() {
2877        let v = eval_ok(
2878            "(defmacro k () `1)
2879             (defmacro k () `2)
2880             (k)",
2881        );
2882        assert!(matches!(v, Value::Int(2)));
2883    }
2884
2885    #[test]
2886    fn user_macro_unbound_template_var_errors() {
2887        // ,y refers to a name not bound in the macro's parameter list
2888        // and not defined in the surrounding scope. Under the
2889        // full-eval expander this surfaces as a proper unbound-symbol
2890        // error at expansion time, with the offending symbol in the
2891        // payload — strictly better than the legacy "compile" error.
2892        let mut i: Interpreter<NoHost> = Interpreter::new();
2893        install_primitives(&mut i);
2894        let forms = read_spanned("(defmacro bad (x) `(list ,y)) (bad 1)").unwrap();
2895        let err = i.eval_program(&forms, &mut NoHost).unwrap_err();
2896        match err {
2897            EvalError::UnboundSymbol { name, .. } => assert_eq!(&*name, "y"),
2898            other => panic!("expected UnboundSymbol, got {other:?}"),
2899        }
2900    }
2901
2902    #[test]
2903    fn defpoint_template_keyword_registers_as_macro() {
2904        // `defpoint-template` is the typed-DSL spelling of `defmacro` —
2905        // the runtime should accept both.
2906        let v = eval_ok(
2907            "(defpoint-template double (x) `(* ,x 2))
2908             (double 7)",
2909        );
2910        assert!(matches!(v, Value::Int(14)));
2911    }
2912
2913    #[test]
2914    fn defcheck_keyword_registers_as_macro() {
2915        let v = eval_ok(
2916            "(defcheck always-7 () `7)
2917             (always-7)",
2918        );
2919        assert!(matches!(v, Value::Int(7)));
2920    }
2921
2922    #[test]
2923    fn macro_call_evaluated_with_runtime_arg() {
2924        // Macro arg is itself an expression — the substituted expression
2925        // is evaluated *after* expansion, so the arg's runtime value is
2926        // what reaches the expanded form.
2927        let v = eval_ok(
2928            "(defmacro double (x) `(+ ,x ,x))
2929             (define n 13)
2930             (double n)",
2931        );
2932        assert!(matches!(v, Value::Int(26)));
2933    }
2934
2935    #[test]
2936    fn macro_persists_across_eval_program_calls() {
2937        // The expander state outlives a single eval_program call — REPL
2938        // semantics rely on this.
2939        let mut i: Interpreter<NoHost> = Interpreter::new();
2940        install_primitives(&mut i);
2941        let mut host = NoHost;
2942        let defs = read_spanned("(defmacro inc (x) `(+ ,x 1))").unwrap();
2943        i.eval_program(&defs, &mut host).unwrap();
2944        assert_eq!(i.expander().len(), 1);
2945
2946        let call = read_spanned("(inc 41)").unwrap();
2947        let v = i.eval_program(&call, &mut host).unwrap();
2948        assert!(matches!(v, Value::Int(42)));
2949    }
2950
2951    #[test]
2952    fn macro_expansion_inside_lambda_body() {
2953        let v = eval_ok(
2954            "(defmacro sq (x) `(* ,x ,x))
2955             ((lambda (n) (sq n)) 9)",
2956        );
2957        assert!(matches!(v, Value::Int(81)));
2958    }
2959
2960    #[test]
2961    fn no_macros_registered_keeps_eval_program_a_passthrough() {
2962        // Sanity: with no macros registered, eval_program should still run
2963        // every existing test path correctly. Touching the same code as
2964        // the rest of the suite — this just asserts the optimization
2965        // we baked in (skip expand when expander is empty) didn't
2966        // accidentally drop forms.
2967        let v = eval_ok("(+ 1 2 3)");
2968        assert!(matches!(v, Value::Int(6)));
2969    }
2970
2971    #[test]
2972    fn eval_top_form_drives_one_form_at_a_time() {
2973        let mut i: Interpreter<NoHost> = Interpreter::new();
2974        install_primitives(&mut i);
2975        let mut host = NoHost;
2976        let forms = read_spanned("(defmacro id (x) `,x) (id 42)").unwrap();
2977
2978        // First form: registers, returns Nil.
2979        let r0 = i.eval_top_form(&forms[0], &mut host).unwrap();
2980        assert!(matches!(r0, Value::Nil));
2981
2982        // Second form: macro expanded → 42.
2983        let r1 = i.eval_top_form(&forms[1], &mut host).unwrap();
2984        assert!(matches!(r1, Value::Int(42)));
2985    }
2986
2987    // ── Full-eval macroexpansion power tests ──────────────────────
2988    //
2989    // These exercise the Racket/CL/Clojure-grade macro model: the
2990    // macro body is a regular Lisp program evaluated at expansion time
2991    // with full access to every primitive and library fn.
2992
2993    use crate::install_full_stdlib_with;
2994
2995    fn run_full(src: &str) -> Value {
2996        let mut i: Interpreter<NoHost> = Interpreter::new();
2997        install_full_stdlib_with(&mut i, &mut NoHost);
2998        let forms = read_spanned(src).unwrap();
2999        i.eval_program(&forms, &mut NoHost).unwrap()
3000    }
3001
3002    #[test]
3003    fn macro_can_use_map_at_expansion_time() {
3004        // The macro body uses (map ...) at expansion time to transform
3005        // each arg into a different form. Result: a `(list ...)` whose
3006        // children are the squared symbols' representations.
3007        let v = run_full(
3008            "(defmacro double-each (&rest xs)
3009               `(list ,@(map (lambda (x) (* x 2)) xs)))
3010             (double-each 1 2 3 4 5)",
3011        );
3012        assert_eq!(format!("{v}"), "(2 4 6 8 10)");
3013    }
3014
3015    #[test]
3016    fn macro_can_use_foldl_at_expansion_time() {
3017        // The expansion ITSELF is built by folding — the macro returns
3018        // a sum-of-args expression, but only after expansion-time
3019        // computation chooses the additive form.
3020        let v = run_full(
3021            "(defmacro static-sum (&rest xs)
3022               (foldl + 0 xs))
3023             (static-sum 1 2 3 4 5)",
3024        );
3025        assert!(matches!(v, Value::Int(15)));
3026    }
3027
3028    #[test]
3029    fn macro_can_use_filter_at_expansion_time() {
3030        // Macro args arrive as source-form Values: literals stay
3031        // literals, but `(- 4)` is a List not a negative number.
3032        // Use direct negative literals so the filter sees integers.
3033        let v = run_full(
3034            "(defmacro sum-positives (&rest xs)
3035               `(+ ,@(filter positive? xs)))
3036             (sum-positives 1 -2 3 -4 5)",
3037        );
3038        // Filter to (1 3 5) at expansion → emit (+ 1 3 5) → 9.
3039        assert!(matches!(v, Value::Int(9)));
3040    }
3041
3042    #[test]
3043    fn macro_can_recursively_emit_let_chain() {
3044        // (chain-let (a 1) (b 2) (c 3) body) →
3045        //   (let ((a 1)) (let ((b 2)) (let ((c 3)) body))).
3046        let v = run_full(
3047            "(defmacro chain-let (binding &rest more)
3048               (if (null? more)
3049                   `(let (,binding) #t)
3050                   `(let (,binding) (chain-let ,@more))))
3051             (chain-let (a 1) (b 2) (c 3))",
3052        );
3053        assert!(matches!(v, Value::Bool(true)));
3054    }
3055
3056    #[test]
3057    fn macro_can_use_gensym_for_hygiene() {
3058        // The macro introduces a fresh local binding via gensym, so
3059        // no name collision risk.
3060        let v = run_full(
3061            "(defmacro swap-bind (init body)
3062               (let ((tmp (gensym \"tmp\")))
3063                 `(let ((,tmp ,init))
3064                    (+ ,tmp ,tmp))))
3065             (swap-bind 21 #t)",
3066        );
3067        assert!(matches!(v, Value::Int(42)));
3068    }
3069
3070    #[test]
3071    fn macro_can_inspect_arg_shape() {
3072        // Detect whether the arg is a list and emit different code.
3073        let v = run_full(
3074            "(defmacro shape-aware (x)
3075               (if (list? x)
3076                   `(+ ,@x)         ;; sum the children
3077                   `,x))            ;; pass through scalars
3078             (+ (shape-aware (1 2 3)) (shape-aware 100))",
3079        );
3080        // (1 2 3) → 6; 100 → 100; total → 106.
3081        assert!(matches!(v, Value::Int(106)));
3082    }
3083
3084    #[test]
3085    fn macro_can_call_user_helper_fn() {
3086        // Define a helper at top level; macro body calls it at expand.
3087        let v = run_full(
3088            "(define (square x) (* x x))
3089             (defmacro static-square (n) (square n))
3090             (static-square 7)",
3091        );
3092        assert!(matches!(v, Value::Int(49)));
3093    }
3094
3095    #[test]
3096    fn macro_emitting_quoted_form_round_trips() {
3097        // A macro that produces a quoted constant — the (quote x)
3098        // representation must round-trip cleanly.
3099        let v = run_full(
3100            "(defmacro literal-list (&rest xs)
3101               `(quote ,xs))
3102             (literal-list a b c)",
3103        );
3104        let s = format!("{v}");
3105        assert!(s.contains('a') && s.contains('b') && s.contains('c'));
3106    }
3107
3108    #[test]
3109    fn quasiquote_inside_quasiquote_in_macro_output_is_preserved() {
3110        // A macro that emits a quasiquote at runtime — the runtime
3111        // should see a quasiquote and evaluate it.
3112        let v = run_full(
3113            "(defmacro emit-qq (x) `(quasiquote (a (unquote ,x) c)))
3114             (let ((q (emit-qq 99))) q)",
3115        );
3116        // Result is the runtime-value (a 99 c).
3117        assert_eq!(format!("{v}"), "(a 99 c)");
3118    }
3119
3120    #[test]
3121    fn macro_body_can_define_locals_and_dispatch() {
3122        // Macro body uses let + cond + map — full programmability.
3123        let v = run_full(
3124            "(defmacro classify-args (&rest xs)
3125               (let ((evens (filter even? xs))
3126                     (odds  (filter odd?  xs)))
3127                 `(list (list :evens ,@evens)
3128                        (list :odds  ,@odds))))
3129             (classify-args 1 2 3 4 5 6)",
3130        );
3131        let s = format!("{v}");
3132        assert!(s.contains(":evens 2 4 6"));
3133        assert!(s.contains(":odds 1 3 5"));
3134    }
3135
3136    // ── Tail-call optimization tests ──────────────────────────────
3137    //
3138    // These prove the trampoline catches the standard tail positions:
3139    // direct self-recursion through `if`, mutual recursion, deep
3140    // recursion through `cond`, `let`-body, and `begin`. Without TCO,
3141    // each would stack-overflow at ~10k frames; with TCO they run in
3142    // bounded space.
3143
3144    #[test]
3145    fn tco_self_recursion_via_if() {
3146        // Sum integers 1..n via accumulator. Tail call inside `if` else
3147        // branch. n=100_000 would overflow the default Rust stack
3148        // without TCO.
3149        let v = run_full(
3150            "(define (sum n acc)
3151               (if (= n 0)
3152                   acc
3153                   (sum (- n 1) (+ acc n))))
3154             (sum 100000 0)",
3155        );
3156        // n*(n+1)/2 = 5_000_050_000
3157        assert!(matches!(v, Value::Int(5_000_050_000)));
3158    }
3159
3160    #[test]
3161    fn tco_mutual_recursion() {
3162        // Two closures call each other in tail position. Trampoline
3163        // must support the closure swap.
3164        let v = run_full(
3165            "(define (even-r? n) (if (= n 0) #t (odd-r? (- n 1))))
3166             (define (odd-r?  n) (if (= n 0) #f (even-r? (- n 1))))
3167             (even-r? 50000)",
3168        );
3169        assert!(matches!(v, Value::Bool(true)));
3170    }
3171
3172    #[test]
3173    fn tco_via_cond_branch() {
3174        let v = run_full(
3175            "(define (countdown n)
3176               (cond
3177                 ((<= n 0) :done)
3178                 (else (countdown (- n 1)))))
3179             (countdown 50000)",
3180        );
3181        assert!(matches!(v, Value::Keyword(s) if &*s == "done"));
3182    }
3183
3184    #[test]
3185    fn tco_via_let_body() {
3186        // Tail call inside the BODY of a `let`. Trampoline must respect
3187        // that the let frame is on env when entering the call.
3188        let v = run_full(
3189            "(define (loop-let n)
3190               (let ((m (- n 1)))
3191                 (if (<= n 0) :done (loop-let m))))
3192             (loop-let 50000)",
3193        );
3194        assert!(matches!(v, Value::Keyword(s) if &*s == "done"));
3195    }
3196
3197    #[test]
3198    fn tco_via_begin_last_form() {
3199        let v = run_full(
3200            "(define (counter n)
3201               (begin
3202                 (+ 1 1)
3203                 (+ 2 2)
3204                 (if (<= n 0) :done (counter (- n 1)))))
3205             (counter 50000)",
3206        );
3207        assert!(matches!(v, Value::Keyword(s) if &*s == "done"));
3208    }
3209
3210    #[test]
3211    fn tco_via_when_unless() {
3212        let v = run_full(
3213            "(define (drain n)
3214               (when (> n 0)
3215                 (drain (- n 1))))
3216             (drain 50000)",
3217        );
3218        // when's else branch returns nil; here recurses inside.
3219        assert!(matches!(v, Value::Nil));
3220    }
3221
3222    #[test]
3223    fn tco_through_and_or_short_circuit_last() {
3224        // `and` returns the last value if all are truthy. The last form
3225        // is in tail position.
3226        let v = run_full(
3227            "(define (loop-and n)
3228               (and #t #t (if (<= n 0) :done (loop-and (- n 1)))))
3229             (loop-and 30000)",
3230        );
3231        assert!(matches!(v, Value::Keyword(s) if &*s == "done"));
3232    }
3233
3234    #[test]
3235    fn non_tail_recursion_still_works_for_small_n() {
3236        // Non-tail recursion: (* n (fact (- n 1))) — the multiply
3237        // happens AFTER the recursive call returns, so it's not a tail
3238        // call. Should still work for moderate n via the regular stack.
3239        let v = run_full(
3240            "(define (fact n)
3241               (if (= n 0) 1 (* n (fact (- n 1)))))
3242             (fact 12)",
3243        );
3244        // 12! = 479_001_600
3245        assert!(matches!(v, Value::Int(479_001_600)));
3246    }
3247
3248    // ── Structured errors / try / catch ────────────────────────────
3249
3250    #[test]
3251    fn error_constructor_returns_error_value() {
3252        let v = run_full("(error :validation \"bad input\")");
3253        match v {
3254            Value::Error(e) => {
3255                assert_eq!(&*e.tag, "validation");
3256                assert_eq!(&*e.message, "bad input");
3257                assert!(e.data.is_empty());
3258            }
3259            other => panic!("{other:?}"),
3260        }
3261    }
3262
3263    #[test]
3264    fn ex_info_uses_default_tag() {
3265        let v = run_full("(ex-info \"validation failed\" (list :field \"email\" :code 42))");
3266        match v {
3267            Value::Error(e) => {
3268                assert_eq!(&*e.tag, "ex-info");
3269                assert_eq!(&*e.message, "validation failed");
3270                assert_eq!(e.data.len(), 2);
3271            }
3272            other => panic!("{other:?}"),
3273        }
3274    }
3275
3276    #[test]
3277    fn error_predicate() {
3278        let v = run_full("(error? (error :x \"y\"))");
3279        assert!(matches!(v, Value::Bool(true)));
3280        let v = run_full("(error? 42)");
3281        assert!(matches!(v, Value::Bool(false)));
3282    }
3283
3284    #[test]
3285    fn error_accessors() {
3286        let v = run_full(
3287            "(let ((e (ex-info \"oops\" (list :user-id 42))))
3288               (list (error-tag e) (error-message e) (error-data-get e :user-id)))",
3289        );
3290        assert_eq!(format!("{v}"), "(:ex-info \"oops\" 42)");
3291    }
3292
3293    #[test]
3294    fn try_catches_thrown_error() {
3295        let v = run_full(
3296            "(try
3297               (throw (ex-info \"boom\" (list :code 500)))
3298               (catch (e)
3299                 (error-message e)))",
3300        );
3301        assert_eq!(format!("{v}"), "\"boom\"");
3302    }
3303
3304    #[test]
3305    fn try_returns_body_value_when_no_throw() {
3306        let v = run_full(
3307            "(try
3308               (+ 1 2 3)
3309               (catch (e) :unreachable))",
3310        );
3311        assert!(matches!(v, Value::Int(6)));
3312    }
3313
3314    #[test]
3315    fn try_catches_runtime_errors_too() {
3316        // Division by zero is a Rust-side EvalError, not a user throw.
3317        // The catch handler should still observe it (wrapped to
3318        // Value::Error with tag :division-by-zero).
3319        let v = run_full(
3320            "(try
3321               (/ 1 0)
3322               (catch (e) (error-tag e)))",
3323        );
3324        assert!(matches!(v, Value::Keyword(s) if &*s == "division-by-zero"));
3325    }
3326
3327    #[test]
3328    fn try_catches_unbound_symbol_error() {
3329        let v = run_full(
3330            "(try
3331               undefined-var
3332               (catch (e) (error-tag e)))",
3333        );
3334        assert!(matches!(v, Value::Keyword(s) if &*s == "unbound-symbol"));
3335    }
3336
3337    #[test]
3338    fn try_catches_arity_mismatch() {
3339        let v = run_full(
3340            "(try
3341               ((lambda (x y) (+ x y)) 1)
3342               (catch (e) (error-tag e)))",
3343        );
3344        assert!(matches!(v, Value::Keyword(s) if &*s == "arity-mismatch"));
3345    }
3346
3347    #[test]
3348    fn nested_try_inner_handler_takes_precedence() {
3349        let v = run_full(
3350            "(try
3351               (try
3352                 (throw (ex-info \"inner\" ()))
3353                 (catch (e) :inner-caught))
3354               (catch (e) :outer-caught))",
3355        );
3356        assert!(matches!(v, Value::Keyword(s) if &*s == "inner-caught"));
3357    }
3358
3359    #[test]
3360    fn outer_try_catches_when_handler_rethrows() {
3361        let v = run_full(
3362            "(try
3363               (try
3364                 (throw (ex-info \"first\" ()))
3365                 (catch (e) (throw (ex-info \"rethrown\" ()))))
3366               (catch (e) (error-message e)))",
3367        );
3368        assert_eq!(format!("{v}"), "\"rethrown\"");
3369    }
3370
3371    #[test]
3372    fn throw_propagates_when_no_try() {
3373        // Without try, throw bubbles up as EvalError::User.
3374        let mut i: Interpreter<NoHost> = Interpreter::new();
3375        install_full_stdlib_with(&mut i, &mut NoHost);
3376        let forms = read_spanned("(throw (ex-info \"unhandled\" (list :code 99)))").unwrap();
3377        let err = i.eval_program(&forms, &mut NoHost).unwrap_err();
3378        match err {
3379            EvalError::User { value, .. } => match value {
3380                Value::Error(e) => {
3381                    assert_eq!(&*e.message, "unhandled");
3382                }
3383                other => panic!("{other:?}"),
3384            },
3385            other => panic!("{other:?}"),
3386        }
3387    }
3388
3389    // ── macroexpand-1 / macroexpand introspection ─────────────────
3390
3391    #[test]
3392    fn macroexpand_one_step() {
3393        let v = run_full(
3394            "(defmacro twice (x) `(* ,x 2))
3395             (macroexpand-1 '(twice 7))",
3396        );
3397        // Single step: (twice 7) → (* 7 2)
3398        assert_eq!(format!("{v}"), "(* 7 2)");
3399    }
3400
3401    #[test]
3402    fn macroexpand_full_until_fixed_point() {
3403        let v = run_full(
3404            "(defmacro twice (x) `(* ,x 2))
3405             (defmacro quad (x) `(twice (twice ,x)))
3406             (macroexpand '(quad 5))",
3407        );
3408        // (quad 5) → (twice (twice 5)) → (twice (* 5 2)) → (* (* 5 2) 2)
3409        assert_eq!(format!("{v}"), "(* (* 5 2) 2)");
3410    }
3411
3412    #[test]
3413    fn macroexpand_returns_unchanged_for_non_macro() {
3414        let v = run_full("(macroexpand-1 '(+ 1 2 3))");
3415        // + isn't a macro — passes through.
3416        assert_eq!(format!("{v}"), "(+ 1 2 3)");
3417    }
3418
3419    #[test]
3420    fn macroexpand_one_does_not_recurse_into_children() {
3421        // Only the head is expanded one level. Inner macro calls remain.
3422        let v = run_full(
3423            "(defmacro twice (x) `(* ,x 2))
3424             (defmacro outer (x) `(list ,x))
3425             (macroexpand-1 '(outer (twice 3)))",
3426        );
3427        // (outer (twice 3)) → (list (twice 3))   — inner macro NOT expanded.
3428        assert_eq!(format!("{v}"), "(list (twice 3))");
3429    }
3430
3431    #[test]
3432    fn macroexpand_recurses_into_children() {
3433        let v = run_full(
3434            "(defmacro twice (x) `(* ,x 2))
3435             (defmacro outer (x) `(list ,x))
3436             (macroexpand '(outer (twice 3)))",
3437        );
3438        // Full expansion expands inner: (list (* 3 2))
3439        assert_eq!(format!("{v}"), "(list (* 3 2))");
3440    }
3441
3442    // ── Module system: provide / require / qualified names ────────
3443
3444    fn run_with_modules(modules: &[(&str, &str)], src: &str) -> Value {
3445        use crate::module::MapLoader;
3446        let mut i: Interpreter<NoHost> = Interpreter::new();
3447        install_full_stdlib_with(&mut i, &mut NoHost);
3448        let mut loader = MapLoader::new();
3449        for (path, source) in modules {
3450            loader.insert(*path, *source);
3451        }
3452        i.set_loader(Arc::new(loader));
3453        let forms = read_spanned(src).unwrap();
3454        i.eval_program(&forms, &mut NoHost).unwrap()
3455    }
3456
3457    fn run_with_modules_err(modules: &[(&str, &str)], src: &str) -> EvalError {
3458        use crate::module::MapLoader;
3459        let mut i: Interpreter<NoHost> = Interpreter::new();
3460        install_full_stdlib_with(&mut i, &mut NoHost);
3461        let mut loader = MapLoader::new();
3462        for (path, source) in modules {
3463            loader.insert(*path, *source);
3464        }
3465        i.set_loader(Arc::new(loader));
3466        let forms = read_spanned(src).unwrap();
3467        i.eval_program(&forms, &mut NoHost).unwrap_err()
3468    }
3469
3470    #[test]
3471    fn require_with_explicit_alias_imports_qualified_names() {
3472        let v = run_with_modules(
3473            &[(
3474                "lib/math",
3475                "(define square (lambda (x) (* x x)))
3476                 (define cube (lambda (x) (* x x x)))
3477                 (provide square cube)",
3478            )],
3479            "(require \"lib/math\" :as math)
3480             (math/square 7)",
3481        );
3482        assert!(matches!(v, Value::Int(49)));
3483    }
3484
3485    #[test]
3486    fn require_uses_path_as_default_alias() {
3487        let v = run_with_modules(
3488            &[("lib/math", "(define double (lambda (x) (* x 2))) (provide double)")],
3489            "(require \"lib/math\")
3490             (lib/math/double 21)",
3491        );
3492        // No explicit :as alias → bound under the path itself, so
3493        // `lib/math/double` is the qualified name.
3494        assert!(matches!(v, Value::Int(42)));
3495    }
3496
3497    #[test]
3498    fn require_refer_imports_unqualified_names() {
3499        let v = run_with_modules(
3500            &[(
3501                "lib/math",
3502                "(define square (lambda (x) (* x x)))
3503                 (define cube (lambda (x) (* x x x)))
3504                 (provide square cube)",
3505            )],
3506            "(require \"lib/math\" :refer (square))
3507             (square 6)",
3508        );
3509        assert!(matches!(v, Value::Int(36)));
3510    }
3511
3512    #[test]
3513    fn require_does_not_import_non_provided() {
3514        // `private` is defined but NOT provided — should not be
3515        // accessible from the importing module.
3516        let err = run_with_modules_err(
3517            &[(
3518                "lib/secret",
3519                "(define public 1)
3520                 (define private 2)
3521                 (provide public)",
3522            )],
3523            "(require \"lib/secret\" :as s)
3524             s/private",
3525        );
3526        match err {
3527            EvalError::UnboundSymbol { name, .. } => assert_eq!(&*name, "s/private"),
3528            other => panic!("{other:?}"),
3529        }
3530    }
3531
3532    #[test]
3533    fn require_chain_a_imports_b() {
3534        let v = run_with_modules(
3535            &[
3536                (
3537                    "lib/util",
3538                    "(define inc1 (lambda (n) (+ n 1)))
3539                     (provide inc1)",
3540                ),
3541                (
3542                    "lib/wrapper",
3543                    "(require \"lib/util\" :as u)
3544                     (define inc2 (lambda (n) (u/inc1 (u/inc1 n))))
3545                     (provide inc2)",
3546                ),
3547            ],
3548            "(require \"lib/wrapper\" :as w)
3549             (w/inc2 10)",
3550        );
3551        assert!(matches!(v, Value::Int(12)));
3552    }
3553
3554    #[test]
3555    fn require_module_not_found() {
3556        let err = run_with_modules_err(&[], "(require \"missing/module\")");
3557        // Surfaces as a Value::Error inside EvalError::User.
3558        match err {
3559            EvalError::User { value, .. } => match value {
3560                Value::Error(e) => {
3561                    assert_eq!(&*e.tag, "module-not-found");
3562                    assert!(e.message.contains("missing/module"));
3563                }
3564                other => panic!("{other:?}"),
3565            },
3566            other => panic!("{other:?}"),
3567        }
3568    }
3569
3570    #[test]
3571    fn circular_require_detected() {
3572        let err = run_with_modules_err(
3573            &[
3574                ("a", "(require \"b\") (provide x) (define x 1)"),
3575                ("b", "(require \"a\") (provide y) (define y 2)"),
3576            ],
3577            "(require \"a\")",
3578        );
3579        match err {
3580            EvalError::User { value, .. } => match value {
3581                Value::Error(e) => assert_eq!(&*e.tag, "circular-require"),
3582                other => panic!("{other:?}"),
3583            },
3584            other => panic!("{other:?}"),
3585        }
3586    }
3587
3588    #[test]
3589    fn provide_at_top_level_errors() {
3590        // Without being inside a require, (provide ...) is meaningless.
3591        let mut i: Interpreter<NoHost> = Interpreter::new();
3592        install_full_stdlib_with(&mut i, &mut NoHost);
3593        let forms = read_spanned("(provide x)").unwrap();
3594        let err = i.eval_program(&forms, &mut NoHost).unwrap_err();
3595        assert!(matches!(err, EvalError::BadSpecialForm { form, .. } if &*form == "provide"));
3596    }
3597
3598    #[test]
3599    fn require_refer_unknown_name_errors() {
3600        let err = run_with_modules_err(
3601            &[(
3602                "lib/math",
3603                "(define square (lambda (x) (* x x))) (provide square)",
3604            )],
3605            "(require \"lib/math\" :refer (square cube))",
3606        );
3607        match err {
3608            EvalError::User { value, .. } => match value {
3609                Value::Error(e) => {
3610                    assert!(matches!(&*e.tag, "not-defined" | "not-exported"));
3611                }
3612                other => panic!("{other:?}"),
3613            },
3614            other => panic!("{other:?}"),
3615        }
3616    }
3617
3618    #[test]
3619    fn require_caches_module_load_once() {
3620        let v = run_with_modules(
3621            &[(
3622                "lib/foo",
3623                "(define x 42) (provide x)",
3624            )],
3625            "(require \"lib/foo\" :as a)
3626             (require \"lib/foo\" :as b)
3627             (+ a/x b/x)",
3628        );
3629        // Both alias to the same cached module.
3630        assert!(matches!(v, Value::Int(84)));
3631    }
3632
3633    // ---- macro-phase seal ------------------------------------------
3634    //
3635    // Measured before this landed: `(define *g* 0)` `(defmacro leak ()
3636    // (set! *g* 99))` `(leak)` `*g*` returned Int(99) — a macro body wrote
3637    // the interpreter's globals, and the runtime program could read it.
3638    // The mechanism was that `Env::set` takes `&self` and mutates through
3639    // `Arc`-shared frames, so cloning the env shared them.
3640
3641    #[test]
3642    fn macro_body_cannot_set_a_global() {
3643        let mut interp = Interpreter::new();
3644        install_primitives(&mut interp);
3645        let src = "(define *g* 0) (defmacro leak () (set! *g* 99)) (leak)";
3646        let forms = tatara_lisp::read_spanned(src).expect("parse");
3647        let err = interp
3648            .eval_program(&forms, &mut ())
3649            .expect_err("a macro must not be able to set! a global");
3650        let msg = format!("{err}");
3651        assert!(
3652            msg.contains("sealed") || msg.contains("cannot `set!`"),
3653            "expected a sealed-write diagnostic, got: {msg}"
3654        );
3655    }
3656
3657    #[test]
3658    fn the_global_is_actually_unchanged_after_a_refused_macro_set() {
3659        let mut interp = Interpreter::new();
3660        install_primitives(&mut interp);
3661        let forms = tatara_lisp::read_spanned(
3662            "(define *g* 0) (defmacro leak () (set! *g* 99))",
3663        )
3664        .expect("parse");
3665        interp.eval_program(&forms, &mut ()).expect("setup");
3666        // The expansion fails; the global must still read 0.
3667        let call = tatara_lisp::read_spanned("(leak)").expect("parse");
3668        let _ = interp.eval_program(&call, &mut ());
3669        let read = tatara_lisp::read_spanned("*g*").expect("parse");
3670        let v = interp.eval_program(&read, &mut ()).expect("read *g*");
3671        assert!(
3672            matches!(v, Value::Int(0)),
3673            "global was mutated by a macro body despite the seal: {v:?}"
3674        );
3675    }
3676
3677    /// Anti-vacuity: the seal must not break ORDINARY `set!`. If it did,
3678    /// the tests above would pass for the wrong reason.
3679    #[test]
3680    fn ordinary_set_still_works_at_runtime() {
3681        let mut interp = Interpreter::new();
3682        install_primitives(&mut interp);
3683        let forms =
3684            tatara_lisp::read_spanned("(define x 1) (set! x 42) x").expect("parse");
3685        let v = interp.eval_program(&forms, &mut ()).expect("runtime set!");
3686        assert!(matches!(v, Value::Int(42)), "got {v:?}");
3687    }
3688
3689    /// A macro body may still `define` and `set!` its OWN locals — the
3690    /// seal only blocks reaching outward.
3691    #[test]
3692    fn macro_body_can_mutate_its_own_locals() {
3693        let mut interp = Interpreter::new();
3694        install_primitives(&mut interp);
3695        let src = "(defmacro m () (begin (define n 1) (set! n 2) n)) (m)";
3696        let forms = tatara_lisp::read_spanned(src).expect("parse");
3697        let v = interp
3698            .eval_program(&forms, &mut ())
3699            .expect("a macro must be able to mutate its own locals");
3700        assert!(matches!(v, Value::Int(2)), "got {v:?}");
3701    }
3702
3703}