diff --git a/dust-lang/Cargo.toml b/dust-lang/Cargo.toml index 39edbc5..a83789b 100644 --- a/dust-lang/Cargo.toml +++ b/dust-lang/Cargo.toml @@ -62,3 +62,23 @@ path = "tests/math/modulo.rs" [[test]] name = "less" path = "tests/comparison/less.rs" + +[[test]] +name = "equal" +path = "tests/comparison/equal.rs" + +[[test]] +name = "less_equal" +path = "tests/comparison/less_equal.rs" + +[[test]] +name = "greater" +path = "tests/comparison/greater.rs" + +[[test]] +name = "not_equal" +path = "tests/comparison/not_equal.rs" + +[[test]] +name = "greater_equal" +path = "tests/comparison/greater_equal.rs" diff --git a/dust-lang/src/vm/action.rs b/dust-lang/src/vm/action.rs index c7956b0..0f75ce9 100644 --- a/dust-lang/src/vm/action.rs +++ b/dust-lang/src/vm/action.rs @@ -171,7 +171,7 @@ pub fn point(instruction: InstructionFields, thread: &mut Thread) { thread.set_list_register(destination, register); } - _ => unimplemented!(), + _ => unreachable!(), } } @@ -216,7 +216,7 @@ pub fn close(instruction: InstructionFields, thread: &mut Thread) { thread.close_list_register(register_index); } } - _ => unimplemented!(), + _ => unreachable!(), } } @@ -275,7 +275,7 @@ pub fn load_constant(instruction: InstructionFields, thread: &mut Thread) { thread.set_string_register(destination, register); } - _ => unimplemented!(), + _ => unreachable!(), } if jump_next { @@ -360,7 +360,7 @@ pub fn load_list(instruction: InstructionFields, thread: &mut Thread) { Pointer::RegisterList(register_index) } - _ => unimplemented!(), + _ => unreachable!(), }; item_pointers.push(pointer); @@ -589,7 +589,7 @@ pub fn add(instruction: InstructionFields, thread: &mut Thread) { thread.set_string_register(destination, register); } - _ => unimplemented!(), + _ => unreachable!(), } } @@ -761,7 +761,7 @@ pub fn multiply(instruction: InstructionFields, thread: &mut Thread) { thread.set_integer_register(destination, register); } - _ => unimplemented!(), + _ => unreachable!(), } } @@ -949,7 +949,181 @@ pub fn test(instruction: InstructionFields, thread: &mut Thread) { pub fn test_set(instruction: InstructionFields, thread: &mut Thread) {} -pub fn equal(instruction: InstructionFields, thread: &mut Thread) {} +pub fn equal(instruction: InstructionFields, thread: &mut Thread) { + let comparator = instruction.d_field; + let left = instruction.b_field as usize; + let left_type = instruction.b_type; + let left_is_constant = instruction.b_is_constant; + let right = instruction.c_field as usize; + let right_type = instruction.c_type; + let right_is_constant = instruction.c_is_constant; + + match (left_type, right_type) { + (TypeCode::BOOLEAN, TypeCode::BOOLEAN) => { + let left_value = if left_is_constant { + if cfg!(debug_assertions) { + thread.get_constant(left).as_boolean().unwrap() + } else { + unsafe { thread.get_constant(left).as_boolean().unwrap_unchecked() } + } + } else { + thread.get_boolean_register(left) + }; + let right_value = if right_is_constant { + if cfg!(debug_assertions) { + thread.get_constant(right).as_boolean().unwrap() + } else { + unsafe { thread.get_constant(right).as_boolean().unwrap_unchecked() } + } + } else { + thread.get_boolean_register(right) + }; + let result = left_value == right_value; + + if result == comparator { + thread.current_frame_mut().ip += 1; + } + } + (TypeCode::BYTE, TypeCode::BYTE) => { + let left_value = if left_is_constant { + if cfg!(debug_assertions) { + thread.get_constant(left).as_byte().unwrap() + } else { + unsafe { thread.get_constant(left).as_byte().unwrap_unchecked() } + } + } else { + thread.get_byte_register(left) + }; + let right_value = if right_is_constant { + if cfg!(debug_assertions) { + thread.get_constant(right).as_byte().unwrap() + } else { + unsafe { thread.get_constant(right).as_byte().unwrap_unchecked() } + } + } else { + thread.get_byte_register(right) + }; + let result = left_value == right_value; + + if result == comparator { + thread.current_frame_mut().ip += 1; + } + } + (TypeCode::CHARACTER, TypeCode::CHARACTER) => { + let left_value = if left_is_constant { + if cfg!(debug_assertions) { + thread.get_constant(left).as_character().unwrap() + } else { + unsafe { thread.get_constant(left).as_character().unwrap_unchecked() } + } + } else { + thread.get_character_register(left) + }; + let right_value = if right_is_constant { + if cfg!(debug_assertions) { + thread.get_constant(right).as_character().unwrap() + } else { + unsafe { thread.get_constant(right).as_character().unwrap_unchecked() } + } + } else { + thread.get_character_register(right) + }; + let result = left_value == right_value; + + if result == comparator { + thread.current_frame_mut().ip += 1; + } + } + (TypeCode::FLOAT, TypeCode::FLOAT) => { + let left_value = if left_is_constant { + if cfg!(debug_assertions) { + thread.get_constant(left).as_float().unwrap() + } else { + unsafe { thread.get_constant(left).as_float().unwrap_unchecked() } + } + } else { + thread.get_float_register(left) + }; + let right_value = if right_is_constant { + if cfg!(debug_assertions) { + thread.get_constant(right).as_float().unwrap() + } else { + unsafe { thread.get_constant(right).as_float().unwrap_unchecked() } + } + } else { + thread.get_float_register(right) + }; + let result = left_value == right_value; + + if result == comparator { + thread.current_frame_mut().ip += 1; + } + } + (TypeCode::INTEGER, TypeCode::INTEGER) => { + let left_value = if left_is_constant { + if cfg!(debug_assertions) { + thread.get_constant(left).as_integer().unwrap() + } else { + unsafe { thread.get_constant(left).as_integer().unwrap_unchecked() } + } + } else { + thread.get_integer_register(left) + }; + let right_value = if right_is_constant { + if cfg!(debug_assertions) { + thread.get_constant(right).as_integer().unwrap() + } else { + unsafe { thread.get_constant(right).as_integer().unwrap_unchecked() } + } + } else { + thread.get_integer_register(right) + }; + let result = left_value == right_value; + + if result == comparator { + thread.current_frame_mut().ip += 1; + } + } + (TypeCode::STRING, TypeCode::STRING) => { + let left_value = if left_is_constant { + if cfg!(debug_assertions) { + thread.get_constant(left).as_string().unwrap().clone() + } else { + unsafe { + thread + .get_constant(left) + .as_string() + .unwrap_unchecked() + .clone() + } + } + } else { + thread.get_string_register(left).clone() + }; + let right_value = if right_is_constant { + if cfg!(debug_assertions) { + thread.get_constant(right).as_string().unwrap().clone() + } else { + unsafe { + thread + .get_constant(right) + .as_string() + .unwrap_unchecked() + .clone() + } + } + } else { + thread.get_string_register(right).clone() + }; + let result = left_value == right_value; + + if result == comparator { + thread.current_frame_mut().ip += 1; + } + } + _ => unreachable!(), + } +} pub fn less(instruction: InstructionFields, thread: &mut Thread) { let comparator = instruction.d_field; @@ -961,6 +1135,31 @@ pub fn less(instruction: InstructionFields, thread: &mut Thread) { let right_is_constant = instruction.c_is_constant; match (left_type, right_type) { + (TypeCode::BOOLEAN, TypeCode::BOOLEAN) => { + let left_value = if left_is_constant { + if cfg!(debug_assertions) { + thread.get_constant(left).as_boolean().unwrap() + } else { + unsafe { thread.get_constant(left).as_boolean().unwrap_unchecked() } + } + } else { + thread.get_boolean_register(left) + }; + let right_value = if right_is_constant { + if cfg!(debug_assertions) { + thread.get_constant(right).as_boolean().unwrap() + } else { + unsafe { thread.get_constant(right).as_boolean().unwrap_unchecked() } + } + } else { + thread.get_boolean_register(right) + }; + let result = left_value < right_value; + + if result == comparator { + thread.current_frame_mut().ip += 1; + } + } (TypeCode::BYTE, TypeCode::BYTE) => { let left_value = if left_is_constant { if cfg!(debug_assertions) { @@ -1098,11 +1297,185 @@ pub fn less(instruction: InstructionFields, thread: &mut Thread) { thread.current_frame_mut().ip += 1; } } - _ => unimplemented!(), + _ => unreachable!(), } } -pub fn less_equal(instruction: InstructionFields, thread: &mut Thread) {} +pub fn less_equal(instruction: InstructionFields, thread: &mut Thread) { + let comparator = instruction.d_field; + let left = instruction.b_field as usize; + let left_type = instruction.b_type; + let left_is_constant = instruction.b_is_constant; + let right = instruction.c_field as usize; + let right_type = instruction.c_type; + let right_is_constant = instruction.c_is_constant; + + match (left_type, right_type) { + (TypeCode::BOOLEAN, TypeCode::BOOLEAN) => { + let left_value = if left_is_constant { + if cfg!(debug_assertions) { + thread.get_constant(left).as_boolean().unwrap() + } else { + unsafe { thread.get_constant(left).as_boolean().unwrap_unchecked() } + } + } else { + thread.get_boolean_register(left) + }; + let right_value = if right_is_constant { + if cfg!(debug_assertions) { + thread.get_constant(right).as_boolean().unwrap() + } else { + unsafe { thread.get_constant(right).as_boolean().unwrap_unchecked() } + } + } else { + thread.get_boolean_register(right) + }; + let result = left_value <= right_value; + + if result == comparator { + thread.current_frame_mut().ip += 1; + } + } + (TypeCode::BYTE, TypeCode::BYTE) => { + let left_value = if left_is_constant { + if cfg!(debug_assertions) { + thread.get_constant(left).as_byte().unwrap() + } else { + unsafe { thread.get_constant(left).as_byte().unwrap_unchecked() } + } + } else { + thread.get_byte_register(left) + }; + let right_value = if right_is_constant { + if cfg!(debug_assertions) { + thread.get_constant(right).as_byte().unwrap() + } else { + unsafe { thread.get_constant(right).as_byte().unwrap_unchecked() } + } + } else { + thread.get_byte_register(right) + }; + let result = left_value <= right_value; + + if result == comparator { + thread.current_frame_mut().ip += 1; + } + } + (TypeCode::CHARACTER, TypeCode::CHARACTER) => { + let left_value = if left_is_constant { + if cfg!(debug_assertions) { + thread.get_constant(left).as_character().unwrap() + } else { + unsafe { thread.get_constant(left).as_character().unwrap_unchecked() } + } + } else { + thread.get_character_register(left) + }; + let right_value = if right_is_constant { + if cfg!(debug_assertions) { + thread.get_constant(right).as_character().unwrap() + } else { + unsafe { thread.get_constant(right).as_character().unwrap_unchecked() } + } + } else { + thread.get_character_register(right) + }; + let result = left_value <= right_value; + + if result == comparator { + thread.current_frame_mut().ip += 1; + } + } + (TypeCode::FLOAT, TypeCode::FLOAT) => { + let left_value = if left_is_constant { + if cfg!(debug_assertions) { + thread.get_constant(left).as_float().unwrap() + } else { + unsafe { thread.get_constant(left).as_float().unwrap_unchecked() } + } + } else { + thread.get_float_register(left) + }; + let right_value = if right_is_constant { + if cfg!(debug_assertions) { + thread.get_constant(right).as_float().unwrap() + } else { + unsafe { thread.get_constant(right).as_float().unwrap_unchecked() } + } + } else { + thread.get_float_register(right) + }; + let result = left_value <= right_value; + + if result == comparator { + thread.current_frame_mut().ip += 1; + } + } + (TypeCode::INTEGER, TypeCode::INTEGER) => { + let left_value = if left_is_constant { + if cfg!(debug_assertions) { + thread.get_constant(left).as_integer().unwrap() + } else { + unsafe { thread.get_constant(left).as_integer().unwrap_unchecked() } + } + } else { + thread.get_integer_register(left) + }; + let right_value = if right_is_constant { + if cfg!(debug_assertions) { + thread.get_constant(right).as_integer().unwrap() + } else { + unsafe { thread.get_constant(right).as_integer().unwrap_unchecked() } + } + } else { + thread.get_integer_register(right) + }; + let result = left_value <= right_value; + + if result == comparator { + thread.current_frame_mut().ip += 1; + } + } + (TypeCode::STRING, TypeCode::STRING) => { + let left_value = if left_is_constant { + if cfg!(debug_assertions) { + thread.get_constant(left).as_string().unwrap().clone() + } else { + unsafe { + thread + .get_constant(left) + .as_string() + .unwrap_unchecked() + .clone() + } + } + } else { + thread.get_string_register(left).clone() + }; + let right_value = if right_is_constant { + if cfg!(debug_assertions) { + thread.get_constant(right).as_string().unwrap().clone() + } else { + unsafe { + thread + .get_constant(right) + .as_string() + .unwrap_unchecked() + .clone() + } + } + } else { + thread.get_string_register(right).clone() + }; + let result = left_value <= right_value; + + if result == comparator { + thread.current_frame_mut().ip += 1; + } + } + _ => unreachable!(), + } +} pub fn negate(instruction: InstructionFields, thread: &mut Thread) {} @@ -1169,7 +1542,7 @@ pub fn r#return(instruction: InstructionFields, thread: &mut Thread) { item_type: abstract_list.item_type, }))); } - _ => unimplemented!(), + _ => unreachable!(), } } else { thread.return_value = Some(None); diff --git a/dust-lang/tests/comparison/equal.rs b/dust-lang/tests/comparison/equal.rs new file mode 100644 index 0000000..ee8a9a0 --- /dev/null +++ b/dust-lang/tests/comparison/equal.rs @@ -0,0 +1,164 @@ +use dust_lang::{ + Chunk, ConcreteValue, DustString, FunctionType, Instruction, Operand, Span, Type, Value, + compile, instruction::TypeCode, run, +}; + +#[test] +fn equal_bytes() { + let source = "0x0A == 0x03"; + let chunk = Chunk { + r#type: FunctionType::new([], [], Type::Boolean), + instructions: vec![ + Instruction::load_encoded(0, 0x0A, TypeCode::BYTE, false), + Instruction::load_encoded(1, 0x03, TypeCode::BYTE, false), + Instruction::equal( + true, + Operand::Register(0, TypeCode::BYTE), + Operand::Register(1, TypeCode::BYTE), + ), + Instruction::jump(1, true), + Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, true), + Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false), + Instruction::r#return(true, 0, TypeCode::BOOLEAN), + ], + positions: vec![ + Span(0, 4), + Span(8, 12), + Span(0, 12), + Span(0, 12), + Span(0, 12), + Span(0, 12), + Span(12, 12), + ], + ..Chunk::default() + }; + let return_value = Some(Value::boolean(false)); + + assert_eq!(chunk, compile(source).unwrap()); + assert_eq!(return_value, run(source).unwrap()); +} + +#[test] +fn equal_characters() { + let source = "'a' == 'b'"; + let chunk = Chunk { + r#type: FunctionType::new([], [], Type::Boolean), + instructions: vec![ + Instruction::equal( + true, + Operand::Constant(0, TypeCode::CHARACTER), + Operand::Constant(1, TypeCode::CHARACTER), + ), + Instruction::jump(1, true), + Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, true), + Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false), + Instruction::r#return(true, 0, TypeCode::BOOLEAN), + ], + positions: vec![ + Span(0, 10), + Span(0, 10), + Span(0, 10), + Span(0, 10), + Span(10, 10), + ], + constants: vec![ConcreteValue::Character('a'), ConcreteValue::Character('b')], + ..Chunk::default() + }; + let return_value = Some(Value::boolean(false)); + + assert_eq!(chunk, compile(source).unwrap()); + assert_eq!(return_value, run(source).unwrap()); +} + +#[test] +fn equal_floats() { + let source = "10.0 == 3.0"; + let chunk = Chunk { + r#type: FunctionType::new([], [], Type::Boolean), + instructions: vec![ + Instruction::equal( + true, + Operand::Constant(0, TypeCode::FLOAT), + Operand::Constant(1, TypeCode::FLOAT), + ), + Instruction::jump(1, true), + Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, true), + Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false), + Instruction::r#return(true, 0, TypeCode::BOOLEAN), + ], + positions: vec![ + Span(0, 11), + Span(0, 11), + Span(0, 11), + Span(0, 11), + Span(11, 11), + ], + constants: vec![ConcreteValue::Float(10.0), ConcreteValue::Float(3.0)], + ..Chunk::default() + }; + let return_value = Some(Value::boolean(false)); + + assert_eq!(chunk, compile(source).unwrap()); + assert_eq!(return_value, run(source).unwrap()); +} + +#[test] +fn equal_integers() { + let source = "10 == 3"; + let chunk = Chunk { + r#type: FunctionType::new([], [], Type::Boolean), + instructions: vec![ + Instruction::equal( + true, + Operand::Constant(0, TypeCode::INTEGER), + Operand::Constant(1, TypeCode::INTEGER), + ), + Instruction::jump(1, true), + Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, true), + Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false), + Instruction::r#return(true, 0, TypeCode::BOOLEAN), + ], + positions: vec![Span(0, 7), Span(0, 7), Span(0, 7), Span(0, 7), Span(7, 7)], + constants: vec![ConcreteValue::Integer(10), ConcreteValue::Integer(3)], + ..Chunk::default() + }; + let return_value = Some(Value::boolean(false)); + + assert_eq!(chunk, compile(source).unwrap()); + assert_eq!(return_value, run(source).unwrap()); +} + +#[test] +fn equal_strings() { + let source = "\"abc\" == \"def\""; + let chunk = Chunk { + r#type: FunctionType::new([], [], Type::Boolean), + instructions: vec![ + Instruction::equal( + true, + Operand::Constant(0, TypeCode::STRING), + Operand::Constant(1, TypeCode::STRING), + ), + Instruction::jump(1, true), + Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, true), + Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false), + Instruction::r#return(true, 0, TypeCode::BOOLEAN), + ], + positions: vec![ + Span(0, 14), + Span(0, 14), + Span(0, 14), + Span(0, 14), + Span(14, 14), + ], + constants: vec![ + ConcreteValue::String(DustString::from("abc")), + ConcreteValue::String(DustString::from("def")), + ], + ..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/comparison/greater.rs b/dust-lang/tests/comparison/greater.rs new file mode 100644 index 0000000..19cc140 --- /dev/null +++ b/dust-lang/tests/comparison/greater.rs @@ -0,0 +1,158 @@ +use dust_lang::{ + Chunk, ConcreteValue, DustString, FunctionType, Instruction, Operand, Span, Type, Value, + compile, instruction::TypeCode, run, +}; + +#[test] +fn greater_bytes() { + let source = "0x0A > 0x03"; + let chunk = Chunk { + r#type: FunctionType::new([], [], Type::Boolean), + instructions: vec![ + Instruction::load_encoded(0, 0x0A, TypeCode::BYTE, false), + Instruction::load_encoded(1, 0x03, TypeCode::BYTE, false), + Instruction::less_equal( + false, + Operand::Register(0, TypeCode::BYTE), + Operand::Register(1, TypeCode::BYTE), + ), + Instruction::jump(1, true), + Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, true), + Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false), + Instruction::r#return(true, 0, TypeCode::BOOLEAN), + ], + positions: vec![ + Span(0, 4), + Span(7, 11), + Span(0, 11), + Span(0, 11), + Span(0, 11), + Span(0, 11), + Span(11, 11), + ], + ..Chunk::default() + }; + let return_value = Some(Value::boolean(true)); + + assert_eq!(chunk, compile(source).unwrap()); + assert_eq!(return_value, run(source).unwrap()); +} + +#[test] +fn greater_characters() { + let source = "'a' > 'b'"; + let chunk = Chunk { + r#type: FunctionType::new([], [], Type::Boolean), + instructions: vec![ + Instruction::less_equal( + false, + Operand::Constant(0, TypeCode::CHARACTER), + Operand::Constant(1, TypeCode::CHARACTER), + ), + Instruction::jump(1, true), + Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, true), + Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false), + Instruction::r#return(true, 0, TypeCode::BOOLEAN), + ], + positions: vec![Span(0, 9), Span(0, 9), Span(0, 9), Span(0, 9), Span(9, 9)], + constants: vec![ConcreteValue::Character('a'), ConcreteValue::Character('b')], + ..Chunk::default() + }; + let return_value = Some(Value::boolean(false)); + + assert_eq!(chunk, compile(source).unwrap()); + assert_eq!(return_value, run(source).unwrap()); +} + +#[test] +fn greater_floats() { + let source = "10.0 > 3.0"; + let chunk = Chunk { + r#type: FunctionType::new([], [], Type::Boolean), + instructions: vec![ + Instruction::less_equal( + false, + Operand::Constant(0, TypeCode::FLOAT), + Operand::Constant(1, TypeCode::FLOAT), + ), + Instruction::jump(1, true), + Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, true), + Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false), + Instruction::r#return(true, 0, TypeCode::BOOLEAN), + ], + positions: vec![ + Span(0, 10), + Span(0, 10), + Span(0, 10), + Span(0, 10), + Span(10, 10), + ], + constants: vec![ConcreteValue::Float(10.0), ConcreteValue::Float(3.0)], + ..Chunk::default() + }; + let return_value = Some(Value::boolean(true)); + + assert_eq!(chunk, compile(source).unwrap()); + assert_eq!(return_value, run(source).unwrap()); +} + +#[test] +fn greater_integers() { + let source = "10 > 3"; + let chunk = Chunk { + r#type: FunctionType::new([], [], Type::Boolean), + instructions: vec![ + Instruction::less_equal( + false, + Operand::Constant(0, TypeCode::INTEGER), + Operand::Constant(1, TypeCode::INTEGER), + ), + Instruction::jump(1, true), + Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, true), + Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false), + Instruction::r#return(true, 0, TypeCode::BOOLEAN), + ], + positions: vec![Span(0, 6), Span(0, 6), Span(0, 6), Span(0, 6), Span(6, 6)], + constants: vec![ConcreteValue::Integer(10), ConcreteValue::Integer(3)], + ..Chunk::default() + }; + let return_value = Some(Value::boolean(true)); + + assert_eq!(chunk, compile(source).unwrap()); + assert_eq!(return_value, run(source).unwrap()); +} + +#[test] +fn greater_strings() { + let source = "\"abc\" > \"def\""; + let chunk = Chunk { + r#type: FunctionType::new([], [], Type::Boolean), + instructions: vec![ + Instruction::less_equal( + false, + Operand::Constant(0, TypeCode::STRING), + Operand::Constant(1, TypeCode::STRING), + ), + Instruction::jump(1, true), + Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, true), + Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false), + Instruction::r#return(true, 0, TypeCode::BOOLEAN), + ], + positions: vec![ + Span(0, 13), + Span(0, 13), + Span(0, 13), + Span(0, 13), + Span(13, 13), + ], + constants: vec![ + ConcreteValue::String(DustString::from("abc")), + ConcreteValue::String(DustString::from("def")), + ], + ..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/comparison/greater_equal.rs b/dust-lang/tests/comparison/greater_equal.rs new file mode 100644 index 0000000..2b1f3e5 --- /dev/null +++ b/dust-lang/tests/comparison/greater_equal.rs @@ -0,0 +1,199 @@ +use dust_lang::{ + Chunk, ConcreteValue, DustString, FunctionType, Instruction, Operand, Span, Type, Value, + compile, instruction::TypeCode, run, +}; + +#[test] +fn great_equal_booleans() { + let source = "true >= false"; + let chunk = Chunk { + r#type: FunctionType::new([], [], Type::Boolean), + instructions: vec![ + Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false), + Instruction::load_encoded(1, false as u8, TypeCode::BOOLEAN, false), + Instruction::less( + false, + Operand::Register(0, TypeCode::BOOLEAN), + Operand::Register(1, TypeCode::BOOLEAN), + ), + Instruction::jump(1, true), + Instruction::load_encoded(2, true as u8, TypeCode::BOOLEAN, true), + Instruction::load_encoded(2, false as u8, TypeCode::BOOLEAN, false), + Instruction::r#return(true, 2, TypeCode::BOOLEAN), + ], + positions: vec![ + Span(0, 4), + Span(8, 13), + Span(0, 13), + Span(0, 13), + Span(0, 13), + Span(0, 13), + Span(13, 13), + ], + ..Chunk::default() + }; + let return_value = Some(Value::boolean(true)); + + assert_eq!(chunk, compile(source).unwrap()); + assert_eq!(return_value, run(source).unwrap()); +} + +#[test] +fn greater_equal_bytes() { + let source = "0x0A >= 0x03"; + let chunk = Chunk { + r#type: FunctionType::new([], [], Type::Boolean), + instructions: vec![ + Instruction::load_encoded(0, 0x0A, TypeCode::BYTE, false), + Instruction::load_encoded(1, 0x03, TypeCode::BYTE, false), + Instruction::less( + false, + Operand::Register(0, TypeCode::BYTE), + Operand::Register(1, TypeCode::BYTE), + ), + Instruction::jump(1, true), + Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, true), + Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false), + Instruction::r#return(true, 0, TypeCode::BOOLEAN), + ], + positions: vec![ + Span(0, 4), + Span(8, 12), + Span(0, 12), + Span(0, 12), + Span(0, 12), + Span(0, 12), + Span(12, 12), + ], + ..Chunk::default() + }; + let return_value = Some(Value::boolean(true)); + + assert_eq!(chunk, compile(source).unwrap()); + assert_eq!(return_value, run(source).unwrap()); +} + +#[test] +fn greater_equal_characters() { + let source = "'a' >= 'b'"; + let chunk = Chunk { + r#type: FunctionType::new([], [], Type::Boolean), + instructions: vec![ + Instruction::less( + false, + Operand::Constant(0, TypeCode::CHARACTER), + Operand::Constant(1, TypeCode::CHARACTER), + ), + Instruction::jump(1, true), + Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, true), + Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false), + Instruction::r#return(true, 0, TypeCode::BOOLEAN), + ], + positions: vec![ + Span(0, 10), + Span(0, 10), + Span(0, 10), + Span(0, 10), + Span(10, 10), + ], + constants: vec![ConcreteValue::Character('a'), ConcreteValue::Character('b')], + ..Chunk::default() + }; + let return_value = Some(Value::boolean(false)); + + assert_eq!(chunk, compile(source).unwrap()); + assert_eq!(return_value, run(source).unwrap()); +} + +#[test] +fn greater_equal_floats() { + let source = "10.0 >= 3.0"; + let chunk = Chunk { + r#type: FunctionType::new([], [], Type::Boolean), + instructions: vec![ + Instruction::less( + false, + Operand::Constant(0, TypeCode::FLOAT), + Operand::Constant(1, TypeCode::FLOAT), + ), + Instruction::jump(1, true), + Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, true), + Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false), + Instruction::r#return(true, 0, TypeCode::BOOLEAN), + ], + positions: vec![ + Span(0, 11), + Span(0, 11), + Span(0, 11), + Span(0, 11), + Span(11, 11), + ], + constants: vec![ConcreteValue::Float(10.0), ConcreteValue::Float(3.0)], + ..Chunk::default() + }; + let return_value = Some(Value::boolean(true)); + + assert_eq!(chunk, compile(source).unwrap()); + assert_eq!(return_value, run(source).unwrap()); +} + +#[test] +fn greater_equal_integers() { + let source = "10 >= 3"; + let chunk = Chunk { + r#type: FunctionType::new([], [], Type::Boolean), + instructions: vec![ + Instruction::less( + false, + Operand::Constant(0, TypeCode::INTEGER), + Operand::Constant(1, TypeCode::INTEGER), + ), + Instruction::jump(1, true), + Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, true), + Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false), + Instruction::r#return(true, 0, TypeCode::BOOLEAN), + ], + positions: vec![Span(0, 7), Span(0, 7), Span(0, 7), Span(0, 7), Span(7, 7)], + constants: vec![ConcreteValue::Integer(10), ConcreteValue::Integer(3)], + ..Chunk::default() + }; + let return_value = Some(Value::boolean(true)); + + assert_eq!(chunk, compile(source).unwrap()); + assert_eq!(return_value, run(source).unwrap()); +} + +#[test] +fn greater_equal_strings() { + let source = "\"abc\" >= \"def\""; + let chunk = Chunk { + r#type: FunctionType::new([], [], Type::Boolean), + instructions: vec![ + Instruction::less( + false, + Operand::Constant(0, TypeCode::STRING), + Operand::Constant(1, TypeCode::STRING), + ), + Instruction::jump(1, true), + Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, true), + Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false), + Instruction::r#return(true, 0, TypeCode::BOOLEAN), + ], + positions: vec![ + Span(0, 14), + Span(0, 14), + Span(0, 14), + Span(0, 14), + Span(14, 14), + ], + constants: vec![ + ConcreteValue::String(DustString::from("abc")), + ConcreteValue::String(DustString::from("def")), + ], + ..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/comparison/less.rs b/dust-lang/tests/comparison/less.rs index 9a04ed5..4d241be 100644 --- a/dust-lang/tests/comparison/less.rs +++ b/dust-lang/tests/comparison/less.rs @@ -3,6 +3,41 @@ use dust_lang::{ compile, instruction::TypeCode, run, }; +#[test] +fn less_booleans() { + let source = "true < false"; + let chunk = Chunk { + r#type: FunctionType::new([], [], Type::Boolean), + instructions: vec![ + Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false), + Instruction::load_encoded(1, false as u8, TypeCode::BOOLEAN, false), + Instruction::less( + true, + Operand::Register(0, TypeCode::BOOLEAN), + Operand::Register(1, TypeCode::BOOLEAN), + ), + Instruction::jump(1, true), + Instruction::load_encoded(2, true as u8, TypeCode::BOOLEAN, true), + Instruction::load_encoded(2, false as u8, TypeCode::BOOLEAN, false), + Instruction::r#return(true, 2, TypeCode::BOOLEAN), + ], + positions: vec![ + Span(0, 4), + Span(7, 12), + Span(0, 12), + Span(0, 12), + Span(0, 12), + Span(0, 12), + Span(12, 12), + ], + ..Chunk::default() + }; + let return_value = Some(Value::boolean(false)); + + assert_eq!(chunk, compile(source).unwrap()); + assert_eq!(return_value, run(source).unwrap()); +} + #[test] fn less_bytes() { let source = "0x0A < 0x03"; diff --git a/dust-lang/tests/comparison/less_equal.rs b/dust-lang/tests/comparison/less_equal.rs new file mode 100644 index 0000000..79d1a47 --- /dev/null +++ b/dust-lang/tests/comparison/less_equal.rs @@ -0,0 +1,199 @@ +use dust_lang::{ + Chunk, ConcreteValue, DustString, FunctionType, Instruction, Operand, Span, Type, Value, + compile, instruction::TypeCode, run, +}; + +#[test] +fn less_equal_booleans() { + let source = "true <= false"; + let chunk = Chunk { + r#type: FunctionType::new([], [], Type::Boolean), + instructions: vec![ + Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false), + Instruction::load_encoded(1, false as u8, TypeCode::BOOLEAN, false), + Instruction::less_equal( + true, + Operand::Register(0, TypeCode::BOOLEAN), + Operand::Register(1, TypeCode::BOOLEAN), + ), + Instruction::jump(1, true), + Instruction::load_encoded(2, true as u8, TypeCode::BOOLEAN, true), + Instruction::load_encoded(2, false as u8, TypeCode::BOOLEAN, false), + Instruction::r#return(true, 2, TypeCode::BOOLEAN), + ], + positions: vec![ + Span(0, 4), + Span(8, 13), + Span(0, 13), + Span(0, 13), + Span(0, 13), + Span(0, 13), + Span(13, 13), + ], + ..Chunk::default() + }; + let return_value = Some(Value::boolean(false)); + + assert_eq!(chunk, compile(source).unwrap()); + assert_eq!(return_value, run(source).unwrap()); +} + +#[test] +fn less_equal_bytes() { + let source = "0x0A <= 0x03"; + let chunk = Chunk { + r#type: FunctionType::new([], [], Type::Boolean), + instructions: vec![ + Instruction::load_encoded(0, 0x0A, TypeCode::BYTE, false), + Instruction::load_encoded(1, 0x03, TypeCode::BYTE, false), + Instruction::less_equal( + true, + Operand::Register(0, TypeCode::BYTE), + Operand::Register(1, TypeCode::BYTE), + ), + Instruction::jump(1, true), + Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, true), + Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false), + Instruction::r#return(true, 0, TypeCode::BOOLEAN), + ], + positions: vec![ + Span(0, 4), + Span(8, 12), + Span(0, 12), + Span(0, 12), + Span(0, 12), + Span(0, 12), + Span(12, 12), + ], + ..Chunk::default() + }; + let return_value = Some(Value::boolean(false)); + + assert_eq!(chunk, compile(source).unwrap()); + assert_eq!(return_value, run(source).unwrap()); +} + +#[test] +fn less_equal_characters() { + let source = "'a' <= 'b'"; + let chunk = Chunk { + r#type: FunctionType::new([], [], Type::Boolean), + instructions: vec![ + Instruction::less_equal( + true, + Operand::Constant(0, TypeCode::CHARACTER), + Operand::Constant(1, TypeCode::CHARACTER), + ), + Instruction::jump(1, true), + Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, true), + Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false), + Instruction::r#return(true, 0, TypeCode::BOOLEAN), + ], + positions: vec![ + Span(0, 10), + Span(0, 10), + Span(0, 10), + Span(0, 10), + Span(10, 10), + ], + constants: vec![ConcreteValue::Character('a'), ConcreteValue::Character('b')], + ..Chunk::default() + }; + let return_value = Some(Value::boolean(true)); + + assert_eq!(chunk, compile(source).unwrap()); + assert_eq!(return_value, run(source).unwrap()); +} + +#[test] +fn less_equal_floats() { + let source = "10.0 <= 3.0"; + let chunk = Chunk { + r#type: FunctionType::new([], [], Type::Boolean), + instructions: vec![ + Instruction::less_equal( + true, + Operand::Constant(0, TypeCode::FLOAT), + Operand::Constant(1, TypeCode::FLOAT), + ), + Instruction::jump(1, true), + Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, true), + Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false), + Instruction::r#return(true, 0, TypeCode::BOOLEAN), + ], + positions: vec![ + Span(0, 11), + Span(0, 11), + Span(0, 11), + Span(0, 11), + Span(11, 11), + ], + constants: vec![ConcreteValue::Float(10.0), ConcreteValue::Float(3.0)], + ..Chunk::default() + }; + let return_value = Some(Value::boolean(false)); + + assert_eq!(chunk, compile(source).unwrap()); + assert_eq!(return_value, run(source).unwrap()); +} + +#[test] +fn less_equal_integers() { + let source = "10 <= 3"; + let chunk = Chunk { + r#type: FunctionType::new([], [], Type::Boolean), + instructions: vec![ + Instruction::less_equal( + true, + Operand::Constant(0, TypeCode::INTEGER), + Operand::Constant(1, TypeCode::INTEGER), + ), + Instruction::jump(1, true), + Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, true), + Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false), + Instruction::r#return(true, 0, TypeCode::BOOLEAN), + ], + positions: vec![Span(0, 7), Span(0, 7), Span(0, 7), Span(0, 7), Span(7, 7)], + constants: vec![ConcreteValue::Integer(10), ConcreteValue::Integer(3)], + ..Chunk::default() + }; + let return_value = Some(Value::boolean(false)); + + assert_eq!(chunk, compile(source).unwrap()); + assert_eq!(return_value, run(source).unwrap()); +} + +#[test] +fn less_equal_strings() { + let source = "\"abc\" <= \"def\""; + let chunk = Chunk { + r#type: FunctionType::new([], [], Type::Boolean), + instructions: vec![ + Instruction::less_equal( + true, + Operand::Constant(0, TypeCode::STRING), + Operand::Constant(1, TypeCode::STRING), + ), + Instruction::jump(1, true), + Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, true), + Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false), + Instruction::r#return(true, 0, TypeCode::BOOLEAN), + ], + positions: vec![ + Span(0, 14), + Span(0, 14), + Span(0, 14), + Span(0, 14), + Span(14, 14), + ], + constants: vec![ + ConcreteValue::String(DustString::from("abc")), + ConcreteValue::String(DustString::from("def")), + ], + ..Chunk::default() + }; + let return_value = Some(Value::boolean(true)); + + assert_eq!(chunk, compile(source).unwrap()); + assert_eq!(return_value, run(source).unwrap()); +} diff --git a/dust-lang/tests/comparison/not_equal.rs b/dust-lang/tests/comparison/not_equal.rs new file mode 100644 index 0000000..d26c2bc --- /dev/null +++ b/dust-lang/tests/comparison/not_equal.rs @@ -0,0 +1,199 @@ +use dust_lang::{ + Chunk, ConcreteValue, DustString, FunctionType, Instruction, Operand, Span, Type, Value, + compile, instruction::TypeCode, run, +}; + +#[test] +fn not_equal_booleans() { + let source = "true != false"; + let chunk = Chunk { + r#type: FunctionType::new([], [], Type::Boolean), + instructions: vec![ + Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false), + Instruction::load_encoded(1, false as u8, TypeCode::BOOLEAN, false), + Instruction::equal( + false, + Operand::Register(0, TypeCode::BOOLEAN), + Operand::Register(1, TypeCode::BOOLEAN), + ), + Instruction::jump(1, true), + Instruction::load_encoded(2, true as u8, TypeCode::BOOLEAN, true), + Instruction::load_encoded(2, false as u8, TypeCode::BOOLEAN, false), + Instruction::r#return(true, 2, TypeCode::BOOLEAN), + ], + positions: vec![ + Span(0, 4), + Span(8, 13), + Span(0, 13), + Span(0, 13), + Span(0, 13), + Span(0, 13), + Span(13, 13), + ], + ..Chunk::default() + }; + let return_value = Some(Value::boolean(true)); + + assert_eq!(chunk, compile(source).unwrap()); + assert_eq!(return_value, run(source).unwrap()); +} + +#[test] +fn not_equal_bytes() { + let source = "0x0A != 0x03"; + let chunk = Chunk { + r#type: FunctionType::new([], [], Type::Boolean), + instructions: vec![ + Instruction::load_encoded(0, 0x0A, TypeCode::BYTE, false), + Instruction::load_encoded(1, 0x03, TypeCode::BYTE, false), + Instruction::equal( + false, + Operand::Register(0, TypeCode::BYTE), + Operand::Register(1, TypeCode::BYTE), + ), + Instruction::jump(1, true), + Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, true), + Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false), + Instruction::r#return(true, 0, TypeCode::BOOLEAN), + ], + positions: vec![ + Span(0, 4), + Span(8, 12), + Span(0, 12), + Span(0, 12), + Span(0, 12), + Span(0, 12), + Span(12, 12), + ], + ..Chunk::default() + }; + let return_value = Some(Value::boolean(true)); + + assert_eq!(chunk, compile(source).unwrap()); + assert_eq!(return_value, run(source).unwrap()); +} + +#[test] +fn not_equal_characters() { + let source = "'a' != 'b'"; + let chunk = Chunk { + r#type: FunctionType::new([], [], Type::Boolean), + instructions: vec![ + Instruction::equal( + false, + Operand::Constant(0, TypeCode::CHARACTER), + Operand::Constant(1, TypeCode::CHARACTER), + ), + Instruction::jump(1, true), + Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, true), + Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false), + Instruction::r#return(true, 0, TypeCode::BOOLEAN), + ], + positions: vec![ + Span(0, 10), + Span(0, 10), + Span(0, 10), + Span(0, 10), + Span(10, 10), + ], + constants: vec![ConcreteValue::Character('a'), ConcreteValue::Character('b')], + ..Chunk::default() + }; + let return_value = Some(Value::boolean(true)); + + assert_eq!(chunk, compile(source).unwrap()); + assert_eq!(return_value, run(source).unwrap()); +} + +#[test] +fn not_equal_floats() { + let source = "10.0 != 3.0"; + let chunk = Chunk { + r#type: FunctionType::new([], [], Type::Boolean), + instructions: vec![ + Instruction::equal( + false, + Operand::Constant(0, TypeCode::FLOAT), + Operand::Constant(1, TypeCode::FLOAT), + ), + Instruction::jump(1, true), + Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, true), + Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false), + Instruction::r#return(true, 0, TypeCode::BOOLEAN), + ], + positions: vec![ + Span(0, 11), + Span(0, 11), + Span(0, 11), + Span(0, 11), + Span(11, 11), + ], + constants: vec![ConcreteValue::Float(10.0), ConcreteValue::Float(3.0)], + ..Chunk::default() + }; + let return_value = Some(Value::boolean(true)); + + assert_eq!(chunk, compile(source).unwrap()); + assert_eq!(return_value, run(source).unwrap()); +} + +#[test] +fn not_equal_integers() { + let source = "10 != 3"; + let chunk = Chunk { + r#type: FunctionType::new([], [], Type::Boolean), + instructions: vec![ + Instruction::equal( + false, + Operand::Constant(0, TypeCode::INTEGER), + Operand::Constant(1, TypeCode::INTEGER), + ), + Instruction::jump(1, true), + Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, true), + Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false), + Instruction::r#return(true, 0, TypeCode::BOOLEAN), + ], + positions: vec![Span(0, 7), Span(0, 7), Span(0, 7), Span(0, 7), Span(7, 7)], + constants: vec![ConcreteValue::Integer(10), ConcreteValue::Integer(3)], + ..Chunk::default() + }; + let return_value = Some(Value::boolean(true)); + + assert_eq!(chunk, compile(source).unwrap()); + assert_eq!(return_value, run(source).unwrap()); +} + +#[test] +fn not_equal_strings() { + let source = "\"abc\" != \"def\""; + let chunk = Chunk { + r#type: FunctionType::new([], [], Type::Boolean), + instructions: vec![ + Instruction::equal( + false, + Operand::Constant(0, TypeCode::STRING), + Operand::Constant(1, TypeCode::STRING), + ), + Instruction::jump(1, true), + Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, true), + Instruction::load_encoded(0, false as u8, TypeCode::BOOLEAN, false), + Instruction::r#return(true, 0, TypeCode::BOOLEAN), + ], + positions: vec![ + Span(0, 14), + Span(0, 14), + Span(0, 14), + Span(0, 14), + Span(14, 14), + ], + constants: vec![ + ConcreteValue::String(DustString::from("abc")), + ConcreteValue::String(DustString::from("def")), + ], + ..Chunk::default() + }; + let return_value = Some(Value::boolean(true)); + + assert_eq!(chunk, compile(source).unwrap()); + assert_eq!(return_value, run(source).unwrap()); +}