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 })
}
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<Value, ChunkError> {
pub fn take_constant(&mut self, index: u8, position: Span) -> Result<Value, ChunkError> {
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<u16, ChunkError> {
pub fn push_constant(&mut self, value: Value, position: Span) -> Result<u8, ChunkError> {
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<u16, ChunkError> {
) -> Result<u8, ChunkError> {
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<u16, ChunkError> {
) -> Result<u8, ChunkError> {
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(&center(&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.
///

View File

@ -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<u8> for Operation {
fn from(byte: u8) -> Self {
match byte {

View File

@ -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<u16, ParseError> {
) -> Result<u8, ParseError> {
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)?;

View File

@ -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<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) {
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<Value, VmError> {
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<Value, VmError> {
if let Ok(value) = self.take(index, position) {
fn take_constant_or_clone_register(
&mut self,
index: u8,
position: Span,
) -> Result<Value, VmError> {
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)
}