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