1use 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
22pub fn eval(form: &Form, env: &mut Env) -> EvalResult {
24 match &form.kind {
25 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)), 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 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 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 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 eval(form, env)
116 }
117
118 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
131fn 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 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 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
163fn eval_symbol(s: &str, env: &mut Env) -> EvalResult {
166 if let Some(v) = env.lookup(s) {
168 return Ok(v);
169 }
170
171 if s.contains('/') && !s.starts_with('/') {
173 let sym = Symbol::parse(s);
174 if let Some(ns_part) = &sym.namespace {
175 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 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
195pub 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
237pub fn is_special_form(s: &str) -> bool {
240 SPECIAL_FORMS.contains(&s)
241}
242
243pub 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
278pub 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
287fn eval_reader_cond(clauses: &[Form], env: &mut Env) -> EvalResult {
290 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
311fn 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 let val = eval(inner, env)?;
332 Ok(val)
333 }
334 _ => Err(EvalError::Runtime(format!(
335 "unknown tagged literal: #{tag}"
336 ))),
337 }
338}
339
340#[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[test]
505 fn test_do() {
506 assert_eq!(eval_str("(do 1 2 3)").unwrap(), long(3));
507 }
508
509 #[test]
512 fn test_quote_list() {
513 let v = eval_str("'(1 2 3)").unwrap();
514 assert!(matches!(v, Value::List(_)));
515 }
516
517 #[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 #[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 #[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 #[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 #[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 #[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 #[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 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 #[test]
621 fn test_reader_cond_rust() {
622 assert_eq!(eval_str("#?(:rust 1 :clj 2)").unwrap(), long(1));
624 }
625
626 #[test]
627 fn test_reader_cond_default() {
628 assert_eq!(eval_str("#?(:clj 2 :default 99)").unwrap(), long(99));
630 }
631
632 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[test]
847 fn test_math_trig() {
848 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 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 #[test]
873 fn test_defprotocol() {
874 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 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 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 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 #[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 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 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 assert!(s.contains("boom"), "got: {s}");
1146 assert!(s.contains("99"), "got: {s}");
1147 }
1148
1149 #[test]
1150 fn test_defrecord_basic() {
1151 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 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 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 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 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 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 #[test]
1365 fn test_star_ns_initial() {
1366 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 let v = eval_src("(find-ns 'user)", &mut env).unwrap();
1412 assert!(matches!(v, Value::Namespace(_)));
1413 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 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 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 let v2 = eval_src("(find-ns 'fresh.ns)", &mut env).unwrap();
1464 assert!(matches!(v2, Value::Namespace(_)));
1465 }
1466
1467 #[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 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 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 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 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 let inside = eval_src("(binding [*x* 1] (set! *x* 2) *x*)", &mut env).unwrap();
1525 assert_eq!(inside, Value::Long(2));
1526 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 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 #[test]
1573 fn test_clojure_test_is_pass() {
1574 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 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 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 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 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 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 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 let v = eval_src("(ns-resolve *ns* 'somevar)", &mut env).unwrap();
1664 assert!(matches!(v, Value::Var(_)));
1665 let v2 = eval_src("(ns-resolve *ns* 'nonexistent)", &mut env).unwrap();
1667 assert_eq!(v2, Value::Nil);
1668 }
1669
1670 #[test]
1673 fn test_assoc_chain_virtualized() {
1674 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 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 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 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 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 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}