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