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