1
0

Begin refactoring to avoid mutating locals in the chunk

This commit is contained in:
Jeff 2024-11-09 13:49:02 -05:00
parent c343b82873
commit d82aed1a93
12 changed files with 113 additions and 208 deletions

View File

@ -19,7 +19,6 @@ use crate::{Disassembler, Instruction, Operation, Span, Type, Value};
#[derive(Clone, PartialOrd, Ord, Serialize, Deserialize)]
pub struct Chunk {
name: Option<String>,
pub is_poisoned: bool,
instructions: Vec<(Instruction, Span)>,
constants: Vec<Value>,
@ -33,7 +32,6 @@ impl Chunk {
pub fn new(name: Option<String>) -> Self {
Self {
name,
is_poisoned: false,
instructions: Vec::new(),
constants: Vec::new(),
locals: Vec::new(),
@ -50,7 +48,6 @@ impl Chunk {
) -> Self {
Self {
name,
is_poisoned: false,
instructions,
constants,
locals,
@ -175,14 +172,6 @@ impl Chunk {
}
}
pub fn expect_not_poisoned(&self) -> Result<(), ChunkError> {
if self.is_poisoned {
Err(ChunkError::PoisonedChunk)
} else {
Ok(())
}
}
pub fn get_constant_type(&self, constant_index: u8) -> Option<Type> {
self.constants
.get(constant_index as usize)
@ -194,16 +183,6 @@ impl Chunk {
}
pub fn get_register_type(&self, register_index: u8) -> Option<Type> {
let local_type_option = self
.locals
.iter()
.find(|local| local.register_index == register_index)
.map(|local| local.r#type.clone());
if let Some(local_type) = local_type_option {
return local_type;
}
self.instructions
.iter()
.enumerate()
@ -316,26 +295,16 @@ pub struct Local {
/// Scope where the variable was declared.
pub scope: Scope,
/// Expected location of a local's value.
pub register_index: u8,
}
impl Local {
/// Creates a new Local instance.
pub fn new(
identifier_index: u8,
r#type: Option<Type>,
mutable: bool,
scope: Scope,
register_index: u8,
) -> Self {
pub fn new(identifier_index: u8, r#type: Option<Type>, mutable: bool, scope: Scope) -> Self {
Self {
identifier_index,
r#type,
is_mutable: mutable,
scope,
register_index,
}
}
}

View File

@ -48,8 +48,9 @@ pub fn compile(source: &str) -> Result<Chunk, DustError> {
pub struct Compiler<'src> {
chunk: Chunk,
lexer: Lexer<'src>,
optimization_count: usize,
local_definitions: Vec<u8>,
optimization_count: usize,
previous_is_expression: bool,
minimum_register: u8,
@ -74,6 +75,7 @@ impl<'src> Compiler<'src> {
Ok(Compiler {
chunk,
lexer,
local_definitions: Vec::new(),
optimization_count: 0,
previous_is_expression: false,
minimum_register: 0,
@ -178,13 +180,10 @@ impl<'src> Compiler<'src> {
let identifier = Value::string(identifier);
let identifier_index = self.chunk.push_or_get_constant(identifier);
self.chunk.locals_mut().push(Local::new(
identifier_index,
r#type,
is_mutable,
scope,
register_index,
));
self.chunk
.locals_mut()
.push(Local::new(identifier_index, r#type, is_mutable, scope));
self.local_definitions.push(register_index);
(self.chunk.locals().len() as u8 - 1, identifier_index)
}
@ -505,7 +504,22 @@ impl<'src> Compiler<'src> {
let local = self.get_local(local_index)?;
is_mutable_local = local.is_mutable;
local.register_index
*self
.local_definitions
.get(local_index as usize)
.ok_or_else(|| {
let identifier = self
.chunk
.constants()
.get(local.identifier_index as usize)
.unwrap()
.to_string();
CompileError::UndeclaredVariable {
identifier,
position: self.current_position,
}
})?
}
Operation::LoadConstant => {
is_constant = true;
@ -821,28 +835,8 @@ impl<'src> Compiler<'src> {
self.parse_expression()?;
let (mut previous_instruction, previous_position) =
self.chunk.instructions_mut().pop().ok_or_else(|| {
CompileError::ExpectedExpression {
found: self.previous_token.to_owned(),
position: self.previous_position,
}
})?;
let register = self.next_register() - 1;
if previous_instruction.operation().is_math() {
let register_index = self.get_local(local_index)?.register_index;
log::trace!("Condensing SET_LOCAL to binary math expression");
previous_instruction.set_a(register_index);
self.emit_instruction(previous_instruction, self.current_position);
return Ok(());
}
let register = self.next_register();
self.emit_instruction(previous_instruction, previous_position);
self.emit_instruction(
Instruction::set_local(register, local_index),
start_position,
@ -1235,13 +1229,12 @@ impl<'src> Compiler<'src> {
} else {
None
};
let register = self.next_register();
self.expect(Token::Equal)?;
self.parse_expression()?;
let (local_index, _) = self.declare_local(identifier, r#type, is_mutable, scope, register);
let register = self.next_register().saturating_sub(1);
let (local_index, _) = self.declare_local(identifier, r#type, is_mutable, scope, register);
self.emit_instruction(
Instruction::define_local(register, local_index, is_mutable),
@ -1294,10 +1287,8 @@ impl<'src> Compiler<'src> {
function_compiler.advance()?;
let register = value_parameters
.as_ref()
.map(|values| values.len() as u8)
.unwrap_or(0);
let register = function_compiler.next_register();
let scope = function_compiler.chunk.current_scope();
let (_, identifier_index) = function_compiler.declare_local(
parameter,

View File

@ -295,7 +295,6 @@ impl<'a> Disassembler<'a> {
identifier_index,
r#type,
scope,
register_index,
is_mutable: mutable,
},
) in self.chunk.locals().iter().enumerate()
@ -319,7 +318,7 @@ impl<'a> Disassembler<'a> {
})
.unwrap_or("unknown".to_string());
let local_display = format!(
"{index:^3} {identifier_display:10} {type_display:16} {mutable:7} {scope:7} {register_index:8}"
"{index:^3} {identifier_display:10} {type_display:16} {mutable:7} {scope:7}"
);
self.push_details(&local_display);

View File

@ -324,13 +324,14 @@ impl NativeFunction {
};
let first_index = to_register.saturating_sub(argument_count);
let arguments = vm.open_nonempty_registers(first_index..to_register, position)?;
for (index, argument) in arguments.into_iter().enumerate() {
for (index, register) in (first_index..to_register).enumerate() {
if index != 0 {
stdout.write(b" ").map_err(map_err)?;
}
let argument = vm.open_register(register, position)?;
if let Value::Concrete(ConcreteValue::String(string)) = argument {
let bytes = string.as_bytes();

View File

@ -1,8 +1,8 @@
//! Virtual machine and errors
use std::{
cmp::Ordering,
collections::HashMap,
fmt::{self, Display, Formatter},
ops::Range,
};
use crate::{
@ -12,8 +12,8 @@ use crate::{
};
pub fn run(source: &str) -> Result<Option<Value>, DustError> {
let mut chunk = compile(source)?;
let mut vm = Vm::new(&mut chunk, None);
let chunk = compile(source)?;
let mut vm = Vm::new(&chunk, None);
vm.run()
.map(|option| option.cloned())
@ -34,8 +34,9 @@ pub fn run_and_display_output(source: &str) {
#[derive(Debug, Eq, PartialEq)]
pub struct Vm<'chunk, 'parent> {
ip: usize,
chunk: &'chunk mut Chunk,
chunk: &'chunk Chunk,
stack: Vec<Register>,
local_definitions: HashMap<u8, u8>,
last_assigned_register: Option<u8>,
parent: Option<&'parent Vm<'chunk, 'parent>>,
}
@ -43,11 +44,12 @@ pub struct Vm<'chunk, 'parent> {
impl<'chunk, 'parent> Vm<'chunk, 'parent> {
const STACK_LIMIT: usize = u16::MAX as usize;
pub fn new(chunk: &'chunk mut Chunk, parent: Option<&'parent Vm<'chunk, 'parent>>) -> Self {
pub fn new(chunk: &'chunk Chunk, parent: Option<&'parent Vm<'chunk, 'parent>>) -> Self {
Self {
ip: 0,
chunk,
stack: Vec::new(),
local_definitions: HashMap::new(),
last_assigned_register: None,
parent,
}
@ -172,24 +174,39 @@ impl<'chunk, 'parent> Vm<'chunk, 'parent> {
let from_register = instruction.a();
let to_local = instruction.b();
self.define_local(to_local, from_register, position)?;
self.define_local(to_local, from_register)?;
}
Operation::GetLocal => {
let to_register = instruction.a();
let local_index = instruction.b();
let local = self.get_local(local_index, position)?;
let local_register = self.local_definitions.get(&local_index).copied().ok_or(
VmError::UndefinedLocal {
local_index,
position,
},
)?;
self.set_register(
to_register,
Register::StackPointer(local.register_index),
Register::StackPointer(local_register),
position,
)?;
}
Operation::SetLocal => {
let register = instruction.a();
let local_index = instruction.b();
let from_register = instruction.a();
let to_local = instruction.b();
let local_register = self.local_definitions.get(&to_local).copied().ok_or(
VmError::UndefinedLocal {
local_index: to_local,
position,
},
)?;
self.define_local(local_index, register, position)?;
self.set_register(
local_register,
Register::StackPointer(from_register),
position,
)?;
}
Operation::Add => {
let to_register = instruction.a();
@ -401,17 +418,17 @@ impl<'chunk, 'parent> Vm<'chunk, 'parent> {
let to_register = instruction.a();
let function_register = instruction.b();
let argument_count = instruction.c();
let value = self.open_register(function_register, position)?.clone();
let mut function =
if let Value::Concrete(ConcreteValue::Function(function)) = value {
function
} else {
return Err(VmError::ExpectedFunction {
found: value,
position,
});
};
let mut function_vm = Vm::new(function.chunk_mut(), Some(self));
let value = self.open_register(function_register, position)?;
let function = if let Value::Concrete(ConcreteValue::Function(function)) = value
{
function
} else {
return Err(VmError::ExpectedFunction {
found: value.clone(),
position,
});
};
let mut function_vm = Vm::new(function.chunk(), Some(self));
let first_argument_index = function_register + 1;
for argument_index in
@ -527,6 +544,8 @@ impl<'chunk, 'parent> Vm<'chunk, 'parent> {
position,
})?;
log::trace!("Open R{register_index} to {register}");
match register {
Register::Value(value) => Ok(value),
Register::StackPointer(register_index) => self.open_register(*register_index, position),
@ -556,67 +575,7 @@ impl<'chunk, 'parent> Vm<'chunk, 'parent> {
}
}
pub fn open_nonempty_registers(
&self,
register_index_range: Range<u8>,
position: Span,
) -> Result<Vec<&Value>, VmError> {
let mut values = Vec::with_capacity(register_index_range.len());
for register_index in register_index_range.clone() {
let register_index = register_index as usize;
let register = self.stack.get(register_index).ok_or_else(|| {
VmError::RegisterIndexOutOfBounds {
index: register_index,
position,
}
})?;
let value = match register {
Register::Value(value) => value,
Register::StackPointer(register_index) => {
self.open_register(*register_index, position)?
}
Register::ConstantPointer(constant_index) => {
self.get_constant(*constant_index, position)?
}
Register::ParentStackPointer(register_index) => {
let parent = self
.parent
.as_ref()
.ok_or(VmError::ExpectedParent { position })?;
parent.open_register(*register_index, position)?
}
Register::ParentConstantPointer(constant_index) => {
let parent = self
.parent
.as_ref()
.ok_or(VmError::ExpectedParent { position })?;
parent.get_constant(*constant_index, position)?
}
Register::Empty => continue,
};
values.push(value);
}
if values.is_empty() {
Err(VmError::EmptyRegisters {
indexes: register_index_range,
position,
})
} else {
Ok(values)
}
}
fn read(&mut self, position: Span) -> Result<&(Instruction, Span), VmError> {
self.chunk
.expect_not_poisoned()
.map_err(|error| VmError::Chunk { error, position })?;
let max_ip = self.chunk.len() - 1;
if self.ip > max_ip {
@ -628,20 +587,10 @@ impl<'chunk, 'parent> Vm<'chunk, 'parent> {
self.get_instruction(self.ip - 1, position)
}
fn define_local(
&mut self,
local_index: u8,
register_index: u8,
position: Span,
) -> Result<(), VmError> {
let local = self
.chunk
.get_local_mut(local_index)
.map_err(|error| VmError::Chunk { error, position })?;
fn define_local(&mut self, local_index: u8, register_index: u8) -> Result<(), VmError> {
log::debug!("Define local L{}", local_index);
local.register_index = register_index;
self.local_definitions.insert(local_index, register_index);
Ok(())
}
@ -694,9 +643,11 @@ pub enum VmError {
// Register errors
EmptyRegister { index: usize, position: Span },
EmptyRegisters { indexes: Range<u8>, position: Span },
RegisterIndexOutOfBounds { index: usize, position: Span },
// Local errors
UndefinedLocal { local_index: u8, position: Span },
// Execution errors
ExpectedBoolean { found: Value, position: Span },
ExpectedFunction { found: Value, position: Span },
@ -717,7 +668,6 @@ impl AnnotatedError for VmError {
match self {
Self::Chunk { .. } => "Chunk error",
Self::EmptyRegister { .. } => "Empty register",
Self::EmptyRegisters { .. } => "Empty registers",
Self::ExpectedBoolean { .. } => "Expected boolean",
Self::ExpectedFunction { .. } => "Expected function",
Self::ExpectedParent { .. } => "Expected parent",
@ -725,6 +675,7 @@ impl AnnotatedError for VmError {
Self::RegisterIndexOutOfBounds { .. } => "Register index out of bounds",
Self::StackOverflow { .. } => "Stack overflow",
Self::StackUnderflow { .. } => "Stack underflow",
Self::UndefinedLocal { .. } => "Undefined local",
Self::Value { .. } => "Value error",
}
}
@ -733,10 +684,6 @@ impl AnnotatedError for VmError {
match self {
Self::Chunk { error, .. } => Some(error.to_string()),
Self::EmptyRegister { index, .. } => Some(format!("Register R{index} is empty")),
Self::EmptyRegisters { indexes: range, .. } => Some(format!(
"Registers R{} to R{} are empty",
range.start, range.end
)),
Self::ExpectedFunction { found, .. } => Some(format!("{found} is not a function")),
Self::RegisterIndexOutOfBounds { index, .. } => {
Some(format!("Register {index} does not exist"))
@ -751,7 +698,6 @@ impl AnnotatedError for VmError {
match self {
Self::Chunk { position, .. } => *position,
Self::EmptyRegister { position, .. } => *position,
Self::EmptyRegisters { position, .. } => *position,
Self::ExpectedBoolean { position, .. } => *position,
Self::ExpectedFunction { position, .. } => *position,
Self::ExpectedParent { position } => *position,
@ -759,6 +705,7 @@ impl AnnotatedError for VmError {
Self::RegisterIndexOutOfBounds { position, .. } => *position,
Self::StackOverflow { position } => *position,
Self::StackUnderflow { position } => *position,
Self::UndefinedLocal { position, .. } => *position,
Self::Value { position, .. } => *position,
}
}

View File

@ -31,7 +31,6 @@ fn equality_assignment_long() {
depth: 0,
block_index: 0
},
0
)]
)),
);
@ -62,7 +61,7 @@ fn equality_assignment_short() {
(Instruction::r#return(true), Span(16, 16)),
],
vec![Value::integer(4), Value::string("a")],
vec![Local::new(1, None, false, Scope::default(), 0)]
vec![Local::new(1, None, false, Scope::default())]
)),
);
@ -120,7 +119,7 @@ fn if_else_assigment_false() {
Value::integer(42),
Value::string("a")
],
vec![Local::new(5, None, false, Scope::default(), 0)]
vec![Local::new(5, None, false, Scope::default())]
)),
);
@ -178,7 +177,7 @@ fn if_else_assigment_true() {
Value::integer(42),
Value::string("a")
],
vec![Local::new(5, None, false, Scope::default(), 0)]
vec![Local::new(5, None, false, Scope::default())]
)),
);

View File

@ -15,8 +15,8 @@ fn function() {
],
vec![Value::string("a"), Value::string("b"),],
vec![
Local::new(0, Some(Type::Integer), false, Scope::default(), 0),
Local::new(1, Some(Type::Integer), false, Scope::default(), 1)
Local::new(0, Some(Type::Integer), false, Scope::default()),
Local::new(1, Some(Type::Integer), false, Scope::default())
]
),
FunctionType {
@ -53,8 +53,8 @@ fn function_call() {
],
vec![Value::string("a"), Value::string("b"),],
vec![
Local::new(0, Some(Type::Integer), false, Scope::default(), 0),
Local::new(1, Some(Type::Integer), false, Scope::default(), 1)
Local::new(0, Some(Type::Integer), false, Scope::default()),
Local::new(1, Some(Type::Integer), false, Scope::default())
]
),
FunctionType {
@ -97,8 +97,8 @@ fn function_declaration() {
],
vec![Value::string("a"), Value::string("b")],
vec![
Local::new(0, Some(Type::Integer), false, Scope::default(), 0),
Local::new(1, Some(Type::Integer), false, Scope::default(), 1)
Local::new(0, Some(Type::Integer), false, Scope::default()),
Local::new(1, Some(Type::Integer), false, Scope::default())
]
),
FunctionType {
@ -117,8 +117,7 @@ fn function_declaration() {
})),
false,
Scope::default(),
0
),],
)],
)),
);

View File

@ -67,8 +67,8 @@ fn variable_and() {
],
vec![Value::string("a"), Value::string("b"),],
vec![
Local::new(0, None, false, Scope::default(), 0),
Local::new(1, None, false, Scope::default(), 1),
Local::new(0, None, false, Scope::default()),
Local::new(1, None, false, Scope::default()),
]
))
);

View File

@ -27,7 +27,7 @@ fn r#while() {
Value::integer(5),
Value::integer(1),
],
vec![Local::new(1, None, true, Scope::default(), 0),]
vec![Local::new(1, None, true, Scope::default())]
)),
);

View File

@ -41,7 +41,7 @@ fn add_assign() {
(Instruction::r#return(true), Span(24, 24))
],
vec![Value::integer(1), Value::string("a"), Value::integer(2)],
vec![Local::new(1, None, true, Scope::default(), 0)]
vec![Local::new(1, None, true, Scope::default())]
))
);
@ -124,7 +124,7 @@ fn divide_assign() {
(Instruction::r#return(true), Span(24, 24))
],
vec![Value::integer(2), Value::string("a")],
vec![Local::new(1, None, true, Scope::default(), 0)]
vec![Local::new(1, None, true, Scope::default())]
))
);
@ -233,7 +233,7 @@ fn multiply_assign() {
(Instruction::r#return(true), Span(23, 23))
],
vec![Value::integer(2), Value::string("a"), Value::integer(3)],
vec![Local::new(1, None, true, Scope::default(), 0),]
vec![Local::new(1, None, true, Scope::default())]
))
);
@ -300,7 +300,7 @@ fn subtract_assign() {
(Instruction::r#return(true), Span(25, 25)),
],
vec![Value::integer(42), Value::string("x"), Value::integer(2)],
vec![Local::new(1, None, true, Scope::default(), 0)]
vec![Local::new(1, None, true, Scope::default())]
)),
);

View File

@ -55,11 +55,11 @@ fn block_scope() {
Value::string("e"),
],
vec![
Local::new(1, None, false, Scope::new(0, 0), 0),
Local::new(3, None, false, Scope::new(1, 1), 1),
Local::new(5, None, false, Scope::new(2, 2), 2),
Local::new(7, None, false, Scope::new(1, 1), 3),
Local::new(8, None, false, Scope::new(0, 0), 4),
Local::new(1, None, false, Scope::new(0, 0)),
Local::new(3, None, false, Scope::new(1, 1)),
Local::new(5, None, false, Scope::new(2, 2)),
Local::new(7, None, false, Scope::new(1, 1)),
Local::new(8, None, false, Scope::new(0, 0)),
]
)),
);
@ -127,15 +127,15 @@ fn multiple_block_scopes() {
Value::string("e"),
],
vec![
Local::new(1, None, false, Scope::new(0, 0), 0),
Local::new(3, None, false, Scope::new(1, 1), 1),
Local::new(5, None, false, Scope::new(2, 2), 2),
Local::new(7, None, false, Scope::new(1, 1), 3),
Local::new(8, None, false, Scope::new(0, 0), 4),
Local::new(3, None, false, Scope::new(1, 3), 5),
Local::new(5, None, false, Scope::new(2, 4), 6),
Local::new(7, None, false, Scope::new(1, 3), 7),
Local::new(9, None, false, Scope::new(0, 0), 8),
Local::new(1, None, false, Scope::new(0, 0)),
Local::new(3, None, false, Scope::new(1, 1)),
Local::new(5, None, false, Scope::new(2, 2)),
Local::new(7, None, false, Scope::new(1, 1)),
Local::new(8, None, false, Scope::new(0, 0)),
Local::new(3, None, false, Scope::new(1, 3)),
Local::new(5, None, false, Scope::new(2, 4)),
Local::new(7, None, false, Scope::new(1, 3)),
Local::new(9, None, false, Scope::new(0, 0)),
]
)),
);

View File

@ -14,7 +14,7 @@ fn define_local() {
(Instruction::r#return(false), Span(11, 11))
],
vec![Value::integer(42), Value::string("x")],
vec![Local::new(1, None, false, Scope::default(), 0)]
vec![Local::new(1, None, false, Scope::default())]
)),
);
@ -55,7 +55,7 @@ fn set_local() {
(Instruction::r#return(true), Span(25, 25)),
],
vec![Value::integer(41), Value::string("x"), Value::integer(42)],
vec![Local::new(1, None, true, Scope::default(), 0)]
vec![Local::new(1, None, true, Scope::default())]
)),
);