Skip to main content

cljrs_interp/
eval.rs

1//! Top-level `eval` dispatcher and form-to-value conversion.
2
3use std::sync::Arc;
4
5use crate::apply::eval_call;
6use crate::special::eval_special;
7use crate::syntax_quote::syntax_quote;
8use cljrs_builtins::form::expand_reader_conds;
9use cljrs_builtins::special::SPECIAL_FORMS;
10use cljrs_env::env::Env;
11use cljrs_env::error::{EvalError, EvalResult};
12use cljrs_gc::GcPtr;
13use cljrs_reader::Form;
14use cljrs_reader::form::FormKind;
15use cljrs_value::value::SetValue;
16use cljrs_value::{
17    FutureState, Keyword, MapValue, PersistentHashSet, PersistentList, PersistentVector, Symbol,
18    Value,
19};
20use regex::Regex;
21
22/// Evaluate a `Form` in the given `Env`.
23pub fn eval(form: &Form, env: &mut Env) -> EvalResult {
24    match &form.kind {
25        // ── Atoms ─────────────────────────────────────────────────────────
26        FormKind::Nil => Ok(Value::Nil),
27        FormKind::Bool(b) => Ok(Value::Bool(*b)),
28        FormKind::Int(n) => Ok(Value::Long(*n)),
29        FormKind::Float(f) => Ok(Value::Double(*f)),
30        FormKind::Symbolic(f) => Ok(Value::Double(*f)), // ##Inf etc.
31        FormKind::Str(s) => Ok(Value::string(s.clone())),
32        FormKind::Char(c) => Ok(Value::Char(*c)),
33        FormKind::BigInt(s) => cljrs_builtins::parse_bigint(s),
34        FormKind::BigDecimal(s) => cljrs_builtins::parse_bigdecimal(s),
35        FormKind::Ratio(s) => cljrs_builtins::parse_ratio(s),
36        FormKind::Regex(s) => {
37            let r = Regex::new(s);
38            match r {
39                Ok(r) => Ok(Value::Pattern(GcPtr::new(r))),
40                Err(e) => Err(EvalError::Runtime(e.to_string())),
41            }
42        }
43
44        // ── Identifiers ───────────────────────────────────────────────────
45        FormKind::Symbol(s) => eval_symbol(s, env),
46        FormKind::Keyword(s) => Ok(Value::keyword(Keyword::parse(s))),
47        FormKind::AutoKeyword(s) => {
48            let full = format!("{}/{}", env.current_ns, s);
49            Ok(Value::keyword(Keyword::parse(&full)))
50        }
51
52        // ── Collections ───────────────────────────────────────────────────
53        FormKind::List(forms) => eval_list(forms, env),
54        FormKind::Vector(forms) => {
55            let mut vals: Vec<Value> = Vec::with_capacity(forms.len());
56            for f in forms {
57                let _root = cljrs_env::gc_roots::root_values(&vals);
58                vals.push(eval(f, env)?);
59            }
60            Ok(Value::Vector(GcPtr::new(PersistentVector::from_iter(vals))))
61        }
62        FormKind::Map(forms) => {
63            if forms.len() % 2 != 0 {
64                return Err(EvalError::Runtime(
65                    "map literal must have an even number of forms".into(),
66                ));
67            }
68            let mut pairs: Vec<Value> = Vec::with_capacity(forms.len());
69            for f in forms {
70                let _root = cljrs_env::gc_roots::root_values(&pairs);
71                pairs.push(eval(f, env)?);
72            }
73            let kv_pairs: Vec<(Value, Value)> = pairs
74                .chunks(2)
75                .map(|pair| (pair[0].clone(), pair[1].clone()))
76                .collect();
77            Ok(Value::Map(MapValue::from_pairs(kv_pairs)))
78        }
79        FormKind::Set(forms) => {
80            let mut vals: Vec<Value> = Vec::with_capacity(forms.len());
81            for f in forms {
82                let _root = cljrs_env::gc_roots::root_values(&vals);
83                vals.push(eval(f, env)?);
84            }
85            Ok(Value::Set(SetValue::Hash(GcPtr::new(
86                PersistentHashSet::from_iter(vals),
87            ))))
88        }
89
90        // ── Reader macros ─────────────────────────────────────────────────
91        FormKind::Quote(inner) => Ok(cljrs_builtins::form::form_to_value(inner)),
92        FormKind::SyntaxQuote(inner) => syntax_quote(inner, env),
93        FormKind::Unquote(_) => Err(EvalError::Runtime("unquote outside syntax-quote".into())),
94        FormKind::UnquoteSplice(_) => Err(EvalError::Runtime(
95            "unquote-splice outside syntax-quote".into(),
96        )),
97        FormKind::Deref(inner) => {
98            let v = eval(inner, env)?;
99            deref_value(v)
100        }
101        FormKind::Var(inner) => {
102            if let FormKind::Symbol(s) = &inner.kind {
103                let parsed = Symbol::parse(s);
104                let ns = parsed.namespace.as_deref().unwrap_or(&env.current_ns);
105                env.globals
106                    .lookup_var_in_ns(ns, &parsed.name)
107                    .map(Value::Var)
108                    .ok_or_else(|| EvalError::UnboundSymbol(s.clone()))
109            } else {
110                Err(EvalError::Runtime("var requires a symbol".into()))
111            }
112        }
113        FormKind::Meta(_, form) => {
114            // Ignore metadata in Phase 4; just eval the annotated form.
115            eval(form, env)
116        }
117
118        // ── Dispatch ──────────────────────────────────────────────────────
119        FormKind::AnonFn(body) => {
120            let expanded = cljrs_builtins::form::expand_anon_fn(body, form.span.clone());
121            eval(&expanded, env)
122        }
123        FormKind::ReaderCond {
124            splicing: _,
125            clauses,
126        } => eval_reader_cond(clauses, env),
127        FormKind::TaggedLiteral(tag, inner) => eval_tagged_literal(tag, inner, env),
128    }
129}
130
131// ── List / call dispatch ──────────────────────────────────────────────────────
132
133fn eval_list(forms: &[Form], env: &mut Env) -> EvalResult {
134    if forms.is_empty() {
135        return Ok(Value::List(GcPtr::new(PersistentList::empty())));
136    }
137
138    // Expand reader conditionals (both splicing and non-splicing) before dispatch.
139    let expanded: Vec<Form>;
140    let forms: &[Form] = if forms
141        .iter()
142        .any(|f| matches!(f.kind, FormKind::ReaderCond { .. }))
143    {
144        expanded = expand_reader_conds(forms);
145        if expanded.is_empty() {
146            return Ok(Value::List(GcPtr::new(PersistentList::empty())));
147        }
148        &expanded
149    } else {
150        forms
151    };
152
153    // Check for special form.
154    if let FormKind::Symbol(s) = &forms[0].kind
155        && is_special_form(s)
156    {
157        return eval_special(s, &forms[1..], env);
158    }
159
160    eval_call(&forms[0], &forms[1..], env)
161}
162
163// ── Symbol resolution ─────────────────────────────────────────────────────────
164
165fn eval_symbol(s: &str, env: &mut Env) -> EvalResult {
166    // Local frames (including closed-over).
167    if let Some(v) = env.lookup(s) {
168        return Ok(v);
169    }
170
171    // Namespace-qualified: `ns/name`
172    if s.contains('/') && !s.starts_with('/') {
173        let sym = Symbol::parse(s);
174        if let Some(ns_part) = &sym.namespace {
175            // Resolve alias first, fall back to literal namespace name.
176            let resolved: Arc<str> = env
177                .globals
178                .resolve_alias(&env.current_ns, ns_part)
179                .unwrap_or_else(|| Arc::from(ns_part.as_ref()));
180            return env
181                .globals
182                .lookup_in_ns(&resolved, &sym.name)
183                .ok_or_else(|| EvalError::UnboundSymbol(s.to_string()));
184        }
185    }
186
187    // JVM class names resolve to themselves as symbols (for instance?, catch, etc.)
188    if is_jvm_class_name(s) {
189        return Ok(Value::symbol(Symbol::simple(s)));
190    }
191
192    Err(EvalError::UnboundSymbol(s.to_string()))
193}
194
195/// Recognise JVM-style class names used in Clojure for `instance?`, `catch`, etc.
196pub fn is_jvm_class_name(s: &str) -> bool {
197    matches!(
198        s,
199        "clojure.lang.BigInt"
200            | "java.math.BigDecimal"
201            | "java.math.BigInteger"
202            | "clojure.lang.Ratio"
203            | "java.lang.Long"
204            | "java.lang.Double"
205            | "java.lang.String"
206            | "java.lang.Boolean"
207            | "java.lang.Character"
208            | "java.lang.Number"
209            | "clojure.lang.Symbol"
210            | "clojure.lang.Keyword"
211            | "clojure.lang.PersistentList"
212            | "clojure.lang.PersistentVector"
213            | "clojure.lang.PersistentHashMap"
214            | "clojure.lang.PersistentHashSet"
215            | "clojure.lang.PersistentArrayMap"
216            | "clojure.lang.IFn"
217            | "clojure.lang.ISeq"
218            | "clojure.lang.IPending"
219            | "clojure.lang.Atom"
220            | "clojure.lang.Var"
221            | "clojure.lang.Namespace"
222            | "java.util.UUID"
223            | "java.lang.Exception"
224            | "java.lang.Throwable"
225            | "java.lang.Error"
226            | "Exception"
227            | "Throwable"
228            | "Error"
229            | "clojure.lang.ExceptionInfo"
230            | "clojure.lang.IEditableCollection"
231            | "Boolean"
232            | "clojure.lang.PersistentQueue"
233            | "java.util.regex.Pattern"
234    )
235}
236
237// ── is_special_form ───────────────────────────────────────────────────────────
238
239pub fn is_special_form(s: &str) -> bool {
240    SPECIAL_FORMS.contains(&s)
241}
242
243// ── eval_body ─────────────────────────────────────────────────────────────────
244
245/// Dereference a value: used by `@x` reader macro and the `deref` builtin.
246pub fn deref_value(v: Value) -> EvalResult {
247    match v {
248        Value::Atom(a) => Ok(a.get().deref()),
249        Value::Var(var) => cljrs_env::dynamics::deref_var(&var)
250            .ok_or_else(|| EvalError::Runtime("unbound var".into())),
251        Value::Volatile(vol) => Ok(vol.get().deref()),
252        Value::Delay(d) => d.get().force().map_err(EvalError::Runtime),
253        Value::Agent(a) => Ok(a.get().get_state()),
254        Value::Reduced(inner) => Ok(*inner),
255        Value::Promise(p) => Ok(p.get().deref_blocking()),
256        Value::Future(f) => {
257            let mut guard = f.get().state.lock().unwrap();
258            loop {
259                match &*guard {
260                    FutureState::Done(v) => return Ok(v.clone()),
261                    FutureState::Failed(e) => return Err(EvalError::Runtime(e.clone())),
262                    FutureState::Cancelled => {
263                        return Err(EvalError::Runtime("future was cancelled".into()));
264                    }
265                    FutureState::Running => {
266                        guard = f.get().cond.wait(guard).unwrap();
267                    }
268                }
269            }
270        }
271        other => Err(EvalError::Runtime(format!(
272            "cannot deref {}",
273            other.type_name()
274        ))),
275    }
276}
277
278/// Evaluate a sequence of forms and return the value of the last one.
279pub fn eval_body(forms: &[Form], env: &mut Env) -> EvalResult {
280    let mut result = Value::Nil;
281    for form in forms {
282        result = eval(form, env)?;
283    }
284    Ok(result)
285}
286
287// ── reader cond ───────────────────────────────────────────────────────────────
288
289fn eval_reader_cond(clauses: &[Form], env: &mut Env) -> EvalResult {
290    // clauses = [kw form kw form ...]
291    let mut i = 0;
292    let mut default: Option<&Form> = None;
293    while i + 1 < clauses.len() {
294        match &clauses[i].kind {
295            FormKind::Keyword(k) if k == "rust" => {
296                return eval(&clauses[i + 1], env);
297            }
298            FormKind::Keyword(k) if k == "default" => {
299                default = Some(&clauses[i + 1]);
300            }
301            _ => {}
302        }
303        i += 2;
304    }
305    match default {
306        Some(f) => eval(f, env),
307        None => Ok(Value::Nil),
308    }
309}
310
311// ── tagged literals ──────────────────────────────────────────────────────────
312
313fn eval_tagged_literal(tag: &str, inner: &Form, env: &mut Env) -> EvalResult {
314    match tag {
315        "uuid" => {
316            let val = eval(inner, env)?;
317            match &val {
318                Value::Str(s) => {
319                    let uuid = uuid::Uuid::parse_str(s.get())
320                        .map_err(|e| EvalError::Runtime(format!("invalid UUID: {e}")))?;
321                    Ok(Value::Uuid(uuid.as_u128()))
322                }
323                _ => Err(EvalError::Runtime(format!(
324                    "#uuid expects a string, got {}",
325                    val.type_name()
326                ))),
327            }
328        }
329        "inst" => {
330            // TODO: implement #inst for date/time literals
331            let val = eval(inner, env)?;
332            Ok(val)
333        }
334        _ => Err(EvalError::Runtime(format!(
335            "unknown tagged literal: #{tag}"
336        ))),
337    }
338}
339
340// ── Tests ─────────────────────────────────────────────────────────────────────
341
342#[cfg(test)]
343mod tests {
344    use super::*;
345    use cljrs_env::env::GlobalEnv;
346    use std::sync::Arc;
347
348    fn make_env() -> (Arc<GlobalEnv>, Env) {
349        let globals = crate::standard_env(None, None, None);
350        let env = Env::new(globals.clone(), "user");
351        (globals, env)
352    }
353
354    fn eval_str(src: &str) -> EvalResult {
355        let (_, mut env) = make_env();
356        eval_src(src, &mut env)
357    }
358
359    fn eval_src(src: &str, env: &mut Env) -> EvalResult {
360        let mut parser = cljrs_reader::Parser::new(src.to_string(), "<test>".to_string());
361        let forms = parser.parse_all().map_err(EvalError::Read)?;
362        let mut result = Value::Nil;
363        for form in forms {
364            result = eval(&form, env)?;
365        }
366        Ok(result)
367    }
368
369    fn long(n: i64) -> Value {
370        Value::Long(n)
371    }
372    fn bool_v(b: bool) -> Value {
373        Value::Bool(b)
374    }
375
376    // ── Atoms ─────────────────────────────────────────────────────────────
377
378    #[test]
379    fn test_literal_int() {
380        assert_eq!(eval_str("42").unwrap(), long(42));
381    }
382
383    #[test]
384    fn test_literal_string() {
385        assert!(matches!(eval_str("\"hello\"").unwrap(), Value::Str(_)));
386    }
387
388    #[test]
389    fn test_literal_nil() {
390        assert_eq!(eval_str("nil").unwrap(), Value::Nil);
391    }
392
393    #[test]
394    fn test_literal_true() {
395        assert_eq!(eval_str("true").unwrap(), bool_v(true));
396    }
397
398    #[test]
399    fn test_literal_false() {
400        assert_eq!(eval_str("false").unwrap(), bool_v(false));
401    }
402
403    // ── Arithmetic ────────────────────────────────────────────────────────
404
405    #[test]
406    fn test_add() {
407        assert_eq!(eval_str("(+ 1 2)").unwrap(), long(3));
408    }
409
410    #[test]
411    fn test_mul() {
412        assert_eq!(eval_str("(* 2 3)").unwrap(), long(6));
413    }
414
415    #[test]
416    fn test_div_exact() {
417        assert_eq!(eval_str("(/ 10 2)").unwrap(), long(5));
418    }
419
420    #[test]
421    fn test_sub() {
422        assert_eq!(eval_str("(- 10 3)").unwrap(), long(7));
423    }
424
425    // ── let ───────────────────────────────────────────────────────────────
426
427    #[test]
428    fn test_let_simple() {
429        assert_eq!(eval_str("(let* [x 1 y 2] (+ x y))").unwrap(), long(3));
430    }
431
432    #[test]
433    fn test_let_shadowing() {
434        assert_eq!(eval_str("(let* [x 1] (let* [x 10] x))").unwrap(), long(10));
435    }
436
437    // ── fn + call ─────────────────────────────────────────────────────────
438
439    #[test]
440    fn test_fn_call() {
441        assert_eq!(eval_str("((fn* [x] (* x x)) 5)").unwrap(), long(25));
442    }
443
444    #[test]
445    fn test_closure_capture() {
446        assert_eq!(
447            eval_str("(let* [n 3] ((fn* [x] (+ x n)) 4))").unwrap(),
448            long(7)
449        );
450    }
451
452    #[test]
453    fn test_multi_arity_fn() {
454        assert_eq!(
455            eval_str("((fn* ([x] x) ([x y] (+ x y))) 1 2)").unwrap(),
456            long(3)
457        );
458    }
459
460    // ── recur / loop ──────────────────────────────────────────────────────
461
462    #[test]
463    fn test_loop_recur() {
464        let result =
465            eval_str("(loop* [i 0 acc 0] (if (= i 5) acc (recur (inc i) (+ acc i))))").unwrap();
466        assert_eq!(result, long(10));
467    }
468
469    // ── def / defn ────────────────────────────────────────────────────────
470
471    #[test]
472    fn test_def() {
473        let (_, mut env) = make_env();
474        eval_src("(def x 42)", &mut env).unwrap();
475        assert_eq!(eval_src("x", &mut env).unwrap(), long(42));
476    }
477
478    #[test]
479    fn test_defn() {
480        let (_, mut env) = make_env();
481        eval_src("(defn square [x] (* x x))", &mut env).unwrap();
482        assert_eq!(eval_src("(square 7)", &mut env).unwrap(), long(49));
483    }
484
485    // ── if ────────────────────────────────────────────────────────────────
486
487    #[test]
488    fn test_if_truthy() {
489        assert_eq!(eval_str("(if true 1 2)").unwrap(), long(1));
490    }
491
492    #[test]
493    fn test_if_falsy() {
494        assert_eq!(eval_str("(if false 1 2)").unwrap(), long(2));
495    }
496
497    #[test]
498    fn test_if_nil_branch() {
499        assert_eq!(eval_str("(if false 1)").unwrap(), Value::Nil);
500    }
501
502    // ── do ────────────────────────────────────────────────────────────────
503
504    #[test]
505    fn test_do() {
506        assert_eq!(eval_str("(do 1 2 3)").unwrap(), long(3));
507    }
508
509    // ── quote ─────────────────────────────────────────────────────────────
510
511    #[test]
512    fn test_quote_list() {
513        let v = eval_str("'(1 2 3)").unwrap();
514        assert!(matches!(v, Value::List(_)));
515    }
516
517    // ── keyword lookup ────────────────────────────────────────────────────
518
519    #[test]
520    fn test_keyword_lookup() {
521        assert_eq!(eval_str("(:a {:a 1})").unwrap(), long(1));
522    }
523
524    #[test]
525    fn test_keyword_lookup_missing() {
526        assert_eq!(eval_str("(:b {:a 1})").unwrap(), Value::Nil);
527    }
528
529    // ── Map / Vector / Set literals ───────────────────────────────────────
530
531    #[test]
532    fn test_map_literal() {
533        let v = eval_str("{:a 1 :b 2}").unwrap();
534        assert!(matches!(v, Value::Map(_)));
535        if let Value::Map(m) = &v {
536            assert_eq!(m.count(), 2);
537        }
538    }
539
540    #[test]
541    fn test_vector_literal() {
542        let v = eval_str("[1 2 3]").unwrap();
543        assert!(matches!(v, Value::Vector(_)));
544    }
545
546    #[test]
547    fn test_set_literal() {
548        let v = eval_str("#{1 2 3}").unwrap();
549        assert!(matches!(v, Value::Set(_)));
550    }
551
552    // ── set! ──────────────────────────────────────────────────────────────
553
554    #[test]
555    fn test_set_bang() {
556        let (_, mut env) = make_env();
557        eval_src("(def x 1)", &mut env).unwrap();
558        eval_src("(set! x 99)", &mut env).unwrap();
559        assert_eq!(eval_src("x", &mut env).unwrap(), long(99));
560    }
561
562    // ── throw / try / catch ───────────────────────────────────────────────
563
564    #[test]
565    fn test_throw_catch() {
566        let v = eval_str("(try (throw (ex-info \"oops\" {})) (catch Exception e (ex-message e)))")
567            .unwrap();
568        assert!(matches!(v, Value::Str(_)));
569    }
570
571    #[test]
572    fn test_try_no_throw() {
573        assert_eq!(eval_str("(try 42)").unwrap(), long(42));
574    }
575
576    // ── destructuring ─────────────────────────────────────────────────────
577
578    #[test]
579    fn test_sequential_destructure() {
580        assert_eq!(eval_str("(let* [[a b] [1 2]] (+ a b))").unwrap(), long(3));
581    }
582
583    #[test]
584    fn test_rest_destructure() {
585        let v = eval_str("(let* [[h & t] [1 2 3]] t)").unwrap();
586        assert!(matches!(v, Value::List(_)));
587        if let Value::List(l) = &v {
588            assert_eq!(l.get().count(), 2);
589        }
590    }
591
592    // ── defmacro ──────────────────────────────────────────────────────────
593
594    #[test]
595    fn test_defmacro() {
596        let (_, mut env) = make_env();
597        eval_src("(defmacro my-if [t a b] (list 'if t a b))", &mut env).unwrap();
598        assert_eq!(eval_src("(my-if true 1 2)", &mut env).unwrap(), long(1));
599        assert_eq!(eval_src("(my-if false 1 2)", &mut env).unwrap(), long(2));
600    }
601
602    // ── syntax-quote ──────────────────────────────────────────────────────
603
604    #[test]
605    fn test_syntax_quote_basic() {
606        let (_, mut env) = make_env();
607        eval_src("(def b 2)", &mut env).unwrap();
608        let v = eval_src("`(a ~b)", &mut env).unwrap();
609        assert!(matches!(v, Value::List(_)));
610        if let Value::List(l) = &v {
611            // Should be (user/a 2)
612            let items: Vec<_> = l.get().iter().cloned().collect();
613            assert_eq!(items.len(), 2);
614            assert_eq!(items[1], long(2));
615        }
616    }
617
618    // ── reader conditionals ───────────────────────────────────────────────
619
620    #[test]
621    fn test_reader_cond_rust() {
622        // :rust branch selected.
623        assert_eq!(eval_str("#?(:rust 1 :clj 2)").unwrap(), long(1));
624    }
625
626    #[test]
627    fn test_reader_cond_default() {
628        // No :rust; fall through to :default.
629        assert_eq!(eval_str("#?(:clj 2 :default 99)").unwrap(), long(99));
630    }
631
632    // ── Error cases ───────────────────────────────────────────────────────
633
634    #[test]
635    fn test_unbound_symbol() {
636        let r = eval_str("undefined-var-xyz");
637        assert!(matches!(r, Err(EvalError::UnboundSymbol(_))));
638    }
639
640    #[test]
641    fn test_wrong_arity() {
642        let (_, mut env) = make_env();
643        eval_src("(defn one-arg [x] x)", &mut env).unwrap();
644        let r = eval_src("(one-arg 1 2)", &mut env);
645        assert!(matches!(r, Err(EvalError::Arity { .. })));
646    }
647
648    #[test]
649    fn test_not_callable() {
650        let r = eval_str("(42 1 2)");
651        assert!(matches!(r, Err(EvalError::NotCallable(_))));
652    }
653
654    // ── Higher-order functions (bootstrap) ────────────────────────────────
655
656    #[test]
657    fn test_map_fn() {
658        assert_eq!(
659            eval_str("(vec (map inc [1 2 3]))").unwrap(),
660            eval_str("[2 3 4]").unwrap()
661        );
662    }
663
664    #[test]
665    fn test_filter_fn() {
666        assert_eq!(
667            eval_str("(vec (filter odd? [1 2 3 4 5]))").unwrap(),
668            eval_str("[1 3 5]").unwrap()
669        );
670    }
671
672    #[test]
673    fn test_reduce_fn() {
674        assert_eq!(eval_str("(reduce + [1 2 3 4 5])").unwrap(), long(15));
675    }
676
677    #[test]
678    fn test_apply_fn() {
679        assert_eq!(eval_str("(apply + [1 2 3])").unwrap(), long(6));
680    }
681
682    #[test]
683    fn test_atom_ops() {
684        let (_, mut env) = make_env();
685        eval_src("(def a (atom 0))", &mut env).unwrap();
686        eval_src("(swap! a inc)", &mut env).unwrap();
687        assert_eq!(eval_src("(deref a)", &mut env).unwrap(), long(1));
688    }
689
690    #[test]
691    fn test_when_macro() {
692        assert_eq!(eval_str("(when true 42)").unwrap(), long(42));
693        assert_eq!(eval_str("(when false 42)").unwrap(), Value::Nil);
694    }
695
696    #[test]
697    fn test_cond_macro() {
698        assert_eq!(eval_str("(cond false 1 true 2)").unwrap(), long(2));
699    }
700
701    #[test]
702    fn test_and_or() {
703        assert_eq!(eval_str("(and 1 2 3)").unwrap(), long(3));
704        assert_eq!(eval_str("(and 1 false 3)").unwrap(), bool_v(false));
705        assert_eq!(eval_str("(or false nil 42)").unwrap(), long(42));
706        assert_eq!(eval_str("(or false nil)").unwrap(), Value::Nil);
707    }
708
709    // ── Phase 5: Lazy sequences ───────────────────────────────────────────
710
711    #[test]
712    fn test_lazy_range() {
713        assert_eq!(
714            eval_str("(= (into [] (take 5 (range))) [0 1 2 3 4])").unwrap(),
715            bool_v(true)
716        );
717    }
718
719    #[test]
720    fn test_lazy_range_bounded() {
721        assert_eq!(
722            eval_str("(= (into [] (range 3)) [0 1 2])").unwrap(),
723            bool_v(true)
724        );
725    }
726
727    #[test]
728    fn test_lazy_iterate() {
729        assert_eq!(
730            eval_str("(= (into [] (take 3 (iterate inc 0))) [0 1 2])").unwrap(),
731            bool_v(true)
732        );
733    }
734
735    #[test]
736    fn test_lazy_repeat() {
737        assert_eq!(
738            eval_str("(= (into [] (take 3 (repeat :x))) [:x :x :x])").unwrap(),
739            bool_v(true)
740        );
741    }
742
743    #[test]
744    fn test_lazy_cycle() {
745        assert_eq!(
746            eval_str("(= (into [] (take 5 (cycle [1 2]))) [1 2 1 2 1])").unwrap(),
747            bool_v(true)
748        );
749    }
750
751    // ── Phase 5: Associative destructuring ───────────────────────────────
752
753    #[test]
754    fn test_assoc_destructure() {
755        assert_eq!(
756            eval_str("(let [{:keys [a b]} {:a 1 :b 2}] (+ a b))").unwrap(),
757            long(3)
758        );
759    }
760
761    #[test]
762    fn test_assoc_destructure_or() {
763        assert_eq!(
764            eval_str("(let [{:keys [a b] :or {b 99}} {:a 1}] b)").unwrap(),
765            long(99)
766        );
767    }
768
769    // ── Phase 5: letfn ───────────────────────────────────────────────────
770
771    #[test]
772    fn test_letfn() {
773        assert_eq!(
774            eval_str("(letfn [(fact [n] (if (= n 0) 1 (* n (fact (dec n)))))] (fact 5))").unwrap(),
775            long(120)
776        );
777    }
778
779    // ── Phase 5: namespace ops ────────────────────────────────────────────
780
781    #[test]
782    fn test_in_ns() {
783        let (_, mut env) = make_env();
784        eval_src("(in-ns 'mytest)", &mut env).unwrap();
785        assert_eq!(env.current_ns.as_ref(), "mytest");
786        eval_src("(in-ns 'user)", &mut env).unwrap();
787        assert_eq!(env.current_ns.as_ref(), "user");
788    }
789
790    // ── Phase 5: spit / slurp ─────────────────────────────────────────────
791
792    #[test]
793    fn test_spit_slurp() {
794        let path = std::env::temp_dir().join("cljrs_test_spit_slurp.txt");
795        let path_str = path.to_str().unwrap();
796        let src = format!(
797            r#"(do (spit "{}" "hello clojurust") (slurp "{}"))"#,
798            path_str, path_str
799        );
800        let result = eval_str(&src).unwrap();
801        if let Value::Str(s) = result {
802            assert_eq!(s.get().as_str(), "hello clojurust");
803        } else {
804            panic!("expected string result from slurp");
805        }
806        let _ = std::fs::remove_file(path);
807    }
808
809    // ── Phase 5: update-in ───────────────────────────────────────────────
810
811    #[test]
812    fn test_update_in() {
813        assert_eq!(
814            eval_str("(= (update-in {:a {:b 1}} [:a :b] inc) {:a {:b 2}})").unwrap(),
815            bool_v(true)
816        );
817    }
818
819    // ── Phase 5: if-let / when-let ────────────────────────────────────────
820
821    #[test]
822    fn test_if_let_truthy() {
823        assert_eq!(eval_str("(if-let [x 42] x :nope)").unwrap(), long(42));
824    }
825
826    #[test]
827    fn test_if_let_falsy() {
828        assert_eq!(
829            eval_str("(if-let [x nil] x :nope)").unwrap(),
830            eval_str(":nope").unwrap()
831        );
832    }
833
834    #[test]
835    fn test_when_let_truthy() {
836        assert_eq!(eval_str("(when-let [x 7] (* x 2))").unwrap(), long(14));
837    }
838
839    #[test]
840    fn test_when_let_falsy() {
841        assert_eq!(eval_str("(when-let [x nil] 99)").unwrap(), Value::Nil);
842    }
843
844    // ── Phase 5: math functions ───────────────────────────────────────────
845
846    #[test]
847    fn test_math_trig() {
848        // sin(0) = 0, cos(0) = 1
849        assert_eq!(eval_str("(Math/sin 0)").unwrap(), Value::Double(0.0));
850        assert_eq!(eval_str("(Math/cos 0)").unwrap(), Value::Double(1.0));
851    }
852
853    #[test]
854    fn test_math_constants() {
855        assert!(
856            matches!(eval_str("Math/PI").unwrap(), Value::Double(v) if (v - std::f64::consts::PI).abs() < 1e-10)
857        );
858        assert!(
859            matches!(eval_str("Math/E").unwrap(), Value::Double(v) if (v - std::f64::consts::E).abs() < 1e-10)
860        );
861    }
862
863    #[test]
864    fn test_math_log_exp() {
865        // exp(0) = 1, log(1) = 0
866        assert_eq!(eval_str("(Math/exp 0)").unwrap(), Value::Double(1.0));
867        assert_eq!(eval_str("(Math/log 1)").unwrap(), Value::Double(0.0));
868    }
869
870    // ── Phase 6: Protocols & Multimethods ─────────────────────────────────
871
872    #[test]
873    fn test_defprotocol() {
874        // Defining a protocol creates a callable ProtocolFn that errors without impl.
875        let result = eval_str(
876            r#"
877            (defprotocol Greet
878              (greet [this]))
879            (greet "hello")
880            "#,
881        );
882        assert!(result.is_err());
883        let msg = result.unwrap_err().to_string();
884        assert!(msg.contains("No implementation"), "got: {msg}");
885    }
886
887    #[test]
888    fn test_extend_type() {
889        let result = eval_str(
890            r#"
891            (defprotocol Greet
892              (greet [this]))
893            (extend-type String
894              Greet
895              (greet [this] (str "Hello, " this "!")))
896            (greet "world")
897            "#,
898        )
899        .unwrap();
900        assert_eq!(result, Value::string("Hello, world!"));
901    }
902
903    #[test]
904    fn test_protocol_dispatch() {
905        let result = eval_str(
906            r#"
907            (defprotocol Describable
908              (describe [this]))
909            (extend-type String
910              Describable
911              (describe [this] (str "string:" this)))
912            (extend-type Long
913              Describable
914              (describe [this] (str "long:" this)))
915            [(describe "hi") (describe 42)]
916            "#,
917        )
918        .unwrap();
919        assert!(matches!(result, Value::Vector(_)));
920        let s = format!("{}", result);
921        assert!(s.contains("string:hi"), "got: {s}");
922        assert!(s.contains("long:42"), "got: {s}");
923    }
924
925    #[test]
926    fn test_extend_protocol() {
927        let result = eval_str(
928            r#"
929            (defprotocol Showable
930              (show [this]))
931            (extend-protocol Showable
932              String
933              (show [this] (str "S:" this))
934              Long
935              (show [this] (str "L:" this)))
936            [(show "x") (show 7)]
937            "#,
938        )
939        .unwrap();
940        let s = format!("{}", result);
941        assert!(s.contains("S:x"), "got: {s}");
942        assert!(s.contains("L:7"), "got: {s}");
943    }
944
945    #[test]
946    fn test_satisfies() {
947        let result = eval_str(
948            r#"
949            (defprotocol Animal
950              (speak [this]))
951            (extend-type String
952              Animal
953              (speak [this] this))
954            [(satisfies? Animal "dog") (satisfies? Animal 42)]
955            "#,
956        )
957        .unwrap();
958        let s = format!("{}", result);
959        assert!(s.contains("true"), "got: {s}");
960        assert!(s.contains("false"), "got: {s}");
961    }
962
963    #[test]
964    fn test_defmulti_defmethod() {
965        // Note: fn param destructuring not yet supported; use explicit map lookups.
966        let result = eval_str(
967            r#"
968            (defmulti area :shape)
969            (defmethod area :circle [m] (* 3 (:r m) (:r m)))
970            (defmethod area :rectangle [m] (* (:w m) (:h m)))
971            [(area {:shape :circle :r 2}) (area {:shape :rectangle :w 3 :h 4})]
972            "#,
973        )
974        .unwrap();
975        let s = format!("{}", result);
976        // circle: 3*2*2=12, rectangle: 3*4=12
977        assert!(s.contains("12"), "got: {s}");
978    }
979
980    #[test]
981    fn test_default_dispatch() {
982        let result = eval_str(
983            r#"
984            (defmulti classify :kind)
985            (defmethod classify :default [x] :unknown)
986            (defmethod classify :cat [x] :meow)
987            [(classify {:kind :dog}) (classify {:kind :cat})]
988            "#,
989        )
990        .unwrap();
991        let s = format!("{}", result);
992        assert!(s.contains(":unknown"), "got: {s}");
993        assert!(s.contains(":meow"), "got: {s}");
994    }
995
996    #[test]
997    fn test_prefer_method() {
998        // prefer-method shouldn't error; just records preference
999        let result = eval_str(
1000            r#"
1001            (defmulti foo identity)
1002            (defmethod foo :a [x] 1)
1003            (prefer-method foo :a :b)
1004            (foo :a)
1005            "#,
1006        )
1007        .unwrap();
1008        assert_eq!(result, Value::Long(1));
1009    }
1010
1011    #[test]
1012    fn test_remove_method() {
1013        let result = eval_str(
1014            r#"
1015            (defmulti bar identity)
1016            (defmethod bar :x [_] 99)
1017            (remove-method bar :x)
1018            (bar :x)
1019            "#,
1020        );
1021        assert!(result.is_err());
1022        let msg = result.unwrap_err().to_string();
1023        assert!(msg.contains("No method"), "got: {msg}");
1024    }
1025
1026    // ── Phase 7: Concurrency primitives ──────────────────────────────────────
1027
1028    #[test]
1029    fn test_compare_and_set() {
1030        let result = eval_str(
1031            r#"
1032            (let [a (atom 10)]
1033              [(compare-and-set! a 10 20)   ; succeeds: 10 == 10
1034               (compare-and-set! a 10 30)   ; fails:    20 != 10
1035               @a])
1036            "#,
1037        )
1038        .unwrap();
1039        let s = format!("{}", result);
1040        assert!(s.contains("true"), "got: {s}");
1041        assert!(s.contains("false"), "got: {s}");
1042        assert!(s.contains("20"), "got: {s}");
1043    }
1044
1045    #[test]
1046    fn test_volatile() {
1047        let result = eval_str(
1048            r#"
1049            (let [v (volatile! 1)]
1050              (vreset! v 2)
1051              (vswap! v + 10)
1052              @v)
1053            "#,
1054        )
1055        .unwrap();
1056        assert_eq!(result, Value::Long(12));
1057    }
1058
1059    #[test]
1060    fn test_delay() {
1061        // Body should not be evaluated until forced.
1062        let result = eval_str(
1063            r#"
1064            (let [calls (atom 0)
1065                  d (delay (swap! calls inc) 42)]
1066              [@calls (force d) @calls (force d) @calls])
1067            "#,
1068        )
1069        .unwrap();
1070        let s = format!("{}", result);
1071        // calls starts at 0, force evaluates body once (returns 42), second force uses cache
1072        // s = [0 42 1 42 1]
1073        assert!(s.starts_with("[0 42 1 42 1]"), "got: {s}");
1074    }
1075
1076    #[test]
1077    fn test_realized() {
1078        let result = eval_str(
1079            r#"
1080            (let [d (delay 99)]
1081              [(realized? d) (force d) (realized? d)])
1082            "#,
1083        )
1084        .unwrap();
1085        let s = format!("{}", result);
1086        assert!(s.starts_with("[false 99 true]"), "got: {s}");
1087    }
1088
1089    #[test]
1090    fn test_promise() {
1091        let result = eval_str(
1092            r#"
1093            (let [p (promise)]
1094              (deliver p 42)
1095              (deliver p 99)  ; second deliver is ignored
1096              @p)
1097            "#,
1098        )
1099        .unwrap();
1100        assert_eq!(result, Value::Long(42));
1101    }
1102
1103    #[test]
1104    fn test_future() {
1105        let result = eval_str(
1106            r#"
1107            (let [f (future (+ 1 2))]
1108              @f)
1109            "#,
1110        )
1111        .unwrap();
1112        assert_eq!(result, Value::Long(3));
1113    }
1114
1115    #[test]
1116    fn test_agent_send() {
1117        let result = eval_str(
1118            r#"
1119            (let [a (agent 0)]
1120              (send a + 1)
1121              (send a + 2)
1122              (await a)
1123              @a)
1124            "#,
1125        )
1126        .unwrap();
1127        assert_eq!(result, Value::Long(3));
1128    }
1129
1130    #[test]
1131    fn test_agent_error_restart() {
1132        let result = eval_str(
1133            r#"
1134            (let [a (agent 10)]
1135              (send a (fn [_] (throw (ex-info "boom" {}))))
1136              (await a)
1137              (let [err (agent-error a)]
1138                (restart-agent a 99)
1139                [err @a]))
1140            "#,
1141        )
1142        .unwrap();
1143        let s = format!("{}", result);
1144        // err should be a string containing "boom", @a should be 99
1145        assert!(s.contains("boom"), "got: {s}");
1146        assert!(s.contains("99"), "got: {s}");
1147    }
1148
1149    #[test]
1150    fn test_defrecord_basic() {
1151        // Constructor and field access via keyword.
1152        let result = eval_str(
1153            r#"
1154            (defrecord Point [x y])
1155            (let [p (->Point 3 4)]
1156              [(:x p) (:y p)])
1157            "#,
1158        )
1159        .unwrap();
1160        assert_eq!(result.to_string(), "[3 4]");
1161    }
1162
1163    #[test]
1164    fn test_defrecord_map_constructor() {
1165        let result = eval_str(
1166            r#"
1167            (defrecord Color [r g b])
1168            (let [c (map->Color {:r 255 :g 128 :b 0})]
1169              [(:r c) (:g c) (:b c)])
1170            "#,
1171        )
1172        .unwrap();
1173        assert_eq!(result.to_string(), "[255 128 0]");
1174    }
1175
1176    #[test]
1177    fn test_defrecord_assoc() {
1178        // assoc on a record returns a new record of the same type.
1179        let result = eval_str(
1180            r#"
1181            (defrecord Pt [x y])
1182            (let [p (->Pt 1 2)
1183                  q (assoc p :x 99)]
1184              [(:x q) (:y q) (record? q)])
1185            "#,
1186        )
1187        .unwrap();
1188        assert_eq!(result.to_string(), "[99 2 true]");
1189    }
1190
1191    #[test]
1192    fn test_defrecord_with_protocol() {
1193        let result = eval_str(
1194            r#"
1195            (defprotocol IShape
1196              (area [this]))
1197            (defrecord Circle [radius]
1198              IShape
1199              (area [this] (* 3 (:radius this) (:radius this))))
1200            (let [c (->Circle 5)]
1201              (area c))
1202            "#,
1203        )
1204        .unwrap();
1205        assert_eq!(result, cljrs_value::Value::Long(75));
1206    }
1207
1208    #[test]
1209    fn test_instance_q() {
1210        let result = eval_str(
1211            r#"
1212            (defrecord Dog [name])
1213            (let [d (->Dog "Rex")]
1214              [(instance? Dog d) (instance? Dog 42)])
1215            "#,
1216        )
1217        .unwrap();
1218        assert_eq!(result.to_string(), "[true false]");
1219    }
1220
1221    #[test]
1222    fn test_reify_basic() {
1223        let result = eval_str(
1224            r#"
1225            (defprotocol IGreet
1226              (greet [this name]))
1227            (let [greeter (reify IGreet
1228                            (greet [this name] (str "Hello, " name "!")))]
1229              (greet greeter "World"))
1230            "#,
1231        )
1232        .unwrap();
1233        assert_eq!(result.to_string(), "\"Hello, World!\"");
1234    }
1235
1236    // ── require / load-file ───────────────────────────────────────────────
1237
1238    fn temp_ns_dir(test_name: &str) -> std::path::PathBuf {
1239        let dir = std::env::temp_dir().join(format!("cljrs_test_{test_name}"));
1240        let _ = std::fs::remove_dir_all(&dir);
1241        std::fs::create_dir_all(&dir).unwrap();
1242        dir
1243    }
1244
1245    fn make_env_with_paths(paths: Vec<std::path::PathBuf>) -> (Arc<GlobalEnv>, Env) {
1246        use crate::standard_env_with_paths;
1247        let globals = standard_env_with_paths(None, None, None, paths);
1248        let env = Env::new(globals.clone(), "user");
1249        (globals, env)
1250    }
1251
1252    #[test]
1253    fn test_require_as() {
1254        let dir = temp_ns_dir("require_as");
1255        std::fs::write(
1256            dir.join("mylib.cljrs"),
1257            "(ns mylib) (defn greet [n] (str \"hello \" n))",
1258        )
1259        .unwrap();
1260        let (_, mut env) = make_env_with_paths(vec![dir]);
1261        let result = eval_src("(require '[mylib :as ml]) (ml/greet \"world\")", &mut env).unwrap();
1262        assert_eq!(result.to_string(), "\"hello world\"");
1263    }
1264
1265    #[test]
1266    fn test_require_refer() {
1267        let dir = temp_ns_dir("require_refer");
1268        std::fs::write(
1269            dir.join("myutil.cljrs"),
1270            "(ns myutil) (defn twice [x] (* 2 x))",
1271        )
1272        .unwrap();
1273        let (_, mut env) = make_env_with_paths(vec![dir]);
1274        let result = eval_src("(require '[myutil :refer [twice]]) (twice 21)", &mut env).unwrap();
1275        assert_eq!(result, Value::Long(42));
1276    }
1277
1278    #[test]
1279    fn test_require_refer_all() {
1280        let dir = temp_ns_dir("require_refer_all");
1281        std::fs::write(
1282            dir.join("mymath.cljrs"),
1283            "(ns mymath) (defn square [x] (* x x))",
1284        )
1285        .unwrap();
1286        let (_, mut env) = make_env_with_paths(vec![dir]);
1287        let result = eval_src("(require '[mymath :refer :all]) (square 7)", &mut env).unwrap();
1288        assert_eq!(result, Value::Long(49));
1289    }
1290
1291    #[test]
1292    fn test_ns_require_clause() {
1293        let dir = temp_ns_dir("ns_require");
1294        std::fs::write(
1295            dir.join("greeter.cljrs"),
1296            "(ns greeter) (defn hi [n] (str \"Hi \" n))",
1297        )
1298        .unwrap();
1299        let (_, mut env) = make_env_with_paths(vec![dir]);
1300        let result = eval_src(
1301            "(ns myapp (:require [greeter :as g])) (g/hi \"Alice\")",
1302            &mut env,
1303        )
1304        .unwrap();
1305        assert_eq!(result.to_string(), "\"Hi Alice\"");
1306    }
1307
1308    #[test]
1309    fn test_require_idempotent() {
1310        let dir = temp_ns_dir("require_idempotent");
1311        // File has a side effect tracked via an atom
1312        std::fs::write(
1313            dir.join("counter.cljrs"),
1314            "(ns counter) (def loaded-count (atom 0)) (swap! loaded-count inc)",
1315        )
1316        .unwrap();
1317        let (globals, mut env) = make_env_with_paths(vec![dir]);
1318        eval_src("(require 'counter)", &mut env).unwrap();
1319        eval_src("(require 'counter)", &mut env).unwrap();
1320        // The atom should have been incremented only once.
1321        let count = globals.lookup_in_ns("counter", "loaded-count").unwrap();
1322        if let Value::Atom(a) = count {
1323            assert_eq!(a.get().deref(), Value::Long(1));
1324        } else {
1325            panic!("expected atom");
1326        }
1327    }
1328
1329    #[test]
1330    fn test_require_not_found() {
1331        let (_, mut env) = make_env_with_paths(vec![]);
1332        let err = eval_src("(require 'nonexistent.ns)", &mut env).unwrap_err();
1333        let msg = format!("{err:?}");
1334        assert!(msg.contains("nonexistent.ns"), "unexpected error: {msg}");
1335    }
1336
1337    #[test]
1338    fn test_require_circular() {
1339        let dir = temp_ns_dir("require_circular");
1340        // a requires b, b requires a
1341        std::fs::write(dir.join("cira.cljrs"), "(ns cira (:require [cirb]))").unwrap();
1342        std::fs::write(dir.join("cirb.cljrs"), "(ns cirb (:require [cira]))").unwrap();
1343        let (_, mut env) = make_env_with_paths(vec![dir]);
1344        let err = eval_src("(require 'cira)", &mut env).unwrap_err();
1345        let msg = format!("{err:?}");
1346        assert!(
1347            msg.contains("circular"),
1348            "expected circular error, got: {msg}"
1349        );
1350    }
1351
1352    #[test]
1353    fn test_load_file() {
1354        let dir = temp_ns_dir("load_file");
1355        let path = dir.join("script.cljrs");
1356        std::fs::write(&path, "(+ 1 2)").unwrap();
1357        let (_, mut env) = make_env_with_paths(vec![]);
1358        let result = eval_src(&format!("(load-file \"{}\")", path.display()), &mut env).unwrap();
1359        assert_eq!(result, Value::Long(3));
1360    }
1361
1362    // ── *ns* and namespace reflection ─────────────────────────────────────────
1363
1364    #[test]
1365    fn test_star_ns_initial() {
1366        // After standard_env(), *ns* should be the user namespace.
1367        let (_, mut env) = make_env();
1368        let v = eval_src("*ns*", &mut env).unwrap();
1369        match v {
1370            Value::Namespace(ns) => assert_eq!(ns.get().name.as_ref(), "user"),
1371            other => panic!("expected Namespace, got {other:?}"),
1372        }
1373    }
1374
1375    #[test]
1376    fn test_star_ns_after_in_ns() {
1377        let (_, mut env) = make_env();
1378        eval_src("(in-ns 'myns)", &mut env).unwrap();
1379        let v = eval_src("*ns*", &mut env).unwrap();
1380        match v {
1381            Value::Namespace(ns) => assert_eq!(ns.get().name.as_ref(), "myns"),
1382            other => panic!("expected Namespace, got {other:?}"),
1383        }
1384    }
1385
1386    #[test]
1387    fn test_star_ns_after_ns_form() {
1388        let (_, mut env) = make_env();
1389        eval_src("(ns mytest.ns)", &mut env).unwrap();
1390        let v = eval_src("*ns*", &mut env).unwrap();
1391        match v {
1392            Value::Namespace(ns) => assert_eq!(ns.get().name.as_ref(), "mytest.ns"),
1393            other => panic!("expected Namespace, got {other:?}"),
1394        }
1395    }
1396
1397    #[test]
1398    fn test_ns_name() {
1399        let (_, mut env) = make_env();
1400        let v = eval_src("(ns-name *ns*)", &mut env).unwrap();
1401        match v {
1402            Value::Symbol(s) => assert_eq!(s.get().name.as_ref(), "user"),
1403            other => panic!("expected Symbol, got {other:?}"),
1404        }
1405    }
1406
1407    #[test]
1408    fn test_find_ns() {
1409        let (_, mut env) = make_env();
1410        // known ns
1411        let v = eval_src("(find-ns 'user)", &mut env).unwrap();
1412        assert!(matches!(v, Value::Namespace(_)));
1413        // unknown ns
1414        let v2 = eval_src("(find-ns 'nonexistent)", &mut env).unwrap();
1415        assert_eq!(v2, Value::Nil);
1416    }
1417
1418    #[test]
1419    fn test_all_ns() {
1420        let (_, mut env) = make_env();
1421        let v = eval_src("(all-ns)", &mut env).unwrap();
1422        // Should be a list containing at least user and clojure.core
1423        let names: Vec<String> = match &v {
1424            Value::List(l) => l
1425                .get()
1426                .iter()
1427                .filter_map(|ns| match ns {
1428                    Value::Namespace(n) => Some(n.get().name.as_ref().to_string()),
1429                    _ => None,
1430                })
1431                .collect(),
1432            other => panic!("expected list, got {other:?}"),
1433        };
1434        assert!(names.contains(&"user".to_string()));
1435        assert!(names.contains(&"clojure.core".to_string()));
1436    }
1437
1438    #[test]
1439    fn test_ns_interns() {
1440        let (_, mut env) = make_env();
1441        eval_src("(def my-test-var 42)", &mut env).unwrap();
1442        let v = eval_src("(ns-interns *ns*)", &mut env).unwrap();
1443        let Value::Map(m) = v else {
1444            panic!("expected map")
1445        };
1446        // The map should contain 'my-test-var
1447        let sym = Value::Symbol(cljrs_gc::GcPtr::new(cljrs_value::Symbol {
1448            namespace: None,
1449            name: Arc::from("my-test-var"),
1450        }));
1451        assert!(m.get(&sym).is_some());
1452    }
1453
1454    #[test]
1455    fn test_create_ns() {
1456        let (_, mut env) = make_env();
1457        let v = eval_src("(create-ns 'fresh.ns)", &mut env).unwrap();
1458        match v {
1459            Value::Namespace(ns) => assert_eq!(ns.get().name.as_ref(), "fresh.ns"),
1460            other => panic!("expected Namespace, got {other:?}"),
1461        }
1462        // find-ns should now find it
1463        let v2 = eval_src("(find-ns 'fresh.ns)", &mut env).unwrap();
1464        assert!(matches!(v2, Value::Namespace(_)));
1465    }
1466
1467    // ── Dynamic variables (Phase 9) ───────────────────────────────────────────
1468
1469    #[test]
1470    fn test_dynamic_var_basic() {
1471        let (globals, mut env) = make_env();
1472        let result = eval_src("(def ^:dynamic *x* 10) (binding [*x* 42] *x*)", &mut env).unwrap();
1473        assert_eq!(result, Value::Long(42));
1474        // verify root is still bound
1475        let root = globals.lookup_in_ns("user", "*x*");
1476        assert_eq!(root, Some(Value::Long(10)));
1477    }
1478
1479    #[test]
1480    fn test_dynamic_var_restore() {
1481        let (_globals, mut env) = make_env();
1482        eval_src("(def ^:dynamic *x* 10)", &mut env).unwrap();
1483        eval_src("(binding [*x* 42] *x*)", &mut env).unwrap();
1484        // After binding block, value restored to root
1485        let val = eval_src("*x*", &mut env).unwrap();
1486        assert_eq!(val, Value::Long(10));
1487    }
1488
1489    #[test]
1490    fn test_dynamic_var_nested() {
1491        let (_, mut env) = make_env();
1492        eval_src("(def ^:dynamic *x* 1)", &mut env).unwrap();
1493        let result = eval_src("(binding [*x* 2] (binding [*x* 3] *x*))", &mut env).unwrap();
1494        assert_eq!(result, Value::Long(3));
1495        // After both blocks
1496        let val = eval_src("*x*", &mut env).unwrap();
1497        assert_eq!(val, Value::Long(1));
1498    }
1499
1500    #[test]
1501    fn test_dynamic_var_unaffected() {
1502        let (_, mut env) = make_env();
1503        eval_src("(def ^:dynamic *x* 10)", &mut env).unwrap();
1504        eval_src("(def y 99)", &mut env).unwrap();
1505        eval_src("(binding [*x* 42] *x*)", &mut env).unwrap();
1506        // non-dynamic var y is unchanged
1507        let val = eval_src("y", &mut env).unwrap();
1508        assert_eq!(val, Value::Long(99));
1509    }
1510
1511    #[test]
1512    fn test_binding_conveyance() {
1513        let (_, mut env) = make_env();
1514        eval_src("(def ^:dynamic *x* 10)", &mut env).unwrap();
1515        let result = eval_src("(binding [*x* 42] @(future *x*))", &mut env).unwrap();
1516        assert_eq!(result, Value::Long(42));
1517    }
1518
1519    #[test]
1520    fn test_var_set_in_binding() {
1521        let (_, mut env) = make_env();
1522        eval_src("(def ^:dynamic *x* 10)", &mut env).unwrap();
1523        // set! inside binding sets thread-local
1524        let inside = eval_src("(binding [*x* 1] (set! *x* 2) *x*)", &mut env).unwrap();
1525        assert_eq!(inside, Value::Long(2));
1526        // root still 10
1527        let root = eval_src("*x*", &mut env).unwrap();
1528        assert_eq!(root, Value::Long(10));
1529    }
1530
1531    #[test]
1532    fn test_with_bindings_star() {
1533        let (_, mut env) = make_env();
1534        eval_src("(def ^:dynamic *x* 10)", &mut env).unwrap();
1535        let result = eval_src("(with-bindings* {#'*x* 99} (fn [] *x*))", &mut env).unwrap();
1536        assert_eq!(result, Value::Long(99));
1537    }
1538
1539    #[test]
1540    fn test_meta_on_var() {
1541        let (_, mut env) = make_env();
1542        eval_src("(def ^:dynamic *x* 1)", &mut env).unwrap();
1543        let m = eval_src("(meta #'*x*)", &mut env).unwrap();
1544        // meta should be {:dynamic true}
1545        if let Value::Map(mv) = &m {
1546            let kw = Value::keyword(cljrs_value::Keyword::parse("dynamic"));
1547            assert_eq!(mv.get(&kw), Some(Value::Bool(true)));
1548        } else {
1549            panic!("expected map, got {m:?}");
1550        }
1551    }
1552
1553    #[test]
1554    fn test_bound_pred() {
1555        let (_, mut env) = make_env();
1556        eval_src("(def ^:dynamic *x* 1)", &mut env).unwrap();
1557        let t = eval_src("(bound? #'*x*)", &mut env).unwrap();
1558        assert_eq!(t, Value::Bool(true));
1559    }
1560
1561    #[test]
1562    fn test_alter_var_root() {
1563        let (_, mut env) = make_env();
1564        eval_src("(def x 1)", &mut env).unwrap();
1565        eval_src("(alter-var-root #'x inc)", &mut env).unwrap();
1566        let val = eval_src("x", &mut env).unwrap();
1567        assert_eq!(val, Value::Long(2));
1568    }
1569
1570    // ── clojure.test ─────────────────────────────────────────────────────────
1571
1572    #[test]
1573    fn test_clojure_test_is_pass() {
1574        // (is expr) returns true on a passing assertion.
1575        let (_, mut env) = make_env();
1576        eval_src(
1577            "(require '[clojure.test :refer [is deftest run-tests]])",
1578            &mut env,
1579        )
1580        .unwrap();
1581        let v = eval_src("(is (= 1 1))", &mut env).unwrap();
1582        assert_eq!(v, Value::Bool(true));
1583    }
1584
1585    #[test]
1586    fn test_clojure_test_is_fail() {
1587        // (is expr) returns false on a failing assertion.
1588        let (_, mut env) = make_env();
1589        eval_src("(require '[clojure.test :refer [is]])", &mut env).unwrap();
1590        let v = eval_src("(is (= 1 2))", &mut env).unwrap();
1591        assert_eq!(v, Value::Bool(false));
1592    }
1593
1594    #[test]
1595    fn test_clojure_test_is_catch_error() {
1596        // (is expr) catches runtime errors and returns false.
1597        let (_, mut env) = make_env();
1598        eval_src("(require '[clojure.test :refer [is]])", &mut env).unwrap();
1599        let v = eval_src("(is (/ 1 0))", &mut env).unwrap();
1600        assert_eq!(v, Value::Bool(false));
1601    }
1602
1603    #[test]
1604    fn test_clojure_test_deftest_and_run() {
1605        // deftest + run-tests smoke test: counters reflect pass/fail.
1606        let (_, mut env) = make_env();
1607        eval_src(
1608            "(require '[clojure.test :refer [deftest is run-tests]])",
1609            &mut env,
1610        )
1611        .unwrap();
1612        eval_src("(deftest my-passing-test (is (= 1 1)))", &mut env).unwrap();
1613        eval_src("(deftest my-failing-test (is (= 1 2)))", &mut env).unwrap();
1614        let counters = eval_src("(run-tests)", &mut env).unwrap();
1615        // Should have run 2 tests, 1 pass, 1 fail.
1616        if let Value::Map(m) = counters {
1617            let get = |k: &str| {
1618                m.get(&Value::keyword(cljrs_value::Keyword {
1619                    namespace: None,
1620                    name: Arc::from(k),
1621                }))
1622            };
1623            assert_eq!(get("test"), Some(Value::Long(2)));
1624            assert_eq!(get("pass"), Some(Value::Long(1)));
1625            assert_eq!(get("fail"), Some(Value::Long(1)));
1626            assert_eq!(get("error"), Some(Value::Long(0)));
1627        } else {
1628            panic!("expected map from run-tests, got {counters:?}");
1629        }
1630    }
1631
1632    #[test]
1633    fn test_alter_meta_bang() {
1634        // alter-meta! applies fn to var's meta and stores result.
1635        let (_, mut env) = make_env();
1636        eval_src("(def myvar 42)", &mut env).unwrap();
1637        eval_src("(alter-meta! #'myvar assoc :foo :bar)", &mut env).unwrap();
1638        let m = eval_src("(meta #'myvar)", &mut env).unwrap();
1639        if let Value::Map(map) = m {
1640            let foo_key = Value::keyword(cljrs_value::Keyword {
1641                namespace: None,
1642                name: Arc::from("foo"),
1643            });
1644            assert!(map.get(&foo_key).is_some());
1645        } else {
1646            panic!("expected map, got {m:?}");
1647        }
1648    }
1649
1650    #[test]
1651    fn test_catch_runtime_error() {
1652        // (try (/ 1 0) (catch Exception e "caught")) => "caught"
1653        let (_, mut env) = make_env();
1654        let v = eval_src(r#"(try (/ 1 0) (catch Exception e "caught"))"#, &mut env).unwrap();
1655        assert_eq!(v, Value::string("caught".to_string()));
1656    }
1657
1658    #[test]
1659    fn test_ns_resolve() {
1660        let (_, mut env) = make_env();
1661        eval_src("(def somevar 99)", &mut env).unwrap();
1662        // ns-resolve with current ns returns the var.
1663        let v = eval_src("(ns-resolve *ns* 'somevar)", &mut env).unwrap();
1664        assert!(matches!(v, Value::Var(_)));
1665        // ns-resolve for non-existent symbol returns nil.
1666        let v2 = eval_src("(ns-resolve *ns* 'nonexistent)", &mut env).unwrap();
1667        assert_eq!(v2, Value::Nil);
1668    }
1669
1670    // ── Persistent structure virtualization ──────────────────────────────
1671
1672    #[test]
1673    fn test_assoc_chain_virtualized() {
1674        // Assoc chain where intermediates aren't used — should be virtualized.
1675        let v = eval_str(
1676            "(let [m {}
1677                   a (assoc m :x 1)
1678                   b (assoc a :y 2)
1679                   c (assoc b :z 3)]
1680               c)",
1681        )
1682        .unwrap();
1683        // Result should be {:x 1, :y 2, :z 3}.
1684        assert!(matches!(&v, Value::Map(_)));
1685        if let Value::Map(m) = &v {
1686            assert_eq!(m.count(), 3);
1687            assert_eq!(m.get(&Value::keyword(Keyword::simple("x"))), Some(long(1)));
1688            assert_eq!(m.get(&Value::keyword(Keyword::simple("y"))), Some(long(2)));
1689            assert_eq!(m.get(&Value::keyword(Keyword::simple("z"))), Some(long(3)));
1690        }
1691    }
1692
1693    #[test]
1694    fn test_conj_chain_virtualized() {
1695        // Conj chain on a vector.
1696        let v = eval_str(
1697            "(let [v [1]
1698                   a (conj v 2)
1699                   b (conj a 3)
1700                   c (conj b 4)]
1701               c)",
1702        )
1703        .unwrap();
1704        assert_eq!(v, eval_str("[1 2 3 4]").unwrap());
1705    }
1706
1707    #[test]
1708    fn test_assoc_chain_intermediate_used_no_virtualize() {
1709        // If an intermediate is used in the body, virtualization should not apply,
1710        // but the result should still be correct.
1711        let v = eval_str(
1712            "(let [a (assoc {} :x 1)
1713                   b (assoc a :y 2)]
1714               (list (count a) (count b)))",
1715        )
1716        .unwrap();
1717        // a has 1 entry, b has 2.
1718        if let Value::List(l) = &v {
1719            let items: Vec<_> = l.get().iter().cloned().collect();
1720            assert_eq!(items, vec![long(1), long(2)]);
1721        } else {
1722            panic!("expected list, got {:?}", v);
1723        }
1724    }
1725
1726    #[test]
1727    fn test_assoc_chain_on_existing_map() {
1728        // Chain on an existing non-empty map.
1729        let v = eval_str(
1730            "(let [m {:a 1}
1731                   a (assoc m :b 2)
1732                   b (assoc a :c 3)]
1733               b)",
1734        )
1735        .unwrap();
1736        if let Value::Map(m) = &v {
1737            assert_eq!(m.count(), 3);
1738        } else {
1739            panic!("expected map");
1740        }
1741    }
1742}