diff --git a/dust-lang/Cargo.toml b/dust-lang/Cargo.toml index c6993bb..b286211 100644 --- a/dust-lang/Cargo.toml +++ b/dust-lang/Cargo.toml @@ -42,3 +42,7 @@ harness = false [[test]] name = "add" path = "tests/math/add.rs" + +[[test]] +name = "subtract" +path = "tests/math/subtract.rs" diff --git a/dust-lang/src/compiler/mod.rs b/dust-lang/src/compiler/mod.rs index eeacaf4..309dc40 100644 --- a/dust-lang/src/compiler/mod.rs +++ b/dust-lang/src/compiler/mod.rs @@ -924,8 +924,9 @@ impl<'src> Compiler<'src> { }); } }; + let position = Span(left_position.0, right_position.1); - self.emit_instruction(instruction, r#type, operator_position); + self.emit_instruction(instruction, r#type, position); Ok(()) } diff --git a/dust-lang/src/vm/action.rs b/dust-lang/src/vm/action.rs index e390c9a..ddc7d49 100644 --- a/dust-lang/src/vm/action.rs +++ b/dust-lang/src/vm/action.rs @@ -441,6 +441,30 @@ pub fn add(instruction: InstructionFields, thread: &mut Thread) { 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 sum = left_value + right_value; + let register = Register::Value(sum); + + thread.set_float_register(destination, register); + } (TypeCode::STRING, TypeCode::STRING) => { let left_value = if left_is_constant { if cfg!(debug_assertions) { @@ -505,29 +529,65 @@ pub fn add(instruction: InstructionFields, thread: &mut Thread) { thread.set_string_register(destination, register); } - (TypeCode::FLOAT, TypeCode::FLOAT) => { + (TypeCode::STRING, TypeCode::CHARACTER) => { let left_value = if left_is_constant { if cfg!(debug_assertions) { - thread.get_constant(left).as_float().unwrap() + thread.get_constant(left).as_string().unwrap().clone() } else { - unsafe { thread.get_constant(left).as_float().unwrap_unchecked() } + unsafe { + thread + .get_constant(left) + .as_string() + .unwrap_unchecked() + .clone() + } } } else { - thread.get_float_register(left) + thread.get_string_register(left).clone() }; let right_value = if right_is_constant { if cfg!(debug_assertions) { - thread.get_constant(right).as_float().unwrap() + thread.get_constant(right).as_character().unwrap() } else { - unsafe { thread.get_constant(right).as_float().unwrap_unchecked() } + unsafe { thread.get_constant(right).as_character().unwrap_unchecked() } } } else { - thread.get_float_register(right) + thread.get_character_register(right) }; - let sum = left_value + right_value; + let mut sum = left_value.clone(); + + sum.push(*right_value); + let register = Register::Value(sum); - thread.set_float_register(destination, register); + thread.set_string_register(destination, register); + } + (TypeCode::CHARACTER, TypeCode::STRING) => { + 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_string().unwrap() + } else { + unsafe { thread.get_constant(right).as_string().unwrap_unchecked() } + } + } else { + thread.get_string_register(right) + }; + let mut sum = right_value.clone(); + + sum.insert(0, *left_value); + + let register = Register::Value(sum); + + thread.set_string_register(destination, register); } _ => unimplemented!(), } diff --git a/dust-lang/tests/math/add.rs b/dust-lang/tests/math/add.rs index 8689b6b..6f27dd8 100644 --- a/dust-lang/tests/math/add.rs +++ b/dust-lang/tests/math/add.rs @@ -18,7 +18,7 @@ fn add_bytes() { ), Instruction::r#return(true, 2, TypeCode::BYTE), ], - positions: vec![Span(0, 4), Span(7, 11), Span(5, 6), Span(11, 11)], + positions: vec![Span(0, 4), Span(7, 11), Span(0, 11), Span(11, 11)], ..Chunk::default() }; let return_value = Some(Value::byte(0x2A)); @@ -57,11 +57,11 @@ fn add_many_bytes() { positions: vec![ Span(0, 4), Span(7, 11), - Span(5, 6), + Span(0, 11), Span(14, 18), - Span(12, 13), + Span(0, 18), Span(21, 25), - Span(19, 20), + Span(0, 25), Span(25, 25), ], ..Chunk::default() @@ -85,7 +85,7 @@ fn add_characters() { ), Instruction::r#return(true, 0, TypeCode::STRING), ], - positions: vec![Span(4, 5), Span(9, 9)], + positions: vec![Span(0, 9), Span(9, 9)], constants: vec![ConcreteValue::Character('a'), ConcreteValue::Character('b')], ..Chunk::default() }; @@ -95,6 +95,44 @@ fn add_characters() { assert_eq!(return_value, run(source).unwrap()); } +#[test] +fn add_many_characters() { + let source = "'a' + 'b' + 'c' + 'd'"; + let chunk = Chunk { + r#type: FunctionType::new([], [], Type::String), + instructions: vec![ + Instruction::add( + 0, + Operand::Constant(0, TypeCode::CHARACTER), + Operand::Constant(1, TypeCode::CHARACTER), + ), + Instruction::add( + 1, + Operand::Register(0, TypeCode::STRING), + Operand::Constant(2, TypeCode::CHARACTER), + ), + Instruction::add( + 2, + Operand::Register(1, TypeCode::STRING), + Operand::Constant(3, TypeCode::CHARACTER), + ), + Instruction::r#return(true, 2, TypeCode::STRING), + ], + positions: vec![Span(0, 9), Span(0, 15), Span(0, 21), Span(21, 21)], + constants: vec![ + ConcreteValue::Character('a'), + ConcreteValue::Character('b'), + ConcreteValue::Character('c'), + ConcreteValue::Character('d'), + ], + ..Chunk::default() + }; + let return_value = Some(Value::string("abcd")); + + assert_eq!(chunk, compile(source).unwrap()); + assert_eq!(return_value, run(source).unwrap()); +} + #[test] fn add_floats() { let source = "2.40 + 40.02"; @@ -108,7 +146,7 @@ fn add_floats() { ), Instruction::r#return(true, 0, TypeCode::FLOAT), ], - positions: vec![Span(5, 6), Span(12, 12)], + positions: vec![Span(0, 12), Span(12, 12)], constants: vec![ConcreteValue::Float(2.40), ConcreteValue::Float(40.02)], ..Chunk::default() }; @@ -118,6 +156,39 @@ fn add_floats() { assert_eq!(return_value, run(source).unwrap()); } +#[test] +fn add_many_floats() { + let source = "2.40 + 40.02 + 2.40 + 40.02"; + let chunk = Chunk { + r#type: FunctionType::new([], [], Type::Float), + instructions: vec![ + Instruction::add( + 0, + Operand::Constant(0, TypeCode::FLOAT), + Operand::Constant(1, TypeCode::FLOAT), + ), + Instruction::add( + 1, + Operand::Register(0, TypeCode::FLOAT), + Operand::Constant(0, TypeCode::FLOAT), + ), + Instruction::add( + 2, + Operand::Register(1, TypeCode::FLOAT), + Operand::Constant(1, TypeCode::FLOAT), + ), + Instruction::r#return(true, 2, TypeCode::FLOAT), + ], + positions: vec![Span(0, 12), Span(0, 19), Span(0, 27), Span(27, 27)], + constants: vec![ConcreteValue::Float(2.40), ConcreteValue::Float(40.02)], + ..Chunk::default() + }; + let return_value = Some(Value::float(84.84)); + + assert_eq!(chunk, compile(source).unwrap()); + assert_eq!(return_value, run(source).unwrap()); +} + #[test] fn add_integers() { let source = "40 + 2"; @@ -131,7 +202,7 @@ fn add_integers() { ), Instruction::r#return(true, 0, TypeCode::INTEGER), ], - positions: vec![Span(3, 4), Span(6, 6)], + positions: vec![Span(0, 6), Span(6, 6)], constants: vec![ConcreteValue::Integer(40), ConcreteValue::Integer(2)], ..Chunk::default() }; @@ -141,6 +212,39 @@ fn add_integers() { assert_eq!(return_value, run(source).unwrap()); } +#[test] +fn add_many_integers() { + let source = "40 + 2 + 40 + 2"; + let chunk = Chunk { + r#type: FunctionType::new([], [], Type::Integer), + instructions: vec![ + Instruction::add( + 0, + Operand::Constant(0, TypeCode::INTEGER), + Operand::Constant(1, TypeCode::INTEGER), + ), + Instruction::add( + 1, + Operand::Register(0, TypeCode::INTEGER), + Operand::Constant(0, TypeCode::INTEGER), + ), + Instruction::add( + 2, + Operand::Register(1, TypeCode::INTEGER), + Operand::Constant(1, TypeCode::INTEGER), + ), + Instruction::r#return(true, 2, TypeCode::INTEGER), + ], + positions: vec![Span(0, 6), Span(0, 11), Span(0, 15), Span(15, 15)], + constants: vec![ConcreteValue::Integer(40), ConcreteValue::Integer(2)], + ..Chunk::default() + }; + let return_value = Some(Value::integer(84)); + + assert_eq!(chunk, compile(source).unwrap()); + assert_eq!(return_value, run(source).unwrap()); +} + #[test] fn add_strings() { let source = "\"Hello, \" + \"World!\""; @@ -154,7 +258,7 @@ fn add_strings() { ), Instruction::r#return(true, 0, TypeCode::STRING), ], - positions: vec![Span(10, 11), Span(20, 20)], + positions: vec![Span(0, 20), Span(20, 20)], constants: vec![ ConcreteValue::String(DustString::from("Hello, ")), ConcreteValue::String(DustString::from("World!")), @@ -166,3 +270,41 @@ fn add_strings() { assert_eq!(chunk, compile(source).unwrap()); assert_eq!(return_value, run(source).unwrap()); } + +#[test] +fn add_many_strings() { + let source = "\"foo\" + \"bar\" + \"baz\" + \"buzz\""; + let chunk = Chunk { + r#type: FunctionType::new([], [], Type::String), + instructions: vec![ + Instruction::add( + 0, + Operand::Constant(0, TypeCode::STRING), + Operand::Constant(1, TypeCode::STRING), + ), + Instruction::add( + 1, + Operand::Register(0, TypeCode::STRING), + Operand::Constant(2, TypeCode::STRING), + ), + Instruction::add( + 2, + Operand::Register(1, TypeCode::STRING), + Operand::Constant(3, TypeCode::STRING), + ), + Instruction::r#return(true, 2, TypeCode::STRING), + ], + positions: vec![Span(0, 13), Span(0, 21), Span(0, 30), Span(30, 30)], + constants: vec![ + ConcreteValue::String(DustString::from("foo")), + ConcreteValue::String(DustString::from("bar")), + ConcreteValue::String(DustString::from("baz")), + ConcreteValue::String(DustString::from("buzz")), + ], + ..Chunk::default() + }; + let return_value = Some(Value::string("foobarbazbuzz")); + + assert_eq!(chunk, compile(source).unwrap()); + assert_eq!(return_value, run(source).unwrap()); +} diff --git a/dust-lang/tests/math/subtract.rs b/dust-lang/tests/math/subtract.rs new file mode 100644 index 0000000..e69de29