From f2ee01d66f078b934e4917a12a577886efd2f5a7 Mon Sep 17 00:00:00 2001 From: Jeff Date: Tue, 11 Feb 2025 02:22:14 -0500 Subject: [PATCH] Add comparison tests for less than operator; Implement less than operator in VM --- dust-lang/Cargo.toml | 4 + dust-lang/src/compiler/mod.rs | 9 +- dust-lang/src/instruction/mod.rs | 2 + dust-lang/src/vm/action.rs | 101 ++++++++++++++++-- dust-lang/tests/comparison/less.rs | 158 +++++++++++++++++++++++++++++ 5 files changed, 263 insertions(+), 11 deletions(-) create mode 100644 dust-lang/tests/comparison/less.rs diff --git a/dust-lang/Cargo.toml b/dust-lang/Cargo.toml index 91d6934..39edbc5 100644 --- a/dust-lang/Cargo.toml +++ b/dust-lang/Cargo.toml @@ -58,3 +58,7 @@ path = "tests/math/divide.rs" [[test]] name = "modulo" path = "tests/math/modulo.rs" + +[[test]] +name = "less" +path = "tests/comparison/less.rs" diff --git a/dust-lang/src/compiler/mod.rs b/dust-lang/src/compiler/mod.rs index ab19e67..fe2f3a2 100644 --- a/dust-lang/src/compiler/mod.rs +++ b/dust-lang/src/compiler/mod.rs @@ -1010,11 +1010,12 @@ impl<'src> Compiler<'src> { let load_true = Instruction::load_encoded(destination, true as u8, TypeCode::BOOLEAN, true); let load_false = Instruction::load_encoded(destination, false as u8, TypeCode::BOOLEAN, false); + let comparison_position = Span(left_position.0, right_position.1); - self.emit_instruction(comparison, Type::Boolean, operator_position); - self.emit_instruction(jump, Type::None, operator_position); - self.emit_instruction(load_true, Type::Boolean, operator_position); - self.emit_instruction(load_false, Type::Boolean, operator_position); + self.emit_instruction(comparison, Type::Boolean, comparison_position); + self.emit_instruction(jump, Type::None, comparison_position); + self.emit_instruction(load_true, Type::Boolean, comparison_position); + self.emit_instruction(load_false, Type::Boolean, comparison_position); Ok(()) } diff --git a/dust-lang/src/instruction/mod.rs b/dust-lang/src/instruction/mod.rs index 2fc30ec..a675fc4 100644 --- a/dust-lang/src/instruction/mod.rs +++ b/dust-lang/src/instruction/mod.rs @@ -722,6 +722,8 @@ impl Display for Operand { fn fmt(&self, f: &mut Formatter) -> fmt::Result { match self { Operand::Constant(index, r#type) => match *r#type { + TypeCode::BOOLEAN => write!(f, "C_{}", index), + TypeCode::BYTE => write!(f, "C_{}", index), TypeCode::CHARACTER => write!(f, "C_{}", index), TypeCode::INTEGER => write!(f, "C_{}", index), TypeCode::FLOAT => write!(f, "C_{}", index), diff --git a/dust-lang/src/vm/action.rs b/dust-lang/src/vm/action.rs index 6f8fbbb..c7956b0 100644 --- a/dust-lang/src/vm/action.rs +++ b/dust-lang/src/vm/action.rs @@ -961,6 +961,81 @@ pub fn less(instruction: InstructionFields, thread: &mut Thread) { let right_is_constant = instruction.c_is_constant; match (left_type, right_type) { + (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) { @@ -986,24 +1061,36 @@ pub fn less(instruction: InstructionFields, thread: &mut Thread) { thread.current_frame_mut().ip += 1; } } - (TypeCode::BYTE, TypeCode::BYTE) => { + (TypeCode::STRING, TypeCode::STRING) => { let left_value = if left_is_constant { if cfg!(debug_assertions) { - thread.get_constant(left).as_byte().unwrap() + thread.get_constant(left).as_string().unwrap().clone() } else { - unsafe { thread.get_constant(left).as_byte().unwrap_unchecked() } + unsafe { + thread + .get_constant(left) + .as_string() + .unwrap_unchecked() + .clone() + } } } else { - thread.get_byte_register(left) + thread.get_string_register(left).clone() }; let right_value = if right_is_constant { if cfg!(debug_assertions) { - thread.get_constant(right).as_byte().unwrap() + thread.get_constant(right).as_string().unwrap().clone() } else { - unsafe { thread.get_constant(right).as_byte().unwrap_unchecked() } + unsafe { + thread + .get_constant(right) + .as_string() + .unwrap_unchecked() + .clone() + } } } else { - thread.get_byte_register(right) + thread.get_string_register(right).clone() }; let result = left_value < right_value; diff --git a/dust-lang/tests/comparison/less.rs b/dust-lang/tests/comparison/less.rs new file mode 100644 index 0000000..9a04ed5 --- /dev/null +++ b/dust-lang/tests/comparison/less.rs @@ -0,0 +1,158 @@ +use dust_lang::{ + Chunk, ConcreteValue, DustString, FunctionType, Instruction, Operand, Span, Type, Value, + compile, instruction::TypeCode, run, +}; + +#[test] +fn less_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( + 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(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(false)); + + assert_eq!(chunk, compile(source).unwrap()); + assert_eq!(return_value, run(source).unwrap()); +} + +#[test] +fn less_characters() { + let source = "'a' < 'b'"; + let chunk = Chunk { + r#type: FunctionType::new([], [], Type::Boolean), + instructions: vec![ + Instruction::less( + 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, 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(true)); + + assert_eq!(chunk, compile(source).unwrap()); + assert_eq!(return_value, run(source).unwrap()); +} + +#[test] +fn less_floats() { + let source = "10.0 < 3.0"; + let chunk = Chunk { + r#type: FunctionType::new([], [], Type::Boolean), + instructions: vec![ + Instruction::less( + 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, 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(false)); + + assert_eq!(chunk, compile(source).unwrap()); + assert_eq!(return_value, run(source).unwrap()); +} + +#[test] +fn less_integers() { + let source = "10 < 3"; + let chunk = Chunk { + r#type: FunctionType::new([], [], Type::Boolean), + instructions: vec![ + Instruction::less( + 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, 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(false)); + + assert_eq!(chunk, compile(source).unwrap()); + assert_eq!(return_value, run(source).unwrap()); +} + +#[test] +fn less_strings() { + let source = "\"abc\" < \"def\""; + let chunk = Chunk { + r#type: FunctionType::new([], [], Type::Boolean), + instructions: vec![ + Instruction::less( + 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, 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(true)); + + assert_eq!(chunk, compile(source).unwrap()); + assert_eq!(return_value, run(source).unwrap()); +}