#![cfg(test)] use std::cell::RefCell; use std::rc::Rc; use crate::symbol_table::SymbolTable; use crate::scope_resolution::ScopeResolver; use crate::reduced_ast::reduce; use crate::eval::State; fn evaluate_all_outputs(input: &str) -> Vec> { let (mut ast, source_map) = crate::util::quick_ast(input); let source_map = Rc::new(RefCell::new(source_map)); let symbol_table = Rc::new(RefCell::new(SymbolTable::new(source_map))); symbol_table.borrow_mut().add_top_level_symbols(&ast).unwrap(); { let mut scope_resolver = ScopeResolver::new(symbol_table.clone()); let _ = scope_resolver.resolve(&mut ast); } let reduced = reduce(&ast, &symbol_table.borrow()); let mut state = State::new(); let all_output = state.evaluate(reduced, true); all_output } macro_rules! test_in_fresh_env { ($string:expr, $correct:expr) => { { let all_output = evaluate_all_outputs($string); let ref output = all_output.last().unwrap(); assert_eq!(**output, Ok($correct.to_string())); } } } #[test] fn test_basic_eval() { test_in_fresh_env!("1 + 2", "3"); test_in_fresh_env!("let mut a = 1; a = 2", "Unit"); /* test_in_fresh_env!("let mut a = 1; a = 2; a", "2"); test_in_fresh_env!(r#"("a", 1 + 2)"#, r#"("a", 3)"#); */ } #[test] fn op_eval() { test_in_fresh_env!("- 13", "-13"); test_in_fresh_env!("10 - 2", "8"); } #[test] fn function_eval() { test_in_fresh_env!("fn oi(x) { x + 1 }; oi(4)", "5"); test_in_fresh_env!("fn oi(x) { x + 1 }; oi(1+2)", "4"); } #[test] fn scopes() { let scope_ok = r#" let a = 20 fn haha() { let a = 10 a } haha() "#; test_in_fresh_env!(scope_ok, "10"); let scope_ok = r#" let a = 20 fn queque() { let a = 10 a } a "#; test_in_fresh_env!(scope_ok, "20"); } #[test] fn if_is_patterns() { let source = r#" type Option = Some(T) | None let x = Option::Some(9); if x is Option::Some(q) then { q } else { 0 }"#; test_in_fresh_env!(source, "9"); let source = r#" type Option = Some(T) | None let x = Option::None; if x is Option::Some(q) then { q } else { 0 }"#; test_in_fresh_env!(source, "0"); } #[test] fn full_if_matching() { let source = r#" type Option = Some(T) | None let a = Option::None if a { is Option::None then 4, is Option::Some(x) then x } "#; test_in_fresh_env!(source, "4"); let source = r#" type Option = Some(T) | None let a = Option::Some(99) if a { is Option::None then 4, is Option::Some(x) then x } "#; test_in_fresh_env!(source, "99"); let source = r#" let a = 10 if a { is 10 then "x", is 4 then "y" } "#; test_in_fresh_env!(source, "\"x\""); let source = r#" let a = 10 if a { is 15 then "x", is 10 then "y" } "#; test_in_fresh_env!(source, "\"y\""); } #[test] fn string_pattern() { let source = r#" let a = "foo" if a { is "foo" then "x", is _ then "y" } "#; test_in_fresh_env!(source, "\"x\""); } #[test] fn boolean_pattern() { let source = r#" let a = true if a { is true then "x", is false then "y" } "#; test_in_fresh_env!(source, "\"x\""); } #[test] fn boolean_pattern_2() { let source = r#" let a = false if a { is true then "x", is false then "y" } "#; test_in_fresh_env!(source, "\"y\""); } #[test] fn ignore_pattern() { let source = r#" type Option = Some(T) | None if Option::Some(10) { is _ then "hella" } "#; test_in_fresh_env!(source, "\"hella\""); } #[test] fn tuple_pattern() { let source = r#" if (1, 2) { is (1, x) then x, is _ then 99 } "#; test_in_fresh_env!(source, 2); } #[test] fn tuple_pattern_2() { let source = r#" if (1, 2) { is (10, x) then x, is (y, x) then x + y } "#; test_in_fresh_env!(source, 3); } #[test] fn tuple_pattern_3() { let source = r#" if (1, 5) { is (10, x) then x, is (1, x) then x } "#; test_in_fresh_env!(source, 5); } #[test] fn tuple_pattern_4() { let source = r#" if (1, 5) { is (10, x) then x, is (1, x) then x, } "#; test_in_fresh_env!(source, 5); } #[test] fn prim_obj_pattern() { let source = r#" type Stuff = Mulch(Nat) | Jugs(Nat, String) | Mardok let a = Stuff::Mulch(20) let b = Stuff::Jugs(1, "haha") let c = Stuff::Mardok let x = if a { is Stuff::Mulch(20) then "x", is _ then "ERR" } let y = if b { is Stuff::Mulch(n) then "ERR", is Stuff::Jugs(2, _) then "ERR", is Stuff::Jugs(1, s) then s, is _ then "ERR", } let z = if c { is Stuff::Jugs(_, _) then "ERR", is Stuff::Mardok then "NIGH", is _ then "ERR", } (x, y, z) "#; test_in_fresh_env!(source, r#"("x", "haha", "NIGH")"#); } #[test] fn basic_lambda_syntax() { let source = r#" let q = \(x, y) { x * y } let x = q(5,2) let y = \(m, n, o) { m + n + o }(1,2,3) (x, y) "#; test_in_fresh_env!(source, r"(10, 6)"); } #[test] fn lambda_syntax_2() { let source = r#" fn milta() { \(x) { x + 33 } } milta()(10) "#; test_in_fresh_env!(source, "43"); } #[test] fn import_all() { let source = r#" type Option = Some(T) | None import Option::* let x = Some(9); if x is Some(q) then { q } else { 0 }"#; test_in_fresh_env!(source, "9"); }