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)]
pub struct Chunk {
code: Vec<(Instruction, Span)>,
instructions: Vec<(Instruction, Span)>,
constants: Vec<Option<Value>>,
identifiers: Vec<Local>,
locals: Vec<Local>,
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<Value>,
identifiers: Vec<Local>,
) -> 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<Value, ChunkError> {
pub fn use_constant(&mut self, index: usize, position: Span) -> Result<Value, ChunkError> {
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<u16, ChunkError> {
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<u16, ChunkError> {
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<Local> {
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("------ ---------------- -------------------- --------\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!(
"{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<Value>,
}
impl Local {
pub fn new(identifier: Identifier, depth: usize) -> Self {
Self { identifier, depth }
pub fn new(identifier: Identifier, depth: usize, value: Option<Value>) -> 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, .. } => {

View File

@ -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)
}
}
}

View File

@ -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<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> {
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,

View File

@ -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)]
)),
);
}

View File

@ -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<Value, VmError> {
fn clone(&mut self, index: usize, position: Span) -> Result<Value, VmError> {
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<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> {
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;

View File

@ -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());
}
}
}