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::Cancelled => {
389 return Err(EvalError::Thrown(CljxFuture::cancelled_error()));
390 }
391 FutureState::Running => {
392 guard = f.get().cond.wait(guard).unwrap();
393 }
394 }
395 }
396 }
397 other => Err(EvalError::Runtime(format!(
398 "cannot deref {}",
399 other.type_name()
400 ))),
401 }
402}
403
404pub fn eval_body(forms: &[Form], env: &mut Env) -> EvalResult {
406 let mut result = Value::Nil;
407 for form in forms {
408 result = eval(form, env)?;
409 }
410 Ok(result)
411}
412
413fn eval_reader_cond(clauses: &[Form], env: &mut Env) -> EvalResult {
416 match crate::builtins::form::select_reader_cond(clauses) {
417 Some(f) => eval(f, env),
418 None => Ok(Value::Nil),
419 }
420}
421
422fn eval_tagged_literal(tag: &str, inner: &Form, env: &mut Env) -> EvalResult {
425 match tag {
426 "uuid" => {
427 let val = eval(inner, env)?;
428 match &val {
429 Value::Str(s) => {
430 let uuid = uuid::Uuid::parse_str(s.get())
431 .map_err(|e| EvalError::Runtime(format!("invalid UUID: {e}")))?;
432 Ok(Value::uuid(uuid.as_u128()))
433 }
434 _ => Err(EvalError::Runtime(format!(
435 "#uuid expects a string, got {}",
436 val.type_name()
437 ))),
438 }
439 }
440 "inst" => {
441 let val = eval(inner, env)?;
443 Ok(val)
444 }
445 _ => Err(EvalError::Runtime(format!(
446 "unknown tagged literal: #{tag}"
447 ))),
448 }
449}
450
451#[cfg(test)]
454mod tests {
455 use super::*;
456 use crate::env::env::GlobalEnv;
457 use std::sync::Arc;
458
459 fn make_env() -> (Arc<GlobalEnv>, Env) {
460 let globals = crate::Runtime::builder()
461 .execution_mode(crate::ExecutionMode::TreeWalk)
462 .eager_clojure_test(true)
463 .build()
464 .expect("runtime")
465 .into_globals();
466 let env = Env::new(globals.clone(), "user");
467 (globals, env)
468 }
469
470 fn eval_str(src: &str) -> EvalResult {
471 let (_, mut env) = make_env();
472 eval_src(src, &mut env)
473 }
474
475 fn eval_src(src: &str, env: &mut Env) -> EvalResult {
476 let mut parser = cljrs_reader::Parser::new(src.to_string(), "<test>".to_string());
477 let forms = parser.parse_all().map_err(EvalError::Read)?;
478 let mut result = Value::Nil;
479 for form in forms {
480 result = eval(&form, env)?;
481 }
482 Ok(result)
483 }
484
485 fn long(n: i64) -> Value {
486 Value::Long(n)
487 }
488 fn bool_v(b: bool) -> Value {
489 Value::Bool(b)
490 }
491
492 #[test]
495 fn test_literal_int() {
496 assert_eq!(eval_str("42").unwrap(), long(42));
497 }
498
499 #[test]
500 fn test_literal_string() {
501 assert!(matches!(eval_str("\"hello\"").unwrap(), Value::Str(_)));
502 }
503
504 #[test]
505 fn test_literal_nil() {
506 assert_eq!(eval_str("nil").unwrap(), Value::Nil);
507 }
508
509 #[test]
510 fn test_literal_true() {
511 assert_eq!(eval_str("true").unwrap(), bool_v(true));
512 }
513
514 #[test]
515 fn test_literal_false() {
516 assert_eq!(eval_str("false").unwrap(), bool_v(false));
517 }
518
519 #[test]
522 fn test_add() {
523 assert_eq!(eval_str("(+ 1 2)").unwrap(), long(3));
524 }
525
526 #[test]
527 fn test_mul() {
528 assert_eq!(eval_str("(* 2 3)").unwrap(), long(6));
529 }
530
531 #[test]
532 fn test_div_exact() {
533 assert_eq!(eval_str("(/ 10 2)").unwrap(), long(5));
534 }
535
536 #[test]
537 fn test_sub() {
538 assert_eq!(eval_str("(- 10 3)").unwrap(), long(7));
539 }
540
541 #[test]
544 fn test_let_simple() {
545 assert_eq!(eval_str("(let* [x 1 y 2] (+ x y))").unwrap(), long(3));
546 }
547
548 #[test]
549 fn test_let_shadowing() {
550 assert_eq!(eval_str("(let* [x 1] (let* [x 10] x))").unwrap(), long(10));
551 }
552
553 #[test]
556 fn test_fn_call() {
557 assert_eq!(eval_str("((fn* [x] (* x x)) 5)").unwrap(), long(25));
558 }
559
560 #[test]
561 fn test_closure_capture() {
562 assert_eq!(
563 eval_str("(let* [n 3] ((fn* [x] (+ x n)) 4))").unwrap(),
564 long(7)
565 );
566 }
567
568 #[test]
569 fn test_multi_arity_fn() {
570 assert_eq!(
571 eval_str("((fn* ([x] x) ([x y] (+ x y))) 1 2)").unwrap(),
572 long(3)
573 );
574 }
575
576 #[test]
579 fn test_loop_recur() {
580 let result =
581 eval_str("(loop* [i 0 acc 0] (if (= i 5) acc (recur (inc i) (+ acc i))))").unwrap();
582 assert_eq!(result, long(10));
583 }
584
585 #[test]
588 fn test_def() {
589 let (_, mut env) = make_env();
590 eval_src("(def x 42)", &mut env).unwrap();
591 assert_eq!(eval_src("x", &mut env).unwrap(), long(42));
592 }
593
594 #[test]
595 fn test_defn() {
596 let (_, mut env) = make_env();
597 eval_src("(defn square [x] (* x x))", &mut env).unwrap();
598 assert_eq!(eval_src("(square 7)", &mut env).unwrap(), long(49));
599 }
600
601 #[test]
604 fn test_if_truthy() {
605 assert_eq!(eval_str("(if true 1 2)").unwrap(), long(1));
606 }
607
608 #[test]
609 fn test_if_falsy() {
610 assert_eq!(eval_str("(if false 1 2)").unwrap(), long(2));
611 }
612
613 #[test]
614 fn test_if_nil_branch() {
615 assert_eq!(eval_str("(if false 1)").unwrap(), Value::Nil);
616 }
617
618 #[test]
621 fn test_do() {
622 assert_eq!(eval_str("(do 1 2 3)").unwrap(), long(3));
623 }
624
625 #[test]
628 fn test_quote_list() {
629 let v = eval_str("'(1 2 3)").unwrap();
630 assert!(matches!(v, Value::List(_)));
631 }
632
633 #[test]
636 fn test_keyword_lookup() {
637 assert_eq!(eval_str("(:a {:a 1})").unwrap(), long(1));
638 }
639
640 #[test]
641 fn test_keyword_lookup_missing() {
642 assert_eq!(eval_str("(:b {:a 1})").unwrap(), Value::Nil);
643 }
644
645 #[test]
648 fn test_map_literal() {
649 let v = eval_str("{:a 1 :b 2}").unwrap();
650 assert!(matches!(v, Value::Map(_)));
651 if let Value::Map(m) = &v {
652 assert_eq!(m.count(), 2);
653 }
654 }
655
656 #[test]
657 fn test_vector_literal() {
658 let v = eval_str("[1 2 3]").unwrap();
659 assert!(matches!(v, Value::Vector(_)));
660 }
661
662 #[test]
663 fn test_set_literal() {
664 let v = eval_str("#{1 2 3}").unwrap();
665 assert!(matches!(v, Value::Set(_)));
666 }
667
668 #[test]
669 fn test_contains_q_vector_non_integer_key_returns_false() {
670 assert_eq!(eval_str("(contains? [1 2 3] :a)").unwrap(), bool_v(false));
673 assert_eq!(
674 eval_str("(contains? [1 2 3] \"x\")").unwrap(),
675 bool_v(false)
676 );
677 assert_eq!(eval_str("(contains? [1 2 3] 0)").unwrap(), bool_v(true));
679 assert_eq!(eval_str("(contains? [1 2 3] 9)").unwrap(), bool_v(false));
680 }
681
682 #[test]
685 fn test_set_bang() {
686 let (_, mut env) = make_env();
687 eval_src("(def x 1)", &mut env).unwrap();
688 eval_src("(set! x 99)", &mut env).unwrap();
689 assert_eq!(eval_src("x", &mut env).unwrap(), long(99));
690 }
691
692 #[test]
695 fn test_throw_catch() {
696 let v = eval_str("(try (throw (ex-info \"oops\" {})) (catch Exception e (ex-message e)))")
697 .unwrap();
698 assert!(matches!(v, Value::Str(_)));
699 }
700
701 #[test]
702 fn test_try_no_throw() {
703 assert_eq!(eval_str("(try 42)").unwrap(), long(42));
704 }
705
706 #[test]
707 fn test_catch_default_catches_internal_value_errors() {
708 for src in [
712 "(try (nth [1 2 3] 10) (catch :default e \"caught\"))",
713 "(try (aget (long-array 3) 10) (catch :default e \"caught\"))",
714 "(try (aset (long-array 3) 10 5) (catch :default e \"caught\"))",
715 ] {
716 let v = eval_str(src).unwrap();
717 assert_eq!(v, Value::string("caught"), "{src}");
718 }
719 }
720
721 #[test]
722 fn test_catch_default_binds_clean_ex_message() {
723 let v = eval_str("(try (nth [1 2 3] 10) (catch :default e (ex-message e)))").unwrap();
727 assert_eq!(v, Value::string("index out of bounds: 10 >= 3"));
728 }
729
730 #[test]
731 fn test_finally_runs_and_value_is_discarded() {
732 let v = eval_str("(let [a (atom 0)] (try 1 (finally (reset! a 5))) @a)").unwrap();
734 assert_eq!(v, long(5));
735 assert_eq!(eval_str("(try 1 (finally 2))").unwrap(), long(1));
736 }
737
738 #[test]
739 fn test_finally_runs_after_catch() {
740 let v = eval_str(
741 "(let [a (atom 0)]
742 (try (throw (ex-info \"x\" {})) (catch Exception e :caught)
743 (finally (reset! a 9)))
744 @a)",
745 )
746 .unwrap();
747 assert_eq!(v, long(9));
748 }
749
750 #[test]
751 fn test_finally_exception_propagates() {
752 assert!(eval_str("(try 1 (finally (throw (ex-info \"boom\" {}))))").is_err());
754 }
755
756 #[test]
757 fn test_catch_reader_conditional_type() {
758 let v =
764 eval_str(r#"(try (throw (ex-info "boom" {})) (catch #?(:rust Exception) e :caught))"#)
765 .unwrap();
766 let kw_caught = Value::keyword(cljrs_value::Keyword::simple("caught"));
767 assert_eq!(v, kw_caught, "single-branch reader cond");
768
769 let v = eval_str(concat!(
771 "(try (throw (ex-info \"boom\" {})) ",
772 "(catch #?(:clj Throwable :cljs :default :rust Exception) e :caught))",
773 ))
774 .unwrap();
775 assert_eq!(v, kw_caught, "multi-branch reader cond");
776
777 let v =
780 eval_str(r#"(try (throw (ex-info "boom" {})) (catch #?(:rust :default) e :caught))"#)
781 .unwrap();
782 assert_eq!(v, kw_caught, "reader cond resolves to :default");
783
784 let result =
786 eval_str(r#"(try (throw (ex-info "boom" {})) (catch #?(:clj Throwable) e :caught))"#);
787 assert!(
788 result.is_err(),
789 "no matching :rust branch must let exception escape"
790 );
791 }
792
793 #[test]
794 fn test_nth_negative_index() {
795 assert_eq!(
797 eval_str("(= (nth [10 20 30] -1 :nf) :nf)").unwrap(),
798 bool_v(true)
799 );
800 assert!(eval_str("(nth [10 20 30] -1)").is_err());
801 assert!(eval_str("(nth '(1 2 3) -1)").is_err());
802 assert_eq!(
804 eval_str("(= (nth (range) -1 :nf) :nf)").unwrap(),
805 bool_v(true)
806 );
807 assert_eq!(
808 eval_str("(= (nth '(1 2 3) -1 :nf) :nf)").unwrap(),
809 bool_v(true)
810 );
811 }
812
813 #[test]
816 fn test_sequential_destructure() {
817 assert_eq!(eval_str("(let* [[a b] [1 2]] (+ a b))").unwrap(), long(3));
818 }
819
820 #[test]
821 fn test_rest_destructure() {
822 let v = eval_str("(let* [[h & t] [1 2 3]] t)").unwrap();
823 assert!(matches!(v, Value::List(_)));
824 if let Value::List(l) = &v {
825 assert_eq!(l.get().count(), 2);
826 }
827 }
828
829 #[test]
832 fn test_defmacro() {
833 let (_, mut env) = make_env();
834 eval_src("(defmacro my-if [t a b] (list 'if t a b))", &mut env).unwrap();
835 assert_eq!(eval_src("(my-if true 1 2)", &mut env).unwrap(), long(1));
836 assert_eq!(eval_src("(my-if false 1 2)", &mut env).unwrap(), long(2));
837 }
838
839 #[test]
842 fn test_syntax_quote_basic() {
843 let (_, mut env) = make_env();
844 eval_src("(def b 2)", &mut env).unwrap();
845 let v = eval_src("`(a ~b)", &mut env).unwrap();
846 assert!(matches!(v, Value::List(_)));
847 if let Value::List(l) = &v {
848 let items: Vec<_> = l.get().iter().cloned().collect();
850 assert_eq!(items.len(), 2);
851 assert_eq!(items[1], long(2));
852 }
853 }
854
855 #[test]
858 fn test_reader_cond_rust() {
859 assert_eq!(eval_str("#?(:rust 1 :clj 2)").unwrap(), long(1));
861 }
862
863 #[test]
864 fn test_reader_cond_default() {
865 assert_eq!(eval_str("#?(:clj 2 :default 99)").unwrap(), long(99));
867 }
868
869 #[test]
870 fn test_reader_cond_splice_in_vector() {
871 assert_eq!(
872 eval_str("[1 #?@(:rust [:a :b]) 2]").unwrap(),
873 eval_str("[1 :a :b 2]").unwrap()
874 );
875 }
876
877 #[test]
878 fn test_reader_cond_splice_in_set() {
879 assert_eq!(
880 eval_str("#{1 #?@(:rust [2 3]) 4}").unwrap(),
881 eval_str("#{1 2 3 4}").unwrap()
882 );
883 }
884
885 #[test]
886 fn test_reader_cond_splice_in_map() {
887 assert_eq!(
888 eval_str("{:a 1 #?@(:rust [:b 2]) :c 3}").unwrap(),
889 eval_str("{:a 1 :b 2 :c 3}").unwrap()
890 );
891 }
892
893 #[test]
894 fn test_reader_cond_splice_in_call_args() {
895 assert_eq!(
896 eval_str("(vector 1 #?@(:rust [2 3]) 4)").unwrap(),
897 eval_str("[1 2 3 4]").unwrap()
898 );
899 }
900
901 #[test]
902 fn test_reader_cond_splice_no_match_removed() {
903 assert_eq!(
904 eval_str("[1 #?@(:clj [:a :b]) 2]").unwrap(),
905 eval_str("[1 2]").unwrap()
906 );
907 }
908
909 proptest::proptest! {
914 #![proptest_config(proptest::prelude::ProptestConfig::with_cases(48))]
915 #[test]
919 fn prop_splice_evals_as_inline_in_every_container(
920 np in 0usize..3, nm in 0usize..3, ns in 0usize..3,
921 ) {
922 let kw_run = |start: usize, n: usize| {
923 (start..start + n)
924 .map(|i| format!(":v{i}"))
925 .collect::<Vec<_>>()
926 .join(" ")
927 };
928 let p = kw_run(0, np);
929 let m = kw_run(np, nm);
930 let s = kw_run(np + nm, ns);
931 let (_g, mut env) = make_env();
932 for (open, close, quote) in
933 [("[", "]", ""), ("#{", "}", ""), ("(", ")", "'"), ("(vector ", ")", "")]
934 {
935 let spliced = format!("{quote}{open}{p} #?@(:rust [{m}]) {s}{close}");
936 let inlined = format!("{quote}{open}{p} {m} {s}{close}");
937 proptest::prop_assert_eq!(
938 eval_src(&spliced, &mut env).unwrap(),
939 eval_src(&inlined, &mut env).unwrap(),
940 "container {}{}",
941 open,
942 close
943 );
944 }
945 }
946 }
947
948 #[derive(Clone, Debug)]
955 enum Slot {
956 Plain,
958 NonSplice { matches: bool },
960 Splice { matches: bool, n: usize },
962 }
963
964 fn slot_strat() -> impl proptest::strategy::Strategy<Value = Slot> {
965 use proptest::strategy::{Just, Strategy};
966 proptest::prop_oneof![
967 Just(Slot::Plain),
968 proptest::bool::ANY.prop_map(|matches| Slot::NonSplice { matches }),
969 (proptest::bool::ANY, 0usize..3).prop_map(|(matches, n)| Slot::Splice { matches, n }),
970 ]
971 }
972
973 fn render_slots(slots: &[Slot], unit: &dyn Fn(usize) -> String) -> (String, String) {
978 let mut next = 0usize;
979 let (mut written, mut inlined) = (Vec::new(), Vec::new());
980 for slot in slots {
981 match *slot {
982 Slot::Plain => {
983 let u = unit(next);
984 next += 1;
985 written.push(u.clone());
986 inlined.push(u);
987 }
988 Slot::NonSplice { matches } => {
989 let u = unit(next);
990 next += 1;
991 let key = if matches { "rust" } else { "clj" };
992 written.push(format!("#?(:{key} {u})"));
993 if matches {
994 inlined.push(u);
995 }
996 }
997 Slot::Splice { matches, n } => {
998 let us: Vec<String> = (0..n)
999 .map(|_| {
1000 let u = unit(next);
1001 next += 1;
1002 u
1003 })
1004 .collect();
1005 let key = if matches { "rust" } else { "clj" };
1006 written.push(format!("#?@(:{key} [{}])", us.join(" ")));
1007 if matches {
1008 inlined.extend(us);
1009 }
1010 }
1011 }
1012 }
1013 (written.join(" "), inlined.join(" "))
1014 }
1015
1016 fn pair_slot_strat() -> impl proptest::strategy::Strategy<Value = Slot> {
1020 use proptest::strategy::{Just, Strategy};
1021 proptest::prop_oneof![
1022 Just(Slot::Plain),
1023 (proptest::bool::ANY, 0usize..3).prop_map(|(matches, n)| Slot::Splice { matches, n }),
1024 ]
1025 }
1026
1027 fn kw_unit(i: usize) -> String {
1028 format!(":k{i}")
1029 }
1030
1031 fn binding_unit(i: usize) -> String {
1034 format!("x{i} {i}")
1035 }
1036
1037 proptest::proptest! {
1038 #![proptest_config(proptest::prelude::ProptestConfig::with_cases(48))]
1039
1040 #[test]
1046 fn prop_splice_agrees_across_eval_quote_and_syntax_quote(
1047 slots in proptest::collection::vec(slot_strat(), 0..5),
1048 ) {
1049 let (written, inlined) = render_slots(&slots, &kw_unit);
1050 let even = inlined.split_whitespace().count().is_multiple_of(2);
1051 let (_g, mut env) = make_env();
1052 for (open, close) in [("[", "]"), ("#{", "}"), ("{", "}")] {
1053 if open == "{" && !even {
1054 continue;
1055 }
1056 for prefix in ["", "'", "`"] {
1057 let got = eval_src(&format!("{prefix}{open}{written}{close}"), &mut env);
1058 let want = eval_src(&format!("{prefix}{open}{inlined}{close}"), &mut env);
1059 proptest::prop_assert_eq!(
1060 got.map_err(|e| e.to_string()),
1061 want.map_err(|e| e.to_string()),
1062 "prefix {:?} container {}{}", prefix, open, close
1063 );
1064 }
1065 }
1066 for prefix in ["'", "`"] {
1068 let got = eval_src(&format!("{prefix}({written})"), &mut env);
1069 let want = eval_src(&format!("{prefix}({inlined})"), &mut env);
1070 proptest::prop_assert_eq!(
1071 got.map_err(|e| e.to_string()),
1072 want.map_err(|e| e.to_string()),
1073 "prefix {:?} list", prefix
1074 );
1075 }
1076 }
1077
1078 #[test]
1081 fn prop_splice_in_binding_vectors_equals_inline(
1082 slots in proptest::collection::vec(pair_slot_strat(), 0..4),
1083 ) {
1084 let (written, inlined) = render_slots(&slots, &binding_unit);
1085 let names: Vec<String> = inlined
1086 .split_whitespace()
1087 .step_by(2)
1088 .map(str::to_string)
1089 .collect();
1090 let body = format!("[{}]", names.join(" "));
1091 let (_g, mut env) = make_env();
1092 for head in ["let*", "loop*"] {
1093 let got = eval_src(&format!("({head} [{written}] {body})"), &mut env);
1094 let want = eval_src(&format!("({head} [{inlined}] {body})"), &mut env);
1095 proptest::prop_assert_eq!(
1096 got.map_err(|e| e.to_string()),
1097 want.map_err(|e| e.to_string()),
1098 "{}", head
1099 );
1100 }
1101 }
1102 }
1103
1104 #[test]
1105 fn quoted_map_splice_lands_in_key_value_positions() {
1106 assert_eq!(
1107 eval_str("'{:a 1 #?@(:rust [:b 2]) :c 3}").unwrap(),
1108 eval_str("{:a 1 :b 2 :c 3}").unwrap()
1109 );
1110 }
1111
1112 #[test]
1113 fn syntax_quoted_splice_is_expanded_not_nil() {
1114 assert_eq!(
1115 eval_str("`(1 #?@(:rust [2 3]) 4)").unwrap(),
1116 eval_str("'(1 2 3 4)").unwrap()
1117 );
1118 }
1119
1120 #[test]
1121 fn map_literal_with_only_an_unmatched_conditional_reads_as_empty() {
1122 assert_eq!(eval_str("{#?(:clj :a)}").unwrap(), eval_str("{}").unwrap());
1125 }
1126
1127 #[test]
1128 fn loop_binding_vector_expands_splices() {
1129 assert_eq!(eval_str("(loop* [#?@(:rust [x 1])] x)").unwrap(), long(1));
1130 }
1131
1132 #[test]
1135 fn test_unbound_symbol() {
1136 let r = eval_str("undefined-var-xyz");
1137 assert!(matches!(r, Err(EvalError::UnboundSymbol(_))));
1138 }
1139
1140 #[test]
1141 fn test_wrong_arity() {
1142 let (_, mut env) = make_env();
1143 eval_src("(defn one-arg [x] x)", &mut env).unwrap();
1144 let r = eval_src("(one-arg 1 2)", &mut env);
1145 assert!(matches!(r, Err(EvalError::Arity { .. })));
1146 }
1147
1148 #[test]
1149 fn test_not_callable() {
1150 let r = eval_str("(42 1 2)");
1151 assert!(matches!(r, Err(EvalError::NotCallable(_))));
1152 }
1153
1154 #[test]
1157 fn test_map_fn() {
1158 assert_eq!(
1159 eval_str("(vec (map inc [1 2 3]))").unwrap(),
1160 eval_str("[2 3 4]").unwrap()
1161 );
1162 }
1163
1164 #[test]
1165 fn test_filter_fn() {
1166 assert_eq!(
1167 eval_str("(vec (filter odd? [1 2 3 4 5]))").unwrap(),
1168 eval_str("[1 3 5]").unwrap()
1169 );
1170 }
1171
1172 #[test]
1173 fn test_reduce_fn() {
1174 assert_eq!(eval_str("(reduce + [1 2 3 4 5])").unwrap(), long(15));
1175 }
1176
1177 #[test]
1178 fn test_apply_fn() {
1179 assert_eq!(eval_str("(apply + [1 2 3])").unwrap(), long(6));
1180 }
1181
1182 #[test]
1183 fn test_atom_ops() {
1184 let (_, mut env) = make_env();
1185 eval_src("(def a (atom 0))", &mut env).unwrap();
1186 eval_src("(swap! a inc)", &mut env).unwrap();
1187 assert_eq!(eval_src("(deref a)", &mut env).unwrap(), long(1));
1188 }
1189
1190 #[test]
1191 fn test_when_macro() {
1192 assert_eq!(eval_str("(when true 42)").unwrap(), long(42));
1193 assert_eq!(eval_str("(when false 42)").unwrap(), Value::Nil);
1194 }
1195
1196 #[test]
1197 fn test_cond_macro() {
1198 assert_eq!(eval_str("(cond false 1 true 2)").unwrap(), long(2));
1199 }
1200
1201 #[test]
1202 fn test_and_or() {
1203 assert_eq!(eval_str("(and 1 2 3)").unwrap(), long(3));
1204 assert_eq!(eval_str("(and 1 false 3)").unwrap(), bool_v(false));
1205 assert_eq!(eval_str("(or false nil 42)").unwrap(), long(42));
1206 assert_eq!(eval_str("(or false nil)").unwrap(), Value::Nil);
1207 }
1208
1209 #[test]
1212 fn test_lazy_range() {
1213 assert_eq!(
1214 eval_str("(= (into [] (take 5 (range))) [0 1 2 3 4])").unwrap(),
1215 bool_v(true)
1216 );
1217 }
1218
1219 #[test]
1220 fn test_lazy_range_bounded() {
1221 assert_eq!(
1222 eval_str("(= (into [] (range 3)) [0 1 2])").unwrap(),
1223 bool_v(true)
1224 );
1225 }
1226
1227 #[test]
1228 fn test_lazy_iterate() {
1229 assert_eq!(
1230 eval_str("(= (into [] (take 3 (iterate inc 0))) [0 1 2])").unwrap(),
1231 bool_v(true)
1232 );
1233 }
1234
1235 #[test]
1236 fn test_lazy_repeat() {
1237 assert_eq!(
1238 eval_str("(= (into [] (take 3 (repeat :x))) [:x :x :x])").unwrap(),
1239 bool_v(true)
1240 );
1241 }
1242
1243 #[test]
1244 fn test_lazy_cycle() {
1245 assert_eq!(
1246 eval_str("(= (into [] (take 5 (cycle [1 2]))) [1 2 1 2 1])").unwrap(),
1247 bool_v(true)
1248 );
1249 }
1250
1251 #[test]
1254 fn test_assoc_destructure() {
1255 assert_eq!(
1256 eval_str("(let [{:keys [a b]} {:a 1 :b 2}] (+ a b))").unwrap(),
1257 long(3)
1258 );
1259 }
1260
1261 #[test]
1262 fn test_assoc_destructure_or() {
1263 assert_eq!(
1264 eval_str("(let [{:keys [a b] :or {b 99}} {:a 1}] b)").unwrap(),
1265 long(99)
1266 );
1267 }
1268
1269 #[test]
1272 fn test_letfn() {
1273 assert_eq!(
1274 eval_str("(letfn [(fact [n] (if (= n 0) 1 (* n (fact (dec n)))))] (fact 5))").unwrap(),
1275 long(120)
1276 );
1277 }
1278
1279 #[test]
1286 fn test_letfn_forward_reference() {
1287 assert_eq!(
1288 eval_str("(letfn [(f [n] (g n)) (g [n] (* n 2))] (f 21))").unwrap(),
1289 long(42)
1290 );
1291 }
1292
1293 #[test]
1294 fn test_letfn_mutual_recursion() {
1295 let defs = "(letfn [(my-even? [n] (if (= n 0) true (my-odd? (dec n))))
1298 (my-odd? [n] (if (= n 0) false (my-even? (dec n))))]";
1299 assert_eq!(
1300 eval_str(&format!("{defs} (my-even? 10))")).unwrap(),
1301 Value::Bool(true)
1302 );
1303 assert_eq!(
1304 eval_str(&format!("{defs} (my-odd? 10))")).unwrap(),
1305 Value::Bool(false)
1306 );
1307 }
1308
1309 #[test]
1310 fn test_letfn_three_way_mutual_recursion() {
1311 let defs = "(letfn [(a [n] (if (= n 0) 1 (b (dec n))))
1312 (b [n] (if (= n 0) 2 (c (dec n))))
1313 (c [n] (if (= n 0) 3 (a (dec n))))]";
1314 for (arg, want) in [(0, 1), (1, 2), (2, 3), (3, 1)] {
1315 assert_eq!(
1316 eval_str(&format!("{defs} (a {arg}))")).unwrap(),
1317 long(want),
1318 "(a {arg})"
1319 );
1320 }
1321 }
1322
1323 #[test]
1324 fn test_letfn_closes_over_outer_local() {
1325 assert_eq!(
1326 eval_str("(let [start 10] (letfn [(step [n] (+ n start))] (step 5)))").unwrap(),
1327 long(15)
1328 );
1329 }
1330
1331 #[test]
1332 fn test_letfn_shadows_enclosing_binding() {
1333 assert_eq!(
1336 eval_str("(let [f 1] (letfn [(g [] (f)) (f [] 7)] (g)))").unwrap(),
1337 long(7)
1338 );
1339 }
1340
1341 #[test]
1342 fn test_letfn_binding_name_must_be_a_symbol() {
1343 assert!(eval_str("(letfn [(42 [n] n)] 1)").is_err());
1344 }
1345
1346 #[test]
1349 fn test_in_ns() {
1350 let (_, mut env) = make_env();
1351 eval_src("(in-ns 'mytest)", &mut env).unwrap();
1352 assert_eq!(env.current_ns.as_ref(), "mytest");
1353 eval_src("(in-ns 'user)", &mut env).unwrap();
1354 assert_eq!(env.current_ns.as_ref(), "user");
1355 }
1356
1357 #[test]
1360 fn test_spit_slurp() {
1361 let path = std::env::temp_dir().join("cljrs_test_spit_slurp.txt");
1362 let path_str = path.to_str().unwrap();
1363 let src = format!(
1364 r#"(do (spit "{}" "hello clojurust") (slurp "{}"))"#,
1365 path_str, path_str
1366 );
1367 let result = eval_str(&src).unwrap();
1368 if let Value::Str(s) = result {
1369 assert_eq!(s.get().as_str(), "hello clojurust");
1370 } else {
1371 panic!("expected string result from slurp");
1372 }
1373 let _ = std::fs::remove_file(path);
1374 }
1375
1376 #[test]
1379 fn test_update_in() {
1380 assert_eq!(
1381 eval_str("(= (update-in {:a {:b 1}} [:a :b] inc) {:a {:b 2}})").unwrap(),
1382 bool_v(true)
1383 );
1384 }
1385
1386 #[test]
1389 fn test_if_let_truthy() {
1390 assert_eq!(eval_str("(if-let [x 42] x :nope)").unwrap(), long(42));
1391 }
1392
1393 #[test]
1394 fn test_if_let_falsy() {
1395 assert_eq!(
1396 eval_str("(if-let [x nil] x :nope)").unwrap(),
1397 eval_str(":nope").unwrap()
1398 );
1399 }
1400
1401 #[test]
1402 fn test_when_let_truthy() {
1403 assert_eq!(eval_str("(when-let [x 7] (* x 2))").unwrap(), long(14));
1404 }
1405
1406 #[test]
1407 fn test_when_let_falsy() {
1408 assert_eq!(eval_str("(when-let [x nil] 99)").unwrap(), Value::Nil);
1409 }
1410
1411 #[test]
1414 fn test_math_trig() {
1415 assert_eq!(eval_str("(Math/sin 0)").unwrap(), Value::Double(0.0));
1417 assert_eq!(eval_str("(Math/cos 0)").unwrap(), Value::Double(1.0));
1418 }
1419
1420 #[test]
1421 fn test_math_constants() {
1422 assert!(
1423 matches!(eval_str("Math/PI").unwrap(), Value::Double(v) if (v - std::f64::consts::PI).abs() < 1e-10)
1424 );
1425 assert!(
1426 matches!(eval_str("Math/E").unwrap(), Value::Double(v) if (v - std::f64::consts::E).abs() < 1e-10)
1427 );
1428 }
1429
1430 #[test]
1431 fn test_math_log_exp() {
1432 assert_eq!(eval_str("(Math/exp 0)").unwrap(), Value::Double(1.0));
1434 assert_eq!(eval_str("(Math/log 1)").unwrap(), Value::Double(0.0));
1435 }
1436
1437 #[test]
1440 fn test_defprotocol() {
1441 let result = eval_str(
1443 r#"
1444 (defprotocol Greet
1445 (greet [this]))
1446 (greet "hello")
1447 "#,
1448 );
1449 assert!(result.is_err());
1450 let msg = result.unwrap_err().to_string();
1451 assert!(msg.contains("No implementation"), "got: {msg}");
1452 }
1453
1454 #[test]
1455 fn test_extend_type() {
1456 let result = eval_str(
1457 r#"
1458 (defprotocol Greet
1459 (greet [this]))
1460 (extend-type String
1461 Greet
1462 (greet [this] (str "Hello, " this "!")))
1463 (greet "world")
1464 "#,
1465 )
1466 .unwrap();
1467 assert_eq!(result, Value::string("Hello, world!"));
1468 }
1469
1470 #[test]
1471 fn test_protocol_dispatch() {
1472 let result = eval_str(
1473 r#"
1474 (defprotocol Describable
1475 (describe [this]))
1476 (extend-type String
1477 Describable
1478 (describe [this] (str "string:" this)))
1479 (extend-type Long
1480 Describable
1481 (describe [this] (str "long:" this)))
1482 [(describe "hi") (describe 42)]
1483 "#,
1484 )
1485 .unwrap();
1486 assert!(matches!(result, Value::Vector(_)));
1487 let s = format!("{}", result);
1488 assert!(s.contains("string:hi"), "got: {s}");
1489 assert!(s.contains("long:42"), "got: {s}");
1490 }
1491
1492 #[test]
1493 fn test_extend_protocol() {
1494 let result = eval_str(
1495 r#"
1496 (defprotocol Showable
1497 (show [this]))
1498 (extend-protocol Showable
1499 String
1500 (show [this] (str "S:" this))
1501 Long
1502 (show [this] (str "L:" this)))
1503 [(show "x") (show 7)]
1504 "#,
1505 )
1506 .unwrap();
1507 let s = format!("{}", result);
1508 assert!(s.contains("S:x"), "got: {s}");
1509 assert!(s.contains("L:7"), "got: {s}");
1510 }
1511
1512 #[test]
1513 fn test_extend_via_metadata() {
1514 let result = eval_str(
1521 r#"
1522 (defprotocol IRender
1523 :extend-via-metadata true
1524 (create-element [this tag-name]))
1525 (def renderer (with-meta {} {`create-element (fn [this tag-name] (str "made-" tag-name))}))
1526 (create-element renderer "div")
1527 "#,
1528 )
1529 .unwrap();
1530 assert_eq!(result, Value::string("made-div"));
1531 }
1532
1533 #[test]
1534 fn test_extend_via_metadata_falls_back_to_type_tag() {
1535 let result = eval_str(
1538 r#"
1539 (defprotocol IRender
1540 :extend-via-metadata true
1541 (create-element [this tag-name]))
1542 (extend-type Map
1543 IRender
1544 (create-element [this tag-name] (str "type-tag-" tag-name)))
1545 [(create-element {} "span")
1546 (create-element (with-meta {} {`create-element (fn [this tag-name] (str "meta-" tag-name))}) "div")]
1547 "#,
1548 )
1549 .unwrap();
1550 let s = format!("{}", result);
1551 assert!(s.contains("type-tag-span"), "got: {s}");
1552 assert!(s.contains("meta-div"), "got: {s}");
1553 }
1554
1555 #[test]
1556 fn test_extend_via_metadata_cross_ns() {
1557 let dir = temp_ns_dir("extend_via_metadata_cross_ns");
1564 std::fs::create_dir_all(dir.join("replicant")).unwrap();
1565 std::fs::write(
1566 dir.join("replicant").join("core.cljrs"),
1567 r#"(ns replicant.core)
1568 (defprotocol IRender
1569 :extend-via-metadata true
1570 (create-element [this tag-name]))"#,
1571 )
1572 .unwrap();
1573 let (_, mut env) = make_env_with_paths(vec![dir]);
1574 let result = eval_src(
1575 r#"
1576 (ns mutation-log-test
1577 (:require [replicant.core :refer [create-element]]))
1578 (def renderer (with-meta {} {`create-element (fn [this tag-name] (str "made-" tag-name))}))
1579 (create-element renderer "div")
1580 "#,
1581 &mut env,
1582 )
1583 .unwrap();
1584 assert_eq!(result, Value::string("made-div"));
1585 }
1586
1587 #[test]
1588 fn test_extend_via_metadata_cross_ns_via_alias() {
1589 let dir = temp_ns_dir("extend_via_metadata_cross_ns_via_alias");
1598 std::fs::create_dir_all(dir.join("replicant")).unwrap();
1599 std::fs::write(
1600 dir.join("replicant").join("protocols.cljrs"),
1601 r#"(ns replicant.protocols)
1602 (defprotocol IRender
1603 :extend-via-metadata true
1604 (attached? [this el]))"#,
1605 )
1606 .unwrap();
1607 let (_, mut env) = make_env_with_paths(vec![dir]);
1608 let result = eval_src(
1609 r#"
1610 (ns mutation-log-test
1611 (:require [replicant.protocols :as p]))
1612 (def r (with-meta {:log []} {`p/attached? (fn [_ el] el)}))
1613 (p/attached? r :el)
1614 "#,
1615 &mut env,
1616 )
1617 .unwrap();
1618 assert_eq!(result, Value::keyword(Keyword::simple("el")));
1619 }
1620
1621 #[test]
1622 fn test_satisfies() {
1623 let result = eval_str(
1624 r#"
1625 (defprotocol Animal
1626 (speak [this]))
1627 (extend-type String
1628 Animal
1629 (speak [this] this))
1630 [(satisfies? Animal "dog") (satisfies? Animal 42)]
1631 "#,
1632 )
1633 .unwrap();
1634 let s = format!("{}", result);
1635 assert!(s.contains("true"), "got: {s}");
1636 assert!(s.contains("false"), "got: {s}");
1637 }
1638
1639 #[test]
1640 fn test_defmulti_defmethod() {
1641 let result = eval_str(
1643 r#"
1644 (defmulti area :shape)
1645 (defmethod area :circle [m] (* 3 (:r m) (:r m)))
1646 (defmethod area :rectangle [m] (* (:w m) (:h m)))
1647 [(area {:shape :circle :r 2}) (area {:shape :rectangle :w 3 :h 4})]
1648 "#,
1649 )
1650 .unwrap();
1651 let s = format!("{}", result);
1652 assert!(s.contains("12"), "got: {s}");
1654 }
1655
1656 #[test]
1657 fn test_default_dispatch() {
1658 let result = eval_str(
1659 r#"
1660 (defmulti classify :kind)
1661 (defmethod classify :default [x] :unknown)
1662 (defmethod classify :cat [x] :meow)
1663 [(classify {:kind :dog}) (classify {:kind :cat})]
1664 "#,
1665 )
1666 .unwrap();
1667 let s = format!("{}", result);
1668 assert!(s.contains(":unknown"), "got: {s}");
1669 assert!(s.contains(":meow"), "got: {s}");
1670 }
1671
1672 #[test]
1673 fn test_prefer_method() {
1674 let result = eval_str(
1676 r#"
1677 (defmulti foo identity)
1678 (defmethod foo :a [x] 1)
1679 (prefer-method foo :a :b)
1680 (foo :a)
1681 "#,
1682 )
1683 .unwrap();
1684 assert_eq!(result, Value::Long(1));
1685 }
1686
1687 #[test]
1688 fn test_remove_method() {
1689 let result = eval_str(
1690 r#"
1691 (defmulti bar identity)
1692 (defmethod bar :x [_] 99)
1693 (remove-method bar :x)
1694 (bar :x)
1695 "#,
1696 );
1697 assert!(result.is_err());
1698 let msg = result.unwrap_err().to_string();
1699 assert!(msg.contains("No method"), "got: {msg}");
1700 }
1701
1702 #[test]
1705 fn test_compare_and_set() {
1706 let result = eval_str(
1707 r#"
1708 (let [a (atom 10)]
1709 [(compare-and-set! a 10 20) ; succeeds: 10 == 10
1710 (compare-and-set! a 10 30) ; fails: 20 != 10
1711 @a])
1712 "#,
1713 )
1714 .unwrap();
1715 let s = format!("{}", result);
1716 assert!(s.contains("true"), "got: {s}");
1717 assert!(s.contains("false"), "got: {s}");
1718 assert!(s.contains("20"), "got: {s}");
1719 }
1720
1721 #[test]
1722 fn test_volatile() {
1723 let result = eval_str(
1724 r#"
1725 (let [v (volatile! 1)]
1726 (vreset! v 2)
1727 (vswap! v + 10)
1728 @v)
1729 "#,
1730 )
1731 .unwrap();
1732 assert_eq!(result, Value::Long(12));
1733 }
1734
1735 #[test]
1736 fn test_delay() {
1737 let result = eval_str(
1739 r#"
1740 (let [calls (atom 0)
1741 d (delay (swap! calls inc) 42)]
1742 [@calls (force d) @calls (force d) @calls])
1743 "#,
1744 )
1745 .unwrap();
1746 let s = format!("{}", result);
1747 assert!(s.starts_with("[0 42 1 42 1]"), "got: {s}");
1750 }
1751
1752 #[test]
1753 fn test_realized() {
1754 let result = eval_str(
1755 r#"
1756 (let [d (delay 99)]
1757 [(realized? d) (force d) (realized? d)])
1758 "#,
1759 )
1760 .unwrap();
1761 let s = format!("{}", result);
1762 assert!(s.starts_with("[false 99 true]"), "got: {s}");
1763 }
1764
1765 #[test]
1766 fn test_promise() {
1767 let result = eval_str(
1768 r#"
1769 (let [p (promise)]
1770 (deliver p 42)
1771 (deliver p 99) ; second deliver is ignored
1772 @p)
1773 "#,
1774 )
1775 .unwrap();
1776 assert_eq!(result, Value::Long(42));
1777 }
1778
1779 #[test]
1780 #[ignore = "clojure.core/future is undefined by design: @f deadlocks the one \
1781 thread the task needs (GcPtr: !Send). cljrs.core.experimental/future \
1782 is the cooperative stand-in, read with (await f)"]
1783 fn test_future() {
1784 let result = eval_str(
1785 r#"
1786 (let [f (future (+ 1 2))]
1787 @f)
1788 "#,
1789 )
1790 .unwrap();
1791 assert_eq!(result, Value::Long(3));
1792 }
1793
1794 #[test]
1795 #[ignore = "agent not yet implemented (Phase A1 — GcPtr: !Send)"]
1796 fn test_agent_send() {
1797 let result = eval_str(
1798 r#"
1799 (let [a (agent 0)]
1800 (send a + 1)
1801 (send a + 2)
1802 (await-agent a)
1803 @a)
1804 "#,
1805 )
1806 .unwrap();
1807 assert_eq!(result, Value::Long(3));
1808 }
1809
1810 #[test]
1811 #[ignore = "agent not yet implemented (Phase A1 — GcPtr: !Send)"]
1812 fn test_agent_error_restart() {
1813 let result = eval_str(
1814 r#"
1815 (let [a (agent 10)]
1816 (send a (fn [_] (throw (ex-info "boom" {}))))
1817 (await-agent a)
1818 (let [err (agent-error a)]
1819 (restart-agent a 99)
1820 [err @a]))
1821 "#,
1822 )
1823 .unwrap();
1824 let s = format!("{}", result);
1825 assert!(s.contains("boom"), "got: {s}");
1827 assert!(s.contains("99"), "got: {s}");
1828 }
1829
1830 #[test]
1831 fn test_defrecord_basic() {
1832 let result = eval_str(
1834 r#"
1835 (defrecord Point [x y])
1836 (let [p (->Point 3 4)]
1837 [(:x p) (:y p)])
1838 "#,
1839 )
1840 .unwrap();
1841 assert_eq!(result.to_string(), "[3 4]");
1842 }
1843
1844 #[test]
1845 fn test_defrecord_map_constructor() {
1846 let result = eval_str(
1847 r#"
1848 (defrecord Color [r g b])
1849 (let [c (map->Color {:r 255 :g 128 :b 0})]
1850 [(:r c) (:g c) (:b c)])
1851 "#,
1852 )
1853 .unwrap();
1854 assert_eq!(result.to_string(), "[255 128 0]");
1855 }
1856
1857 #[test]
1858 fn test_defrecord_assoc() {
1859 let result = eval_str(
1861 r#"
1862 (defrecord Pt [x y])
1863 (let [p (->Pt 1 2)
1864 q (assoc p :x 99)]
1865 [(:x q) (:y q) (record? q)])
1866 "#,
1867 )
1868 .unwrap();
1869 assert_eq!(result.to_string(), "[99 2 true]");
1870 }
1871
1872 #[test]
1873 fn test_defrecord_with_protocol() {
1874 let result = eval_str(
1875 r#"
1876 (defprotocol IShape
1877 (area [this]))
1878 (defrecord Circle [radius]
1879 IShape
1880 (area [this] (* 3 (:radius this) (:radius this))))
1881 (let [c (->Circle 5)]
1882 (area c))
1883 "#,
1884 )
1885 .unwrap();
1886 assert_eq!(result, cljrs_value::Value::Long(75));
1887 }
1888
1889 #[test]
1890 fn test_instance_q() {
1891 let result = eval_str(
1892 r#"
1893 (defrecord Dog [name])
1894 (let [d (->Dog "Rex")]
1895 [(instance? Dog d) (instance? Dog 42)])
1896 "#,
1897 )
1898 .unwrap();
1899 assert_eq!(result.to_string(), "[true false]");
1900 }
1901
1902 #[test]
1903 fn test_reify_basic() {
1904 let result = eval_str(
1905 r#"
1906 (defprotocol IGreet
1907 (greet [this name]))
1908 (let [greeter (reify IGreet
1909 (greet [this name] (str "Hello, " name "!")))]
1910 (greet greeter "World"))
1911 "#,
1912 )
1913 .unwrap();
1914 assert_eq!(result.to_string(), "\"Hello, World!\"");
1915 }
1916
1917 fn temp_ns_dir(test_name: &str) -> std::path::PathBuf {
1920 let dir = std::env::temp_dir().join(format!("cljrs_test_{test_name}"));
1921 let _ = std::fs::remove_dir_all(&dir);
1922 std::fs::create_dir_all(&dir).unwrap();
1923 dir
1924 }
1925
1926 fn make_env_with_paths(paths: Vec<std::path::PathBuf>) -> (Arc<GlobalEnv>, Env) {
1927 let globals = crate::Runtime::builder()
1928 .execution_mode(crate::ExecutionMode::TreeWalk)
1929 .eager_clojure_test(true)
1930 .source_paths(paths)
1931 .build()
1932 .expect("runtime")
1933 .into_globals();
1934 let env = Env::new(globals.clone(), "user");
1935 (globals, env)
1936 }
1937
1938 #[test]
1939 fn test_require_as() {
1940 let dir = temp_ns_dir("require_as");
1941 std::fs::write(
1942 dir.join("mylib.cljrs"),
1943 "(ns mylib) (defn greet [n] (str \"hello \" n))",
1944 )
1945 .unwrap();
1946 let (_, mut env) = make_env_with_paths(vec![dir]);
1947 let result = eval_src("(require '[mylib :as ml]) (ml/greet \"world\")", &mut env).unwrap();
1948 assert_eq!(result.to_string(), "\"hello world\"");
1949 }
1950
1951 #[test]
1952 fn test_require_refer() {
1953 let dir = temp_ns_dir("require_refer");
1954 std::fs::write(
1955 dir.join("myutil.cljrs"),
1956 "(ns myutil) (defn twice [x] (* 2 x))",
1957 )
1958 .unwrap();
1959 let (_, mut env) = make_env_with_paths(vec![dir]);
1960 let result = eval_src("(require '[myutil :refer [twice]]) (twice 21)", &mut env).unwrap();
1961 assert_eq!(result, Value::Long(42));
1962 }
1963
1964 #[test]
1965 fn test_require_refer_all() {
1966 let dir = temp_ns_dir("require_refer_all");
1967 std::fs::write(
1968 dir.join("mymath.cljrs"),
1969 "(ns mymath) (defn square [x] (* x x))",
1970 )
1971 .unwrap();
1972 let (_, mut env) = make_env_with_paths(vec![dir]);
1973 let result = eval_src("(require '[mymath :refer :all]) (square 7)", &mut env).unwrap();
1974 assert_eq!(result, Value::Long(49));
1975 }
1976
1977 #[test]
1978 fn test_ns_require_clause() {
1979 let dir = temp_ns_dir("ns_require");
1980 std::fs::write(
1981 dir.join("greeter.cljrs"),
1982 "(ns greeter) (defn hi [n] (str \"Hi \" n))",
1983 )
1984 .unwrap();
1985 let (_, mut env) = make_env_with_paths(vec![dir]);
1986 let result = eval_src(
1987 "(ns myapp (:require [greeter :as g])) (g/hi \"Alice\")",
1988 &mut env,
1989 )
1990 .unwrap();
1991 assert_eq!(result.to_string(), "\"Hi Alice\"");
1992 }
1993
1994 #[test]
1995 fn test_var_quote_alias_resolution() {
1996 let dir = temp_ns_dir("var_quote_alias");
1999 std::fs::create_dir_all(dir.join("lib")).unwrap();
2001 std::fs::write(
2002 dir.join("lib/core.cljrs"),
2003 "(ns lib.core) (defn public [x] (* x 2))",
2004 )
2005 .unwrap();
2006 let (_, mut env) = make_env_with_paths(vec![dir]);
2007 let call_result = eval_src("(require '[lib.core :as l]) (l/public 21)", &mut env).unwrap();
2009 assert_eq!(call_result, Value::Long(42));
2010 let var_result = eval_src("#'l/public", &mut env).unwrap();
2012 assert!(
2013 matches!(var_result, Value::Var(_)),
2014 "expected Var, got {var_result:?}"
2015 );
2016 assert_eq!(var_result.to_string(), "#'lib.core/public");
2017 let var_special = eval_src("(var l/public)", &mut env).unwrap();
2019 assert_eq!(var_special.to_string(), "#'lib.core/public");
2020 }
2021
2022 #[test]
2023 fn test_require_idempotent() {
2024 let dir = temp_ns_dir("require_idempotent");
2025 std::fs::write(
2027 dir.join("counter.cljrs"),
2028 "(ns counter) (def loaded-count (atom 0)) (swap! loaded-count inc)",
2029 )
2030 .unwrap();
2031 let (globals, mut env) = make_env_with_paths(vec![dir]);
2032 eval_src("(require 'counter)", &mut env).unwrap();
2033 eval_src("(require 'counter)", &mut env).unwrap();
2034 let count = globals.lookup_in_ns("counter", "loaded-count").unwrap();
2036 if let Value::Atom(a) = count {
2037 assert_eq!(a.get().deref(), Value::Long(1));
2038 } else {
2039 panic!("expected atom");
2040 }
2041 }
2042
2043 #[test]
2044 fn test_require_not_found() {
2045 let (_, mut env) = make_env_with_paths(vec![]);
2046 let err = eval_src("(require 'nonexistent.ns)", &mut env).unwrap_err();
2047 let msg = format!("{err:?}");
2048 assert!(msg.contains("nonexistent.ns"), "unexpected error: {msg}");
2049 }
2050
2051 #[test]
2052 fn test_require_circular() {
2053 let dir = temp_ns_dir("require_circular");
2054 std::fs::write(dir.join("cira.cljrs"), "(ns cira (:require [cirb]))").unwrap();
2056 std::fs::write(dir.join("cirb.cljrs"), "(ns cirb (:require [cira]))").unwrap();
2057 let (_, mut env) = make_env_with_paths(vec![dir]);
2058 let err = eval_src("(require 'cira)", &mut env).unwrap_err();
2059 let msg = format!("{err:?}");
2060 assert!(
2061 msg.contains("circular"),
2062 "expected circular error, got: {msg}"
2063 );
2064 }
2065
2066 #[test]
2067 fn test_load_file() {
2068 let dir = temp_ns_dir("load_file");
2069 let path = dir.join("script.cljrs");
2070 std::fs::write(&path, "(+ 1 2)").unwrap();
2071 let (_, mut env) = make_env_with_paths(vec![]);
2072 let result = eval_src(&format!("(load-file \"{}\")", path.display()), &mut env).unwrap();
2073 assert_eq!(result, Value::Long(3));
2074 }
2075
2076 #[test]
2079 fn test_star_ns_initial() {
2080 let (_, mut env) = make_env();
2082 let v = eval_src("*ns*", &mut env).unwrap();
2083 match v {
2084 Value::Namespace(ns) => assert_eq!(ns.get().name.as_ref(), "user"),
2085 other => panic!("expected Namespace, got {other:?}"),
2086 }
2087 }
2088
2089 #[test]
2090 fn test_star_ns_after_in_ns() {
2091 let (_, mut env) = make_env();
2092 eval_src("(in-ns 'myns)", &mut env).unwrap();
2093 let v = eval_src("*ns*", &mut env).unwrap();
2094 match v {
2095 Value::Namespace(ns) => assert_eq!(ns.get().name.as_ref(), "myns"),
2096 other => panic!("expected Namespace, got {other:?}"),
2097 }
2098 }
2099
2100 #[test]
2101 fn test_star_ns_after_ns_form() {
2102 let (_, mut env) = make_env();
2103 eval_src("(ns mytest.ns)", &mut env).unwrap();
2104 let v = eval_src("*ns*", &mut env).unwrap();
2105 match v {
2106 Value::Namespace(ns) => assert_eq!(ns.get().name.as_ref(), "mytest.ns"),
2107 other => panic!("expected Namespace, got {other:?}"),
2108 }
2109 }
2110
2111 #[test]
2112 fn test_ns_name() {
2113 let (_, mut env) = make_env();
2114 let v = eval_src("(ns-name *ns*)", &mut env).unwrap();
2115 match v {
2116 Value::Symbol(s) => assert_eq!(s.get().name.as_ref(), "user"),
2117 other => panic!("expected Symbol, got {other:?}"),
2118 }
2119 }
2120
2121 #[test]
2122 fn test_find_ns() {
2123 let (_, mut env) = make_env();
2124 let v = eval_src("(find-ns 'user)", &mut env).unwrap();
2126 assert!(matches!(v, Value::Namespace(_)));
2127 let v2 = eval_src("(find-ns 'nonexistent)", &mut env).unwrap();
2129 assert_eq!(v2, Value::Nil);
2130 }
2131
2132 #[test]
2133 fn test_all_ns() {
2134 let (_, mut env) = make_env();
2135 let v = eval_src("(all-ns)", &mut env).unwrap();
2136 let names: Vec<String> = match &v {
2138 Value::List(l) => l
2139 .get()
2140 .iter()
2141 .filter_map(|ns| match ns {
2142 Value::Namespace(n) => Some(n.get().name.as_ref().to_string()),
2143 _ => None,
2144 })
2145 .collect(),
2146 other => panic!("expected list, got {other:?}"),
2147 };
2148 assert!(names.contains(&"user".to_string()));
2149 assert!(names.contains(&"clojure.core".to_string()));
2150 }
2151
2152 #[test]
2153 fn test_ns_interns() {
2154 let (_, mut env) = make_env();
2155 eval_src("(def my-test-var 42)", &mut env).unwrap();
2156 let v = eval_src("(ns-interns *ns*)", &mut env).unwrap();
2157 let Value::Map(m) = v else {
2158 panic!("expected map")
2159 };
2160 let sym = Value::symbol(cljrs_value::Symbol::simple("my-test-var"));
2162 assert!(m.get(&sym).is_some());
2163 }
2164
2165 #[test]
2166 fn test_create_ns() {
2167 let (_, mut env) = make_env();
2168 let v = eval_src("(create-ns 'fresh.ns)", &mut env).unwrap();
2169 match v {
2170 Value::Namespace(ns) => assert_eq!(ns.get().name.as_ref(), "fresh.ns"),
2171 other => panic!("expected Namespace, got {other:?}"),
2172 }
2173 let v2 = eval_src("(find-ns 'fresh.ns)", &mut env).unwrap();
2175 assert!(matches!(v2, Value::Namespace(_)));
2176 }
2177
2178 #[test]
2181 fn test_dynamic_var_basic() {
2182 let (globals, mut env) = make_env();
2183 let result = eval_src("(def ^:dynamic *x* 10) (binding [*x* 42] *x*)", &mut env).unwrap();
2184 assert_eq!(result, Value::Long(42));
2185 let root = globals.lookup_in_ns("user", "*x*");
2187 assert_eq!(root, Some(Value::Long(10)));
2188 }
2189
2190 #[test]
2191 fn test_dynamic_var_restore() {
2192 let (_globals, mut env) = make_env();
2193 eval_src("(def ^:dynamic *x* 10)", &mut env).unwrap();
2194 eval_src("(binding [*x* 42] *x*)", &mut env).unwrap();
2195 let val = eval_src("*x*", &mut env).unwrap();
2197 assert_eq!(val, Value::Long(10));
2198 }
2199
2200 #[test]
2201 fn test_dynamic_var_nested() {
2202 let (_, mut env) = make_env();
2203 eval_src("(def ^:dynamic *x* 1)", &mut env).unwrap();
2204 let result = eval_src("(binding [*x* 2] (binding [*x* 3] *x*))", &mut env).unwrap();
2205 assert_eq!(result, Value::Long(3));
2206 let val = eval_src("*x*", &mut env).unwrap();
2208 assert_eq!(val, Value::Long(1));
2209 }
2210
2211 #[test]
2212 fn test_dynamic_var_unaffected() {
2213 let (_, mut env) = make_env();
2214 eval_src("(def ^:dynamic *x* 10)", &mut env).unwrap();
2215 eval_src("(def y 99)", &mut env).unwrap();
2216 eval_src("(binding [*x* 42] *x*)", &mut env).unwrap();
2217 let val = eval_src("y", &mut env).unwrap();
2219 assert_eq!(val, Value::Long(99));
2220 }
2221
2222 #[test]
2223 #[ignore = "clojure.core/future is undefined by design; the experimental \
2224 stand-in does not convey dynamic bindings to its task"]
2225 fn test_binding_conveyance() {
2226 let (_, mut env) = make_env();
2227 eval_src("(def ^:dynamic *x* 10)", &mut env).unwrap();
2228 let result = eval_src("(binding [*x* 42] @(future *x*))", &mut env).unwrap();
2229 assert_eq!(result, Value::Long(42));
2230 }
2231
2232 #[test]
2233 fn test_var_set_in_binding() {
2234 let (_, mut env) = make_env();
2235 eval_src("(def ^:dynamic *x* 10)", &mut env).unwrap();
2236 let inside = eval_src("(binding [*x* 1] (set! *x* 2) *x*)", &mut env).unwrap();
2238 assert_eq!(inside, Value::Long(2));
2239 let root = eval_src("*x*", &mut env).unwrap();
2241 assert_eq!(root, Value::Long(10));
2242 }
2243
2244 #[test]
2245 fn test_with_bindings_star() {
2246 let (_, mut env) = make_env();
2247 eval_src("(def ^:dynamic *x* 10)", &mut env).unwrap();
2248 let result = eval_src("(with-bindings* {#'*x* 99} (fn [] *x*))", &mut env).unwrap();
2249 assert_eq!(result, Value::Long(99));
2250 }
2251
2252 #[test]
2253 fn test_binding_fully_qualified_cross_ns_dynamic_var() {
2254 let (_, mut env) = make_env();
2255 let result = eval_src(
2256 r#"
2257 (ns other.ns)
2258 (def ^:dynamic *dispatch* nil)
2259 (ns user)
2260 (binding [other.ns/*dispatch* (fn [x] x)]
2261 (other.ns/*dispatch* 42))
2262 "#,
2263 &mut env,
2264 )
2265 .unwrap();
2266 assert_eq!(result, Value::Long(42));
2267 }
2268
2269 #[test]
2270 fn test_binding_aliased_cross_ns_dynamic_var() {
2271 let dir = temp_ns_dir("binding_aliased_cross_ns_dynamic_var");
2277 std::fs::create_dir_all(dir.join("replicant")).unwrap();
2278 std::fs::write(
2279 dir.join("replicant").join("core.cljrs"),
2280 r#"(ns replicant.core)
2281 (def ^:dynamic *dispatch* nil)
2282 (defn call-dispatch [x] (*dispatch* x))"#,
2283 )
2284 .unwrap();
2285 let (_, mut env) = make_env_with_paths(vec![dir]);
2286 let result = eval_src(
2287 r#"
2288 (ns life-cycle-test
2289 (:require [replicant.core :as r]))
2290 (binding [r/*dispatch* (fn [x] x)]
2291 (r/call-dispatch 42))
2292 "#,
2293 &mut env,
2294 )
2295 .unwrap();
2296 assert_eq!(result, Value::Long(42));
2297 }
2298
2299 #[test]
2300 fn test_meta_on_var() {
2301 let (_, mut env) = make_env();
2302 eval_src("(def ^:dynamic *x* 1)", &mut env).unwrap();
2303 let m = eval_src("(meta #'*x*)", &mut env).unwrap();
2304 if let Value::Map(mv) = &m {
2306 let kw = Value::keyword(cljrs_value::Keyword::parse("dynamic"));
2307 assert_eq!(mv.get(&kw), Some(Value::Bool(true)));
2308 } else {
2309 panic!("expected map, got {m:?}");
2310 }
2311 }
2312
2313 #[test]
2314 fn test_bound_pred() {
2315 let (_, mut env) = make_env();
2316 eval_src("(def ^:dynamic *x* 1)", &mut env).unwrap();
2317 let t = eval_src("(bound? #'*x*)", &mut env).unwrap();
2318 assert_eq!(t, Value::Bool(true));
2319 }
2320
2321 #[test]
2322 fn test_alter_var_root() {
2323 let (_, mut env) = make_env();
2324 eval_src("(def x 1)", &mut env).unwrap();
2325 eval_src("(alter-var-root #'x inc)", &mut env).unwrap();
2326 let val = eval_src("x", &mut env).unwrap();
2327 assert_eq!(val, Value::Long(2));
2328 }
2329
2330 #[test]
2333 fn test_clojure_test_is_pass() {
2334 let (_, mut env) = make_env();
2336 eval_src(
2337 "(require '[clojure.test :refer [is deftest run-tests]])",
2338 &mut env,
2339 )
2340 .unwrap();
2341 let v = eval_src("(is (= 1 1))", &mut env).unwrap();
2342 assert_eq!(v, Value::Bool(true));
2343 }
2344
2345 #[test]
2346 fn test_clojure_test_is_fail() {
2347 let (_, mut env) = make_env();
2349 eval_src("(require '[clojure.test :refer [is]])", &mut env).unwrap();
2350 let v = eval_src("(is (= 1 2))", &mut env).unwrap();
2351 assert_eq!(v, Value::Bool(false));
2352 }
2353
2354 #[test]
2355 fn test_clojure_test_is_catch_error() {
2356 let (_, mut env) = make_env();
2358 eval_src("(require '[clojure.test :refer [is]])", &mut env).unwrap();
2359 let v = eval_src("(is (/ 1 0))", &mut env).unwrap();
2360 assert_eq!(v, Value::Bool(false));
2361 }
2362
2363 #[test]
2364 fn test_clojure_test_deftest_and_run() {
2365 let (_, mut env) = make_env();
2367 eval_src(
2368 "(require '[clojure.test :refer [deftest is run-tests]])",
2369 &mut env,
2370 )
2371 .unwrap();
2372 eval_src("(deftest my-passing-test (is (= 1 1)))", &mut env).unwrap();
2373 eval_src("(deftest my-failing-test (is (= 1 2)))", &mut env).unwrap();
2374 let counters = eval_src("(run-tests)", &mut env).unwrap();
2375 if let Value::Map(m) = counters {
2377 let get = |k: &str| {
2378 m.get(&Value::keyword(cljrs_value::Keyword {
2379 namespace: None,
2380 name: Arc::from(k),
2381 }))
2382 };
2383 assert_eq!(get("test"), Some(Value::Long(2)));
2384 assert_eq!(get("pass"), Some(Value::Long(1)));
2385 assert_eq!(get("fail"), Some(Value::Long(1)));
2386 assert_eq!(get("error"), Some(Value::Long(0)));
2387 } else {
2388 panic!("expected map from run-tests, got {counters:?}");
2389 }
2390 }
2391
2392 #[test]
2393 fn test_alter_meta_bang() {
2394 let (_, mut env) = make_env();
2396 eval_src("(def myvar 42)", &mut env).unwrap();
2397 eval_src("(alter-meta! #'myvar assoc :foo :bar)", &mut env).unwrap();
2398 let m = eval_src("(meta #'myvar)", &mut env).unwrap();
2399 if let Value::Map(map) = m {
2400 let foo_key = Value::keyword(cljrs_value::Keyword {
2401 namespace: None,
2402 name: Arc::from("foo"),
2403 });
2404 assert!(map.get(&foo_key).is_some());
2405 } else {
2406 panic!("expected map, got {m:?}");
2407 }
2408 }
2409
2410 #[test]
2411 fn test_catch_runtime_error() {
2412 let (_, mut env) = make_env();
2414 let v = eval_src(r#"(try (/ 1 0) (catch Exception e "caught"))"#, &mut env).unwrap();
2415 assert_eq!(v, Value::string("caught".to_string()));
2416 }
2417
2418 #[test]
2419 fn test_ns_resolve() {
2420 let (_, mut env) = make_env();
2421 eval_src("(def somevar 99)", &mut env).unwrap();
2422 let v = eval_src("(ns-resolve *ns* 'somevar)", &mut env).unwrap();
2424 assert!(matches!(v, Value::Var(_)));
2425 let v2 = eval_src("(ns-resolve *ns* 'nonexistent)", &mut env).unwrap();
2427 assert_eq!(v2, Value::Nil);
2428 }
2429
2430 #[test]
2433 fn test_assoc_chain_virtualized() {
2434 let v = eval_str(
2436 "(let [m {}
2437 a (assoc m :x 1)
2438 b (assoc a :y 2)
2439 c (assoc b :z 3)]
2440 c)",
2441 )
2442 .unwrap();
2443 assert!(matches!(&v, Value::Map(_)));
2445 if let Value::Map(m) = &v {
2446 assert_eq!(m.count(), 3);
2447 assert_eq!(m.get(&Value::keyword(Keyword::simple("x"))), Some(long(1)));
2448 assert_eq!(m.get(&Value::keyword(Keyword::simple("y"))), Some(long(2)));
2449 assert_eq!(m.get(&Value::keyword(Keyword::simple("z"))), Some(long(3)));
2450 }
2451 }
2452
2453 #[test]
2454 fn test_conj_chain_virtualized() {
2455 let v = eval_str(
2457 "(let [v [1]
2458 a (conj v 2)
2459 b (conj a 3)
2460 c (conj b 4)]
2461 c)",
2462 )
2463 .unwrap();
2464 assert_eq!(v, eval_str("[1 2 3 4]").unwrap());
2465 }
2466
2467 #[test]
2468 fn test_assoc_chain_intermediate_used_no_virtualize() {
2469 let v = eval_str(
2472 "(let [a (assoc {} :x 1)
2473 b (assoc a :y 2)]
2474 (list (count a) (count b)))",
2475 )
2476 .unwrap();
2477 if let Value::List(l) = &v {
2479 let items: Vec<_> = l.get().iter().cloned().collect();
2480 assert_eq!(items, vec![long(1), long(2)]);
2481 } else {
2482 panic!("expected list, got {:?}", v);
2483 }
2484 }
2485
2486 #[test]
2487 fn test_assoc_chain_on_existing_map() {
2488 let v = eval_str(
2490 "(let [m {:a 1}
2491 a (assoc m :b 2)
2492 b (assoc a :c 3)]
2493 b)",
2494 )
2495 .unwrap();
2496 if let Value::Map(m) = &v {
2497 assert_eq!(m.count(), 3);
2498 } else {
2499 panic!("expected map");
2500 }
2501 }
2502
2503 #[test]
2506 fn test_post_condition_percent_bound() {
2507 let v = eval_str("(defn g [x] {:post [(pos? %)]} (inc x)) (g 5)").unwrap();
2509 assert_eq!(v, long(6));
2510 }
2511
2512 #[test]
2513 fn test_post_condition_violation_throws() {
2514 let r = eval_str("(defn g [x] {:post [(neg? %)]} (inc x)) (g 5)");
2516 assert!(r.is_err(), "expected error from failing :post condition");
2517 }
2518
2519 #[test]
2520 fn test_pre_condition_passes() {
2521 let v = eval_str("(defn g [x] {:pre [(pos? x)]} (inc x)) (g 5)").unwrap();
2522 assert_eq!(v, long(6));
2523 }
2524
2525 #[test]
2526 fn test_pre_condition_violation_throws() {
2527 let r = eval_str("(defn g [x] {:pre [(pos? x)]} (inc x)) (g -1)");
2528 assert!(r.is_err(), "expected error from failing :pre condition");
2529 }
2530
2531 #[test]
2532 fn test_pre_and_post_conditions() {
2533 let v = eval_str("(defn g [x] {:pre [(pos? x)] :post [(> % x)]} (inc x)) (g 3)").unwrap();
2534 assert_eq!(v, long(4));
2535 }
2536
2537 #[test]
2538 fn test_post_condition_no_pre() {
2539 let v = eval_str("(defn h [x] {:post [(number? %)]} (inc x)) (h 2)").unwrap();
2541 assert_eq!(v, long(3));
2542 }
2543
2544 #[test]
2545 fn test_pre_condition_no_post() {
2546 let v = eval_str("(defn h [x] {:pre [(number? x)]} x) (h 42)").unwrap();
2548 assert_eq!(v, long(42));
2549 }
2550}