Experiment with instruction optimization
This commit is contained in:
parent
6ff25a22ec
commit
78c9ed97e2
@ -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, .. } => {
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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)]
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user