diff --git a/schala-lang/language/src/eval.rs b/schala-lang/language/src/eval.rs index 17cc1aa..cb5fcca 100644 --- a/schala-lang/language/src/eval.rs +++ b/schala-lang/language/src/eval.rs @@ -11,6 +11,8 @@ use crate::reduced_ast::{BoundVars, ReducedAST, Stmt, Expr, Lit, Func, Alternati use crate::symbol_table::{SymbolSpec, Symbol, SymbolTable}; use crate::builtin::Builtin; +mod test; + pub struct State<'a> { values: ScopeStack<'a, Rc, ValueEntry>, symbol_table_handle: Rc>, @@ -491,257 +493,3 @@ impl<'a> State<'a> { }) } } - -#[cfg(test)] -mod eval_tests { - use std::cell::RefCell; - use std::rc::Rc; - - use crate::symbol_table::SymbolTable; - use crate::eval::State; - - fn evaluate_all_outputs(input: &str) -> Vec> { - let symbol_table = Rc::new(RefCell::new(SymbolTable::new())); - let mut state = State::new(symbol_table); - let ast = crate::util::quick_ast(input); - state.symbol_table_handle.borrow_mut().add_top_level_symbols(&ast).unwrap(); - let reduced = ast.reduce(&state.symbol_table_handle.borrow()); - 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 haha() { - 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 = Some(9); if x is Some(q) then { q } else { 0 }"#; - test_in_fresh_env!(source, "9"); - - let source = r#" -type Option = Some(T) | None -let x = None; if x is 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 = None -if a { is None -> 4, is Some(x) -> x } -"#; - test_in_fresh_env!(source, "4"); - - let source = r#" -type Option = Some(T) | None -let a = Some(99) -if a { is None -> 4, is Some(x) -> x } -"#; - test_in_fresh_env!(source, "99"); - - let source = r#" -let a = 10 -if a { is 10 -> "x", is 4 -> "y" } -"#; - test_in_fresh_env!(source, "\"x\""); - - let source = r#" -let a = 10 -if a { is 15 -> "x", is 10 -> "y" } -"#; - test_in_fresh_env!(source, "\"y\""); - } - - #[test] - fn string_pattern() { - let source = r#" -let a = "foo" -if a { is "foo" -> "x", is _ -> "y" } -"#; - test_in_fresh_env!(source, "\"x\""); - } - - #[test] - fn boolean_pattern() { - let source = r#" -let a = true -if a { - is true -> "x", - is false -> "y" -} -"#; - test_in_fresh_env!(source, "\"x\""); - } - - #[test] - fn boolean_pattern_2() { - let source = r#" -let a = false -if a { is true -> "x", is false -> "y" } -"#; - test_in_fresh_env!(source, "\"y\""); - } - - #[test] - fn ignore_pattern() { - let source = r#" -type Option = Some(T) | None -if Some(10) { - is _ -> "hella" -} -"#; - test_in_fresh_env!(source, "\"hella\""); - } - - #[test] - fn tuple_pattern() { - let source = r#" -if (1, 2) { - is (1, x) -> x, - is _ -> 99 -} -"#; - test_in_fresh_env!(source, 2); - } - - - #[test] - fn tuple_pattern_2() { - let source = r#" -if (1, 2) { - is (10, x) -> x, - is (y, x) -> x + y -} -"#; - test_in_fresh_env!(source, 3); - } - - #[test] - fn tuple_pattern_3() { - let source = r#" -if (1, 5) { - is (10, x) -> x, - is (1, x) -> x -} -"#; - test_in_fresh_env!(source, 5); - } - - #[test] - fn tuple_pattern_4() { - let source = r#" -if (1, 5) { - is (10, x) -> x, - is (1, x) -> 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 = Mulch(20) -let b = Jugs(1, "haha") -let c = Mardok - -let x = if a { - is Mulch(20) -> "x", - is _ -> "ERR" -} - -let y = if b { - is Mulch(n) -> "ERR", - is Jugs(2, _) -> "ERR", - is Jugs(1, s) -> s, - is _ -> "ERR", -} - -let z = if c { - is Jugs(_, _) -> "ERR", - is Mardok -> "NIGH", - is _ -> "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"); - } -} diff --git a/schala-lang/language/src/eval/test.rs b/schala-lang/language/src/eval/test.rs new file mode 100644 index 0000000..76da84a --- /dev/null +++ b/schala-lang/language/src/eval/test.rs @@ -0,0 +1,252 @@ +#![cfg(test)] + +use std::cell::RefCell; +use std::rc::Rc; + +use crate::symbol_table::SymbolTable; +use crate::eval::State; + +fn evaluate_all_outputs(input: &str) -> Vec> { + let symbol_table = Rc::new(RefCell::new(SymbolTable::new())); + let mut state = State::new(symbol_table); + let ast = crate::util::quick_ast(input); + state.symbol_table_handle.borrow_mut().add_top_level_symbols(&ast).unwrap(); + let reduced = ast.reduce(&state.symbol_table_handle.borrow()); + 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 haha() { + 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 = Some(9); if x is Some(q) then { q } else { 0 }"#; + test_in_fresh_env!(source, "9"); + + let source = r#" +type Option = Some(T) | None +let x = None; if x is 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 = None +if a { is None -> 4, is Some(x) -> x } +"#; +test_in_fresh_env!(source, "4"); + +let source = r#" +type Option = Some(T) | None +let a = Some(99) +if a { is None -> 4, is Some(x) -> x } +"#; +test_in_fresh_env!(source, "99"); + +let source = r#" +let a = 10 +if a { is 10 -> "x", is 4 -> "y" } +"#; +test_in_fresh_env!(source, "\"x\""); + +let source = r#" +let a = 10 +if a { is 15 -> "x", is 10 -> "y" } +"#; +test_in_fresh_env!(source, "\"y\""); +} + +#[test] +fn string_pattern() { + let source = r#" +let a = "foo" +if a { is "foo" -> "x", is _ -> "y" } +"#; +test_in_fresh_env!(source, "\"x\""); +} + +#[test] +fn boolean_pattern() { + let source = r#" +let a = true +if a { + is true -> "x", + is false -> "y" +} +"#; +test_in_fresh_env!(source, "\"x\""); +} + +#[test] +fn boolean_pattern_2() { + let source = r#" +let a = false +if a { is true -> "x", is false -> "y" } +"#; +test_in_fresh_env!(source, "\"y\""); +} + +#[test] +fn ignore_pattern() { + let source = r#" +type Option = Some(T) | None +if Some(10) { + is _ -> "hella" +} +"#; +test_in_fresh_env!(source, "\"hella\""); +} + +#[test] +fn tuple_pattern() { + let source = r#" +if (1, 2) { + is (1, x) -> x, + is _ -> 99 +} +"#; +test_in_fresh_env!(source, 2); +} + + +#[test] +fn tuple_pattern_2() { + let source = r#" +if (1, 2) { + is (10, x) -> x, + is (y, x) -> x + y +} +"#; +test_in_fresh_env!(source, 3); +} + +#[test] +fn tuple_pattern_3() { + let source = r#" +if (1, 5) { + is (10, x) -> x, + is (1, x) -> x +} +"#; +test_in_fresh_env!(source, 5); +} + +#[test] +fn tuple_pattern_4() { + let source = r#" +if (1, 5) { + is (10, x) -> x, + is (1, x) -> 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 = Mulch(20) +let b = Jugs(1, "haha") +let c = Mardok + +let x = if a { + is Mulch(20) -> "x", + is _ -> "ERR" +} + +let y = if b { + is Mulch(n) -> "ERR", + is Jugs(2, _) -> "ERR", + is Jugs(1, s) -> s, + is _ -> "ERR", +} + +let z = if c { + is Jugs(_, _) -> "ERR", + is Mardok -> "NIGH", + is _ -> "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"); + }