From 78c9ed97e2ab13aa708d98ddba6d6203c07a95a9 Mon Sep 17 00:00:00 2001 From: Jeff Date: Thu, 12 Sep 2024 09:11:49 -0400 Subject: [PATCH] Experiment with instruction optimization --- dust-lang/src/chunk.rs | 149 +++++++++++++++++++++------------- dust-lang/src/instruction.rs | 146 ++++++++++++++------------------- dust-lang/src/parser/mod.rs | 109 +++++++++++++++++++------ dust-lang/src/parser/tests.rs | 4 +- dust-lang/src/vm.rs | 80 +++++++++++------- dust-shell/src/main.rs | 2 +- 6 files changed, 291 insertions(+), 199 deletions(-) diff --git a/dust-lang/src/chunk.rs b/dust-lang/src/chunk.rs index 1547166..209d2b4 100644 --- a/dust-lang/src/chunk.rs +++ b/dust-lang/src/chunk.rs @@ -6,64 +6,68 @@ use crate::{AnnotatedError, Identifier, Instruction, Span, Value}; #[derive(Clone)] pub struct Chunk { - code: Vec<(Instruction, Span)>, + instructions: Vec<(Instruction, Span)>, constants: Vec>, - identifiers: Vec, + locals: Vec, scope_depth: usize, } impl Chunk { pub fn new() -> Self { Self { - code: Vec::new(), + instructions: Vec::new(), constants: Vec::new(), - identifiers: Vec::new(), + locals: Vec::new(), scope_depth: 0, } } pub fn with_data( - code: Vec<(Instruction, Span)>, + instructions: Vec<(Instruction, Span)>, constants: Vec, identifiers: Vec, ) -> Self { Self { - code, + instructions, constants: constants.into_iter().map(Some).collect(), - identifiers, + locals: identifiers, scope_depth: 0, } } pub fn len(&self) -> usize { - self.code.len() + self.instructions.len() } pub fn is_empty(&self) -> bool { - self.code.is_empty() + self.instructions.is_empty() } pub fn scope_depth(&self) -> usize { self.scope_depth } - pub fn get_code( + pub fn get_instruction( &self, offset: usize, position: Span, ) -> Result<&(Instruction, Span), ChunkError> { - self.code + self.instructions .get(offset) .ok_or(ChunkError::CodeIndexOfBounds { offset, position }) } - pub fn push_code(&mut self, instruction: Instruction, position: Span) { - self.code.push((instruction, position)); + pub fn push_instruction(&mut self, instruction: Instruction, position: Span) { + self.instructions.push((instruction, position)); } - pub fn get_constant(&self, index: u16, position: Span) -> Result<&Value, ChunkError> { + pub fn pop_instruction(&mut self) -> Option<(Instruction, Span)> { + self.instructions.pop() + } + + pub fn get_constant(&self, index: usize, position: Span) -> Result<&Value, ChunkError> { self.constants - .get(index as usize) + .get(index) .ok_or(ChunkError::ConstantIndexOutOfBounds { index, position }) .and_then(|value| { value @@ -72,9 +76,9 @@ impl Chunk { }) } - pub fn use_constant(&mut self, index: u16, position: Span) -> Result { + pub fn use_constant(&mut self, index: usize, position: Span) -> Result { self.constants - .get_mut(index as usize) + .get_mut(index) .ok_or_else(|| ChunkError::ConstantIndexOutOfBounds { index, position })? .take() .ok_or(ChunkError::ConstantAlreadyUsed { index, position }) @@ -93,31 +97,31 @@ impl Chunk { } pub fn contains_identifier(&self, identifier: &Identifier) -> bool { - self.identifiers + self.locals .iter() .any(|local| &local.identifier == identifier) } - pub fn get_local(&self, index: u16, position: Span) -> Result<&Local, ChunkError> { - self.identifiers + pub fn get_local(&self, index: usize, position: Span) -> Result<&Local, ChunkError> { + self.locals .get(index as usize) - .ok_or(ChunkError::IdentifierIndexOutOfBounds { index, position }) + .ok_or(ChunkError::LocalIndexOutOfBounds { index, position }) } pub fn get_identifier(&self, index: u8) -> Option<&Identifier> { - if let Some(local) = self.identifiers.get(index as usize) { + if let Some(local) = self.locals.get(index as usize) { Some(&local.identifier) } else { None } } - pub fn get_identifier_index( + pub fn get_local_index( &self, identifier: &Identifier, position: Span, ) -> Result { - self.identifiers + self.locals .iter() .rev() .enumerate() @@ -134,25 +138,39 @@ impl Chunk { }) } - pub fn declare_variable( + pub fn declare_local( &mut self, identifier: Identifier, position: Span, ) -> Result { - let starting_length = self.identifiers.len(); + let starting_length = self.locals.len(); if starting_length + 1 > (u8::MAX as usize) { Err(ChunkError::IdentifierOverflow { position }) } else { - self.identifiers.push(Local { - identifier, - depth: self.scope_depth, - }); + self.locals + .push(Local::new(identifier, self.scope_depth, None)); Ok(starting_length as u16) } } + pub fn define_local( + &mut self, + index: usize, + value: Value, + position: Span, + ) -> Result<(), ChunkError> { + let local = self + .locals + .get_mut(index) + .ok_or_else(|| ChunkError::LocalIndexOutOfBounds { index, position })?; + + local.value = Some(value); + + Ok(()) + } + pub fn begin_scope(&mut self) { self.scope_depth += 1; } @@ -162,17 +180,17 @@ impl Chunk { } pub fn clear(&mut self) { - self.code.clear(); + self.instructions.clear(); self.constants.clear(); - self.identifiers.clear(); + self.locals.clear(); } pub fn identifiers(&self) -> &[Local] { - &self.identifiers + &self.locals } pub fn pop_identifier(&mut self) -> Option { - self.identifiers.pop() + self.locals.pop() } pub fn disassemble(&self, name: &str) -> String { @@ -186,13 +204,13 @@ impl Chunk { output.push_str(&format!("{name_buffer}{name}{name_buffer}\n")); output.push_str(&format!("{name_buffer}{underline}{name_buffer}\n",)); output.push_str(" Code \n"); - output.push_str("------ ---------------- ------------------ --------\n"); - output.push_str("OFFSET INSTRUCTION INFO POSITION\n"); - output.push_str("------ ---------------- ------------------ --------\n"); + output.push_str("------ ---------------- -------------------- --------\n"); + output.push_str("OFFSET INSTRUCTION INFO POSITION\n"); + output.push_str("------ ---------------- -------------------- --------\n"); - for (offset, (instruction, position)) in self.code.iter().enumerate() { + for (offset, (instruction, position)) in self.instructions.iter().enumerate() { let display = format!( - "{offset:^6} {:35} {position}\n", + "{offset:^6} {:37} {position}\n", instruction.disassemble(self) ); @@ -220,13 +238,29 @@ impl Chunk { output.push_str(&display); } - output.push_str("\n Identifiers\n"); - output.push_str("----- ---------- -----\n"); - output.push_str("INDEX NAME DEPTH\n"); - output.push_str("----- ---------- -----\n"); + output.push_str("\n Locals\n"); + output.push_str("----- ---------- ----- -----\n"); + output.push_str("INDEX NAME DEPTH VALUE\n"); + output.push_str("----- ---------- ----- -----\n"); - for (index, Local { identifier, depth }) in self.identifiers.iter().enumerate() { - let display = format!("{index:3} {:10} {depth}\n", identifier.as_str()); + for ( + index, + Local { + identifier, + depth, + value, + }, + ) in self.locals.iter().enumerate() + { + let value_display = value + .as_ref() + .map(|value| value.to_string()) + .unwrap_or_else(|| "EMPTY".to_string()); + + let display = format!( + "{index:3} {:10} {depth:<5} {value_display}\n", + identifier.as_str() + ); output.push_str(&display); } @@ -256,9 +290,9 @@ impl Eq for Chunk {} impl PartialEq for Chunk { fn eq(&self, other: &Self) -> bool { - self.code == other.code + self.instructions == other.instructions && self.constants == other.constants - && self.identifiers == other.identifiers + && self.locals == other.locals } } @@ -266,11 +300,16 @@ impl PartialEq for Chunk { pub struct Local { pub identifier: Identifier, pub depth: usize, + pub value: Option, } impl Local { - pub fn new(identifier: Identifier, depth: usize) -> Self { - Self { identifier, depth } + pub fn new(identifier: Identifier, depth: usize, value: Option) -> Self { + Self { + identifier, + depth, + value, + } } } @@ -281,18 +320,18 @@ pub enum ChunkError { position: Span, }, ConstantAlreadyUsed { - index: u16, + index: usize, position: Span, }, ConstantOverflow { position: Span, }, ConstantIndexOutOfBounds { - index: u16, + index: usize, position: Span, }, - IdentifierIndexOutOfBounds { - index: u16, + LocalIndexOutOfBounds { + index: usize, position: Span, }, IdentifierOverflow { @@ -315,7 +354,7 @@ impl AnnotatedError for ChunkError { ChunkError::ConstantAlreadyUsed { .. } => "Constant already used", ChunkError::ConstantOverflow { .. } => "Constant overflow", ChunkError::ConstantIndexOutOfBounds { .. } => "Constant index out of bounds", - ChunkError::IdentifierIndexOutOfBounds { .. } => "Identifier index out of bounds", + ChunkError::LocalIndexOutOfBounds { .. } => "Identifier index out of bounds", ChunkError::IdentifierOverflow { .. } => "Identifier overflow", ChunkError::IdentifierNotFound { .. } => "Identifier not found", } @@ -330,7 +369,7 @@ impl AnnotatedError for ChunkError { ChunkError::ConstantIndexOutOfBounds { index, .. } => { Some(format!("Constant index: {}", index)) } - ChunkError::IdentifierIndexOutOfBounds { index, .. } => { + ChunkError::LocalIndexOutOfBounds { index, .. } => { Some(format!("Identifier index: {}", index)) } ChunkError::IdentifierNotFound { identifier, .. } => { diff --git a/dust-lang/src/instruction.rs b/dust-lang/src/instruction.rs index b7d5e55..71071cc 100644 --- a/dust-lang/src/instruction.rs +++ b/dust-lang/src/instruction.rs @@ -5,7 +5,7 @@ use crate::{Chunk, Span}; #[derive(Clone, Copy, Debug, PartialEq)] pub struct Instruction { pub operation: Operation, - pub to_register: u8, + pub destination: u8, pub arguments: [u8; 2], } @@ -17,15 +17,15 @@ impl Instruction { Instruction { operation, - to_register, + destination: to_register, arguments, } } pub fn encode(&self) -> u32 { - let operation = u32::from(self.operation as u8); - let to_register = u32::from(self.to_register); - let arguments = u32::from(self.arguments[0]) << 8 | u32::from(self.arguments[1]); + 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 } @@ -33,7 +33,7 @@ impl Instruction { pub fn r#move(to_register: u8, from_register: u8) -> Instruction { Instruction { operation: Operation::Move, - to_register, + destination: to_register, arguments: [from_register, 0], } } @@ -41,7 +41,7 @@ impl Instruction { pub fn close(to_register: u8) -> Instruction { Instruction { operation: Operation::Close, - to_register, + destination: to_register, arguments: [0, 0], } } @@ -49,7 +49,7 @@ impl Instruction { pub fn load_constant(to_register: u8, constant_index: u16) -> Instruction { Instruction { operation: Operation::LoadConstant, - to_register, + destination: to_register, arguments: constant_index.to_le_bytes(), } } @@ -57,7 +57,7 @@ impl Instruction { pub fn declare_variable(to_register: u8, variable_index: u16) -> Instruction { Instruction { operation: Operation::DeclareVariable, - to_register, + destination: to_register, arguments: variable_index.to_le_bytes(), } } @@ -65,15 +65,15 @@ impl Instruction { pub fn get_variable(to_register: u8, variable_index: u16) -> Instruction { Instruction { operation: Operation::GetVariable, - to_register, + destination: to_register, arguments: variable_index.to_le_bytes(), } } - pub fn set_variable(from_register: u8, variable_index: u16) -> Instruction { + pub fn set_local(from_register: u8, variable_index: u16) -> Instruction { Instruction { operation: Operation::SetVariable, - to_register: from_register, + destination: from_register, arguments: variable_index.to_le_bytes(), } } @@ -81,7 +81,7 @@ impl Instruction { pub fn add(to_register: u8, left_register: u8, right_register: u8) -> Instruction { Instruction { operation: Operation::Add, - to_register, + destination: to_register, arguments: [left_register, right_register], } } @@ -89,7 +89,7 @@ impl Instruction { pub fn subtract(to_register: u8, left_register: u8, right_register: u8) -> Instruction { Instruction { operation: Operation::Subtract, - to_register, + destination: to_register, arguments: [left_register, right_register], } } @@ -97,7 +97,7 @@ impl Instruction { pub fn multiply(to_register: u8, left_register: u8, right_register: u8) -> Instruction { Instruction { operation: Operation::Multiply, - to_register, + destination: to_register, arguments: [left_register, right_register], } } @@ -105,7 +105,7 @@ impl Instruction { pub fn divide(to_register: u8, left_register: u8, right_register: u8) -> Instruction { Instruction { operation: Operation::Divide, - to_register, + destination: to_register, arguments: [left_register, right_register], } } @@ -113,7 +113,7 @@ impl Instruction { pub fn negate(to_register: u8, from_register: u8) -> Instruction { Instruction { operation: Operation::Negate, - to_register, + destination: to_register, arguments: [from_register, 0], } } @@ -121,7 +121,7 @@ impl Instruction { pub fn r#return() -> Instruction { Instruction { operation: Operation::Return, - to_register: 0, + destination: 0, arguments: [0, 0], } } @@ -132,34 +132,34 @@ impl Instruction { format!( "{:16} R({}) R({})", self.operation.to_string(), - self.to_register, + self.destination, self.arguments[0] ) } - Operation::Close => format!("{} R({})", self.operation, self.to_register), + Operation::Close => format!("{:16} R({})", self.operation, self.destination), Operation::LoadConstant => { - let constant_index = u16::from_le_bytes(self.arguments); + let constant_index = u16::from_le_bytes(self.arguments) as usize; let constant_display = match chunk.get_constant(constant_index, Span(0, 0)) { Ok(value) => value.to_string(), Err(error) => format!("{:?}", error), }; format!( - "{:16} R({}) = C({}) {} ", + "{:16} R({}) = C({}) {}", self.operation.to_string(), - self.to_register, + self.destination, constant_index, constant_display ) } Operation::DeclareVariable => { - let identifier_index = u16::from_le_bytes([self.arguments[0], self.arguments[1]]); + let local_index = u16::from_le_bytes([self.arguments[0], self.arguments[1]]); format!( - "{:16} R[C({})] = R({})", + "{:16} L({}) = R({})", self.operation.to_string(), - identifier_index, - self.to_register + local_index, + self.destination ) } Operation::GetVariable => { @@ -168,7 +168,7 @@ impl Instruction { format!( "{:16} R{} = R[I({})]", self.operation.to_string(), - self.to_register, + self.destination, identifier_index ) } @@ -179,50 +179,50 @@ impl Instruction { "{:16} R[C({})] = R({})", self.operation.to_string(), identifier_index, - self.to_register + self.destination ) } Operation::Add => { format!( - "{:16} R({}) = R({}) + R({})", + "{:16} R({}) = RC({}) + RC({})", self.operation.to_string(), - self.to_register, + self.destination, self.arguments[0], self.arguments[1] ) } Operation::Subtract => { format!( - "{:16} R({}) = R({}) - R({})", + "{:16} R({}) = RC({}) - RC({})", self.operation.to_string(), - self.to_register, + self.destination, self.arguments[0], self.arguments[1] ) } Operation::Multiply => { format!( - "{:16} R({}) = R({}) * R({})", + "{:16} R({}) = RC({}) * RC({})", self.operation.to_string(), - self.to_register, + self.destination, self.arguments[0], self.arguments[1] ) } Operation::Divide => { format!( - "{:16} R({}) = R({}) / R({})", + "{:16} R({}) = RC({}) / RC({})", self.operation.to_string(), - self.to_register, + self.destination, self.arguments[0], self.arguments[1] ) } Operation::Negate => { format!( - "{:16} R({}) = -R({})", + "{:16} R({}) = -RC({})", self.operation.to_string(), - self.to_register, + self.destination, self.arguments[0] ) } @@ -239,22 +239,18 @@ impl Display for Instruction { Operation::Move => { write!( f, - "{:16} R({}) R({})", - self.operation.to_string(), - self.to_register, - self.arguments[0] + "{} R({}) R({})", + self.operation, self.destination, self.arguments[0] ) } - Operation::Close => write!(f, "{} R({})", self.operation, self.to_register), + Operation::Close => write!(f, "{} R({})", self.operation, self.destination), Operation::LoadConstant => { let constant_index = u16::from_le_bytes(self.arguments); write!( f, - "{:16} R({}) C({})", - self.operation.to_string(), - self.to_register, - constant_index + "{} R({}) = C({})", + self.operation, self.destination, constant_index ) } Operation::DeclareVariable => { @@ -262,10 +258,8 @@ impl Display for Instruction { write!( f, - "{:16} R[C({})] = R({})", - self.operation.to_string(), - identifier_index, - self.to_register + "{} L({}) = R({})", + self.operation, identifier_index, self.destination ) } Operation::GetVariable => { @@ -273,10 +267,8 @@ impl Display for Instruction { write!( f, - "{:16} R{} = R[I({})]", - self.operation.to_string(), - self.to_register, - identifier_index + "{} R{} = R[I({})]", + self.operation, self.destination, identifier_index ) } Operation::SetVariable => { @@ -284,63 +276,47 @@ impl Display for Instruction { write!( f, - "{:16} R[C({})] = R({})", - self.operation.to_string(), - identifier_index, - self.to_register + "{} R[C({})] = R({})", + self.operation, identifier_index, self.destination ) } Operation::Add => { write!( f, - "{:16} R({}) = R({}) + R({})", - self.operation.to_string(), - self.to_register, - self.arguments[0], - self.arguments[1] + "{} R({}) = RC({}) + RC({})", + self.operation, self.destination, self.arguments[0], self.arguments[1] ) } Operation::Subtract => { write!( f, - "{:16} R({}) = R({}) - R({})", - self.operation.to_string(), - self.to_register, - self.arguments[0], - self.arguments[1] + "{} R({}) = RC({}) - RC({})", + self.operation, self.destination, self.arguments[0], self.arguments[1] ) } Operation::Multiply => { write!( f, - "{:16} R({}) = R({}) * R({})", - self.operation.to_string(), - self.to_register, - self.arguments[0], - self.arguments[1] + "{} R({}) = RC({}) * RC({})", + self.operation, self.destination, self.arguments[0], self.arguments[1] ) } Operation::Divide => { write!( f, - "{:16} R({}) = R({}) / R({})", - self.operation.to_string(), - self.to_register, - self.arguments[0], - self.arguments[1] + "{} R({}) = RC({}) / RC({})", + self.operation, self.destination, self.arguments[0], self.arguments[1] ) } Operation::Negate => { write!( f, - "{:16} R({}) = -R({})", - self.operation.to_string(), - self.to_register, - self.arguments[0] + "{} R({}) = -RC({})", + self.operation, self.destination, self.arguments[0] ) } Operation::Return => { - write!(f, "{:16}", self.operation.to_string()) + write!(f, "{}", self.operation) } } } diff --git a/dust-lang/src/parser/mod.rs b/dust-lang/src/parser/mod.rs index 298bc71..7356b51 100644 --- a/dust-lang/src/parser/mod.rs +++ b/dust-lang/src/parser/mod.rs @@ -9,7 +9,7 @@ use std::{ use crate::{ dust_error::AnnotatedError, Chunk, ChunkError, DustError, Identifier, Instruction, LexError, - Lexer, Span, Token, TokenKind, TokenOwned, Value, + Lexer, Operation, Span, Token, TokenKind, TokenOwned, Value, }; pub fn parse(source: &str) -> Result { @@ -75,6 +75,20 @@ impl<'src> Parser<'src> { } } + fn decrement_register(&mut self) -> Result<(), ParseError> { + let current = self.current_register; + + if current == 0 { + Err(ParseError::RegisterUnderflow { + position: self.current_position, + }) + } else { + self.current_register -= 1; + + Ok(()) + } + } + fn advance(&mut self) -> Result<(), ParseError> { if self.is_eof() { return Ok(()); @@ -113,7 +127,7 @@ impl<'src> Parser<'src> { } fn emit_instruction(&mut self, instruction: Instruction, position: Span) { - self.chunk.push_code(instruction, position); + self.chunk.push_instruction(instruction, position); } fn emit_constant(&mut self, value: Value) -> Result<(), ParseError> { @@ -241,18 +255,61 @@ impl<'src> Parser<'src> { self.parse(rule.precedence.increment())?; - let to_register = if self.current_register < 2 { - self.current_register + 2 - } else { - self.current_register + let previous_instruction = self.chunk.pop_instruction(); + let right_register = match previous_instruction { + Some(( + Instruction { + operation: Operation::LoadConstant, + arguments, + .. + }, + _, + )) => { + self.decrement_register()?; + + arguments[0] + } + Some((instruction, position)) => { + self.chunk.push_instruction(instruction, position); + + self.current_register - 1 + } + _ => self.current_register - 1, }; - let left_register = to_register - 2; - let right_register = to_register - 1; - let byte = match operator { - TokenKind::Plus => Instruction::add(to_register, left_register, right_register), - TokenKind::Minus => Instruction::subtract(to_register, left_register, right_register), - TokenKind::Star => Instruction::multiply(to_register, left_register, right_register), - TokenKind::Slash => Instruction::divide(to_register, left_register, right_register), + let last_instruction = self.chunk.pop_instruction(); + let left_register = match last_instruction { + Some(( + Instruction { + operation: Operation::LoadConstant, + arguments, + .. + }, + _, + )) => { + self.decrement_register()?; + + arguments[0] + } + Some((instruction, position)) => { + self.chunk.push_instruction(instruction, position); + + self.current_register - 2 + } + _ => self.current_register - 2, + }; + let instruction = match operator { + TokenKind::Plus => { + Instruction::add(self.current_register, left_register, right_register) + } + TokenKind::Minus => { + Instruction::subtract(self.current_register, left_register, right_register) + } + TokenKind::Star => { + Instruction::multiply(self.current_register, left_register, right_register) + } + TokenKind::Slash => { + Instruction::divide(self.current_register, left_register, right_register) + } _ => { return Err(ParseError::ExpectedTokenMultiple { expected: vec![ @@ -268,7 +325,7 @@ impl<'src> Parser<'src> { }; self.increment_register()?; - self.emit_instruction(byte, operator_position); + self.emit_instruction(instruction, operator_position); Ok(()) } @@ -279,21 +336,16 @@ impl<'src> Parser<'src> { fn parse_named_variable(&mut self, allow_assignment: bool) -> Result<(), ParseError> { let token = self.previous_token.to_owned(); - let identifier_index = self.parse_identifier_from(token, self.previous_position)?; + let local_index = self.parse_identifier_from(token, self.previous_position)?; if allow_assignment && self.allow(TokenKind::Equal)? { self.parse_expression()?; self.emit_instruction( - Instruction::set_variable(self.current_register, identifier_index), + Instruction::set_local(self.current_register, local_index), self.previous_position, ); self.increment_register()?; - } else { - self.emit_instruction( - Instruction::get_variable(self.current_register - 1, identifier_index), - self.previous_position, - ); } Ok(()) @@ -307,8 +359,8 @@ impl<'src> Parser<'src> { if let TokenOwned::Identifier(text) = token { let identifier = Identifier::new(text); - if let Ok(identifier_index) = self.chunk.get_identifier_index(&identifier, position) { - Ok(identifier_index) + if let Ok(local_index) = self.chunk.get_local_index(&identifier, position) { + Ok(local_index) } else { Err(ParseError::UndefinedVariable { identifier, @@ -392,13 +444,12 @@ impl<'src> Parser<'src> { self.expect(TokenKind::Equal)?; self.parse_expression()?; - let identifier_index = self.chunk.declare_variable(identifier, position)?; + let local_index = self.chunk.declare_local(identifier, position)?; self.emit_instruction( - Instruction::declare_variable(self.current_register, identifier_index), + Instruction::declare_variable(self.current_register - 1, local_index), position, ); - self.increment_register()?; Ok(()) } @@ -661,6 +712,9 @@ pub enum ParseError { RegisterOverflow { position: Span, }, + RegisterUnderflow { + position: Span, + }, // Wrappers around foreign errors Chunk(ChunkError), @@ -694,6 +748,7 @@ impl AnnotatedError for ParseError { Self::InvalidAssignmentTarget { .. } => "Invalid assignment target", Self::UndefinedVariable { .. } => "Undefined variable", Self::RegisterOverflow { .. } => "Register overflow", + Self::RegisterUnderflow { .. } => "Register underflow", Self::Chunk { .. } => "Chunk error", Self::Lex(_) => "Lex error", Self::ParseFloatError { .. } => "Failed to parse float", @@ -717,6 +772,7 @@ impl AnnotatedError for ParseError { Some(format!("Undefined variable \"{identifier}\"")) } Self::RegisterOverflow { .. } => None, + Self::RegisterUnderflow { .. } => None, Self::Chunk(error) => error.details(), Self::Lex(error) => error.details(), Self::ParseFloatError { error, .. } => Some(error.to_string()), @@ -732,6 +788,7 @@ impl AnnotatedError for ParseError { Self::InvalidAssignmentTarget { position, .. } => *position, Self::UndefinedVariable { position, .. } => *position, Self::RegisterOverflow { position } => *position, + Self::RegisterUnderflow { position } => *position, Self::Chunk(error) => error.position(), Self::Lex(error) => error.position(), Self::ParseFloatError { position, .. } => *position, diff --git a/dust-lang/src/parser/tests.rs b/dust-lang/src/parser/tests.rs index cc13fb2..bcbfa44 100644 --- a/dust-lang/src/parser/tests.rs +++ b/dust-lang/src/parser/tests.rs @@ -12,8 +12,8 @@ fn let_statement() { (Instruction::declare_variable(1, 0), Span(4, 5)), ], vec![Value::integer(42),], - vec![Local::new(Identifier::new("x"), 0)] - )) + vec![Local::new(Identifier::new("x"), 0, None)] + )), ); } diff --git a/dust-lang/src/vm.rs b/dust-lang/src/vm.rs index 3ee3e55..64c4692 100644 --- a/dust-lang/src/vm.rs +++ b/dust-lang/src/vm.rs @@ -38,69 +38,77 @@ impl Vm { Operation::Move => todo!(), Operation::Close => todo!(), Operation::LoadConstant => { - let register_index = instruction.to_register as usize; - let constant_index = u16::from_le_bytes(instruction.arguments); + let constant_index = u16::from_le_bytes(instruction.arguments) as usize; let value = self.chunk.use_constant(constant_index, position)?; - self.insert(value, register_index, position)?; + self.insert(value, instruction.destination as usize, position)?; } Operation::DeclareVariable => { - let register_index = instruction.to_register as usize; - let identifier_index = u16::from_le_bytes(instruction.arguments) as usize; - let value = self.take(identifier_index, position)?; + let register_index = instruction.destination as usize; + let local_index = u16::from_le_bytes(instruction.arguments) as usize; + let value = self.clone(register_index, position)?; - self.insert(value, register_index, position)?; + self.chunk.define_local(local_index, value, position)?; } Operation::GetVariable => { let identifier_index = u16::from_le_bytes(instruction.arguments) as usize; - let value = self.take(identifier_index, position)?; + let value = self.clone(identifier_index, position)?; self.insert(value, identifier_index, position)?; } Operation::SetVariable => todo!(), Operation::Add => { - let left = self.take(instruction.arguments[0] as usize, position)?; - let right = self.take(instruction.arguments[1] as usize, position)?; + let left = + self.take_or_use_constant(instruction.arguments[0] as usize, position)?; + let right = + self.take_or_use_constant(instruction.arguments[1] as usize, position)?; let sum = left .add(&right) .map_err(|error| VmError::Value { error, position })?; - self.insert(sum, instruction.to_register as usize, position)?; + self.insert(sum, instruction.destination as usize, position)?; } Operation::Subtract => { - let left = self.take(instruction.arguments[0] as usize, position)?; - let right = self.take(instruction.arguments[1] as usize, position)?; + let left = + self.take_or_use_constant(instruction.arguments[0] as usize, position)?; + let right = + self.take_or_use_constant(instruction.arguments[1] as usize, position)?; let difference = left .subtract(&right) .map_err(|error| VmError::Value { error, position })?; - self.insert(difference, instruction.to_register as usize, position)?; + self.insert(difference, instruction.destination as usize, position)?; } Operation::Multiply => { - let left = self.take(instruction.arguments[0] as usize, position)?; - let right = self.take(instruction.arguments[1] as usize, position)?; + let left = + self.take_or_use_constant(instruction.arguments[0] as usize, position)?; + let right = + self.take_or_use_constant(instruction.arguments[1] as usize, position)?; let product = left .multiply(&right) .map_err(|error| VmError::Value { error, position })?; - self.insert(product, instruction.to_register as usize, position)?; + self.insert(product, instruction.destination as usize, position)?; } Operation::Divide => { - let left = self.take(instruction.arguments[0] as usize, position)?; - let right = self.take(instruction.arguments[1] as usize, position)?; + let left = + self.take_or_use_constant(instruction.arguments[0] as usize, position)?; + let right = + self.take_or_use_constant(instruction.arguments[1] as usize, position)?; let quotient = left .divide(&right) .map_err(|error| VmError::Value { error, position })?; - self.insert(quotient, instruction.to_register as usize, position)?; + self.insert(quotient, instruction.destination as usize, position)?; } Operation::Negate => { - let value = self.take(instruction.arguments[0] as usize, position)?; + let value = + self.take_or_use_constant(instruction.arguments[0] as usize, position)?; let negated = value .negate() .map_err(|error| VmError::Value { error, position })?; - self.insert(negated, instruction.to_register as usize, position)?; + self.insert(negated, instruction.destination as usize, position)?; } Operation::Return => { let value = self.pop(position)?; @@ -117,21 +125,23 @@ impl Vm { if self.register_stack.len() == Self::STACK_LIMIT { Err(VmError::StackOverflow { position }) } else { - if index == self.register_stack.len() { - self.register_stack.push(Some(value)); - } else { - self.register_stack[index] = Some(value); + while index >= self.register_stack.len() { + self.register_stack.push(None); } + self.register_stack[index] = Some(value); + Ok(()) } } - fn take(&mut self, index: usize, position: Span) -> Result { + fn clone(&mut self, index: usize, position: Span) -> Result { if let Some(register) = self.register_stack.get_mut(index) { let value = register - .clone() - .ok_or(VmError::EmptyRegister { index, position })?; + .take() + .ok_or(VmError::EmptyRegister { index, position })? + .into_reference(); + let _ = register.insert(value.clone()); Ok(value) } else { @@ -139,6 +149,16 @@ impl Vm { } } + fn take_or_use_constant(&mut self, index: usize, position: Span) -> Result { + if let Ok(value) = self.clone(index, position) { + Ok(value) + } else { + let value = self.chunk.use_constant(index, position)?; + + Ok(value) + } + } + fn pop(&mut self, position: Span) -> Result { if let Some(register) = self.register_stack.pop() { let value = register.ok_or(VmError::EmptyRegister { @@ -153,7 +173,7 @@ impl Vm { } fn read(&mut self, position: Span) -> Result<&(Instruction, Span), VmError> { - let current = self.chunk.get_code(self.ip, position)?; + let current = self.chunk.get_instruction(self.ip, position)?; self.ip += 1; diff --git a/dust-shell/src/main.rs b/dust-shell/src/main.rs index 66bdaf3..6866e94 100644 --- a/dust-shell/src/main.rs +++ b/dust-shell/src/main.rs @@ -62,7 +62,7 @@ fn parse_and_display_errors(source: &str) { match parse(source) { Ok(chunk) => println!("{}", chunk.disassemble("Dust CLI Input")), Err(error) => { - eprintln!("{:?}", error); + eprintln!("{}", error.report()); } } }