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