Add comparison tests for lists; Edit README.md; Add benches to Cargo.toml
This commit is contained in:
parent
4c019410e8
commit
a53e0018cf
@ -29,6 +29,10 @@ fn fib (n: int) -> int {
|
|||||||
write_line(fib(25))
|
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
|
## Highlights
|
||||||
|
|
||||||
|
@ -31,6 +31,18 @@ criterion = { version = "0.5.1", features = ["html_reports"] }
|
|||||||
name = "addictive_addition"
|
name = "addictive_addition"
|
||||||
harness = false
|
harness = false
|
||||||
|
|
||||||
|
[[bench]]
|
||||||
|
name = "addictive_subtraction"
|
||||||
|
harness = false
|
||||||
|
|
||||||
|
[[bench]]
|
||||||
|
name = "addictive_multiplication"
|
||||||
|
harness = false
|
||||||
|
|
||||||
|
[[bench]]
|
||||||
|
name = "addictive_division"
|
||||||
|
harness = false
|
||||||
|
|
||||||
[[bench]]
|
[[bench]]
|
||||||
name = "fibonacci"
|
name = "fibonacci"
|
||||||
harness = false
|
harness = false
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use std::time::Duration;
|
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;
|
use dust_lang::run;
|
||||||
|
|
||||||
const SOURCE: &str = r#"
|
const SOURCE: &str = r#"
|
||||||
@ -9,9 +9,13 @@ const SOURCE: &str = r#"
|
|||||||
while i < 1_000 {
|
while i < 1_000 {
|
||||||
i += 1
|
i += 1
|
||||||
|
|
||||||
spawn(
|
spawn(fn () {
|
||||||
fn () { random_int(0, 10); }
|
let mut j = 0
|
||||||
)
|
|
||||||
|
while j < 5_000_000 {
|
||||||
|
j += 1
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
|
@ -855,6 +855,7 @@ impl<'src> Compiler<'src> {
|
|||||||
let operand = instruction.as_operand();
|
let operand = instruction.as_operand();
|
||||||
let push_back = match instruction.operation() {
|
let push_back = match instruction.operation() {
|
||||||
Operation::LOAD_ENCODED
|
Operation::LOAD_ENCODED
|
||||||
|
| Operation::LOAD_LIST
|
||||||
| Operation::LOAD_SELF
|
| Operation::LOAD_SELF
|
||||||
| Operation::ADD
|
| Operation::ADD
|
||||||
| Operation::SUBTRACT
|
| Operation::SUBTRACT
|
||||||
@ -997,6 +998,22 @@ impl<'src> Compiler<'src> {
|
|||||||
position: self.current_position,
|
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 = self.current_token;
|
||||||
let operator_position = self.current_position;
|
let operator_position = self.current_position;
|
||||||
@ -1012,30 +1029,18 @@ impl<'src> Compiler<'src> {
|
|||||||
found: self.previous_token.to_owned(),
|
found: self.previous_token.to_owned(),
|
||||||
position: self.previous_position,
|
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 right type is a valid type for comparison
|
||||||
// TODO: Check if the left and right types are compatible
|
|
||||||
|
|
||||||
if push_back_left {
|
let (right, push_back_right) = self.handle_binary_argument(&right_instruction);
|
||||||
self.instructions
|
|
||||||
.push((left_instruction, left_type, left_position));
|
|
||||||
}
|
|
||||||
|
|
||||||
if push_back_right {
|
if push_back_right {
|
||||||
self.instructions
|
self.instructions
|
||||||
.push((right_instruction, right_type, right_position));
|
.push((right_instruction, right_type, right_position));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Check if the left and right types are compatible
|
||||||
|
|
||||||
let comparison = match operator {
|
let comparison = match operator {
|
||||||
Token::DoubleEqual => Instruction::equal(true, left, right),
|
Token::DoubleEqual => Instruction::equal(true, left, right),
|
||||||
Token::BangEqual => Instruction::equal(false, 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> {
|
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) =
|
let (left_instruction, left_type, left_position) =
|
||||||
self.instructions
|
self.instructions
|
||||||
.pop()
|
.pop()
|
||||||
@ -1084,42 +1086,19 @@ impl<'src> Compiler<'src> {
|
|||||||
found: self.previous_token.to_owned(),
|
found: self.previous_token.to_owned(),
|
||||||
position: self.previous_position,
|
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
|
// TODO: Check if the left type is boolean
|
||||||
|
|
||||||
self.advance()?;
|
let (left, push_back_left) = self.handle_binary_argument(&left_instruction);
|
||||||
self.parse_sub_expression(&rule.precedence)?;
|
|
||||||
|
|
||||||
let (mut right_instruction, right_type, right_position) = self
|
if push_back_left {
|
||||||
.instructions
|
self.instructions
|
||||||
.pop()
|
.push((left_instruction, left_type.clone(), left_position));
|
||||||
.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);
|
|
||||||
|
|
||||||
|
let operator = self.current_token;
|
||||||
|
let operator_position = self.current_position;
|
||||||
|
let rule = ParseRule::from(&operator);
|
||||||
let test_boolean = match operator {
|
let test_boolean = match operator {
|
||||||
Token::DoubleAmpersand => true,
|
Token::DoubleAmpersand => true,
|
||||||
Token::DoublePipe => false,
|
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);
|
let jump = Instruction::jump(1, true);
|
||||||
|
|
||||||
self.emit_instruction(test, Type::None, operator_position);
|
self.emit_instruction(test, Type::None, operator_position);
|
||||||
self.emit_instruction(jump, 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();
|
let instructions_length = self.instructions.len();
|
||||||
|
|
||||||
|
@ -660,7 +660,7 @@ impl Instruction {
|
|||||||
Operation::EQUAL => Equal::from(*self).to_string(),
|
Operation::EQUAL => Equal::from(*self).to_string(),
|
||||||
Operation::LESS => Less::from(*self).to_string(),
|
Operation::LESS => Less::from(*self).to_string(),
|
||||||
Operation::LESS_EQUAL => LessEqual::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::TEST_SET => TestSet::from(*self).to_string(),
|
||||||
Operation::CALL => Call::from(*self).to_string(),
|
Operation::CALL => Call::from(*self).to_string(),
|
||||||
Operation::CALL_NATIVE => CallNative::from(*self).to_string(),
|
Operation::CALL_NATIVE => CallNative::from(*self).to_string(),
|
||||||
|
@ -11,7 +11,7 @@ pub struct Operation(pub u8);
|
|||||||
impl Operation {
|
impl Operation {
|
||||||
pub const NO_OP: Operation = Operation(0);
|
pub const NO_OP: Operation = Operation(0);
|
||||||
|
|
||||||
// Stack manipulation
|
// Register manipulation
|
||||||
pub const MOVE: Operation = Operation(1);
|
pub const MOVE: Operation = Operation(1);
|
||||||
pub const CLOSE: Operation = Operation(2);
|
pub const CLOSE: Operation = Operation(2);
|
||||||
|
|
||||||
|
@ -9,8 +9,8 @@ pub struct Test {
|
|||||||
pub test_value: bool,
|
pub test_value: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Instruction> for Test {
|
impl From<&Instruction> for Test {
|
||||||
fn from(instruction: Instruction) -> Self {
|
fn from(instruction: &Instruction) -> Self {
|
||||||
let operand_register = instruction.b_field();
|
let operand_register = instruction.b_field();
|
||||||
let test_value = instruction.c_field() != 0;
|
let test_value = instruction.c_field() != 0;
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ impl Thread {
|
|||||||
|
|
||||||
let instruction = &instructions[ip];
|
let instruction = &instructions[ip];
|
||||||
|
|
||||||
info!("Run instruction {}", instruction.operation());
|
info!("IP = {ip} Run {}", instruction.operation());
|
||||||
|
|
||||||
match instruction.operation() {
|
match instruction.operation() {
|
||||||
Operation::MOVE => {
|
Operation::MOVE => {
|
||||||
@ -1334,7 +1334,19 @@ impl Thread {
|
|||||||
}
|
}
|
||||||
_ => unreachable!("Invalid LESS_EQUAL instruction"),
|
_ => 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 => {
|
Operation::JUMP => {
|
||||||
let offset = instruction.b_field() as usize;
|
let offset = instruction.b_field() as usize;
|
||||||
let is_positive = instruction.c_field() != 0;
|
let is_positive = instruction.c_field() != 0;
|
||||||
|
@ -159,3 +159,51 @@ fn equal_strings() {
|
|||||||
assert_eq!(chunk, compile(source).unwrap());
|
assert_eq!(chunk, compile(source).unwrap());
|
||||||
assert_eq!(return_value, run(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());
|
||||||
|
}
|
||||||
|
@ -153,3 +153,51 @@ fn greater_strings() {
|
|||||||
assert_eq!(chunk, compile(source).unwrap());
|
assert_eq!(chunk, compile(source).unwrap());
|
||||||
assert_eq!(return_value, run(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());
|
||||||
|
}
|
||||||
|
@ -194,3 +194,51 @@ fn greater_equal_strings() {
|
|||||||
assert_eq!(chunk, compile(source).unwrap());
|
assert_eq!(chunk, compile(source).unwrap());
|
||||||
assert_eq!(return_value, run(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());
|
||||||
|
}
|
||||||
|
@ -188,3 +188,51 @@ fn less_strings() {
|
|||||||
assert_eq!(chunk, compile(source).unwrap());
|
assert_eq!(chunk, compile(source).unwrap());
|
||||||
assert_eq!(return_value, run(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());
|
||||||
|
}
|
||||||
|
@ -194,3 +194,51 @@ fn less_equal_strings() {
|
|||||||
assert_eq!(chunk, compile(source).unwrap());
|
assert_eq!(chunk, compile(source).unwrap());
|
||||||
assert_eq!(return_value, run(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());
|
||||||
|
}
|
||||||
|
@ -194,3 +194,51 @@ fn not_equal_strings() {
|
|||||||
assert_eq!(chunk, compile(source).unwrap());
|
assert_eq!(chunk, compile(source).unwrap());
|
||||||
assert_eq!(return_value, run(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());
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user