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