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::value::SetValue;
16use cljrs_value::{
17 FutureState, Keyword, MapValue, PersistentHashSet, PersistentList, PersistentVector, Symbol,
18 Value,
19};
20use regex::Regex;
21
22pub fn eval(form: &Form, env: &mut Env) -> EvalResult {
24 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 = Regex::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::interp::standard_env(None, None, None);
451 let env = Env::new(globals.clone(), "user");
452 (globals, env)
453 }
454
455 fn eval_str(src: &str) -> EvalResult {
456 let (_, mut env) = make_env();
457 eval_src(src, &mut env)
458 }
459
460 fn eval_src(src: &str, env: &mut Env) -> EvalResult {
461 let mut parser = cljrs_reader::Parser::new(src.to_string(), "<test>".to_string());
462 let forms = parser.parse_all().map_err(EvalError::Read)?;
463 let mut result = Value::Nil;
464 for form in forms {
465 result = eval(&form, env)?;
466 }
467 Ok(result)
468 }
469
470 fn long(n: i64) -> Value {
471 Value::Long(n)
472 }
473 fn bool_v(b: bool) -> Value {
474 Value::Bool(b)
475 }
476
477 #[test]
480 fn test_literal_int() {
481 assert_eq!(eval_str("42").unwrap(), long(42));
482 }
483
484 #[test]
485 fn test_literal_string() {
486 assert!(matches!(eval_str("\"hello\"").unwrap(), Value::Str(_)));
487 }
488
489 #[test]
490 fn test_literal_nil() {
491 assert_eq!(eval_str("nil").unwrap(), Value::Nil);
492 }
493
494 #[test]
495 fn test_literal_true() {
496 assert_eq!(eval_str("true").unwrap(), bool_v(true));
497 }
498
499 #[test]
500 fn test_literal_false() {
501 assert_eq!(eval_str("false").unwrap(), bool_v(false));
502 }
503
504 #[test]
507 fn test_add() {
508 assert_eq!(eval_str("(+ 1 2)").unwrap(), long(3));
509 }
510
511 #[test]
512 fn test_mul() {
513 assert_eq!(eval_str("(* 2 3)").unwrap(), long(6));
514 }
515
516 #[test]
517 fn test_div_exact() {
518 assert_eq!(eval_str("(/ 10 2)").unwrap(), long(5));
519 }
520
521 #[test]
522 fn test_sub() {
523 assert_eq!(eval_str("(- 10 3)").unwrap(), long(7));
524 }
525
526 #[test]
529 fn test_let_simple() {
530 assert_eq!(eval_str("(let* [x 1 y 2] (+ x y))").unwrap(), long(3));
531 }
532
533 #[test]
534 fn test_let_shadowing() {
535 assert_eq!(eval_str("(let* [x 1] (let* [x 10] x))").unwrap(), long(10));
536 }
537
538 #[test]
541 fn test_fn_call() {
542 assert_eq!(eval_str("((fn* [x] (* x x)) 5)").unwrap(), long(25));
543 }
544
545 #[test]
546 fn test_closure_capture() {
547 assert_eq!(
548 eval_str("(let* [n 3] ((fn* [x] (+ x n)) 4))").unwrap(),
549 long(7)
550 );
551 }
552
553 #[test]
554 fn test_multi_arity_fn() {
555 assert_eq!(
556 eval_str("((fn* ([x] x) ([x y] (+ x y))) 1 2)").unwrap(),
557 long(3)
558 );
559 }
560
561 #[test]
564 fn test_loop_recur() {
565 let result =
566 eval_str("(loop* [i 0 acc 0] (if (= i 5) acc (recur (inc i) (+ acc i))))").unwrap();
567 assert_eq!(result, long(10));
568 }
569
570 #[test]
573 fn test_def() {
574 let (_, mut env) = make_env();
575 eval_src("(def x 42)", &mut env).unwrap();
576 assert_eq!(eval_src("x", &mut env).unwrap(), long(42));
577 }
578
579 #[test]
580 fn test_defn() {
581 let (_, mut env) = make_env();
582 eval_src("(defn square [x] (* x x))", &mut env).unwrap();
583 assert_eq!(eval_src("(square 7)", &mut env).unwrap(), long(49));
584 }
585
586 #[test]
589 fn test_if_truthy() {
590 assert_eq!(eval_str("(if true 1 2)").unwrap(), long(1));
591 }
592
593 #[test]
594 fn test_if_falsy() {
595 assert_eq!(eval_str("(if false 1 2)").unwrap(), long(2));
596 }
597
598 #[test]
599 fn test_if_nil_branch() {
600 assert_eq!(eval_str("(if false 1)").unwrap(), Value::Nil);
601 }
602
603 #[test]
606 fn test_do() {
607 assert_eq!(eval_str("(do 1 2 3)").unwrap(), long(3));
608 }
609
610 #[test]
613 fn test_quote_list() {
614 let v = eval_str("'(1 2 3)").unwrap();
615 assert!(matches!(v, Value::List(_)));
616 }
617
618 #[test]
621 fn test_keyword_lookup() {
622 assert_eq!(eval_str("(:a {:a 1})").unwrap(), long(1));
623 }
624
625 #[test]
626 fn test_keyword_lookup_missing() {
627 assert_eq!(eval_str("(:b {:a 1})").unwrap(), Value::Nil);
628 }
629
630 #[test]
633 fn test_map_literal() {
634 let v = eval_str("{:a 1 :b 2}").unwrap();
635 assert!(matches!(v, Value::Map(_)));
636 if let Value::Map(m) = &v {
637 assert_eq!(m.count(), 2);
638 }
639 }
640
641 #[test]
642 fn test_vector_literal() {
643 let v = eval_str("[1 2 3]").unwrap();
644 assert!(matches!(v, Value::Vector(_)));
645 }
646
647 #[test]
648 fn test_set_literal() {
649 let v = eval_str("#{1 2 3}").unwrap();
650 assert!(matches!(v, Value::Set(_)));
651 }
652
653 #[test]
654 fn test_contains_q_vector_non_integer_key_returns_false() {
655 assert_eq!(eval_str("(contains? [1 2 3] :a)").unwrap(), bool_v(false));
658 assert_eq!(
659 eval_str("(contains? [1 2 3] \"x\")").unwrap(),
660 bool_v(false)
661 );
662 assert_eq!(eval_str("(contains? [1 2 3] 0)").unwrap(), bool_v(true));
664 assert_eq!(eval_str("(contains? [1 2 3] 9)").unwrap(), bool_v(false));
665 }
666
667 #[test]
670 fn test_set_bang() {
671 let (_, mut env) = make_env();
672 eval_src("(def x 1)", &mut env).unwrap();
673 eval_src("(set! x 99)", &mut env).unwrap();
674 assert_eq!(eval_src("x", &mut env).unwrap(), long(99));
675 }
676
677 #[test]
680 fn test_throw_catch() {
681 let v = eval_str("(try (throw (ex-info \"oops\" {})) (catch Exception e (ex-message e)))")
682 .unwrap();
683 assert!(matches!(v, Value::Str(_)));
684 }
685
686 #[test]
687 fn test_try_no_throw() {
688 assert_eq!(eval_str("(try 42)").unwrap(), long(42));
689 }
690
691 #[test]
692 fn test_catch_default_catches_internal_value_errors() {
693 for src in [
697 "(try (nth [1 2 3] 10) (catch :default e \"caught\"))",
698 "(try (aget (long-array 3) 10) (catch :default e \"caught\"))",
699 "(try (aset (long-array 3) 10 5) (catch :default e \"caught\"))",
700 ] {
701 let v = eval_str(src).unwrap();
702 assert_eq!(v, Value::string("caught"), "{src}");
703 }
704 }
705
706 #[test]
707 fn test_catch_default_binds_clean_ex_message() {
708 let v = eval_str("(try (nth [1 2 3] 10) (catch :default e (ex-message e)))").unwrap();
712 assert_eq!(v, Value::string("index out of bounds: 10 >= 3"));
713 }
714
715 #[test]
716 fn test_finally_runs_and_value_is_discarded() {
717 let v = eval_str("(let [a (atom 0)] (try 1 (finally (reset! a 5))) @a)").unwrap();
719 assert_eq!(v, long(5));
720 assert_eq!(eval_str("(try 1 (finally 2))").unwrap(), long(1));
721 }
722
723 #[test]
724 fn test_finally_runs_after_catch() {
725 let v = eval_str(
726 "(let [a (atom 0)]
727 (try (throw (ex-info \"x\" {})) (catch Exception e :caught)
728 (finally (reset! a 9)))
729 @a)",
730 )
731 .unwrap();
732 assert_eq!(v, long(9));
733 }
734
735 #[test]
736 fn test_finally_exception_propagates() {
737 assert!(eval_str("(try 1 (finally (throw (ex-info \"boom\" {}))))").is_err());
739 }
740
741 #[test]
742 fn test_catch_reader_conditional_type() {
743 let v =
749 eval_str(r#"(try (throw (ex-info "boom" {})) (catch #?(:rust Exception) e :caught))"#)
750 .unwrap();
751 let kw_caught = Value::keyword(cljrs_value::Keyword::simple("caught"));
752 assert_eq!(v, kw_caught, "single-branch reader cond");
753
754 let v = eval_str(concat!(
756 "(try (throw (ex-info \"boom\" {})) ",
757 "(catch #?(:clj Throwable :cljs :default :rust Exception) e :caught))",
758 ))
759 .unwrap();
760 assert_eq!(v, kw_caught, "multi-branch reader cond");
761
762 let v =
765 eval_str(r#"(try (throw (ex-info "boom" {})) (catch #?(:rust :default) e :caught))"#)
766 .unwrap();
767 assert_eq!(v, kw_caught, "reader cond resolves to :default");
768
769 let result =
771 eval_str(r#"(try (throw (ex-info "boom" {})) (catch #?(:clj Throwable) e :caught))"#);
772 assert!(
773 result.is_err(),
774 "no matching :rust branch must let exception escape"
775 );
776 }
777
778 #[test]
779 fn test_nth_negative_index() {
780 assert_eq!(
782 eval_str("(= (nth [10 20 30] -1 :nf) :nf)").unwrap(),
783 bool_v(true)
784 );
785 assert!(eval_str("(nth [10 20 30] -1)").is_err());
786 assert!(eval_str("(nth '(1 2 3) -1)").is_err());
787 assert_eq!(
789 eval_str("(= (nth (range) -1 :nf) :nf)").unwrap(),
790 bool_v(true)
791 );
792 assert_eq!(
793 eval_str("(= (nth '(1 2 3) -1 :nf) :nf)").unwrap(),
794 bool_v(true)
795 );
796 }
797
798 #[test]
801 fn test_sequential_destructure() {
802 assert_eq!(eval_str("(let* [[a b] [1 2]] (+ a b))").unwrap(), long(3));
803 }
804
805 #[test]
806 fn test_rest_destructure() {
807 let v = eval_str("(let* [[h & t] [1 2 3]] t)").unwrap();
808 assert!(matches!(v, Value::List(_)));
809 if let Value::List(l) = &v {
810 assert_eq!(l.get().count(), 2);
811 }
812 }
813
814 #[test]
817 fn test_defmacro() {
818 let (_, mut env) = make_env();
819 eval_src("(defmacro my-if [t a b] (list 'if t a b))", &mut env).unwrap();
820 assert_eq!(eval_src("(my-if true 1 2)", &mut env).unwrap(), long(1));
821 assert_eq!(eval_src("(my-if false 1 2)", &mut env).unwrap(), long(2));
822 }
823
824 #[test]
827 fn test_syntax_quote_basic() {
828 let (_, mut env) = make_env();
829 eval_src("(def b 2)", &mut env).unwrap();
830 let v = eval_src("`(a ~b)", &mut env).unwrap();
831 assert!(matches!(v, Value::List(_)));
832 if let Value::List(l) = &v {
833 let items: Vec<_> = l.get().iter().cloned().collect();
835 assert_eq!(items.len(), 2);
836 assert_eq!(items[1], long(2));
837 }
838 }
839
840 #[test]
843 fn test_reader_cond_rust() {
844 assert_eq!(eval_str("#?(:rust 1 :clj 2)").unwrap(), long(1));
846 }
847
848 #[test]
849 fn test_reader_cond_default() {
850 assert_eq!(eval_str("#?(:clj 2 :default 99)").unwrap(), long(99));
852 }
853
854 #[test]
855 fn test_reader_cond_splice_in_vector() {
856 assert_eq!(
857 eval_str("[1 #?@(:rust [:a :b]) 2]").unwrap(),
858 eval_str("[1 :a :b 2]").unwrap()
859 );
860 }
861
862 #[test]
863 fn test_reader_cond_splice_in_set() {
864 assert_eq!(
865 eval_str("#{1 #?@(:rust [2 3]) 4}").unwrap(),
866 eval_str("#{1 2 3 4}").unwrap()
867 );
868 }
869
870 #[test]
871 fn test_reader_cond_splice_in_map() {
872 assert_eq!(
873 eval_str("{:a 1 #?@(:rust [:b 2]) :c 3}").unwrap(),
874 eval_str("{:a 1 :b 2 :c 3}").unwrap()
875 );
876 }
877
878 #[test]
879 fn test_reader_cond_splice_in_call_args() {
880 assert_eq!(
881 eval_str("(vector 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_no_match_removed() {
888 assert_eq!(
889 eval_str("[1 #?@(:clj [:a :b]) 2]").unwrap(),
890 eval_str("[1 2]").unwrap()
891 );
892 }
893
894 proptest::proptest! {
895 #![proptest_config(proptest::prelude::ProptestConfig::with_cases(48))]
896 #[test]
900 fn prop_splice_evals_as_inline_in_every_container(
901 np in 0usize..3, nm in 0usize..3, ns in 0usize..3,
902 ) {
903 let kw_run = |start: usize, n: usize| {
904 (start..start + n)
905 .map(|i| format!(":v{i}"))
906 .collect::<Vec<_>>()
907 .join(" ")
908 };
909 let p = kw_run(0, np);
910 let m = kw_run(np, nm);
911 let s = kw_run(np + nm, ns);
912 for (open, close, quote) in
913 [("[", "]", ""), ("#{", "}", ""), ("(", ")", "'"), ("(vector ", ")", "")]
914 {
915 let spliced = format!("{quote}{open}{p} #?@(:rust [{m}]) {s}{close}");
916 let inlined = format!("{quote}{open}{p} {m} {s}{close}");
917 proptest::prop_assert_eq!(
918 eval_str(&spliced).unwrap(),
919 eval_str(&inlined).unwrap(),
920 "container {}{}",
921 open,
922 close
923 );
924 }
925 }
926 }
927
928 #[derive(Clone, Debug)]
935 enum Slot {
936 Plain,
938 NonSplice { matches: bool },
940 Splice { matches: bool, n: usize },
942 }
943
944 fn slot_strat() -> impl proptest::strategy::Strategy<Value = Slot> {
945 use proptest::strategy::{Just, Strategy};
946 proptest::prop_oneof![
947 Just(Slot::Plain),
948 proptest::bool::ANY.prop_map(|matches| Slot::NonSplice { matches }),
949 (proptest::bool::ANY, 0usize..3).prop_map(|(matches, n)| Slot::Splice { matches, n }),
950 ]
951 }
952
953 fn render_slots(slots: &[Slot], unit: &dyn Fn(usize) -> String) -> (String, String) {
958 let mut next = 0usize;
959 let (mut written, mut inlined) = (Vec::new(), Vec::new());
960 for slot in slots {
961 match *slot {
962 Slot::Plain => {
963 let u = unit(next);
964 next += 1;
965 written.push(u.clone());
966 inlined.push(u);
967 }
968 Slot::NonSplice { matches } => {
969 let u = unit(next);
970 next += 1;
971 let key = if matches { "rust" } else { "clj" };
972 written.push(format!("#?(:{key} {u})"));
973 if matches {
974 inlined.push(u);
975 }
976 }
977 Slot::Splice { matches, n } => {
978 let us: Vec<String> = (0..n)
979 .map(|_| {
980 let u = unit(next);
981 next += 1;
982 u
983 })
984 .collect();
985 let key = if matches { "rust" } else { "clj" };
986 written.push(format!("#?@(:{key} [{}])", us.join(" ")));
987 if matches {
988 inlined.extend(us);
989 }
990 }
991 }
992 }
993 (written.join(" "), inlined.join(" "))
994 }
995
996 fn pair_slot_strat() -> impl proptest::strategy::Strategy<Value = Slot> {
1000 use proptest::strategy::{Just, Strategy};
1001 proptest::prop_oneof![
1002 Just(Slot::Plain),
1003 (proptest::bool::ANY, 0usize..3).prop_map(|(matches, n)| Slot::Splice { matches, n }),
1004 ]
1005 }
1006
1007 fn kw_unit(i: usize) -> String {
1008 format!(":k{i}")
1009 }
1010
1011 fn binding_unit(i: usize) -> String {
1014 format!("x{i} {i}")
1015 }
1016
1017 proptest::proptest! {
1018 #![proptest_config(proptest::prelude::ProptestConfig::with_cases(48))]
1019
1020 #[test]
1026 fn prop_splice_agrees_across_eval_quote_and_syntax_quote(
1027 slots in proptest::collection::vec(slot_strat(), 0..5),
1028 ) {
1029 let (written, inlined) = render_slots(&slots, &kw_unit);
1030 let even = inlined.split_whitespace().count().is_multiple_of(2);
1031 for (open, close) in [("[", "]"), ("#{", "}"), ("{", "}")] {
1032 if open == "{" && !even {
1033 continue;
1034 }
1035 for prefix in ["", "'", "`"] {
1036 let got = eval_str(&format!("{prefix}{open}{written}{close}"));
1037 let want = eval_str(&format!("{prefix}{open}{inlined}{close}"));
1038 proptest::prop_assert_eq!(
1039 got.map_err(|e| e.to_string()),
1040 want.map_err(|e| e.to_string()),
1041 "prefix {:?} container {}{}", prefix, open, close
1042 );
1043 }
1044 }
1045 for prefix in ["'", "`"] {
1047 let got = eval_str(&format!("{prefix}({written})"));
1048 let want = eval_str(&format!("{prefix}({inlined})"));
1049 proptest::prop_assert_eq!(
1050 got.map_err(|e| e.to_string()),
1051 want.map_err(|e| e.to_string()),
1052 "prefix {:?} list", prefix
1053 );
1054 }
1055 }
1056
1057 #[test]
1060 fn prop_splice_in_binding_vectors_equals_inline(
1061 slots in proptest::collection::vec(pair_slot_strat(), 0..4),
1062 ) {
1063 let (written, inlined) = render_slots(&slots, &binding_unit);
1064 let names: Vec<String> = inlined
1065 .split_whitespace()
1066 .step_by(2)
1067 .map(str::to_string)
1068 .collect();
1069 let body = format!("[{}]", names.join(" "));
1070 for head in ["let*", "loop*"] {
1071 let got = eval_str(&format!("({head} [{written}] {body})"));
1072 let want = eval_str(&format!("({head} [{inlined}] {body})"));
1073 proptest::prop_assert_eq!(
1074 got.map_err(|e| e.to_string()),
1075 want.map_err(|e| e.to_string()),
1076 "{}", head
1077 );
1078 }
1079 }
1080 }
1081
1082 #[test]
1083 fn quoted_map_splice_lands_in_key_value_positions() {
1084 assert_eq!(
1085 eval_str("'{:a 1 #?@(:rust [:b 2]) :c 3}").unwrap(),
1086 eval_str("{:a 1 :b 2 :c 3}").unwrap()
1087 );
1088 }
1089
1090 #[test]
1091 fn syntax_quoted_splice_is_expanded_not_nil() {
1092 assert_eq!(
1093 eval_str("`(1 #?@(:rust [2 3]) 4)").unwrap(),
1094 eval_str("'(1 2 3 4)").unwrap()
1095 );
1096 }
1097
1098 #[test]
1099 fn map_literal_with_only_an_unmatched_conditional_reads_as_empty() {
1100 assert_eq!(eval_str("{#?(:clj :a)}").unwrap(), eval_str("{}").unwrap());
1103 }
1104
1105 #[test]
1106 fn loop_binding_vector_expands_splices() {
1107 assert_eq!(eval_str("(loop* [#?@(:rust [x 1])] x)").unwrap(), long(1));
1108 }
1109
1110 #[test]
1113 fn test_unbound_symbol() {
1114 let r = eval_str("undefined-var-xyz");
1115 assert!(matches!(r, Err(EvalError::UnboundSymbol(_))));
1116 }
1117
1118 #[test]
1119 fn test_wrong_arity() {
1120 let (_, mut env) = make_env();
1121 eval_src("(defn one-arg [x] x)", &mut env).unwrap();
1122 let r = eval_src("(one-arg 1 2)", &mut env);
1123 assert!(matches!(r, Err(EvalError::Arity { .. })));
1124 }
1125
1126 #[test]
1127 fn test_not_callable() {
1128 let r = eval_str("(42 1 2)");
1129 assert!(matches!(r, Err(EvalError::NotCallable(_))));
1130 }
1131
1132 #[test]
1135 fn test_map_fn() {
1136 assert_eq!(
1137 eval_str("(vec (map inc [1 2 3]))").unwrap(),
1138 eval_str("[2 3 4]").unwrap()
1139 );
1140 }
1141
1142 #[test]
1143 fn test_filter_fn() {
1144 assert_eq!(
1145 eval_str("(vec (filter odd? [1 2 3 4 5]))").unwrap(),
1146 eval_str("[1 3 5]").unwrap()
1147 );
1148 }
1149
1150 #[test]
1151 fn test_reduce_fn() {
1152 assert_eq!(eval_str("(reduce + [1 2 3 4 5])").unwrap(), long(15));
1153 }
1154
1155 #[test]
1156 fn test_apply_fn() {
1157 assert_eq!(eval_str("(apply + [1 2 3])").unwrap(), long(6));
1158 }
1159
1160 #[test]
1161 fn test_atom_ops() {
1162 let (_, mut env) = make_env();
1163 eval_src("(def a (atom 0))", &mut env).unwrap();
1164 eval_src("(swap! a inc)", &mut env).unwrap();
1165 assert_eq!(eval_src("(deref a)", &mut env).unwrap(), long(1));
1166 }
1167
1168 #[test]
1169 fn test_when_macro() {
1170 assert_eq!(eval_str("(when true 42)").unwrap(), long(42));
1171 assert_eq!(eval_str("(when false 42)").unwrap(), Value::Nil);
1172 }
1173
1174 #[test]
1175 fn test_cond_macro() {
1176 assert_eq!(eval_str("(cond false 1 true 2)").unwrap(), long(2));
1177 }
1178
1179 #[test]
1180 fn test_and_or() {
1181 assert_eq!(eval_str("(and 1 2 3)").unwrap(), long(3));
1182 assert_eq!(eval_str("(and 1 false 3)").unwrap(), bool_v(false));
1183 assert_eq!(eval_str("(or false nil 42)").unwrap(), long(42));
1184 assert_eq!(eval_str("(or false nil)").unwrap(), Value::Nil);
1185 }
1186
1187 #[test]
1190 fn test_lazy_range() {
1191 assert_eq!(
1192 eval_str("(= (into [] (take 5 (range))) [0 1 2 3 4])").unwrap(),
1193 bool_v(true)
1194 );
1195 }
1196
1197 #[test]
1198 fn test_lazy_range_bounded() {
1199 assert_eq!(
1200 eval_str("(= (into [] (range 3)) [0 1 2])").unwrap(),
1201 bool_v(true)
1202 );
1203 }
1204
1205 #[test]
1206 fn test_lazy_iterate() {
1207 assert_eq!(
1208 eval_str("(= (into [] (take 3 (iterate inc 0))) [0 1 2])").unwrap(),
1209 bool_v(true)
1210 );
1211 }
1212
1213 #[test]
1214 fn test_lazy_repeat() {
1215 assert_eq!(
1216 eval_str("(= (into [] (take 3 (repeat :x))) [:x :x :x])").unwrap(),
1217 bool_v(true)
1218 );
1219 }
1220
1221 #[test]
1222 fn test_lazy_cycle() {
1223 assert_eq!(
1224 eval_str("(= (into [] (take 5 (cycle [1 2]))) [1 2 1 2 1])").unwrap(),
1225 bool_v(true)
1226 );
1227 }
1228
1229 #[test]
1232 fn test_assoc_destructure() {
1233 assert_eq!(
1234 eval_str("(let [{:keys [a b]} {:a 1 :b 2}] (+ a b))").unwrap(),
1235 long(3)
1236 );
1237 }
1238
1239 #[test]
1240 fn test_assoc_destructure_or() {
1241 assert_eq!(
1242 eval_str("(let [{:keys [a b] :or {b 99}} {:a 1}] b)").unwrap(),
1243 long(99)
1244 );
1245 }
1246
1247 #[test]
1250 fn test_letfn() {
1251 assert_eq!(
1252 eval_str("(letfn [(fact [n] (if (= n 0) 1 (* n (fact (dec n)))))] (fact 5))").unwrap(),
1253 long(120)
1254 );
1255 }
1256
1257 #[test]
1260 fn test_in_ns() {
1261 let (_, mut env) = make_env();
1262 eval_src("(in-ns 'mytest)", &mut env).unwrap();
1263 assert_eq!(env.current_ns.as_ref(), "mytest");
1264 eval_src("(in-ns 'user)", &mut env).unwrap();
1265 assert_eq!(env.current_ns.as_ref(), "user");
1266 }
1267
1268 #[test]
1271 fn test_spit_slurp() {
1272 let path = std::env::temp_dir().join("cljrs_test_spit_slurp.txt");
1273 let path_str = path.to_str().unwrap();
1274 let src = format!(
1275 r#"(do (spit "{}" "hello clojurust") (slurp "{}"))"#,
1276 path_str, path_str
1277 );
1278 let result = eval_str(&src).unwrap();
1279 if let Value::Str(s) = result {
1280 assert_eq!(s.get().as_str(), "hello clojurust");
1281 } else {
1282 panic!("expected string result from slurp");
1283 }
1284 let _ = std::fs::remove_file(path);
1285 }
1286
1287 #[test]
1290 fn test_update_in() {
1291 assert_eq!(
1292 eval_str("(= (update-in {:a {:b 1}} [:a :b] inc) {:a {:b 2}})").unwrap(),
1293 bool_v(true)
1294 );
1295 }
1296
1297 #[test]
1300 fn test_if_let_truthy() {
1301 assert_eq!(eval_str("(if-let [x 42] x :nope)").unwrap(), long(42));
1302 }
1303
1304 #[test]
1305 fn test_if_let_falsy() {
1306 assert_eq!(
1307 eval_str("(if-let [x nil] x :nope)").unwrap(),
1308 eval_str(":nope").unwrap()
1309 );
1310 }
1311
1312 #[test]
1313 fn test_when_let_truthy() {
1314 assert_eq!(eval_str("(when-let [x 7] (* x 2))").unwrap(), long(14));
1315 }
1316
1317 #[test]
1318 fn test_when_let_falsy() {
1319 assert_eq!(eval_str("(when-let [x nil] 99)").unwrap(), Value::Nil);
1320 }
1321
1322 #[test]
1325 fn test_math_trig() {
1326 assert_eq!(eval_str("(Math/sin 0)").unwrap(), Value::Double(0.0));
1328 assert_eq!(eval_str("(Math/cos 0)").unwrap(), Value::Double(1.0));
1329 }
1330
1331 #[test]
1332 fn test_math_constants() {
1333 assert!(
1334 matches!(eval_str("Math/PI").unwrap(), Value::Double(v) if (v - std::f64::consts::PI).abs() < 1e-10)
1335 );
1336 assert!(
1337 matches!(eval_str("Math/E").unwrap(), Value::Double(v) if (v - std::f64::consts::E).abs() < 1e-10)
1338 );
1339 }
1340
1341 #[test]
1342 fn test_math_log_exp() {
1343 assert_eq!(eval_str("(Math/exp 0)").unwrap(), Value::Double(1.0));
1345 assert_eq!(eval_str("(Math/log 1)").unwrap(), Value::Double(0.0));
1346 }
1347
1348 #[test]
1351 fn test_defprotocol() {
1352 let result = eval_str(
1354 r#"
1355 (defprotocol Greet
1356 (greet [this]))
1357 (greet "hello")
1358 "#,
1359 );
1360 assert!(result.is_err());
1361 let msg = result.unwrap_err().to_string();
1362 assert!(msg.contains("No implementation"), "got: {msg}");
1363 }
1364
1365 #[test]
1366 fn test_extend_type() {
1367 let result = eval_str(
1368 r#"
1369 (defprotocol Greet
1370 (greet [this]))
1371 (extend-type String
1372 Greet
1373 (greet [this] (str "Hello, " this "!")))
1374 (greet "world")
1375 "#,
1376 )
1377 .unwrap();
1378 assert_eq!(result, Value::string("Hello, world!"));
1379 }
1380
1381 #[test]
1382 fn test_protocol_dispatch() {
1383 let result = eval_str(
1384 r#"
1385 (defprotocol Describable
1386 (describe [this]))
1387 (extend-type String
1388 Describable
1389 (describe [this] (str "string:" this)))
1390 (extend-type Long
1391 Describable
1392 (describe [this] (str "long:" this)))
1393 [(describe "hi") (describe 42)]
1394 "#,
1395 )
1396 .unwrap();
1397 assert!(matches!(result, Value::Vector(_)));
1398 let s = format!("{}", result);
1399 assert!(s.contains("string:hi"), "got: {s}");
1400 assert!(s.contains("long:42"), "got: {s}");
1401 }
1402
1403 #[test]
1404 fn test_extend_protocol() {
1405 let result = eval_str(
1406 r#"
1407 (defprotocol Showable
1408 (show [this]))
1409 (extend-protocol Showable
1410 String
1411 (show [this] (str "S:" this))
1412 Long
1413 (show [this] (str "L:" this)))
1414 [(show "x") (show 7)]
1415 "#,
1416 )
1417 .unwrap();
1418 let s = format!("{}", result);
1419 assert!(s.contains("S:x"), "got: {s}");
1420 assert!(s.contains("L:7"), "got: {s}");
1421 }
1422
1423 #[test]
1424 fn test_extend_via_metadata() {
1425 let result = eval_str(
1432 r#"
1433 (defprotocol IRender
1434 :extend-via-metadata true
1435 (create-element [this tag-name]))
1436 (def renderer (with-meta {} {`create-element (fn [this tag-name] (str "made-" tag-name))}))
1437 (create-element renderer "div")
1438 "#,
1439 )
1440 .unwrap();
1441 assert_eq!(result, Value::string("made-div"));
1442 }
1443
1444 #[test]
1445 fn test_extend_via_metadata_falls_back_to_type_tag() {
1446 let result = eval_str(
1449 r#"
1450 (defprotocol IRender
1451 :extend-via-metadata true
1452 (create-element [this tag-name]))
1453 (extend-type Map
1454 IRender
1455 (create-element [this tag-name] (str "type-tag-" tag-name)))
1456 [(create-element {} "span")
1457 (create-element (with-meta {} {`create-element (fn [this tag-name] (str "meta-" tag-name))}) "div")]
1458 "#,
1459 )
1460 .unwrap();
1461 let s = format!("{}", result);
1462 assert!(s.contains("type-tag-span"), "got: {s}");
1463 assert!(s.contains("meta-div"), "got: {s}");
1464 }
1465
1466 #[test]
1467 fn test_extend_via_metadata_cross_ns() {
1468 let dir = temp_ns_dir("extend_via_metadata_cross_ns");
1475 std::fs::create_dir_all(dir.join("replicant")).unwrap();
1476 std::fs::write(
1477 dir.join("replicant").join("core.cljrs"),
1478 r#"(ns replicant.core)
1479 (defprotocol IRender
1480 :extend-via-metadata true
1481 (create-element [this tag-name]))"#,
1482 )
1483 .unwrap();
1484 let (_, mut env) = make_env_with_paths(vec![dir]);
1485 let result = eval_src(
1486 r#"
1487 (ns mutation-log-test
1488 (:require [replicant.core :refer [create-element]]))
1489 (def renderer (with-meta {} {`create-element (fn [this tag-name] (str "made-" tag-name))}))
1490 (create-element renderer "div")
1491 "#,
1492 &mut env,
1493 )
1494 .unwrap();
1495 assert_eq!(result, Value::string("made-div"));
1496 }
1497
1498 #[test]
1499 fn test_extend_via_metadata_cross_ns_via_alias() {
1500 let dir = temp_ns_dir("extend_via_metadata_cross_ns_via_alias");
1509 std::fs::create_dir_all(dir.join("replicant")).unwrap();
1510 std::fs::write(
1511 dir.join("replicant").join("protocols.cljrs"),
1512 r#"(ns replicant.protocols)
1513 (defprotocol IRender
1514 :extend-via-metadata true
1515 (attached? [this el]))"#,
1516 )
1517 .unwrap();
1518 let (_, mut env) = make_env_with_paths(vec![dir]);
1519 let result = eval_src(
1520 r#"
1521 (ns mutation-log-test
1522 (:require [replicant.protocols :as p]))
1523 (def r (with-meta {:log []} {`p/attached? (fn [_ el] el)}))
1524 (p/attached? r :el)
1525 "#,
1526 &mut env,
1527 )
1528 .unwrap();
1529 assert_eq!(result, Value::keyword(Keyword::simple("el")));
1530 }
1531
1532 #[test]
1533 fn test_satisfies() {
1534 let result = eval_str(
1535 r#"
1536 (defprotocol Animal
1537 (speak [this]))
1538 (extend-type String
1539 Animal
1540 (speak [this] this))
1541 [(satisfies? Animal "dog") (satisfies? Animal 42)]
1542 "#,
1543 )
1544 .unwrap();
1545 let s = format!("{}", result);
1546 assert!(s.contains("true"), "got: {s}");
1547 assert!(s.contains("false"), "got: {s}");
1548 }
1549
1550 #[test]
1551 fn test_defmulti_defmethod() {
1552 let result = eval_str(
1554 r#"
1555 (defmulti area :shape)
1556 (defmethod area :circle [m] (* 3 (:r m) (:r m)))
1557 (defmethod area :rectangle [m] (* (:w m) (:h m)))
1558 [(area {:shape :circle :r 2}) (area {:shape :rectangle :w 3 :h 4})]
1559 "#,
1560 )
1561 .unwrap();
1562 let s = format!("{}", result);
1563 assert!(s.contains("12"), "got: {s}");
1565 }
1566
1567 #[test]
1568 fn test_default_dispatch() {
1569 let result = eval_str(
1570 r#"
1571 (defmulti classify :kind)
1572 (defmethod classify :default [x] :unknown)
1573 (defmethod classify :cat [x] :meow)
1574 [(classify {:kind :dog}) (classify {:kind :cat})]
1575 "#,
1576 )
1577 .unwrap();
1578 let s = format!("{}", result);
1579 assert!(s.contains(":unknown"), "got: {s}");
1580 assert!(s.contains(":meow"), "got: {s}");
1581 }
1582
1583 #[test]
1584 fn test_prefer_method() {
1585 let result = eval_str(
1587 r#"
1588 (defmulti foo identity)
1589 (defmethod foo :a [x] 1)
1590 (prefer-method foo :a :b)
1591 (foo :a)
1592 "#,
1593 )
1594 .unwrap();
1595 assert_eq!(result, Value::Long(1));
1596 }
1597
1598 #[test]
1599 fn test_remove_method() {
1600 let result = eval_str(
1601 r#"
1602 (defmulti bar identity)
1603 (defmethod bar :x [_] 99)
1604 (remove-method bar :x)
1605 (bar :x)
1606 "#,
1607 );
1608 assert!(result.is_err());
1609 let msg = result.unwrap_err().to_string();
1610 assert!(msg.contains("No method"), "got: {msg}");
1611 }
1612
1613 #[test]
1616 fn test_compare_and_set() {
1617 let result = eval_str(
1618 r#"
1619 (let [a (atom 10)]
1620 [(compare-and-set! a 10 20) ; succeeds: 10 == 10
1621 (compare-and-set! a 10 30) ; fails: 20 != 10
1622 @a])
1623 "#,
1624 )
1625 .unwrap();
1626 let s = format!("{}", result);
1627 assert!(s.contains("true"), "got: {s}");
1628 assert!(s.contains("false"), "got: {s}");
1629 assert!(s.contains("20"), "got: {s}");
1630 }
1631
1632 #[test]
1633 fn test_volatile() {
1634 let result = eval_str(
1635 r#"
1636 (let [v (volatile! 1)]
1637 (vreset! v 2)
1638 (vswap! v + 10)
1639 @v)
1640 "#,
1641 )
1642 .unwrap();
1643 assert_eq!(result, Value::Long(12));
1644 }
1645
1646 #[test]
1647 fn test_delay() {
1648 let result = eval_str(
1650 r#"
1651 (let [calls (atom 0)
1652 d (delay (swap! calls inc) 42)]
1653 [@calls (force d) @calls (force d) @calls])
1654 "#,
1655 )
1656 .unwrap();
1657 let s = format!("{}", result);
1658 assert!(s.starts_with("[0 42 1 42 1]"), "got: {s}");
1661 }
1662
1663 #[test]
1664 fn test_realized() {
1665 let result = eval_str(
1666 r#"
1667 (let [d (delay 99)]
1668 [(realized? d) (force d) (realized? d)])
1669 "#,
1670 )
1671 .unwrap();
1672 let s = format!("{}", result);
1673 assert!(s.starts_with("[false 99 true]"), "got: {s}");
1674 }
1675
1676 #[test]
1677 fn test_promise() {
1678 let result = eval_str(
1679 r#"
1680 (let [p (promise)]
1681 (deliver p 42)
1682 (deliver p 99) ; second deliver is ignored
1683 @p)
1684 "#,
1685 )
1686 .unwrap();
1687 assert_eq!(result, Value::Long(42));
1688 }
1689
1690 #[test]
1691 #[ignore = "future/thread spawn not yet implemented (Phase A1 — GcPtr: !Send)"]
1692 fn test_future() {
1693 let result = eval_str(
1694 r#"
1695 (let [f (future (+ 1 2))]
1696 @f)
1697 "#,
1698 )
1699 .unwrap();
1700 assert_eq!(result, Value::Long(3));
1701 }
1702
1703 #[test]
1704 #[ignore = "agent not yet implemented (Phase A1 — GcPtr: !Send)"]
1705 fn test_agent_send() {
1706 let result = eval_str(
1707 r#"
1708 (let [a (agent 0)]
1709 (send a + 1)
1710 (send a + 2)
1711 (await-agent a)
1712 @a)
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_error_restart() {
1722 let result = eval_str(
1723 r#"
1724 (let [a (agent 10)]
1725 (send a (fn [_] (throw (ex-info "boom" {}))))
1726 (await-agent a)
1727 (let [err (agent-error a)]
1728 (restart-agent a 99)
1729 [err @a]))
1730 "#,
1731 )
1732 .unwrap();
1733 let s = format!("{}", result);
1734 assert!(s.contains("boom"), "got: {s}");
1736 assert!(s.contains("99"), "got: {s}");
1737 }
1738
1739 #[test]
1740 fn test_defrecord_basic() {
1741 let result = eval_str(
1743 r#"
1744 (defrecord Point [x y])
1745 (let [p (->Point 3 4)]
1746 [(:x p) (:y p)])
1747 "#,
1748 )
1749 .unwrap();
1750 assert_eq!(result.to_string(), "[3 4]");
1751 }
1752
1753 #[test]
1754 fn test_defrecord_map_constructor() {
1755 let result = eval_str(
1756 r#"
1757 (defrecord Color [r g b])
1758 (let [c (map->Color {:r 255 :g 128 :b 0})]
1759 [(:r c) (:g c) (:b c)])
1760 "#,
1761 )
1762 .unwrap();
1763 assert_eq!(result.to_string(), "[255 128 0]");
1764 }
1765
1766 #[test]
1767 fn test_defrecord_assoc() {
1768 let result = eval_str(
1770 r#"
1771 (defrecord Pt [x y])
1772 (let [p (->Pt 1 2)
1773 q (assoc p :x 99)]
1774 [(:x q) (:y q) (record? q)])
1775 "#,
1776 )
1777 .unwrap();
1778 assert_eq!(result.to_string(), "[99 2 true]");
1779 }
1780
1781 #[test]
1782 fn test_defrecord_with_protocol() {
1783 let result = eval_str(
1784 r#"
1785 (defprotocol IShape
1786 (area [this]))
1787 (defrecord Circle [radius]
1788 IShape
1789 (area [this] (* 3 (:radius this) (:radius this))))
1790 (let [c (->Circle 5)]
1791 (area c))
1792 "#,
1793 )
1794 .unwrap();
1795 assert_eq!(result, cljrs_value::Value::Long(75));
1796 }
1797
1798 #[test]
1799 fn test_instance_q() {
1800 let result = eval_str(
1801 r#"
1802 (defrecord Dog [name])
1803 (let [d (->Dog "Rex")]
1804 [(instance? Dog d) (instance? Dog 42)])
1805 "#,
1806 )
1807 .unwrap();
1808 assert_eq!(result.to_string(), "[true false]");
1809 }
1810
1811 #[test]
1812 fn test_reify_basic() {
1813 let result = eval_str(
1814 r#"
1815 (defprotocol IGreet
1816 (greet [this name]))
1817 (let [greeter (reify IGreet
1818 (greet [this name] (str "Hello, " name "!")))]
1819 (greet greeter "World"))
1820 "#,
1821 )
1822 .unwrap();
1823 assert_eq!(result.to_string(), "\"Hello, World!\"");
1824 }
1825
1826 fn temp_ns_dir(test_name: &str) -> std::path::PathBuf {
1829 let dir = std::env::temp_dir().join(format!("cljrs_test_{test_name}"));
1830 let _ = std::fs::remove_dir_all(&dir);
1831 std::fs::create_dir_all(&dir).unwrap();
1832 dir
1833 }
1834
1835 fn make_env_with_paths(paths: Vec<std::path::PathBuf>) -> (Arc<GlobalEnv>, Env) {
1836 use crate::interp::standard_env_with_paths;
1837 let globals = standard_env_with_paths(None, None, None, paths);
1838 let env = Env::new(globals.clone(), "user");
1839 (globals, env)
1840 }
1841
1842 #[test]
1843 fn test_require_as() {
1844 let dir = temp_ns_dir("require_as");
1845 std::fs::write(
1846 dir.join("mylib.cljrs"),
1847 "(ns mylib) (defn greet [n] (str \"hello \" n))",
1848 )
1849 .unwrap();
1850 let (_, mut env) = make_env_with_paths(vec![dir]);
1851 let result = eval_src("(require '[mylib :as ml]) (ml/greet \"world\")", &mut env).unwrap();
1852 assert_eq!(result.to_string(), "\"hello world\"");
1853 }
1854
1855 #[test]
1856 fn test_require_refer() {
1857 let dir = temp_ns_dir("require_refer");
1858 std::fs::write(
1859 dir.join("myutil.cljrs"),
1860 "(ns myutil) (defn twice [x] (* 2 x))",
1861 )
1862 .unwrap();
1863 let (_, mut env) = make_env_with_paths(vec![dir]);
1864 let result = eval_src("(require '[myutil :refer [twice]]) (twice 21)", &mut env).unwrap();
1865 assert_eq!(result, Value::Long(42));
1866 }
1867
1868 #[test]
1869 fn test_require_refer_all() {
1870 let dir = temp_ns_dir("require_refer_all");
1871 std::fs::write(
1872 dir.join("mymath.cljrs"),
1873 "(ns mymath) (defn square [x] (* x x))",
1874 )
1875 .unwrap();
1876 let (_, mut env) = make_env_with_paths(vec![dir]);
1877 let result = eval_src("(require '[mymath :refer :all]) (square 7)", &mut env).unwrap();
1878 assert_eq!(result, Value::Long(49));
1879 }
1880
1881 #[test]
1882 fn test_ns_require_clause() {
1883 let dir = temp_ns_dir("ns_require");
1884 std::fs::write(
1885 dir.join("greeter.cljrs"),
1886 "(ns greeter) (defn hi [n] (str \"Hi \" n))",
1887 )
1888 .unwrap();
1889 let (_, mut env) = make_env_with_paths(vec![dir]);
1890 let result = eval_src(
1891 "(ns myapp (:require [greeter :as g])) (g/hi \"Alice\")",
1892 &mut env,
1893 )
1894 .unwrap();
1895 assert_eq!(result.to_string(), "\"Hi Alice\"");
1896 }
1897
1898 #[test]
1899 fn test_var_quote_alias_resolution() {
1900 let dir = temp_ns_dir("var_quote_alias");
1903 std::fs::create_dir_all(dir.join("lib")).unwrap();
1905 std::fs::write(
1906 dir.join("lib/core.cljrs"),
1907 "(ns lib.core) (defn public [x] (* x 2))",
1908 )
1909 .unwrap();
1910 let (_, mut env) = make_env_with_paths(vec![dir]);
1911 let call_result = eval_src("(require '[lib.core :as l]) (l/public 21)", &mut env).unwrap();
1913 assert_eq!(call_result, Value::Long(42));
1914 let var_result = eval_src("#'l/public", &mut env).unwrap();
1916 assert!(
1917 matches!(var_result, Value::Var(_)),
1918 "expected Var, got {var_result:?}"
1919 );
1920 assert_eq!(var_result.to_string(), "#'lib.core/public");
1921 let var_special = eval_src("(var l/public)", &mut env).unwrap();
1923 assert_eq!(var_special.to_string(), "#'lib.core/public");
1924 }
1925
1926 #[test]
1927 fn test_require_idempotent() {
1928 let dir = temp_ns_dir("require_idempotent");
1929 std::fs::write(
1931 dir.join("counter.cljrs"),
1932 "(ns counter) (def loaded-count (atom 0)) (swap! loaded-count inc)",
1933 )
1934 .unwrap();
1935 let (globals, mut env) = make_env_with_paths(vec![dir]);
1936 eval_src("(require 'counter)", &mut env).unwrap();
1937 eval_src("(require 'counter)", &mut env).unwrap();
1938 let count = globals.lookup_in_ns("counter", "loaded-count").unwrap();
1940 if let Value::Atom(a) = count {
1941 assert_eq!(a.get().deref(), Value::Long(1));
1942 } else {
1943 panic!("expected atom");
1944 }
1945 }
1946
1947 #[test]
1948 fn test_require_not_found() {
1949 let (_, mut env) = make_env_with_paths(vec![]);
1950 let err = eval_src("(require 'nonexistent.ns)", &mut env).unwrap_err();
1951 let msg = format!("{err:?}");
1952 assert!(msg.contains("nonexistent.ns"), "unexpected error: {msg}");
1953 }
1954
1955 #[test]
1956 fn test_require_circular() {
1957 let dir = temp_ns_dir("require_circular");
1958 std::fs::write(dir.join("cira.cljrs"), "(ns cira (:require [cirb]))").unwrap();
1960 std::fs::write(dir.join("cirb.cljrs"), "(ns cirb (:require [cira]))").unwrap();
1961 let (_, mut env) = make_env_with_paths(vec![dir]);
1962 let err = eval_src("(require 'cira)", &mut env).unwrap_err();
1963 let msg = format!("{err:?}");
1964 assert!(
1965 msg.contains("circular"),
1966 "expected circular error, got: {msg}"
1967 );
1968 }
1969
1970 #[test]
1971 fn test_load_file() {
1972 let dir = temp_ns_dir("load_file");
1973 let path = dir.join("script.cljrs");
1974 std::fs::write(&path, "(+ 1 2)").unwrap();
1975 let (_, mut env) = make_env_with_paths(vec![]);
1976 let result = eval_src(&format!("(load-file \"{}\")", path.display()), &mut env).unwrap();
1977 assert_eq!(result, Value::Long(3));
1978 }
1979
1980 #[test]
1983 fn test_star_ns_initial() {
1984 let (_, mut env) = make_env();
1986 let v = eval_src("*ns*", &mut env).unwrap();
1987 match v {
1988 Value::Namespace(ns) => assert_eq!(ns.get().name.as_ref(), "user"),
1989 other => panic!("expected Namespace, got {other:?}"),
1990 }
1991 }
1992
1993 #[test]
1994 fn test_star_ns_after_in_ns() {
1995 let (_, mut env) = make_env();
1996 eval_src("(in-ns 'myns)", &mut env).unwrap();
1997 let v = eval_src("*ns*", &mut env).unwrap();
1998 match v {
1999 Value::Namespace(ns) => assert_eq!(ns.get().name.as_ref(), "myns"),
2000 other => panic!("expected Namespace, got {other:?}"),
2001 }
2002 }
2003
2004 #[test]
2005 fn test_star_ns_after_ns_form() {
2006 let (_, mut env) = make_env();
2007 eval_src("(ns mytest.ns)", &mut env).unwrap();
2008 let v = eval_src("*ns*", &mut env).unwrap();
2009 match v {
2010 Value::Namespace(ns) => assert_eq!(ns.get().name.as_ref(), "mytest.ns"),
2011 other => panic!("expected Namespace, got {other:?}"),
2012 }
2013 }
2014
2015 #[test]
2016 fn test_ns_name() {
2017 let (_, mut env) = make_env();
2018 let v = eval_src("(ns-name *ns*)", &mut env).unwrap();
2019 match v {
2020 Value::Symbol(s) => assert_eq!(s.get().name.as_ref(), "user"),
2021 other => panic!("expected Symbol, got {other:?}"),
2022 }
2023 }
2024
2025 #[test]
2026 fn test_find_ns() {
2027 let (_, mut env) = make_env();
2028 let v = eval_src("(find-ns 'user)", &mut env).unwrap();
2030 assert!(matches!(v, Value::Namespace(_)));
2031 let v2 = eval_src("(find-ns 'nonexistent)", &mut env).unwrap();
2033 assert_eq!(v2, Value::Nil);
2034 }
2035
2036 #[test]
2037 fn test_all_ns() {
2038 let (_, mut env) = make_env();
2039 let v = eval_src("(all-ns)", &mut env).unwrap();
2040 let names: Vec<String> = match &v {
2042 Value::List(l) => l
2043 .get()
2044 .iter()
2045 .filter_map(|ns| match ns {
2046 Value::Namespace(n) => Some(n.get().name.as_ref().to_string()),
2047 _ => None,
2048 })
2049 .collect(),
2050 other => panic!("expected list, got {other:?}"),
2051 };
2052 assert!(names.contains(&"user".to_string()));
2053 assert!(names.contains(&"clojure.core".to_string()));
2054 }
2055
2056 #[test]
2057 fn test_ns_interns() {
2058 let (_, mut env) = make_env();
2059 eval_src("(def my-test-var 42)", &mut env).unwrap();
2060 let v = eval_src("(ns-interns *ns*)", &mut env).unwrap();
2061 let Value::Map(m) = v else {
2062 panic!("expected map")
2063 };
2064 let sym = Value::symbol(cljrs_value::Symbol::simple("my-test-var"));
2066 assert!(m.get(&sym).is_some());
2067 }
2068
2069 #[test]
2070 fn test_create_ns() {
2071 let (_, mut env) = make_env();
2072 let v = eval_src("(create-ns 'fresh.ns)", &mut env).unwrap();
2073 match v {
2074 Value::Namespace(ns) => assert_eq!(ns.get().name.as_ref(), "fresh.ns"),
2075 other => panic!("expected Namespace, got {other:?}"),
2076 }
2077 let v2 = eval_src("(find-ns 'fresh.ns)", &mut env).unwrap();
2079 assert!(matches!(v2, Value::Namespace(_)));
2080 }
2081
2082 #[test]
2085 fn test_dynamic_var_basic() {
2086 let (globals, mut env) = make_env();
2087 let result = eval_src("(def ^:dynamic *x* 10) (binding [*x* 42] *x*)", &mut env).unwrap();
2088 assert_eq!(result, Value::Long(42));
2089 let root = globals.lookup_in_ns("user", "*x*");
2091 assert_eq!(root, Some(Value::Long(10)));
2092 }
2093
2094 #[test]
2095 fn test_dynamic_var_restore() {
2096 let (_globals, mut env) = make_env();
2097 eval_src("(def ^:dynamic *x* 10)", &mut env).unwrap();
2098 eval_src("(binding [*x* 42] *x*)", &mut env).unwrap();
2099 let val = eval_src("*x*", &mut env).unwrap();
2101 assert_eq!(val, Value::Long(10));
2102 }
2103
2104 #[test]
2105 fn test_dynamic_var_nested() {
2106 let (_, mut env) = make_env();
2107 eval_src("(def ^:dynamic *x* 1)", &mut env).unwrap();
2108 let result = eval_src("(binding [*x* 2] (binding [*x* 3] *x*))", &mut env).unwrap();
2109 assert_eq!(result, Value::Long(3));
2110 let val = eval_src("*x*", &mut env).unwrap();
2112 assert_eq!(val, Value::Long(1));
2113 }
2114
2115 #[test]
2116 fn test_dynamic_var_unaffected() {
2117 let (_, mut env) = make_env();
2118 eval_src("(def ^:dynamic *x* 10)", &mut env).unwrap();
2119 eval_src("(def y 99)", &mut env).unwrap();
2120 eval_src("(binding [*x* 42] *x*)", &mut env).unwrap();
2121 let val = eval_src("y", &mut env).unwrap();
2123 assert_eq!(val, Value::Long(99));
2124 }
2125
2126 #[test]
2127 #[ignore = "future/thread spawn not yet implemented (Phase A1 — GcPtr: !Send)"]
2128 fn test_binding_conveyance() {
2129 let (_, mut env) = make_env();
2130 eval_src("(def ^:dynamic *x* 10)", &mut env).unwrap();
2131 let result = eval_src("(binding [*x* 42] @(future *x*))", &mut env).unwrap();
2132 assert_eq!(result, Value::Long(42));
2133 }
2134
2135 #[test]
2136 fn test_var_set_in_binding() {
2137 let (_, mut env) = make_env();
2138 eval_src("(def ^:dynamic *x* 10)", &mut env).unwrap();
2139 let inside = eval_src("(binding [*x* 1] (set! *x* 2) *x*)", &mut env).unwrap();
2141 assert_eq!(inside, Value::Long(2));
2142 let root = eval_src("*x*", &mut env).unwrap();
2144 assert_eq!(root, Value::Long(10));
2145 }
2146
2147 #[test]
2148 fn test_with_bindings_star() {
2149 let (_, mut env) = make_env();
2150 eval_src("(def ^:dynamic *x* 10)", &mut env).unwrap();
2151 let result = eval_src("(with-bindings* {#'*x* 99} (fn [] *x*))", &mut env).unwrap();
2152 assert_eq!(result, Value::Long(99));
2153 }
2154
2155 #[test]
2156 fn test_binding_fully_qualified_cross_ns_dynamic_var() {
2157 let (_, mut env) = make_env();
2158 let result = eval_src(
2159 r#"
2160 (ns other.ns)
2161 (def ^:dynamic *dispatch* nil)
2162 (ns user)
2163 (binding [other.ns/*dispatch* (fn [x] x)]
2164 (other.ns/*dispatch* 42))
2165 "#,
2166 &mut env,
2167 )
2168 .unwrap();
2169 assert_eq!(result, Value::Long(42));
2170 }
2171
2172 #[test]
2173 fn test_binding_aliased_cross_ns_dynamic_var() {
2174 let dir = temp_ns_dir("binding_aliased_cross_ns_dynamic_var");
2180 std::fs::create_dir_all(dir.join("replicant")).unwrap();
2181 std::fs::write(
2182 dir.join("replicant").join("core.cljrs"),
2183 r#"(ns replicant.core)
2184 (def ^:dynamic *dispatch* nil)
2185 (defn call-dispatch [x] (*dispatch* x))"#,
2186 )
2187 .unwrap();
2188 let (_, mut env) = make_env_with_paths(vec![dir]);
2189 let result = eval_src(
2190 r#"
2191 (ns life-cycle-test
2192 (:require [replicant.core :as r]))
2193 (binding [r/*dispatch* (fn [x] x)]
2194 (r/call-dispatch 42))
2195 "#,
2196 &mut env,
2197 )
2198 .unwrap();
2199 assert_eq!(result, Value::Long(42));
2200 }
2201
2202 #[test]
2203 fn test_meta_on_var() {
2204 let (_, mut env) = make_env();
2205 eval_src("(def ^:dynamic *x* 1)", &mut env).unwrap();
2206 let m = eval_src("(meta #'*x*)", &mut env).unwrap();
2207 if let Value::Map(mv) = &m {
2209 let kw = Value::keyword(cljrs_value::Keyword::parse("dynamic"));
2210 assert_eq!(mv.get(&kw), Some(Value::Bool(true)));
2211 } else {
2212 panic!("expected map, got {m:?}");
2213 }
2214 }
2215
2216 #[test]
2217 fn test_bound_pred() {
2218 let (_, mut env) = make_env();
2219 eval_src("(def ^:dynamic *x* 1)", &mut env).unwrap();
2220 let t = eval_src("(bound? #'*x*)", &mut env).unwrap();
2221 assert_eq!(t, Value::Bool(true));
2222 }
2223
2224 #[test]
2225 fn test_alter_var_root() {
2226 let (_, mut env) = make_env();
2227 eval_src("(def x 1)", &mut env).unwrap();
2228 eval_src("(alter-var-root #'x inc)", &mut env).unwrap();
2229 let val = eval_src("x", &mut env).unwrap();
2230 assert_eq!(val, Value::Long(2));
2231 }
2232
2233 #[test]
2236 fn test_clojure_test_is_pass() {
2237 let (_, mut env) = make_env();
2239 eval_src(
2240 "(require '[clojure.test :refer [is deftest run-tests]])",
2241 &mut env,
2242 )
2243 .unwrap();
2244 let v = eval_src("(is (= 1 1))", &mut env).unwrap();
2245 assert_eq!(v, Value::Bool(true));
2246 }
2247
2248 #[test]
2249 fn test_clojure_test_is_fail() {
2250 let (_, mut env) = make_env();
2252 eval_src("(require '[clojure.test :refer [is]])", &mut env).unwrap();
2253 let v = eval_src("(is (= 1 2))", &mut env).unwrap();
2254 assert_eq!(v, Value::Bool(false));
2255 }
2256
2257 #[test]
2258 fn test_clojure_test_is_catch_error() {
2259 let (_, mut env) = make_env();
2261 eval_src("(require '[clojure.test :refer [is]])", &mut env).unwrap();
2262 let v = eval_src("(is (/ 1 0))", &mut env).unwrap();
2263 assert_eq!(v, Value::Bool(false));
2264 }
2265
2266 #[test]
2267 fn test_clojure_test_deftest_and_run() {
2268 let (_, mut env) = make_env();
2270 eval_src(
2271 "(require '[clojure.test :refer [deftest is run-tests]])",
2272 &mut env,
2273 )
2274 .unwrap();
2275 eval_src("(deftest my-passing-test (is (= 1 1)))", &mut env).unwrap();
2276 eval_src("(deftest my-failing-test (is (= 1 2)))", &mut env).unwrap();
2277 let counters = eval_src("(run-tests)", &mut env).unwrap();
2278 if let Value::Map(m) = counters {
2280 let get = |k: &str| {
2281 m.get(&Value::keyword(cljrs_value::Keyword {
2282 namespace: None,
2283 name: Arc::from(k),
2284 }))
2285 };
2286 assert_eq!(get("test"), Some(Value::Long(2)));
2287 assert_eq!(get("pass"), Some(Value::Long(1)));
2288 assert_eq!(get("fail"), Some(Value::Long(1)));
2289 assert_eq!(get("error"), Some(Value::Long(0)));
2290 } else {
2291 panic!("expected map from run-tests, got {counters:?}");
2292 }
2293 }
2294
2295 #[test]
2296 fn test_alter_meta_bang() {
2297 let (_, mut env) = make_env();
2299 eval_src("(def myvar 42)", &mut env).unwrap();
2300 eval_src("(alter-meta! #'myvar assoc :foo :bar)", &mut env).unwrap();
2301 let m = eval_src("(meta #'myvar)", &mut env).unwrap();
2302 if let Value::Map(map) = m {
2303 let foo_key = Value::keyword(cljrs_value::Keyword {
2304 namespace: None,
2305 name: Arc::from("foo"),
2306 });
2307 assert!(map.get(&foo_key).is_some());
2308 } else {
2309 panic!("expected map, got {m:?}");
2310 }
2311 }
2312
2313 #[test]
2314 fn test_catch_runtime_error() {
2315 let (_, mut env) = make_env();
2317 let v = eval_src(r#"(try (/ 1 0) (catch Exception e "caught"))"#, &mut env).unwrap();
2318 assert_eq!(v, Value::string("caught".to_string()));
2319 }
2320
2321 #[test]
2322 fn test_ns_resolve() {
2323 let (_, mut env) = make_env();
2324 eval_src("(def somevar 99)", &mut env).unwrap();
2325 let v = eval_src("(ns-resolve *ns* 'somevar)", &mut env).unwrap();
2327 assert!(matches!(v, Value::Var(_)));
2328 let v2 = eval_src("(ns-resolve *ns* 'nonexistent)", &mut env).unwrap();
2330 assert_eq!(v2, Value::Nil);
2331 }
2332
2333 #[test]
2336 fn test_assoc_chain_virtualized() {
2337 let v = eval_str(
2339 "(let [m {}
2340 a (assoc m :x 1)
2341 b (assoc a :y 2)
2342 c (assoc b :z 3)]
2343 c)",
2344 )
2345 .unwrap();
2346 assert!(matches!(&v, Value::Map(_)));
2348 if let Value::Map(m) = &v {
2349 assert_eq!(m.count(), 3);
2350 assert_eq!(m.get(&Value::keyword(Keyword::simple("x"))), Some(long(1)));
2351 assert_eq!(m.get(&Value::keyword(Keyword::simple("y"))), Some(long(2)));
2352 assert_eq!(m.get(&Value::keyword(Keyword::simple("z"))), Some(long(3)));
2353 }
2354 }
2355
2356 #[test]
2357 fn test_conj_chain_virtualized() {
2358 let v = eval_str(
2360 "(let [v [1]
2361 a (conj v 2)
2362 b (conj a 3)
2363 c (conj b 4)]
2364 c)",
2365 )
2366 .unwrap();
2367 assert_eq!(v, eval_str("[1 2 3 4]").unwrap());
2368 }
2369
2370 #[test]
2371 fn test_assoc_chain_intermediate_used_no_virtualize() {
2372 let v = eval_str(
2375 "(let [a (assoc {} :x 1)
2376 b (assoc a :y 2)]
2377 (list (count a) (count b)))",
2378 )
2379 .unwrap();
2380 if let Value::List(l) = &v {
2382 let items: Vec<_> = l.get().iter().cloned().collect();
2383 assert_eq!(items, vec![long(1), long(2)]);
2384 } else {
2385 panic!("expected list, got {:?}", v);
2386 }
2387 }
2388
2389 #[test]
2390 fn test_assoc_chain_on_existing_map() {
2391 let v = eval_str(
2393 "(let [m {:a 1}
2394 a (assoc m :b 2)
2395 b (assoc a :c 3)]
2396 b)",
2397 )
2398 .unwrap();
2399 if let Value::Map(m) = &v {
2400 assert_eq!(m.count(), 3);
2401 } else {
2402 panic!("expected map");
2403 }
2404 }
2405
2406 #[test]
2409 fn test_post_condition_percent_bound() {
2410 let v = eval_str("(defn g [x] {:post [(pos? %)]} (inc x)) (g 5)").unwrap();
2412 assert_eq!(v, long(6));
2413 }
2414
2415 #[test]
2416 fn test_post_condition_violation_throws() {
2417 let r = eval_str("(defn g [x] {:post [(neg? %)]} (inc x)) (g 5)");
2419 assert!(r.is_err(), "expected error from failing :post condition");
2420 }
2421
2422 #[test]
2423 fn test_pre_condition_passes() {
2424 let v = eval_str("(defn g [x] {:pre [(pos? x)]} (inc x)) (g 5)").unwrap();
2425 assert_eq!(v, long(6));
2426 }
2427
2428 #[test]
2429 fn test_pre_condition_violation_throws() {
2430 let r = eval_str("(defn g [x] {:pre [(pos? x)]} (inc x)) (g -1)");
2431 assert!(r.is_err(), "expected error from failing :pre condition");
2432 }
2433
2434 #[test]
2435 fn test_pre_and_post_conditions() {
2436 let v = eval_str("(defn g [x] {:pre [(pos? x)] :post [(> % x)]} (inc x)) (g 3)").unwrap();
2437 assert_eq!(v, long(4));
2438 }
2439
2440 #[test]
2441 fn test_post_condition_no_pre() {
2442 let v = eval_str("(defn h [x] {:post [(number? %)]} (inc x)) (h 2)").unwrap();
2444 assert_eq!(v, long(3));
2445 }
2446
2447 #[test]
2448 fn test_pre_condition_no_post() {
2449 let v = eval_str("(defn h [x] {:pre [(number? x)]} x) (h 42)").unwrap();
2451 assert_eq!(v, long(42));
2452 }
2453}