use crate::*;
fn ev(src: &str) -> Value {
eval_expr(src).unwrap_or_else(|e| panic!("Eval failed for: {}\nError: {}", src, e))
}
fn run_main(src: &str) -> (Value, Vec<String>) {
eval_main(src).unwrap_or_else(|e| panic!("Run failed for:\n{}\nError: {}", src, e))
}
fn run_ok(src: &str) -> Env {
run(src).unwrap_or_else(|e| panic!("Run failed for:\n{}\nError: {}", src, e))
}
#[allow(dead_code)]
fn lookup(src: &str, name: &str) -> Value {
let env = run_ok(src);
env.lookup(name).cloned()
.unwrap_or_else(|| panic!("'{}' not found in env", name))
}
#[test]
fn lit_int() { assert_eq!(ev("42"), Value::Int(42)); }
#[test]
fn lit_float() { assert_eq!(ev("3.14"), Value::Float(3.14)); }
#[test]
fn lit_bool() {
assert_eq!(ev("true"), Value::Bool(true));
assert_eq!(ev("false"), Value::Bool(false));
}
#[test]
fn lit_string() { assert_eq!(ev("\"hello\""), Value::Str("hello".into())); }
#[test]
fn add() { assert_eq!(ev("2 + 3"), Value::Int(5)); }
#[test]
fn sub() { assert_eq!(ev("10 - 4"), Value::Int(6)); }
#[test]
fn mul() { assert_eq!(ev("6 * 7"), Value::Int(42)); }
#[test]
fn div() { assert_eq!(ev("15 / 3"), Value::Int(5)); }
#[test]
fn modulo() { assert_eq!(ev("17 % 5"), Value::Int(2)); }
#[test]
fn neg() { assert_eq!(ev("-42"), Value::Int(-42)); }
#[test]
fn precedence() { assert_eq!(ev("2 + 3 * 4"), Value::Int(14)); }
#[test]
fn parens() { assert_eq!(ev("(2 + 3) * 4"), Value::Int(20)); }
#[test]
fn eq() { assert_eq!(ev("1 == 1"), Value::Bool(true)); }
#[test]
fn neq() { assert_eq!(ev("1 != 2"), Value::Bool(true)); }
#[test]
fn lt() { assert_eq!(ev("1 < 2"), Value::Bool(true)); }
#[test]
fn gt() { assert_eq!(ev("2 > 1"), Value::Bool(true)); }
#[test]
fn lte() { assert_eq!(ev("1 <= 1"), Value::Bool(true)); }
#[test]
fn gte() { assert_eq!(ev("2 >= 3"), Value::Bool(false)); }
#[test]
fn and_true() { assert_eq!(ev("true && true"), Value::Bool(true)); }
#[test]
fn and_false() { assert_eq!(ev("true && false"), Value::Bool(false)); }
#[test]
fn or_true() { assert_eq!(ev("false || true"), Value::Bool(true)); }
#[test]
fn list_literal() {
assert_eq!(ev("[1 2 3]"), Value::List(vec![Value::Int(1), Value::Int(2), Value::Int(3)]));
}
#[test]
fn empty_list() {
assert_eq!(ev("[]"), Value::List(vec![]));
}
#[test]
fn list_concat() {
assert_eq!(
ev("[1 2] ++ [3 4]"),
Value::List(vec![Value::Int(1), Value::Int(2), Value::Int(3), Value::Int(4)])
);
}
#[test]
fn range() {
assert_eq!(
ev("[1..5]"),
Value::List(vec![Value::Int(1), Value::Int(2), Value::Int(3), Value::Int(4), Value::Int(5)])
);
}
#[test]
fn cond_true() { assert_eq!(ev("? true -> 1 : 2"), Value::Int(1)); }
#[test]
fn cond_false() { assert_eq!(ev("? false -> 1 : 2"), Value::Int(2)); }
#[test]
fn cond_comparison() { assert_eq!(ev("? 5 > 3 -> 10 : 20"), Value::Int(10)); }
#[test]
fn lambda_apply() {
assert_eq!(ev("(\\x -> x + 1) 5"), Value::Int(6));
}
#[test]
fn lambda_closure() {
let (_v, _) = run_main("a = 10\nf = \\x -> x + a\nresult = f 5");
let env = run_ok("a = 10\nf = \\x -> x + a\nresult = f 5");
match env.lookup("result") {
Some(Value::Func { equations, .. }) => {
let mut evaluator = Evaluator::new();
let v = evaluator.eval(&env, &equations[0].body).unwrap();
assert_eq!(v, Value::Int(15));
}
other => panic!("Expected result func, got: {:?}", other),
}
}
#[test]
fn pipe_simple() {
let env = run_ok("double x = x * 2\nresult = 5 |> double");
match env.lookup("result") {
Some(Value::Func { equations, .. }) => {
let mut evaluator = Evaluator::new();
let v = evaluator.eval(&env, &equations[0].body).unwrap();
assert_eq!(v, Value::Int(10));
}
other => panic!("Expected result, got: {:?}", other),
}
}
#[test]
fn pattern_match_literal() {
let env = run_ok("f 0 = 100\nf n = n");
let mut ev = Evaluator::new();
let f = env.lookup("f").unwrap().clone();
assert_eq!(ev.apply(f.clone(), Value::Int(0)).unwrap(), Value::Int(100));
assert_eq!(ev.apply(f, Value::Int(5)).unwrap(), Value::Int(5));
}
#[test]
fn pattern_match_list() {
let env = run_ok("len [] = 0\nlen (x:xs) = 1 + len xs");
let mut ev = Evaluator::new();
let f = env.lookup("len").unwrap().clone();
let list = Value::List(vec![Value::Int(1), Value::Int(2), Value::Int(3)]);
assert_eq!(ev.apply(f, list).unwrap(), Value::Int(3));
}
#[test]
fn full_factorial() {
let env = run_ok("fac 0 = 1\nfac n = n * fac (n - 1)");
let mut ev = Evaluator::new();
let f = env.lookup("fac").unwrap().clone();
assert_eq!(ev.apply(f.clone(), Value::Int(0)).unwrap(), Value::Int(1));
assert_eq!(ev.apply(f.clone(), Value::Int(1)).unwrap(), Value::Int(1));
assert_eq!(ev.apply(f.clone(), Value::Int(5)).unwrap(), Value::Int(120));
assert_eq!(ev.apply(f, Value::Int(10)).unwrap(), Value::Int(3628800));
}
#[test]
fn full_map() {
let env = run_ok("map f [] = []\nmap f (x:xs) = f x : map f xs\ndouble x = x * 2");
let mut ev = Evaluator::new();
let map_f = env.lookup("map").unwrap().clone();
let double = env.lookup("double").unwrap().clone();
let list = Value::List(vec![Value::Int(1), Value::Int(2), Value::Int(3)]);
let partial = ev.apply(map_f, double).unwrap();
let result = ev.apply(partial, list).unwrap();
assert_eq!(result, Value::List(vec![Value::Int(2), Value::Int(4), Value::Int(6)]));
}
#[test]
fn full_quicksort() {
let src = "\
qsort [] = []
qsort (p:xs) = qsort lo ++ [p] ++ qsort hi
lo = [x | x <- xs , x <= p]
hi = [x | x <- xs , x > p]";
let env = run_ok(src);
let mut ev = Evaluator::new();
let f = env.lookup("qsort").unwrap().clone();
let unsorted = Value::List(vec![
Value::Int(3), Value::Int(1), Value::Int(4), Value::Int(1), Value::Int(5), Value::Int(9),
]);
let sorted = ev.apply(f, unsorted).unwrap();
assert_eq!(sorted, Value::List(vec![
Value::Int(1), Value::Int(1), Value::Int(3), Value::Int(4), Value::Int(5), Value::Int(9),
]));
}
#[test]
fn full_fizzbuzz_single() {
let src = "\
fizzbuzz n =
? n % 15 == 0 -> \"FizzBuzz\"
: ? n % 3 == 0 -> \"Fizz\"
: ? n % 5 == 0 -> \"Buzz\"
: show n";
let env = run_ok(src);
let mut ev = Evaluator::new();
let f = env.lookup("fizzbuzz").unwrap().clone();
assert_eq!(ev.apply(f.clone(), Value::Int(15)).unwrap(), Value::Str("FizzBuzz".into()));
assert_eq!(ev.apply(f.clone(), Value::Int(3)).unwrap(), Value::Str("Fizz".into()));
assert_eq!(ev.apply(f.clone(), Value::Int(5)).unwrap(), Value::Str("Buzz".into()));
assert_eq!(ev.apply(f, Value::Int(7)).unwrap(), Value::Str("7".into()));
}
#[test]
fn list_comp_simple() {
let src = "squares = [x | x <- [1..5]]";
let env = run_ok(src);
match env.lookup("squares") {
Some(Value::Func { equations, .. }) => {
let mut ev = Evaluator::new();
let v = ev.eval(&env, &equations[0].body).unwrap();
assert_eq!(v, Value::List(vec![
Value::Int(1), Value::Int(2), Value::Int(3), Value::Int(4), Value::Int(5)
]));
}
_ => panic!("squares not found"),
}
}
#[test]
fn list_comp_with_guard() {
let src = "evens = [x | x <- [1..10] , even x]";
let env = run_ok(src);
match env.lookup("evens") {
Some(Value::Func { equations, .. }) => {
let mut ev = Evaluator::new();
let v = ev.eval(&env, &equations[0].body).unwrap();
assert_eq!(v, Value::List(vec![
Value::Int(2), Value::Int(4), Value::Int(6), Value::Int(8), Value::Int(10)
]));
}
_ => panic!("evens not found"),
}
}
#[test]
fn adt_constructors() {
let src = "Maybe a = Just a | None\nx = Just 42\ny = None";
let env = run_ok(src);
let mut ev = Evaluator::new();
match env.lookup("x") {
Some(Value::Func { equations, .. }) => {
let v = ev.eval(&env, &equations[0].body).unwrap();
assert_eq!(v, Value::Con("Just".into(), vec![Value::Int(42)]));
}
_ => panic!("x not found"),
}
}
#[test]
fn builtin_length() {
assert_eq!(ev("length [1 2 3]"), Value::Int(3));
}
#[test]
fn builtin_head_tail() {
assert_eq!(ev("head [1 2 3]"), Value::Int(1));
assert_eq!(
ev("tail [1 2 3]"),
Value::List(vec![Value::Int(2), Value::Int(3)])
);
}
#[test]
fn builtin_sum() {
assert_eq!(ev("sum [1 2 3 4 5]"), Value::Int(15));
}
#[test]
fn builtin_even_odd() {
assert_eq!(ev("even 4"), Value::Bool(true));
assert_eq!(ev("even 3"), Value::Bool(false));
assert_eq!(ev("odd 3"), Value::Bool(true));
}
#[test]
fn error_div_zero() {
assert!(eval_expr("1 / 0").is_err());
}
#[test]
fn error_head_empty() {
assert!(eval_expr("head []").is_err());
}
#[test]
fn record_literal() {
let val = ev("{x = 3, y = 4}");
assert_eq!(val, Value::Record(vec![
("x".into(), Value::Int(3)),
("y".into(), Value::Int(4)),
]));
}
#[test]
fn record_field_access() {
let env = run_ok("p = {x = 10, y = 20}\nmain = p.x");
match env.lookup("main") {
Some(Value::Func { equations, .. }) => {
let mut ev = Evaluator::new();
let v = ev.eval(&env, &equations[0].body).unwrap();
assert_eq!(v, Value::Int(10));
}
_ => panic!("main not found"),
}
}
#[test]
fn record_pattern_match() {
let env = run_ok("get_x {x = v, y = _} = v\nmain = get_x {x = 42, y = 0}");
let mut ev = Evaluator::new();
let f = env.lookup("get_x").unwrap().clone();
let rec = Value::Record(vec![
("x".into(), Value::Int(42)),
("y".into(), Value::Int(0)),
]);
assert_eq!(ev.apply(f, rec).unwrap(), Value::Int(42));
}
#[test]
fn record_field_in_function() {
let env = run_ok("dist_sq p = p.x * p.x + p.y * p.y");
let mut ev = Evaluator::new();
let f = env.lookup("dist_sq").unwrap().clone();
let p = Value::Record(vec![
("x".into(), Value::Int(3)),
("y".into(), Value::Int(4)),
]);
assert_eq!(ev.apply(f, p).unwrap(), Value::Int(25));
}
#[test]
fn module_simple() {
let src = "\
mod Math
square x = x * x
use Math (square)
main = square 7";
let (val, _) = run_main(src);
assert_eq!(val, Value::Int(49));
}
#[test]
fn module_constant() {
let src = "\
mod Consts
pi = 314
use Consts (pi)
main = pi";
let (val, _) = run_main(src);
assert_eq!(val, Value::Int(314));
}
#[test]
fn row_poly_field_access() {
let env = run_ok("get_x r = r.x");
let mut ev = Evaluator::new();
let f = env.lookup("get_x").unwrap().clone();
let r1 = Value::Record(vec![("x".into(), Value::Int(5)), ("y".into(), Value::Int(10))]);
let r2 = Value::Record(vec![("x".into(), Value::Int(99)), ("z".into(), Value::Bool(true))]);
assert_eq!(ev.apply(f.clone(), r1).unwrap(), Value::Int(5));
assert_eq!(ev.apply(f, r2).unwrap(), Value::Int(99));
}
#[test]
fn string_concat_op() {
assert_eq!(ev("\"hello\" ++ \" world\""), Value::Str("hello world".into()));
}
#[test]
fn string_eq() {
assert_eq!(ev("\"abc\" == \"abc\""), Value::Bool(true));
assert_eq!(ev("\"abc\" == \"def\""), Value::Bool(false));
}
#[test]
fn string_show_int() {
assert_eq!(ev("show 42"), Value::Str("42".into()));
}
#[test]
fn compose_op() {
let env = run_ok("double x = x * 2\nadd1 x = x + 1\nmain = (double >> add1) 5");
match env.lookup("main") {
Some(Value::Func { equations, .. }) => {
let mut ev = Evaluator::new();
let v = ev.eval(&env, &equations[0].body).unwrap();
assert_eq!(v, Value::Int(11));
}
_ => panic!("main not found"),
}
}
#[test]
fn where_bindings_basic() {
let src = "hyp a b = root\n sq_sum = a * a + b * b\n root = sq_sum";
let env = run_ok(src);
let mut ev = Evaluator::new();
let f = env.lookup("hyp").unwrap().clone();
let partial = ev.apply(f, Value::Int(3)).unwrap();
let result = ev.apply(partial, Value::Int(4)).unwrap();
assert_eq!(result, Value::Int(25));
}
#[test]
fn full_fibonacci() {
let src = "fib 0 = 0\nfib 1 = 1\nfib n = fib (n - 1) + fib (n - 2)";
let env = run_ok(src);
let mut ev = Evaluator::new();
let f = env.lookup("fib").unwrap().clone();
assert_eq!(ev.apply(f.clone(), Value::Int(0)).unwrap(), Value::Int(0));
assert_eq!(ev.apply(f.clone(), Value::Int(1)).unwrap(), Value::Int(1));
assert_eq!(ev.apply(f.clone(), Value::Int(7)).unwrap(), Value::Int(13));
assert_eq!(ev.apply(f, Value::Int(10)).unwrap(), Value::Int(55));
}
#[test]
fn full_adt_pattern_matching() {
let src = "\
Maybe a = Just a | None
fromJust (Just v) = v
fromJust None = 0";
let env = run_ok(src);
let mut ev = Evaluator::new();
let f = env.lookup("fromJust").unwrap().clone();
let just42 = Value::Con("Just".into(), vec![Value::Int(42)]);
let none = Value::Con("None".into(), vec![]);
assert_eq!(ev.apply(f.clone(), just42).unwrap(), Value::Int(42));
assert_eq!(ev.apply(f, none).unwrap(), Value::Int(0));
}
#[test]
fn full_higher_order_foldl() {
let src = "\
myfoldl f acc [] = acc
myfoldl f acc (x:xs) = myfoldl f (f acc x) xs
result = myfoldl (\\acc x -> acc + x) 0 [1 2 3 4 5]";
let env = run_ok(src);
match env.lookup("result") {
Some(Value::Func { equations, .. }) => {
let mut ev = Evaluator::new();
let v = ev.eval(&env, &equations[0].body).unwrap();
assert_eq!(v, Value::Int(15));
}
_ => panic!("result not found"),
}
}
#[test]
fn typeclass_show_adt() {
let src = "\
Color = Red | Green | Blue
trait Show a
show : a -> String
impl Show Color
show Red = \"Red\"
show Green = \"Green\"
show Blue = \"Blue\"
main = show Red";
let (val, _) = run_main(src);
assert_eq!(val, Value::Str("Red".into()));
}
#[test]
fn typeclass_impl_fallback_to_existing() {
let src = "\
Color = Red | Green
show_color Red = \"Red\"
show_color Green = \"Green\"
show_color _ = \"Other\"
impl ShowColor Color
show_color Red = \"Red\"
show_color Green = \"Green\"
main = show_color Green";
let (val, _) = run_main(src);
assert_eq!(val, Value::Str("Green".into()));
}
#[test]
fn float_eq_true() {
assert_eq!(ev("1.5 == 1.5"), Value::Bool(true));
}
#[test]
fn float_eq_false() {
assert_eq!(ev("1.5 == 2.0"), Value::Bool(false));
}
#[test]
fn float_neq() {
assert_eq!(ev("1.5 != 2.0"), Value::Bool(true));
}
#[test]
fn float_lt_true() {
assert_eq!(ev("1.5 < 2.0"), Value::Bool(true));
}
#[test]
fn float_lt_false() {
assert_eq!(ev("2.0 < 1.5"), Value::Bool(false));
}
#[test]
fn float_gt_true() {
assert_eq!(ev("3.0 > 1.5"), Value::Bool(true));
}
#[test]
fn float_gt_false() {
assert_eq!(ev("1.0 > 3.0"), Value::Bool(false));
}
#[test]
fn float_lte_equal() {
assert_eq!(ev("1.0 <= 1.0"), Value::Bool(true));
}
#[test]
fn float_lte_less() {
assert_eq!(ev("1.0 <= 2.0"), Value::Bool(true));
}
#[test]
fn float_gte_equal() {
assert_eq!(ev("2.5 >= 2.5"), Value::Bool(true));
}
#[test]
fn float_gte_greater() {
assert_eq!(ev("3.0 >= 1.5"), Value::Bool(true));
}
#[test]
fn record_eq_true() {
assert_eq!(ev("{x = 1} == {x = 1}"), Value::Bool(true));
}
#[test]
fn record_eq_false() {
assert_eq!(ev("{x = 1} == {x = 2}"), Value::Bool(false));
}
#[test]
fn list_eq_true() {
assert_eq!(ev("[1 2 3] == [1 2 3]"), Value::Bool(true));
}
#[test]
fn list_eq_false() {
assert_eq!(ev("[1 2] == [1 3]"), Value::Bool(false));
}
#[test]
fn show_float_fractional() {
let src = "main = show 3.14";
let (val, _) = run_main(src);
assert_eq!(val, Value::Str("3.14".into()));
}
#[test]
fn show_float_whole() {
let src = "main = show 3.0";
let (val, _) = run_main(src);
assert_eq!(val, Value::Str("3.0".into()));
}
#[test]
fn show_float_half() {
let src = "main = show 0.5";
let (val, _) = run_main(src);
assert_eq!(val, Value::Str("0.5".into()));
}
#[test]
fn pow_int() {
assert_eq!(ev("2 ** 10"), Value::Int(1024));
}
#[test]
fn pow_int_zero() {
assert_eq!(ev("5 ** 0"), Value::Int(1));
}
#[test]
fn pow_int_one() {
assert_eq!(ev("7 ** 1"), Value::Int(7));
}
#[test]
fn pow_float() {
assert_eq!(ev("2.0 ** 3.0"), Value::Float(8.0));
}
#[test]
fn pow_right_assoc() {
assert_eq!(ev("2 ** 3 ** 2"), Value::Int(512));
}
#[test]
fn sqrt_float() {
assert_eq!(ev("sqrt 4.0"), Value::Float(2.0));
}
#[test]
fn sqrt_nine() {
assert_eq!(ev("sqrt 9.0"), Value::Float(3.0));
}
#[test]
fn abs_neg_int() {
assert_eq!(ev("abs (0 - 5)"), Value::Int(5));
}
#[test]
fn abs_pos_int() {
assert_eq!(ev("abs 5"), Value::Int(5));
}
#[test]
fn abs_float() {
assert_eq!(ev("abs (0.0 - 3.5)"), Value::Float(3.5));
}
#[test]
fn floor_float() {
assert_eq!(ev("floor 3.7"), Value::Float(3.0));
}
#[test]
fn floor_neg_float() {
assert_eq!(ev("floor (0.0 - 3.2)"), Value::Float(-4.0));
}
#[test]
fn ceil_float() {
assert_eq!(ev("ceil 3.2"), Value::Float(4.0));
}
#[test]
fn round_float_up() {
assert_eq!(ev("round 3.6"), Value::Float(4.0));
}
#[test]
fn round_float_down() {
assert_eq!(ev("round 3.2"), Value::Float(3.0));
}
#[test]
fn typeclass_adt_float_eval() {
let src = "\
Expr = Num Float | Add Expr Expr
eval_expr (Num x) = x
eval_expr (Add a b) = eval_expr a + eval_expr b
main = eval_expr (Add (Num 1.5) (Num 2.5))";
let (val, _) = run_main(src);
assert_eq!(val, Value::Float(4.0));
}
#[test]
fn unit_literal() {
assert_eq!(ev("()"), Value::Unit);
}
#[test]
fn print_returns_unit() {
let (val, output) = run_main("main = print 42");
assert_eq!(val, Value::Unit);
assert_eq!(output, vec!["42"]);
}
#[test]
fn print_string() {
let (_, output) = run_main(r#"main = print "hello""#);
assert_eq!(output, vec!["hello"]);
}
#[test]
fn seq_two_prints() {
let (val, output) = run_main(r#"main = print "hello" ; print "world""#);
assert_eq!(val, Value::Unit);
assert_eq!(output, vec!["hello", "world"]);
}
#[test]
fn seq_returns_right() {
assert_eq!(ev("42 ; 99"), Value::Int(99));
}
#[test]
fn seq_discards_left() {
assert_eq!(ev("() ; true"), Value::Bool(true));
}
#[test]
fn seq_chain_three_prints() {
let (_, output) = run_main(
r#"main = print "a" ; print "b" ; print "c""#
);
assert_eq!(output, vec!["a", "b", "c"]);
}
#[test]
fn print_any_type() {
let (_, output) = run_main("main = print true");
assert_eq!(output, vec!["true"]);
}
#[test]
fn print_list() {
let (_, output) = run_main("main = print [1 2 3]");
assert_eq!(output, vec!["[1 2 3]"]);
}
#[test]
fn seq_with_pipe() {
let (_, output) = run_main(
r#"main = print "hello" ; [1 2 3] |> print"#
);
assert_eq!(output, vec!["hello", "[1 2 3]"]);
}
#[test]
fn unit_eq() {
assert_eq!(ev("() == ()"), Value::Bool(true));
}
#[test]
fn run_float_add() {
let (val, _) = run_main("main = 3.14 + 2.71");
assert_eq!(val, Value::Float(5.85));
}
#[test]
fn run_float_sub() {
let (val, _) = run_main("main = 5.0 - 1.5");
assert_eq!(val, Value::Float(3.5));
}
#[test]
fn run_float_mul() {
let (val, _) = run_main("main = 2.0 * 3.0");
assert_eq!(val, Value::Float(6.0));
}
#[test]
fn run_float_div() {
let (val, _) = run_main("main = 10.0 / 4.0");
assert_eq!(val, Value::Float(2.5));
}
#[test]
fn run_float_pow() {
let (val, _) = run_main("main = 2.0 ** 3.0");
assert_eq!(val, Value::Float(8.0));
}
#[test]
fn run_float_with_sqrt() {
let (val, _) = run_main("main = sqrt 4.0 + 1.0");
assert_eq!(val, Value::Float(3.0));
}
#[test]
fn run_string_concat() {
let (val, _) = run_main("main = \"hello\" ++ \" world\"");
assert_eq!(val, Value::Str("hello world".into()));
}
#[test]
fn div_int_by_float_zero() {
let result = eval_expr("5 / 0.0");
assert!(result.is_err());
assert!(result.unwrap_err().message.contains("Division by zero"));
}
#[test]
fn div_float_by_int_zero() {
let result = eval_expr("5.0 / 0");
assert!(result.is_err());
assert!(result.unwrap_err().message.contains("Division by zero"));
}
#[test]
fn pow_int_overflow() {
let result = eval_expr("10 ** 20");
assert!(result.is_err());
assert!(result.unwrap_err().message.contains("overflow"));
}
#[test]
fn pow_int_normal() {
assert_eq!(ev("2 ** 10"), Value::Int(1024));
}
#[test]
fn or_false_false() { assert_eq!(ev("false || false"), Value::Bool(false)); }
#[test]
fn not_builtin() {
assert_eq!(ev("not true"), Value::Bool(false));
assert_eq!(ev("not false"), Value::Bool(true));
}
#[test]
fn string_neq() {
assert_eq!(ev("\"abc\" != \"def\""), Value::Bool(true));
assert_eq!(ev("\"abc\" != \"abc\""), Value::Bool(false));
}
#[test]
fn string_length_builtin() {
assert_eq!(ev("length \"hello\""), Value::Int(5));
assert_eq!(ev("length \"\""), Value::Int(0));
}
#[test]
fn show_bool() {
let (val, _) = run_main("main = show true");
assert_eq!(val, Value::Str("true".into()));
let (val2, _) = run_main("main = show false");
assert_eq!(val2, Value::Str("false".into()));
}
#[test]
fn show_list_int() {
let (val, _) = run_main("main = show [1 2 3]");
assert_eq!(val, Value::Str("[1 2 3]".into()));
}
#[test]
fn show_in_string_concat() {
let (val, _) = run_main(r#"main = "n=" ++ show 42"#);
assert_eq!(val, Value::Str("n=42".into()));
}
#[test]
fn list_comp_double_transform() {
let (val, _) = run_main("main = [x * 2 | x <- [1..5]]");
assert_eq!(val, Value::List(vec![
Value::Int(2), Value::Int(4), Value::Int(6), Value::Int(8), Value::Int(10)
]));
}
#[test]
fn cons_builds_list() {
let (val, _) = run_main("main = 1 : 2 : 3 : []");
assert_eq!(val, Value::List(vec![
Value::Int(1), Value::Int(2), Value::Int(3)
]));
}
#[test]
fn full_gcd() {
let src = "gcd a 0 = a\ngcd a b = gcd b (a % b)";
let env = run_ok(src);
let mut ev = Evaluator::new();
let f = env.lookup("gcd").unwrap().clone();
let partial = ev.apply(f, Value::Int(48)).unwrap();
assert_eq!(ev.apply(partial, Value::Int(18)).unwrap(), Value::Int(6));
}
#[test]
fn record_three_fields() {
let val = ev("{a = 10, b = 20, c = 30}");
assert_eq!(val, Value::Record(vec![
("a".into(), Value::Int(10)),
("b".into(), Value::Int(20)),
("c".into(), Value::Int(30)),
]));
}
#[test]
fn record_float_field() {
let env = run_ok("p = {pi = 3.14, r = 2.0}\nmain = p.pi");
match env.lookup("main") {
Some(Value::Func { equations, .. }) => {
let mut ev = Evaluator::new();
let v = ev.eval(&env, &equations[0].body).unwrap();
assert_eq!(v, Value::Float(3.14));
}
_ => panic!("main not found"),
}
}
#[test]
fn pipe_chain_two_steps() {
let env = run_ok("double x = x * 2\nresult = 5 |> double |> double");
match env.lookup("result") {
Some(Value::Func { equations, .. }) => {
let mut ev = Evaluator::new();
let v = ev.eval(&env, &equations[0].body).unwrap();
assert_eq!(v, Value::Int(20));
}
_ => panic!("result not found"),
}
}
#[test]
fn module_two_imports() {
let src = "\
mod Math
double x = x * 2
square x = x * x
use Math (double square)
main = double (square 3)";
let (val, _) = run_main(src);
assert_eq!(val, Value::Int(18));
}
#[test]
fn print_float_output() {
let (_, output) = run_main("main = print 3.14");
assert_eq!(output, vec!["3.14"]);
}
#[test]
fn filter_builtin() {
assert_eq!(
ev("filter even [1 2 3 4 5 6]"),
Value::List(vec![Value::Int(2), Value::Int(4), Value::Int(6)])
);
}
#[test]
fn map_builtin() {
assert_eq!(
ev("map (\\x -> x * 2) [1 2 3]"),
Value::List(vec![Value::Int(2), Value::Int(4), Value::Int(6)])
);
}
#[test]
fn foldl_builtin() {
assert_eq!(
ev("foldl (\\acc x -> acc + x) 0 [1 2 3 4 5]"),
Value::Int(15)
);
}