diff --git a/dust-lang/Cargo.toml b/dust-lang/Cargo.toml index 67d787a..52f25cf 100644 --- a/dust-lang/Cargo.toml +++ b/dust-lang/Cargo.toml @@ -50,3 +50,7 @@ path = "tests/math/subtract.rs" [[test]] name = "multiply" path = "tests/math/multiply.rs" + +[[test]] +name = "divide" +path = "tests/math/divide.rs" diff --git a/dust-lang/src/instruction/mod.rs b/dust-lang/src/instruction/mod.rs index 2e2538d..763bfeb 100644 --- a/dust-lang/src/instruction/mod.rs +++ b/dust-lang/src/instruction/mod.rs @@ -353,6 +353,20 @@ impl Instruction { Operand::Register(destination, left.as_type()) } + Operation::DIVIDE => { + let Divide { + destination, left, .. + } = Divide::from(*self); + + Operand::Register(destination, left.as_type()) + } + Operation::MODULO => { + let Modulo { + destination, left, .. + } = Modulo::from(*self); + + Operand::Register(destination, left.as_type()) + } unsupported => todo!("Support {unsupported}"), } } diff --git a/dust-lang/src/vm/action.rs b/dust-lang/src/vm/action.rs index c166b09..582235e 100644 --- a/dust-lang/src/vm/action.rs +++ b/dust-lang/src/vm/action.rs @@ -765,8 +765,91 @@ pub fn multiply(instruction: InstructionFields, thread: &mut Thread) { } } -pub fn divide(instruction: InstructionFields, thread: &mut Thread) {} +pub fn divide(instruction: InstructionFields, thread: &mut Thread) { + let destination = instruction.a_field as usize; + 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::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.saturating_div(*right_value); + let register = Register::Value(result); + + thread.set_byte_register(destination, register); + } + (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; + let register = Register::Value(result); + + thread.set_float_register(destination, register); + } + (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.saturating_div(*right_value); + let register = Register::Value(result); + + thread.set_integer_register(destination, register); + } + _ => unreachable!(), + } +} pub fn modulo(instruction: InstructionFields, thread: &mut Thread) {} pub fn test(instruction: InstructionFields, thread: &mut Thread) {} diff --git a/dust-lang/tests/math/divide.rs b/dust-lang/tests/math/divide.rs new file mode 100644 index 0000000..6080ec9 --- /dev/null +++ b/dust-lang/tests/math/divide.rs @@ -0,0 +1,171 @@ +use dust_lang::{ + Chunk, ConcreteValue, FunctionType, Instruction, Operand, Span, Type, Value, compile, + instruction::TypeCode, run, +}; + +#[test] +fn divide_bytes() { + let source = "0x0A / 0x02"; + let chunk = Chunk { + r#type: FunctionType::new([], [], Type::Byte), + instructions: vec![ + Instruction::load_encoded(0, 10, TypeCode::BYTE, false), + Instruction::load_encoded(1, 2, TypeCode::BYTE, false), + Instruction::divide( + 2, + Operand::Register(0, TypeCode::BYTE), + Operand::Register(1, TypeCode::BYTE), + ), + Instruction::r#return(true, 2, TypeCode::BYTE), + ], + positions: vec![Span(0, 4), Span(7, 11), Span(0, 11), Span(11, 11)], + ..Chunk::default() + }; + let return_value = Some(Value::byte(0x05)); + + assert_eq!(chunk, compile(source).unwrap()); + assert_eq!(return_value, run(source).unwrap()); +} + +#[test] +fn divide_many_bytes() { + let source = "0x0A / 0x02 / 0x02"; + let chunk = Chunk { + r#type: FunctionType::new([], [], Type::Byte), + instructions: vec![ + Instruction::load_encoded(0, 10, TypeCode::BYTE, false), + Instruction::load_encoded(1, 2, TypeCode::BYTE, false), + Instruction::divide( + 2, + Operand::Register(0, TypeCode::BYTE), + Operand::Register(1, TypeCode::BYTE), + ), + Instruction::load_encoded(3, 2, TypeCode::BYTE, false), + Instruction::divide( + 4, + Operand::Register(2, TypeCode::BYTE), + Operand::Register(3, TypeCode::BYTE), + ), + Instruction::r#return(true, 4, TypeCode::BYTE), + ], + positions: vec![ + Span(0, 4), + Span(7, 11), + Span(0, 11), + Span(14, 18), + Span(0, 18), + Span(18, 18), + ], + ..Chunk::default() + }; + let return_value = Some(Value::byte(0x02)); + + assert_eq!(chunk, compile(source).unwrap()); + assert_eq!(return_value, run(source).unwrap()); +} + +#[test] +fn divide_floats() { + let source = "1.0 / 0.25"; + let chunk = Chunk { + r#type: FunctionType::new([], [], Type::Float), + instructions: vec![ + Instruction::divide( + 0, + Operand::Constant(0, TypeCode::FLOAT), + Operand::Constant(1, TypeCode::FLOAT), + ), + Instruction::r#return(true, 0, TypeCode::FLOAT), + ], + positions: vec![Span(0, 10), Span(10, 10)], + constants: vec![ConcreteValue::Float(1.0), ConcreteValue::Float(0.25)], + ..Chunk::default() + }; + let return_value = Some(Value::float(4.0)); + + assert_eq!(chunk, compile(source).unwrap()); + assert_eq!(return_value, run(source).unwrap()); +} + +#[test] +fn divide_many_floats() { + let source = "1.0 / 0.25 / 0.5"; + let chunk = Chunk { + r#type: FunctionType::new([], [], Type::Float), + instructions: vec![ + Instruction::divide( + 0, + Operand::Constant(0, TypeCode::FLOAT), + Operand::Constant(1, TypeCode::FLOAT), + ), + Instruction::divide( + 1, + Operand::Register(0, TypeCode::FLOAT), + Operand::Constant(2, TypeCode::FLOAT), + ), + Instruction::r#return(true, 1, TypeCode::FLOAT), + ], + positions: vec![Span(0, 10), Span(0, 16), Span(16, 16)], + constants: vec![ + ConcreteValue::Float(1.0), + ConcreteValue::Float(0.25), + ConcreteValue::Float(0.5), + ], + ..Chunk::default() + }; + let return_value = Some(Value::float(8.0)); + + assert_eq!(chunk, compile(source).unwrap()); + assert_eq!(return_value, run(source).unwrap()); +} + +#[test] +fn divide_integers() { + let source = "10 / 2"; + let chunk = Chunk { + r#type: FunctionType::new([], [], Type::Integer), + instructions: vec![ + Instruction::divide( + 0, + Operand::Constant(0, TypeCode::INTEGER), + Operand::Constant(1, TypeCode::INTEGER), + ), + Instruction::r#return(true, 0, TypeCode::INTEGER), + ], + positions: vec![Span(0, 6), Span(6, 6)], + constants: vec![ConcreteValue::Integer(10), ConcreteValue::Integer(2)], + ..Chunk::default() + }; + let return_value = Some(Value::integer(5)); + + assert_eq!(chunk, compile(source).unwrap()); + assert_eq!(return_value, run(source).unwrap()); +} + +#[test] +fn divide_many_integers() { + let source = "10 / 2 / 2"; + let chunk = Chunk { + r#type: FunctionType::new([], [], Type::Integer), + instructions: vec![ + Instruction::divide( + 0, + Operand::Constant(0, TypeCode::INTEGER), + Operand::Constant(1, TypeCode::INTEGER), + ), + Instruction::divide( + 1, + Operand::Register(0, TypeCode::INTEGER), + Operand::Constant(1, TypeCode::INTEGER), + ), + Instruction::r#return(true, 1, TypeCode::INTEGER), + ], + positions: vec![Span(0, 6), Span(0, 10), Span(10, 10)], + constants: vec![ConcreteValue::Integer(10), ConcreteValue::Integer(2)], + ..Chunk::default() + }; + let return_value = Some(Value::integer(2)); + + assert_eq!(chunk, compile(source).unwrap()); + assert_eq!(return_value, run(source).unwrap()); +}