#![cfg(test)] use test_case::test_case; use crate::{symbol_table::SymbolTable, tree_walk_eval::State}; fn evaluate_input(input: &str) -> Result { let ast = crate::util::quick_ast(input); let mut symbol_table = SymbolTable::new(); symbol_table.process_ast(&ast).unwrap(); let reduced_ir = crate::reduced_ir::reduce(&ast, &symbol_table); reduced_ir.debug(&symbol_table); println!("========"); symbol_table.debug(); let mut state = State::new(); let mut outputs = state.evaluate(reduced_ir, true); outputs.pop().unwrap() } fn eval_assert(input: &str, expected: &str) { assert_eq!(evaluate_input(input), Ok(expected.to_string())); } #[test] fn test_basic_eval() { eval_assert("1 + 2", "3"); eval_assert("let mut a = 1; a = 2", "()"); eval_assert("let mut a = 1; a = a + 2; a", "3"); } #[test] fn op_eval() { eval_assert("- 13", "-13"); eval_assert("10 - 2", "8"); } #[test] fn function_eval() { eval_assert("fn oi(x) { x + 1 }; oi(4)", "5"); eval_assert("fn oi(x) { x + 1 }; oi(1+2)", "4"); } #[test] fn scopes() { let scope_ok = r#" let a = 20 fn haha() { let something = 38 let a = 10 a } haha() "#; eval_assert(scope_ok, "10"); let scope_ok = r#" let a = 20 fn queque() { let a = 10 a } a "#; eval_assert(scope_ok, "20"); } #[test] fn adt_output_1() { let source = r#" type Option = Some(T) | None let a = Option::None let b = Option::Some(10) (b, a) "#; eval_assert(source, "(Some(10), None)"); } #[test] fn basic_if_statement() { let source = r#" let a = 10 let b = 10 if a == b then { 69 } else { 420 } "#; eval_assert(source, "69"); } #[test] fn basic_patterns_1() { let source = r#" let x = 10 let a = if x is 10 then { 255 } else { 256 } let b = if 23 is 99 then { 255 } else { 256 } let c = if true is false then { 9 } else { 10 } let d = if "xxx" is "yyy" then { 20 } else { 30 } (a, b, c, d) "#; eval_assert(source, "(255, 256, 10, 30)"); } #[test_case("sanchez", "1")] #[test_case("mouri", "2")] #[test_case("hella", "3")] #[test_case("cyrus", "4")] fn basic_patterns_2(input: &str, expected: &str) { let mut source = format!(r#"let x = "{}""#, input); source.push_str( r#" if x { is "sanchez" then 1 is "mouri" then 2 is "hella" then 3 is _ then 4 } "#, ); eval_assert(&source, expected); } #[test_case(r#"(45, "panda", false, 2.2)"#, r#""yes""#)] #[test_case(r#"(99, "panda", false, -2.45)"#, r#""maybe""#)] fn tuple_patterns(input: &str, expected: &str) { let mut source = format!("let x = {}", input); source.push_str( r#" if x { is (45, "pablo", _, 28.4) then "no" is (_, "panda", _, 2.2) then "yes" is _ then "maybe" }"#, ); eval_assert(&source, expected); } #[test] fn if_is_patterns() { let source = r#" type Option = Some(T) | None let q = "a string" let x = Option::Some(9); if x is Option::Some(q) then { q } else { 0 }"#; eval_assert(source, "9"); let source = r#" type Option = Some(T) | None let q = "a string" let outer = 2 let x = Option::None; if x is Option::Some(q) then { q } else { -2 + outer }"#; eval_assert(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 } "#; eval_assert(source, "4"); let source = r#" type Option = Some(T) | None let sara = Option::Some(99) if sara { is Option::None then 1 + 3, is Option::Some(x) then x } "#; eval_assert(source, "99"); let source = r#" let a = 10 if a { is 10 then "x", is 4 then "y" } "#; eval_assert(source, "\"x\""); let source = r#" let a = 10 if a { is 15 then "x", is 10 then "y" } "#; eval_assert(source, "\"y\""); } //TODO - I can probably cut down some of these #[test] fn string_pattern() { let source = r#" let a = "foo" if a { is "foo" then "x", is _ then "y" } "#; eval_assert(source, "\"x\""); } #[test] fn boolean_pattern() { let source = r#" let a = true if a { is true then "x", is false then "y" } "#; eval_assert(source, "\"x\""); } #[test] fn boolean_pattern_2() { let source = r#" let a = false if a { is true then "x", is false then "y" } "#; eval_assert(source, "\"y\""); } #[test] fn ignore_pattern() { let source = r#" type Option = Some(T) | None if Option::Some(10) { is _ then "hella" } "#; eval_assert(source, "\"hella\""); } #[test] fn tuple_pattern() { let source = r#" if (1, 2) { is (1, x) then x, is _ then 99 } "#; eval_assert(source, "2"); } #[test] fn tuple_pattern_2() { let source = r#" if (1, 2) { is (10, x) then x, is (y, x) then x + y } "#; eval_assert(source, "3"); } #[test] fn tuple_pattern_3() { let source = r#" if (1, 5) { is (10, x) then x, is (1, x) then x } "#; eval_assert(source, "5"); } #[test] fn tuple_pattern_4() { let source = r#" if (1, 5) { is (10, x) then x, is (1, x) then x, } "#; eval_assert(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) "#; eval_assert(source, r#"("x", "haha", "NIGH")"#); } #[test] fn basic_lambda_evaluation_1() { 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) "#; eval_assert(source, r"(10, 6)"); } #[test] fn basic_lambda_evaluation_2() { let source = r#" fn milta() { \(x) { x + 33 } } milta()(10) "#; eval_assert(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 }"#; eval_assert(source, "9"); }