Add comparison tests for less than operator; Implement less than operator in VM
This commit is contained in:
parent
dfa5f6858c
commit
f2ee01d66f
@ -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"
|
||||
|
@ -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(())
|
||||
}
|
||||
|
@ -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),
|
||||
|
@ -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;
|
||||
|
||||
|
158
dust-lang/tests/comparison/less.rs
Normal file
158
dust-lang/tests/comparison/less.rs
Normal file
@ -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());
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user