diff --git a/dust-lang/Cargo.toml b/dust-lang/Cargo.toml index 8f466e5..66826f0 100644 --- a/dust-lang/Cargo.toml +++ b/dust-lang/Cargo.toml @@ -98,3 +98,11 @@ path = "tests/logic/and_and.rs" [[test]] name = "or_or" path = "tests/logic/or_or.rs" + +[[test]] +name = "and_or" +path = "tests/logic/and_or.rs" + +[[test]] +name = "or_and" +path = "tests/logic/or_and.rs" diff --git a/dust-lang/src/compiler/parse_rule.rs b/dust-lang/src/compiler/parse_rule.rs index 90261c5..23e48aa 100644 --- a/dust-lang/src/compiler/parse_rule.rs +++ b/dust-lang/src/compiler/parse_rule.rs @@ -72,7 +72,7 @@ impl From<&Token<'_>> for ParseRule<'_> { Token::DoubleAmpersand => ParseRule { prefix: None, infix: Some(Compiler::parse_logical_binary), - precedence: Precedence::LogicalAnd, + precedence: Precedence::Logic, }, Token::DoubleEqual => ParseRule { prefix: None, @@ -82,7 +82,7 @@ impl From<&Token<'_>> for ParseRule<'_> { Token::DoublePipe => ParseRule { prefix: None, infix: Some(Compiler::parse_logical_binary), - precedence: Precedence::LogicalOr, + precedence: Precedence::Logic, }, Token::DoubleDot => ParseRule { prefix: Some(Compiler::expect_expression), @@ -284,14 +284,13 @@ impl From<&Token<'_>> for ParseRule<'_> { /// Operator precedence levels. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub enum Precedence { - Primary = 9, - Call = 8, - Unary = 7, - Factor = 6, - Term = 5, - Comparison = 4, - LogicalAnd = 3, - LogicalOr = 2, + Primary = 8, + Call = 7, + Unary = 6, + Factor = 5, + Term = 4, + Comparison = 3, + Logic = 2, Assignment = 1, None = 0, } @@ -300,9 +299,8 @@ impl Precedence { pub fn increment(&self) -> Self { match self { Precedence::None => Precedence::Assignment, - Precedence::Assignment => Precedence::LogicalOr, - Precedence::LogicalOr => Precedence::LogicalAnd, - Precedence::LogicalAnd => Precedence::Comparison, + Precedence::Assignment => Precedence::Logic, + Precedence::Logic => Precedence::Comparison, Precedence::Comparison => Precedence::Term, Precedence::Term => Precedence::Factor, Precedence::Factor => Precedence::Unary, diff --git a/dust-lang/tests/logic/and_or.rs b/dust-lang/tests/logic/and_or.rs new file mode 100644 index 0000000..4af65f8 --- /dev/null +++ b/dust-lang/tests/logic/and_or.rs @@ -0,0 +1,267 @@ +use dust_lang::{ + Chunk, FunctionType, Instruction, Span, Type, Value, compile, instruction::TypeCode, run, +}; + +#[test] +fn true_and_true_or_true() { + let source = "true && true || true"; + let chunk = Chunk { + r#type: FunctionType::new([], [], Type::Boolean), + instructions: vec![ + Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false), + Instruction::test(0, true), + Instruction::jump(4, true), + Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false), + Instruction::test(0, false), + Instruction::jump(1, true), + Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false), + Instruction::r#return(true, 0, TypeCode::BOOLEAN), + ], + positions: vec![ + Span(0, 4), + Span(5, 7), + Span(5, 7), + Span(8, 12), + Span(13, 15), + Span(13, 15), + Span(16, 20), + Span(20, 20), + ], + ..Chunk::default() + }; + let return_value = Some(Value::boolean(true)); + + assert_eq!(chunk, compile(source).unwrap()); + assert_eq!(return_value, run(source).unwrap()); +} + +#[test] +fn true_and_false_or_true() { + let source = "true && false || true"; + let chunk = Chunk { + r#type: FunctionType::new([], [], Type::Boolean), + instructions: vec![ + Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false), + Instruction::test(0, true), + Instruction::jump(4, true), + Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false), + Instruction::test(0, false), + Instruction::jump(1, true), + Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false), + Instruction::r#return(true, 0, TypeCode::BOOLEAN), + ], + positions: vec![ + Span(0, 4), + Span(5, 7), + Span(5, 7), + Span(8, 13), + Span(14, 16), + Span(14, 16), + Span(17, 21), + Span(21, 21), + ], + ..Chunk::default() + }; + let return_value = Some(Value::boolean(true)); + + assert_eq!(chunk, compile(source).unwrap()); + assert_eq!(return_value, run(source).unwrap()); +} + +#[test] +fn true_and_true_or_false() { + let source = "true && true || false"; + let chunk = Chunk { + r#type: FunctionType::new([], [], Type::Boolean), + instructions: vec![ + Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false), + Instruction::test(0, true), + Instruction::jump(4, true), + Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false), + Instruction::test(0, false), + Instruction::jump(1, true), + Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false), + Instruction::r#return(true, 0, TypeCode::BOOLEAN), + ], + positions: vec![ + Span(0, 4), + Span(5, 7), + Span(5, 7), + Span(8, 12), + Span(13, 15), + Span(13, 15), + Span(16, 21), + Span(21, 21), + ], + ..Chunk::default() + }; + let return_value = Some(Value::boolean(true)); + + assert_eq!(chunk, compile(source).unwrap()); + assert_eq!(return_value, run(source).unwrap()); +} + +#[test] +fn true_and_false_or_false() { + let source = "true && false || false"; + let chunk = Chunk { + r#type: FunctionType::new([], [], Type::Boolean), + instructions: vec![ + Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false), + Instruction::test(0, true), + Instruction::jump(4, true), + Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false), + Instruction::test(0, false), + Instruction::jump(1, true), + Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false), + Instruction::r#return(true, 0, TypeCode::BOOLEAN), + ], + positions: vec![ + Span(0, 4), + Span(5, 7), + Span(5, 7), + Span(8, 13), + Span(14, 16), + Span(14, 16), + Span(17, 22), + Span(22, 22), + ], + ..Chunk::default() + }; + let return_value = Some(Value::boolean(false)); + + assert_eq!(chunk, compile(source).unwrap()); + assert_eq!(return_value, run(source).unwrap()); +} + +#[test] +fn false_and_true_or_true() { + let source = "false && true || true"; + let chunk = Chunk { + r#type: FunctionType::new([], [], Type::Boolean), + instructions: vec![ + Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false), + Instruction::test(0, true), + Instruction::jump(4, true), + Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false), + Instruction::test(0, false), + Instruction::jump(1, true), + Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false), + Instruction::r#return(true, 0, TypeCode::BOOLEAN), + ], + positions: vec![ + Span(0, 5), + Span(6, 8), + Span(6, 8), + Span(9, 13), + Span(14, 16), + Span(14, 16), + Span(17, 21), + Span(21, 21), + ], + ..Chunk::default() + }; + let return_value = Some(Value::boolean(false)); + + assert_eq!(chunk, compile(source).unwrap()); + assert_eq!(return_value, run(source).unwrap()); +} + +#[test] +fn false_and_false_or_true() { + let source = "false && false || true"; + let chunk = Chunk { + r#type: FunctionType::new([], [], Type::Boolean), + instructions: vec![ + Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false), + Instruction::test(0, true), + Instruction::jump(4, true), + Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false), + Instruction::test(0, false), + Instruction::jump(1, true), + Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false), + Instruction::r#return(true, 0, TypeCode::BOOLEAN), + ], + positions: vec![ + Span(0, 5), + Span(6, 8), + Span(6, 8), + Span(9, 14), + Span(15, 17), + Span(15, 17), + Span(18, 22), + Span(22, 22), + ], + ..Chunk::default() + }; + let return_value = Some(Value::boolean(false)); + + assert_eq!(chunk, compile(source).unwrap()); + assert_eq!(return_value, run(source).unwrap()); +} + +#[test] +fn false_and_true_or_false() { + let source = "false && true || false"; + let chunk = Chunk { + r#type: FunctionType::new([], [], Type::Boolean), + instructions: vec![ + Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false), + Instruction::test(0, true), + Instruction::jump(4, true), + Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false), + Instruction::test(0, false), + Instruction::jump(1, true), + Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false), + Instruction::r#return(true, 0, TypeCode::BOOLEAN), + ], + positions: vec![ + Span(0, 5), + Span(6, 8), + Span(6, 8), + Span(9, 13), + Span(14, 16), + Span(14, 16), + Span(17, 22), + Span(22, 22), + ], + ..Chunk::default() + }; + let return_value = Some(Value::boolean(false)); + + assert_eq!(chunk, compile(source).unwrap()); + assert_eq!(return_value, run(source).unwrap()); +} + +#[test] +fn false_and_false_or_false() { + let source = "false && false || false"; + let chunk = Chunk { + r#type: FunctionType::new([], [], Type::Boolean), + instructions: vec![ + Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false), + Instruction::test(0, true), + Instruction::jump(4, true), + Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false), + Instruction::test(0, false), + Instruction::jump(1, true), + Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false), + Instruction::r#return(true, 0, TypeCode::BOOLEAN), + ], + positions: vec![ + Span(0, 5), + Span(6, 8), + Span(6, 8), + Span(9, 14), + Span(15, 17), + Span(15, 17), + Span(18, 23), + Span(23, 23), + ], + ..Chunk::default() + }; + let return_value = Some(Value::boolean(false)); + + assert_eq!(chunk, compile(source).unwrap()); + assert_eq!(return_value, run(source).unwrap()); +} diff --git a/dust-lang/tests/logic/or_and.rs b/dust-lang/tests/logic/or_and.rs new file mode 100644 index 0000000..856f153 --- /dev/null +++ b/dust-lang/tests/logic/or_and.rs @@ -0,0 +1,266 @@ +use dust_lang::{ + Chunk, FunctionType, Instruction, Span, Type, Value, compile, instruction::TypeCode, run, +}; + +#[test] +fn true_or_true_and_true() { + let source = "true || true && true"; + let chunk = Chunk { + r#type: FunctionType::new([], [], Type::Boolean), + instructions: vec![ + Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false), + Instruction::test(0, false), + Instruction::jump(4, true), + Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false), + Instruction::test(0, true), + Instruction::jump(1, true), + Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false), + Instruction::r#return(true, 0, TypeCode::BOOLEAN), + ], + positions: vec![ + Span(0, 4), + Span(5, 7), + Span(5, 7), + Span(8, 12), + Span(13, 15), + Span(13, 15), + Span(16, 20), + Span(20, 20), + ], + ..Chunk::default() + }; + let return_value = Some(Value::boolean(true)); + + assert_eq!(chunk, compile(source).unwrap()); + assert_eq!(return_value, run(source).unwrap()); +} + +#[test] +fn true_or_false_or_true() { + let source = "true || false || true"; + let chunk = Chunk { + r#type: FunctionType::new([], [], Type::Boolean), + instructions: vec![ + Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false), + Instruction::test(0, false), + Instruction::jump(4, true), + Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false), + Instruction::test(0, false), + Instruction::jump(1, true), + Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false), + Instruction::r#return(true, 0, TypeCode::BOOLEAN), + ], + positions: vec![ + Span(0, 4), + Span(5, 7), + Span(5, 7), + Span(8, 13), + Span(14, 16), + Span(14, 16), + Span(17, 21), + Span(21, 21), + ], + ..Chunk::default() + }; + let return_value = Some(Value::boolean(true)); + + assert_eq!(chunk, compile(source).unwrap()); + assert_eq!(return_value, run(source).unwrap()); +} + +#[test] +fn true_or_true_and_false() { + let source = "true || true && false"; + let chunk = Chunk { + r#type: FunctionType::new([], [], Type::Boolean), + instructions: vec![ + Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false), + Instruction::test(0, false), + Instruction::jump(4, true), + Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false), + Instruction::test(0, true), + Instruction::jump(1, true), + Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false), + Instruction::r#return(true, 0, TypeCode::BOOLEAN), + ], + positions: vec![ + Span(0, 4), + Span(5, 7), + Span(5, 7), + Span(8, 12), + Span(13, 15), + Span(13, 15), + Span(16, 21), + Span(21, 21), + ], + ..Chunk::default() + }; + let return_value = Some(Value::boolean(true)); + + assert_eq!(chunk, compile(source).unwrap()); + assert_eq!(return_value, run(source).unwrap()); +} + +#[test] +fn true_or_false_and_false() { + let source = "true || false && false"; + let chunk = Chunk { + r#type: FunctionType::new([], [], Type::Boolean), + instructions: vec![ + Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false), + Instruction::test(0, false), + Instruction::jump(4, true), + Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false), + Instruction::test(0, true), + Instruction::jump(1, true), + Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false), + Instruction::r#return(true, 0, TypeCode::BOOLEAN), + ], + positions: vec![ + Span(0, 4), + Span(5, 7), + Span(5, 7), + Span(8, 13), + Span(14, 16), + Span(14, 16), + Span(17, 22), + Span(22, 22), + ], + ..Chunk::default() + }; + let return_value = Some(Value::boolean(true)); + + assert_eq!(chunk, compile(source).unwrap()); + assert_eq!(return_value, run(source).unwrap()); +} +#[test] +fn false_or_true_and_true() { + let source = "false || true && true"; + let chunk = Chunk { + r#type: FunctionType::new([], [], Type::Boolean), + instructions: vec![ + Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false), + Instruction::test(0, false), + Instruction::jump(4, true), + Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false), + Instruction::test(0, true), + Instruction::jump(1, true), + Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false), + Instruction::r#return(true, 0, TypeCode::BOOLEAN), + ], + positions: vec![ + Span(0, 5), + Span(6, 8), + Span(6, 8), + Span(9, 13), + Span(14, 16), + Span(14, 16), + Span(17, 21), + Span(21, 21), + ], + ..Chunk::default() + }; + let return_value = Some(Value::boolean(true)); + + assert_eq!(chunk, compile(source).unwrap()); + assert_eq!(return_value, run(source).unwrap()); +} + +#[test] +fn false_or_false_and_true() { + let source = "false || false && true"; + let chunk = Chunk { + r#type: FunctionType::new([], [], Type::Boolean), + instructions: vec![ + Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false), + Instruction::test(0, false), + Instruction::jump(4, true), + Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false), + Instruction::test(0, true), + Instruction::jump(1, true), + Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false), + Instruction::r#return(true, 0, TypeCode::BOOLEAN), + ], + positions: vec![ + Span(0, 5), + Span(6, 8), + Span(6, 8), + Span(9, 14), + Span(15, 17), + Span(15, 17), + Span(18, 22), + Span(22, 22), + ], + ..Chunk::default() + }; + let return_value = Some(Value::boolean(false)); + + assert_eq!(chunk, compile(source).unwrap()); + assert_eq!(return_value, run(source).unwrap()); +} + +#[test] +fn false_or_true_and_false() { + let source = "false || true && false"; + let chunk = Chunk { + r#type: FunctionType::new([], [], Type::Boolean), + instructions: vec![ + Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false), + Instruction::test(0, false), + Instruction::jump(4, true), + Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false), + Instruction::test(0, true), + Instruction::jump(1, true), + Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false), + Instruction::r#return(true, 0, TypeCode::BOOLEAN), + ], + positions: vec![ + Span(0, 5), + Span(6, 8), + Span(6, 8), + Span(9, 13), + Span(14, 16), + Span(14, 16), + Span(17, 22), + Span(22, 22), + ], + ..Chunk::default() + }; + let return_value = Some(Value::boolean(false)); + + assert_eq!(chunk, compile(source).unwrap()); + assert_eq!(return_value, run(source).unwrap()); +} + +#[test] +fn false_or_false_and_false() { + let source = "false || false && false"; + let chunk = Chunk { + r#type: FunctionType::new([], [], Type::Boolean), + instructions: vec![ + Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false), + Instruction::test(0, false), + Instruction::jump(4, true), + Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false), + Instruction::test(0, true), + Instruction::jump(1, true), + Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false), + Instruction::r#return(true, 0, TypeCode::BOOLEAN), + ], + positions: vec![ + Span(0, 5), + Span(6, 8), + Span(6, 8), + Span(9, 14), + Span(15, 17), + Span(15, 17), + Span(18, 23), + Span(23, 23), + ], + ..Chunk::default() + }; + let return_value = Some(Value::boolean(false)); + + assert_eq!(chunk, compile(source).unwrap()); + assert_eq!(return_value, run(source).unwrap()); +}