1
0

Experiment with instruction optimization

This commit is contained in:
Jeff 2024-09-12 09:11:49 -04:00
parent 6ff25a22ec
commit 78c9ed97e2
6 changed files with 291 additions and 199 deletions

View File

@ -6,64 +6,68 @@ use crate::{AnnotatedError, Identifier, Instruction, Span, Value};
#[derive(Clone)] #[derive(Clone)]
pub struct Chunk { pub struct Chunk {
code: Vec<(Instruction, Span)>, instructions: Vec<(Instruction, Span)>,
constants: Vec<Option<Value>>, constants: Vec<Option<Value>>,
identifiers: Vec<Local>, locals: Vec<Local>,
scope_depth: usize, scope_depth: usize,
} }
impl Chunk { impl Chunk {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
code: Vec::new(), instructions: Vec::new(),
constants: Vec::new(), constants: Vec::new(),
identifiers: Vec::new(), locals: Vec::new(),
scope_depth: 0, scope_depth: 0,
} }
} }
pub fn with_data( pub fn with_data(
code: Vec<(Instruction, Span)>, instructions: Vec<(Instruction, Span)>,
constants: Vec<Value>, constants: Vec<Value>,
identifiers: Vec<Local>, identifiers: Vec<Local>,
) -> Self { ) -> Self {
Self { Self {
code, instructions,
constants: constants.into_iter().map(Some).collect(), constants: constants.into_iter().map(Some).collect(),
identifiers, locals: identifiers,
scope_depth: 0, scope_depth: 0,
} }
} }
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
self.code.len() self.instructions.len()
} }
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
self.code.is_empty() self.instructions.is_empty()
} }
pub fn scope_depth(&self) -> usize { pub fn scope_depth(&self) -> usize {
self.scope_depth self.scope_depth
} }
pub fn get_code( pub fn get_instruction(
&self, &self,
offset: usize, offset: usize,
position: Span, position: Span,
) -> Result<&(Instruction, Span), ChunkError> { ) -> Result<&(Instruction, Span), ChunkError> {
self.code self.instructions
.get(offset) .get(offset)
.ok_or(ChunkError::CodeIndexOfBounds { offset, position }) .ok_or(ChunkError::CodeIndexOfBounds { offset, position })
} }
pub fn push_code(&mut self, instruction: Instruction, position: Span) { pub fn push_instruction(&mut self, instruction: Instruction, position: Span) {
self.code.push((instruction, position)); 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 self.constants
.get(index as usize) .get(index)
.ok_or(ChunkError::ConstantIndexOutOfBounds { index, position }) .ok_or(ChunkError::ConstantIndexOutOfBounds { index, position })
.and_then(|value| { .and_then(|value| {
value value
@ -72,9 +76,9 @@ impl Chunk {
}) })
} }
pub fn use_constant(&mut self, index: u16, position: Span) -> Result<Value, ChunkError> { pub fn use_constant(&mut self, index: usize, position: Span) -> Result<Value, ChunkError> {
self.constants self.constants
.get_mut(index as usize) .get_mut(index)
.ok_or_else(|| ChunkError::ConstantIndexOutOfBounds { index, position })? .ok_or_else(|| ChunkError::ConstantIndexOutOfBounds { index, position })?
.take() .take()
.ok_or(ChunkError::ConstantAlreadyUsed { index, position }) .ok_or(ChunkError::ConstantAlreadyUsed { index, position })
@ -93,31 +97,31 @@ impl Chunk {
} }
pub fn contains_identifier(&self, identifier: &Identifier) -> bool { pub fn contains_identifier(&self, identifier: &Identifier) -> bool {
self.identifiers self.locals
.iter() .iter()
.any(|local| &local.identifier == identifier) .any(|local| &local.identifier == identifier)
} }
pub fn get_local(&self, index: u16, position: Span) -> Result<&Local, ChunkError> { pub fn get_local(&self, index: usize, position: Span) -> Result<&Local, ChunkError> {
self.identifiers self.locals
.get(index as usize) .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> { 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) Some(&local.identifier)
} else { } else {
None None
} }
} }
pub fn get_identifier_index( pub fn get_local_index(
&self, &self,
identifier: &Identifier, identifier: &Identifier,
position: Span, position: Span,
) -> Result<u16, ChunkError> { ) -> Result<u16, ChunkError> {
self.identifiers self.locals
.iter() .iter()
.rev() .rev()
.enumerate() .enumerate()
@ -134,25 +138,39 @@ impl Chunk {
}) })
} }
pub fn declare_variable( pub fn declare_local(
&mut self, &mut self,
identifier: Identifier, identifier: Identifier,
position: Span, position: Span,
) -> Result<u16, ChunkError> { ) -> Result<u16, ChunkError> {
let starting_length = self.identifiers.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.identifiers.push(Local { self.locals
identifier, .push(Local::new(identifier, self.scope_depth, None));
depth: self.scope_depth,
});
Ok(starting_length as u16) 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) { pub fn begin_scope(&mut self) {
self.scope_depth += 1; self.scope_depth += 1;
} }
@ -162,17 +180,17 @@ impl Chunk {
} }
pub fn clear(&mut self) { pub fn clear(&mut self) {
self.code.clear(); self.instructions.clear();
self.constants.clear(); self.constants.clear();
self.identifiers.clear(); self.locals.clear();
} }
pub fn identifiers(&self) -> &[Local] { pub fn identifiers(&self) -> &[Local] {
&self.identifiers &self.locals
} }
pub fn pop_identifier(&mut self) -> Option<Local> { pub fn pop_identifier(&mut self) -> Option<Local> {
self.identifiers.pop() self.locals.pop()
} }
pub fn disassemble(&self, name: &str) -> String { 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}{name}{name_buffer}\n"));
output.push_str(&format!("{name_buffer}{underline}{name_buffer}\n",)); output.push_str(&format!("{name_buffer}{underline}{name_buffer}\n",));
output.push_str(" Code \n"); output.push_str(" Code \n");
output.push_str("------ ---------------- ------------------ --------\n"); output.push_str("------ ---------------- -------------------- --------\n");
output.push_str("OFFSET INSTRUCTION INFO POSITION\n"); output.push_str("OFFSET INSTRUCTION INFO POSITION\n");
output.push_str("------ ---------------- ------------------ --------\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!( let display = format!(
"{offset:^6} {:35} {position}\n", "{offset:^6} {:37} {position}\n",
instruction.disassemble(self) instruction.disassemble(self)
); );
@ -220,13 +238,29 @@ impl Chunk {
output.push_str(&display); output.push_str(&display);
} }
output.push_str("\n Identifiers\n"); output.push_str("\n Locals\n");
output.push_str("----- ---------- -----\n"); output.push_str("----- ---------- ----- -----\n");
output.push_str("INDEX NAME DEPTH\n"); output.push_str("INDEX NAME DEPTH VALUE\n");
output.push_str("----- ---------- -----\n"); output.push_str("----- ---------- ----- -----\n");
for (index, Local { identifier, depth }) in self.identifiers.iter().enumerate() { for (
let display = format!("{index:3} {:10} {depth}\n", identifier.as_str()); 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); output.push_str(&display);
} }
@ -256,9 +290,9 @@ impl Eq for Chunk {}
impl PartialEq for Chunk { impl PartialEq for Chunk {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
self.code == other.code self.instructions == other.instructions
&& self.constants == other.constants && self.constants == other.constants
&& self.identifiers == other.identifiers && self.locals == other.locals
} }
} }
@ -266,11 +300,16 @@ impl PartialEq for Chunk {
pub struct Local { pub struct Local {
pub identifier: Identifier, pub identifier: Identifier,
pub depth: usize, pub depth: usize,
pub value: Option<Value>,
} }
impl Local { impl Local {
pub fn new(identifier: Identifier, depth: usize) -> Self { pub fn new(identifier: Identifier, depth: usize, value: Option<Value>) -> Self {
Self { identifier, depth } Self {
identifier,
depth,
value,
}
} }
} }
@ -281,18 +320,18 @@ pub enum ChunkError {
position: Span, position: Span,
}, },
ConstantAlreadyUsed { ConstantAlreadyUsed {
index: u16, index: usize,
position: Span, position: Span,
}, },
ConstantOverflow { ConstantOverflow {
position: Span, position: Span,
}, },
ConstantIndexOutOfBounds { ConstantIndexOutOfBounds {
index: u16, index: usize,
position: Span, position: Span,
}, },
IdentifierIndexOutOfBounds { LocalIndexOutOfBounds {
index: u16, index: usize,
position: Span, position: Span,
}, },
IdentifierOverflow { IdentifierOverflow {
@ -315,7 +354,7 @@ impl AnnotatedError for ChunkError {
ChunkError::ConstantAlreadyUsed { .. } => "Constant already used", ChunkError::ConstantAlreadyUsed { .. } => "Constant already used",
ChunkError::ConstantOverflow { .. } => "Constant overflow", ChunkError::ConstantOverflow { .. } => "Constant overflow",
ChunkError::ConstantIndexOutOfBounds { .. } => "Constant index out of bounds", 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::IdentifierOverflow { .. } => "Identifier overflow",
ChunkError::IdentifierNotFound { .. } => "Identifier not found", ChunkError::IdentifierNotFound { .. } => "Identifier not found",
} }
@ -330,7 +369,7 @@ impl AnnotatedError for ChunkError {
ChunkError::ConstantIndexOutOfBounds { index, .. } => { ChunkError::ConstantIndexOutOfBounds { index, .. } => {
Some(format!("Constant index: {}", index)) Some(format!("Constant index: {}", index))
} }
ChunkError::IdentifierIndexOutOfBounds { index, .. } => { ChunkError::LocalIndexOutOfBounds { index, .. } => {
Some(format!("Identifier index: {}", index)) Some(format!("Identifier index: {}", index))
} }
ChunkError::IdentifierNotFound { identifier, .. } => { ChunkError::IdentifierNotFound { identifier, .. } => {

View File

@ -5,7 +5,7 @@ use crate::{Chunk, Span};
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
pub struct Instruction { pub struct Instruction {
pub operation: Operation, pub operation: Operation,
pub to_register: u8, pub destination: u8,
pub arguments: [u8; 2], pub arguments: [u8; 2],
} }
@ -17,15 +17,15 @@ impl Instruction {
Instruction { Instruction {
operation, operation,
to_register, destination: to_register,
arguments, arguments,
} }
} }
pub fn encode(&self) -> u32 { pub fn encode(&self) -> u32 {
let operation = u32::from(self.operation as u8); let operation = self.operation as u8 as u32;
let to_register = u32::from(self.to_register); let to_register = self.destination as u32;
let arguments = u32::from(self.arguments[0]) << 8 | u32::from(self.arguments[1]); let arguments = (self.arguments[0] as u32) << 8 | (self.arguments[1] as u32);
operation << 24 | to_register << 16 | arguments operation << 24 | to_register << 16 | arguments
} }
@ -33,7 +33,7 @@ impl Instruction {
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,
to_register, destination: to_register,
arguments: [from_register, 0], arguments: [from_register, 0],
} }
} }
@ -41,7 +41,7 @@ impl Instruction {
pub fn close(to_register: u8) -> Instruction { pub fn close(to_register: u8) -> Instruction {
Instruction { Instruction {
operation: Operation::Close, operation: Operation::Close,
to_register, destination: to_register,
arguments: [0, 0], arguments: [0, 0],
} }
} }
@ -49,7 +49,7 @@ impl Instruction {
pub fn load_constant(to_register: u8, constant_index: u16) -> Instruction { pub fn load_constant(to_register: u8, constant_index: u16) -> Instruction {
Instruction { Instruction {
operation: Operation::LoadConstant, operation: Operation::LoadConstant,
to_register, destination: to_register,
arguments: constant_index.to_le_bytes(), arguments: constant_index.to_le_bytes(),
} }
} }
@ -57,7 +57,7 @@ impl Instruction {
pub fn declare_variable(to_register: u8, variable_index: u16) -> Instruction { pub fn declare_variable(to_register: u8, variable_index: u16) -> Instruction {
Instruction { Instruction {
operation: Operation::DeclareVariable, operation: Operation::DeclareVariable,
to_register, destination: to_register,
arguments: variable_index.to_le_bytes(), arguments: variable_index.to_le_bytes(),
} }
} }
@ -65,15 +65,15 @@ impl Instruction {
pub fn get_variable(to_register: u8, variable_index: u16) -> Instruction { pub fn get_variable(to_register: u8, variable_index: u16) -> Instruction {
Instruction { Instruction {
operation: Operation::GetVariable, operation: Operation::GetVariable,
to_register, destination: to_register,
arguments: variable_index.to_le_bytes(), 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 { Instruction {
operation: Operation::SetVariable, operation: Operation::SetVariable,
to_register: from_register, destination: from_register,
arguments: variable_index.to_le_bytes(), 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 { pub fn add(to_register: u8, left_register: u8, right_register: u8) -> Instruction {
Instruction { Instruction {
operation: Operation::Add, operation: Operation::Add,
to_register, destination: to_register,
arguments: [left_register, right_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 { pub fn subtract(to_register: u8, left_register: u8, right_register: u8) -> Instruction {
Instruction { Instruction {
operation: Operation::Subtract, operation: Operation::Subtract,
to_register, destination: to_register,
arguments: [left_register, right_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 { pub fn multiply(to_register: u8, left_register: u8, right_register: u8) -> Instruction {
Instruction { Instruction {
operation: Operation::Multiply, operation: Operation::Multiply,
to_register, destination: to_register,
arguments: [left_register, right_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 { pub fn divide(to_register: u8, left_register: u8, right_register: u8) -> Instruction {
Instruction { Instruction {
operation: Operation::Divide, operation: Operation::Divide,
to_register, destination: to_register,
arguments: [left_register, right_register], arguments: [left_register, right_register],
} }
} }
@ -113,7 +113,7 @@ impl Instruction {
pub fn negate(to_register: u8, from_register: u8) -> Instruction { pub fn negate(to_register: u8, from_register: u8) -> Instruction {
Instruction { Instruction {
operation: Operation::Negate, operation: Operation::Negate,
to_register, destination: to_register,
arguments: [from_register, 0], arguments: [from_register, 0],
} }
} }
@ -121,7 +121,7 @@ impl Instruction {
pub fn r#return() -> Instruction { pub fn r#return() -> Instruction {
Instruction { Instruction {
operation: Operation::Return, operation: Operation::Return,
to_register: 0, destination: 0,
arguments: [0, 0], arguments: [0, 0],
} }
} }
@ -132,34 +132,34 @@ impl Instruction {
format!( format!(
"{:16} R({}) R({})", "{:16} R({}) R({})",
self.operation.to_string(), self.operation.to_string(),
self.to_register, self.destination,
self.arguments[0] self.arguments[0]
) )
} }
Operation::Close => format!("{} R({})", self.operation, self.to_register), Operation::Close => format!("{:16} R({})", self.operation, self.destination),
Operation::LoadConstant => { 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)) { let constant_display = match chunk.get_constant(constant_index, Span(0, 0)) {
Ok(value) => value.to_string(), Ok(value) => value.to_string(),
Err(error) => format!("{:?}", error), Err(error) => format!("{:?}", error),
}; };
format!( format!(
"{:16} R({}) = C({}) {} ", "{:16} R({}) = C({}) {}",
self.operation.to_string(), self.operation.to_string(),
self.to_register, self.destination,
constant_index, constant_index,
constant_display constant_display
) )
} }
Operation::DeclareVariable => { 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!( format!(
"{:16} R[C({})] = R({})", "{:16} L({}) = R({})",
self.operation.to_string(), self.operation.to_string(),
identifier_index, local_index,
self.to_register self.destination
) )
} }
Operation::GetVariable => { Operation::GetVariable => {
@ -168,7 +168,7 @@ impl Instruction {
format!( format!(
"{:16} R{} = R[I({})]", "{:16} R{} = R[I({})]",
self.operation.to_string(), self.operation.to_string(),
self.to_register, self.destination,
identifier_index identifier_index
) )
} }
@ -179,50 +179,50 @@ impl Instruction {
"{:16} R[C({})] = R({})", "{:16} R[C({})] = R({})",
self.operation.to_string(), self.operation.to_string(),
identifier_index, identifier_index,
self.to_register self.destination
) )
} }
Operation::Add => { Operation::Add => {
format!( format!(
"{:16} R({}) = R({}) + R({})", "{:16} R({}) = RC({}) + RC({})",
self.operation.to_string(), self.operation.to_string(),
self.to_register, self.destination,
self.arguments[0], self.arguments[0],
self.arguments[1] self.arguments[1]
) )
} }
Operation::Subtract => { Operation::Subtract => {
format!( format!(
"{:16} R({}) = R({}) - R({})", "{:16} R({}) = RC({}) - RC({})",
self.operation.to_string(), self.operation.to_string(),
self.to_register, self.destination,
self.arguments[0], self.arguments[0],
self.arguments[1] self.arguments[1]
) )
} }
Operation::Multiply => { Operation::Multiply => {
format!( format!(
"{:16} R({}) = R({}) * R({})", "{:16} R({}) = RC({}) * RC({})",
self.operation.to_string(), self.operation.to_string(),
self.to_register, self.destination,
self.arguments[0], self.arguments[0],
self.arguments[1] self.arguments[1]
) )
} }
Operation::Divide => { Operation::Divide => {
format!( format!(
"{:16} R({}) = R({}) / R({})", "{:16} R({}) = RC({}) / RC({})",
self.operation.to_string(), self.operation.to_string(),
self.to_register, self.destination,
self.arguments[0], self.arguments[0],
self.arguments[1] self.arguments[1]
) )
} }
Operation::Negate => { Operation::Negate => {
format!( format!(
"{:16} R({}) = -R({})", "{:16} R({}) = -RC({})",
self.operation.to_string(), self.operation.to_string(),
self.to_register, self.destination,
self.arguments[0] self.arguments[0]
) )
} }
@ -239,22 +239,18 @@ impl Display for Instruction {
Operation::Move => { Operation::Move => {
write!( write!(
f, f,
"{:16} R({}) R({})", "{} R({}) R({})",
self.operation.to_string(), self.operation, self.destination, self.arguments[0]
self.to_register,
self.arguments[0]
) )
} }
Operation::Close => write!(f, "{} R({})", self.operation, self.to_register), Operation::Close => write!(f, "{} R({})", self.operation, self.destination),
Operation::LoadConstant => { Operation::LoadConstant => {
let constant_index = u16::from_le_bytes(self.arguments); let constant_index = u16::from_le_bytes(self.arguments);
write!( write!(
f, f,
"{:16} R({}) C({})", "{} R({}) = C({})",
self.operation.to_string(), self.operation, self.destination, constant_index
self.to_register,
constant_index
) )
} }
Operation::DeclareVariable => { Operation::DeclareVariable => {
@ -262,10 +258,8 @@ impl Display for Instruction {
write!( write!(
f, f,
"{:16} R[C({})] = R({})", "{} L({}) = R({})",
self.operation.to_string(), self.operation, identifier_index, self.destination
identifier_index,
self.to_register
) )
} }
Operation::GetVariable => { Operation::GetVariable => {
@ -273,10 +267,8 @@ impl Display for Instruction {
write!( write!(
f, f,
"{:16} R{} = R[I({})]", "{} R{} = R[I({})]",
self.operation.to_string(), self.operation, self.destination, identifier_index
self.to_register,
identifier_index
) )
} }
Operation::SetVariable => { Operation::SetVariable => {
@ -284,63 +276,47 @@ impl Display for Instruction {
write!( write!(
f, f,
"{:16} R[C({})] = R({})", "{} R[C({})] = R({})",
self.operation.to_string(), self.operation, identifier_index, self.destination
identifier_index,
self.to_register
) )
} }
Operation::Add => { Operation::Add => {
write!( write!(
f, f,
"{:16} R({}) = R({}) + R({})", "{} R({}) = RC({}) + RC({})",
self.operation.to_string(), self.operation, self.destination, self.arguments[0], self.arguments[1]
self.to_register,
self.arguments[0],
self.arguments[1]
) )
} }
Operation::Subtract => { Operation::Subtract => {
write!( write!(
f, f,
"{:16} R({}) = R({}) - R({})", "{} R({}) = RC({}) - RC({})",
self.operation.to_string(), self.operation, self.destination, self.arguments[0], self.arguments[1]
self.to_register,
self.arguments[0],
self.arguments[1]
) )
} }
Operation::Multiply => { Operation::Multiply => {
write!( write!(
f, f,
"{:16} R({}) = R({}) * R({})", "{} R({}) = RC({}) * RC({})",
self.operation.to_string(), self.operation, self.destination, self.arguments[0], self.arguments[1]
self.to_register,
self.arguments[0],
self.arguments[1]
) )
} }
Operation::Divide => { Operation::Divide => {
write!( write!(
f, f,
"{:16} R({}) = R({}) / R({})", "{} R({}) = RC({}) / RC({})",
self.operation.to_string(), self.operation, self.destination, self.arguments[0], self.arguments[1]
self.to_register,
self.arguments[0],
self.arguments[1]
) )
} }
Operation::Negate => { Operation::Negate => {
write!( write!(
f, f,
"{:16} R({}) = -R({})", "{} R({}) = -RC({})",
self.operation.to_string(), self.operation, self.destination, self.arguments[0]
self.to_register,
self.arguments[0]
) )
} }
Operation::Return => { Operation::Return => {
write!(f, "{:16}", self.operation.to_string()) write!(f, "{}", self.operation)
} }
} }
} }

View File

@ -9,7 +9,7 @@ use std::{
use crate::{ use crate::{
dust_error::AnnotatedError, Chunk, ChunkError, DustError, Identifier, Instruction, LexError, 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<Chunk, DustError> { pub fn parse(source: &str) -> Result<Chunk, DustError> {
@ -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> { fn advance(&mut self) -> Result<(), ParseError> {
if self.is_eof() { if self.is_eof() {
return Ok(()); return Ok(());
@ -113,7 +127,7 @@ impl<'src> Parser<'src> {
} }
fn emit_instruction(&mut self, instruction: Instruction, position: Span) { 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> { fn emit_constant(&mut self, value: Value) -> Result<(), ParseError> {
@ -241,18 +255,61 @@ impl<'src> Parser<'src> {
self.parse(rule.precedence.increment())?; self.parse(rule.precedence.increment())?;
let to_register = if self.current_register < 2 { let previous_instruction = self.chunk.pop_instruction();
self.current_register + 2 let right_register = match previous_instruction {
} else { Some((
self.current_register 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 last_instruction = self.chunk.pop_instruction();
let right_register = to_register - 1; let left_register = match last_instruction {
let byte = match operator { Some((
TokenKind::Plus => Instruction::add(to_register, left_register, right_register), Instruction {
TokenKind::Minus => Instruction::subtract(to_register, left_register, right_register), operation: Operation::LoadConstant,
TokenKind::Star => Instruction::multiply(to_register, left_register, right_register), arguments,
TokenKind::Slash => Instruction::divide(to_register, left_register, right_register), ..
},
_,
)) => {
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 { return Err(ParseError::ExpectedTokenMultiple {
expected: vec![ expected: vec![
@ -268,7 +325,7 @@ impl<'src> Parser<'src> {
}; };
self.increment_register()?; self.increment_register()?;
self.emit_instruction(byte, operator_position); self.emit_instruction(instruction, operator_position);
Ok(()) Ok(())
} }
@ -279,21 +336,16 @@ 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 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)? { if allow_assignment && self.allow(TokenKind::Equal)? {
self.parse_expression()?; self.parse_expression()?;
self.emit_instruction( self.emit_instruction(
Instruction::set_variable(self.current_register, identifier_index), Instruction::set_local(self.current_register, local_index),
self.previous_position, self.previous_position,
); );
self.increment_register()?; self.increment_register()?;
} else {
self.emit_instruction(
Instruction::get_variable(self.current_register - 1, identifier_index),
self.previous_position,
);
} }
Ok(()) Ok(())
@ -307,8 +359,8 @@ impl<'src> Parser<'src> {
if let TokenOwned::Identifier(text) = token { if let TokenOwned::Identifier(text) = token {
let identifier = Identifier::new(text); let identifier = Identifier::new(text);
if let Ok(identifier_index) = self.chunk.get_identifier_index(&identifier, position) { if let Ok(local_index) = self.chunk.get_local_index(&identifier, position) {
Ok(identifier_index) Ok(local_index)
} else { } else {
Err(ParseError::UndefinedVariable { Err(ParseError::UndefinedVariable {
identifier, identifier,
@ -392,13 +444,12 @@ impl<'src> Parser<'src> {
self.expect(TokenKind::Equal)?; self.expect(TokenKind::Equal)?;
self.parse_expression()?; self.parse_expression()?;
let identifier_index = self.chunk.declare_variable(identifier, position)?; let local_index = self.chunk.declare_local(identifier, position)?;
self.emit_instruction( self.emit_instruction(
Instruction::declare_variable(self.current_register, identifier_index), Instruction::declare_variable(self.current_register - 1, local_index),
position, position,
); );
self.increment_register()?;
Ok(()) Ok(())
} }
@ -661,6 +712,9 @@ pub enum ParseError {
RegisterOverflow { RegisterOverflow {
position: Span, position: Span,
}, },
RegisterUnderflow {
position: Span,
},
// Wrappers around foreign errors // Wrappers around foreign errors
Chunk(ChunkError), Chunk(ChunkError),
@ -694,6 +748,7 @@ impl AnnotatedError for ParseError {
Self::InvalidAssignmentTarget { .. } => "Invalid assignment target", Self::InvalidAssignmentTarget { .. } => "Invalid assignment target",
Self::UndefinedVariable { .. } => "Undefined variable", Self::UndefinedVariable { .. } => "Undefined variable",
Self::RegisterOverflow { .. } => "Register overflow", Self::RegisterOverflow { .. } => "Register overflow",
Self::RegisterUnderflow { .. } => "Register underflow",
Self::Chunk { .. } => "Chunk error", Self::Chunk { .. } => "Chunk error",
Self::Lex(_) => "Lex error", Self::Lex(_) => "Lex error",
Self::ParseFloatError { .. } => "Failed to parse float", Self::ParseFloatError { .. } => "Failed to parse float",
@ -717,6 +772,7 @@ impl AnnotatedError for ParseError {
Some(format!("Undefined variable \"{identifier}\"")) Some(format!("Undefined variable \"{identifier}\""))
} }
Self::RegisterOverflow { .. } => None, Self::RegisterOverflow { .. } => None,
Self::RegisterUnderflow { .. } => None,
Self::Chunk(error) => error.details(), Self::Chunk(error) => error.details(),
Self::Lex(error) => error.details(), Self::Lex(error) => error.details(),
Self::ParseFloatError { error, .. } => Some(error.to_string()), Self::ParseFloatError { error, .. } => Some(error.to_string()),
@ -732,6 +788,7 @@ impl AnnotatedError for ParseError {
Self::InvalidAssignmentTarget { position, .. } => *position, Self::InvalidAssignmentTarget { position, .. } => *position,
Self::UndefinedVariable { position, .. } => *position, Self::UndefinedVariable { position, .. } => *position,
Self::RegisterOverflow { position } => *position, Self::RegisterOverflow { position } => *position,
Self::RegisterUnderflow { position } => *position,
Self::Chunk(error) => error.position(), Self::Chunk(error) => error.position(),
Self::Lex(error) => error.position(), Self::Lex(error) => error.position(),
Self::ParseFloatError { position, .. } => *position, Self::ParseFloatError { position, .. } => *position,

View File

@ -12,8 +12,8 @@ fn let_statement() {
(Instruction::declare_variable(1, 0), Span(4, 5)), (Instruction::declare_variable(1, 0), Span(4, 5)),
], ],
vec![Value::integer(42),], vec![Value::integer(42),],
vec![Local::new(Identifier::new("x"), 0)] vec![Local::new(Identifier::new("x"), 0, None)]
)) )),
); );
} }

View File

@ -38,69 +38,77 @@ impl Vm {
Operation::Move => todo!(), Operation::Move => todo!(),
Operation::Close => todo!(), Operation::Close => todo!(),
Operation::LoadConstant => { Operation::LoadConstant => {
let register_index = instruction.to_register as usize; let constant_index = u16::from_le_bytes(instruction.arguments) as usize;
let constant_index = u16::from_le_bytes(instruction.arguments);
let value = self.chunk.use_constant(constant_index, position)?; 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 => { Operation::DeclareVariable => {
let register_index = instruction.to_register as usize; let register_index = instruction.destination as usize;
let identifier_index = u16::from_le_bytes(instruction.arguments) as usize; let local_index = u16::from_le_bytes(instruction.arguments) as usize;
let value = self.take(identifier_index, position)?; let value = self.clone(register_index, position)?;
self.insert(value, register_index, position)?; self.chunk.define_local(local_index, value, position)?;
} }
Operation::GetVariable => { Operation::GetVariable => {
let identifier_index = u16::from_le_bytes(instruction.arguments) as usize; 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)?; self.insert(value, identifier_index, position)?;
} }
Operation::SetVariable => todo!(), Operation::SetVariable => todo!(),
Operation::Add => { Operation::Add => {
let left = self.take(instruction.arguments[0] as usize, position)?; let left =
let right = self.take(instruction.arguments[1] as usize, position)?; 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 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.to_register as usize, position)?; self.insert(sum, instruction.destination as usize, position)?;
} }
Operation::Subtract => { Operation::Subtract => {
let left = self.take(instruction.arguments[0] as usize, position)?; let left =
let right = self.take(instruction.arguments[1] as usize, position)?; 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 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.to_register as usize, position)?; self.insert(difference, instruction.destination as usize, position)?;
} }
Operation::Multiply => { Operation::Multiply => {
let left = self.take(instruction.arguments[0] as usize, position)?; let left =
let right = self.take(instruction.arguments[1] as usize, position)?; 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 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.to_register as usize, position)?; self.insert(product, instruction.destination as usize, position)?;
} }
Operation::Divide => { Operation::Divide => {
let left = self.take(instruction.arguments[0] as usize, position)?; let left =
let right = self.take(instruction.arguments[1] as usize, position)?; 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 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.to_register as usize, position)?; self.insert(quotient, instruction.destination as usize, position)?;
} }
Operation::Negate => { 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 let negated = value
.negate() .negate()
.map_err(|error| VmError::Value { error, position })?; .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 => { Operation::Return => {
let value = self.pop(position)?; let value = self.pop(position)?;
@ -117,21 +125,23 @@ impl Vm {
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 {
if index == self.register_stack.len() { while index >= self.register_stack.len() {
self.register_stack.push(Some(value)); self.register_stack.push(None);
} else {
self.register_stack[index] = Some(value);
} }
self.register_stack[index] = Some(value);
Ok(()) Ok(())
} }
} }
fn take(&mut self, index: usize, position: Span) -> Result<Value, VmError> { fn clone(&mut self, index: usize, position: Span) -> Result<Value, VmError> {
if let Some(register) = self.register_stack.get_mut(index) { if let Some(register) = self.register_stack.get_mut(index) {
let value = register let value = register
.clone() .take()
.ok_or(VmError::EmptyRegister { index, position })?; .ok_or(VmError::EmptyRegister { index, position })?
.into_reference();
let _ = register.insert(value.clone());
Ok(value) Ok(value)
} else { } else {
@ -139,6 +149,16 @@ impl Vm {
} }
} }
fn take_or_use_constant(&mut self, index: usize, position: Span) -> Result<Value, VmError> {
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<Value, VmError> { fn pop(&mut self, position: Span) -> Result<Value, VmError> {
if let Some(register) = self.register_stack.pop() { if let Some(register) = self.register_stack.pop() {
let value = register.ok_or(VmError::EmptyRegister { let value = register.ok_or(VmError::EmptyRegister {
@ -153,7 +173,7 @@ impl Vm {
} }
fn read(&mut self, position: Span) -> Result<&(Instruction, Span), VmError> { 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; self.ip += 1;

View File

@ -62,7 +62,7 @@ fn parse_and_display_errors(source: &str) {
match parse(source) { match parse(source) {
Ok(chunk) => println!("{}", chunk.disassemble("Dust CLI Input")), Ok(chunk) => println!("{}", chunk.disassemble("Dust CLI Input")),
Err(error) => { Err(error) => {
eprintln!("{:?}", error); eprintln!("{}", error.report());
} }
} }
} }