1
0

Add comparison tests for lists; Edit README.md; Add benches to Cargo.toml

This commit is contained in:
Jeff 2025-02-17 15:09:43 -05:00
parent 4c019410e8
commit a53e0018cf
14 changed files with 382 additions and 61 deletions

View File

@ -29,6 +29,10 @@ fn fib (n: int) -> int {
write_line(fib(25))
```
> [!IMPORTANT]
> Dust is still experimental. Currently, development is more focused on exploring ideas for
> optimization and performance than on stability or feature completeness. This will change as the
> project matures.
## Highlights

View File

@ -31,6 +31,18 @@ criterion = { version = "0.5.1", features = ["html_reports"] }
name = "addictive_addition"
harness = false
[[bench]]
name = "addictive_subtraction"
harness = false
[[bench]]
name = "addictive_multiplication"
harness = false
[[bench]]
name = "addictive_division"
harness = false
[[bench]]
name = "fibonacci"
harness = false

View File

@ -1,6 +1,6 @@
use std::time::Duration;
use criterion::{Criterion, black_box, criterion_group, criterion_main};
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use dust_lang::run;
const SOURCE: &str = r#"
@ -9,9 +9,13 @@ const SOURCE: &str = r#"
while i < 1_000 {
i += 1
spawn(
fn () { random_int(0, 10); }
)
spawn(fn () {
let mut j = 0
while j < 5_000_000 {
j += 1
}
})
}
"#;

View File

@ -855,6 +855,7 @@ impl<'src> Compiler<'src> {
let operand = instruction.as_operand();
let push_back = match instruction.operation() {
Operation::LOAD_ENCODED
| Operation::LOAD_LIST
| Operation::LOAD_SELF
| Operation::ADD
| Operation::SUBTRACT
@ -997,6 +998,22 @@ impl<'src> Compiler<'src> {
position: self.current_position,
});
}
let (left_instruction, left_type, left_position) =
self.instructions
.pop()
.ok_or_else(|| CompileError::ExpectedExpression {
found: self.previous_token.to_owned(),
position: self.previous_position,
})?;
// TODO: Check if the left type is a valid type for comparison
let (left, push_back_left) = self.handle_binary_argument(&left_instruction);
if push_back_left {
self.instructions
.push((left_instruction, left_type, left_position));
}
let operator = self.current_token;
let operator_position = self.current_position;
@ -1012,30 +1029,18 @@ impl<'src> Compiler<'src> {
found: self.previous_token.to_owned(),
position: self.previous_position,
})?;
let (left_instruction, left_type, left_position) =
self.instructions
.pop()
.ok_or_else(|| CompileError::ExpectedExpression {
found: self.previous_token.to_owned(),
position: self.previous_position,
})?;
let (left, push_back_left) = self.handle_binary_argument(&left_instruction);
let (right, push_back_right) = self.handle_binary_argument(&right_instruction);
// TODO: Check if the left type is a valid type for comparison
// TODO: Check if the right type is a valid type for comparison
// TODO: Check if the left and right types are compatible
if push_back_left {
self.instructions
.push((left_instruction, left_type, left_position));
}
let (right, push_back_right) = self.handle_binary_argument(&right_instruction);
if push_back_right {
self.instructions
.push((right_instruction, right_type, right_position));
}
// TODO: Check if the left and right types are compatible
let comparison = match operator {
Token::DoubleEqual => Instruction::equal(true, left, right),
Token::BangEqual => Instruction::equal(false, left, right),
@ -1074,9 +1079,6 @@ impl<'src> Compiler<'src> {
}
fn parse_logical_binary(&mut self) -> Result<(), CompileError> {
let operator = self.current_token;
let operator_position = self.current_position;
let rule = ParseRule::from(&operator);
let (left_instruction, left_type, left_position) =
self.instructions
.pop()
@ -1084,42 +1086,19 @@ impl<'src> Compiler<'src> {
found: self.previous_token.to_owned(),
position: self.previous_position,
})?;
let operand_register = if left_instruction.operation() == Operation::MOVE {
let Move { operand: to, .. } = Move::from(&left_instruction);
let local = self.get_local(to.index())?;
local.register_index
} else if left_instruction.yields_value() {
let register = left_instruction.a_field();
self.instructions
.push((left_instruction, left_type, left_position));
register
} else {
return Err(CompileError::ExpectedExpression {
found: self.previous_token.to_owned(),
position: self.previous_position,
});
};
// TODO: Check if the left type is boolean
self.advance()?;
self.parse_sub_expression(&rule.precedence)?;
let (left, push_back_left) = self.handle_binary_argument(&left_instruction);
let (mut right_instruction, right_type, right_position) = self
.instructions
.pop()
.ok_or_else(|| CompileError::ExpectedExpression {
found: self.previous_token.to_owned(),
position: self.previous_position,
})?;
// TODO: Check if the right type is boolean
right_instruction.set_a_field(operand_register);
if push_back_left {
self.instructions
.push((left_instruction, left_type.clone(), left_position));
}
let operator = self.current_token;
let operator_position = self.current_position;
let rule = ParseRule::from(&operator);
let test_boolean = match operator {
Token::DoubleAmpersand => true,
Token::DoublePipe => false,
@ -1131,13 +1110,35 @@ impl<'src> Compiler<'src> {
});
}
};
let test = Instruction::test(operand_register, test_boolean);
let test = Instruction::test(left.index(), test_boolean);
let jump = Instruction::jump(1, true);
self.emit_instruction(test, Type::None, operator_position);
self.emit_instruction(jump, Type::None, operator_position);
self.instructions
.push((right_instruction, right_type, right_position));
self.advance()?;
self.parse_sub_expression(&rule.precedence)?;
// TODO: Check if the right type is boolean
if matches!(
self.get_last_operations(),
Some([
Operation::EQUAL | Operation::LESS | Operation::LESS_EQUAL,
Operation::JUMP,
Operation::LOAD_ENCODED | Operation::LOAD_CONSTANT,
Operation::LOAD_ENCODED | Operation::LOAD_CONSTANT,
])
) {
let instruction_count = self.instructions.len();
let loaders = self
.instructions
.get_many_mut([instruction_count - 1, instruction_count - 2])
.unwrap(); // Safe because the indices in bounds and do not overlap
loaders[0].0.set_a_field(left.index());
loaders[1].0.set_a_field(left.index());
}
let instructions_length = self.instructions.len();

View File

@ -660,7 +660,7 @@ impl Instruction {
Operation::EQUAL => Equal::from(*self).to_string(),
Operation::LESS => Less::from(*self).to_string(),
Operation::LESS_EQUAL => LessEqual::from(*self).to_string(),
Operation::TEST => Test::from(*self).to_string(),
Operation::TEST => Test::from(self).to_string(),
Operation::TEST_SET => TestSet::from(*self).to_string(),
Operation::CALL => Call::from(*self).to_string(),
Operation::CALL_NATIVE => CallNative::from(*self).to_string(),

View File

@ -11,7 +11,7 @@ pub struct Operation(pub u8);
impl Operation {
pub const NO_OP: Operation = Operation(0);
// Stack manipulation
// Register manipulation
pub const MOVE: Operation = Operation(1);
pub const CLOSE: Operation = Operation(2);

View File

@ -9,8 +9,8 @@ pub struct Test {
pub test_value: bool,
}
impl From<Instruction> for Test {
fn from(instruction: Instruction) -> Self {
impl From<&Instruction> for Test {
fn from(instruction: &Instruction) -> Self {
let operand_register = instruction.b_field();
let test_value = instruction.c_field() != 0;

View File

@ -51,7 +51,7 @@ impl Thread {
let instruction = &instructions[ip];
info!("Run instruction {}", instruction.operation());
info!("IP = {ip} Run {}", instruction.operation());
match instruction.operation() {
Operation::MOVE => {
@ -1334,7 +1334,19 @@ impl Thread {
}
_ => unreachable!("Invalid LESS_EQUAL instruction"),
},
Operation::TEST => {
let operand_register_index = instruction.b_field() as usize;
let test_value = instruction.c_field() != 0;
let operand_boolean = current_frame
.registers
.booleans
.get(operand_register_index)
.copy_value();
if operand_boolean == test_value {
current_frame.ip += 1;
}
}
Operation::JUMP => {
let offset = instruction.b_field() as usize;
let is_positive = instruction.c_field() != 0;

View File

@ -159,3 +159,51 @@ fn equal_strings() {
assert_eq!(chunk, compile(source).unwrap());
assert_eq!(return_value, run(source).unwrap());
}
#[test]
fn equal_lists() {
let source = "[1, 2, 3] == [4, 5, 6]";
let chunk = Chunk {
r#type: FunctionType::new([], [], Type::Boolean),
instructions: vec![
Instruction::load_constant(0, 0, TypeCode::INTEGER, false),
Instruction::load_constant(1, 1, TypeCode::INTEGER, false),
Instruction::load_constant(2, 2, TypeCode::INTEGER, false),
Instruction::load_list(0, TypeCode::INTEGER, 0, 2, false),
Instruction::load_constant(3, 3, TypeCode::INTEGER, false),
Instruction::load_constant(4, 4, TypeCode::INTEGER, false),
Instruction::load_constant(5, 5, TypeCode::INTEGER, false),
Instruction::load_list(1, TypeCode::INTEGER, 3, 5, false),
Instruction::equal(
true,
Operand::Register(0, TypeCode::LIST),
Operand::Register(1, TypeCode::LIST),
),
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(1, 2),
Span(4, 5),
Span(7, 8),
Span(0, 9),
Span(14, 15),
Span(17, 18),
Span(20, 21),
Span(13, 22),
Span(0, 22),
Span(0, 22),
Span(0, 22),
Span(0, 22),
Span(22, 22),
],
integer_constants: vec![1, 2, 3, 4, 5, 6],
..Chunk::default()
};
let return_value = Some(Value::boolean(false));
assert_eq!(chunk, compile(source).unwrap());
assert_eq!(return_value, run(source).unwrap());
}

View File

@ -153,3 +153,51 @@ fn greater_strings() {
assert_eq!(chunk, compile(source).unwrap());
assert_eq!(return_value, run(source).unwrap());
}
#[test]
fn greater_lists() {
let source = "[1, 2, 3] > [4, 5, 6]";
let chunk = Chunk {
r#type: FunctionType::new([], [], Type::Boolean),
instructions: vec![
Instruction::load_constant(0, 0, TypeCode::INTEGER, false),
Instruction::load_constant(1, 1, TypeCode::INTEGER, false),
Instruction::load_constant(2, 2, TypeCode::INTEGER, false),
Instruction::load_list(0, TypeCode::INTEGER, 0, 2, false),
Instruction::load_constant(3, 3, TypeCode::INTEGER, false),
Instruction::load_constant(4, 4, TypeCode::INTEGER, false),
Instruction::load_constant(5, 5, TypeCode::INTEGER, false),
Instruction::load_list(1, TypeCode::INTEGER, 3, 5, false),
Instruction::less_equal(
false,
Operand::Register(0, TypeCode::LIST),
Operand::Register(1, TypeCode::LIST),
),
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(1, 2),
Span(4, 5),
Span(7, 8),
Span(0, 9),
Span(13, 14),
Span(16, 17),
Span(19, 20),
Span(12, 21),
Span(0, 21),
Span(0, 21),
Span(0, 21),
Span(0, 21),
Span(21, 21),
],
integer_constants: vec![1, 2, 3, 4, 5, 6],
..Chunk::default()
};
let return_value = Some(Value::boolean(false));
assert_eq!(chunk, compile(source).unwrap());
assert_eq!(return_value, run(source).unwrap());
}

View File

@ -194,3 +194,51 @@ fn greater_equal_strings() {
assert_eq!(chunk, compile(source).unwrap());
assert_eq!(return_value, run(source).unwrap());
}
#[test]
fn greater_equal_lists() {
let source = "[1, 2, 3] >= [4, 5, 6]";
let chunk = Chunk {
r#type: FunctionType::new([], [], Type::Boolean),
instructions: vec![
Instruction::load_constant(0, 0, TypeCode::INTEGER, false),
Instruction::load_constant(1, 1, TypeCode::INTEGER, false),
Instruction::load_constant(2, 2, TypeCode::INTEGER, false),
Instruction::load_list(0, TypeCode::INTEGER, 0, 2, false),
Instruction::load_constant(3, 3, TypeCode::INTEGER, false),
Instruction::load_constant(4, 4, TypeCode::INTEGER, false),
Instruction::load_constant(5, 5, TypeCode::INTEGER, false),
Instruction::load_list(1, TypeCode::INTEGER, 3, 5, false),
Instruction::less(
false,
Operand::Register(0, TypeCode::LIST),
Operand::Register(1, TypeCode::LIST),
),
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(1, 2),
Span(4, 5),
Span(7, 8),
Span(0, 9),
Span(14, 15),
Span(17, 18),
Span(20, 21),
Span(13, 22),
Span(0, 22),
Span(0, 22),
Span(0, 22),
Span(0, 22),
Span(22, 22),
],
integer_constants: vec![1, 2, 3, 4, 5, 6],
..Chunk::default()
};
let return_value = Some(Value::boolean(false));
assert_eq!(chunk, compile(source).unwrap());
assert_eq!(return_value, run(source).unwrap());
}

View File

@ -188,3 +188,51 @@ fn less_strings() {
assert_eq!(chunk, compile(source).unwrap());
assert_eq!(return_value, run(source).unwrap());
}
#[test]
fn less_lists() {
let source = "[1, 2, 3] < [4, 5, 6]";
let chunk = Chunk {
r#type: FunctionType::new([], [], Type::Boolean),
instructions: vec![
Instruction::load_constant(0, 0, TypeCode::INTEGER, false),
Instruction::load_constant(1, 1, TypeCode::INTEGER, false),
Instruction::load_constant(2, 2, TypeCode::INTEGER, false),
Instruction::load_list(0, TypeCode::INTEGER, 0, 2, false),
Instruction::load_constant(3, 3, TypeCode::INTEGER, false),
Instruction::load_constant(4, 4, TypeCode::INTEGER, false),
Instruction::load_constant(5, 5, TypeCode::INTEGER, false),
Instruction::load_list(1, TypeCode::INTEGER, 3, 5, false),
Instruction::less(
true,
Operand::Register(0, TypeCode::LIST),
Operand::Register(1, TypeCode::LIST),
),
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(1, 2),
Span(4, 5),
Span(7, 8),
Span(0, 9),
Span(13, 14),
Span(16, 17),
Span(19, 20),
Span(12, 21),
Span(0, 21),
Span(0, 21),
Span(0, 21),
Span(0, 21),
Span(21, 21),
],
integer_constants: vec![1, 2, 3, 4, 5, 6],
..Chunk::default()
};
let return_value = Some(Value::boolean(true));
assert_eq!(chunk, compile(source).unwrap());
assert_eq!(return_value, run(source).unwrap());
}

View File

@ -194,3 +194,51 @@ fn less_equal_strings() {
assert_eq!(chunk, compile(source).unwrap());
assert_eq!(return_value, run(source).unwrap());
}
#[test]
fn less_equal_lists() {
let source = "[1, 2, 3] <= [4, 5, 6]";
let chunk = Chunk {
r#type: FunctionType::new([], [], Type::Boolean),
instructions: vec![
Instruction::load_constant(0, 0, TypeCode::INTEGER, false),
Instruction::load_constant(1, 1, TypeCode::INTEGER, false),
Instruction::load_constant(2, 2, TypeCode::INTEGER, false),
Instruction::load_list(0, TypeCode::INTEGER, 0, 2, false),
Instruction::load_constant(3, 3, TypeCode::INTEGER, false),
Instruction::load_constant(4, 4, TypeCode::INTEGER, false),
Instruction::load_constant(5, 5, TypeCode::INTEGER, false),
Instruction::load_list(1, TypeCode::INTEGER, 3, 5, false),
Instruction::less_equal(
true,
Operand::Register(0, TypeCode::LIST),
Operand::Register(1, TypeCode::LIST),
),
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(1, 2),
Span(4, 5),
Span(7, 8),
Span(0, 9),
Span(14, 15),
Span(17, 18),
Span(20, 21),
Span(13, 22),
Span(0, 22),
Span(0, 22),
Span(0, 22),
Span(0, 22),
Span(22, 22),
],
integer_constants: vec![1, 2, 3, 4, 5, 6],
..Chunk::default()
};
let return_value = Some(Value::boolean(true));
assert_eq!(chunk, compile(source).unwrap());
assert_eq!(return_value, run(source).unwrap());
}

View File

@ -194,3 +194,51 @@ fn not_equal_strings() {
assert_eq!(chunk, compile(source).unwrap());
assert_eq!(return_value, run(source).unwrap());
}
#[test]
fn not_equal_lists() {
let source = "[1, 2, 3] != [4, 5, 6]";
let chunk = Chunk {
r#type: FunctionType::new([], [], Type::Boolean),
instructions: vec![
Instruction::load_constant(0, 0, TypeCode::INTEGER, false),
Instruction::load_constant(1, 1, TypeCode::INTEGER, false),
Instruction::load_constant(2, 2, TypeCode::INTEGER, false),
Instruction::load_list(0, TypeCode::INTEGER, 0, 2, false),
Instruction::load_constant(3, 3, TypeCode::INTEGER, false),
Instruction::load_constant(4, 4, TypeCode::INTEGER, false),
Instruction::load_constant(5, 5, TypeCode::INTEGER, false),
Instruction::load_list(1, TypeCode::INTEGER, 3, 5, false),
Instruction::equal(
false,
Operand::Register(0, TypeCode::LIST),
Operand::Register(1, TypeCode::LIST),
),
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(1, 2),
Span(4, 5),
Span(7, 8),
Span(0, 9),
Span(14, 15),
Span(17, 18),
Span(20, 21),
Span(13, 22),
Span(0, 22),
Span(0, 22),
Span(0, 22),
Span(0, 22),
Span(22, 22),
],
integer_constants: vec![1, 2, 3, 4, 5, 6],
..Chunk::default()
};
let return_value = Some(Value::boolean(true));
assert_eq!(chunk, compile(source).unwrap());
assert_eq!(return_value, run(source).unwrap());
}