1
0

Implement lists; Add tests

This commit is contained in:
Jeff 2025-02-07 17:40:08 -05:00
parent 8cc5661944
commit 1d0824165d
9 changed files with 636 additions and 62 deletions

View File

@ -225,7 +225,7 @@ impl<'src> Compiler<'src> {
/// Note for maintainers: Do not give a name when compiling functions, only the main chunk. This
/// will allow [`Compiler::function_name`] to be both the name used for recursive calls and the
/// name of the function when it is compiled. The name can later be seen in the VM's call stack.
pub fn finish(self) -> Chunk {
pub fn finish(mut self) -> Chunk {
let boolean_register_count = self.next_boolean_register() as usize;
let byte_register_count = self.next_byte_register() as usize;
let character_register_count = self.next_character_register() as usize;
@ -352,7 +352,7 @@ impl<'src> Compiler<'src> {
.unwrap_or(self.minimum_string_register)
}
fn next_list_register(&self) -> u16 {
fn next_list_register(&mut self) -> u16 {
self.instructions
.iter()
.rev()
@ -1242,10 +1242,18 @@ impl<'src> Compiler<'src> {
let mut item_type = Type::None;
let mut start_register = None;
let mut length = 0;
while !self.allow(Token::RightBracket)? && !self.is_eof() {
while !self.allow(Token::RightBracket)? {
let next_boolean_register = self.next_boolean_register();
let next_byte_register = self.next_byte_register();
let next_character_register = self.next_character_register();
let next_float_register = self.next_float_register();
let next_integer_register = self.next_integer_register();
let next_string_register = self.next_string_register();
let next_list_register = self.next_list_register();
self.parse_expression()?;
self.allow(Token::Comma)?;
if item_type == Type::None {
item_type = self.get_last_instruction_type();
@ -1272,18 +1280,123 @@ impl<'src> Compiler<'src> {
start_register = Some(first_index);
}
length += 1;
match self.get_last_instruction_type() {
Type::Boolean => {
let used_boolean_registers =
self.next_boolean_register() - next_boolean_register;
self.allow(Token::Comma)?;
if used_boolean_registers > 1 {
let close = Instruction::close(
next_boolean_register,
next_boolean_register + used_boolean_registers,
TypeCode::BOOLEAN,
);
self.emit_instruction(close, Type::None, self.current_position);
}
}
Type::Byte => {
let used_byte_registers = self.next_byte_register() - 1 - next_byte_register;
if used_byte_registers > 1 {
let close = Instruction::close(
next_byte_register,
next_byte_register + used_byte_registers,
TypeCode::BYTE,
);
self.emit_instruction(close, Type::None, self.current_position);
}
}
Type::Character => {
let used_character_registers =
self.next_character_register() - 1 - next_character_register;
if used_character_registers > 1 {
let close = Instruction::close(
next_character_register,
next_character_register + used_character_registers,
TypeCode::CHARACTER,
);
self.emit_instruction(close, Type::None, self.current_position);
}
}
Type::Float => {
let used_float_registers = self.next_float_register() - 1 - next_float_register;
if used_float_registers > 1 {
let close = Instruction::close(
next_float_register,
next_float_register + used_float_registers,
TypeCode::FLOAT,
);
self.emit_instruction(close, Type::None, self.current_position);
}
}
Type::Integer => {
let used_integer_registers =
self.next_integer_register() - 1 - next_integer_register;
if used_integer_registers > 1 {
let close = Instruction::close(
next_integer_register,
next_integer_register + used_integer_registers,
TypeCode::INTEGER,
);
self.emit_instruction(close, Type::None, self.current_position);
}
}
Type::String => {
let used_string_registers =
self.next_string_register() - 1 - next_string_register;
if used_string_registers > 1 {
let close = Instruction::close(
next_string_register,
next_string_register + used_string_registers,
TypeCode::STRING,
);
self.emit_instruction(close, Type::None, self.current_position);
}
}
Type::List(_) => {
let used_list_registers = self.next_list_register() - next_list_register - 1;
if used_list_registers > 1 {
let close = Instruction::close(
next_list_register,
next_list_register + used_list_registers - 1,
TypeCode::LIST,
);
self.emit_instruction(close, Type::None, self.current_position);
}
}
_ => unimplemented!(),
}
}
let destination = self.next_list_register();
let end = self.previous_position.1;
let end_register = match item_type {
Type::Boolean => self.next_boolean_register() - 1,
Type::Byte => self.next_byte_register() - 1,
Type::Character => self.next_character_register() - 1,
Type::Float => self.next_float_register() - 1,
Type::Integer => self.next_integer_register() - 1,
Type::String => self.next_string_register() - 1,
Type::List(_) => self.next_list_register() - 1,
_ => todo!(),
};
let destination = self.next_list_register();
let load_list = Instruction::load_list(
destination,
item_type.type_code(),
start_register.unwrap_or(0),
length,
end_register,
false,
);

View File

@ -2,11 +2,12 @@ use std::fmt::{self, Display, Formatter};
use crate::{Instruction, Operation};
use super::InstructionFields;
use super::{InstructionFields, TypeCode};
pub struct Close {
pub from: u16,
pub to: u16,
pub r#type: TypeCode,
}
impl From<Instruction> for Close {
@ -14,6 +15,7 @@ impl From<Instruction> for Close {
Close {
from: instruction.b_field(),
to: instruction.c_field(),
r#type: instruction.b_type(),
}
}
}
@ -22,11 +24,13 @@ impl From<Close> for Instruction {
fn from(close: Close) -> Self {
let operation = Operation::CLOSE;
let b_field = close.from;
let b_type = close.r#type;
let c_field = close.to;
InstructionFields {
operation,
b_field,
b_type,
c_field,
..Default::default()
}
@ -36,8 +40,17 @@ impl From<Close> for Instruction {
impl Display for Close {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
let Close { from, to } = self;
let Close { from, to, r#type } = self;
write!(f, "{from}..={to}")
match *r#type {
TypeCode::BOOLEAN => write!(f, "R_BOOL_{from}..=R_BOOL_{to}"),
TypeCode::BYTE => write!(f, "R_BYTE_{from}..=R_BYTE_{to}"),
TypeCode::CHARACTER => write!(f, "R_CHAR_{from}..=R_CHAR_{to}"),
TypeCode::FLOAT => write!(f, "R_FLOAT_{from}..=R_FLOAT_{to}"),
TypeCode::INTEGER => write!(f, "R_INT_{from}..=R_INT_{to}"),
TypeCode::STRING => write!(f, "R_STR_{from}..=R_STR_{to}"),
TypeCode::LIST => write!(f, "R_LIST_{from}..=R_LIST_{to}"),
unsupported => panic!("Unsupported type code: {:?}", unsupported),
}
}
}

View File

@ -8,7 +8,7 @@ pub struct LoadList {
pub destination: u16,
pub item_type: TypeCode,
pub start_register: u16,
pub length: u16,
pub end_register: u16,
pub jump_next: bool,
}
@ -17,14 +17,14 @@ impl From<Instruction> for LoadList {
let destination = instruction.a_field();
let start_register = instruction.b_field();
let item_type = instruction.b_type();
let length = instruction.c_field();
let end_register = instruction.c_field();
let jump_next = instruction.d_field();
LoadList {
destination,
item_type,
start_register,
length,
end_register,
jump_next,
}
}
@ -37,7 +37,7 @@ impl From<LoadList> for Instruction {
a_field: load_list.destination,
b_field: load_list.start_register,
b_type: load_list.item_type,
c_field: load_list.length,
c_field: load_list.end_register,
d_field: load_list.jump_next,
..Default::default()
}
@ -51,19 +51,38 @@ impl Display for LoadList {
destination,
item_type,
start_register,
length,
end_register,
jump_next,
} = self;
let type_caps = item_type.to_string().to_uppercase();
write!(f, "R_LIST_{destination} = [")?;
for (i, register_index) in (*start_register..(start_register + length)).enumerate() {
if i > 0 {
write!(f, ", ")?;
match *item_type {
TypeCode::BOOLEAN => {
write!(f, "R_BOOL_{start_register}..=R_BOOL_{end_register}")?;
}
write!(f, "R_{type_caps}_{register_index}")?;
TypeCode::BYTE => {
write!(f, "R_BYTE_{start_register}..=R_BYTE_{end_register}")?;
}
TypeCode::CHARACTER => {
write!(f, "R_CHAR_{start_register}..=R_CHAR_{end_register}")?;
}
TypeCode::FLOAT => {
write!(f, "R_FLOAT_{start_register}..=R_FLOAT_{end_register}")?;
}
TypeCode::INTEGER => {
write!(f, "R_INT_{start_register}..=R_INT_{end_register}")?;
}
TypeCode::STRING => {
write!(f, "R_STR_{start_register}..=R_STR_{end_register}")?;
}
TypeCode::LIST => {
write!(f, "R_LIST_{start_register}..=R_LIST_{end_register}")?;
}
TypeCode::FUNCTION => {
write!(f, "R_FN_{start_register}..=R_FN_{end_register}")?;
}
unknown => panic!("Unknown type code: {}", unknown.0),
}
write!(f, "]")?;

View File

@ -326,8 +326,8 @@ impl Instruction {
Instruction::from(Point { destination, to })
}
pub fn close(from: u16, to: u16) -> Instruction {
Instruction::from(Close { from, to })
pub fn close(from: u16, to: u16, r#type: TypeCode) -> Instruction {
Instruction::from(Close { from, to, r#type })
}
pub fn load_encoded(
@ -370,14 +370,14 @@ impl Instruction {
destination: u16,
item_type: TypeCode,
start_register: u16,
length: u16,
end_register: u16,
jump_next: bool,
) -> Instruction {
Instruction::from(LoadList {
destination,
item_type,
start_register,
length,
end_register,
jump_next,
})
}
@ -583,7 +583,7 @@ impl Instruction {
| Operation::TEST_SET
| Operation::JUMP
| Operation::RETURN => false,
_ => self.operation().panic_from_unknown_code(),
unknown => panic!("Unknown operation: {}", unknown.0),
}
}
@ -592,6 +592,7 @@ impl Instruction {
match operation {
Operation::POINT => Point::from(*self).to_string(),
Operation::CLOSE => Close::from(*self).to_string(),
Operation::LOAD_ENCODED => LoadEncoded::from(*self).to_string(),
Operation::LOAD_CONSTANT => LoadConstant::from(*self).to_string(),
Operation::LOAD_FUNCTION => LoadFunction::from(*self).to_string(),
@ -613,8 +614,7 @@ impl Instruction {
Operation::CALL_NATIVE => CallNative::from(*self).to_string(),
Operation::JUMP => Jump::from(*self).to_string(),
Operation::RETURN => Return::from(*self).to_string(),
_ => operation.panic_from_unknown_code(),
unknown => panic!("Unknown operation: {}", unknown.0),
}
}
}

View File

@ -53,6 +53,7 @@ impl Operation {
pub fn name(&self) -> &'static str {
match *self {
Self::POINT => "POINT",
Self::CLOSE => "CLOSE",
Self::LOAD_ENCODED => "LOAD_ENCODED",
Self::LOAD_CONSTANT => "LOAD_CONSTANT",
Self::LOAD_FUNCTION => "LOAD_FUNCTION",
@ -74,7 +75,7 @@ impl Operation {
Self::CALL_NATIVE => "CALL_NATIVE",
Self::JUMP => "JUMP",
Self::RETURN => "RETURN",
_ => self.panic_from_unknown_code(),
unknown => panic!("Unknown operation: {}", unknown.0),
}
}
@ -95,10 +96,6 @@ impl Operation {
Operation::EQUAL | Operation::LESS | Operation::LESS_EQUAL
)
}
pub fn panic_from_unknown_code(self) -> ! {
panic!("Unknown operation code: {}", self.0);
}
}
impl Debug for Operation {

View File

@ -70,7 +70,50 @@ pub const RUNNER_LOGIC_TABLE: [RunnerLogic; 23] = [
pub fn point(_: InstructionFields, thread: &mut Thread) {}
pub fn close(_: InstructionFields, thread: &mut Thread) {}
pub fn close(instruction: InstructionFields, thread: &mut Thread) {
let from = instruction.b_field as usize;
let to = instruction.c_field as usize;
let r#type = instruction.b_type;
match r#type {
TypeCode::BOOLEAN => {
for register_index in from..=to {
thread.close_boolean_register(register_index);
}
}
TypeCode::BYTE => {
for register_index in from..=to {
thread.close_byte_register(register_index);
}
}
TypeCode::CHARACTER => {
for register_index in from..=to {
thread.close_character_register(register_index);
}
}
TypeCode::FLOAT => {
for register_index in from..=to {
thread.close_float_register(register_index);
}
}
TypeCode::INTEGER => {
for register_index in from..=to {
thread.close_integer_register(register_index);
}
}
TypeCode::STRING => {
for register_index in from..=to {
thread.close_string_register(register_index);
}
}
TypeCode::LIST => {
for register_index in from..=to {
thread.close_list_register(register_index);
}
}
_ => unimplemented!(),
}
}
pub fn load_encoded(instruction: InstructionFields, thread: &mut Thread) {
let destination = instruction.a_field;
@ -139,20 +182,79 @@ pub fn load_list(instruction: InstructionFields, thread: &mut Thread) {
let destination = instruction.a_field;
let start_register = instruction.b_field;
let item_type = instruction.b_type;
let length = instruction.c_field;
let end_register = instruction.c_field;
let jump_next = instruction.d_field;
let mut item_pointers = Vec::with_capacity(length as usize);
let length = (end_register - start_register + 1) as usize;
let mut item_pointers = Vec::with_capacity(length);
for register_index in start_register..=end_register {
let register_index = register_index as usize;
for register_index in start_register..start_register + length {
let pointer = match item_type {
TypeCode::BOOLEAN => Pointer::RegisterBoolean(register_index as usize),
TypeCode::BYTE => Pointer::RegisterByte(register_index as usize),
TypeCode::CHARACTER => Pointer::RegisterCharacter(register_index as usize),
TypeCode::FLOAT => Pointer::RegisterFloat(register_index as usize),
TypeCode::INTEGER => Pointer::RegisterInteger(register_index as usize),
TypeCode::STRING => Pointer::RegisterString(register_index as usize),
TypeCode::LIST => Pointer::RegisterList(register_index as usize),
TypeCode::BOOLEAN => {
let is_closed = thread.is_boolean_register_closed(register_index);
if is_closed {
continue;
}
Pointer::RegisterBoolean(register_index)
}
TypeCode::BYTE => {
let is_closed = thread.is_byte_register_closed(register_index);
if is_closed {
continue;
}
Pointer::RegisterByte(register_index)
}
TypeCode::CHARACTER => {
let is_closed = thread.is_character_register_closed(register_index);
if is_closed {
continue;
}
Pointer::RegisterCharacter(register_index)
}
TypeCode::FLOAT => {
let is_closed = thread.is_float_register_closed(register_index);
if is_closed {
continue;
}
Pointer::RegisterFloat(register_index)
}
TypeCode::INTEGER => {
let is_closed = thread.is_integer_register_closed(register_index);
if is_closed {
continue;
}
Pointer::RegisterInteger(register_index)
}
TypeCode::STRING => {
let is_closed = thread.is_string_register_closed(register_index);
if is_closed {
continue;
}
Pointer::RegisterString(register_index)
}
TypeCode::LIST => {
let is_closed = thread.is_list_register_closed(register_index);
if is_closed {
continue;
}
Pointer::RegisterList(register_index)
}
_ => unimplemented!(),
};

View File

@ -80,18 +80,28 @@ impl Default for RegisterTable {
}
}
#[derive(Clone, Debug, PartialEq)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Register<T> {
Empty,
Value(T),
Closed(T),
Pointer(Pointer),
}
impl<T> Register<T> {
pub fn close(mut self) {
if let Self::Value(value) = self {
self = Self::Closed(value);
}
}
}
impl<T: Display> Display for Register<T> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
Self::Empty => write!(f, "empty"),
Self::Value(value) => write!(f, "{value}"),
Self::Closed(value) => write!(f, "Closed({value})"),
Self::Value(value) => write!(f, "Value({value})"),
Self::Pointer(pointer) => write!(f, "Pointer({pointer:?})"),
}
}

View File

@ -2,10 +2,7 @@ use std::{collections::HashMap, sync::Arc, thread::JoinHandle};
use tracing::{info, trace};
use crate::{
AbstractList, Chunk, ConcreteValue, DustString, Span, Value, instruction::TypeCode,
vm::CallFrame,
};
use crate::{AbstractList, Chunk, ConcreteValue, DustString, Span, Value, vm::CallFrame};
use super::call_frame::{Pointer, Register};
@ -180,6 +177,7 @@ impl Thread {
match register {
Register::Value(value) => value,
Register::Closed(value) => value,
Register::Pointer(pointer) => self.get_pointer_to_boolean(pointer),
Register::Empty => panic!("Attempted to get value from empty register"),
}
@ -215,6 +213,38 @@ impl Thread {
*old_register = new_register;
}
pub fn is_boolean_register_closed(&self, register_index: usize) -> bool {
let register = if cfg!(debug_assertions) {
self.call_stack
.last()
.unwrap()
.registers
.booleans
.get(register_index)
.unwrap()
} else {
unsafe {
self.call_stack
.last()
.unwrap_unchecked()
.registers
.booleans
.get_unchecked(register_index)
}
};
matches!(register, Register::Closed(_))
}
pub fn close_boolean_register(&mut self, register_index: usize) {
self.current_frame_mut()
.registers
.booleans
.get_mut(register_index)
.unwrap()
.close();
}
pub fn get_byte_register(&self, register_index: usize) -> &u8 {
let register = if cfg!(debug_assertions) {
self.call_stack
@ -237,6 +267,7 @@ impl Thread {
match register {
Register::Value(value) => value,
Register::Closed(value) => value,
Register::Pointer(pointer) => self.get_pointer_to_byte(pointer),
Register::Empty => panic!("Attempted to get value from empty register"),
}
@ -272,6 +303,38 @@ impl Thread {
*old_register = new_register;
}
pub fn is_byte_register_closed(&self, register_index: usize) -> bool {
let register = if cfg!(debug_assertions) {
self.call_stack
.last()
.unwrap()
.registers
.bytes
.get(register_index)
.unwrap()
} else {
unsafe {
self.call_stack
.last()
.unwrap_unchecked()
.registers
.bytes
.get_unchecked(register_index)
}
};
matches!(register, Register::Closed(_))
}
pub fn close_byte_register(&mut self, register_index: usize) {
self.current_frame_mut()
.registers
.bytes
.get_mut(register_index)
.unwrap()
.close();
}
pub fn get_character_register(&self, register_index: usize) -> &char {
let register = if cfg!(debug_assertions) {
self.call_stack
@ -294,6 +357,7 @@ impl Thread {
match register {
Register::Value(value) => value,
Register::Closed(value) => value,
Register::Pointer(pointer) => self.get_pointer_to_character(pointer),
Register::Empty => panic!("Attempted to get value from empty register"),
}
@ -334,6 +398,38 @@ impl Thread {
*old_register = new_register;
}
pub fn is_character_register_closed(&self, register_index: usize) -> bool {
let register = if cfg!(debug_assertions) {
self.call_stack
.last()
.unwrap()
.registers
.characters
.get(register_index)
.unwrap()
} else {
unsafe {
self.call_stack
.last()
.unwrap_unchecked()
.registers
.characters
.get_unchecked(register_index)
}
};
matches!(register, Register::Closed(_))
}
pub fn close_character_register(&mut self, register_index: usize) {
self.current_frame_mut()
.registers
.characters
.get_mut(register_index)
.unwrap()
.close();
}
pub fn get_float_register(&self, register_index: usize) -> &f64 {
let register = if cfg!(debug_assertions) {
self.call_stack
@ -356,6 +452,7 @@ impl Thread {
match register {
Register::Value(value) => value,
Register::Closed(value) => value,
Register::Pointer(pointer) => self.get_pointer_to_float(pointer),
Register::Empty => panic!("Attempted to get value from empty register"),
}
@ -394,6 +491,38 @@ impl Thread {
*old_register = new_register;
}
pub fn is_float_register_closed(&self, register_index: usize) -> bool {
let register = if cfg!(debug_assertions) {
self.call_stack
.last()
.unwrap()
.registers
.floats
.get(register_index)
.unwrap()
} else {
unsafe {
self.call_stack
.last()
.unwrap_unchecked()
.registers
.floats
.get_unchecked(register_index)
}
};
matches!(register, Register::Closed(_))
}
pub fn close_float_register(&mut self, register_index: usize) {
self.current_frame_mut()
.registers
.floats
.get_mut(register_index)
.unwrap()
.close();
}
pub fn get_integer_register(&self, register_index: usize) -> &i64 {
let register = if cfg!(debug_assertions) {
self.call_stack
@ -416,6 +545,7 @@ impl Thread {
match register {
Register::Value(value) => value,
Register::Closed(value) => value,
Register::Pointer(pointer) => self.get_pointer_to_integer(pointer),
Register::Empty => panic!("Attempted to get value from empty register"),
}
@ -454,6 +584,38 @@ impl Thread {
*old_register = new_register;
}
pub fn is_integer_register_closed(&self, register_index: usize) -> bool {
let register = if cfg!(debug_assertions) {
self.call_stack
.last()
.unwrap()
.registers
.integers
.get(register_index)
.unwrap()
} else {
unsafe {
self.call_stack
.last()
.unwrap_unchecked()
.registers
.integers
.get_unchecked(register_index)
}
};
matches!(register, Register::Closed(_))
}
pub fn close_integer_register(&mut self, register_index: usize) {
self.current_frame_mut()
.registers
.integers
.get_mut(register_index)
.unwrap()
.close();
}
pub fn get_string_register(&self, register_index: usize) -> &DustString {
let register = if cfg!(debug_assertions) {
self.call_stack
@ -476,6 +638,7 @@ impl Thread {
match register {
Register::Value(value) => value,
Register::Closed(value) => value,
Register::Pointer(pointer) => self.get_pointer_to_string(pointer),
Register::Empty => panic!("Attempted to get value from empty register"),
}
@ -518,6 +681,48 @@ impl Thread {
*old_register = new_register;
}
pub fn is_string_register_closed(&self, register_index: usize) -> bool {
let register = if cfg!(debug_assertions) {
self.call_stack
.last()
.unwrap()
.registers
.strings
.get(register_index)
.unwrap()
} else {
unsafe {
self.call_stack
.last()
.unwrap_unchecked()
.registers
.strings
.get_unchecked(register_index)
}
};
matches!(register, Register::Closed(_))
}
pub fn close_string_register(&mut self, register_index: usize) {
let current_frame = self.current_frame_mut();
current_frame.registers.strings.push(Register::Empty);
let old_register = current_frame.registers.strings.swap_remove(register_index);
if let Register::Value(value) = old_register {
current_frame
.registers
.strings
.push(Register::Closed(value));
let _ = current_frame.registers.strings.swap_remove(register_index);
} else {
panic!("Attempted to close non-value register");
}
}
pub fn get_list_register(&self, register_index: usize) -> &AbstractList {
let register = if cfg!(debug_assertions) {
self.call_stack
@ -540,6 +745,7 @@ impl Thread {
match register {
Register::Value(value) => value,
Register::Closed(value) => value,
Register::Pointer(pointer) => self.get_pointer_to_list(pointer),
Register::Empty => panic!("Attempted to get value from empty register"),
}
@ -579,6 +785,45 @@ impl Thread {
*old_register = new_register;
}
pub fn is_list_register_closed(&self, register_index: usize) -> bool {
let register = if cfg!(debug_assertions) {
self.call_stack
.last()
.unwrap()
.registers
.lists
.get(register_index)
.unwrap()
} else {
unsafe {
self.call_stack
.last()
.unwrap_unchecked()
.registers
.lists
.get_unchecked(register_index)
}
};
matches!(register, Register::Closed(_))
}
pub fn close_list_register(&mut self, register_index: usize) {
let current_frame = self.current_frame_mut();
current_frame.registers.lists.push(Register::Empty);
let old_register = current_frame.registers.lists.swap_remove(register_index);
if let Register::Value(value) = old_register {
current_frame.registers.lists.push(Register::Closed(value));
let _ = current_frame.registers.lists.swap_remove(register_index);
} else {
panic!("Attempted to close non-value register");
}
}
pub fn get_constant(&self, constant_index: usize) -> &ConcreteValue {
if cfg!(debug_assertions) {
self.chunk.constants.get(constant_index).unwrap()

View File

@ -1,6 +1,6 @@
use dust_lang::{
AbstractList, Chunk, ConcreteValue, DustString, FunctionType, Instruction, Span, Type, Value,
compile, instruction::TypeCode, run, vm::Pointer,
Chunk, ConcreteValue, DustString, FunctionType, Instruction, Span, Type, Value, compile,
instruction::TypeCode, run,
};
#[test]
@ -165,7 +165,7 @@ fn load_boolean_list() {
instructions: vec![
Instruction::load_encoded(0, true as u8, TypeCode::BOOLEAN, false),
Instruction::load_encoded(1, false as u8, TypeCode::BOOLEAN, false),
Instruction::load_list(0, TypeCode::BOOLEAN, 0, 2, false),
Instruction::load_list(0, TypeCode::BOOLEAN, 0, 1, false),
Instruction::r#return(true, 0, TypeCode::LIST),
],
positions: vec![Span(0, 13), Span(13, 13)],
@ -191,7 +191,7 @@ fn load_byte_list() {
instructions: vec![
Instruction::load_encoded(0, 0x2a, TypeCode::BYTE, false),
Instruction::load_encoded(1, 0x42, TypeCode::BYTE, false),
Instruction::load_list(0, TypeCode::BYTE, 0, 2, false),
Instruction::load_list(0, TypeCode::BYTE, 0, 1, false),
Instruction::r#return(true, 0, TypeCode::LIST),
],
positions: vec![Span(0, 15), Span(15, 15)],
@ -217,7 +217,7 @@ fn load_character_list() {
instructions: vec![
Instruction::load_constant(0, 0, TypeCode::CHARACTER, false),
Instruction::load_constant(1, 1, TypeCode::CHARACTER, false),
Instruction::load_list(0, TypeCode::CHARACTER, 0, 2, false),
Instruction::load_list(0, TypeCode::CHARACTER, 0, 1, false),
Instruction::r#return(true, 0, TypeCode::LIST),
],
positions: vec![Span(0, 9), Span(9, 9)],
@ -244,7 +244,7 @@ fn load_float_list() {
instructions: vec![
Instruction::load_constant(0, 0, TypeCode::FLOAT, false),
Instruction::load_constant(1, 1, TypeCode::FLOAT, false),
Instruction::load_list(0, TypeCode::FLOAT, 0, 2, false),
Instruction::load_list(0, TypeCode::FLOAT, 0, 1, false),
Instruction::r#return(true, 0, TypeCode::LIST),
],
positions: vec![Span(0, 15), Span(15, 15)],
@ -272,7 +272,7 @@ fn load_integer_list() {
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, 3, false),
Instruction::load_list(0, TypeCode::INTEGER, 0, 2, false),
Instruction::r#return(true, 0, TypeCode::LIST),
],
positions: vec![Span(0, 9), Span(9, 9)],
@ -304,7 +304,7 @@ fn load_string_list() {
instructions: vec![
Instruction::load_constant(0, 0, TypeCode::STRING, false),
Instruction::load_constant(1, 1, TypeCode::STRING, false),
Instruction::load_list(0, TypeCode::STRING, 0, 2, false),
Instruction::load_list(0, TypeCode::STRING, 0, 1, false),
Instruction::r#return(true, 0, TypeCode::LIST),
],
positions: vec![Span(0, 19), Span(19, 19)],
@ -334,11 +334,11 @@ fn load_nested_list() {
instructions: vec![
Instruction::load_constant(0, 0, TypeCode::INTEGER, false),
Instruction::load_constant(1, 1, TypeCode::INTEGER, false),
Instruction::load_list(0, TypeCode::INTEGER, 0, 2, false),
Instruction::load_list(0, TypeCode::INTEGER, 0, 1, false),
Instruction::load_constant(2, 2, TypeCode::INTEGER, false),
Instruction::load_constant(3, 3, TypeCode::INTEGER, false),
Instruction::load_list(1, TypeCode::INTEGER, 2, 2, false),
Instruction::load_list(2, TypeCode::LIST, 0, 2, false),
Instruction::load_list(1, TypeCode::INTEGER, 2, 3, false),
Instruction::load_list(2, TypeCode::LIST, 0, 1, false),
Instruction::r#return(true, 2, TypeCode::LIST),
],
positions: vec![
@ -367,3 +367,78 @@ fn load_nested_list() {
assert_eq!(chunk, compile(source).unwrap());
assert_eq!(return_value, run(source).unwrap());
}
#[test]
fn load_deeply_nested_list() {
let source = "[[[1, 2], [3, 4]], [[5, 6], [7, 8]]]";
let chunk = Chunk {
r#type: FunctionType {
return_type: Type::List(TypeCode::LIST),
..FunctionType::default()
},
instructions: vec![
Instruction::load_constant(0, 0, TypeCode::INTEGER, false),
Instruction::load_constant(1, 1, TypeCode::INTEGER, false),
Instruction::load_list(0, TypeCode::INTEGER, 0, 1, false),
Instruction::load_constant(2, 2, TypeCode::INTEGER, false),
Instruction::load_constant(3, 3, TypeCode::INTEGER, false),
Instruction::load_list(1, TypeCode::INTEGER, 2, 3, false),
Instruction::load_list(2, TypeCode::LIST, 0, 1, false),
Instruction::close(0, 1, TypeCode::LIST),
Instruction::load_constant(4, 4, TypeCode::INTEGER, false),
Instruction::load_constant(5, 5, TypeCode::INTEGER, false),
Instruction::load_list(3, TypeCode::INTEGER, 4, 5, false),
Instruction::load_constant(6, 6, TypeCode::INTEGER, false),
Instruction::load_constant(7, 7, TypeCode::INTEGER, false),
Instruction::load_list(4, TypeCode::INTEGER, 6, 7, false),
Instruction::load_list(5, TypeCode::LIST, 3, 4, false),
Instruction::close(3, 4, TypeCode::LIST),
Instruction::load_list(6, TypeCode::LIST, 2, 5, false),
Instruction::r#return(true, 6, TypeCode::LIST),
],
positions: vec![
Span(3, 4),
Span(6, 7),
Span(2, 8),
Span(11, 12),
Span(14, 15),
Span(10, 16),
Span(1, 17),
Span(17, 18),
Span(21, 22),
Span(24, 25),
Span(20, 26),
Span(29, 30),
Span(32, 33),
Span(28, 34),
Span(19, 35),
Span(35, 36),
Span(0, 36),
Span(36, 36),
],
constants: vec![
ConcreteValue::Integer(1),
ConcreteValue::Integer(2),
ConcreteValue::Integer(3),
ConcreteValue::Integer(4),
ConcreteValue::Integer(5),
ConcreteValue::Integer(6),
ConcreteValue::Integer(7),
ConcreteValue::Integer(8),
],
..Chunk::default()
};
let return_value = Some(Value::Concrete(ConcreteValue::List(vec![
ConcreteValue::List(vec![
ConcreteValue::List(vec![ConcreteValue::Integer(1), ConcreteValue::Integer(2)]),
ConcreteValue::List(vec![ConcreteValue::Integer(3), ConcreteValue::Integer(4)]),
]),
ConcreteValue::List(vec![
ConcreteValue::List(vec![ConcreteValue::Integer(5), ConcreteValue::Integer(6)]),
ConcreteValue::List(vec![ConcreteValue::Integer(7), ConcreteValue::Integer(8)]),
]),
])));
assert_eq!(chunk, compile(source).unwrap());
assert_eq!(return_value, run(source).unwrap());
}