diff --git a/tests/assignment.rs b/tests/assignment.rs new file mode 100644 index 0000000..1ff91a0 --- /dev/null +++ b/tests/assignment.rs @@ -0,0 +1,46 @@ +use dust_lang::*; + +#[test] +fn simple_assignment() { + let test = interpret("x = 1 x"); + + assert_eq!(Ok(Value::Integer(1)), test); +} + +#[test] +fn simple_assignment_with_type() { + let test = interpret("x = 1 x"); + + assert_eq!(Ok(Value::Integer(1)), test); +} + +#[test] +fn list_add_assign() { + let test = interpret( + " + x <[int]> = [] + x += 1 + x + ", + ); + + assert_eq!( + Ok(Value::List(List::with_items(vec![Value::Integer(1)]))), + test + ); +} + +#[test] +fn list_add_wrong_type() { + let result = interpret( + " + x <[str]> = [] + x += 1 + ", + ); + + assert!(result.unwrap_err().is_error(&Error::TypeCheck { + expected: Type::String, + actual: Type::Integer + })) +} diff --git a/tests/block.rs b/tests/block.rs new file mode 100644 index 0000000..e79300d --- /dev/null +++ b/tests/block.rs @@ -0,0 +1,46 @@ +use dust_lang::*; + +#[test] +fn simple() { + assert_eq!(interpret("{ 1 }"), Ok(Value::Integer(1))); +} + +#[test] +fn nested() { + assert_eq!(interpret("{ 1 { 1 + 1 } }"), Ok(Value::Integer(2))); +} + +#[test] +fn with_return() { + assert_eq!(interpret("{ return 1; 1 + 1; }"), Ok(Value::Integer(1))); +} + +#[test] +fn async_with_return() { + assert_eq!( + interpret( + " + async { + return 1 + 1 + 1 + 3 + } + " + ), + Ok(Value::Integer(1)) + ); +} + +#[test] +fn root_returns_like_block() { + assert_eq!( + interpret( + " + return 1 + 1 + 1 + 3 + " + ), + Ok(Value::Integer(1)) + ); +} diff --git a/tests/built_in_values.rs b/tests/built_in_values.rs new file mode 100644 index 0000000..1ceff66 --- /dev/null +++ b/tests/built_in_values.rs @@ -0,0 +1,6 @@ +use dust_lang::*; + +#[test] +fn args() { + assert!(interpret("args").is_ok_and(|value| value.is_list())); +} diff --git a/tests/for_loop.rs b/tests/for_loop.rs new file mode 100644 index 0000000..7023062 --- /dev/null +++ b/tests/for_loop.rs @@ -0,0 +1,28 @@ +use dust_lang::*; + +#[test] +fn simple_for_loop() { + let result = interpret("for i in [1 2 3] { output(i) }"); + + assert_eq!(Ok(Value::none()), result); +} + +#[test] +fn modify_value() { + let result = interpret( + " + list = [] + for i in [1 2 3] { list += i } + list + ", + ); + + assert_eq!( + Ok(Value::List(List::with_items(vec![ + Value::Integer(1), + Value::Integer(2), + Value::Integer(3), + ]))), + result + ); +} diff --git a/tests/if_else.rs b/tests/if_else.rs new file mode 100644 index 0000000..c5624e8 --- /dev/null +++ b/tests/if_else.rs @@ -0,0 +1,61 @@ +use dust_lang::*; + +#[test] +fn r#if() { + assert_eq!( + interpret("if true { 'true' }"), + Ok(Value::string("true".to_string())) + ); +} + +#[test] +fn if_else() { + assert_eq!( + interpret("if false { 1 } else { 2 }"), + Ok(Value::Integer(2)) + ); + assert_eq!( + interpret("if true { 1.0 } else { 42.0 }"), + Ok(Value::Float(1.0)) + ); +} + +#[test] +fn if_else_else_if_else() { + assert_eq!( + interpret( + " + if false { + 'no' + } else if 1 + 1 == 3 { + 'nope' + } else { + 'ok' + } + " + ), + Ok(Value::string("ok".to_string())) + ); +} + +#[test] +fn if_else_if_else_if_else_if_else() { + assert_eq!( + interpret( + " + if false { + 'no' + } else if 1 + 1 == 1 { + 'nope' + } else if 9 / 2 == 4 { + 'nope' + } else if 'foo' == 'bar' { + 'nope' + } else { + 'ok' + } + " + ), + Ok(Value::string("ok".to_string())) + ); +} diff --git a/tests/index.rs b/tests/index.rs new file mode 100644 index 0000000..15f9423 --- /dev/null +++ b/tests/index.rs @@ -0,0 +1,41 @@ +use dust_lang::*; + +#[test] +fn list_index() { + let test = interpret("x = [1 [2] 3] x:1:0").unwrap(); + + assert_eq!(Value::Integer(2), test); +} + +#[test] +fn map_index() { + let test = interpret("x = {y = {z = 2}} x:y:z").unwrap(); + + assert_eq!(Value::Integer(2), test); +} + +#[test] +fn index_function_calls() { + assert_eq!( + interpret( + " + x = [1 2 3] + y = () { 2 } + x:(y()) + ", + ), + Ok(Value::Integer(3)) + ); + + assert_eq!( + interpret( + " + x = { + y = () { 2 } + } + x:y() + ", + ), + Ok(Value::Integer(2)) + ); +} diff --git a/tests/interpret.rs b/tests/interpret.rs deleted file mode 100644 index 087ce30..0000000 --- a/tests/interpret.rs +++ /dev/null @@ -1,509 +0,0 @@ -mod functions; - -mod structure { - use std::collections::BTreeMap; - - use dust_lang::*; - - #[test] - fn simple_structure() { - let result = interpret("struct { x = 0 }"); - - let mut btree_map = BTreeMap::new(); - - btree_map.insert("x".to_string(), (Some(Value::Integer(0)), Type::Integer)); - - assert_eq!(Ok(Value::Structure(btree_map)), result); - } -} - -mod assignment { - use dust_lang::*; - - #[test] - fn simple_assignment() { - let test = interpret("x = 1 x"); - - assert_eq!(Ok(Value::Integer(1)), test); - } - - #[test] - fn simple_assignment_with_type() { - let test = interpret("x = 1 x"); - - assert_eq!(Ok(Value::Integer(1)), test); - } - - #[test] - fn list_add_assign() { - let test = interpret( - " - x <[int]> = [] - x += 1 - x - ", - ); - - assert_eq!( - Ok(Value::List(List::with_items(vec![Value::Integer(1)]))), - test - ); - } - - #[test] - fn list_add_wrong_type() { - let result = interpret( - " - x <[str]> = [] - x += 1 - ", - ); - - assert!(result.unwrap_err().is_error(&Error::TypeCheck { - expected: Type::String, - actual: Type::Integer - })) - } -} - -mod for_loop { - use dust_lang::*; - - #[test] - fn simple_for_loop() { - let result = interpret("for i in [1 2 3] { output(i) }"); - - assert_eq!(Ok(Value::none()), result); - } - - #[test] - fn modify_value() { - let result = interpret( - " - list = [] - for i in [1 2 3] { list += i } - list - ", - ); - - assert_eq!( - Ok(Value::List(List::with_items(vec![ - Value::Integer(1), - Value::Integer(2), - Value::Integer(3), - ]))), - result - ); - } -} - -mod logic { - use dust_lang::*; - - #[test] - fn complex_logic_sequence() { - let result = - interpret("(length([0]) == 1) && (length([0 0]) == 2) && (length([0 0 0]) == 3)"); - - assert_eq!(Ok(Value::Boolean(true)), result); - } -} - -mod value { - use dust_lang::*; - - #[test] - fn empty() { - assert_eq!(interpret("x = 9"), Ok(Value::Option(None))); - assert_eq!(interpret("x = 1 + 1"), Ok(Value::Option(None))); - } - - #[test] - fn integer() { - assert_eq!(interpret("1"), Ok(Value::Integer(1))); - assert_eq!(interpret("123"), Ok(Value::Integer(123))); - assert_eq!(interpret("-666"), Ok(Value::Integer(-666))); - } - - #[test] - fn float() { - assert_eq!(interpret("0.1"), Ok(Value::Float(0.1))); - assert_eq!(interpret("12.3"), Ok(Value::Float(12.3))); - assert_eq!(interpret("-6.66"), Ok(Value::Float(-6.66))); - } - - #[test] - fn string() { - assert_eq!(interpret("\"one\""), Ok(Value::string("one".to_string()))); - assert_eq!(interpret("'one'"), Ok(Value::string("one".to_string()))); - assert_eq!(interpret("`one`"), Ok(Value::string("one".to_string()))); - assert_eq!(interpret("`'one'`"), Ok(Value::string("'one'".to_string()))); - assert_eq!(interpret("'`one`'"), Ok(Value::string("`one`".to_string()))); - assert_eq!( - interpret("\"'one'\""), - Ok(Value::string("'one'".to_string())) - ); - } - - #[test] - fn list() { - assert_eq!( - interpret("[1, 2, 'foobar']"), - Ok(Value::List(List::with_items(vec![ - Value::Integer(1), - Value::Integer(2), - Value::string("foobar".to_string()), - ]))) - ); - } - - #[test] - fn map() { - let map = Map::new(); - - map.set("x".to_string(), Value::Integer(1), None).unwrap(); - map.set("foo".to_string(), Value::string("bar".to_string()), None) - .unwrap(); - - assert_eq!(interpret("{ x = 1, foo = 'bar' }"), Ok(Value::Map(map))); - } - - #[test] - fn map_types() { - let map = Map::new(); - - map.set("x".to_string(), Value::Integer(1), Some(Type::Integer)) - .unwrap(); - map.set( - "foo".to_string(), - Value::string("bar".to_string()), - Some(Type::String), - ) - .unwrap(); - - assert_eq!( - interpret("{ x = 1, foo = 'bar' }"), - Ok(Value::Map(map)) - ); - } - - #[test] - fn map_type_errors() { - assert!(interpret("{ foo = 'bar' }") - .unwrap_err() - .is_error(&Error::TypeCheck { - expected: Type::Boolean, - actual: Type::String - })) - } - - #[test] - fn function() { - let result = interpret("() { 1 }"); - let value = result.unwrap(); - let function = value.as_function().unwrap(); - let function = if let Function::ContextDefined(function) = function { - function - } else { - panic!("Something is wrong with this test..."); - }; - - assert_eq!(&Vec::::with_capacity(0), function.parameters()); - assert_eq!(&Type::Integer, function.return_type()); - - let result = interpret("(x ) { true }"); - let value = result.unwrap(); - let function = value.as_function().unwrap(); - let function = if let Function::ContextDefined(function) = function { - function - } else { - panic!("Something is wrong with this test..."); - }; - - assert_eq!( - &vec![Identifier::new("x".to_string())], - function.parameters() - ); - assert_eq!(&Type::Boolean, function.return_type()); - } - - #[test] - fn option() { - let result = interpret("x = some(1); x").unwrap(); - - assert_eq!(Value::Option(Some(Box::new(Value::Integer(1)))), result); - } -} - -mod if_else { - use dust_lang::*; - - #[test] - fn r#if() { - assert_eq!( - interpret("if true { 'true' }"), - Ok(Value::string("true".to_string())) - ); - } - - #[test] - fn if_else() { - assert_eq!( - interpret("if false { 1 } else { 2 }"), - Ok(Value::Integer(2)) - ); - assert_eq!( - interpret("if true { 1.0 } else { 42.0 }"), - Ok(Value::Float(1.0)) - ); - } - - #[test] - fn if_else_else_if_else() { - assert_eq!( - interpret( - " - if false { - 'no' - } else if 1 + 1 == 3 { - 'nope' - } else { - 'ok' - } - " - ), - Ok(Value::string("ok".to_string())) - ); - } - - #[test] - fn if_else_if_else_if_else_if_else() { - assert_eq!( - interpret( - " - if false { - 'no' - } else if 1 + 1 == 1 { - 'nope' - } else if 9 / 2 == 4 { - 'nope' - } else if 'foo' == 'bar' { - 'nope' - } else { - 'ok' - } - " - ), - Ok(Value::string("ok".to_string())) - ); - } -} - -mod index { - use dust_lang::*; - - #[test] - fn list_index() { - let test = interpret("x = [1 [2] 3] x:1:0").unwrap(); - - assert_eq!(Value::Integer(2), test); - } - - #[test] - fn map_index() { - let test = interpret("x = {y = {z = 2}} x:y:z").unwrap(); - - assert_eq!(Value::Integer(2), test); - } - - #[test] - fn index_function_calls() { - assert_eq!( - interpret( - " - x = [1 2 3] - y = () { 2 } - x:(y()) - ", - ), - Ok(Value::Integer(3)) - ); - - assert_eq!( - interpret( - " - x = { - y = () { 2 } - } - x:y() - ", - ), - Ok(Value::Integer(2)) - ); - } -} - -mod r#match { - use dust_lang::*; - - #[test] - fn r#match() { - let test = interpret( - " - match 1 { - 3 => false - 2 => { false } - 1 => true - } - ", - ) - .unwrap(); - - assert_eq!(Value::Boolean(true), test); - } - - #[test] - fn match_assignment() { - let test = interpret( - " - x = match 1 { - 3 => false - 2 => { false } - 1 => true - } - x - ", - ) - .unwrap(); - - assert_eq!(Value::Boolean(true), test); - } -} - -mod r#while { - use dust_lang::*; - - #[test] - fn while_loop() { - assert_eq!(interpret("while false { 'foo' }"), Ok(Value::Option(None))) - } - - #[test] - fn while_loop_iteration_count() { - assert_eq!( - interpret("i = 0; while i < 3 { i += 1 }; i"), - Ok(Value::Integer(3)) - ) - } -} - -mod type_definition { - use dust_lang::*; - - #[test] - fn simple_type_check() { - let result = interpret("x = 1"); - - assert!(result.unwrap_err().is_error(&Error::TypeCheck { - expected: Type::Boolean, - actual: Type::Integer - })); - } - - #[test] - fn argument_count_check() { - let source = " - foo = (x ) { - x - } - foo() - "; - let result = interpret(&source); - - assert_eq!( - "Expected 1 arguments, but got 0. Occured at (4, 12) to (4, 17). Source: foo()", - result.unwrap_err().to_string() - ) - } - - #[test] - fn callback_type_check() { - let result = interpret( - " - x = (cb <() -> bool>) { - cb() - } - x(() { 1 }) - ", - ); - - assert!(result.unwrap_err().is_error(&Error::TypeCheck { - expected: Type::Function { - parameter_types: vec![], - return_type: Box::new(Type::Boolean), - }, - actual: Type::Function { - parameter_types: vec![], - return_type: Box::new(Type::Integer), - }, - })); - } -} - -mod blocks { - use dust_lang::*; - - #[test] - fn simple() { - assert_eq!(interpret("{ 1 }"), Ok(Value::Integer(1))); - } - - #[test] - fn nested() { - assert_eq!(interpret("{ 1 { 1 + 1 } }"), Ok(Value::Integer(2))); - } - - #[test] - fn with_return() { - assert_eq!(interpret("{ return 1; 1 + 1; }"), Ok(Value::Integer(1))); - } - - #[test] - fn async_with_return() { - assert_eq!( - interpret( - " - async { - return 1 - 1 + 1 - 3 - } - " - ), - Ok(Value::Integer(1)) - ); - } - - #[test] - fn root_returns_like_block() { - assert_eq!( - interpret( - " - return 1 - 1 + 1 - 3 - " - ), - Ok(Value::Integer(1)) - ); - } -} - -mod built_in_values { - use dust_lang::*; - - #[test] - fn args() { - assert!(interpret("args").is_ok_and(|value| value.is_list())); - } -} diff --git a/tests/logic.rs b/tests/logic.rs new file mode 100644 index 0000000..88db197 --- /dev/null +++ b/tests/logic.rs @@ -0,0 +1,8 @@ +use dust_lang::*; + +#[test] +fn complex_logic_sequence() { + let result = interpret("(length([0]) == 1) && (length([0 0]) == 2) && (length([0 0 0]) == 3)"); + + assert_eq!(Ok(Value::Boolean(true)), result); +} diff --git a/tests/match.rs b/tests/match.rs new file mode 100644 index 0000000..9260b85 --- /dev/null +++ b/tests/match.rs @@ -0,0 +1,34 @@ +use dust_lang::*; + +#[test] +fn r#match() { + let test = interpret( + " + match 1 { + 3 => false + 2 => { false } + 1 => true + } + ", + ) + .unwrap(); + + assert_eq!(Value::Boolean(true), test); +} + +#[test] +fn match_assignment() { + let test = interpret( + " + x = match 1 { + 3 => false + 2 => { false } + 1 => true + } + x + ", + ) + .unwrap(); + + assert_eq!(Value::Boolean(true), test); +} diff --git a/tests/structure.rs b/tests/structure.rs new file mode 100644 index 0000000..c0f5c93 --- /dev/null +++ b/tests/structure.rs @@ -0,0 +1,14 @@ +use std::collections::BTreeMap; + +use dust_lang::*; + +#[test] +fn simple_structure() { + let result = interpret("struct { x = 0 }"); + + let mut btree_map = BTreeMap::new(); + + btree_map.insert("x".to_string(), (Some(Value::Integer(0)), Type::Integer)); + + assert_eq!(Ok(Value::Structure(btree_map)), result); +} diff --git a/tests/types.rs b/tests/types.rs new file mode 100644 index 0000000..39b818b --- /dev/null +++ b/tests/types.rs @@ -0,0 +1,50 @@ +use dust_lang::*; + +#[test] +fn simple_type_check() { + let result = interpret("x = 1"); + + assert!(result.unwrap_err().is_error(&Error::TypeCheck { + expected: Type::Boolean, + actual: Type::Integer + })); +} + +#[test] +fn argument_count_check() { + let source = " + foo = (x ) { + x + } + foo() + "; + let result = interpret(&source); + + assert_eq!( + "Expected 1 arguments, but got 0. Occured at (4, 12) to (4, 17). Source: foo()", + result.unwrap_err().to_string() + ) +} + +#[test] +fn callback_type_check() { + let result = interpret( + " + x = (cb <() -> bool>) { + cb() + } + x(() { 1 }) + ", + ); + + assert!(result.unwrap_err().is_error(&Error::TypeCheck { + expected: Type::Function { + parameter_types: vec![], + return_type: Box::new(Type::Boolean), + }, + actual: Type::Function { + parameter_types: vec![], + return_type: Box::new(Type::Integer), + }, + })); +} diff --git a/tests/value.rs b/tests/value.rs new file mode 100644 index 0000000..e60c13c --- /dev/null +++ b/tests/value.rs @@ -0,0 +1,123 @@ +use dust_lang::*; + +#[test] +fn empty() { + assert_eq!(interpret("x = 9"), Ok(Value::Option(None))); + assert_eq!(interpret("x = 1 + 1"), Ok(Value::Option(None))); +} + +#[test] +fn integer() { + assert_eq!(interpret("1"), Ok(Value::Integer(1))); + assert_eq!(interpret("123"), Ok(Value::Integer(123))); + assert_eq!(interpret("-666"), Ok(Value::Integer(-666))); +} + +#[test] +fn float() { + assert_eq!(interpret("0.1"), Ok(Value::Float(0.1))); + assert_eq!(interpret("12.3"), Ok(Value::Float(12.3))); + assert_eq!(interpret("-6.66"), Ok(Value::Float(-6.66))); +} + +#[test] +fn string() { + assert_eq!(interpret("\"one\""), Ok(Value::string("one".to_string()))); + assert_eq!(interpret("'one'"), Ok(Value::string("one".to_string()))); + assert_eq!(interpret("`one`"), Ok(Value::string("one".to_string()))); + assert_eq!(interpret("`'one'`"), Ok(Value::string("'one'".to_string()))); + assert_eq!(interpret("'`one`'"), Ok(Value::string("`one`".to_string()))); + assert_eq!( + interpret("\"'one'\""), + Ok(Value::string("'one'".to_string())) + ); +} + +#[test] +fn list() { + assert_eq!( + interpret("[1, 2, 'foobar']"), + Ok(Value::List(List::with_items(vec![ + Value::Integer(1), + Value::Integer(2), + Value::string("foobar".to_string()), + ]))) + ); +} + +#[test] +fn map() { + let map = Map::new(); + + map.set("x".to_string(), Value::Integer(1), None).unwrap(); + map.set("foo".to_string(), Value::string("bar".to_string()), None) + .unwrap(); + + assert_eq!(interpret("{ x = 1, foo = 'bar' }"), Ok(Value::Map(map))); +} + +#[test] +fn map_types() { + let map = Map::new(); + + map.set("x".to_string(), Value::Integer(1), Some(Type::Integer)) + .unwrap(); + map.set( + "foo".to_string(), + Value::string("bar".to_string()), + Some(Type::String), + ) + .unwrap(); + + assert_eq!( + interpret("{ x = 1, foo = 'bar' }"), + Ok(Value::Map(map)) + ); +} + +#[test] +fn map_type_errors() { + assert!(interpret("{ foo = 'bar' }") + .unwrap_err() + .is_error(&Error::TypeCheck { + expected: Type::Boolean, + actual: Type::String + })) +} + +#[test] +fn function() { + let result = interpret("() { 1 }"); + let value = result.unwrap(); + let function = value.as_function().unwrap(); + let function = if let Function::ContextDefined(function) = function { + function + } else { + panic!("Something is wrong with this test..."); + }; + + assert_eq!(&Vec::::with_capacity(0), function.parameters()); + assert_eq!(&Type::Integer, function.return_type()); + + let result = interpret("(x ) { true }"); + let value = result.unwrap(); + let function = value.as_function().unwrap(); + let function = if let Function::ContextDefined(function) = function { + function + } else { + panic!("Something is wrong with this test..."); + }; + + assert_eq!( + &vec![Identifier::new("x".to_string())], + function.parameters() + ); + assert_eq!(&Type::Boolean, function.return_type()); +} + +#[test] +fn option() { + let result = interpret("x = some(1); x").unwrap(); + + assert_eq!(Value::Option(Some(Box::new(Value::Integer(1)))), result); +} diff --git a/tests/while.rs b/tests/while.rs new file mode 100644 index 0000000..380f666 --- /dev/null +++ b/tests/while.rs @@ -0,0 +1,14 @@ +use dust_lang::*; + +#[test] +fn while_loop() { + assert_eq!(interpret("while false { 'foo' }"), Ok(Value::Option(None))) +} + +#[test] +fn while_loop_iteration_count() { + assert_eq!( + interpret("i = 0; while i < 3 { i += 1 }; i"), + Ok(Value::Integer(3)) + ) +}