diff --git a/dust-lang/src/chunk.rs b/dust-lang/src/chunk.rs index 9c9e692..6b85f28 100644 --- a/dust-lang/src/chunk.rs +++ b/dust-lang/src/chunk.rs @@ -154,17 +154,16 @@ impl Chunk { pub fn define_local( &mut self, - local_index: usize, + local_index: u8, register_index: u8, position: Span, ) -> Result<(), ChunkError> { - let local = - self.locals - .get_mut(local_index) - .ok_or_else(|| ChunkError::LocalIndexOutOfBounds { - index: local_index, - position, - })?; + let local = self.locals.get_mut(local_index as usize).ok_or_else(|| { + ChunkError::LocalIndexOutOfBounds { + index: local_index as usize, + position, + } + })?; local.register_index = Some(register_index); @@ -319,7 +318,7 @@ impl<'a> ChunkDisassembler<'a> { for (offset, (instruction, position)) in self.chunk.instructions.iter().enumerate() { let position = position.to_string(); - let operation = instruction.operation.to_string(); + let operation = instruction.operation().to_string(); let info_option = instruction.disassembly_info(Some(self.chunk)); let instruction_display = if let Some(info) = info_option { format!("{offset:<7} {operation:14} {info:25} {position:8}") diff --git a/dust-lang/src/instruction.rs b/dust-lang/src/instruction.rs index 167fcac..ac716c6 100644 --- a/dust-lang/src/instruction.rs +++ b/dust-lang/src/instruction.rs @@ -2,112 +2,166 @@ use std::fmt::{self, Display, Formatter}; use crate::{Chunk, Span}; -#[derive(Clone, Copy, Debug, PartialEq)] -pub struct Instruction { - pub operation: Operation, - pub destination: u8, - pub arguments: [u8; 2], -} +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Instruction(u32); impl Instruction { pub fn r#move(to_register: u8, from_register: u8) -> Instruction { - Instruction { - operation: Operation::Move, - destination: to_register, - arguments: [from_register, 0], - } + let mut instruction = Instruction(Operation::Move as u32); + + instruction.set_destination(to_register); + instruction.set_first_argument(from_register); + + instruction } pub fn close(to_register: u8) -> Instruction { - Instruction { - operation: Operation::Close, - destination: to_register, - arguments: [0, 0], - } + let mut instruction = Instruction(Operation::Close as u32); + + instruction.set_destination(to_register); + + instruction } pub fn load_constant(to_register: u8, constant_index: u8) -> Instruction { - Instruction { - operation: Operation::LoadConstant, - destination: to_register, - arguments: [constant_index, 0], - } + let mut instruction = Instruction(Operation::LoadConstant as u32); + + instruction.set_destination(to_register); + instruction.set_first_argument(constant_index); + + instruction } pub fn declare_local(to_register: u8, variable_index: u8) -> Instruction { - Instruction { - operation: Operation::DeclareLocal, - destination: to_register, - arguments: [variable_index, 0], - } + let mut instruction = Instruction(Operation::DeclareLocal as u32); + + instruction.set_destination(to_register); + instruction.set_first_argument(variable_index); + + instruction } pub fn get_local(to_register: u8, variable_index: u8) -> Instruction { - Instruction { - operation: Operation::GetLocal, - destination: to_register, - arguments: [variable_index, 0], - } + let mut instruction = Instruction(Operation::GetLocal as u32); + + instruction.set_destination(to_register); + instruction.set_first_argument(variable_index); + + instruction } pub fn set_local(from_register: u8, variable_index: u8) -> Instruction { - Instruction { - operation: Operation::SetLocal, - destination: from_register, - arguments: [variable_index, 0], - } + let mut instruction = Instruction(Operation::SetLocal as u32); + + instruction.set_destination(from_register); + instruction.set_first_argument(variable_index); + + instruction } - pub fn add(to_register: u8, left_register: u8, right_register: u8) -> Instruction { - Instruction { - operation: Operation::Add, - destination: to_register, - arguments: [left_register, right_register], - } + pub fn add(to_register: u8, left_index: u8, right_index: u8) -> Instruction { + let mut instruction = Instruction(Operation::Add as u32); + + instruction.set_destination(to_register); + instruction.set_first_argument(left_index); + instruction.set_second_argument(right_index); + + instruction } - pub fn subtract(to_register: u8, left_register: u8, right_register: u8) -> Instruction { - Instruction { - operation: Operation::Subtract, - destination: to_register, - arguments: [left_register, right_register], - } + pub fn subtract(to_register: u8, left_index: u8, right_index: u8) -> Instruction { + let mut instruction = Instruction(Operation::Subtract as u32); + + instruction.set_destination(to_register); + instruction.set_first_argument(left_index); + instruction.set_second_argument(right_index); + + instruction } - pub fn multiply(to_register: u8, left_register: u8, right_register: u8) -> Instruction { - Instruction { - operation: Operation::Multiply, - destination: to_register, - arguments: [left_register, right_register], - } + pub fn multiply(to_register: u8, left_index: u8, right_index: u8) -> Instruction { + let mut instruction = Instruction(Operation::Multiply as u32); + + instruction.set_destination(to_register); + instruction.set_first_argument(left_index); + instruction.set_second_argument(right_index); + + instruction } - pub fn divide(to_register: u8, left_register: u8, right_register: u8) -> Instruction { - Instruction { - operation: Operation::Divide, - destination: to_register, - arguments: [left_register, right_register], - } + pub fn divide(to_register: u8, left_index: u8, right_index: u8) -> Instruction { + let mut instruction = Instruction(Operation::Divide as u32); + + instruction.set_destination(to_register); + instruction.set_first_argument(left_index); + instruction.set_second_argument(right_index); + + instruction } - pub fn negate(to_register: u8, from_register: u8) -> Instruction { - Instruction { - operation: Operation::Negate, - destination: to_register, - arguments: [from_register, 0], - } + pub fn negate(to_register: u8, from_index: u8) -> Instruction { + let mut instruction = Instruction(Operation::Negate as u32); + + instruction.set_destination(to_register); + instruction.set_first_argument(from_index); + + instruction } pub fn r#return() -> Instruction { - Instruction { - operation: Operation::Return, - destination: 0, - arguments: [0, 0], - } + Instruction(Operation::Return as u32) + } + + pub fn set_first_argument_to_constant(&mut self) { + self.0 |= 0b1000_0000; + } + + pub fn first_argument_is_constant(&self) -> bool { + self.0 & 0b1000_0000 != 0 + } + + pub fn set_second_argument_to_constant(&mut self) { + self.0 |= 0b0100_0000; + } + + pub fn second_argument_is_constant(&self) -> bool { + self.0 & 0b0100_0000 != 0 + } + + pub fn destination(&self) -> u8 { + (self.0 >> 24) as u8 + } + + pub fn set_destination(&mut self, destination: u8) { + self.0 |= (destination as u32) << 24; + } + + pub fn first_argument(&self) -> u8 { + (self.0 >> 16) as u8 + } + + pub fn set_first_argument(&mut self, argument: u8) { + self.0 |= (argument as u32) << 16; + } + + pub fn second_argument(&self) -> u8 { + (self.0 >> 8) as u8 + } + + pub fn set_second_argument(&mut self, argument: u8) { + self.0 |= (argument as u32) << 8; + } + + pub fn operation(&self) -> Operation { + Operation::from((self.0 & 0b0000_0000_0011_1111) as u8) + } + + pub fn set_operation(&mut self, operation: Operation) { + self.0 |= u8::from(operation) as u32; } pub fn disassemble(&self, chunk: &Chunk) -> String { - let mut disassembled = format!("{:16} ", self.operation.to_string()); + let mut disassembled = format!("{:16} ", self.operation().to_string()); if let Some(info) = self.disassembly_info(Some(chunk)) { disassembled.push_str(&info); @@ -117,30 +171,37 @@ impl Instruction { } pub fn disassembly_info(&self, chunk: Option<&Chunk>) -> Option { - let info = match self.operation { + let info = match self.operation() { Operation::Move => { - format!("R({}) = R({})", self.destination, self.arguments[0]) + format!("R({}) = R({})", self.destination(), self.first_argument()) } - Operation::Close => format!("R({})", self.destination), + Operation::Close => format!("R({})", self.destination()), Operation::LoadConstant => { - let constant_index = self.arguments[0]; + let constant_index = self.first_argument(); if let Some(chunk) = chunk { match chunk.get_constant(constant_index, Span(0, 0)) { Ok(value) => { - format!("R({}) = C({}) {}", self.destination, constant_index, value) + format!( + "R({}) = C({}) {}", + self.destination(), + constant_index, + value + ) } Err(error) => format!( "R({}) = C({}) {:?}", - self.destination, constant_index, error + self.destination(), + constant_index, + error ), } } else { - format!("R({}) = C({})", self.destination, constant_index) + format!("R({}) = C({})", self.destination(), constant_index) } } Operation::DeclareLocal => { - let local_index = self.arguments[0]; + let local_index = self.first_argument(); let identifier_display = if let Some(chunk) = chunk { match chunk.get_identifier(local_index) { Some(identifier) => identifier.to_string(), @@ -152,16 +213,18 @@ impl Instruction { format!( "L({}) = R({}) {}", - local_index, self.destination, identifier_display + local_index, + self.destination(), + identifier_display ) } Operation::GetLocal => { - let local_index = self.arguments[0]; + let local_index = self.first_argument(); - format!("R({}) = L({})", self.destination, local_index) + format!("R({}) = L({})", self.destination(), local_index) } Operation::SetLocal => { - let local_index = self.arguments[0]; + let local_index = self.first_argument(); let identifier_display = if let Some(chunk) = chunk { match chunk.get_identifier(local_index) { Some(identifier) => identifier.to_string(), @@ -173,35 +236,73 @@ impl Instruction { format!( "L({}) = R({}) {}", - local_index, self.destination, identifier_display + local_index, + self.destination(), + identifier_display ) } Operation::Add => { - format!( - "R({}) = RC({}) + RC({})", - self.destination, self.arguments[0], self.arguments[1] - ) + let destination = self.destination(); + let first_argument = if self.first_argument_is_constant() { + format!("C({})", self.first_argument()) + } else { + format!("R({})", self.first_argument()) + }; + let second_argument = if self.second_argument_is_constant() { + format!("C({})", self.second_argument()) + } else { + format!("R({})", self.second_argument()) + }; + + format!("R({destination}) = {first_argument} + {second_argument}",) } Operation::Subtract => { - format!( - "R({}) = RC({}) - RC({})", - self.destination, self.arguments[0], self.arguments[1] - ) + let destination = self.destination(); + let first_argument = if self.first_argument_is_constant() { + format!("C({})", self.first_argument()) + } else { + format!("R({})", self.first_argument()) + }; + let second_argument = if self.second_argument_is_constant() { + format!("C({})", self.second_argument()) + } else { + format!("R({})", self.second_argument()) + }; + + format!("R({destination}) = {first_argument} - {second_argument}",) } Operation::Multiply => { - format!( - "R({}) = RC({}) * RC({})", - self.destination, self.arguments[0], self.arguments[1] - ) + let destination = self.destination(); + let first_argument = if self.first_argument_is_constant() { + format!("C({})", self.first_argument()) + } else { + format!("R({})", self.first_argument()) + }; + let second_argument = if self.second_argument_is_constant() { + format!("C({})", self.second_argument()) + } else { + format!("R({})", self.second_argument()) + }; + + format!("R({destination}) = {first_argument} * {second_argument}",) } Operation::Divide => { - format!( - "R({}) = RC({}) / RC({})", - self.destination, self.arguments[0], self.arguments[1] - ) + let destination = self.destination(); + let first_argument = if self.first_argument_is_constant() { + format!("C({})", self.first_argument()) + } else { + format!("R({})", self.first_argument()) + }; + let second_argument = if self.second_argument_is_constant() { + format!("C({})", self.second_argument()) + } else { + format!("R({})", self.second_argument()) + }; + + format!("R({destination}) = {first_argument} / {second_argument}",) } Operation::Negate => { - format!("R({}) = -RC({})", self.destination, self.arguments[0]) + format!("R({}) = -RC({})", self.destination(), self.first_argument()) } Operation::Return => return None, }; @@ -213,38 +314,51 @@ impl Instruction { impl Display for Instruction { fn fmt(&self, f: &mut Formatter) -> fmt::Result { if let Some(info) = self.disassembly_info(None) { - write!(f, "{} {}", self.operation, info) + write!(f, "{} {}", self.operation(), info) } else { - write!(f, "{}", self.operation) + write!(f, "{}", self.operation()) } } } +const MOVE: u8 = 0b0000_0000; +const CLOSE: u8 = 0b000_0001; +const LOAD_CONSTANT: u8 = 0b0000_0010; +const DECLARE_LOCAL: u8 = 0b0000_0011; +const GET_LOCAL: u8 = 0b0000_0100; +const SET_LOCAL: u8 = 0b0000_0101; +const ADD: u8 = 0b0000_0110; +const SUBTRACT: u8 = 0b0000_0111; +const MULTIPLY: u8 = 0b0000_1000; +const DIVIDE: u8 = 0b0000_1001; +const NEGATE: u8 = 0b0000_1010; +const RETURN: u8 = 0b0000_1011; + #[derive(Clone, Copy, Debug, PartialEq)] pub enum Operation { // Stack manipulation - Move = 0, - Close = 1, + Move = MOVE as isize, + Close = CLOSE as isize, // Constants - LoadConstant = 2, + LoadConstant = LOAD_CONSTANT as isize, // Variables - DeclareLocal = 3, - GetLocal = 4, - SetLocal = 5, + DeclareLocal = DECLARE_LOCAL as isize, + GetLocal = GET_LOCAL as isize, + SetLocal = SET_LOCAL as isize, // Binary operations - Add = 6, - Subtract = 7, - Multiply = 8, - Divide = 9, + Add = ADD as isize, + Subtract = SUBTRACT as isize, + Multiply = MULTIPLY as isize, + Divide = DIVIDE as isize, // Unary operations - Negate = 10, + Negate = NEGATE as isize, // Control flow - Return = 11, + Return = RETURN as isize, } impl Operation { @@ -259,22 +373,41 @@ impl Operation { impl From for Operation { fn from(byte: u8) -> Self { match byte { - 0 => Operation::Move, - 1 => Operation::Close, - 2 => Operation::LoadConstant, - 3 => Operation::DeclareLocal, - 4 => Operation::GetLocal, - 5 => Operation::SetLocal, - 6 => Operation::Add, - 7 => Operation::Subtract, - 8 => Operation::Multiply, - 9 => Operation::Divide, - 10 => Operation::Negate, + MOVE => Operation::Move, + CLOSE => Operation::Close, + LOAD_CONSTANT => Operation::LoadConstant, + DECLARE_LOCAL => Operation::DeclareLocal, + GET_LOCAL => Operation::GetLocal, + SET_LOCAL => Operation::SetLocal, + ADD => Operation::Add, + SUBTRACT => Operation::Subtract, + MULTIPLY => Operation::Multiply, + DIVIDE => Operation::Divide, + NEGATE => Operation::Negate, _ => Operation::Return, } } } +impl From for u8 { + fn from(operation: Operation) -> Self { + match operation { + Operation::Move => MOVE, + Operation::Close => CLOSE, + Operation::LoadConstant => LOAD_CONSTANT, + Operation::DeclareLocal => DECLARE_LOCAL, + Operation::GetLocal => GET_LOCAL, + Operation::SetLocal => SET_LOCAL, + Operation::Add => ADD, + Operation::Subtract => SUBTRACT, + Operation::Multiply => MULTIPLY, + Operation::Divide => DIVIDE, + Operation::Negate => NEGATE, + Operation::Return => RETURN, + } + } +} + impl Display for Operation { fn fmt(&self, f: &mut Formatter) -> fmt::Result { match self { @@ -296,12 +429,157 @@ impl Display for Operation { #[cfg(test)] mod tests { - use std::mem::size_of; - use super::*; #[test] - fn instruction_is_32_bits() { - assert_eq!(size_of::(), 4); + fn r#move() { + let mut instruction = Instruction::r#move(255, 255); + + instruction.set_first_argument_to_constant(); + instruction.set_second_argument_to_constant(); + + assert_eq!(instruction.operation(), Operation::Move); + assert_eq!(instruction.destination(), 255); + assert_eq!(instruction.first_argument(), 255); + assert!(instruction.first_argument_is_constant()); + assert!(instruction.second_argument_is_constant()); + } + + #[test] + fn close() { + let mut instruction = Instruction::close(255); + + instruction.set_first_argument_to_constant(); + instruction.set_second_argument_to_constant(); + + assert_eq!(instruction.operation(), Operation::Close); + assert!(instruction.first_argument_is_constant()); + assert!(instruction.second_argument_is_constant()); + } + + #[test] + fn load_constant() { + let mut instruction = Instruction::load_constant(255, 255); + + instruction.set_first_argument_to_constant(); + instruction.set_second_argument_to_constant(); + + assert_eq!(instruction.operation(), Operation::LoadConstant); + assert_eq!(instruction.destination(), 255); + assert_eq!(instruction.first_argument(), 255); + assert!(instruction.first_argument_is_constant()); + assert!(instruction.second_argument_is_constant()); + } + + #[test] + fn declare_local() { + let mut instruction = Instruction::declare_local(255, 255); + + instruction.set_first_argument_to_constant(); + instruction.set_second_argument_to_constant(); + + assert_eq!(instruction.operation(), Operation::DeclareLocal); + assert_eq!(instruction.destination(), 255); + assert_eq!(instruction.first_argument(), 255); + assert!(instruction.first_argument_is_constant()); + assert!(instruction.second_argument_is_constant()); + } + + #[test] + fn add() { + let mut instruction = Instruction::add(255, 255, 2); + + instruction.set_operation(Operation::Add); + + instruction.set_first_argument_to_constant(); + instruction.set_second_argument_to_constant(); + + assert_eq!(instruction.operation(), Operation::Add); + assert_eq!(instruction.destination(), 255); + assert_eq!(instruction.first_argument(), 255); + assert_eq!(instruction.second_argument(), 2); + assert!(instruction.first_argument_is_constant()); + assert!(instruction.second_argument_is_constant()); + } + + #[test] + fn subtract() { + let mut instruction = Instruction::subtract(255, 255, 255); + + instruction.set_operation(Operation::Subtract); + + instruction.set_first_argument_to_constant(); + instruction.set_second_argument_to_constant(); + + assert_eq!(instruction.operation(), Operation::Subtract); + assert_eq!(instruction.destination(), 255); + assert_eq!(instruction.first_argument(), 255); + assert_eq!(instruction.second_argument(), 255); + assert!(instruction.first_argument_is_constant()); + assert!(instruction.second_argument_is_constant()); + } + + #[test] + fn multiply() { + let mut instruction = Instruction::multiply(255, 255, 255); + + instruction.set_operation(Operation::Multiply); + + instruction.set_first_argument_to_constant(); + instruction.set_second_argument_to_constant(); + + assert_eq!(instruction.operation(), Operation::Multiply); + assert_eq!(instruction.destination(), 255); + assert_eq!(instruction.first_argument(), 255); + assert_eq!(instruction.second_argument(), 255); + assert!(instruction.first_argument_is_constant()); + assert!(instruction.second_argument_is_constant()); + } + + #[test] + fn divide() { + let mut instruction = Instruction::divide(255, 255, 255); + + instruction.set_operation(Operation::Divide); + + instruction.set_first_argument_to_constant(); + instruction.set_second_argument_to_constant(); + + assert_eq!(instruction.operation(), Operation::Divide); + assert_eq!(instruction.destination(), 255); + assert_eq!(instruction.first_argument(), 255); + assert_eq!(instruction.second_argument(), 255); + assert!(instruction.first_argument_is_constant()); + assert!(instruction.second_argument_is_constant()); + } + + #[test] + fn negate() { + let mut instruction = Instruction::negate(255, 255); + + instruction.set_operation(Operation::Negate); + + instruction.set_first_argument_to_constant(); + instruction.set_second_argument_to_constant(); + + assert_eq!(instruction.operation(), Operation::Negate); + assert_eq!(instruction.destination(), 255); + assert_eq!(instruction.first_argument(), 255); + assert!(instruction.first_argument_is_constant()); + assert!(instruction.second_argument_is_constant()); + } + + #[test] + fn r#return() { + let mut instruction = Instruction::r#return(); + + instruction.set_operation(Operation::Return); + + instruction.set_first_argument_to_constant(); + instruction.set_second_argument_to_constant(); + + assert_eq!(instruction.operation(), Operation::Return); + assert!(instruction.first_argument_is_constant()); + assert!(instruction.second_argument_is_constant()); } } diff --git a/dust-lang/src/parser/mod.rs b/dust-lang/src/parser/mod.rs index 33d08da..4af4326 100644 --- a/dust-lang/src/parser/mod.rs +++ b/dust-lang/src/parser/mod.rs @@ -258,15 +258,11 @@ impl<'src> Parser<'src> { let mut push_back_right = false; let (right_instruction, right_position) = self.chunk.pop_instruction(self.current_position)?; - let right_register = match right_instruction { - Instruction { - operation: Operation::LoadConstant, - arguments, - .. - } => { + let right = match right_instruction.operation() { + Operation::LoadConstant => { self.decrement_register()?; - arguments[0] + right_instruction.first_argument() } _ => { push_back_right = true; @@ -277,15 +273,11 @@ impl<'src> Parser<'src> { let mut push_back_left = false; let (left_instruction, left_position) = self.chunk.pop_instruction(self.current_position)?; - let left_register = match left_instruction { - Instruction { - operation: Operation::LoadConstant, - arguments, - .. - } => { + let left = match left_instruction.operation() { + Operation::LoadConstant => { self.decrement_register()?; - arguments[0] + left_instruction.first_argument() } _ => { push_back_left = true; @@ -294,28 +286,19 @@ impl<'src> Parser<'src> { } }; - if push_back_right { - self.chunk - .push_instruction(right_instruction, right_position); - } - if push_back_left { - self.chunk.push_instruction(left_instruction, left_position); + self.emit_instruction(left_instruction, left_position); } - 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) - } + if push_back_right { + self.emit_instruction(right_instruction, right_position); + } + + let mut instruction = match operator { + TokenKind::Plus => Instruction::add(self.current_register, left, right), + TokenKind::Minus => Instruction::subtract(self.current_register, left, right), + TokenKind::Star => Instruction::multiply(self.current_register, left, right), + TokenKind::Slash => Instruction::divide(self.current_register, left, right), _ => { return Err(ParseError::ExpectedTokenMultiple { expected: vec![ @@ -330,6 +313,14 @@ impl<'src> Parser<'src> { } }; + if !push_back_left { + instruction.set_first_argument_to_constant(); + } + + if !push_back_right { + instruction.set_second_argument_to_constant(); + } + self.increment_register()?; self.emit_instruction(instruction, operator_position); @@ -351,14 +342,14 @@ impl<'src> Parser<'src> { let (mut previous_instruction, previous_position) = self.chunk.pop_instruction(self.previous_position)?; - if previous_instruction.operation.is_binary() { + 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; + previous_instruction.set_destination(register_index); self.emit_instruction(previous_instruction, self.previous_position); self.decrement_register()?; @@ -483,23 +474,15 @@ impl<'src> Parser<'src> { let (previous_instruction, previous_position) = self.chunk.pop_instruction(self.current_position)?; - if let Instruction { - operation: Operation::GetLocal, - destination, - arguments, - } = previous_instruction - { - self.emit_instruction( - Instruction { - operation: Operation::Move, - destination, - arguments, - }, - position, + if let Operation::GetLocal = previous_instruction.operation() { + let move_instruction = Instruction::r#move( + previous_instruction.destination(), + previous_instruction.first_argument(), ); + + self.emit_instruction(move_instruction, position); } else { - self.chunk - .push_instruction(previous_instruction, previous_position); + self.emit_instruction(previous_instruction, previous_position); } self.emit_instruction( diff --git a/dust-lang/src/vm.rs b/dust-lang/src/vm.rs index 6f9152e..b9c0c9c 100644 --- a/dust-lang/src/vm.rs +++ b/dust-lang/src/vm.rs @@ -38,31 +38,31 @@ impl Vm { while let Ok((instruction, position)) = self.read(Span(0, 0)).copied() { log::trace!("Running instruction {instruction} at {position}"); - match instruction.operation { + match instruction.operation() { Operation::Move => { - let from = instruction.arguments[0]; - let to = instruction.destination; + let from = instruction.first_argument(); + let to = instruction.destination(); let value = self.clone(from, position)?; self.insert(value, to, position)?; } Operation::Close => todo!(), Operation::LoadConstant => { - let to_register = instruction.destination; - let from_constant = instruction.arguments[0]; + let to_register = instruction.destination(); + let from_constant = instruction.first_argument(); let value = self.chunk.take_constant(from_constant, position)?; self.insert(value, to_register, position)?; } Operation::DeclareLocal => { - let from_register = instruction.destination; - let to_local = u16::from_le_bytes(instruction.arguments) as usize; + let from_register = instruction.destination(); + let to_local = instruction.first_argument(); self.chunk.define_local(to_local, from_register, position)?; } Operation::GetLocal => { - let register_index = instruction.destination; - let local_index = instruction.arguments[0]; + let register_index = instruction.destination(); + let local_index = instruction.first_argument(); let local = self.chunk.get_local(local_index, position)?; let value = if let Some(value_index) = &local.register_index { self.clone(*value_index, position)? @@ -76,63 +76,90 @@ impl Vm { self.insert(value, register_index, position)?; } Operation::SetLocal => { - let from_register = instruction.destination; - let to_local = instruction.arguments[0] as usize; + let from_register = instruction.destination(); + let to_local = instruction.first_argument(); self.chunk.define_local(to_local, from_register, position)?; } Operation::Add => { - let left = - self.take_constant_or_clone_register(instruction.arguments[0], position)?; - let right = - self.take_constant_or_clone_register(instruction.arguments[1], position)?; + let left = self.take_constant_or_clone_register( + instruction.first_argument(), + instruction.first_argument_is_constant(), + position, + )?; + let right = self.take_constant_or_clone_register( + instruction.second_argument(), + instruction.second_argument_is_constant(), + position, + )?; let sum = left .add(&right) .map_err(|error| VmError::Value { error, position })?; - self.insert(sum, instruction.destination, position)?; + self.insert(sum, instruction.destination(), position)?; } Operation::Subtract => { - let left = - self.take_constant_or_clone_register(instruction.arguments[0], position)?; - let right = - self.take_constant_or_clone_register(instruction.arguments[1], position)?; + let left = self.take_constant_or_clone_register( + instruction.first_argument(), + instruction.first_argument_is_constant(), + position, + )?; + let right = self.take_constant_or_clone_register( + instruction.second_argument(), + instruction.second_argument_is_constant(), + position, + )?; let difference = left .subtract(&right) .map_err(|error| VmError::Value { error, position })?; - self.insert(difference, instruction.destination, position)?; + self.insert(difference, instruction.destination(), position)?; } Operation::Multiply => { - let left = - self.take_constant_or_clone_register(instruction.arguments[0], position)?; - let right = - self.take_constant_or_clone_register(instruction.arguments[1], position)?; + let left = self.take_constant_or_clone_register( + instruction.first_argument(), + instruction.first_argument_is_constant(), + position, + )?; + let right = self.take_constant_or_clone_register( + instruction.second_argument(), + instruction.second_argument_is_constant(), + position, + )?; let product = left .multiply(&right) .map_err(|error| VmError::Value { error, position })?; - self.insert(product, instruction.destination, position)?; + self.insert(product, instruction.destination(), position)?; } Operation::Divide => { - let left = - self.take_constant_or_clone_register(instruction.arguments[0], position)?; - let right = - self.take_constant_or_clone_register(instruction.arguments[1], position)?; + let left = self.take_constant_or_clone_register( + instruction.first_argument(), + instruction.first_argument_is_constant(), + position, + )?; + let right = self.take_constant_or_clone_register( + instruction.second_argument(), + instruction.second_argument_is_constant(), + position, + )?; let quotient = left .divide(&right) .map_err(|error| VmError::Value { error, position })?; - self.insert(quotient, instruction.destination, position)?; + self.insert(quotient, instruction.destination(), position)?; } Operation::Negate => { - let value = - self.take_constant_or_clone_register(instruction.arguments[0], position)?; + let value = self.take_constant_or_clone_register( + instruction.first_argument(), + instruction.first_argument_is_constant(), + position, + )?; let negated = value .negate() .map_err(|error| VmError::Value { error, position })?; - self.insert(negated, instruction.destination, position)?; + self.insert(negated, instruction.destination(), position)?; } Operation::Return => { let value = self.pop(position)?; @@ -186,14 +213,13 @@ impl Vm { fn take_constant_or_clone_register( &mut self, index: u8, + is_constant: bool, position: Span, ) -> Result { - if let Ok(value) = self.chunk.take_constant(index, position) { - Ok(value) + if is_constant { + Ok(self.chunk.take_constant(index, position)?) } else { - let value = self.clone(index, position)?; - - Ok(value) + self.clone(index, position) } }