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