1use std::sync::Arc;
4
5use crate::builtins::form::{expand_pairs, expand_reader_conds, expand_reader_conds_cow};
6use crate::builtins::special::SPECIAL_FORMS;
7use crate::env::env::Env;
8use crate::env::error::{EvalError, EvalResult};
9use crate::interp::apply::eval_call;
10use crate::interp::special::eval_special;
11use crate::interp::syntax_quote::syntax_quote;
12use cljrs_gc::GcPtr;
13use cljrs_reader::Form;
14use cljrs_reader::form::FormKind;
15use cljrs_value::regex::Pattern;
16use cljrs_value::value::SetValue;
17use cljrs_value::{
18 FutureState, Keyword, MapValue, PersistentHashSet, PersistentList, PersistentVector, Symbol,
19 Value,
20};
21
22pub fn eval(form: &Form, env: &mut Env) -> EvalResult {
24 if !crate::env::gas::charge(1) {
25 return Err(EvalError::GasExhausted);
26 }
27 match &form.kind {
28 FormKind::Nil => Ok(Value::Nil),
30 FormKind::Bool(b) => Ok(Value::Bool(*b)),
31 FormKind::Int(n) => Ok(Value::Long(*n)),
32 FormKind::Float(f) => Ok(Value::Double(*f)),
33 FormKind::Symbolic(f) => Ok(Value::Double(*f)), FormKind::Str(s) => Ok(Value::string(s.clone())),
35 FormKind::Char(c) => Ok(Value::Char(*c)),
36 FormKind::BigInt(s) => crate::builtins::parse_bigint(s),
37 FormKind::BigDecimal(s) => crate::builtins::parse_bigdecimal(s),
38 FormKind::Ratio(s) => crate::builtins::parse_ratio(s),
39 FormKind::Regex(s) => {
40 let r = Pattern::new(s);
41 match r {
42 Ok(r) => Ok(Value::Pattern(GcPtr::new(r))),
43 Err(e) => Err(EvalError::Runtime(e.to_string())),
44 }
45 }
46
47 FormKind::Symbol(s) => eval_symbol(s, env),
49 FormKind::Keyword(s) => Ok(Value::keyword(Keyword::parse(s))),
50 FormKind::AutoKeyword(s) => {
51 let full = env
52 .globals
53 .resolve_auto_keyword(&env.current_ns, s)
54 .map_err(EvalError::Runtime)?;
55 Ok(Value::keyword(Keyword::parse(&full)))
56 }
57 FormKind::AutoSymbol(s) => {
60 let full = env
61 .globals
62 .resolve_auto_keyword(&env.current_ns, s)
63 .map_err(EvalError::Runtime)?;
64 eval_symbol(&full, env)
65 }
66
67 FormKind::List(forms) => eval_list(forms, env),
69 FormKind::Vector(forms) => {
70 let forms = expand_reader_conds_cow(forms);
71 let mut vals: Vec<Value> = Vec::with_capacity(forms.len());
72 for f in forms.iter() {
73 let _root = crate::env::gc_roots::root_values(&vals);
74 vals.push(eval(f, env)?);
75 }
76 Ok(Value::Vector(GcPtr::new(PersistentVector::from_iter(vals))))
77 }
78 FormKind::Map(forms) => {
79 let forms = expand_pairs(forms).map_err(|_| {
80 EvalError::Runtime("map literal must have an even number of forms".into())
81 })?;
82 let mut pairs: Vec<Value> = Vec::with_capacity(forms.len());
83 for f in forms.iter() {
84 let _root = crate::env::gc_roots::root_values(&pairs);
85 pairs.push(eval(f, env)?);
86 }
87 let kv_pairs: Vec<(Value, Value)> = pairs
88 .chunks(2)
89 .map(|pair| (pair[0].clone(), pair[1].clone()))
90 .collect();
91 Ok(Value::Map(MapValue::from_pairs(kv_pairs)))
92 }
93 FormKind::Set(forms) => {
94 let forms = expand_reader_conds_cow(forms);
95 let mut vals: Vec<Value> = Vec::with_capacity(forms.len());
96 for f in forms.iter() {
97 let _root = crate::env::gc_roots::root_values(&vals);
98 vals.push(eval(f, env)?);
99 }
100 Ok(Value::Set(SetValue::Hash(GcPtr::new(
101 PersistentHashSet::from_iter(vals),
102 ))))
103 }
104
105 FormKind::Quote(inner) => {
110 let resolved = crate::builtins::form::resolve_auto_forms(inner, env)?;
111 crate::builtins::form::form_to_value(&resolved)
112 }
113 FormKind::SyntaxQuote(inner) => syntax_quote(inner, env),
114 FormKind::Unquote(_) => Err(EvalError::Runtime("unquote outside syntax-quote".into())),
115 FormKind::UnquoteSplice(_) => Err(EvalError::Runtime(
116 "unquote-splice outside syntax-quote".into(),
117 )),
118 FormKind::Deref(inner) => {
119 let v = eval(inner, env)?;
120 if env.is_async && matches!(v, Value::Future(_)) {
121 return Err(EvalError::Runtime(
122 "deref (@) on a future is not allowed inside an ^:async function; use (await ...) instead".into(),
123 ));
124 }
125 deref_value(v)
126 }
127 FormKind::Var(inner) => {
128 if let FormKind::Symbol(s) = &inner.kind {
129 let parsed = Symbol::parse(s);
130 let ns: Arc<str> = match parsed.namespace.as_deref() {
131 Some(ns_part) => env
132 .globals
133 .resolve_alias(&env.current_ns, ns_part)
134 .unwrap_or_else(|| Arc::from(ns_part)),
135 None => env.current_ns.clone(),
136 };
137 env.globals
138 .lookup_var_in_ns(&ns, &parsed.name)
139 .map(Value::Var)
140 .ok_or_else(|| EvalError::UnboundSymbol(s.clone()))
141 } else {
142 Err(EvalError::Runtime("var requires a symbol".into()))
143 }
144 }
145 FormKind::Meta(meta, form) => {
146 let value = eval(form, env)?;
154 if !form.takes_runtime_meta() {
155 return Ok(value);
156 }
157 let m = crate::builtins::form::expand_meta_annotation(meta, &mut |f| eval(f, env))?;
158 Ok(crate::builtins::form::attach_meta(value, m))
159 }
160
161 FormKind::AnonFn(body) => {
163 let expanded = crate::builtins::form::expand_anon_fn(body, form.span.clone());
164 eval(&expanded, env)
165 }
166 FormKind::ReaderCond {
167 splicing: _,
168 clauses,
169 } => eval_reader_cond(clauses, env),
170 FormKind::TaggedLiteral(tag, inner) => eval_tagged_literal(tag, inner, env),
171 }
172}
173
174pub fn eval_with_gas(form: &Form, env: &mut Env, credits: u64) -> EvalResult {
180 let meter = crate::env::gas::GasMeter::new(credits);
181 let _guard = crate::env::gas::GasGuard::install(meter);
182 eval(form, env)
183}
184
185fn eval_list(forms: &[Form], env: &mut Env) -> EvalResult {
188 if forms.is_empty() {
189 return Ok(Value::List(GcPtr::new(PersistentList::empty())));
190 }
191
192 let expanded: Vec<Form>;
194 let forms: &[Form] = if forms
195 .iter()
196 .any(|f| matches!(f.kind, FormKind::ReaderCond { .. }))
197 {
198 expanded = expand_reader_conds(forms);
199 if expanded.is_empty() {
200 return Ok(Value::List(GcPtr::new(PersistentList::empty())));
201 }
202 &expanded
203 } else {
204 forms
205 };
206
207 if let Some(s) = forms[0].as_symbol()
209 && is_special_form(s)
210 {
211 return eval_special(s, &forms[1..], env);
212 }
213
214 eval_call(&forms[0], &forms[1..], env)
215}
216
217fn eval_symbol(s: &str, env: &mut Env) -> EvalResult {
220 let sym = Symbol::parse(s);
221
222 #[cfg(not(target_arch = "wasm32"))]
225 if let Some(ref commit) = sym.version.clone() {
226 crate::env::policy::check_versioned_lookup()?;
227 return crate::interp::versioned::resolve_versioned_symbol(&sym, commit, env);
228 }
229 #[cfg(target_arch = "wasm32")]
230 if sym.version.is_some() {
231 return Err(crate::env::error::EvalError::Runtime(
232 "versioned symbols are not supported in WASM".to_string(),
233 ));
234 }
235
236 if let Some(v) = env.lookup_local_frames(s) {
239 return Ok(v);
240 }
241
242 #[cfg(not(target_arch = "wasm32"))]
247 if let Some(commit) = env.versioned_eval_commit.clone() {
248 let is_same_ns = sym.namespace.is_none()
249 || sym.namespace.as_deref() == Some(env.current_ns.as_ref())
250 || sym.namespace.as_deref()
251 == Some(crate::env::versioned::base_ns_name(&env.current_ns));
252 if is_same_ns {
253 return crate::interp::versioned::resolve_versioned_symbol(&sym, &commit, env);
254 }
255 }
256
257 if let Some(v) = env.globals.lookup_in_ns(&env.current_ns, s) {
259 return Ok(v);
260 }
261
262 if s.contains('/')
264 && !s.starts_with('/')
265 && let Some(ns_part) = &sym.namespace
266 {
267 let resolved: Arc<str> = env
268 .globals
269 .resolve_alias(&env.current_ns, ns_part)
270 .unwrap_or_else(|| Arc::from(ns_part.as_ref()));
271 #[cfg(not(target_arch = "wasm32"))]
275 let resolved: Arc<str> = if env.current_ns.as_ref() != resolved.as_ref()
276 && crate::env::versioned::base_ns_name(&env.current_ns) == resolved.as_ref()
277 {
278 env.current_ns.clone()
279 } else {
280 resolved
281 };
282 return env
283 .globals
284 .lookup_in_ns(&resolved, &sym.name)
285 .ok_or_else(|| EvalError::UnboundSymbol(s.to_string()));
286 }
287
288 if is_jvm_class_name(s) {
290 return Ok(Value::symbol(Symbol::simple(s)));
291 }
292
293 Err(EvalError::UnboundSymbol(s.to_string()))
294}
295
296pub fn is_jvm_class_name(s: &str) -> bool {
298 matches!(
299 s,
300 "clojure.lang.BigInt"
301 | "java.math.BigDecimal"
302 | "java.math.BigInteger"
303 | "clojure.lang.Ratio"
304 | "java.lang.Long"
305 | "java.lang.Double"
306 | "java.lang.String"
307 | "java.lang.Boolean"
308 | "java.lang.Character"
309 | "java.lang.Number"
310 | "clojure.lang.Symbol"
311 | "clojure.lang.Keyword"
312 | "clojure.lang.PersistentList"
313 | "clojure.lang.PersistentVector"
314 | "clojure.lang.PersistentHashMap"
315 | "clojure.lang.PersistentHashSet"
316 | "clojure.lang.PersistentArrayMap"
317 | "clojure.lang.IFn"
318 | "clojure.lang.ISeq"
319 | "clojure.lang.IPending"
320 | "clojure.lang.Atom"
321 | "clojure.lang.Var"
322 | "clojure.lang.Namespace"
323 | "java.util.UUID"
324 | "java.lang.Exception"
325 | "java.lang.Throwable"
326 | "java.lang.Error"
327 | "Exception"
328 | "Throwable"
329 | "Error"
330 | "clojure.lang.ExceptionInfo"
331 | "clojure.lang.IEditableCollection"
332 | "Boolean"
333 | "clojure.lang.PersistentQueue"
334 | "java.util.regex.Pattern"
335 )
336}
337
338pub fn is_special_form(s: &str) -> bool {
341 SPECIAL_FORMS.contains(&s)
342}
343
344pub fn deref_value(v: Value) -> EvalResult {
348 match v {
349 Value::Atom(a) => Ok(a.get().deref()),
350 Value::SharedAtom(sa) => Ok(cljrs_value::demote(&sa.deref_val())),
351 Value::Var(var) => crate::env::dynamics::deref_var(&var)
352 .ok_or_else(|| EvalError::Runtime("unbound var".into())),
353 Value::Volatile(vol) => Ok(vol.get().deref()),
354 Value::Delay(d) => d.get().force().map_err(EvalError::Runtime),
355 Value::Agent(a) => Ok(a.get().get_state()),
356 Value::Reduced(inner) => Ok(*inner),
357 Value::Promise(p) => Ok(p.get().deref_blocking()),
358 Value::Future(f) => {
359 let mut guard = f.get().state.lock().unwrap();
360 loop {
361 match &*guard {
362 FutureState::Done(v) => {
363 f.get().mark_observed();
364 return Ok(v.clone());
365 }
366 FutureState::Failed(v) => {
367 f.get().mark_observed();
368 return Err(EvalError::Thrown(v.clone()));
369 }
370 FutureState::GasExhausted => {
371 f.get().mark_observed();
372 return Err(EvalError::GasExhausted);
373 }
374 FutureState::Cancelled => {
375 return Err(EvalError::Runtime("future was cancelled".into()));
376 }
377 FutureState::Running => {
378 guard = f.get().cond.wait(guard).unwrap();
379 }
380 }
381 }
382 }
383 other => Err(EvalError::Runtime(format!(
384 "cannot deref {}",
385 other.type_name()
386 ))),
387 }
388}
389
390pub fn eval_body(forms: &[Form], env: &mut Env) -> EvalResult {
392 let mut result = Value::Nil;
393 for form in forms {
394 result = eval(form, env)?;
395 }
396 Ok(result)
397}
398
399fn eval_reader_cond(clauses: &[Form], env: &mut Env) -> EvalResult {
402 let mut i = 0;
404 let mut default: Option<&Form> = None;
405 while i + 1 < clauses.len() {
406 match &clauses[i].kind {
407 FormKind::Keyword(k) if k == "rust" => {
408 return eval(&clauses[i + 1], env);
409 }
410 FormKind::Keyword(k) if k == "default" => {
411 default = Some(&clauses[i + 1]);
412 }
413 _ => {}
414 }
415 i += 2;
416 }
417 match default {
418 Some(f) => eval(f, env),
419 None => Ok(Value::Nil),
420 }
421}
422
423fn eval_tagged_literal(tag: &str, inner: &Form, env: &mut Env) -> EvalResult {
426 match tag {
427 "uuid" => {
428 let val = eval(inner, env)?;
429 match &val {
430 Value::Str(s) => {
431 let uuid = uuid::Uuid::parse_str(s.get())
432 .map_err(|e| EvalError::Runtime(format!("invalid UUID: {e}")))?;
433 Ok(Value::Uuid(uuid.as_u128()))
434 }
435 _ => Err(EvalError::Runtime(format!(
436 "#uuid expects a string, got {}",
437 val.type_name()
438 ))),
439 }
440 }
441 "inst" => {
442 let val = eval(inner, env)?;
444 Ok(val)
445 }
446 _ => Err(EvalError::Runtime(format!(
447 "unknown tagged literal: #{tag}"
448 ))),
449 }
450}
451
452#[cfg(test)]
455mod tests {
456 use super::*;
457 use crate::env::env::GlobalEnv;
458 use std::sync::Arc;
459
460 fn make_env() -> (Arc<GlobalEnv>, Env) {
461 let globals = crate::Runtime::builder()
462 .execution_mode(crate::ExecutionMode::TreeWalk)
463 .eager_clojure_test(true)
464 .build()
465 .expect("runtime")
466 .into_globals();
467 let env = Env::new(globals.clone(), "user");
468 (globals, env)
469 }
470
471 fn eval_str(src: &str) -> EvalResult {
472 let (_, mut env) = make_env();
473 eval_src(src, &mut env)
474 }
475
476 fn eval_src(src: &str, env: &mut Env) -> EvalResult {
477 let mut parser = cljrs_reader::Parser::new(src.to_string(), "<test>".to_string());
478 let forms = parser.parse_all().map_err(EvalError::Read)?;
479 let mut result = Value::Nil;
480 for form in forms {
481 result = eval(&form, env)?;
482 }
483 Ok(result)
484 }
485
486 fn long(n: i64) -> Value {
487 Value::Long(n)
488 }
489 fn bool_v(b: bool) -> Value {
490 Value::Bool(b)
491 }
492
493 #[test]
496 fn test_literal_int() {
497 assert_eq!(eval_str("42").unwrap(), long(42));
498 }
499
500 #[test]
501 fn test_literal_string() {
502 assert!(matches!(eval_str("\"hello\"").unwrap(), Value::Str(_)));
503 }
504
505 #[test]
506 fn test_literal_nil() {
507 assert_eq!(eval_str("nil").unwrap(), Value::Nil);
508 }
509
510 #[test]
511 fn test_literal_true() {
512 assert_eq!(eval_str("true").unwrap(), bool_v(true));
513 }
514
515 #[test]
516 fn test_literal_false() {
517 assert_eq!(eval_str("false").unwrap(), bool_v(false));
518 }
519
520 #[test]
523 fn test_add() {
524 assert_eq!(eval_str("(+ 1 2)").unwrap(), long(3));
525 }
526
527 #[test]
528 fn test_mul() {
529 assert_eq!(eval_str("(* 2 3)").unwrap(), long(6));
530 }
531
532 #[test]
533 fn test_div_exact() {
534 assert_eq!(eval_str("(/ 10 2)").unwrap(), long(5));
535 }
536
537 #[test]
538 fn test_sub() {
539 assert_eq!(eval_str("(- 10 3)").unwrap(), long(7));
540 }
541
542 #[test]
545 fn test_let_simple() {
546 assert_eq!(eval_str("(let* [x 1 y 2] (+ x y))").unwrap(), long(3));
547 }
548
549 #[test]
550 fn test_let_shadowing() {
551 assert_eq!(eval_str("(let* [x 1] (let* [x 10] x))").unwrap(), long(10));
552 }
553
554 #[test]
557 fn test_fn_call() {
558 assert_eq!(eval_str("((fn* [x] (* x x)) 5)").unwrap(), long(25));
559 }
560
561 #[test]
562 fn test_closure_capture() {
563 assert_eq!(
564 eval_str("(let* [n 3] ((fn* [x] (+ x n)) 4))").unwrap(),
565 long(7)
566 );
567 }
568
569 #[test]
570 fn test_multi_arity_fn() {
571 assert_eq!(
572 eval_str("((fn* ([x] x) ([x y] (+ x y))) 1 2)").unwrap(),
573 long(3)
574 );
575 }
576
577 #[test]
580 fn test_loop_recur() {
581 let result =
582 eval_str("(loop* [i 0 acc 0] (if (= i 5) acc (recur (inc i) (+ acc i))))").unwrap();
583 assert_eq!(result, long(10));
584 }
585
586 #[test]
589 fn test_def() {
590 let (_, mut env) = make_env();
591 eval_src("(def x 42)", &mut env).unwrap();
592 assert_eq!(eval_src("x", &mut env).unwrap(), long(42));
593 }
594
595 #[test]
596 fn test_defn() {
597 let (_, mut env) = make_env();
598 eval_src("(defn square [x] (* x x))", &mut env).unwrap();
599 assert_eq!(eval_src("(square 7)", &mut env).unwrap(), long(49));
600 }
601
602 #[test]
605 fn test_if_truthy() {
606 assert_eq!(eval_str("(if true 1 2)").unwrap(), long(1));
607 }
608
609 #[test]
610 fn test_if_falsy() {
611 assert_eq!(eval_str("(if false 1 2)").unwrap(), long(2));
612 }
613
614 #[test]
615 fn test_if_nil_branch() {
616 assert_eq!(eval_str("(if false 1)").unwrap(), Value::Nil);
617 }
618
619 #[test]
622 fn test_do() {
623 assert_eq!(eval_str("(do 1 2 3)").unwrap(), long(3));
624 }
625
626 #[test]
629 fn test_quote_list() {
630 let v = eval_str("'(1 2 3)").unwrap();
631 assert!(matches!(v, Value::List(_)));
632 }
633
634 #[test]
637 fn test_keyword_lookup() {
638 assert_eq!(eval_str("(:a {:a 1})").unwrap(), long(1));
639 }
640
641 #[test]
642 fn test_keyword_lookup_missing() {
643 assert_eq!(eval_str("(:b {:a 1})").unwrap(), Value::Nil);
644 }
645
646 #[test]
649 fn test_map_literal() {
650 let v = eval_str("{:a 1 :b 2}").unwrap();
651 assert!(matches!(v, Value::Map(_)));
652 if let Value::Map(m) = &v {
653 assert_eq!(m.count(), 2);
654 }
655 }
656
657 #[test]
658 fn test_vector_literal() {
659 let v = eval_str("[1 2 3]").unwrap();
660 assert!(matches!(v, Value::Vector(_)));
661 }
662
663 #[test]
664 fn test_set_literal() {
665 let v = eval_str("#{1 2 3}").unwrap();
666 assert!(matches!(v, Value::Set(_)));
667 }
668
669 #[test]
670 fn test_contains_q_vector_non_integer_key_returns_false() {
671 assert_eq!(eval_str("(contains? [1 2 3] :a)").unwrap(), bool_v(false));
674 assert_eq!(
675 eval_str("(contains? [1 2 3] \"x\")").unwrap(),
676 bool_v(false)
677 );
678 assert_eq!(eval_str("(contains? [1 2 3] 0)").unwrap(), bool_v(true));
680 assert_eq!(eval_str("(contains? [1 2 3] 9)").unwrap(), bool_v(false));
681 }
682
683 #[test]
686 fn test_set_bang() {
687 let (_, mut env) = make_env();
688 eval_src("(def x 1)", &mut env).unwrap();
689 eval_src("(set! x 99)", &mut env).unwrap();
690 assert_eq!(eval_src("x", &mut env).unwrap(), long(99));
691 }
692
693 #[test]
696 fn test_throw_catch() {
697 let v = eval_str("(try (throw (ex-info \"oops\" {})) (catch Exception e (ex-message e)))")
698 .unwrap();
699 assert!(matches!(v, Value::Str(_)));
700 }
701
702 #[test]
703 fn test_try_no_throw() {
704 assert_eq!(eval_str("(try 42)").unwrap(), long(42));
705 }
706
707 #[test]
708 fn test_catch_default_catches_internal_value_errors() {
709 for src in [
713 "(try (nth [1 2 3] 10) (catch :default e \"caught\"))",
714 "(try (aget (long-array 3) 10) (catch :default e \"caught\"))",
715 "(try (aset (long-array 3) 10 5) (catch :default e \"caught\"))",
716 ] {
717 let v = eval_str(src).unwrap();
718 assert_eq!(v, Value::string("caught"), "{src}");
719 }
720 }
721
722 #[test]
723 fn test_catch_default_binds_clean_ex_message() {
724 let v = eval_str("(try (nth [1 2 3] 10) (catch :default e (ex-message e)))").unwrap();
728 assert_eq!(v, Value::string("index out of bounds: 10 >= 3"));
729 }
730
731 #[test]
732 fn test_finally_runs_and_value_is_discarded() {
733 let v = eval_str("(let [a (atom 0)] (try 1 (finally (reset! a 5))) @a)").unwrap();
735 assert_eq!(v, long(5));
736 assert_eq!(eval_str("(try 1 (finally 2))").unwrap(), long(1));
737 }
738
739 #[test]
740 fn test_finally_runs_after_catch() {
741 let v = eval_str(
742 "(let [a (atom 0)]
743 (try (throw (ex-info \"x\" {})) (catch Exception e :caught)
744 (finally (reset! a 9)))
745 @a)",
746 )
747 .unwrap();
748 assert_eq!(v, long(9));
749 }
750
751 #[test]
752 fn test_finally_exception_propagates() {
753 assert!(eval_str("(try 1 (finally (throw (ex-info \"boom\" {}))))").is_err());
755 }
756
757 #[test]
758 fn test_catch_reader_conditional_type() {
759 let v =
765 eval_str(r#"(try (throw (ex-info "boom" {})) (catch #?(:rust Exception) e :caught))"#)
766 .unwrap();
767 let kw_caught = Value::keyword(cljrs_value::Keyword::simple("caught"));
768 assert_eq!(v, kw_caught, "single-branch reader cond");
769
770 let v = eval_str(concat!(
772 "(try (throw (ex-info \"boom\" {})) ",
773 "(catch #?(:clj Throwable :cljs :default :rust Exception) e :caught))",
774 ))
775 .unwrap();
776 assert_eq!(v, kw_caught, "multi-branch reader cond");
777
778 let v =
781 eval_str(r#"(try (throw (ex-info "boom" {})) (catch #?(:rust :default) e :caught))"#)
782 .unwrap();
783 assert_eq!(v, kw_caught, "reader cond resolves to :default");
784
785 let result =
787 eval_str(r#"(try (throw (ex-info "boom" {})) (catch #?(:clj Throwable) e :caught))"#);
788 assert!(
789 result.is_err(),
790 "no matching :rust branch must let exception escape"
791 );
792 }
793
794 #[test]
795 fn test_nth_negative_index() {
796 assert_eq!(
798 eval_str("(= (nth [10 20 30] -1 :nf) :nf)").unwrap(),
799 bool_v(true)
800 );
801 assert!(eval_str("(nth [10 20 30] -1)").is_err());
802 assert!(eval_str("(nth '(1 2 3) -1)").is_err());
803 assert_eq!(
805 eval_str("(= (nth (range) -1 :nf) :nf)").unwrap(),
806 bool_v(true)
807 );
808 assert_eq!(
809 eval_str("(= (nth '(1 2 3) -1 :nf) :nf)").unwrap(),
810 bool_v(true)
811 );
812 }
813
814 #[test]
817 fn test_sequential_destructure() {
818 assert_eq!(eval_str("(let* [[a b] [1 2]] (+ a b))").unwrap(), long(3));
819 }
820
821 #[test]
822 fn test_rest_destructure() {
823 let v = eval_str("(let* [[h & t] [1 2 3]] t)").unwrap();
824 assert!(matches!(v, Value::List(_)));
825 if let Value::List(l) = &v {
826 assert_eq!(l.get().count(), 2);
827 }
828 }
829
830 #[test]
833 fn test_defmacro() {
834 let (_, mut env) = make_env();
835 eval_src("(defmacro my-if [t a b] (list 'if t a b))", &mut env).unwrap();
836 assert_eq!(eval_src("(my-if true 1 2)", &mut env).unwrap(), long(1));
837 assert_eq!(eval_src("(my-if false 1 2)", &mut env).unwrap(), long(2));
838 }
839
840 #[test]
843 fn test_syntax_quote_basic() {
844 let (_, mut env) = make_env();
845 eval_src("(def b 2)", &mut env).unwrap();
846 let v = eval_src("`(a ~b)", &mut env).unwrap();
847 assert!(matches!(v, Value::List(_)));
848 if let Value::List(l) = &v {
849 let items: Vec<_> = l.get().iter().cloned().collect();
851 assert_eq!(items.len(), 2);
852 assert_eq!(items[1], long(2));
853 }
854 }
855
856 #[test]
859 fn test_reader_cond_rust() {
860 assert_eq!(eval_str("#?(:rust 1 :clj 2)").unwrap(), long(1));
862 }
863
864 #[test]
865 fn test_reader_cond_default() {
866 assert_eq!(eval_str("#?(:clj 2 :default 99)").unwrap(), long(99));
868 }
869
870 #[test]
871 fn test_reader_cond_splice_in_vector() {
872 assert_eq!(
873 eval_str("[1 #?@(:rust [:a :b]) 2]").unwrap(),
874 eval_str("[1 :a :b 2]").unwrap()
875 );
876 }
877
878 #[test]
879 fn test_reader_cond_splice_in_set() {
880 assert_eq!(
881 eval_str("#{1 #?@(:rust [2 3]) 4}").unwrap(),
882 eval_str("#{1 2 3 4}").unwrap()
883 );
884 }
885
886 #[test]
887 fn test_reader_cond_splice_in_map() {
888 assert_eq!(
889 eval_str("{:a 1 #?@(:rust [:b 2]) :c 3}").unwrap(),
890 eval_str("{:a 1 :b 2 :c 3}").unwrap()
891 );
892 }
893
894 #[test]
895 fn test_reader_cond_splice_in_call_args() {
896 assert_eq!(
897 eval_str("(vector 1 #?@(:rust [2 3]) 4)").unwrap(),
898 eval_str("[1 2 3 4]").unwrap()
899 );
900 }
901
902 #[test]
903 fn test_reader_cond_splice_no_match_removed() {
904 assert_eq!(
905 eval_str("[1 #?@(:clj [:a :b]) 2]").unwrap(),
906 eval_str("[1 2]").unwrap()
907 );
908 }
909
910 proptest::proptest! {
911 #![proptest_config(proptest::prelude::ProptestConfig::with_cases(48))]
912 #[test]
916 fn prop_splice_evals_as_inline_in_every_container(
917 np in 0usize..3, nm in 0usize..3, ns in 0usize..3,
918 ) {
919 let kw_run = |start: usize, n: usize| {
920 (start..start + n)
921 .map(|i| format!(":v{i}"))
922 .collect::<Vec<_>>()
923 .join(" ")
924 };
925 let p = kw_run(0, np);
926 let m = kw_run(np, nm);
927 let s = kw_run(np + nm, ns);
928 for (open, close, quote) in
929 [("[", "]", ""), ("#{", "}", ""), ("(", ")", "'"), ("(vector ", ")", "")]
930 {
931 let spliced = format!("{quote}{open}{p} #?@(:rust [{m}]) {s}{close}");
932 let inlined = format!("{quote}{open}{p} {m} {s}{close}");
933 proptest::prop_assert_eq!(
934 eval_str(&spliced).unwrap(),
935 eval_str(&inlined).unwrap(),
936 "container {}{}",
937 open,
938 close
939 );
940 }
941 }
942 }
943
944 #[derive(Clone, Debug)]
951 enum Slot {
952 Plain,
954 NonSplice { matches: bool },
956 Splice { matches: bool, n: usize },
958 }
959
960 fn slot_strat() -> impl proptest::strategy::Strategy<Value = Slot> {
961 use proptest::strategy::{Just, Strategy};
962 proptest::prop_oneof![
963 Just(Slot::Plain),
964 proptest::bool::ANY.prop_map(|matches| Slot::NonSplice { matches }),
965 (proptest::bool::ANY, 0usize..3).prop_map(|(matches, n)| Slot::Splice { matches, n }),
966 ]
967 }
968
969 fn render_slots(slots: &[Slot], unit: &dyn Fn(usize) -> String) -> (String, String) {
974 let mut next = 0usize;
975 let (mut written, mut inlined) = (Vec::new(), Vec::new());
976 for slot in slots {
977 match *slot {
978 Slot::Plain => {
979 let u = unit(next);
980 next += 1;
981 written.push(u.clone());
982 inlined.push(u);
983 }
984 Slot::NonSplice { matches } => {
985 let u = unit(next);
986 next += 1;
987 let key = if matches { "rust" } else { "clj" };
988 written.push(format!("#?(:{key} {u})"));
989 if matches {
990 inlined.push(u);
991 }
992 }
993 Slot::Splice { matches, n } => {
994 let us: Vec<String> = (0..n)
995 .map(|_| {
996 let u = unit(next);
997 next += 1;
998 u
999 })
1000 .collect();
1001 let key = if matches { "rust" } else { "clj" };
1002 written.push(format!("#?@(:{key} [{}])", us.join(" ")));
1003 if matches {
1004 inlined.extend(us);
1005 }
1006 }
1007 }
1008 }
1009 (written.join(" "), inlined.join(" "))
1010 }
1011
1012 fn pair_slot_strat() -> impl proptest::strategy::Strategy<Value = Slot> {
1016 use proptest::strategy::{Just, Strategy};
1017 proptest::prop_oneof![
1018 Just(Slot::Plain),
1019 (proptest::bool::ANY, 0usize..3).prop_map(|(matches, n)| Slot::Splice { matches, n }),
1020 ]
1021 }
1022
1023 fn kw_unit(i: usize) -> String {
1024 format!(":k{i}")
1025 }
1026
1027 fn binding_unit(i: usize) -> String {
1030 format!("x{i} {i}")
1031 }
1032
1033 proptest::proptest! {
1034 #![proptest_config(proptest::prelude::ProptestConfig::with_cases(48))]
1035
1036 #[test]
1042 fn prop_splice_agrees_across_eval_quote_and_syntax_quote(
1043 slots in proptest::collection::vec(slot_strat(), 0..5),
1044 ) {
1045 let (written, inlined) = render_slots(&slots, &kw_unit);
1046 let even = inlined.split_whitespace().count().is_multiple_of(2);
1047 for (open, close) in [("[", "]"), ("#{", "}"), ("{", "}")] {
1048 if open == "{" && !even {
1049 continue;
1050 }
1051 for prefix in ["", "'", "`"] {
1052 let got = eval_str(&format!("{prefix}{open}{written}{close}"));
1053 let want = eval_str(&format!("{prefix}{open}{inlined}{close}"));
1054 proptest::prop_assert_eq!(
1055 got.map_err(|e| e.to_string()),
1056 want.map_err(|e| e.to_string()),
1057 "prefix {:?} container {}{}", prefix, open, close
1058 );
1059 }
1060 }
1061 for prefix in ["'", "`"] {
1063 let got = eval_str(&format!("{prefix}({written})"));
1064 let want = eval_str(&format!("{prefix}({inlined})"));
1065 proptest::prop_assert_eq!(
1066 got.map_err(|e| e.to_string()),
1067 want.map_err(|e| e.to_string()),
1068 "prefix {:?} list", prefix
1069 );
1070 }
1071 }
1072
1073 #[test]
1076 fn prop_splice_in_binding_vectors_equals_inline(
1077 slots in proptest::collection::vec(pair_slot_strat(), 0..4),
1078 ) {
1079 let (written, inlined) = render_slots(&slots, &binding_unit);
1080 let names: Vec<String> = inlined
1081 .split_whitespace()
1082 .step_by(2)
1083 .map(str::to_string)
1084 .collect();
1085 let body = format!("[{}]", names.join(" "));
1086 for head in ["let*", "loop*"] {
1087 let got = eval_str(&format!("({head} [{written}] {body})"));
1088 let want = eval_str(&format!("({head} [{inlined}] {body})"));
1089 proptest::prop_assert_eq!(
1090 got.map_err(|e| e.to_string()),
1091 want.map_err(|e| e.to_string()),
1092 "{}", head
1093 );
1094 }
1095 }
1096 }
1097
1098 #[test]
1099 fn quoted_map_splice_lands_in_key_value_positions() {
1100 assert_eq!(
1101 eval_str("'{:a 1 #?@(:rust [:b 2]) :c 3}").unwrap(),
1102 eval_str("{:a 1 :b 2 :c 3}").unwrap()
1103 );
1104 }
1105
1106 #[test]
1107 fn syntax_quoted_splice_is_expanded_not_nil() {
1108 assert_eq!(
1109 eval_str("`(1 #?@(:rust [2 3]) 4)").unwrap(),
1110 eval_str("'(1 2 3 4)").unwrap()
1111 );
1112 }
1113
1114 #[test]
1115 fn map_literal_with_only_an_unmatched_conditional_reads_as_empty() {
1116 assert_eq!(eval_str("{#?(:clj :a)}").unwrap(), eval_str("{}").unwrap());
1119 }
1120
1121 #[test]
1122 fn loop_binding_vector_expands_splices() {
1123 assert_eq!(eval_str("(loop* [#?@(:rust [x 1])] x)").unwrap(), long(1));
1124 }
1125
1126 #[test]
1129 fn test_unbound_symbol() {
1130 let r = eval_str("undefined-var-xyz");
1131 assert!(matches!(r, Err(EvalError::UnboundSymbol(_))));
1132 }
1133
1134 #[test]
1135 fn test_wrong_arity() {
1136 let (_, mut env) = make_env();
1137 eval_src("(defn one-arg [x] x)", &mut env).unwrap();
1138 let r = eval_src("(one-arg 1 2)", &mut env);
1139 assert!(matches!(r, Err(EvalError::Arity { .. })));
1140 }
1141
1142 #[test]
1143 fn test_not_callable() {
1144 let r = eval_str("(42 1 2)");
1145 assert!(matches!(r, Err(EvalError::NotCallable(_))));
1146 }
1147
1148 #[test]
1151 fn test_map_fn() {
1152 assert_eq!(
1153 eval_str("(vec (map inc [1 2 3]))").unwrap(),
1154 eval_str("[2 3 4]").unwrap()
1155 );
1156 }
1157
1158 #[test]
1159 fn test_filter_fn() {
1160 assert_eq!(
1161 eval_str("(vec (filter odd? [1 2 3 4 5]))").unwrap(),
1162 eval_str("[1 3 5]").unwrap()
1163 );
1164 }
1165
1166 #[test]
1167 fn test_reduce_fn() {
1168 assert_eq!(eval_str("(reduce + [1 2 3 4 5])").unwrap(), long(15));
1169 }
1170
1171 #[test]
1172 fn test_apply_fn() {
1173 assert_eq!(eval_str("(apply + [1 2 3])").unwrap(), long(6));
1174 }
1175
1176 #[test]
1177 fn test_atom_ops() {
1178 let (_, mut env) = make_env();
1179 eval_src("(def a (atom 0))", &mut env).unwrap();
1180 eval_src("(swap! a inc)", &mut env).unwrap();
1181 assert_eq!(eval_src("(deref a)", &mut env).unwrap(), long(1));
1182 }
1183
1184 #[test]
1185 fn test_when_macro() {
1186 assert_eq!(eval_str("(when true 42)").unwrap(), long(42));
1187 assert_eq!(eval_str("(when false 42)").unwrap(), Value::Nil);
1188 }
1189
1190 #[test]
1191 fn test_cond_macro() {
1192 assert_eq!(eval_str("(cond false 1 true 2)").unwrap(), long(2));
1193 }
1194
1195 #[test]
1196 fn test_and_or() {
1197 assert_eq!(eval_str("(and 1 2 3)").unwrap(), long(3));
1198 assert_eq!(eval_str("(and 1 false 3)").unwrap(), bool_v(false));
1199 assert_eq!(eval_str("(or false nil 42)").unwrap(), long(42));
1200 assert_eq!(eval_str("(or false nil)").unwrap(), Value::Nil);
1201 }
1202
1203 #[test]
1206 fn test_lazy_range() {
1207 assert_eq!(
1208 eval_str("(= (into [] (take 5 (range))) [0 1 2 3 4])").unwrap(),
1209 bool_v(true)
1210 );
1211 }
1212
1213 #[test]
1214 fn test_lazy_range_bounded() {
1215 assert_eq!(
1216 eval_str("(= (into [] (range 3)) [0 1 2])").unwrap(),
1217 bool_v(true)
1218 );
1219 }
1220
1221 #[test]
1222 fn test_lazy_iterate() {
1223 assert_eq!(
1224 eval_str("(= (into [] (take 3 (iterate inc 0))) [0 1 2])").unwrap(),
1225 bool_v(true)
1226 );
1227 }
1228
1229 #[test]
1230 fn test_lazy_repeat() {
1231 assert_eq!(
1232 eval_str("(= (into [] (take 3 (repeat :x))) [:x :x :x])").unwrap(),
1233 bool_v(true)
1234 );
1235 }
1236
1237 #[test]
1238 fn test_lazy_cycle() {
1239 assert_eq!(
1240 eval_str("(= (into [] (take 5 (cycle [1 2]))) [1 2 1 2 1])").unwrap(),
1241 bool_v(true)
1242 );
1243 }
1244
1245 #[test]
1248 fn test_assoc_destructure() {
1249 assert_eq!(
1250 eval_str("(let [{:keys [a b]} {:a 1 :b 2}] (+ a b))").unwrap(),
1251 long(3)
1252 );
1253 }
1254
1255 #[test]
1256 fn test_assoc_destructure_or() {
1257 assert_eq!(
1258 eval_str("(let [{:keys [a b] :or {b 99}} {:a 1}] b)").unwrap(),
1259 long(99)
1260 );
1261 }
1262
1263 #[test]
1266 fn test_letfn() {
1267 assert_eq!(
1268 eval_str("(letfn [(fact [n] (if (= n 0) 1 (* n (fact (dec n)))))] (fact 5))").unwrap(),
1269 long(120)
1270 );
1271 }
1272
1273 #[test]
1276 fn test_in_ns() {
1277 let (_, mut env) = make_env();
1278 eval_src("(in-ns 'mytest)", &mut env).unwrap();
1279 assert_eq!(env.current_ns.as_ref(), "mytest");
1280 eval_src("(in-ns 'user)", &mut env).unwrap();
1281 assert_eq!(env.current_ns.as_ref(), "user");
1282 }
1283
1284 #[test]
1287 fn test_spit_slurp() {
1288 let path = std::env::temp_dir().join("cljrs_test_spit_slurp.txt");
1289 let path_str = path.to_str().unwrap();
1290 let src = format!(
1291 r#"(do (spit "{}" "hello clojurust") (slurp "{}"))"#,
1292 path_str, path_str
1293 );
1294 let result = eval_str(&src).unwrap();
1295 if let Value::Str(s) = result {
1296 assert_eq!(s.get().as_str(), "hello clojurust");
1297 } else {
1298 panic!("expected string result from slurp");
1299 }
1300 let _ = std::fs::remove_file(path);
1301 }
1302
1303 #[test]
1306 fn test_update_in() {
1307 assert_eq!(
1308 eval_str("(= (update-in {:a {:b 1}} [:a :b] inc) {:a {:b 2}})").unwrap(),
1309 bool_v(true)
1310 );
1311 }
1312
1313 #[test]
1316 fn test_if_let_truthy() {
1317 assert_eq!(eval_str("(if-let [x 42] x :nope)").unwrap(), long(42));
1318 }
1319
1320 #[test]
1321 fn test_if_let_falsy() {
1322 assert_eq!(
1323 eval_str("(if-let [x nil] x :nope)").unwrap(),
1324 eval_str(":nope").unwrap()
1325 );
1326 }
1327
1328 #[test]
1329 fn test_when_let_truthy() {
1330 assert_eq!(eval_str("(when-let [x 7] (* x 2))").unwrap(), long(14));
1331 }
1332
1333 #[test]
1334 fn test_when_let_falsy() {
1335 assert_eq!(eval_str("(when-let [x nil] 99)").unwrap(), Value::Nil);
1336 }
1337
1338 #[test]
1341 fn test_math_trig() {
1342 assert_eq!(eval_str("(Math/sin 0)").unwrap(), Value::Double(0.0));
1344 assert_eq!(eval_str("(Math/cos 0)").unwrap(), Value::Double(1.0));
1345 }
1346
1347 #[test]
1348 fn test_math_constants() {
1349 assert!(
1350 matches!(eval_str("Math/PI").unwrap(), Value::Double(v) if (v - std::f64::consts::PI).abs() < 1e-10)
1351 );
1352 assert!(
1353 matches!(eval_str("Math/E").unwrap(), Value::Double(v) if (v - std::f64::consts::E).abs() < 1e-10)
1354 );
1355 }
1356
1357 #[test]
1358 fn test_math_log_exp() {
1359 assert_eq!(eval_str("(Math/exp 0)").unwrap(), Value::Double(1.0));
1361 assert_eq!(eval_str("(Math/log 1)").unwrap(), Value::Double(0.0));
1362 }
1363
1364 #[test]
1367 fn test_defprotocol() {
1368 let result = eval_str(
1370 r#"
1371 (defprotocol Greet
1372 (greet [this]))
1373 (greet "hello")
1374 "#,
1375 );
1376 assert!(result.is_err());
1377 let msg = result.unwrap_err().to_string();
1378 assert!(msg.contains("No implementation"), "got: {msg}");
1379 }
1380
1381 #[test]
1382 fn test_extend_type() {
1383 let result = eval_str(
1384 r#"
1385 (defprotocol Greet
1386 (greet [this]))
1387 (extend-type String
1388 Greet
1389 (greet [this] (str "Hello, " this "!")))
1390 (greet "world")
1391 "#,
1392 )
1393 .unwrap();
1394 assert_eq!(result, Value::string("Hello, world!"));
1395 }
1396
1397 #[test]
1398 fn test_protocol_dispatch() {
1399 let result = eval_str(
1400 r#"
1401 (defprotocol Describable
1402 (describe [this]))
1403 (extend-type String
1404 Describable
1405 (describe [this] (str "string:" this)))
1406 (extend-type Long
1407 Describable
1408 (describe [this] (str "long:" this)))
1409 [(describe "hi") (describe 42)]
1410 "#,
1411 )
1412 .unwrap();
1413 assert!(matches!(result, Value::Vector(_)));
1414 let s = format!("{}", result);
1415 assert!(s.contains("string:hi"), "got: {s}");
1416 assert!(s.contains("long:42"), "got: {s}");
1417 }
1418
1419 #[test]
1420 fn test_extend_protocol() {
1421 let result = eval_str(
1422 r#"
1423 (defprotocol Showable
1424 (show [this]))
1425 (extend-protocol Showable
1426 String
1427 (show [this] (str "S:" this))
1428 Long
1429 (show [this] (str "L:" this)))
1430 [(show "x") (show 7)]
1431 "#,
1432 )
1433 .unwrap();
1434 let s = format!("{}", result);
1435 assert!(s.contains("S:x"), "got: {s}");
1436 assert!(s.contains("L:7"), "got: {s}");
1437 }
1438
1439 #[test]
1440 fn test_extend_via_metadata() {
1441 let result = eval_str(
1448 r#"
1449 (defprotocol IRender
1450 :extend-via-metadata true
1451 (create-element [this tag-name]))
1452 (def renderer (with-meta {} {`create-element (fn [this tag-name] (str "made-" tag-name))}))
1453 (create-element renderer "div")
1454 "#,
1455 )
1456 .unwrap();
1457 assert_eq!(result, Value::string("made-div"));
1458 }
1459
1460 #[test]
1461 fn test_extend_via_metadata_falls_back_to_type_tag() {
1462 let result = eval_str(
1465 r#"
1466 (defprotocol IRender
1467 :extend-via-metadata true
1468 (create-element [this tag-name]))
1469 (extend-type Map
1470 IRender
1471 (create-element [this tag-name] (str "type-tag-" tag-name)))
1472 [(create-element {} "span")
1473 (create-element (with-meta {} {`create-element (fn [this tag-name] (str "meta-" tag-name))}) "div")]
1474 "#,
1475 )
1476 .unwrap();
1477 let s = format!("{}", result);
1478 assert!(s.contains("type-tag-span"), "got: {s}");
1479 assert!(s.contains("meta-div"), "got: {s}");
1480 }
1481
1482 #[test]
1483 fn test_extend_via_metadata_cross_ns() {
1484 let dir = temp_ns_dir("extend_via_metadata_cross_ns");
1491 std::fs::create_dir_all(dir.join("replicant")).unwrap();
1492 std::fs::write(
1493 dir.join("replicant").join("core.cljrs"),
1494 r#"(ns replicant.core)
1495 (defprotocol IRender
1496 :extend-via-metadata true
1497 (create-element [this tag-name]))"#,
1498 )
1499 .unwrap();
1500 let (_, mut env) = make_env_with_paths(vec![dir]);
1501 let result = eval_src(
1502 r#"
1503 (ns mutation-log-test
1504 (:require [replicant.core :refer [create-element]]))
1505 (def renderer (with-meta {} {`create-element (fn [this tag-name] (str "made-" tag-name))}))
1506 (create-element renderer "div")
1507 "#,
1508 &mut env,
1509 )
1510 .unwrap();
1511 assert_eq!(result, Value::string("made-div"));
1512 }
1513
1514 #[test]
1515 fn test_extend_via_metadata_cross_ns_via_alias() {
1516 let dir = temp_ns_dir("extend_via_metadata_cross_ns_via_alias");
1525 std::fs::create_dir_all(dir.join("replicant")).unwrap();
1526 std::fs::write(
1527 dir.join("replicant").join("protocols.cljrs"),
1528 r#"(ns replicant.protocols)
1529 (defprotocol IRender
1530 :extend-via-metadata true
1531 (attached? [this el]))"#,
1532 )
1533 .unwrap();
1534 let (_, mut env) = make_env_with_paths(vec![dir]);
1535 let result = eval_src(
1536 r#"
1537 (ns mutation-log-test
1538 (:require [replicant.protocols :as p]))
1539 (def r (with-meta {:log []} {`p/attached? (fn [_ el] el)}))
1540 (p/attached? r :el)
1541 "#,
1542 &mut env,
1543 )
1544 .unwrap();
1545 assert_eq!(result, Value::keyword(Keyword::simple("el")));
1546 }
1547
1548 #[test]
1549 fn test_satisfies() {
1550 let result = eval_str(
1551 r#"
1552 (defprotocol Animal
1553 (speak [this]))
1554 (extend-type String
1555 Animal
1556 (speak [this] this))
1557 [(satisfies? Animal "dog") (satisfies? Animal 42)]
1558 "#,
1559 )
1560 .unwrap();
1561 let s = format!("{}", result);
1562 assert!(s.contains("true"), "got: {s}");
1563 assert!(s.contains("false"), "got: {s}");
1564 }
1565
1566 #[test]
1567 fn test_defmulti_defmethod() {
1568 let result = eval_str(
1570 r#"
1571 (defmulti area :shape)
1572 (defmethod area :circle [m] (* 3 (:r m) (:r m)))
1573 (defmethod area :rectangle [m] (* (:w m) (:h m)))
1574 [(area {:shape :circle :r 2}) (area {:shape :rectangle :w 3 :h 4})]
1575 "#,
1576 )
1577 .unwrap();
1578 let s = format!("{}", result);
1579 assert!(s.contains("12"), "got: {s}");
1581 }
1582
1583 #[test]
1584 fn test_default_dispatch() {
1585 let result = eval_str(
1586 r#"
1587 (defmulti classify :kind)
1588 (defmethod classify :default [x] :unknown)
1589 (defmethod classify :cat [x] :meow)
1590 [(classify {:kind :dog}) (classify {:kind :cat})]
1591 "#,
1592 )
1593 .unwrap();
1594 let s = format!("{}", result);
1595 assert!(s.contains(":unknown"), "got: {s}");
1596 assert!(s.contains(":meow"), "got: {s}");
1597 }
1598
1599 #[test]
1600 fn test_prefer_method() {
1601 let result = eval_str(
1603 r#"
1604 (defmulti foo identity)
1605 (defmethod foo :a [x] 1)
1606 (prefer-method foo :a :b)
1607 (foo :a)
1608 "#,
1609 )
1610 .unwrap();
1611 assert_eq!(result, Value::Long(1));
1612 }
1613
1614 #[test]
1615 fn test_remove_method() {
1616 let result = eval_str(
1617 r#"
1618 (defmulti bar identity)
1619 (defmethod bar :x [_] 99)
1620 (remove-method bar :x)
1621 (bar :x)
1622 "#,
1623 );
1624 assert!(result.is_err());
1625 let msg = result.unwrap_err().to_string();
1626 assert!(msg.contains("No method"), "got: {msg}");
1627 }
1628
1629 #[test]
1632 fn test_compare_and_set() {
1633 let result = eval_str(
1634 r#"
1635 (let [a (atom 10)]
1636 [(compare-and-set! a 10 20) ; succeeds: 10 == 10
1637 (compare-and-set! a 10 30) ; fails: 20 != 10
1638 @a])
1639 "#,
1640 )
1641 .unwrap();
1642 let s = format!("{}", result);
1643 assert!(s.contains("true"), "got: {s}");
1644 assert!(s.contains("false"), "got: {s}");
1645 assert!(s.contains("20"), "got: {s}");
1646 }
1647
1648 #[test]
1649 fn test_volatile() {
1650 let result = eval_str(
1651 r#"
1652 (let [v (volatile! 1)]
1653 (vreset! v 2)
1654 (vswap! v + 10)
1655 @v)
1656 "#,
1657 )
1658 .unwrap();
1659 assert_eq!(result, Value::Long(12));
1660 }
1661
1662 #[test]
1663 fn test_delay() {
1664 let result = eval_str(
1666 r#"
1667 (let [calls (atom 0)
1668 d (delay (swap! calls inc) 42)]
1669 [@calls (force d) @calls (force d) @calls])
1670 "#,
1671 )
1672 .unwrap();
1673 let s = format!("{}", result);
1674 assert!(s.starts_with("[0 42 1 42 1]"), "got: {s}");
1677 }
1678
1679 #[test]
1680 fn test_realized() {
1681 let result = eval_str(
1682 r#"
1683 (let [d (delay 99)]
1684 [(realized? d) (force d) (realized? d)])
1685 "#,
1686 )
1687 .unwrap();
1688 let s = format!("{}", result);
1689 assert!(s.starts_with("[false 99 true]"), "got: {s}");
1690 }
1691
1692 #[test]
1693 fn test_promise() {
1694 let result = eval_str(
1695 r#"
1696 (let [p (promise)]
1697 (deliver p 42)
1698 (deliver p 99) ; second deliver is ignored
1699 @p)
1700 "#,
1701 )
1702 .unwrap();
1703 assert_eq!(result, Value::Long(42));
1704 }
1705
1706 #[test]
1707 #[ignore = "future/thread spawn not yet implemented (Phase A1 — GcPtr: !Send)"]
1708 fn test_future() {
1709 let result = eval_str(
1710 r#"
1711 (let [f (future (+ 1 2))]
1712 @f)
1713 "#,
1714 )
1715 .unwrap();
1716 assert_eq!(result, Value::Long(3));
1717 }
1718
1719 #[test]
1720 #[ignore = "agent not yet implemented (Phase A1 — GcPtr: !Send)"]
1721 fn test_agent_send() {
1722 let result = eval_str(
1723 r#"
1724 (let [a (agent 0)]
1725 (send a + 1)
1726 (send a + 2)
1727 (await-agent a)
1728 @a)
1729 "#,
1730 )
1731 .unwrap();
1732 assert_eq!(result, Value::Long(3));
1733 }
1734
1735 #[test]
1736 #[ignore = "agent not yet implemented (Phase A1 — GcPtr: !Send)"]
1737 fn test_agent_error_restart() {
1738 let result = eval_str(
1739 r#"
1740 (let [a (agent 10)]
1741 (send a (fn [_] (throw (ex-info "boom" {}))))
1742 (await-agent a)
1743 (let [err (agent-error a)]
1744 (restart-agent a 99)
1745 [err @a]))
1746 "#,
1747 )
1748 .unwrap();
1749 let s = format!("{}", result);
1750 assert!(s.contains("boom"), "got: {s}");
1752 assert!(s.contains("99"), "got: {s}");
1753 }
1754
1755 #[test]
1756 fn test_defrecord_basic() {
1757 let result = eval_str(
1759 r#"
1760 (defrecord Point [x y])
1761 (let [p (->Point 3 4)]
1762 [(:x p) (:y p)])
1763 "#,
1764 )
1765 .unwrap();
1766 assert_eq!(result.to_string(), "[3 4]");
1767 }
1768
1769 #[test]
1770 fn test_defrecord_map_constructor() {
1771 let result = eval_str(
1772 r#"
1773 (defrecord Color [r g b])
1774 (let [c (map->Color {:r 255 :g 128 :b 0})]
1775 [(:r c) (:g c) (:b c)])
1776 "#,
1777 )
1778 .unwrap();
1779 assert_eq!(result.to_string(), "[255 128 0]");
1780 }
1781
1782 #[test]
1783 fn test_defrecord_assoc() {
1784 let result = eval_str(
1786 r#"
1787 (defrecord Pt [x y])
1788 (let [p (->Pt 1 2)
1789 q (assoc p :x 99)]
1790 [(:x q) (:y q) (record? q)])
1791 "#,
1792 )
1793 .unwrap();
1794 assert_eq!(result.to_string(), "[99 2 true]");
1795 }
1796
1797 #[test]
1798 fn test_defrecord_with_protocol() {
1799 let result = eval_str(
1800 r#"
1801 (defprotocol IShape
1802 (area [this]))
1803 (defrecord Circle [radius]
1804 IShape
1805 (area [this] (* 3 (:radius this) (:radius this))))
1806 (let [c (->Circle 5)]
1807 (area c))
1808 "#,
1809 )
1810 .unwrap();
1811 assert_eq!(result, cljrs_value::Value::Long(75));
1812 }
1813
1814 #[test]
1815 fn test_instance_q() {
1816 let result = eval_str(
1817 r#"
1818 (defrecord Dog [name])
1819 (let [d (->Dog "Rex")]
1820 [(instance? Dog d) (instance? Dog 42)])
1821 "#,
1822 )
1823 .unwrap();
1824 assert_eq!(result.to_string(), "[true false]");
1825 }
1826
1827 #[test]
1828 fn test_reify_basic() {
1829 let result = eval_str(
1830 r#"
1831 (defprotocol IGreet
1832 (greet [this name]))
1833 (let [greeter (reify IGreet
1834 (greet [this name] (str "Hello, " name "!")))]
1835 (greet greeter "World"))
1836 "#,
1837 )
1838 .unwrap();
1839 assert_eq!(result.to_string(), "\"Hello, World!\"");
1840 }
1841
1842 fn temp_ns_dir(test_name: &str) -> std::path::PathBuf {
1845 let dir = std::env::temp_dir().join(format!("cljrs_test_{test_name}"));
1846 let _ = std::fs::remove_dir_all(&dir);
1847 std::fs::create_dir_all(&dir).unwrap();
1848 dir
1849 }
1850
1851 fn make_env_with_paths(paths: Vec<std::path::PathBuf>) -> (Arc<GlobalEnv>, Env) {
1852 let globals = crate::Runtime::builder()
1853 .execution_mode(crate::ExecutionMode::TreeWalk)
1854 .eager_clojure_test(true)
1855 .source_paths(paths)
1856 .build()
1857 .expect("runtime")
1858 .into_globals();
1859 let env = Env::new(globals.clone(), "user");
1860 (globals, env)
1861 }
1862
1863 #[test]
1864 fn test_require_as() {
1865 let dir = temp_ns_dir("require_as");
1866 std::fs::write(
1867 dir.join("mylib.cljrs"),
1868 "(ns mylib) (defn greet [n] (str \"hello \" n))",
1869 )
1870 .unwrap();
1871 let (_, mut env) = make_env_with_paths(vec![dir]);
1872 let result = eval_src("(require '[mylib :as ml]) (ml/greet \"world\")", &mut env).unwrap();
1873 assert_eq!(result.to_string(), "\"hello world\"");
1874 }
1875
1876 #[test]
1877 fn test_require_refer() {
1878 let dir = temp_ns_dir("require_refer");
1879 std::fs::write(
1880 dir.join("myutil.cljrs"),
1881 "(ns myutil) (defn twice [x] (* 2 x))",
1882 )
1883 .unwrap();
1884 let (_, mut env) = make_env_with_paths(vec![dir]);
1885 let result = eval_src("(require '[myutil :refer [twice]]) (twice 21)", &mut env).unwrap();
1886 assert_eq!(result, Value::Long(42));
1887 }
1888
1889 #[test]
1890 fn test_require_refer_all() {
1891 let dir = temp_ns_dir("require_refer_all");
1892 std::fs::write(
1893 dir.join("mymath.cljrs"),
1894 "(ns mymath) (defn square [x] (* x x))",
1895 )
1896 .unwrap();
1897 let (_, mut env) = make_env_with_paths(vec![dir]);
1898 let result = eval_src("(require '[mymath :refer :all]) (square 7)", &mut env).unwrap();
1899 assert_eq!(result, Value::Long(49));
1900 }
1901
1902 #[test]
1903 fn test_ns_require_clause() {
1904 let dir = temp_ns_dir("ns_require");
1905 std::fs::write(
1906 dir.join("greeter.cljrs"),
1907 "(ns greeter) (defn hi [n] (str \"Hi \" n))",
1908 )
1909 .unwrap();
1910 let (_, mut env) = make_env_with_paths(vec![dir]);
1911 let result = eval_src(
1912 "(ns myapp (:require [greeter :as g])) (g/hi \"Alice\")",
1913 &mut env,
1914 )
1915 .unwrap();
1916 assert_eq!(result.to_string(), "\"Hi Alice\"");
1917 }
1918
1919 #[test]
1920 fn test_var_quote_alias_resolution() {
1921 let dir = temp_ns_dir("var_quote_alias");
1924 std::fs::create_dir_all(dir.join("lib")).unwrap();
1926 std::fs::write(
1927 dir.join("lib/core.cljrs"),
1928 "(ns lib.core) (defn public [x] (* x 2))",
1929 )
1930 .unwrap();
1931 let (_, mut env) = make_env_with_paths(vec![dir]);
1932 let call_result = eval_src("(require '[lib.core :as l]) (l/public 21)", &mut env).unwrap();
1934 assert_eq!(call_result, Value::Long(42));
1935 let var_result = eval_src("#'l/public", &mut env).unwrap();
1937 assert!(
1938 matches!(var_result, Value::Var(_)),
1939 "expected Var, got {var_result:?}"
1940 );
1941 assert_eq!(var_result.to_string(), "#'lib.core/public");
1942 let var_special = eval_src("(var l/public)", &mut env).unwrap();
1944 assert_eq!(var_special.to_string(), "#'lib.core/public");
1945 }
1946
1947 #[test]
1948 fn test_require_idempotent() {
1949 let dir = temp_ns_dir("require_idempotent");
1950 std::fs::write(
1952 dir.join("counter.cljrs"),
1953 "(ns counter) (def loaded-count (atom 0)) (swap! loaded-count inc)",
1954 )
1955 .unwrap();
1956 let (globals, mut env) = make_env_with_paths(vec![dir]);
1957 eval_src("(require 'counter)", &mut env).unwrap();
1958 eval_src("(require 'counter)", &mut env).unwrap();
1959 let count = globals.lookup_in_ns("counter", "loaded-count").unwrap();
1961 if let Value::Atom(a) = count {
1962 assert_eq!(a.get().deref(), Value::Long(1));
1963 } else {
1964 panic!("expected atom");
1965 }
1966 }
1967
1968 #[test]
1969 fn test_require_not_found() {
1970 let (_, mut env) = make_env_with_paths(vec![]);
1971 let err = eval_src("(require 'nonexistent.ns)", &mut env).unwrap_err();
1972 let msg = format!("{err:?}");
1973 assert!(msg.contains("nonexistent.ns"), "unexpected error: {msg}");
1974 }
1975
1976 #[test]
1977 fn test_require_circular() {
1978 let dir = temp_ns_dir("require_circular");
1979 std::fs::write(dir.join("cira.cljrs"), "(ns cira (:require [cirb]))").unwrap();
1981 std::fs::write(dir.join("cirb.cljrs"), "(ns cirb (:require [cira]))").unwrap();
1982 let (_, mut env) = make_env_with_paths(vec![dir]);
1983 let err = eval_src("(require 'cira)", &mut env).unwrap_err();
1984 let msg = format!("{err:?}");
1985 assert!(
1986 msg.contains("circular"),
1987 "expected circular error, got: {msg}"
1988 );
1989 }
1990
1991 #[test]
1992 fn test_load_file() {
1993 let dir = temp_ns_dir("load_file");
1994 let path = dir.join("script.cljrs");
1995 std::fs::write(&path, "(+ 1 2)").unwrap();
1996 let (_, mut env) = make_env_with_paths(vec![]);
1997 let result = eval_src(&format!("(load-file \"{}\")", path.display()), &mut env).unwrap();
1998 assert_eq!(result, Value::Long(3));
1999 }
2000
2001 #[test]
2004 fn test_star_ns_initial() {
2005 let (_, mut env) = make_env();
2007 let v = eval_src("*ns*", &mut env).unwrap();
2008 match v {
2009 Value::Namespace(ns) => assert_eq!(ns.get().name.as_ref(), "user"),
2010 other => panic!("expected Namespace, got {other:?}"),
2011 }
2012 }
2013
2014 #[test]
2015 fn test_star_ns_after_in_ns() {
2016 let (_, mut env) = make_env();
2017 eval_src("(in-ns 'myns)", &mut env).unwrap();
2018 let v = eval_src("*ns*", &mut env).unwrap();
2019 match v {
2020 Value::Namespace(ns) => assert_eq!(ns.get().name.as_ref(), "myns"),
2021 other => panic!("expected Namespace, got {other:?}"),
2022 }
2023 }
2024
2025 #[test]
2026 fn test_star_ns_after_ns_form() {
2027 let (_, mut env) = make_env();
2028 eval_src("(ns mytest.ns)", &mut env).unwrap();
2029 let v = eval_src("*ns*", &mut env).unwrap();
2030 match v {
2031 Value::Namespace(ns) => assert_eq!(ns.get().name.as_ref(), "mytest.ns"),
2032 other => panic!("expected Namespace, got {other:?}"),
2033 }
2034 }
2035
2036 #[test]
2037 fn test_ns_name() {
2038 let (_, mut env) = make_env();
2039 let v = eval_src("(ns-name *ns*)", &mut env).unwrap();
2040 match v {
2041 Value::Symbol(s) => assert_eq!(s.get().name.as_ref(), "user"),
2042 other => panic!("expected Symbol, got {other:?}"),
2043 }
2044 }
2045
2046 #[test]
2047 fn test_find_ns() {
2048 let (_, mut env) = make_env();
2049 let v = eval_src("(find-ns 'user)", &mut env).unwrap();
2051 assert!(matches!(v, Value::Namespace(_)));
2052 let v2 = eval_src("(find-ns 'nonexistent)", &mut env).unwrap();
2054 assert_eq!(v2, Value::Nil);
2055 }
2056
2057 #[test]
2058 fn test_all_ns() {
2059 let (_, mut env) = make_env();
2060 let v = eval_src("(all-ns)", &mut env).unwrap();
2061 let names: Vec<String> = match &v {
2063 Value::List(l) => l
2064 .get()
2065 .iter()
2066 .filter_map(|ns| match ns {
2067 Value::Namespace(n) => Some(n.get().name.as_ref().to_string()),
2068 _ => None,
2069 })
2070 .collect(),
2071 other => panic!("expected list, got {other:?}"),
2072 };
2073 assert!(names.contains(&"user".to_string()));
2074 assert!(names.contains(&"clojure.core".to_string()));
2075 }
2076
2077 #[test]
2078 fn test_ns_interns() {
2079 let (_, mut env) = make_env();
2080 eval_src("(def my-test-var 42)", &mut env).unwrap();
2081 let v = eval_src("(ns-interns *ns*)", &mut env).unwrap();
2082 let Value::Map(m) = v else {
2083 panic!("expected map")
2084 };
2085 let sym = Value::symbol(cljrs_value::Symbol::simple("my-test-var"));
2087 assert!(m.get(&sym).is_some());
2088 }
2089
2090 #[test]
2091 fn test_create_ns() {
2092 let (_, mut env) = make_env();
2093 let v = eval_src("(create-ns 'fresh.ns)", &mut env).unwrap();
2094 match v {
2095 Value::Namespace(ns) => assert_eq!(ns.get().name.as_ref(), "fresh.ns"),
2096 other => panic!("expected Namespace, got {other:?}"),
2097 }
2098 let v2 = eval_src("(find-ns 'fresh.ns)", &mut env).unwrap();
2100 assert!(matches!(v2, Value::Namespace(_)));
2101 }
2102
2103 #[test]
2106 fn test_dynamic_var_basic() {
2107 let (globals, mut env) = make_env();
2108 let result = eval_src("(def ^:dynamic *x* 10) (binding [*x* 42] *x*)", &mut env).unwrap();
2109 assert_eq!(result, Value::Long(42));
2110 let root = globals.lookup_in_ns("user", "*x*");
2112 assert_eq!(root, Some(Value::Long(10)));
2113 }
2114
2115 #[test]
2116 fn test_dynamic_var_restore() {
2117 let (_globals, mut env) = make_env();
2118 eval_src("(def ^:dynamic *x* 10)", &mut env).unwrap();
2119 eval_src("(binding [*x* 42] *x*)", &mut env).unwrap();
2120 let val = eval_src("*x*", &mut env).unwrap();
2122 assert_eq!(val, Value::Long(10));
2123 }
2124
2125 #[test]
2126 fn test_dynamic_var_nested() {
2127 let (_, mut env) = make_env();
2128 eval_src("(def ^:dynamic *x* 1)", &mut env).unwrap();
2129 let result = eval_src("(binding [*x* 2] (binding [*x* 3] *x*))", &mut env).unwrap();
2130 assert_eq!(result, Value::Long(3));
2131 let val = eval_src("*x*", &mut env).unwrap();
2133 assert_eq!(val, Value::Long(1));
2134 }
2135
2136 #[test]
2137 fn test_dynamic_var_unaffected() {
2138 let (_, mut env) = make_env();
2139 eval_src("(def ^:dynamic *x* 10)", &mut env).unwrap();
2140 eval_src("(def y 99)", &mut env).unwrap();
2141 eval_src("(binding [*x* 42] *x*)", &mut env).unwrap();
2142 let val = eval_src("y", &mut env).unwrap();
2144 assert_eq!(val, Value::Long(99));
2145 }
2146
2147 #[test]
2148 #[ignore = "future/thread spawn not yet implemented (Phase A1 — GcPtr: !Send)"]
2149 fn test_binding_conveyance() {
2150 let (_, mut env) = make_env();
2151 eval_src("(def ^:dynamic *x* 10)", &mut env).unwrap();
2152 let result = eval_src("(binding [*x* 42] @(future *x*))", &mut env).unwrap();
2153 assert_eq!(result, Value::Long(42));
2154 }
2155
2156 #[test]
2157 fn test_var_set_in_binding() {
2158 let (_, mut env) = make_env();
2159 eval_src("(def ^:dynamic *x* 10)", &mut env).unwrap();
2160 let inside = eval_src("(binding [*x* 1] (set! *x* 2) *x*)", &mut env).unwrap();
2162 assert_eq!(inside, Value::Long(2));
2163 let root = eval_src("*x*", &mut env).unwrap();
2165 assert_eq!(root, Value::Long(10));
2166 }
2167
2168 #[test]
2169 fn test_with_bindings_star() {
2170 let (_, mut env) = make_env();
2171 eval_src("(def ^:dynamic *x* 10)", &mut env).unwrap();
2172 let result = eval_src("(with-bindings* {#'*x* 99} (fn [] *x*))", &mut env).unwrap();
2173 assert_eq!(result, Value::Long(99));
2174 }
2175
2176 #[test]
2177 fn test_binding_fully_qualified_cross_ns_dynamic_var() {
2178 let (_, mut env) = make_env();
2179 let result = eval_src(
2180 r#"
2181 (ns other.ns)
2182 (def ^:dynamic *dispatch* nil)
2183 (ns user)
2184 (binding [other.ns/*dispatch* (fn [x] x)]
2185 (other.ns/*dispatch* 42))
2186 "#,
2187 &mut env,
2188 )
2189 .unwrap();
2190 assert_eq!(result, Value::Long(42));
2191 }
2192
2193 #[test]
2194 fn test_binding_aliased_cross_ns_dynamic_var() {
2195 let dir = temp_ns_dir("binding_aliased_cross_ns_dynamic_var");
2201 std::fs::create_dir_all(dir.join("replicant")).unwrap();
2202 std::fs::write(
2203 dir.join("replicant").join("core.cljrs"),
2204 r#"(ns replicant.core)
2205 (def ^:dynamic *dispatch* nil)
2206 (defn call-dispatch [x] (*dispatch* x))"#,
2207 )
2208 .unwrap();
2209 let (_, mut env) = make_env_with_paths(vec![dir]);
2210 let result = eval_src(
2211 r#"
2212 (ns life-cycle-test
2213 (:require [replicant.core :as r]))
2214 (binding [r/*dispatch* (fn [x] x)]
2215 (r/call-dispatch 42))
2216 "#,
2217 &mut env,
2218 )
2219 .unwrap();
2220 assert_eq!(result, Value::Long(42));
2221 }
2222
2223 #[test]
2224 fn test_meta_on_var() {
2225 let (_, mut env) = make_env();
2226 eval_src("(def ^:dynamic *x* 1)", &mut env).unwrap();
2227 let m = eval_src("(meta #'*x*)", &mut env).unwrap();
2228 if let Value::Map(mv) = &m {
2230 let kw = Value::keyword(cljrs_value::Keyword::parse("dynamic"));
2231 assert_eq!(mv.get(&kw), Some(Value::Bool(true)));
2232 } else {
2233 panic!("expected map, got {m:?}");
2234 }
2235 }
2236
2237 #[test]
2238 fn test_bound_pred() {
2239 let (_, mut env) = make_env();
2240 eval_src("(def ^:dynamic *x* 1)", &mut env).unwrap();
2241 let t = eval_src("(bound? #'*x*)", &mut env).unwrap();
2242 assert_eq!(t, Value::Bool(true));
2243 }
2244
2245 #[test]
2246 fn test_alter_var_root() {
2247 let (_, mut env) = make_env();
2248 eval_src("(def x 1)", &mut env).unwrap();
2249 eval_src("(alter-var-root #'x inc)", &mut env).unwrap();
2250 let val = eval_src("x", &mut env).unwrap();
2251 assert_eq!(val, Value::Long(2));
2252 }
2253
2254 #[test]
2257 fn test_clojure_test_is_pass() {
2258 let (_, mut env) = make_env();
2260 eval_src(
2261 "(require '[clojure.test :refer [is deftest run-tests]])",
2262 &mut env,
2263 )
2264 .unwrap();
2265 let v = eval_src("(is (= 1 1))", &mut env).unwrap();
2266 assert_eq!(v, Value::Bool(true));
2267 }
2268
2269 #[test]
2270 fn test_clojure_test_is_fail() {
2271 let (_, mut env) = make_env();
2273 eval_src("(require '[clojure.test :refer [is]])", &mut env).unwrap();
2274 let v = eval_src("(is (= 1 2))", &mut env).unwrap();
2275 assert_eq!(v, Value::Bool(false));
2276 }
2277
2278 #[test]
2279 fn test_clojure_test_is_catch_error() {
2280 let (_, mut env) = make_env();
2282 eval_src("(require '[clojure.test :refer [is]])", &mut env).unwrap();
2283 let v = eval_src("(is (/ 1 0))", &mut env).unwrap();
2284 assert_eq!(v, Value::Bool(false));
2285 }
2286
2287 #[test]
2288 fn test_clojure_test_deftest_and_run() {
2289 let (_, mut env) = make_env();
2291 eval_src(
2292 "(require '[clojure.test :refer [deftest is run-tests]])",
2293 &mut env,
2294 )
2295 .unwrap();
2296 eval_src("(deftest my-passing-test (is (= 1 1)))", &mut env).unwrap();
2297 eval_src("(deftest my-failing-test (is (= 1 2)))", &mut env).unwrap();
2298 let counters = eval_src("(run-tests)", &mut env).unwrap();
2299 if let Value::Map(m) = counters {
2301 let get = |k: &str| {
2302 m.get(&Value::keyword(cljrs_value::Keyword {
2303 namespace: None,
2304 name: Arc::from(k),
2305 }))
2306 };
2307 assert_eq!(get("test"), Some(Value::Long(2)));
2308 assert_eq!(get("pass"), Some(Value::Long(1)));
2309 assert_eq!(get("fail"), Some(Value::Long(1)));
2310 assert_eq!(get("error"), Some(Value::Long(0)));
2311 } else {
2312 panic!("expected map from run-tests, got {counters:?}");
2313 }
2314 }
2315
2316 #[test]
2317 fn test_alter_meta_bang() {
2318 let (_, mut env) = make_env();
2320 eval_src("(def myvar 42)", &mut env).unwrap();
2321 eval_src("(alter-meta! #'myvar assoc :foo :bar)", &mut env).unwrap();
2322 let m = eval_src("(meta #'myvar)", &mut env).unwrap();
2323 if let Value::Map(map) = m {
2324 let foo_key = Value::keyword(cljrs_value::Keyword {
2325 namespace: None,
2326 name: Arc::from("foo"),
2327 });
2328 assert!(map.get(&foo_key).is_some());
2329 } else {
2330 panic!("expected map, got {m:?}");
2331 }
2332 }
2333
2334 #[test]
2335 fn test_catch_runtime_error() {
2336 let (_, mut env) = make_env();
2338 let v = eval_src(r#"(try (/ 1 0) (catch Exception e "caught"))"#, &mut env).unwrap();
2339 assert_eq!(v, Value::string("caught".to_string()));
2340 }
2341
2342 #[test]
2343 fn test_ns_resolve() {
2344 let (_, mut env) = make_env();
2345 eval_src("(def somevar 99)", &mut env).unwrap();
2346 let v = eval_src("(ns-resolve *ns* 'somevar)", &mut env).unwrap();
2348 assert!(matches!(v, Value::Var(_)));
2349 let v2 = eval_src("(ns-resolve *ns* 'nonexistent)", &mut env).unwrap();
2351 assert_eq!(v2, Value::Nil);
2352 }
2353
2354 #[test]
2357 fn test_assoc_chain_virtualized() {
2358 let v = eval_str(
2360 "(let [m {}
2361 a (assoc m :x 1)
2362 b (assoc a :y 2)
2363 c (assoc b :z 3)]
2364 c)",
2365 )
2366 .unwrap();
2367 assert!(matches!(&v, Value::Map(_)));
2369 if let Value::Map(m) = &v {
2370 assert_eq!(m.count(), 3);
2371 assert_eq!(m.get(&Value::keyword(Keyword::simple("x"))), Some(long(1)));
2372 assert_eq!(m.get(&Value::keyword(Keyword::simple("y"))), Some(long(2)));
2373 assert_eq!(m.get(&Value::keyword(Keyword::simple("z"))), Some(long(3)));
2374 }
2375 }
2376
2377 #[test]
2378 fn test_conj_chain_virtualized() {
2379 let v = eval_str(
2381 "(let [v [1]
2382 a (conj v 2)
2383 b (conj a 3)
2384 c (conj b 4)]
2385 c)",
2386 )
2387 .unwrap();
2388 assert_eq!(v, eval_str("[1 2 3 4]").unwrap());
2389 }
2390
2391 #[test]
2392 fn test_assoc_chain_intermediate_used_no_virtualize() {
2393 let v = eval_str(
2396 "(let [a (assoc {} :x 1)
2397 b (assoc a :y 2)]
2398 (list (count a) (count b)))",
2399 )
2400 .unwrap();
2401 if let Value::List(l) = &v {
2403 let items: Vec<_> = l.get().iter().cloned().collect();
2404 assert_eq!(items, vec![long(1), long(2)]);
2405 } else {
2406 panic!("expected list, got {:?}", v);
2407 }
2408 }
2409
2410 #[test]
2411 fn test_assoc_chain_on_existing_map() {
2412 let v = eval_str(
2414 "(let [m {:a 1}
2415 a (assoc m :b 2)
2416 b (assoc a :c 3)]
2417 b)",
2418 )
2419 .unwrap();
2420 if let Value::Map(m) = &v {
2421 assert_eq!(m.count(), 3);
2422 } else {
2423 panic!("expected map");
2424 }
2425 }
2426
2427 #[test]
2430 fn test_post_condition_percent_bound() {
2431 let v = eval_str("(defn g [x] {:post [(pos? %)]} (inc x)) (g 5)").unwrap();
2433 assert_eq!(v, long(6));
2434 }
2435
2436 #[test]
2437 fn test_post_condition_violation_throws() {
2438 let r = eval_str("(defn g [x] {:post [(neg? %)]} (inc x)) (g 5)");
2440 assert!(r.is_err(), "expected error from failing :post condition");
2441 }
2442
2443 #[test]
2444 fn test_pre_condition_passes() {
2445 let v = eval_str("(defn g [x] {:pre [(pos? x)]} (inc x)) (g 5)").unwrap();
2446 assert_eq!(v, long(6));
2447 }
2448
2449 #[test]
2450 fn test_pre_condition_violation_throws() {
2451 let r = eval_str("(defn g [x] {:pre [(pos? x)]} (inc x)) (g -1)");
2452 assert!(r.is_err(), "expected error from failing :pre condition");
2453 }
2454
2455 #[test]
2456 fn test_pre_and_post_conditions() {
2457 let v = eval_str("(defn g [x] {:pre [(pos? x)] :post [(> % x)]} (inc x)) (g 3)").unwrap();
2458 assert_eq!(v, long(4));
2459 }
2460
2461 #[test]
2462 fn test_post_condition_no_pre() {
2463 let v = eval_str("(defn h [x] {:post [(number? %)]} (inc x)) (h 2)").unwrap();
2465 assert_eq!(v, long(3));
2466 }
2467
2468 #[test]
2469 fn test_pre_condition_no_post() {
2470 let v = eval_str("(defn h [x] {:pre [(number? x)]} x) (h 42)").unwrap();
2472 assert_eq!(v, long(42));
2473 }
2474}