Refactor and debug

This commit is contained in:
Jeff 2024-09-14 21:05:03 -04:00
parent aa8b1215a8
commit ba80774e7b
4 changed files with 156 additions and 118 deletions

View File

@ -60,7 +60,9 @@ impl Chunk {
.ok_or(ChunkError::InstructionUnderflow { position }) .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 self.constants
.get(index) .get(index)
.ok_or(ChunkError::ConstantIndexOutOfBounds { index, position }) .ok_or(ChunkError::ConstantIndexOutOfBounds { index, position })
@ -71,7 +73,9 @@ impl Chunk {
}) })
} }
pub fn take_constant(&mut self, index: usize, position: Span) -> Result<Value, ChunkError> { pub fn take_constant(&mut self, index: u8, position: Span) -> Result<Value, ChunkError> {
let index = index as usize;
self.constants self.constants
.get_mut(index) .get_mut(index)
.ok_or_else(|| ChunkError::ConstantIndexOutOfBounds { index, position })? .ok_or_else(|| ChunkError::ConstantIndexOutOfBounds { index, position })?
@ -79,7 +83,7 @@ impl Chunk {
.ok_or(ChunkError::ConstantAlreadyUsed { index, position }) .ok_or(ChunkError::ConstantAlreadyUsed { index, position })
} }
pub fn push_constant(&mut self, value: Value, position: Span) -> Result<u16, ChunkError> { pub fn push_constant(&mut self, value: Value, position: Span) -> Result<u8, ChunkError> {
let starting_length = self.constants.len(); let starting_length = self.constants.len();
if starting_length + 1 > (u8::MAX as usize) { if starting_length + 1 > (u8::MAX as usize) {
@ -87,17 +91,21 @@ impl Chunk {
} else { } else {
self.constants.push(Some(value)); 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 self.locals
.get(index) .get(index)
.ok_or(ChunkError::LocalIndexOutOfBounds { index, position }) .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) self.locals.get(index).map(|local| &local.identifier)
} }
@ -105,14 +113,14 @@ impl Chunk {
&self, &self,
identifier: &Identifier, identifier: &Identifier,
position: Span, position: Span,
) -> Result<u16, ChunkError> { ) -> Result<u8, ChunkError> {
self.locals self.locals
.iter() .iter()
.enumerate() .enumerate()
.rev() .rev()
.find_map(|(index, local)| { .find_map(|(index, local)| {
if &local.identifier == identifier { if &local.identifier == identifier {
Some(index as u16) Some(index as u8)
} else { } else {
None None
} }
@ -126,17 +134,21 @@ impl Chunk {
pub fn declare_local( pub fn declare_local(
&mut self, &mut self,
identifier: Identifier, identifier: Identifier,
register_index: u8,
position: Span, position: Span,
) -> Result<u16, ChunkError> { ) -> Result<u8, ChunkError> {
let starting_length = self.locals.len(); let starting_length = self.locals.len();
if starting_length + 1 > (u8::MAX as usize) { if starting_length + 1 > (u8::MAX as usize) {
Err(ChunkError::IdentifierOverflow { position }) Err(ChunkError::IdentifierOverflow { position })
} else { } else {
self.locals self.locals.push(Local::new(
.push(Local::new(identifier, self.scope_depth, None)); 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", "Instructions",
"------------", "------------",
"OFFSET OPERATION INFO POSITION", "OFFSET OPERATION INFO POSITION",
"------- -------------- -------------------- --------", "------- -------------- ------------------------- --------",
]; ];
const CONSTANT_HEADER: [&'static str; 5] = [ 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 { pub fn disassemble(&self) -> String {
let center = |line: &str| format!("{line:^width$}\n", width = self.width); let center = |line: &str| format!("{line:^width$}\n", width = self.width);
let style = |line: String| { let style = |line: String| {
@ -298,9 +322,9 @@ impl<'a> ChunkDisassembler<'a> {
let operation = instruction.operation.to_string(); let operation = instruction.operation.to_string();
let info_option = instruction.disassembly_info(Some(self.chunk)); let info_option = instruction.disassembly_info(Some(self.chunk));
let instruction_display = if let Some(info) = info_option { 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 { } else {
format!("{offset:<7} {operation:14} {:20} {position:8}", " ") format!("{offset:<7} {operation:14} {:25} {position:8}", " ")
}; };
disassembled.push_str(&center(&instruction_display)); disassembled.push_str(&center(&instruction_display));
@ -367,18 +391,6 @@ impl<'a> ChunkDisassembler<'a> {
disassembled 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 /// Predicts the capacity of the disassembled output. This is used to pre-allocate the string
/// buffer to avoid reallocations. /// buffer to avoid reallocations.
/// ///

View File

@ -10,26 +10,6 @@ pub struct Instruction {
} }
impl 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 { pub fn r#move(to_register: u8, from_register: u8) -> Instruction {
Instruction { Instruction {
operation: Operation::Move, 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 { Instruction {
operation: Operation::LoadConstant, operation: Operation::LoadConstant,
destination: to_register, 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 { Instruction {
operation: Operation::DeclareLocal, operation: Operation::DeclareLocal,
destination: to_register, 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 { Instruction {
operation: Operation::GetLocal, operation: Operation::GetLocal,
destination: to_register, 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 { Instruction {
operation: Operation::SetLocal, operation: Operation::SetLocal,
destination: from_register, 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::Close => format!("R({})", self.destination),
Operation::LoadConstant => { Operation::LoadConstant => {
let constant_index = u16::from_le_bytes(self.arguments) as usize; let constant_index = self.arguments[0];
if let Some(chunk) = chunk { if let Some(chunk) = chunk {
match chunk.get_constant(constant_index, Span(0, 0)) { match chunk.get_constant(constant_index, Span(0, 0)) {
@ -160,9 +140,9 @@ impl Instruction {
} }
} }
Operation::DeclareLocal => { 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 { 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(), Some(identifier) => identifier.to_string(),
None => "???".to_string(), None => "???".to_string(),
} }
@ -176,14 +156,14 @@ impl Instruction {
) )
} }
Operation::GetLocal => { 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) format!("R({}) = L({})", self.destination, local_index)
} }
Operation::SetLocal => { 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 { 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(), Some(identifier) => identifier.to_string(),
None => "???".to_string(), None => "???".to_string(),
} }
@ -267,6 +247,15 @@ pub enum Operation {
Return = 11, Return = 11,
} }
impl Operation {
pub fn is_binary(&self) -> bool {
matches!(
self,
Operation::Add | Operation::Subtract | Operation::Multiply | Operation::Divide
)
}
}
impl From<u8> for Operation { impl From<u8> for Operation {
fn from(byte: u8) -> Self { fn from(byte: u8) -> Self {
match byte { match byte {

View File

@ -255,8 +255,10 @@ impl<'src> Parser<'src> {
self.parse(rule.precedence.increment())?; self.parse(rule.precedence.increment())?;
let (previous_instruction, position) = self.chunk.pop_instruction(self.current_position)?; let mut push_back_right = false;
let right_register = match previous_instruction { let (right_instruction, right_position) =
self.chunk.pop_instruction(self.current_position)?;
let right_register = match right_instruction {
Instruction { Instruction {
operation: Operation::LoadConstant, operation: Operation::LoadConstant,
arguments, arguments,
@ -267,13 +269,15 @@ impl<'src> Parser<'src> {
arguments[0] arguments[0]
} }
_ => { _ => {
self.chunk.push_instruction(previous_instruction, position); push_back_right = true;
self.current_register - 1 self.current_register - 1
} }
}; };
let (previous_instruction, position) = self.chunk.pop_instruction(self.current_position)?; let mut push_back_left = false;
let left_register = match previous_instruction { let (left_instruction, left_position) =
self.chunk.pop_instruction(self.current_position)?;
let left_register = match left_instruction {
Instruction { Instruction {
operation: Operation::LoadConstant, operation: Operation::LoadConstant,
arguments, arguments,
@ -284,11 +288,21 @@ impl<'src> Parser<'src> {
arguments[0] arguments[0]
} }
_ => { _ => {
self.chunk.push_instruction(previous_instruction, position); push_back_left = true;
self.current_register - 2 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 { let instruction = match operator {
TokenKind::Plus => { TokenKind::Plus => {
Instruction::add(self.current_register, left_register, right_register) 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> { fn parse_named_variable(&mut self, allow_assignment: bool) -> Result<(), ParseError> {
let token = self.previous_token.to_owned(); 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)? { if allow_assignment && self.allow(TokenKind::Equal)? {
self.parse_expression()?; self.parse_expression()?;
self.emit_instruction(
Instruction::set_local(self.current_register, local_index), let (mut previous_instruction, previous_position) =
self.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 { } else {
self.emit_instruction( self.emit_instruction(
Instruction::get_local(self.current_register, local_index), Instruction::get_local(self.current_register, local_index),
self.previous_position, self.previous_position,
); );
self.increment_register()?;
} }
self.increment_register()?;
Ok(()) Ok(())
} }
@ -352,7 +385,7 @@ impl<'src> Parser<'src> {
&mut self, &mut self,
token: TokenOwned, token: TokenOwned,
position: Span, position: Span,
) -> Result<u16, ParseError> { ) -> Result<u8, ParseError> {
if let TokenOwned::Identifier(text) = token { if let TokenOwned::Identifier(text) = token {
let identifier = Identifier::new(text); let identifier = Identifier::new(text);
@ -441,9 +474,12 @@ impl<'src> Parser<'src> {
self.expect(TokenKind::Equal)?; self.expect(TokenKind::Equal)?;
self.parse_expression()?; self.parse_expression()?;
let local_index = self let local_index = self.chunk.declare_local(
.chunk identifier,
.declare_local(identifier, self.current_position)?; self.current_register - 1,
self.current_position,
)?;
let (previous_instruction, previous_position) = let (previous_instruction, previous_position) =
self.chunk.pop_instruction(self.current_position)?; self.chunk.pop_instruction(self.current_position)?;

View File

@ -40,16 +40,16 @@ impl Vm {
match instruction.operation { match instruction.operation {
Operation::Move => { Operation::Move => {
let from = instruction.arguments[0] as usize; let from = instruction.arguments[0];
let to = instruction.destination as usize; let to = instruction.destination;
let value = self.clone(from, position)?; let value = self.clone(from, position)?;
self.insert(value, to, position)?; self.insert(value, to, position)?;
} }
Operation::Close => todo!(), Operation::Close => todo!(),
Operation::LoadConstant => { Operation::LoadConstant => {
let to_register = instruction.destination as usize; let to_register = instruction.destination;
let from_constant = u16::from_le_bytes(instruction.arguments) as usize; let from_constant = instruction.arguments[0];
let value = self.chunk.take_constant(from_constant, position)?; let value = self.chunk.take_constant(from_constant, position)?;
self.insert(value, to_register, position)?; self.insert(value, to_register, position)?;
@ -61,11 +61,11 @@ impl Vm {
self.chunk.define_local(to_local, from_register, position)?; self.chunk.define_local(to_local, from_register, position)?;
} }
Operation::GetLocal => { Operation::GetLocal => {
let register_index = instruction.destination as usize; let register_index = instruction.destination;
let local_index = u16::from_le_bytes(instruction.arguments) as usize; let local_index = instruction.arguments[0];
let local = self.chunk.get_local(local_index, position)?; let local = self.chunk.get_local(local_index, position)?;
let value = if let Some(value_index) = &local.register_index { let value = if let Some(value_index) = &local.register_index {
self.clone(*value_index as usize, position)? self.clone(*value_index, position)?
} else { } else {
return Err(VmError::UndefinedVariable { return Err(VmError::UndefinedVariable {
identifier: local.identifier.clone(), identifier: local.identifier.clone(),
@ -75,59 +75,64 @@ impl Vm {
self.insert(value, register_index, position)?; 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 => { Operation::Add => {
let left = 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 = 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 let sum = left
.add(&right) .add(&right)
.map_err(|error| VmError::Value { error, position })?; .map_err(|error| VmError::Value { error, position })?;
self.insert(sum, instruction.destination as usize, position)?; self.insert(sum, instruction.destination, position)?;
} }
Operation::Subtract => { Operation::Subtract => {
let left = 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 = 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 let difference = left
.subtract(&right) .subtract(&right)
.map_err(|error| VmError::Value { error, position })?; .map_err(|error| VmError::Value { error, position })?;
self.insert(difference, instruction.destination as usize, position)?; self.insert(difference, instruction.destination, position)?;
} }
Operation::Multiply => { Operation::Multiply => {
let left = 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 = 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 let product = left
.multiply(&right) .multiply(&right)
.map_err(|error| VmError::Value { error, position })?; .map_err(|error| VmError::Value { error, position })?;
self.insert(product, instruction.destination as usize, position)?; self.insert(product, instruction.destination, position)?;
} }
Operation::Divide => { Operation::Divide => {
let left = 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 = 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 let quotient = left
.divide(&right) .divide(&right)
.map_err(|error| VmError::Value { error, position })?; .map_err(|error| VmError::Value { error, position })?;
self.insert(quotient, instruction.destination as usize, position)?; self.insert(quotient, instruction.destination, position)?;
} }
Operation::Negate => { Operation::Negate => {
let value = 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 let negated = value
.negate() .negate()
.map_err(|error| VmError::Value { error, position })?; .map_err(|error| VmError::Value { error, position })?;
self.insert(negated, instruction.destination as usize, position)?; self.insert(negated, instruction.destination, position)?;
} }
Operation::Return => { Operation::Return => {
let value = self.pop(position)?; let value = self.pop(position)?;
@ -140,10 +145,12 @@ impl Vm {
Ok(None) 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 { if self.register_stack.len() == Self::STACK_LIMIT {
Err(VmError::StackOverflow { position }) Err(VmError::StackOverflow { position })
} else { } else {
let index = index as usize;
while index >= self.register_stack.len() { while index >= self.register_stack.len() {
self.register_stack.push(None); self.register_stack.push(None);
} }
@ -154,7 +161,9 @@ impl Vm {
} }
} }
fn clone(&mut self, index: usize, position: Span) -> Result<Value, VmError> { fn clone(&mut self, index: u8, position: Span) -> Result<Value, VmError> {
let index = index as usize;
if let Some(register) = self.register_stack.get_mut(index) { if let Some(register) = self.register_stack.get_mut(index) {
let cloneable = if let Some(value) = register.take() { let cloneable = if let Some(value) = register.take() {
if value.is_raw() { if value.is_raw() {
@ -174,23 +183,15 @@ impl Vm {
} }
} }
fn take(&mut self, index: usize, position: Span) -> Result<Value, VmError> { fn take_constant_or_clone_register(
if let Some(register) = self.register_stack.get_mut(index) { &mut self,
if let Some(value) = register.take() { index: u8,
Ok(value) position: Span,
} else { ) -> Result<Value, VmError> {
Err(VmError::EmptyRegister { index, position }) if let Ok(value) = self.chunk.take_constant(index, position) {
}
} else {
Err(VmError::RegisterIndexOutOfBounds { position })
}
}
fn take_or_use_constant(&mut self, index: usize, position: Span) -> Result<Value, VmError> {
if let Ok(value) = self.take(index, position) {
Ok(value) Ok(value)
} else { } else {
let value = self.chunk.take_constant(index, position)?; let value = self.clone(index, position)?;
Ok(value) Ok(value)
} }