use std::sync::Arc;
use cljrs_reader::Parser;
use cljrs_runtime::env::env::{Env, GlobalEnv};
use cljrs_value::Value;
fn make_env() -> (Arc<GlobalEnv>, Env) {
let globals = cljrs_runtime::Runtime::builder()
.execution_mode(cljrs_runtime::ExecutionMode::TreeWalk)
.build()
.expect("runtime")
.into_globals();
let env = Env::new(globals.clone(), "user");
(globals, env)
}
fn eval_fresh(src: &str) -> Result<Value, String> {
let (_, mut env) = make_env();
let mut parser = Parser::new(src.to_string(), "<test>".to_string());
let forms = parser.parse_all().map_err(|e| format!("parse: {e:?}"))?;
let mut result = Value::Nil;
for form in forms {
result = cljrs_runtime::interp::eval::eval(&form, &mut env)
.map_err(|e| format!("eval: {e:?}"))?;
}
Ok(result)
}
fn shows(src: &str) -> String {
let wrapped = format!("(pr-str {src})");
match eval_fresh(&wrapped).unwrap_or_else(|e| panic!("{src}\n{e}")) {
Value::Str(s) => s.get().clone(),
other => panic!("{src}: expected a string, got {other:?}"),
}
}
#[test]
fn one_binding_still_works() {
assert_eq!(shows("(vec (for [x (range 3)] x))"), "[0 1 2]");
assert_eq!(shows("(vec (for [x [1 2 3]] (* x x)))"), "[1 4 9]");
}
#[test]
fn two_bindings_nest() {
assert_eq!(
shows("(vec (for [x (range 2) y (range 2)] [x y]))"),
"[[0 0] [0 1] [1 0] [1 1]]"
);
}
#[test]
fn the_rightmost_binding_varies_fastest() {
assert_eq!(
shows("(vec (for [c \"ab\" n (range 2)] (str c n)))"),
"[\"a0\" \"a1\" \"b0\" \"b1\"]"
);
}
#[test]
fn three_bindings_take_the_product() {
assert_eq!(
shows("(count (for [a (range 2) b (range 3) c (range 4)] [a b c]))"),
"24"
);
}
#[test]
fn an_empty_collection_yields_nothing() {
assert_eq!(shows("(vec (for [x [] y (range 3)] x))"), "[]");
assert_eq!(shows("(vec (for [x (range 3) y []] x))"), "[]");
}
#[test]
fn binding_forms_destructure() {
assert_eq!(shows("(vec (for [[a b] [[1 2] [3 4]]] (+ a b)))"), "[3 7]");
assert_eq!(
shows("(vec (for [{:keys [n]} [{:n 1} {:n 2}]] n))"),
"[1 2]"
);
}
#[test]
fn an_inner_binding_can_use_an_outer_one() {
assert_eq!(
shows("(vec (for [x (range 4) y (range x)] [x y]))"),
"[[1 0] [2 0] [2 1] [3 0] [3 1] [3 2]]"
);
}
#[test]
fn when_skips_and_keeps_going() {
assert_eq!(
shows("(vec (for [x (range 10) :when (even? x)] x))"),
"[0 2 4 6 8]"
);
assert_eq!(shows("(vec (for [x (range 5) :when false] x))"), "[]");
}
#[test]
fn let_binds_inside_the_loop() {
assert_eq!(
shows("(vec (for [x (range 3) :let [y (* 2 x)]] y))"),
"[0 2 4]"
);
}
#[test]
fn when_and_let_compose() {
assert_eq!(
shows("(vec (for [x (range 10) :when (even? x) :let [y (* x x)]] y))"),
"[0 4 16 36 64]"
);
}
#[test]
fn while_stops_rather_than_skipping() {
assert_eq!(
shows("(vec (for [x (range 10) :while (< x 5)] x))"),
"[0 1 2 3 4]"
);
assert_eq!(
shows("(vec (for [x [2 4 5 6 8] :while (even? x)] x))"),
"[2 4]"
);
}
#[test]
fn while_stops_only_its_own_loop() {
assert_eq!(
shows("(vec (for [x (range 2) y (range 5) :while (< y 2)] [x y]))"),
"[[0 0] [0 1] [1 0] [1 1]]"
);
}
#[test]
fn while_sees_the_lets_before_it() {
assert_eq!(
shows("(vec (for [x (range 10) :let [y (* x x)] :while (< y 9)] x))"),
"[0 1 2]"
);
}
#[test]
fn a_while_that_cannot_be_compiled_is_rejected_loudly() {
let err = eval_fresh("(for [x (range 3) :when (even? x) :while (< x 2)] x)")
.expect_err("a :while after a :when should not expand");
assert!(
err.contains(":while must follow its binding"),
"unhelpful error: {err}"
);
}
#[test]
fn the_result_is_lazy() {
assert_eq!(
shows("(vec (take 3 (for [x (range 1000000)] x)))"),
"[0 1 2]"
);
}
#[test]
fn laziness_survives_nesting() {
assert_eq!(
shows("(vec (take 3 (for [x (range 1000000) y (range 2)] [x y])))"),
"[[0 0] [0 1] [1 0]]"
);
}
#[test]
fn a_nil_body_yields_nils_rather_than_vanishing() {
assert_eq!(shows("(vec (for [x (range 2)] nil))"), "[nil nil]");
assert_eq!(
shows("(vec (for [x (range 3)] (when (even? x) x)))"),
"[0 nil 2]"
);
}
#[test]
fn a_multi_form_body_runs_in_order_and_yields_the_last() {
assert_eq!(
shows("(vec (for [x (range 2)] (inc x) (* 10 x)))"),
"[0 10]"
);
}
#[test]
fn a_binding_may_be_named_after_a_core_function() {
assert_eq!(shows("(for [list [1 2]] list)"), "(1 2)");
assert_eq!(shows("(for [x [1 2] :let [list x]] list)"), "(1 2)");
assert_eq!(shows("(for [mapcat [[1] [2]] x mapcat] x)"), "(1 2)");
assert_eq!(shows("(for [map [[1] [2]] x map] x)"), "(1 2)");
assert_eq!(
shows("(for [take-while [[1] [2]] x take-while] x)"),
"(1 2)"
);
assert_eq!(shows("(for [some? [1 2] :while (< some? 2)] some?)"), "(1)");
}
#[test]
fn a_let_init_runs_once_per_element_under_while() {
let src = r#"(let [n (atom 0)]
[(vec (for [x (range 4)
:let [y (do (swap! n inc) x)]
:while (< y 3)]
y))
@n])"#;
assert_eq!(shows(src), "[[0 1 2] 4]");
}
#[test]
fn a_while_still_stops_while_an_inner_when_only_skips() {
assert_eq!(
shows("(for [x (range 6) :while (< x 4) y [x] :when (even? y)] y)"),
"(0 2)"
);
assert_eq!(
shows("(for [x (range 6) :while (< x 4) :let [y (* x 10)]] y)"),
"(0 10 20 30)"
);
}
#[test]
fn a_malformed_binding_vector_is_rejected() {
for src in [
"(for [x [1 2] y] x)",
"(for [x [1 2] :when] x)",
"(for [x] x)",
] {
let err = eval_fresh(src).expect_err("should reject a malformed binding vector");
assert!(
err.contains("even number of forms"),
"unhelpful error for {src}: {err}"
);
}
}
#[test]
fn the_innermost_simple_binding_is_one_to_one_and_lazy() {
assert_eq!(shows("(count (for [x (range 5)] x))"), "5");
assert_eq!(shows("(for [x (range 3)] (inc x))"), "(1 2 3)");
let src = r#"(let [n (atom 0)]
[(vec (take 2 (for [x (range 100)] (do (swap! n inc) x))))
@n])"#;
assert_eq!(shows(src), "[[0 1] 2]", "the body must stay lazy");
}