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