Refactor and debug
This commit is contained in:
parent
aa8b1215a8
commit
ba80774e7b
@ -60,7 +60,9 @@ impl Chunk {
|
|||||||
.ok_or(ChunkError::InstructionUnderflow { position })
|
.ok_or(ChunkError::InstructionUnderflow { position })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_constant(&self, index: usize, position: Span) -> Result<&Value, ChunkError> {
|
pub fn get_constant(&self, index: u8, position: Span) -> Result<&Value, ChunkError> {
|
||||||
|
let index = index as usize;
|
||||||
|
|
||||||
self.constants
|
self.constants
|
||||||
.get(index)
|
.get(index)
|
||||||
.ok_or(ChunkError::ConstantIndexOutOfBounds { index, position })
|
.ok_or(ChunkError::ConstantIndexOutOfBounds { index, position })
|
||||||
@ -71,7 +73,9 @@ impl Chunk {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn take_constant(&mut self, index: usize, position: Span) -> Result<Value, ChunkError> {
|
pub fn take_constant(&mut self, index: u8, position: Span) -> Result<Value, ChunkError> {
|
||||||
|
let index = index as usize;
|
||||||
|
|
||||||
self.constants
|
self.constants
|
||||||
.get_mut(index)
|
.get_mut(index)
|
||||||
.ok_or_else(|| ChunkError::ConstantIndexOutOfBounds { index, position })?
|
.ok_or_else(|| ChunkError::ConstantIndexOutOfBounds { index, position })?
|
||||||
@ -79,7 +83,7 @@ impl Chunk {
|
|||||||
.ok_or(ChunkError::ConstantAlreadyUsed { index, position })
|
.ok_or(ChunkError::ConstantAlreadyUsed { index, position })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push_constant(&mut self, value: Value, position: Span) -> Result<u16, ChunkError> {
|
pub fn push_constant(&mut self, value: Value, position: Span) -> Result<u8, ChunkError> {
|
||||||
let starting_length = self.constants.len();
|
let starting_length = self.constants.len();
|
||||||
|
|
||||||
if starting_length + 1 > (u8::MAX as usize) {
|
if starting_length + 1 > (u8::MAX as usize) {
|
||||||
@ -87,17 +91,21 @@ impl Chunk {
|
|||||||
} else {
|
} else {
|
||||||
self.constants.push(Some(value));
|
self.constants.push(Some(value));
|
||||||
|
|
||||||
Ok(starting_length as u16)
|
Ok(starting_length as u8)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_local(&self, index: usize, position: Span) -> Result<&Local, ChunkError> {
|
pub fn get_local(&self, index: u8, position: Span) -> Result<&Local, ChunkError> {
|
||||||
|
let index = index as usize;
|
||||||
|
|
||||||
self.locals
|
self.locals
|
||||||
.get(index)
|
.get(index)
|
||||||
.ok_or(ChunkError::LocalIndexOutOfBounds { index, position })
|
.ok_or(ChunkError::LocalIndexOutOfBounds { index, position })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_identifier(&self, index: usize) -> Option<&Identifier> {
|
pub fn get_identifier(&self, index: u8) -> Option<&Identifier> {
|
||||||
|
let index = index as usize;
|
||||||
|
|
||||||
self.locals.get(index).map(|local| &local.identifier)
|
self.locals.get(index).map(|local| &local.identifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,14 +113,14 @@ impl Chunk {
|
|||||||
&self,
|
&self,
|
||||||
identifier: &Identifier,
|
identifier: &Identifier,
|
||||||
position: Span,
|
position: Span,
|
||||||
) -> Result<u16, ChunkError> {
|
) -> Result<u8, ChunkError> {
|
||||||
self.locals
|
self.locals
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.rev()
|
.rev()
|
||||||
.find_map(|(index, local)| {
|
.find_map(|(index, local)| {
|
||||||
if &local.identifier == identifier {
|
if &local.identifier == identifier {
|
||||||
Some(index as u16)
|
Some(index as u8)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@ -126,17 +134,21 @@ impl Chunk {
|
|||||||
pub fn declare_local(
|
pub fn declare_local(
|
||||||
&mut self,
|
&mut self,
|
||||||
identifier: Identifier,
|
identifier: Identifier,
|
||||||
|
register_index: u8,
|
||||||
position: Span,
|
position: Span,
|
||||||
) -> Result<u16, ChunkError> {
|
) -> Result<u8, ChunkError> {
|
||||||
let starting_length = self.locals.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.locals
|
self.locals.push(Local::new(
|
||||||
.push(Local::new(identifier, self.scope_depth, None));
|
identifier,
|
||||||
|
self.scope_depth,
|
||||||
|
Some(register_index),
|
||||||
|
));
|
||||||
|
|
||||||
Ok(starting_length as u16)
|
Ok(starting_length as u8)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -241,8 +253,8 @@ impl<'a> ChunkDisassembler<'a> {
|
|||||||
"",
|
"",
|
||||||
"Instructions",
|
"Instructions",
|
||||||
"------------",
|
"------------",
|
||||||
"OFFSET OPERATION INFO POSITION",
|
"OFFSET OPERATION INFO POSITION",
|
||||||
"------- -------------- -------------------- --------",
|
"------- -------------- ------------------------- --------",
|
||||||
];
|
];
|
||||||
|
|
||||||
const CONSTANT_HEADER: [&'static str; 5] = [
|
const CONSTANT_HEADER: [&'static str; 5] = [
|
||||||
@ -274,6 +286,18 @@ impl<'a> ChunkDisassembler<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn width(&mut self, width: usize) -> &mut Self {
|
||||||
|
self.width = width;
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn styled(&mut self, styled: bool) -> &mut Self {
|
||||||
|
self.styled = styled;
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub fn disassemble(&self) -> String {
|
pub fn disassemble(&self) -> String {
|
||||||
let center = |line: &str| format!("{line:^width$}\n", width = self.width);
|
let center = |line: &str| format!("{line:^width$}\n", width = self.width);
|
||||||
let style = |line: String| {
|
let style = |line: String| {
|
||||||
@ -298,9 +322,9 @@ impl<'a> ChunkDisassembler<'a> {
|
|||||||
let operation = instruction.operation.to_string();
|
let operation = instruction.operation.to_string();
|
||||||
let info_option = instruction.disassembly_info(Some(self.chunk));
|
let info_option = instruction.disassembly_info(Some(self.chunk));
|
||||||
let instruction_display = if let Some(info) = info_option {
|
let instruction_display = if let Some(info) = info_option {
|
||||||
format!("{offset:<7} {operation:14} {info:20} {position:8}")
|
format!("{offset:<7} {operation:14} {info:25} {position:8}")
|
||||||
} else {
|
} else {
|
||||||
format!("{offset:<7} {operation:14} {:20} {position:8}", " ")
|
format!("{offset:<7} {operation:14} {:25} {position:8}", " ")
|
||||||
};
|
};
|
||||||
|
|
||||||
disassembled.push_str(¢er(&instruction_display));
|
disassembled.push_str(¢er(&instruction_display));
|
||||||
@ -367,18 +391,6 @@ impl<'a> ChunkDisassembler<'a> {
|
|||||||
disassembled
|
disassembled
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn width(&mut self, width: usize) -> &mut Self {
|
|
||||||
self.width = width;
|
|
||||||
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn styled(&mut self, styled: bool) -> &mut Self {
|
|
||||||
self.styled = styled;
|
|
||||||
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Predicts the capacity of the disassembled output. This is used to pre-allocate the string
|
/// Predicts the capacity of the disassembled output. This is used to pre-allocate the string
|
||||||
/// buffer to avoid reallocations.
|
/// buffer to avoid reallocations.
|
||||||
///
|
///
|
||||||
|
@ -10,26 +10,6 @@ pub struct Instruction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Instruction {
|
impl Instruction {
|
||||||
pub fn decode(bits: u32) -> Instruction {
|
|
||||||
let operation = Operation::from((bits >> 24) as u8);
|
|
||||||
let to_register = ((bits >> 16) & 0xff) as u8;
|
|
||||||
let arguments = [((bits >> 8) & 0xff) as u8, (bits & 0xff) as u8];
|
|
||||||
|
|
||||||
Instruction {
|
|
||||||
operation,
|
|
||||||
destination: to_register,
|
|
||||||
arguments,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn encode(&self) -> u32 {
|
|
||||||
let operation = self.operation as u8 as u32;
|
|
||||||
let to_register = self.destination as u32;
|
|
||||||
let arguments = (self.arguments[0] as u32) << 8 | (self.arguments[1] as u32);
|
|
||||||
|
|
||||||
operation << 24 | to_register << 16 | arguments
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn r#move(to_register: u8, from_register: u8) -> Instruction {
|
pub fn r#move(to_register: u8, from_register: u8) -> Instruction {
|
||||||
Instruction {
|
Instruction {
|
||||||
operation: Operation::Move,
|
operation: Operation::Move,
|
||||||
@ -46,35 +26,35 @@ impl Instruction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_constant(to_register: u8, constant_index: u16) -> Instruction {
|
pub fn load_constant(to_register: u8, constant_index: u8) -> Instruction {
|
||||||
Instruction {
|
Instruction {
|
||||||
operation: Operation::LoadConstant,
|
operation: Operation::LoadConstant,
|
||||||
destination: to_register,
|
destination: to_register,
|
||||||
arguments: constant_index.to_le_bytes(),
|
arguments: [constant_index, 0],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn declare_local(to_register: u8, variable_index: u16) -> Instruction {
|
pub fn declare_local(to_register: u8, variable_index: u8) -> Instruction {
|
||||||
Instruction {
|
Instruction {
|
||||||
operation: Operation::DeclareLocal,
|
operation: Operation::DeclareLocal,
|
||||||
destination: to_register,
|
destination: to_register,
|
||||||
arguments: variable_index.to_le_bytes(),
|
arguments: [variable_index, 0],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_local(to_register: u8, variable_index: u16) -> Instruction {
|
pub fn get_local(to_register: u8, variable_index: u8) -> Instruction {
|
||||||
Instruction {
|
Instruction {
|
||||||
operation: Operation::GetLocal,
|
operation: Operation::GetLocal,
|
||||||
destination: to_register,
|
destination: to_register,
|
||||||
arguments: variable_index.to_le_bytes(),
|
arguments: [variable_index, 0],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_local(from_register: u8, variable_index: u16) -> Instruction {
|
pub fn set_local(from_register: u8, variable_index: u8) -> Instruction {
|
||||||
Instruction {
|
Instruction {
|
||||||
operation: Operation::SetLocal,
|
operation: Operation::SetLocal,
|
||||||
destination: from_register,
|
destination: from_register,
|
||||||
arguments: variable_index.to_le_bytes(),
|
arguments: [variable_index, 0],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,7 +123,7 @@ impl Instruction {
|
|||||||
}
|
}
|
||||||
Operation::Close => format!("R({})", self.destination),
|
Operation::Close => format!("R({})", self.destination),
|
||||||
Operation::LoadConstant => {
|
Operation::LoadConstant => {
|
||||||
let constant_index = u16::from_le_bytes(self.arguments) as usize;
|
let constant_index = self.arguments[0];
|
||||||
|
|
||||||
if let Some(chunk) = chunk {
|
if let Some(chunk) = chunk {
|
||||||
match chunk.get_constant(constant_index, Span(0, 0)) {
|
match chunk.get_constant(constant_index, Span(0, 0)) {
|
||||||
@ -160,9 +140,9 @@ impl Instruction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Operation::DeclareLocal => {
|
Operation::DeclareLocal => {
|
||||||
let local_index = u16::from_le_bytes([self.arguments[0], self.arguments[1]]);
|
let local_index = self.arguments[0];
|
||||||
let identifier_display = if let Some(chunk) = chunk {
|
let identifier_display = if let Some(chunk) = chunk {
|
||||||
match chunk.get_identifier(local_index as usize) {
|
match chunk.get_identifier(local_index) {
|
||||||
Some(identifier) => identifier.to_string(),
|
Some(identifier) => identifier.to_string(),
|
||||||
None => "???".to_string(),
|
None => "???".to_string(),
|
||||||
}
|
}
|
||||||
@ -176,14 +156,14 @@ impl Instruction {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
Operation::GetLocal => {
|
Operation::GetLocal => {
|
||||||
let local_index = u16::from_le_bytes([self.arguments[0], self.arguments[1]]);
|
let local_index = self.arguments[0];
|
||||||
|
|
||||||
format!("R({}) = L({})", self.destination, local_index)
|
format!("R({}) = L({})", self.destination, local_index)
|
||||||
}
|
}
|
||||||
Operation::SetLocal => {
|
Operation::SetLocal => {
|
||||||
let local_index = u16::from_le_bytes([self.arguments[0], self.arguments[1]]);
|
let local_index = self.arguments[0];
|
||||||
let identifier_display = if let Some(chunk) = chunk {
|
let identifier_display = if let Some(chunk) = chunk {
|
||||||
match chunk.get_identifier(local_index as usize) {
|
match chunk.get_identifier(local_index) {
|
||||||
Some(identifier) => identifier.to_string(),
|
Some(identifier) => identifier.to_string(),
|
||||||
None => "???".to_string(),
|
None => "???".to_string(),
|
||||||
}
|
}
|
||||||
@ -267,6 +247,15 @@ pub enum Operation {
|
|||||||
Return = 11,
|
Return = 11,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Operation {
|
||||||
|
pub fn is_binary(&self) -> bool {
|
||||||
|
matches!(
|
||||||
|
self,
|
||||||
|
Operation::Add | Operation::Subtract | Operation::Multiply | Operation::Divide
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<u8> for Operation {
|
impl From<u8> for Operation {
|
||||||
fn from(byte: u8) -> Self {
|
fn from(byte: u8) -> Self {
|
||||||
match byte {
|
match byte {
|
||||||
|
@ -255,8 +255,10 @@ impl<'src> Parser<'src> {
|
|||||||
|
|
||||||
self.parse(rule.precedence.increment())?;
|
self.parse(rule.precedence.increment())?;
|
||||||
|
|
||||||
let (previous_instruction, position) = self.chunk.pop_instruction(self.current_position)?;
|
let mut push_back_right = false;
|
||||||
let right_register = match previous_instruction {
|
let (right_instruction, right_position) =
|
||||||
|
self.chunk.pop_instruction(self.current_position)?;
|
||||||
|
let right_register = match right_instruction {
|
||||||
Instruction {
|
Instruction {
|
||||||
operation: Operation::LoadConstant,
|
operation: Operation::LoadConstant,
|
||||||
arguments,
|
arguments,
|
||||||
@ -267,13 +269,15 @@ impl<'src> Parser<'src> {
|
|||||||
arguments[0]
|
arguments[0]
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
self.chunk.push_instruction(previous_instruction, position);
|
push_back_right = true;
|
||||||
|
|
||||||
self.current_register - 1
|
self.current_register - 1
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let (previous_instruction, position) = self.chunk.pop_instruction(self.current_position)?;
|
let mut push_back_left = false;
|
||||||
let left_register = match previous_instruction {
|
let (left_instruction, left_position) =
|
||||||
|
self.chunk.pop_instruction(self.current_position)?;
|
||||||
|
let left_register = match left_instruction {
|
||||||
Instruction {
|
Instruction {
|
||||||
operation: Operation::LoadConstant,
|
operation: Operation::LoadConstant,
|
||||||
arguments,
|
arguments,
|
||||||
@ -284,11 +288,21 @@ impl<'src> Parser<'src> {
|
|||||||
arguments[0]
|
arguments[0]
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
self.chunk.push_instruction(previous_instruction, position);
|
push_back_left = true;
|
||||||
|
|
||||||
self.current_register - 2
|
self.current_register - 2
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if push_back_right {
|
||||||
|
self.chunk
|
||||||
|
.push_instruction(right_instruction, right_position);
|
||||||
|
}
|
||||||
|
|
||||||
|
if push_back_left {
|
||||||
|
self.chunk.push_instruction(left_instruction, left_position);
|
||||||
|
}
|
||||||
|
|
||||||
let instruction = match operator {
|
let instruction = match operator {
|
||||||
TokenKind::Plus => {
|
TokenKind::Plus => {
|
||||||
Instruction::add(self.current_register, left_register, right_register)
|
Instruction::add(self.current_register, left_register, right_register)
|
||||||
@ -328,23 +342,42 @@ impl<'src> Parser<'src> {
|
|||||||
|
|
||||||
fn parse_named_variable(&mut self, allow_assignment: bool) -> Result<(), ParseError> {
|
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 local_index = self.parse_identifier_from(token, self.previous_position)?;
|
let start_position = self.previous_position;
|
||||||
|
let local_index = self.parse_identifier_from(token, start_position)?;
|
||||||
|
|
||||||
if allow_assignment && self.allow(TokenKind::Equal)? {
|
if allow_assignment && self.allow(TokenKind::Equal)? {
|
||||||
self.parse_expression()?;
|
self.parse_expression()?;
|
||||||
self.emit_instruction(
|
|
||||||
Instruction::set_local(self.current_register, local_index),
|
let (mut previous_instruction, previous_position) =
|
||||||
self.previous_position,
|
self.chunk.pop_instruction(self.previous_position)?;
|
||||||
);
|
|
||||||
|
if previous_instruction.operation.is_binary() {
|
||||||
|
let previous_register = self
|
||||||
|
.chunk
|
||||||
|
.get_local(local_index, start_position)?
|
||||||
|
.register_index;
|
||||||
|
|
||||||
|
if let Some(register_index) = previous_register {
|
||||||
|
previous_instruction.destination = register_index;
|
||||||
|
|
||||||
|
self.emit_instruction(previous_instruction, self.previous_position);
|
||||||
|
self.decrement_register()?;
|
||||||
|
} else {
|
||||||
|
self.emit_instruction(previous_instruction, previous_position);
|
||||||
|
self.emit_instruction(
|
||||||
|
Instruction::set_local(self.current_register - 1, local_index),
|
||||||
|
self.previous_position,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
self.emit_instruction(
|
self.emit_instruction(
|
||||||
Instruction::get_local(self.current_register, local_index),
|
Instruction::get_local(self.current_register, local_index),
|
||||||
self.previous_position,
|
self.previous_position,
|
||||||
);
|
);
|
||||||
|
self.increment_register()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.increment_register()?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -352,7 +385,7 @@ impl<'src> Parser<'src> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
token: TokenOwned,
|
token: TokenOwned,
|
||||||
position: Span,
|
position: Span,
|
||||||
) -> Result<u16, ParseError> {
|
) -> Result<u8, ParseError> {
|
||||||
if let TokenOwned::Identifier(text) = token {
|
if let TokenOwned::Identifier(text) = token {
|
||||||
let identifier = Identifier::new(text);
|
let identifier = Identifier::new(text);
|
||||||
|
|
||||||
@ -441,9 +474,12 @@ impl<'src> Parser<'src> {
|
|||||||
self.expect(TokenKind::Equal)?;
|
self.expect(TokenKind::Equal)?;
|
||||||
self.parse_expression()?;
|
self.parse_expression()?;
|
||||||
|
|
||||||
let local_index = self
|
let local_index = self.chunk.declare_local(
|
||||||
.chunk
|
identifier,
|
||||||
.declare_local(identifier, self.current_position)?;
|
self.current_register - 1,
|
||||||
|
self.current_position,
|
||||||
|
)?;
|
||||||
|
|
||||||
let (previous_instruction, previous_position) =
|
let (previous_instruction, previous_position) =
|
||||||
self.chunk.pop_instruction(self.current_position)?;
|
self.chunk.pop_instruction(self.current_position)?;
|
||||||
|
|
||||||
|
@ -40,16 +40,16 @@ impl Vm {
|
|||||||
|
|
||||||
match instruction.operation {
|
match instruction.operation {
|
||||||
Operation::Move => {
|
Operation::Move => {
|
||||||
let from = instruction.arguments[0] as usize;
|
let from = instruction.arguments[0];
|
||||||
let to = instruction.destination as usize;
|
let to = instruction.destination;
|
||||||
let value = self.clone(from, position)?;
|
let value = self.clone(from, position)?;
|
||||||
|
|
||||||
self.insert(value, to, position)?;
|
self.insert(value, to, position)?;
|
||||||
}
|
}
|
||||||
Operation::Close => todo!(),
|
Operation::Close => todo!(),
|
||||||
Operation::LoadConstant => {
|
Operation::LoadConstant => {
|
||||||
let to_register = instruction.destination as usize;
|
let to_register = instruction.destination;
|
||||||
let from_constant = u16::from_le_bytes(instruction.arguments) as usize;
|
let from_constant = instruction.arguments[0];
|
||||||
let value = self.chunk.take_constant(from_constant, position)?;
|
let value = self.chunk.take_constant(from_constant, position)?;
|
||||||
|
|
||||||
self.insert(value, to_register, position)?;
|
self.insert(value, to_register, position)?;
|
||||||
@ -61,11 +61,11 @@ impl Vm {
|
|||||||
self.chunk.define_local(to_local, from_register, position)?;
|
self.chunk.define_local(to_local, from_register, position)?;
|
||||||
}
|
}
|
||||||
Operation::GetLocal => {
|
Operation::GetLocal => {
|
||||||
let register_index = instruction.destination as usize;
|
let register_index = instruction.destination;
|
||||||
let local_index = u16::from_le_bytes(instruction.arguments) as usize;
|
let local_index = instruction.arguments[0];
|
||||||
let local = self.chunk.get_local(local_index, position)?;
|
let local = self.chunk.get_local(local_index, position)?;
|
||||||
let value = if let Some(value_index) = &local.register_index {
|
let value = if let Some(value_index) = &local.register_index {
|
||||||
self.clone(*value_index as usize, position)?
|
self.clone(*value_index, position)?
|
||||||
} else {
|
} else {
|
||||||
return Err(VmError::UndefinedVariable {
|
return Err(VmError::UndefinedVariable {
|
||||||
identifier: local.identifier.clone(),
|
identifier: local.identifier.clone(),
|
||||||
@ -75,59 +75,64 @@ impl Vm {
|
|||||||
|
|
||||||
self.insert(value, register_index, position)?;
|
self.insert(value, register_index, position)?;
|
||||||
}
|
}
|
||||||
Operation::SetLocal => todo!(),
|
Operation::SetLocal => {
|
||||||
|
let from_register = instruction.destination;
|
||||||
|
let to_local = instruction.arguments[0] as usize;
|
||||||
|
|
||||||
|
self.chunk.define_local(to_local, from_register, position)?;
|
||||||
|
}
|
||||||
Operation::Add => {
|
Operation::Add => {
|
||||||
let left =
|
let left =
|
||||||
self.take_or_use_constant(instruction.arguments[0] as usize, position)?;
|
self.take_constant_or_clone_register(instruction.arguments[0], position)?;
|
||||||
let right =
|
let right =
|
||||||
self.take_or_use_constant(instruction.arguments[1] as usize, position)?;
|
self.take_constant_or_clone_register(instruction.arguments[1], position)?;
|
||||||
let sum = left
|
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.destination as usize, position)?;
|
self.insert(sum, instruction.destination, position)?;
|
||||||
}
|
}
|
||||||
Operation::Subtract => {
|
Operation::Subtract => {
|
||||||
let left =
|
let left =
|
||||||
self.take_or_use_constant(instruction.arguments[0] as usize, position)?;
|
self.take_constant_or_clone_register(instruction.arguments[0], position)?;
|
||||||
let right =
|
let right =
|
||||||
self.take_or_use_constant(instruction.arguments[1] as usize, position)?;
|
self.take_constant_or_clone_register(instruction.arguments[1], position)?;
|
||||||
let difference = left
|
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.destination as usize, position)?;
|
self.insert(difference, instruction.destination, position)?;
|
||||||
}
|
}
|
||||||
Operation::Multiply => {
|
Operation::Multiply => {
|
||||||
let left =
|
let left =
|
||||||
self.take_or_use_constant(instruction.arguments[0] as usize, position)?;
|
self.take_constant_or_clone_register(instruction.arguments[0], position)?;
|
||||||
let right =
|
let right =
|
||||||
self.take_or_use_constant(instruction.arguments[1] as usize, position)?;
|
self.take_constant_or_clone_register(instruction.arguments[1], position)?;
|
||||||
let product = left
|
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.destination as usize, position)?;
|
self.insert(product, instruction.destination, position)?;
|
||||||
}
|
}
|
||||||
Operation::Divide => {
|
Operation::Divide => {
|
||||||
let left =
|
let left =
|
||||||
self.take_or_use_constant(instruction.arguments[0] as usize, position)?;
|
self.take_constant_or_clone_register(instruction.arguments[0], position)?;
|
||||||
let right =
|
let right =
|
||||||
self.take_or_use_constant(instruction.arguments[1] as usize, position)?;
|
self.take_constant_or_clone_register(instruction.arguments[1], position)?;
|
||||||
let quotient = left
|
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.destination as usize, position)?;
|
self.insert(quotient, instruction.destination, position)?;
|
||||||
}
|
}
|
||||||
Operation::Negate => {
|
Operation::Negate => {
|
||||||
let value =
|
let value =
|
||||||
self.take_or_use_constant(instruction.arguments[0] as usize, position)?;
|
self.take_constant_or_clone_register(instruction.arguments[0], position)?;
|
||||||
let negated = value
|
let negated = value
|
||||||
.negate()
|
.negate()
|
||||||
.map_err(|error| VmError::Value { error, position })?;
|
.map_err(|error| VmError::Value { error, position })?;
|
||||||
|
|
||||||
self.insert(negated, instruction.destination as usize, position)?;
|
self.insert(negated, instruction.destination, position)?;
|
||||||
}
|
}
|
||||||
Operation::Return => {
|
Operation::Return => {
|
||||||
let value = self.pop(position)?;
|
let value = self.pop(position)?;
|
||||||
@ -140,10 +145,12 @@ impl Vm {
|
|||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn insert(&mut self, value: Value, index: usize, position: Span) -> Result<(), VmError> {
|
fn insert(&mut self, value: Value, index: u8, position: Span) -> Result<(), VmError> {
|
||||||
if self.register_stack.len() == Self::STACK_LIMIT {
|
if self.register_stack.len() == Self::STACK_LIMIT {
|
||||||
Err(VmError::StackOverflow { position })
|
Err(VmError::StackOverflow { position })
|
||||||
} else {
|
} else {
|
||||||
|
let index = index as usize;
|
||||||
|
|
||||||
while index >= self.register_stack.len() {
|
while index >= self.register_stack.len() {
|
||||||
self.register_stack.push(None);
|
self.register_stack.push(None);
|
||||||
}
|
}
|
||||||
@ -154,7 +161,9 @@ impl Vm {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clone(&mut self, index: usize, position: Span) -> Result<Value, VmError> {
|
fn clone(&mut self, index: u8, position: Span) -> Result<Value, VmError> {
|
||||||
|
let index = index as usize;
|
||||||
|
|
||||||
if let Some(register) = self.register_stack.get_mut(index) {
|
if let Some(register) = self.register_stack.get_mut(index) {
|
||||||
let cloneable = if let Some(value) = register.take() {
|
let cloneable = if let Some(value) = register.take() {
|
||||||
if value.is_raw() {
|
if value.is_raw() {
|
||||||
@ -174,23 +183,15 @@ impl Vm {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn take(&mut self, index: usize, position: Span) -> Result<Value, VmError> {
|
fn take_constant_or_clone_register(
|
||||||
if let Some(register) = self.register_stack.get_mut(index) {
|
&mut self,
|
||||||
if let Some(value) = register.take() {
|
index: u8,
|
||||||
Ok(value)
|
position: Span,
|
||||||
} else {
|
) -> Result<Value, VmError> {
|
||||||
Err(VmError::EmptyRegister { index, position })
|
if let Ok(value) = self.chunk.take_constant(index, position) {
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Err(VmError::RegisterIndexOutOfBounds { position })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn take_or_use_constant(&mut self, index: usize, position: Span) -> Result<Value, VmError> {
|
|
||||||
if let Ok(value) = self.take(index, position) {
|
|
||||||
Ok(value)
|
Ok(value)
|
||||||
} else {
|
} else {
|
||||||
let value = self.chunk.take_constant(index, position)?;
|
let value = self.clone(index, position)?;
|
||||||
|
|
||||||
Ok(value)
|
Ok(value)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user