diff --git a/dust-lang/src/compiler/mod.rs b/dust-lang/src/compiler/mod.rs index 67da724..16e9b24 100644 --- a/dust-lang/src/compiler/mod.rs +++ b/dust-lang/src/compiler/mod.rs @@ -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, ); diff --git a/dust-lang/src/instruction/close.rs b/dust-lang/src/instruction/close.rs index ac5bc77..cc63637 100644 --- a/dust-lang/src/instruction/close.rs +++ b/dust-lang/src/instruction/close.rs @@ -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 for Close { @@ -14,6 +15,7 @@ impl From for Close { Close { from: instruction.b_field(), to: instruction.c_field(), + r#type: instruction.b_type(), } } } @@ -22,11 +24,13 @@ impl From 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 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), + } } } diff --git a/dust-lang/src/instruction/load_list.rs b/dust-lang/src/instruction/load_list.rs index 55fb339..8356005 100644 --- a/dust-lang/src/instruction/load_list.rs +++ b/dust-lang/src/instruction/load_list.rs @@ -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 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 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, "]")?; diff --git a/dust-lang/src/instruction/mod.rs b/dust-lang/src/instruction/mod.rs index a301954..eda6076 100644 --- a/dust-lang/src/instruction/mod.rs +++ b/dust-lang/src/instruction/mod.rs @@ -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), } } } diff --git a/dust-lang/src/instruction/operation.rs b/dust-lang/src/instruction/operation.rs index 4f412c3..b4a22fd 100644 --- a/dust-lang/src/instruction/operation.rs +++ b/dust-lang/src/instruction/operation.rs @@ -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 { diff --git a/dust-lang/src/vm/action.rs b/dust-lang/src/vm/action.rs index 2a60133..2f16638 100644 --- a/dust-lang/src/vm/action.rs +++ b/dust-lang/src/vm/action.rs @@ -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!(), }; diff --git a/dust-lang/src/vm/call_frame.rs b/dust-lang/src/vm/call_frame.rs index 730f569..d2901d1 100644 --- a/dust-lang/src/vm/call_frame.rs +++ b/dust-lang/src/vm/call_frame.rs @@ -80,18 +80,28 @@ impl Default for RegisterTable { } } -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Copy, Debug, PartialEq)] pub enum Register { Empty, Value(T), + Closed(T), Pointer(Pointer), } +impl Register { + pub fn close(mut self) { + if let Self::Value(value) = self { + self = Self::Closed(value); + } + } +} + impl Display for Register { 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:?})"), } } diff --git a/dust-lang/src/vm/thread.rs b/dust-lang/src/vm/thread.rs index a794602..bc81301 100644 --- a/dust-lang/src/vm/thread.rs +++ b/dust-lang/src/vm/thread.rs @@ -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() diff --git a/dust-lang/tests/values/load_values.rs b/dust-lang/tests/values/load_values.rs index c65bdfa..6368839 100644 --- a/dust-lang/tests/values/load_values.rs +++ b/dust-lang/tests/values/load_values.rs @@ -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()); +}