From ba80774e7b1f125a51dd79c2ccccc166e2d8e410 Mon Sep 17 00:00:00 2001 From: Jeff Date: Sat, 14 Sep 2024 21:05:03 -0400 Subject: [PATCH] Refactor and debug --- dust-lang/src/chunk.rs | 68 ++++++++++++++++++------------- dust-lang/src/instruction.rs | 57 +++++++++++--------------- dust-lang/src/parser/mod.rs | 70 ++++++++++++++++++++++++-------- dust-lang/src/vm.rs | 79 ++++++++++++++++++------------------ 4 files changed, 156 insertions(+), 118 deletions(-) diff --git a/dust-lang/src/chunk.rs b/dust-lang/src/chunk.rs index 2d63d91..9c9e692 100644 --- a/dust-lang/src/chunk.rs +++ b/dust-lang/src/chunk.rs @@ -60,7 +60,9 @@ impl Chunk { .ok_or(ChunkError::InstructionUnderflow { position }) } - pub fn get_constant(&self, index: usize, position: Span) -> Result<&Value, ChunkError> { + pub fn get_constant(&self, index: u8, position: Span) -> Result<&Value, ChunkError> { + let index = index as usize; + self.constants .get(index) .ok_or(ChunkError::ConstantIndexOutOfBounds { index, position }) @@ -71,7 +73,9 @@ impl Chunk { }) } - pub fn take_constant(&mut self, index: usize, position: Span) -> Result { + pub fn take_constant(&mut self, index: u8, position: Span) -> Result { + let index = index as usize; + self.constants .get_mut(index) .ok_or_else(|| ChunkError::ConstantIndexOutOfBounds { index, position })? @@ -79,7 +83,7 @@ impl Chunk { .ok_or(ChunkError::ConstantAlreadyUsed { index, position }) } - pub fn push_constant(&mut self, value: Value, position: Span) -> Result { + pub fn push_constant(&mut self, value: Value, position: Span) -> Result { let starting_length = self.constants.len(); if starting_length + 1 > (u8::MAX as usize) { @@ -87,17 +91,21 @@ impl Chunk { } else { self.constants.push(Some(value)); - Ok(starting_length as u16) + Ok(starting_length as u8) } } - pub fn get_local(&self, index: usize, position: Span) -> Result<&Local, ChunkError> { + pub fn get_local(&self, index: u8, position: Span) -> Result<&Local, ChunkError> { + let index = index as usize; + self.locals .get(index) .ok_or(ChunkError::LocalIndexOutOfBounds { index, position }) } - pub fn get_identifier(&self, index: usize) -> Option<&Identifier> { + pub fn get_identifier(&self, index: u8) -> Option<&Identifier> { + let index = index as usize; + self.locals.get(index).map(|local| &local.identifier) } @@ -105,14 +113,14 @@ impl Chunk { &self, identifier: &Identifier, position: Span, - ) -> Result { + ) -> Result { self.locals .iter() .enumerate() .rev() .find_map(|(index, local)| { if &local.identifier == identifier { - Some(index as u16) + Some(index as u8) } else { None } @@ -126,17 +134,21 @@ impl Chunk { pub fn declare_local( &mut self, identifier: Identifier, + register_index: u8, position: Span, - ) -> Result { + ) -> Result { let starting_length = self.locals.len(); if starting_length + 1 > (u8::MAX as usize) { Err(ChunkError::IdentifierOverflow { position }) } else { - self.locals - .push(Local::new(identifier, self.scope_depth, None)); + self.locals.push(Local::new( + identifier, + self.scope_depth, + Some(register_index), + )); - Ok(starting_length as u16) + Ok(starting_length as u8) } } @@ -241,8 +253,8 @@ impl<'a> ChunkDisassembler<'a> { "", "Instructions", "------------", - "OFFSET OPERATION INFO POSITION", - "------- -------------- -------------------- --------", + "OFFSET OPERATION INFO POSITION", + "------- -------------- ------------------------- --------", ]; const CONSTANT_HEADER: [&'static str; 5] = [ @@ -274,6 +286,18 @@ impl<'a> ChunkDisassembler<'a> { } } + pub fn width(&mut self, width: usize) -> &mut Self { + self.width = width; + + self + } + + pub fn styled(&mut self, styled: bool) -> &mut Self { + self.styled = styled; + + self + } + pub fn disassemble(&self) -> String { let center = |line: &str| format!("{line:^width$}\n", width = self.width); let style = |line: String| { @@ -298,9 +322,9 @@ impl<'a> ChunkDisassembler<'a> { let operation = instruction.operation.to_string(); let info_option = instruction.disassembly_info(Some(self.chunk)); let instruction_display = if let Some(info) = info_option { - format!("{offset:<7} {operation:14} {info:20} {position:8}") + format!("{offset:<7} {operation:14} {info:25} {position:8}") } else { - format!("{offset:<7} {operation:14} {:20} {position:8}", " ") + format!("{offset:<7} {operation:14} {:25} {position:8}", " ") }; disassembled.push_str(¢er(&instruction_display)); @@ -367,18 +391,6 @@ impl<'a> ChunkDisassembler<'a> { disassembled } - pub fn width(&mut self, width: usize) -> &mut Self { - self.width = width; - - self - } - - pub fn styled(&mut self, styled: bool) -> &mut Self { - self.styled = styled; - - self - } - /// Predicts the capacity of the disassembled output. This is used to pre-allocate the string /// buffer to avoid reallocations. /// diff --git a/dust-lang/src/instruction.rs b/dust-lang/src/instruction.rs index cf96451..167fcac 100644 --- a/dust-lang/src/instruction.rs +++ b/dust-lang/src/instruction.rs @@ -10,26 +10,6 @@ pub struct Instruction { } impl Instruction { - pub fn decode(bits: u32) -> Instruction { - let operation = Operation::from((bits >> 24) as u8); - let to_register = ((bits >> 16) & 0xff) as u8; - let arguments = [((bits >> 8) & 0xff) as u8, (bits & 0xff) as u8]; - - Instruction { - operation, - destination: to_register, - arguments, - } - } - - pub fn encode(&self) -> u32 { - let operation = self.operation as u8 as u32; - let to_register = self.destination as u32; - let arguments = (self.arguments[0] as u32) << 8 | (self.arguments[1] as u32); - - operation << 24 | to_register << 16 | arguments - } - pub fn r#move(to_register: u8, from_register: u8) -> Instruction { Instruction { operation: Operation::Move, @@ -46,35 +26,35 @@ impl Instruction { } } - pub fn load_constant(to_register: u8, constant_index: u16) -> Instruction { + pub fn load_constant(to_register: u8, constant_index: u8) -> Instruction { Instruction { operation: Operation::LoadConstant, destination: to_register, - arguments: constant_index.to_le_bytes(), + arguments: [constant_index, 0], } } - pub fn declare_local(to_register: u8, variable_index: u16) -> Instruction { + pub fn declare_local(to_register: u8, variable_index: u8) -> Instruction { Instruction { operation: Operation::DeclareLocal, destination: to_register, - arguments: variable_index.to_le_bytes(), + arguments: [variable_index, 0], } } - pub fn get_local(to_register: u8, variable_index: u16) -> Instruction { + pub fn get_local(to_register: u8, variable_index: u8) -> Instruction { Instruction { operation: Operation::GetLocal, destination: to_register, - arguments: variable_index.to_le_bytes(), + arguments: [variable_index, 0], } } - pub fn set_local(from_register: u8, variable_index: u16) -> Instruction { + pub fn set_local(from_register: u8, variable_index: u8) -> Instruction { Instruction { operation: Operation::SetLocal, destination: from_register, - arguments: variable_index.to_le_bytes(), + arguments: [variable_index, 0], } } @@ -143,7 +123,7 @@ impl Instruction { } Operation::Close => format!("R({})", self.destination), Operation::LoadConstant => { - let constant_index = u16::from_le_bytes(self.arguments) as usize; + let constant_index = self.arguments[0]; if let Some(chunk) = chunk { match chunk.get_constant(constant_index, Span(0, 0)) { @@ -160,9 +140,9 @@ impl Instruction { } } Operation::DeclareLocal => { - let local_index = u16::from_le_bytes([self.arguments[0], self.arguments[1]]); + let local_index = self.arguments[0]; let identifier_display = if let Some(chunk) = chunk { - match chunk.get_identifier(local_index as usize) { + match chunk.get_identifier(local_index) { Some(identifier) => identifier.to_string(), None => "???".to_string(), } @@ -176,14 +156,14 @@ impl Instruction { ) } Operation::GetLocal => { - let local_index = u16::from_le_bytes([self.arguments[0], self.arguments[1]]); + let local_index = self.arguments[0]; format!("R({}) = L({})", self.destination, local_index) } Operation::SetLocal => { - let local_index = u16::from_le_bytes([self.arguments[0], self.arguments[1]]); + let local_index = self.arguments[0]; let identifier_display = if let Some(chunk) = chunk { - match chunk.get_identifier(local_index as usize) { + match chunk.get_identifier(local_index) { Some(identifier) => identifier.to_string(), None => "???".to_string(), } @@ -267,6 +247,15 @@ pub enum Operation { Return = 11, } +impl Operation { + pub fn is_binary(&self) -> bool { + matches!( + self, + Operation::Add | Operation::Subtract | Operation::Multiply | Operation::Divide + ) + } +} + impl From for Operation { fn from(byte: u8) -> Self { match byte { diff --git a/dust-lang/src/parser/mod.rs b/dust-lang/src/parser/mod.rs index 2d7344d..33d08da 100644 --- a/dust-lang/src/parser/mod.rs +++ b/dust-lang/src/parser/mod.rs @@ -255,8 +255,10 @@ impl<'src> Parser<'src> { self.parse(rule.precedence.increment())?; - let (previous_instruction, position) = self.chunk.pop_instruction(self.current_position)?; - let right_register = match previous_instruction { + let mut push_back_right = false; + let (right_instruction, right_position) = + self.chunk.pop_instruction(self.current_position)?; + let right_register = match right_instruction { Instruction { operation: Operation::LoadConstant, arguments, @@ -267,13 +269,15 @@ impl<'src> Parser<'src> { arguments[0] } _ => { - self.chunk.push_instruction(previous_instruction, position); + push_back_right = true; self.current_register - 1 } }; - let (previous_instruction, position) = self.chunk.pop_instruction(self.current_position)?; - let left_register = match previous_instruction { + let mut push_back_left = false; + let (left_instruction, left_position) = + self.chunk.pop_instruction(self.current_position)?; + let left_register = match left_instruction { Instruction { operation: Operation::LoadConstant, arguments, @@ -284,11 +288,21 @@ impl<'src> Parser<'src> { arguments[0] } _ => { - self.chunk.push_instruction(previous_instruction, position); + push_back_left = true; self.current_register - 2 } }; + + if push_back_right { + self.chunk + .push_instruction(right_instruction, right_position); + } + + if push_back_left { + self.chunk.push_instruction(left_instruction, left_position); + } + let instruction = match operator { TokenKind::Plus => { Instruction::add(self.current_register, left_register, right_register) @@ -328,23 +342,42 @@ impl<'src> Parser<'src> { fn parse_named_variable(&mut self, allow_assignment: bool) -> Result<(), ParseError> { let token = self.previous_token.to_owned(); - let local_index = self.parse_identifier_from(token, self.previous_position)?; + let start_position = self.previous_position; + let local_index = self.parse_identifier_from(token, start_position)?; if allow_assignment && self.allow(TokenKind::Equal)? { self.parse_expression()?; - self.emit_instruction( - Instruction::set_local(self.current_register, local_index), - self.previous_position, - ); + + let (mut previous_instruction, previous_position) = + self.chunk.pop_instruction(self.previous_position)?; + + if previous_instruction.operation.is_binary() { + let previous_register = self + .chunk + .get_local(local_index, start_position)? + .register_index; + + if let Some(register_index) = previous_register { + previous_instruction.destination = register_index; + + self.emit_instruction(previous_instruction, self.previous_position); + self.decrement_register()?; + } else { + self.emit_instruction(previous_instruction, previous_position); + self.emit_instruction( + Instruction::set_local(self.current_register - 1, local_index), + self.previous_position, + ); + } + } } else { self.emit_instruction( Instruction::get_local(self.current_register, local_index), self.previous_position, ); + self.increment_register()?; } - self.increment_register()?; - Ok(()) } @@ -352,7 +385,7 @@ impl<'src> Parser<'src> { &mut self, token: TokenOwned, position: Span, - ) -> Result { + ) -> Result { if let TokenOwned::Identifier(text) = token { let identifier = Identifier::new(text); @@ -441,9 +474,12 @@ impl<'src> Parser<'src> { self.expect(TokenKind::Equal)?; self.parse_expression()?; - let local_index = self - .chunk - .declare_local(identifier, self.current_position)?; + let local_index = self.chunk.declare_local( + identifier, + self.current_register - 1, + self.current_position, + )?; + let (previous_instruction, previous_position) = self.chunk.pop_instruction(self.current_position)?; diff --git a/dust-lang/src/vm.rs b/dust-lang/src/vm.rs index 81250cc..6f9152e 100644 --- a/dust-lang/src/vm.rs +++ b/dust-lang/src/vm.rs @@ -40,16 +40,16 @@ impl Vm { match instruction.operation { Operation::Move => { - let from = instruction.arguments[0] as usize; - let to = instruction.destination as usize; + let from = instruction.arguments[0]; + let to = instruction.destination; let value = self.clone(from, position)?; self.insert(value, to, position)?; } Operation::Close => todo!(), Operation::LoadConstant => { - let to_register = instruction.destination as usize; - let from_constant = u16::from_le_bytes(instruction.arguments) as usize; + let to_register = instruction.destination; + let from_constant = instruction.arguments[0]; let value = self.chunk.take_constant(from_constant, position)?; self.insert(value, to_register, position)?; @@ -61,11 +61,11 @@ impl Vm { self.chunk.define_local(to_local, from_register, position)?; } Operation::GetLocal => { - let register_index = instruction.destination as usize; - let local_index = u16::from_le_bytes(instruction.arguments) as usize; + let register_index = instruction.destination; + let local_index = instruction.arguments[0]; let local = self.chunk.get_local(local_index, position)?; let value = if let Some(value_index) = &local.register_index { - self.clone(*value_index as usize, position)? + self.clone(*value_index, position)? } else { return Err(VmError::UndefinedVariable { identifier: local.identifier.clone(), @@ -75,59 +75,64 @@ impl Vm { self.insert(value, register_index, position)?; } - Operation::SetLocal => todo!(), + Operation::SetLocal => { + let from_register = instruction.destination; + let to_local = instruction.arguments[0] as usize; + + self.chunk.define_local(to_local, from_register, position)?; + } Operation::Add => { let left = - self.take_or_use_constant(instruction.arguments[0] as usize, position)?; + self.take_constant_or_clone_register(instruction.arguments[0], position)?; let right = - self.take_or_use_constant(instruction.arguments[1] as usize, position)?; + self.take_constant_or_clone_register(instruction.arguments[1], position)?; let sum = left .add(&right) .map_err(|error| VmError::Value { error, position })?; - self.insert(sum, instruction.destination as usize, position)?; + self.insert(sum, instruction.destination, position)?; } Operation::Subtract => { let left = - self.take_or_use_constant(instruction.arguments[0] as usize, position)?; + self.take_constant_or_clone_register(instruction.arguments[0], position)?; let right = - self.take_or_use_constant(instruction.arguments[1] as usize, position)?; + self.take_constant_or_clone_register(instruction.arguments[1], position)?; let difference = left .subtract(&right) .map_err(|error| VmError::Value { error, position })?; - self.insert(difference, instruction.destination as usize, position)?; + self.insert(difference, instruction.destination, position)?; } Operation::Multiply => { let left = - self.take_or_use_constant(instruction.arguments[0] as usize, position)?; + self.take_constant_or_clone_register(instruction.arguments[0], position)?; let right = - self.take_or_use_constant(instruction.arguments[1] as usize, position)?; + self.take_constant_or_clone_register(instruction.arguments[1], position)?; let product = left .multiply(&right) .map_err(|error| VmError::Value { error, position })?; - self.insert(product, instruction.destination as usize, position)?; + self.insert(product, instruction.destination, position)?; } Operation::Divide => { let left = - self.take_or_use_constant(instruction.arguments[0] as usize, position)?; + self.take_constant_or_clone_register(instruction.arguments[0], position)?; let right = - self.take_or_use_constant(instruction.arguments[1] as usize, position)?; + self.take_constant_or_clone_register(instruction.arguments[1], position)?; let quotient = left .divide(&right) .map_err(|error| VmError::Value { error, position })?; - self.insert(quotient, instruction.destination as usize, position)?; + self.insert(quotient, instruction.destination, position)?; } Operation::Negate => { let value = - self.take_or_use_constant(instruction.arguments[0] as usize, position)?; + self.take_constant_or_clone_register(instruction.arguments[0], position)?; let negated = value .negate() .map_err(|error| VmError::Value { error, position })?; - self.insert(negated, instruction.destination as usize, position)?; + self.insert(negated, instruction.destination, position)?; } Operation::Return => { let value = self.pop(position)?; @@ -140,10 +145,12 @@ impl Vm { Ok(None) } - fn insert(&mut self, value: Value, index: usize, position: Span) -> Result<(), VmError> { + fn insert(&mut self, value: Value, index: u8, position: Span) -> Result<(), VmError> { if self.register_stack.len() == Self::STACK_LIMIT { Err(VmError::StackOverflow { position }) } else { + let index = index as usize; + while index >= self.register_stack.len() { self.register_stack.push(None); } @@ -154,7 +161,9 @@ impl Vm { } } - fn clone(&mut self, index: usize, position: Span) -> Result { + fn clone(&mut self, index: u8, position: Span) -> Result { + let index = index as usize; + if let Some(register) = self.register_stack.get_mut(index) { let cloneable = if let Some(value) = register.take() { if value.is_raw() { @@ -174,23 +183,15 @@ impl Vm { } } - fn take(&mut self, index: usize, position: Span) -> Result { - if let Some(register) = self.register_stack.get_mut(index) { - if let Some(value) = register.take() { - Ok(value) - } else { - Err(VmError::EmptyRegister { index, position }) - } - } else { - Err(VmError::RegisterIndexOutOfBounds { position }) - } - } - - fn take_or_use_constant(&mut self, index: usize, position: Span) -> Result { - if let Ok(value) = self.take(index, position) { + fn take_constant_or_clone_register( + &mut self, + index: u8, + position: Span, + ) -> Result { + if let Ok(value) = self.chunk.take_constant(index, position) { Ok(value) } else { - let value = self.chunk.take_constant(index, position)?; + let value = self.clone(index, position)?; Ok(value) }