diff --git a/dust-lang/tests/assignment_errors.rs b/dust-lang/tests/assignment_errors.rs deleted file mode 100644 index 6f22874..0000000 --- a/dust-lang/tests/assignment_errors.rs +++ /dev/null @@ -1,82 +0,0 @@ -use dust_lang::*; - -#[test] -fn add_assign_expects_mutable_variable() { - let source = "1 += 2"; - - assert_eq!( - parse(source), - Err(DustError::Parse { - error: ParseError::ExpectedMutableVariable { - found: Token::Integer("1").to_owned(), - position: Span(0, 1) - }, - source - }) - ); -} - -#[test] -fn divide_assign_expects_mutable_variable() { - let source = "1 -= 2"; - - assert_eq!( - parse(source), - Err(DustError::Parse { - error: ParseError::ExpectedMutableVariable { - found: Token::Integer("1").to_owned(), - position: Span(0, 1) - }, - source - }) - ); -} - -#[test] -fn multiply_assign_expects_mutable_variable() { - let source = "1 *= 2"; - - assert_eq!( - parse(source), - Err(DustError::Parse { - error: ParseError::ExpectedMutableVariable { - found: Token::Integer("1").to_owned(), - position: Span(0, 1) - }, - source - }) - ); -} - -#[test] -fn subtract_assign_expects_mutable_variable() { - let source = "1 -= 2"; - - assert_eq!( - parse(source), - Err(DustError::Parse { - error: ParseError::ExpectedMutableVariable { - found: Token::Integer("1").to_owned(), - position: Span(0, 1) - }, - source - }) - ); -} - -#[test] -fn let_statement_expects_identifier() { - let source = "let 1 = 2"; - - assert_eq!( - parse(source), - Err(DustError::Parse { - error: ParseError::ExpectedToken { - expected: TokenKind::Identifier, - found: Token::Integer("1").to_owned(), - position: Span(4, 5) - }, - source - }) - ); -} diff --git a/dust-lang/tests/basic.rs b/dust-lang/tests/basic.rs new file mode 100644 index 0000000..a06eebc --- /dev/null +++ b/dust-lang/tests/basic.rs @@ -0,0 +1,66 @@ +use dust_lang::*; + +#[test] +fn constant() { + let source = "42"; + + assert_eq!( + parse(source), + Ok(Chunk::with_data( + None, + vec![ + (Instruction::load_constant(0, 0, false), Span(0, 2)), + (Instruction::r#return(true), Span(2, 2)) + ], + vec![Value::integer(42)], + vec![] + )) + ); + + assert_eq!(run(source), Ok(Some(Value::integer(42)))); +} + +#[test] +fn empty() { + let source = ""; + + assert_eq!( + parse(source), + Ok(Chunk::with_data( + None, + vec![(Instruction::r#return(false), Span(0, 0))], + vec![], + vec![] + )) + ); + assert_eq!(run(source), Ok(None)); +} + +#[test] +fn parentheses_precedence() { + let source = "(1 + 2) * 3"; + + assert_eq!( + parse(source), + Ok(Chunk::with_data( + None, + vec![ + ( + *Instruction::add(0, 0, 1) + .set_b_is_constant() + .set_c_is_constant(), + Span(3, 4) + ), + ( + *Instruction::multiply(1, 0, 2).set_c_is_constant(), + Span(8, 9) + ), + (Instruction::r#return(true), Span(11, 11)), + ], + vec![Value::integer(1), Value::integer(2), Value::integer(3)], + vec![] + )) + ); + + assert_eq!(run(source), Ok(Some(Value::integer(9)))); +} diff --git a/dust-lang/tests/expressions.rs b/dust-lang/tests/expressions.rs deleted file mode 100644 index aaee12f..0000000 --- a/dust-lang/tests/expressions.rs +++ /dev/null @@ -1,542 +0,0 @@ -use dust_lang::*; - -#[test] -fn add() { - let source = "1 + 2"; - - assert_eq!( - parse(source), - Ok(Chunk::with_data( - None, - vec![ - ( - *Instruction::add(0, 0, 1) - .set_b_is_constant() - .set_c_is_constant(), - Span(2, 3) - ), - (Instruction::r#return(true), Span(5, 5)) - ], - vec![Value::integer(1), Value::integer(2)], - vec![] - )) - ); - - assert_eq!(run(source), Ok(Some(Value::integer(3)))); -} - -#[test] -fn add_assign() { - let source = "let mut a = 1; a += 2; a"; - - assert_eq!( - parse(source), - Ok(Chunk::with_data( - None, - vec![ - (Instruction::load_constant(0, 0, false), Span(12, 13)), - (Instruction::define_local(0, 0, true), Span(8, 9)), - (*Instruction::add(0, 0, 2).set_c_is_constant(), Span(17, 19)), - (Instruction::get_local(1, 0), Span(23, 24)), - (Instruction::r#return(true), Span(24, 24)) - ], - vec![Value::integer(1), Value::string("a"), Value::integer(2)], - vec![Local::new(1, None, true, Scope::default(), 0)] - )) - ); - - assert_eq!(run(source), Ok(Some(Value::integer(3)))); -} - -#[test] -fn constant() { - let source = "42"; - - assert_eq!( - parse(source), - Ok(Chunk::with_data( - None, - vec![ - (Instruction::load_constant(0, 0, false), Span(0, 2)), - (Instruction::r#return(true), Span(2, 2)) - ], - vec![Value::integer(42)], - vec![] - )) - ); - - assert_eq!(run(source), Ok(Some(Value::integer(42)))); -} - -#[test] -fn define_local() { - let source = "let x = 42;"; - - assert_eq!( - parse(source), - Ok(Chunk::with_data( - None, - vec![ - (Instruction::load_constant(0, 0, false), Span(8, 10)), - (Instruction::define_local(0, 0, false), Span(4, 5)), - (Instruction::r#return(false), Span(11, 11)) - ], - vec![Value::integer(42), Value::string("x")], - vec![Local::new(1, None, false, Scope::default(), 0)] - )), - ); - - assert_eq!(run(source), Ok(None)); -} - -#[test] -fn divide() { - let source = "2 / 2"; - - assert_eq!( - parse(source), - Ok(Chunk::with_data( - None, - vec![ - ( - *Instruction::divide(0, 0, 0) - .set_b_is_constant() - .set_c_is_constant(), - Span(2, 3) - ), - (Instruction::r#return(true), Span(5, 5)) - ], - vec![Value::integer(2)], - vec![] - )) - ); - - assert_eq!(run(source), Ok(Some(Value::integer(1)))); -} - -#[test] -fn divide_assign() { - let source = "let mut a = 2; a /= 2; a"; - - assert_eq!( - parse(source), - Ok(Chunk::with_data( - None, - vec![ - (Instruction::load_constant(0, 0, false), Span(12, 13)), - (Instruction::define_local(0, 0, true), Span(8, 9)), - ( - *Instruction::divide(0, 0, 0).set_c_is_constant(), - Span(17, 19) - ), - (Instruction::get_local(1, 0), Span(23, 24)), - (Instruction::r#return(true), Span(24, 24)) - ], - vec![Value::integer(2), Value::string("a")], - vec![Local::new(1, None, true, Scope::default(), 0)] - )) - ); - - assert_eq!(run(source), Ok(Some(Value::integer(1)))); -} - -#[test] -fn empty() { - let source = ""; - - assert_eq!( - parse(source), - Ok(Chunk::with_data( - None, - vec![(Instruction::r#return(false), Span(0, 0))], - vec![], - vec![] - )) - ); - assert_eq!(run(source), Ok(None)); -} - -#[test] -fn empty_list() { - let source = "[]"; - - assert_eq!( - parse(source), - Ok(Chunk::with_data( - None, - vec![ - (Instruction::load_list(0, 0, 0), Span(0, 2)), - (Instruction::r#return(true), Span(2, 2)), - ], - vec![], - vec![] - )), - ); - - assert_eq!(run(source), Ok(Some(Value::list(0, 0, Type::Any)))); -} - -#[test] -fn list() { - let source = "[1, 2, 3]"; - - assert_eq!( - parse(source), - Ok(Chunk::with_data( - None, - vec![ - (Instruction::load_constant(0, 0, false), Span(1, 2)), - (Instruction::load_constant(1, 1, false), Span(4, 5)), - (Instruction::load_constant(2, 2, false), Span(7, 8)), - (Instruction::load_list(3, 0, 2), Span(0, 9)), - (Instruction::r#return(true), Span(9, 9)), - ], - vec![Value::integer(1), Value::integer(2), Value::integer(3),], - vec![] - )), - ); - - assert_eq!(run(source), Ok(Some(Value::list(0, 2, Type::Integer)))); -} - -#[test] -fn list_with_complex_expression() { - let source = "[1, 2 + 3 - 4 * 5]"; - - assert_eq!( - parse(source), - Ok(Chunk::with_data( - None, - vec![ - (Instruction::load_constant(0, 0, false), Span(1, 2)), - ( - *Instruction::add(1, 1, 2) - .set_b_is_constant() - .set_c_is_constant(), - Span(6, 7) - ), - ( - *Instruction::multiply(2, 3, 4) - .set_b_is_constant() - .set_c_is_constant(), - Span(14, 15) - ), - (Instruction::subtract(3, 1, 2), Span(10, 11)), - (Instruction::close(1, 3), Span(17, 18)), - (Instruction::load_list(4, 0, 3), Span(0, 18)), - (Instruction::r#return(true), Span(18, 18)), - ], - vec![ - Value::integer(1), - Value::integer(2), - Value::integer(3), - Value::integer(4), - Value::integer(5) - ], - vec![] - )), - ); - - assert_eq!(run(source), Ok(Some(Value::list(0, 3, Type::Integer)))); -} - -#[test] -fn list_with_simple_expression() { - let source = "[1, 2 + 3, 4]"; - - assert_eq!( - parse(source), - Ok(Chunk::with_data( - None, - vec![ - (Instruction::load_constant(0, 0, false), Span(1, 2)), - ( - *Instruction::add(1, 1, 2) - .set_b_is_constant() - .set_c_is_constant(), - Span(6, 7) - ), - (Instruction::load_constant(2, 3, false), Span(11, 12)), - (Instruction::load_list(3, 0, 2), Span(0, 13)), - (Instruction::r#return(true), Span(13, 13)), - ], - vec![ - Value::integer(1), - Value::integer(2), - Value::integer(3), - Value::integer(4), - ], - vec![] - )), - ); - - assert_eq!(run(source), Ok(Some(Value::list(0, 2, Type::Integer)))); -} - -#[test] -fn math_operator_precedence() { - let source = "1 + 2 - 3 * 4 / 5"; - - assert_eq!( - parse(source), - Ok(Chunk::with_data( - None, - vec![ - ( - *Instruction::add(0, 0, 1) - .set_b_is_constant() - .set_c_is_constant(), - Span(2, 3) - ), - ( - *Instruction::multiply(1, 2, 3) - .set_b_is_constant() - .set_c_is_constant(), - Span(10, 11) - ), - ( - *Instruction::divide(2, 1, 4).set_c_is_constant(), - Span(14, 15) - ), - (Instruction::subtract(3, 0, 2), Span(6, 7)), - (Instruction::r#return(true), Span(17, 17)), - ], - vec![ - Value::integer(1), - Value::integer(2), - Value::integer(3), - Value::integer(4), - Value::integer(5), - ], - vec![] - )) - ); - - assert_eq!(run(source), Ok(Some(Value::integer(1)))); -} - -#[test] -fn multiply() { - let source = "1 * 2"; - - assert_eq!( - parse(source), - Ok(Chunk::with_data( - None, - vec![ - ( - *Instruction::multiply(0, 0, 1) - .set_b_is_constant() - .set_c_is_constant(), - Span(2, 3) - ), - (Instruction::r#return(true), Span(5, 5)), - ], - vec![Value::integer(1), Value::integer(2)], - vec![] - )) - ); - - assert_eq!(run(source), Ok(Some(Value::integer(2)))); -} - -#[test] -fn multiply_assign() { - let source = "let mut a = 2; a *= 3 a"; - - assert_eq!( - parse(source), - Ok(Chunk::with_data( - None, - vec![ - (Instruction::load_constant(0, 0, false), Span(12, 13)), - (Instruction::define_local(0, 0, true), Span(8, 9)), - ( - *Instruction::multiply(0, 0, 2).set_c_is_constant(), - Span(17, 19) - ), - (Instruction::get_local(1, 0), Span(22, 23)), - (Instruction::r#return(true), Span(23, 23)) - ], - vec![Value::integer(2), Value::string("a"), Value::integer(3)], - vec![Local::new(1, None, true, Scope::default(), 0),] - )) - ); - - assert_eq!(run(source), Ok(Some(Value::integer(6)))); -} - -#[test] -fn negate() { - let source = "-(42)"; - - assert_eq!( - parse(source), - Ok(Chunk::with_data( - None, - vec![ - (*Instruction::negate(0, 0).set_b_is_constant(), Span(0, 1)), - (Instruction::r#return(true), Span(5, 5)), - ], - vec![Value::integer(42)], - vec![] - )), - ); - - assert_eq!(run(source), Ok(Some(Value::integer(-42)))); -} - -#[test] -fn not() { - let source = "!true"; - - assert_eq!( - parse(source), - Ok(Chunk::with_data( - None, - vec![ - (Instruction::load_boolean(0, true, false), Span(1, 5)), - (Instruction::not(1, 0), Span(0, 1)), - (Instruction::r#return(true), Span(5, 5)), - ], - vec![], - vec![] - )), - ); - - assert_eq!(run(source), Ok(Some(Value::boolean(false)))); -} - -#[test] -fn parentheses_precedence() { - let source = "(1 + 2) * 3"; - - assert_eq!( - parse(source), - Ok(Chunk::with_data( - None, - vec![ - ( - *Instruction::add(0, 0, 1) - .set_b_is_constant() - .set_c_is_constant(), - Span(3, 4) - ), - ( - *Instruction::multiply(1, 0, 2).set_c_is_constant(), - Span(8, 9) - ), - (Instruction::r#return(true), Span(11, 11)), - ], - vec![Value::integer(1), Value::integer(2), Value::integer(3)], - vec![] - )) - ); - - assert_eq!(run(source), Ok(Some(Value::integer(9)))); -} - -#[test] -fn set_local() { - let source = "let mut x = 41; x = 42; x"; - - assert_eq!( - parse(source), - Ok(Chunk::with_data( - None, - vec![ - (Instruction::load_constant(0, 0, false), Span(12, 14)), - (Instruction::define_local(0, 0, true), Span(8, 9)), - (Instruction::load_constant(1, 2, false), Span(20, 22)), - (Instruction::set_local(1, 0), Span(16, 17)), - (Instruction::get_local(2, 0), Span(24, 25)), - (Instruction::r#return(true), Span(25, 25)), - ], - vec![Value::integer(41), Value::string("x"), Value::integer(42)], - vec![Local::new(1, None, true, Scope::default(), 0)] - )), - ); - - assert_eq!(run(source), Ok(Some(Value::integer(42)))); -} - -#[test] -fn subtract() { - let source = "1 - 2"; - - assert_eq!( - parse(source), - Ok(Chunk::with_data( - None, - vec![ - ( - *Instruction::subtract(0, 0, 1) - .set_b_is_constant() - .set_c_is_constant(), - Span(2, 3) - ), - (Instruction::r#return(true), Span(5, 5)), - ], - vec![Value::integer(1), Value::integer(2)], - vec![] - )) - ); - - assert_eq!(run(source), Ok(Some(Value::integer(-1)))); -} - -#[test] -fn subtract_assign() { - let source = "let mut x = 42; x -= 2; x"; - - assert_eq!( - parse(source), - Ok(Chunk::with_data( - None, - vec![ - (Instruction::load_constant(0, 0, false), Span(12, 14)), - (Instruction::define_local(0, 0, true), Span(8, 9)), - ( - *Instruction::subtract(0, 0, 2).set_c_is_constant(), - Span(18, 20) - ), - (Instruction::get_local(1, 0), Span(24, 25)), - (Instruction::r#return(true), Span(25, 25)), - ], - vec![Value::integer(42), Value::string("x"), Value::integer(2)], - vec![Local::new(1, None, true, Scope::default(), 0)] - )), - ); - - assert_eq!(run(source), Ok(Some(Value::integer(40)))); -} - -#[test] -fn variable_and() { - let source = "let a = true; let b = false; a && b"; - - assert_eq!( - parse(source), - Ok(Chunk::with_data( - None, - vec![ - (Instruction::load_boolean(0, true, false), Span(8, 12)), - (Instruction::define_local(0, 0, false), Span(4, 5)), - (Instruction::load_boolean(1, false, false), Span(22, 27)), - (Instruction::define_local(1, 1, false), Span(18, 19)), - (Instruction::get_local(2, 0), Span(29, 30)), - (Instruction::test(2, false), Span(31, 33)), - (Instruction::jump(1, true), Span(31, 33)), - (Instruction::get_local(3, 1), Span(34, 35)), - (Instruction::r#return(true), Span(35, 35)), - ], - vec![Value::string("a"), Value::string("b"),], - vec![ - Local::new(0, None, false, Scope::default(), 0), - Local::new(1, None, false, Scope::default(), 1), - ] - )) - ); - - assert_eq!(run(source), Ok(Some(Value::boolean(false)))); -} diff --git a/dust-lang/tests/lists.rs b/dust-lang/tests/lists.rs new file mode 100644 index 0000000..26b1f6a --- /dev/null +++ b/dust-lang/tests/lists.rs @@ -0,0 +1,118 @@ +use dust_lang::*; + +#[test] +fn empty_list() { + let source = "[]"; + + assert_eq!( + parse(source), + Ok(Chunk::with_data( + None, + vec![ + (Instruction::load_list(0, 0, 0), Span(0, 2)), + (Instruction::r#return(true), Span(2, 2)), + ], + vec![], + vec![] + )), + ); + + assert_eq!(run(source), Ok(Some(Value::list(0, 0, Type::Any)))); +} + +#[test] +fn list() { + let source = "[1, 2, 3]"; + + assert_eq!( + parse(source), + Ok(Chunk::with_data( + None, + vec![ + (Instruction::load_constant(0, 0, false), Span(1, 2)), + (Instruction::load_constant(1, 1, false), Span(4, 5)), + (Instruction::load_constant(2, 2, false), Span(7, 8)), + (Instruction::load_list(3, 0, 2), Span(0, 9)), + (Instruction::r#return(true), Span(9, 9)), + ], + vec![Value::integer(1), Value::integer(2), Value::integer(3),], + vec![] + )), + ); + + assert_eq!(run(source), Ok(Some(Value::list(0, 2, Type::Integer)))); +} + +#[test] +fn list_with_complex_expression() { + let source = "[1, 2 + 3 - 4 * 5]"; + + assert_eq!( + parse(source), + Ok(Chunk::with_data( + None, + vec![ + (Instruction::load_constant(0, 0, false), Span(1, 2)), + ( + *Instruction::add(1, 1, 2) + .set_b_is_constant() + .set_c_is_constant(), + Span(6, 7) + ), + ( + *Instruction::multiply(2, 3, 4) + .set_b_is_constant() + .set_c_is_constant(), + Span(14, 15) + ), + (Instruction::subtract(3, 1, 2), Span(10, 11)), + (Instruction::close(1, 3), Span(17, 18)), + (Instruction::load_list(4, 0, 3), Span(0, 18)), + (Instruction::r#return(true), Span(18, 18)), + ], + vec![ + Value::integer(1), + Value::integer(2), + Value::integer(3), + Value::integer(4), + Value::integer(5) + ], + vec![] + )), + ); + + assert_eq!(run(source), Ok(Some(Value::list(0, 3, Type::Integer)))); +} + +#[test] +fn list_with_simple_expression() { + let source = "[1, 2 + 3, 4]"; + + assert_eq!( + parse(source), + Ok(Chunk::with_data( + None, + vec![ + (Instruction::load_constant(0, 0, false), Span(1, 2)), + ( + *Instruction::add(1, 1, 2) + .set_b_is_constant() + .set_c_is_constant(), + Span(6, 7) + ), + (Instruction::load_constant(2, 3, false), Span(11, 12)), + (Instruction::load_list(3, 0, 2), Span(0, 13)), + (Instruction::r#return(true), Span(13, 13)), + ], + vec![ + Value::integer(1), + Value::integer(2), + Value::integer(3), + Value::integer(4), + ], + vec![] + )), + ); + + assert_eq!(run(source), Ok(Some(Value::list(0, 2, Type::Integer)))); +} diff --git a/dust-lang/tests/logic.rs b/dust-lang/tests/logic.rs index 532df4f..8daf40f 100644 --- a/dust-lang/tests/logic.rs +++ b/dust-lang/tests/logic.rs @@ -45,3 +45,33 @@ fn or() { assert_eq!(run(source), Ok(Some(Value::boolean(true)))); } + +#[test] +fn variable_and() { + let source = "let a = true; let b = false; a && b"; + + assert_eq!( + parse(source), + Ok(Chunk::with_data( + None, + vec![ + (Instruction::load_boolean(0, true, false), Span(8, 12)), + (Instruction::define_local(0, 0, false), Span(4, 5)), + (Instruction::load_boolean(1, false, false), Span(22, 27)), + (Instruction::define_local(1, 1, false), Span(18, 19)), + (Instruction::get_local(2, 0), Span(29, 30)), + (Instruction::test(2, false), Span(31, 33)), + (Instruction::jump(1, true), Span(31, 33)), + (Instruction::get_local(3, 1), Span(34, 35)), + (Instruction::r#return(true), Span(35, 35)), + ], + vec![Value::string("a"), Value::string("b"),], + vec![ + Local::new(0, None, false, Scope::default(), 0), + Local::new(1, None, false, Scope::default(), 1), + ] + )) + ); + + assert_eq!(run(source), Ok(Some(Value::boolean(false)))); +} diff --git a/dust-lang/tests/math.rs b/dust-lang/tests/math.rs new file mode 100644 index 0000000..9e632c2 --- /dev/null +++ b/dust-lang/tests/math.rs @@ -0,0 +1,308 @@ +use dust_lang::*; + +#[test] +fn add() { + let source = "1 + 2"; + + assert_eq!( + parse(source), + Ok(Chunk::with_data( + None, + vec![ + ( + *Instruction::add(0, 0, 1) + .set_b_is_constant() + .set_c_is_constant(), + Span(2, 3) + ), + (Instruction::r#return(true), Span(5, 5)) + ], + vec![Value::integer(1), Value::integer(2)], + vec![] + )) + ); + + assert_eq!(run(source), Ok(Some(Value::integer(3)))); +} + +#[test] +fn add_assign() { + let source = "let mut a = 1; a += 2; a"; + + assert_eq!( + parse(source), + Ok(Chunk::with_data( + None, + vec![ + (Instruction::load_constant(0, 0, false), Span(12, 13)), + (Instruction::define_local(0, 0, true), Span(8, 9)), + (*Instruction::add(0, 0, 2).set_c_is_constant(), Span(17, 19)), + (Instruction::get_local(1, 0), Span(23, 24)), + (Instruction::r#return(true), Span(24, 24)) + ], + vec![Value::integer(1), Value::string("a"), Value::integer(2)], + vec![Local::new(1, None, true, Scope::default(), 0)] + )) + ); + + assert_eq!(run(source), Ok(Some(Value::integer(3)))); +} + +#[test] +fn add_assign_expects_mutable_variable() { + let source = "1 += 2"; + + assert_eq!( + parse(source), + Err(DustError::Parse { + error: ParseError::ExpectedMutableVariable { + found: Token::Integer("1").to_owned(), + position: Span(0, 1) + }, + source + }) + ); +} + +#[test] +fn divide() { + let source = "2 / 2"; + + assert_eq!( + parse(source), + Ok(Chunk::with_data( + None, + vec![ + ( + *Instruction::divide(0, 0, 0) + .set_b_is_constant() + .set_c_is_constant(), + Span(2, 3) + ), + (Instruction::r#return(true), Span(5, 5)) + ], + vec![Value::integer(2)], + vec![] + )) + ); + + assert_eq!(run(source), Ok(Some(Value::integer(1)))); +} + +#[test] +fn divide_assign() { + let source = "let mut a = 2; a /= 2; a"; + + assert_eq!( + parse(source), + Ok(Chunk::with_data( + None, + vec![ + (Instruction::load_constant(0, 0, false), Span(12, 13)), + (Instruction::define_local(0, 0, true), Span(8, 9)), + ( + *Instruction::divide(0, 0, 0).set_c_is_constant(), + Span(17, 19) + ), + (Instruction::get_local(1, 0), Span(23, 24)), + (Instruction::r#return(true), Span(24, 24)) + ], + vec![Value::integer(2), Value::string("a")], + vec![Local::new(1, None, true, Scope::default(), 0)] + )) + ); + + assert_eq!(run(source), Ok(Some(Value::integer(1)))); +} + +#[test] +fn divide_assign_expects_mutable_variable() { + let source = "1 -= 2"; + + assert_eq!( + parse(source), + Err(DustError::Parse { + error: ParseError::ExpectedMutableVariable { + found: Token::Integer("1").to_owned(), + position: Span(0, 1) + }, + source + }) + ); +} + +#[test] +fn math_operator_precedence() { + let source = "1 + 2 - 3 * 4 / 5"; + + assert_eq!( + parse(source), + Ok(Chunk::with_data( + None, + vec![ + ( + *Instruction::add(0, 0, 1) + .set_b_is_constant() + .set_c_is_constant(), + Span(2, 3) + ), + ( + *Instruction::multiply(1, 2, 3) + .set_b_is_constant() + .set_c_is_constant(), + Span(10, 11) + ), + ( + *Instruction::divide(2, 1, 4).set_c_is_constant(), + Span(14, 15) + ), + (Instruction::subtract(3, 0, 2), Span(6, 7)), + (Instruction::r#return(true), Span(17, 17)), + ], + vec![ + Value::integer(1), + Value::integer(2), + Value::integer(3), + Value::integer(4), + Value::integer(5), + ], + vec![] + )) + ); + + assert_eq!(run(source), Ok(Some(Value::integer(1)))); +} + +#[test] +fn multiply() { + let source = "1 * 2"; + + assert_eq!( + parse(source), + Ok(Chunk::with_data( + None, + vec![ + ( + *Instruction::multiply(0, 0, 1) + .set_b_is_constant() + .set_c_is_constant(), + Span(2, 3) + ), + (Instruction::r#return(true), Span(5, 5)), + ], + vec![Value::integer(1), Value::integer(2)], + vec![] + )) + ); + + assert_eq!(run(source), Ok(Some(Value::integer(2)))); +} + +#[test] +fn multiply_assign() { + let source = "let mut a = 2; a *= 3 a"; + + assert_eq!( + parse(source), + Ok(Chunk::with_data( + None, + vec![ + (Instruction::load_constant(0, 0, false), Span(12, 13)), + (Instruction::define_local(0, 0, true), Span(8, 9)), + ( + *Instruction::multiply(0, 0, 2).set_c_is_constant(), + Span(17, 19) + ), + (Instruction::get_local(1, 0), Span(22, 23)), + (Instruction::r#return(true), Span(23, 23)) + ], + vec![Value::integer(2), Value::string("a"), Value::integer(3)], + vec![Local::new(1, None, true, Scope::default(), 0),] + )) + ); + + assert_eq!(run(source), Ok(Some(Value::integer(6)))); +} + +#[test] +fn multiply_assign_expects_mutable_variable() { + let source = "1 *= 2"; + + assert_eq!( + parse(source), + Err(DustError::Parse { + error: ParseError::ExpectedMutableVariable { + found: Token::Integer("1").to_owned(), + position: Span(0, 1) + }, + source + }) + ); +} + +#[test] +fn subtract() { + let source = "1 - 2"; + + assert_eq!( + parse(source), + Ok(Chunk::with_data( + None, + vec![ + ( + *Instruction::subtract(0, 0, 1) + .set_b_is_constant() + .set_c_is_constant(), + Span(2, 3) + ), + (Instruction::r#return(true), Span(5, 5)), + ], + vec![Value::integer(1), Value::integer(2)], + vec![] + )) + ); + + assert_eq!(run(source), Ok(Some(Value::integer(-1)))); +} + +#[test] +fn subtract_assign() { + let source = "let mut x = 42; x -= 2; x"; + + assert_eq!( + parse(source), + Ok(Chunk::with_data( + None, + vec![ + (Instruction::load_constant(0, 0, false), Span(12, 14)), + (Instruction::define_local(0, 0, true), Span(8, 9)), + ( + *Instruction::subtract(0, 0, 2).set_c_is_constant(), + Span(18, 20) + ), + (Instruction::get_local(1, 0), Span(24, 25)), + (Instruction::r#return(true), Span(25, 25)), + ], + vec![Value::integer(42), Value::string("x"), Value::integer(2)], + vec![Local::new(1, None, true, Scope::default(), 0)] + )), + ); + + assert_eq!(run(source), Ok(Some(Value::integer(40)))); +} + +#[test] +fn subtract_assign_expects_mutable_variable() { + let source = "1 -= 2"; + + assert_eq!( + parse(source), + Err(DustError::Parse { + error: ParseError::ExpectedMutableVariable { + found: Token::Integer("1").to_owned(), + position: Span(0, 1) + }, + source + }) + ); +} diff --git a/dust-lang/tests/unary_operations.rs b/dust-lang/tests/unary_operations.rs new file mode 100644 index 0000000..e96dc41 --- /dev/null +++ b/dust-lang/tests/unary_operations.rs @@ -0,0 +1,42 @@ +use dust_lang::*; + +#[test] +fn negate() { + let source = "-(42)"; + + assert_eq!( + parse(source), + Ok(Chunk::with_data( + None, + vec![ + (*Instruction::negate(0, 0).set_b_is_constant(), Span(0, 1)), + (Instruction::r#return(true), Span(5, 5)), + ], + vec![Value::integer(42)], + vec![] + )), + ); + + assert_eq!(run(source), Ok(Some(Value::integer(-42)))); +} + +#[test] +fn not() { + let source = "!true"; + + assert_eq!( + parse(source), + Ok(Chunk::with_data( + None, + vec![ + (Instruction::load_boolean(0, true, false), Span(1, 5)), + (Instruction::not(1, 0), Span(0, 1)), + (Instruction::r#return(true), Span(5, 5)), + ], + vec![], + vec![] + )), + ); + + assert_eq!(run(source), Ok(Some(Value::boolean(false)))); +} diff --git a/dust-lang/tests/variables.rs b/dust-lang/tests/variables.rs new file mode 100644 index 0000000..095cf16 --- /dev/null +++ b/dust-lang/tests/variables.rs @@ -0,0 +1,63 @@ +use dust_lang::*; + +#[test] +fn define_local() { + let source = "let x = 42;"; + + assert_eq!( + parse(source), + Ok(Chunk::with_data( + None, + vec![ + (Instruction::load_constant(0, 0, false), Span(8, 10)), + (Instruction::define_local(0, 0, false), Span(4, 5)), + (Instruction::r#return(false), Span(11, 11)) + ], + vec![Value::integer(42), Value::string("x")], + vec![Local::new(1, None, false, Scope::default(), 0)] + )), + ); + + assert_eq!(run(source), Ok(None)); +} + +#[test] +fn let_statement_expects_identifier() { + let source = "let 1 = 2"; + + assert_eq!( + parse(source), + Err(DustError::Parse { + error: ParseError::ExpectedToken { + expected: TokenKind::Identifier, + found: Token::Integer("1").to_owned(), + position: Span(4, 5) + }, + source + }) + ); +} + +#[test] +fn set_local() { + let source = "let mut x = 41; x = 42; x"; + + assert_eq!( + parse(source), + Ok(Chunk::with_data( + None, + vec![ + (Instruction::load_constant(0, 0, false), Span(12, 14)), + (Instruction::define_local(0, 0, true), Span(8, 9)), + (Instruction::load_constant(1, 2, false), Span(20, 22)), + (Instruction::set_local(1, 0), Span(16, 17)), + (Instruction::get_local(2, 0), Span(24, 25)), + (Instruction::r#return(true), Span(25, 25)), + ], + vec![Value::integer(41), Value::string("x"), Value::integer(42)], + vec![Local::new(1, None, true, Scope::default(), 0)] + )), + ); + + assert_eq!(run(source), Ok(Some(Value::integer(42)))); +}