diff --git a/dust-lang/src/compiler.rs b/dust-lang/src/compiler.rs index 03c2f36..bbda60a 100644 --- a/dust-lang/src/compiler.rs +++ b/dust-lang/src/compiler.rs @@ -13,9 +13,15 @@ use std::{ use colored::Colorize; use crate::{ - value::ConcreteValue, AnnotatedError, Chunk, ChunkError, DustError, FunctionType, Instruction, - LexError, Lexer, Local, NativeFunction, Operation, Optimizer, Scope, Span, Token, TokenKind, - TokenOwned, Type, TypeConflict, + instruction::{ + Add, Call, CallNative, Close, DefineLocal, Divide, Equal, GetLocal, Jump, Less, LessEqual, + LoadBoolean, LoadConstant, LoadList, LoadSelf, Modulo, Move, Multiply, Negate, Not, Return, + SetLocal, Subtract, Test, + }, + value::ConcreteValue, + AnnotatedError, Argument, Chunk, ChunkError, DustError, FunctionType, Instruction, LexError, + Lexer, Local, NativeFunction, Operation, Optimizer, Scope, Span, Token, TokenKind, TokenOwned, + Type, TypeConflict, }; /// Compiles the input and returns a chunk. @@ -429,12 +435,12 @@ impl<'src> Compiler<'src> { position: Span, ) -> Result<(), CompileError> { let constant_index = self.chunk.push_or_get_constant(constant); - let register = self.next_register(); - let instruction = Instruction::builder(Operation::LoadConstant) - .a(register) - .b(constant_index) - .c_to_boolean(false) - .build(); + let destination = self.next_register(); + let instruction = Instruction::from(LoadConstant { + destination, + constant_index, + jump_next: false, + }); self.emit_instruction(instruction, position); @@ -448,12 +454,12 @@ impl<'src> Compiler<'src> { self.advance()?; let boolean = text.parse::().unwrap(); - let register = self.next_register(); - let instruction = Instruction::builder(Operation::LoadBoolean) - .a(register) - .b_to_boolean(boolean) - .c_to_boolean(false) - .build(); + let destination = self.next_register(); + let instruction = Instruction::from(LoadBoolean { + destination, + value: boolean, + jump_next: false, + }); self.emit_instruction(instruction, position); @@ -611,37 +617,25 @@ impl<'src> Compiler<'src> { self.parse_expression()?; let (previous_instruction, previous_position) = self.pop_last_instruction()?; - let (push_back, is_constant, argument) = { - match previous_instruction.operation() { - Operation::GetLocal => (false, false, previous_instruction.a()), - Operation::LoadConstant => (false, true, previous_instruction.a()), - Operation::LoadBoolean => (true, false, previous_instruction.a()), - Operation::Close => { - return Err(CompileError::ExpectedExpression { - found: self.previous_token.to_owned(), - position: self.previous_position, - }); - } - _ => (true, false, previous_instruction.a()), - } + let argument = if let Some(argument) = previous_instruction.destination_as_argument() { + argument + } else { + return Err(CompileError::ExpectedExpression { + found: self.previous_token.to_owned(), + position: previous_position, + }); }; - if push_back { - self.emit_instruction(previous_instruction, previous_position); - } - - let register = self.next_register(); + let destination = self.next_register(); let instruction = match operator.kind() { - TokenKind::Bang => Instruction::builder(Operation::Not) - .a(register) - .b(argument) - .b_is_constant(is_constant) - .build(), - TokenKind::Minus => Instruction::builder(Operation::Negate) - .a(register) - .b(argument) - .b_is_constant(is_constant) - .build(), + TokenKind::Bang => Instruction::from(Not { + destination, + argument, + }), + TokenKind::Minus => Instruction::from(Negate { + destination, + argument, + }), _ => { return Err(CompileError::ExpectedTokenMultiple { expected: &[TokenKind::Bang, TokenKind::Minus], @@ -660,48 +654,6 @@ impl<'src> Compiler<'src> { Ok(()) } - fn handle_binary_argument( - &mut self, - instruction: &Instruction, - ) -> Result<(bool, bool, bool, bool, u16), CompileError> { - let mut push_back = false; - let mut is_constant = false; - let mut is_local = false; - let mut is_mutable_local = false; - let argument = match instruction.operation() { - Operation::GetLocal => { - let local_index = instruction.b(); - let local = self.get_local(local_index)?; - is_local = true; - is_mutable_local = local.is_mutable; - - local_index - } - Operation::LoadConstant => { - is_constant = true; - - instruction.b() - } - Operation::Close => { - return Err(CompileError::ExpectedExpression { - found: self.previous_token.to_owned(), - position: self.previous_position, - }); - } - _ => { - push_back = true; - - if instruction.yields_value() { - instruction.a() - } else { - self.next_register() - } - } - }; - - Ok((push_back, is_constant, is_local, is_mutable_local, argument)) - } - fn parse_math_binary(&mut self) -> Result<(), CompileError> { let (left_instruction, left_position) = self.chunk.instructions_mut().pop().ok_or_else(|| { @@ -710,13 +662,17 @@ impl<'src> Compiler<'src> { position: self.previous_position, } })?; - let (push_back_left, left_is_constant, left_is_local, left_is_mutable_local, left) = - self.handle_binary_argument(&left_instruction)?; - - if push_back_left { - self.emit_instruction(left_instruction, left_position); - } - + let left = left_instruction.destination_as_argument().ok_or_else(|| { + CompileError::ExpectedExpression { + found: self.previous_token.to_owned(), + position: left_position, + } + })?; + let left_is_mutable_local = if let Argument::Local(local_index) = left { + self.get_local(local_index)?.is_mutable + } else { + false + }; let operator = self.current_token; let operator_position = self.current_position; let rule = ParseRule::from(&operator); @@ -740,29 +696,69 @@ impl<'src> Compiler<'src> { self.parse_sub_expression(&rule.precedence)?; let (right_instruction, right_position) = self.pop_last_instruction()?; - let (push_back_right, right_is_constant, right_is_local, _, right) = - self.handle_binary_argument(&right_instruction)?; + let right = right_instruction.destination_as_argument().ok_or_else(|| { + CompileError::ExpectedExpression { + found: self.previous_token.to_owned(), + position: right_position, + } + })?; - if push_back_right { - self.emit_instruction(right_instruction, right_position); - } - - let register = if is_assignment { - left + let destination = if is_assignment { + left.index() } else { self.next_register() }; - let operation = match operator { - Token::Plus => Operation::Add, - Token::PlusEqual => Operation::Add, - Token::Minus => Operation::Subtract, - Token::MinusEqual => Operation::Subtract, - Token::Star => Operation::Multiply, - Token::StarEqual => Operation::Multiply, - Token::Slash => Operation::Divide, - Token::SlashEqual => Operation::Divide, - Token::Percent => Operation::Modulo, - Token::PercentEqual => Operation::Modulo, + let instruction = match operator { + Token::Plus => Instruction::from(Add { + destination, + left, + right, + }), + Token::PlusEqual => Instruction::from(Add { + destination, + left, + right, + }), + Token::Minus => Instruction::from(Subtract { + destination, + left, + right, + }), + Token::MinusEqual => Instruction::from(Subtract { + destination, + left, + right, + }), + Token::Star => Instruction::from(Multiply { + destination, + left, + right, + }), + Token::StarEqual => Instruction::from(Multiply { + destination, + left, + right, + }), + Token::Slash => Instruction::from(Divide { + destination, + left, + right, + }), + Token::SlashEqual => Instruction::from(Divide { + destination, + left, + right, + }), + Token::Percent => Instruction::from(Modulo { + destination, + left, + right, + }), + Token::PercentEqual => Instruction::from(Modulo { + destination, + left, + right, + }), _ => { return Err(CompileError::ExpectedTokenMultiple { expected: &[ @@ -783,14 +779,6 @@ impl<'src> Compiler<'src> { } }; - let instruction = Instruction::builder(operation) - .a(register) - .b(left) - .c(right) - .b_is_constant(left_is_constant) - .c_is_constant(right_is_constant) - .build(); - self.emit_instruction(instruction, operator_position); if is_assignment { @@ -818,8 +806,12 @@ impl<'src> Compiler<'src> { position: self.previous_position, } })?; - let (push_back_left, left_is_constant, left_is_local, _, left) = - self.handle_binary_argument(&left_instruction)?; + let left = left_instruction.destination_as_argument().ok_or_else(|| { + CompileError::ExpectedExpression { + found: self.previous_token.to_owned(), + position: left_position, + } + })?; let operator = self.current_token; let operator_position = self.current_position; let rule = ParseRule::from(&operator); @@ -834,15 +826,43 @@ impl<'src> Compiler<'src> { position: self.previous_position, } })?; - let (push_back_right, right_is_constant, right_is_local, _, right) = - self.handle_binary_argument(&right_instruction)?; - let (operation, instruction_boolean) = match operator { - Token::DoubleEqual => (Operation::Equal, true), - Token::BangEqual => (Operation::Equal, false), - Token::Less => (Operation::Less, true), - Token::LessEqual => (Operation::LessEqual, true), - Token::Greater => (Operation::LessEqual, false), - Token::GreaterEqual => (Operation::Less, false), + let right = right_instruction.destination_as_argument().ok_or_else(|| { + CompileError::ExpectedExpression { + found: self.previous_token.to_owned(), + position: right_position, + } + })?; + let instruction = match operator { + Token::DoubleEqual => Instruction::from(Equal { + value: true, + left, + right, + }), + Token::BangEqual => Instruction::from(Equal { + value: false, + left, + right, + }), + Token::Less => Instruction::from(Less { + value: true, + left, + right, + }), + Token::LessEqual => Instruction::from(LessEqual { + value: true, + left, + right, + }), + Token::Greater => Instruction::from(LessEqual { + value: false, + left, + right, + }), + Token::GreaterEqual => Instruction::from(Less { + value: false, + left, + right, + }), _ => { return Err(CompileError::ExpectedTokenMultiple { expected: &[ @@ -858,37 +878,22 @@ impl<'src> Compiler<'src> { }) } }; - let instruction = Instruction::builder(operation) - .a_to_boolean(instruction_boolean) - .b(left) - .c(right) - .b_is_constant(left_is_constant) - .c_is_constant(right_is_constant) - .build(); - - if push_back_left { - self.emit_instruction(left_instruction, left_position); - } - - if push_back_right { - self.emit_instruction(right_instruction, right_position); - } let register = self.next_register(); - let jump = Instruction::builder(Operation::Jump) - .a(1) - .b_to_boolean(true) - .build(); - let load_true = Instruction::builder(Operation::LoadBoolean) - .a(register) - .b_to_boolean(true) - .c_to_boolean(true) - .build(); - let load_false = Instruction::builder(Operation::LoadBoolean) - .a(register) - .b_to_boolean(false) - .c_to_boolean(false) - .build(); + let jump = Instruction::from(Jump { + offset: 1, + is_positive: true, + }); + let load_true = Instruction::from(LoadBoolean { + destination: register, + value: true, + jump_next: true, + }); + let load_false = Instruction::from(LoadBoolean { + destination: register, + value: false, + jump_next: false, + }); self.emit_instruction(instruction, operator_position); self.emit_instruction(jump, operator_position); @@ -910,11 +915,15 @@ impl<'src> Compiler<'src> { }); } - let (_, is_constant, is_local, _, _) = self.handle_binary_argument(&left_instruction)?; + let argument = left_instruction.destination_as_argument().ok_or_else(|| { + CompileError::ExpectedExpression { + found: self.previous_token.to_owned(), + position: left_position, + } + })?; let operator = self.current_token; let operator_position = self.current_position; let rule = ParseRule::from(&operator); - let test_register = left_instruction.a(); let test_boolean = match operator { Token::DoubleAmpersand => true, Token::DoublePipe => false, @@ -926,14 +935,14 @@ impl<'src> Compiler<'src> { }) } }; - let test = Instruction::builder(Operation::Test) - .b(test_register) - .c_to_boolean(test_boolean) - .build(); - let jump = Instruction::builder(Operation::Jump) - .a(1) - .b_to_boolean(true) - .build(); + let test = Instruction::from(Test { + argument, + value: test_boolean, + }); + let jump = Instruction::from(Jump { + offset: 1, + is_positive: true, + }); self.advance()?; self.emit_instruction(left_instruction, left_position); @@ -964,10 +973,8 @@ impl<'src> Compiler<'src> { } else if let Some(native_function) = NativeFunction::from_str(identifier) { return self.parse_native_call(native_function); } else if Some(identifier) == self.chunk.name().map(|string| string.as_str()) { - let register = self.next_register(); - let load_self = Instruction::builder(Operation::LoadSelf) - .a(register) - .build(); + let destination = self.next_register(); + let load_self = Instruction::from(LoadSelf { destination }); self.emit_instruction(load_self, start_position); @@ -1004,10 +1011,10 @@ impl<'src> Compiler<'src> { self.parse_expression()?; let register = self.next_register(); - let set_local = Instruction::builder(Operation::SetLocal) - .a(register) - .b(local_index) - .build(); + let set_local = Instruction::from(SetLocal { + register, + local_index, + }); self.emit_instruction(set_local, start_position); @@ -1023,17 +1030,16 @@ impl<'src> Compiler<'src> { return Ok(()); } - let register = self.next_register(); - let get_local = Instruction::builder(Operation::GetLocal) - .a(register) - .b(local_index) - .build(); + let destination = self.next_register(); + let get_local = Instruction::from(GetLocal { + destination, + local_index, + }); + let r#type = self.get_local(local_index)?.r#type.clone(); self.emit_instruction(get_local, self.previous_position); - let local = self.get_local(local_index)?; - - self.previous_expression_type = local.r#type.clone(); + self.previous_expression_type = r#type; Ok(()) } @@ -1098,30 +1104,31 @@ impl<'src> Compiler<'src> { item_type = self.previous_expression_type.clone(); let actual_register = self.next_register() - 1; - let close = Instruction::builder(Operation::Close) - .a(expected_register) - .b(actual_register) - .build(); if expected_register < actual_register { + let close = Instruction::from(Close { + from: expected_register, + to: actual_register, + }); + self.emit_instruction(close, self.current_position); } self.allow(Token::Comma)?; } - let to_register = self.next_register(); + let destination = self.next_register(); let end = self.current_position.1; - let load_list = Instruction::builder(Operation::LoadList) - .a(to_register) - .b(start_register) - .build(); + let load_list = Instruction::from(LoadList { + destination, + start_register, + }); self.emit_instruction(load_list, Span(start, end)); self.previous_expression_type = Type::List { item_type: Box::new(item_type), - length: (to_register - start_register) as usize, + length: (destination - start_register) as usize, }; Ok(()) @@ -1145,10 +1152,10 @@ impl<'src> Compiler<'src> { self.chunk.instructions_mut().pop(); } else if let Some((instruction, _)) = self.chunk.instructions().last() { let test_register = instruction.a(); - let test = Instruction::builder(Operation::Test) - .b(test_register) - .c_to_boolean(true) - .build(); + let test = Instruction::from(Test { + argument: Argument::Register(test_register), + value: true, + }); self.emit_instruction(test, self.current_position) } @@ -1212,10 +1219,10 @@ impl<'src> Compiler<'src> { skippable.set_c_to_boolean(true); } else { if_block_distance += 1; - let jump = Instruction::builder(Operation::Jump) - .b(else_block_distance) - .c_to_boolean(true) - .build(); + let jump = Instruction::from(Jump { + offset: else_block_distance, + is_positive: true, + }); self.chunk .instructions_mut() @@ -1224,10 +1231,10 @@ impl<'src> Compiler<'src> { } 2.. => { if_block_distance += 1; - let jump = Instruction::builder(Operation::Jump) - .b(else_block_distance) - .c_to_boolean(true) - .build(); + let jump = Instruction::from(Jump { + offset: else_block_distance, + is_positive: true, + }); self.chunk .instructions_mut() @@ -1235,10 +1242,10 @@ impl<'src> Compiler<'src> { } } - let jump = Instruction::builder(Operation::Jump) - .b(if_block_distance) - .c_to_boolean(true) - .build(); + let jump = Instruction::from(Jump { + offset: if_block_distance, + is_positive: true, + }); self.chunk .instructions_mut() @@ -1253,10 +1260,10 @@ impl<'src> Compiler<'src> { } let else_last_register = self.next_register().saturating_sub(1); - let r#move = Instruction::builder(Operation::Move) - .a(if_last_register) - .b(else_last_register) - .build(); + let r#move = Instruction::from(Move { + from: else_last_register, + to: if_last_register, + }); if if_last_register < else_last_register { self.emit_instruction(r#move, self.current_position); @@ -1292,20 +1299,20 @@ impl<'src> Compiler<'src> { let block_end = self.chunk.len() as u16; let jump_distance = block_end - block_start as u16 + 1; - let jump = Instruction::builder(Operation::Jump) - .b(jump_distance) - .c_to_boolean(true) - .build(); + let jump = Instruction::from(Jump { + offset: jump_distance, + is_positive: true, + }); self.chunk .instructions_mut() .insert(block_start, (jump, self.current_position)); let jump_back_distance = block_end - expression_start + 1; - let jump_back = Instruction::builder(Operation::Jump) - .b(jump_back_distance) - .c_to_boolean(false) - .build(); + let jump_back = Instruction::from(Jump { + offset: jump_back_distance, + is_positive: false, + }); self.emit_instruction(jump_back, self.current_position); @@ -1328,10 +1335,10 @@ impl<'src> Compiler<'src> { let actual_register = self.next_register() - 1; if expected_register < actual_register { - let close = Instruction::builder(Operation::Close) - .b(expected_register) - .c(actual_register) - .build(); + let close = Instruction::from(Close { + from: expected_register, + to: actual_register, + }); self.emit_instruction(close, self.current_position); } @@ -1341,13 +1348,13 @@ impl<'src> Compiler<'src> { self.previous_expression_type = *function.r#type().return_type; let end = self.previous_position.1; - let to_register = self.next_register(); - let argument_count = to_register - start_register; - let call_native = Instruction::builder(Operation::CallNative) - .a(start_register) - .b(function as u16) - .c(argument_count) - .build(); + let destination = self.next_register(); + let argument_count = destination - start_register; + let call_native = Instruction::from(CallNative { + destination, + function, + argument_count, + }); self.emit_instruction(call_native, Span(start, end)); @@ -1390,21 +1397,21 @@ impl<'src> Compiler<'src> { self.advance()?; - let has_return_value = if matches!(self.current_token, Token::Semicolon | Token::RightBrace) - { - self.update_return_type(Type::None)?; + let should_return_value = + if matches!(self.current_token, Token::Semicolon | Token::RightBrace) { + self.update_return_type(Type::None)?; - false - } else { - self.parse_expression()?; - self.update_return_type(self.previous_expression_type.clone())?; + false + } else { + self.parse_expression()?; + self.update_return_type(self.previous_expression_type.clone())?; - true - }; + true + }; let end = self.current_position.1; - let r#return = Instruction::builder(Operation::Return) - .b_to_boolean(has_return_value) - .build(); + let r#return = Instruction::from(Return { + should_return_value, + }); self.emit_instruction(r#return, Span(start, end)); @@ -1415,16 +1422,16 @@ impl<'src> Compiler<'src> { fn parse_implicit_return(&mut self) -> Result<(), CompileError> { if self.allow(Token::Semicolon)? { - let r#return = Instruction::builder(Operation::Return) - .b_to_boolean(false) - .build(); + let r#return = Instruction::from(Return { + should_return_value: false, + }); self.emit_instruction(r#return, self.current_position); } else { let should_return_value = self.previous_expression_type != Type::None; - let r#return = Instruction::builder(Operation::Return) - .b_to_boolean(should_return_value) - .build(); + let r#return = Instruction::from(Return { + should_return_value, + }); self.emit_instruction(r#return, self.current_position); } @@ -1469,11 +1476,11 @@ impl<'src> Compiler<'src> { }; let (local_index, _) = self.declare_local(identifier, r#type, is_mutable, self.current_scope); - let define_local = Instruction::builder(Operation::DefineLocal) - .a(register) - .b(local_index) - .c_to_boolean(is_mutable) - .build(); + let define_local = Instruction::from(DefineLocal { + local_index, + register, + is_mutable, + }); self.emit_instruction(define_local, position); @@ -1574,7 +1581,7 @@ impl<'src> Compiler<'src> { let function = ConcreteValue::Function(function_compiler.finish()); let constant_index = self.chunk.push_or_get_constant(function); let function_end = self.current_position.1; - let register = self.next_register(); + let destination = self.next_register(); self.lexer.skip_to(function_end); @@ -1585,27 +1592,27 @@ impl<'src> Compiler<'src> { false, self.current_scope, ); - let load_constant = Instruction::builder(Operation::LoadConstant) - .a(register) - .b(constant_index) - .c_to_boolean(false) - .build(); - let define_local = Instruction::builder(Operation::DefineLocal) - .a(register) - .b(local_index) - .c_to_boolean(false) - .build(); + let load_constant = Instruction::from(LoadConstant { + destination, + constant_index, + jump_next: false, + }); + let define_local = Instruction::from(DefineLocal { + local_index, + register: destination, + is_mutable: false, + }); self.emit_instruction(load_constant, Span(function_start, function_end)); self.emit_instruction(define_local, position); self.previous_expression_type = Type::None; } else { - let load_constant = Instruction::builder(Operation::LoadConstant) - .a(register) - .b(constant_index) - .c_to_boolean(false) - .build(); + let load_constant = Instruction::from(LoadConstant { + destination, + constant_index, + jump_next: false, + }); self.emit_instruction(load_constant, Span(function_start, function_end)); @@ -1632,8 +1639,13 @@ impl<'src> Compiler<'src> { }); } - let function_register = last_instruction.a(); - let register_type = self.get_register_type(function_register)?; + let function = last_instruction.destination_as_argument().ok_or_else(|| { + CompileError::ExpectedExpression { + found: self.previous_token.to_owned(), + position: self.previous_position, + } + })?; + let register_type = self.get_register_type(function.index())?; let function_return_type = match register_type { Type::Function(function_type) => *function_type.return_type, Type::SelfChunk => (*self.chunk.r#type().return_type).clone(), @@ -1655,12 +1667,13 @@ impl<'src> Compiler<'src> { self.parse_expression()?; let actual_register = self.next_register() - 1; - let close = Instruction::builder(Operation::Close) - .b(expected_register) - .c(actual_register) - .build(); if expected_register < actual_register { + let close = Instruction::from(Close { + from: expected_register, + to: actual_register, + }); + self.emit_instruction(close, self.current_position); } @@ -1668,13 +1681,13 @@ impl<'src> Compiler<'src> { } let end = self.current_position.1; - let to_register = self.next_register(); - let argument_count = to_register - function_register - 1; - let call = Instruction::builder(Operation::Call) - .a(to_register) - .b(function_register) - .c(argument_count) - .build(); + let destination = self.next_register(); + let argument_count = self.next_register() - function.index() - 1; + let call = Instruction::from(Call { + destination, + function, + argument_count, + }); self.emit_instruction(call, Span(start, end)); diff --git a/dust-lang/src/instruction.rs b/dust-lang/src/instruction.rs deleted file mode 100644 index c336e51..0000000 --- a/dust-lang/src/instruction.rs +++ /dev/null @@ -1,964 +0,0 @@ -//! An operation and its arguments for the Dust virtual machine. -//! -//! Each instruction is a 64-bit unsigned integer that is divided into five fields: -//! - Bits 0-8: The operation code. -//! - Bit 9: Boolean flag indicating whether the B argument is a constant. -//! - Bit 10: Boolean flag indicating whether the C argument is a constant. -//! - Bit 11: Boolean flag indicating whether the A argument is a local. -//! - Bit 12: Boolean flag indicating whether the B argument is a local. -//! - Bit 13: Boolean flag indicating whether the C argument is a local. -//! - Bits 17-32: The A argument, -//! - Bits 33-48: The B argument. -//! - Bits 49-63: The C argument. -//! -//! Be careful when working with instructions directly. When modifying an instruction, be sure to -//! account for the fact that setting the A, B, or C arguments to 0 will have no effect. It is -//! usually best to remove instructions and insert new ones in their place instead of mutating them. - -use serde::{Deserialize, Serialize}; - -use crate::{Chunk, NativeFunction, Operation}; - -pub struct InstructionBuilder { - operation: Operation, - a: u16, - b: u16, - c: u16, - b_is_constant: bool, - c_is_constant: bool, - a_is_local: bool, - b_is_local: bool, - c_is_local: bool, -} - -impl InstructionBuilder { - pub fn build(&self) -> Instruction { - Instruction( - (self.operation as u64) - | ((self.b_is_constant as u64) << 9) - | ((self.c_is_constant as u64) << 10) - | ((self.a_is_local as u64) << 11) - | ((self.b_is_local as u64) << 12) - | ((self.c_is_local as u64) << 13) - | ((self.a as u64) << 16) - | ((self.b as u64) << 32) - | ((self.c as u64) << 48), - ) - } - - pub fn a(&mut self, a: u16) -> &mut Self { - self.a = a; - self - } - - pub fn a_to_boolean(&mut self, a: bool) -> &mut Self { - self.a = a as u16; - self - } - - pub fn b(&mut self, b: u16) -> &mut Self { - self.b = b; - self - } - - pub fn b_to_boolean(&mut self, b: bool) -> &mut Self { - self.b = b as u16; - self - } - - pub fn c(&mut self, c: u16) -> &mut Self { - self.c = c; - self - } - - pub fn c_to_boolean(&mut self, c: bool) -> &mut Self { - self.c = c as u16; - self - } - - pub fn b_is_constant(&mut self, b_is_constant: bool) -> &mut Self { - self.b_is_constant = b_is_constant; - self - } - - pub fn c_is_constant(&mut self, c_is_constant: bool) -> &mut Self { - self.c_is_constant = c_is_constant; - self - } - - pub fn a_is_local(&mut self, a_is_local: bool) -> &mut Self { - self.a_is_local = a_is_local; - self - } - - pub fn b_is_local(&mut self, b_is_local: bool) -> &mut Self { - self.b_is_local = b_is_local; - self - } - - pub fn c_is_local(&mut self, c_is_local: bool) -> &mut Self { - self.c_is_local = c_is_local; - self - } -} - -impl From<&Instruction> for InstructionBuilder { - fn from(instruction: &Instruction) -> Self { - InstructionBuilder { - operation: instruction.operation(), - a: instruction.a(), - b: instruction.b(), - c: instruction.c(), - b_is_constant: instruction.b_is_constant(), - c_is_constant: instruction.c_is_constant(), - a_is_local: instruction.a_is_local(), - b_is_local: instruction.b_is_local(), - c_is_local: instruction.c_is_local(), - } - } -} - -impl From for InstructionBuilder { - fn from(instruction: Instruction) -> Self { - InstructionBuilder { - operation: instruction.operation(), - a: instruction.a(), - b: instruction.b(), - c: instruction.c(), - b_is_constant: instruction.b_is_constant(), - c_is_constant: instruction.c_is_constant(), - a_is_local: instruction.a_is_local(), - b_is_local: instruction.b_is_local(), - c_is_local: instruction.c_is_local(), - } - } -} - -impl From<&mut Instruction> for InstructionBuilder { - fn from(instruction: &mut Instruction) -> Self { - InstructionBuilder { - operation: instruction.operation(), - a: instruction.a(), - b: instruction.b(), - c: instruction.c(), - b_is_constant: instruction.b_is_constant(), - c_is_constant: instruction.c_is_constant(), - a_is_local: instruction.a_is_local(), - b_is_local: instruction.b_is_local(), - c_is_local: instruction.c_is_local(), - } - } -} - -/// An operation and its arguments for the Dust virtual machine. -/// -/// See the [module-level documentation](index.html) for more information. -#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize)] -pub struct Instruction(u64); - -impl Instruction { - pub fn new(operation: Operation) -> Instruction { - Instruction(operation as u64) - } - - pub fn builder(operation: Operation) -> InstructionBuilder { - InstructionBuilder { - operation, - a: 0, - b: 0, - c: 0, - b_is_constant: false, - c_is_constant: false, - a_is_local: false, - b_is_local: false, - c_is_local: false, - } - } - - pub fn operation(&self) -> Operation { - Operation::from((self.0 & 0b11111111) as u8) - } - - pub fn set_b_is_constant(&mut self) -> &mut Self { - self.0 = (self.0 & !(1 << 9)) | ((true as u64) << 9); - - self - } - - pub fn set_c_is_constant(&mut self) -> &mut Self { - self.0 = (self.0 & !(1 << 10)) | ((true as u64) << 10); - - self - } - - pub fn set_a_is_local(&mut self) -> &mut Self { - self.0 = (self.0 & !(1 << 11)) | ((true as u64) << 11); - - self - } - - pub fn set_b_is_local(&mut self) -> &mut Self { - self.0 = (self.0 & !(1 << 12)) | ((true as u64) << 12); - - self - } - - pub fn set_c_is_local(&mut self) -> &mut Self { - self.0 = (self.0 & !(1 << 13)) | ((true as u64) << 13); - - self - } - - pub fn a(&self) -> u16 { - ((self.0 >> 16) & 0b1111111111111111) as u16 - } - - pub fn a_as_boolean(&self) -> bool { - self.a() != 0 - } - - pub fn set_a(&mut self, a: u16) -> &mut Self { - self.0 = (self.0 & !(0b1111111111111111 << 16)) | ((a as u64) << 16); - - self - } - - pub fn set_a_to_boolean(&mut self, boolean: bool) -> &mut Self { - self.0 = (self.0 & !(0b1111111111111111 << 16)) | ((boolean as u64) << 16); - - self - } - - pub fn b(&self) -> u16 { - ((self.0 >> 32) & 0b1111111111111111) as u16 - } - - pub fn b_as_boolean(&self) -> bool { - self.b() != 0 - } - - pub fn set_b(&mut self, b: u16) -> &mut Self { - self.0 = (self.0 & !(0b1111111111111111 << 32)) | ((b as u64) << 32); - - self - } - - pub fn set_b_to_boolean(&mut self, boolean: bool) -> &mut Self { - self.0 = (self.0 & !(0b1111111111111111 << 32)) | ((boolean as u64) << 32); - - self - } - - pub fn c(&self) -> u16 { - ((self.0 >> 48) & 0b1111111111111111) as u16 - } - - pub fn c_as_boolean(&self) -> bool { - self.c() != 0 - } - - pub fn set_c(&mut self, c: u16) -> &mut Self { - self.0 = (self.0 & !(0b1111111111111111 << 48)) | ((c as u64) << 48); - - self - } - - pub fn set_c_to_boolean(&mut self, boolean: bool) -> &mut Self { - self.0 = (self.0 & !(0b1111111111111111 << 48)) | ((boolean as u64) << 48); - - self - } - - pub fn b_is_constant(&self) -> bool { - (self.0 >> 9) & 1 == 1 - } - - pub fn c_is_constant(&self) -> bool { - (self.0 >> 10) & 1 == 1 - } - - pub fn a_is_local(&self) -> bool { - (self.0 >> 11) & 1 == 1 - } - - pub fn b_is_local(&self) -> bool { - (self.0 >> 12) & 1 == 1 - } - - pub fn c_is_local(&self) -> bool { - (self.0 >> 13) & 1 == 1 - } - - pub fn r#move(to_register: u16, from_register: u16) -> Instruction { - *Instruction::new(Operation::Move) - .set_b(to_register) - .set_c(from_register) - } - - pub fn close(from_register: u16, to_register: u16) -> Instruction { - *Instruction::new(Operation::Close) - .set_b(from_register) - .set_c(to_register) - } - - pub fn load_boolean(to_register: u16, value: bool, skip: bool) -> Instruction { - *Instruction::new(Operation::LoadBoolean) - .set_a(to_register) - .set_b_to_boolean(value) - .set_c_to_boolean(skip) - } - - pub fn load_constant(to_register: u16, constant_index: u16, skip: bool) -> Instruction { - *Instruction::new(Operation::LoadConstant) - .set_a(to_register) - .set_b(constant_index) - .set_c_to_boolean(skip) - } - - pub fn load_list(to_register: u16, start_register: u16) -> Instruction { - *Instruction::new(Operation::LoadList) - .set_a(to_register) - .set_b(start_register) - } - - pub fn load_self(to_register: u16) -> Instruction { - *Instruction::new(Operation::LoadSelf).set_a(to_register) - } - - pub fn define_local(to_register: u16, local_index: u16, is_mutable: bool) -> Instruction { - *Instruction::new(Operation::DefineLocal) - .set_a(to_register) - .set_b(local_index) - .set_c_to_boolean(is_mutable) - } - - pub fn get_local(to_register: u16, local_index: u16) -> Instruction { - *Instruction::new(Operation::GetLocal) - .set_a(to_register) - .set_b(local_index) - } - - pub fn set_local(from_register: u16, local_index: u16) -> Instruction { - *Instruction::new(Operation::SetLocal) - .set_a(from_register) - .set_b(local_index) - } - - pub fn add(to_register: u16, left_index: u16, right_index: u16) -> Instruction { - *Instruction::new(Operation::Add) - .set_a(to_register) - .set_b(left_index) - .set_c(right_index) - } - - pub fn subtract(to_register: u16, left_index: u16, right_index: u16) -> Instruction { - *Instruction::new(Operation::Subtract) - .set_a(to_register) - .set_b(left_index) - .set_c(right_index) - } - - pub fn multiply(to_register: u16, left_index: u16, right_index: u16) -> Instruction { - *Instruction::new(Operation::Multiply) - .set_a(to_register) - .set_b(left_index) - .set_c(right_index) - } - - pub fn divide(to_register: u16, left_index: u16, right_index: u16) -> Instruction { - *Instruction::new(Operation::Divide) - .set_a(to_register) - .set_b(left_index) - .set_c(right_index) - } - - pub fn modulo(to_register: u16, left_index: u16, right_index: u16) -> Instruction { - *Instruction::new(Operation::Modulo) - .set_a(to_register) - .set_b(left_index) - .set_c(right_index) - } - - pub fn test(test_register: u16, test_value: bool) -> Instruction { - *Instruction::new(Operation::Test) - .set_b(test_register) - .set_c_to_boolean(test_value) - } - - pub fn test_set(to_register: u16, argument_index: u16, test_value: bool) -> Instruction { - *Instruction::new(Operation::TestSet) - .set_a(to_register) - .set_b(argument_index) - .set_c_to_boolean(test_value) - } - - pub fn equal(comparison_boolean: bool, left_index: u16, right_index: u16) -> Instruction { - *Instruction::new(Operation::Equal) - .set_a_to_boolean(comparison_boolean) - .set_b(left_index) - .set_c(right_index) - } - - pub fn less(comparison_boolean: bool, left_index: u16, right_index: u16) -> Instruction { - *Instruction::new(Operation::Less) - .set_a_to_boolean(comparison_boolean) - .set_b(left_index) - .set_c(right_index) - } - - pub fn less_equal(comparison_boolean: bool, left_index: u16, right_index: u16) -> Instruction { - *Instruction::new(Operation::LessEqual) - .set_a_to_boolean(comparison_boolean) - .set_b(left_index) - .set_c(right_index) - } - - pub fn negate(to_register: u16, from_index: u16) -> Instruction { - *Instruction::new(Operation::Negate) - .set_a(to_register) - .set_b(from_index) - } - - pub fn not(to_register: u16, from_index: u16) -> Instruction { - *Instruction::new(Operation::Not) - .set_a(to_register) - .set_b(from_index) - } - - pub fn jump(jump_offset: u16, is_positive: bool) -> Instruction { - *Instruction::new(Operation::Jump) - .set_b(jump_offset) - .set_c_to_boolean(is_positive) - } - - pub fn call(to_register: u16, function_register: u16, argument_count: u16) -> Instruction { - *Instruction::new(Operation::Call) - .set_a(to_register) - .set_b(function_register) - .set_c(argument_count) - } - - pub fn call_native( - to_register: u16, - native_fn: NativeFunction, - argument_count: u16, - ) -> Instruction { - *Instruction::new(Operation::CallNative) - .set_a(to_register) - .set_b(native_fn as u16) - .set_c(argument_count) - } - - pub fn r#return(should_return_value: bool) -> Instruction { - *Instruction::new(Operation::Return).set_b_to_boolean(should_return_value) - } - pub fn yields_value(&self) -> bool { - match self.operation() { - Operation::LoadBoolean - | Operation::LoadConstant - | Operation::LoadList - | Operation::LoadSelf - | Operation::GetLocal - | Operation::Add - | Operation::Subtract - | Operation::Multiply - | Operation::Divide - | Operation::Modulo - | Operation::Equal - | Operation::Less - | Operation::LessEqual - | Operation::Negate - | Operation::Not - | Operation::Call => true, - - Operation::CallNative => { - let function = NativeFunction::from(self.b()); - - function.returns_value() - } - - Operation::Move - | Operation::Close - | Operation::DefineLocal - | Operation::SetLocal - | Operation::Test - | Operation::TestSet - | Operation::Jump - | Operation::Return => true, - } - } - - pub fn disassembly_info(&self, chunk: &Chunk) -> String { - let InstructionBuilder { - operation, - a, - b, - c, - b_is_constant, - c_is_constant, - a_is_local, - b_is_local, - c_is_local, - } = InstructionBuilder::from(self); - let format_arguments = || { - let first_argument = if b_is_constant { - format!("C{}", b) - } else { - format!("R{}", b) - }; - let second_argument = if c_is_constant { - format!("C{}", c) - } else { - format!("R{}", c) - }; - - (first_argument, second_argument) - }; - - match operation { - Operation::Move => format!("R{a} = R{b}"), - Operation::Close => { - format!("R{b}..R{c}") - } - Operation::LoadBoolean => { - let boolean = b != 0; - let jump = c != 0; - - if jump { - format!("R{a} = {boolean} && JUMP +1") - } else { - format!("R{a} {boolean}") - } - } - Operation::LoadConstant => { - let jump = c != 0; - - if jump { - format!("R{a} = C{b} JUMP +1") - } else { - format!("R{a} = C{b}") - } - } - Operation::LoadList => { - format!("R{a} = [R{b}..=R{c}]",) - } - Operation::LoadSelf => { - let name = chunk - .name() - .map(|idenifier| idenifier.as_str()) - .unwrap_or("self"); - - format!("R{a} = {name}") - } - Operation::DefineLocal => { - format!("L{b} = R{a}") - } - Operation::GetLocal => { - format!("R{a} = L{b}") - } - Operation::SetLocal => { - format!("L{b} = R{a}") - } - Operation::Add => { - let (first_argument, second_argument) = format_arguments(); - - format!("R{a} = {first_argument} + {second_argument}",) - } - Operation::Subtract => { - let (first_argument, second_argument) = format_arguments(); - - format!("R{a} = {first_argument} - {second_argument}",) - } - Operation::Multiply => { - let (first_argument, second_argument) = format_arguments(); - - format!("R{a} = {first_argument} * {second_argument}",) - } - Operation::Divide => { - let (first_argument, second_argument) = format_arguments(); - - format!("R{a} = {first_argument} / {second_argument}",) - } - Operation::Modulo => { - let (first_argument, second_argument) = format_arguments(); - - format!("R{a} = {first_argument} % {second_argument}",) - } - Operation::Test => { - let test_register = if b_is_constant { - format!("C{b}") - } else { - format!("R{b}") - }; - let test_value = c != 0; - let bang = if test_value { "" } else { "!" }; - - format!("if {bang}{test_register} {{ JUMP +1 }}",) - } - Operation::TestSet => { - let test_register = if b_is_constant { - format!("C{b}") - } else { - format!("R{b}") - }; - let test_value = c != 0; - let bang = if test_value { "" } else { "!" }; - - format!( - "if {bang}R{test_register} {{ JUMP +1 }} else {{ R{a} = R{test_register} }}" - ) - } - Operation::Equal => { - let comparison_symbol = if a != 0 { "==" } else { "!=" }; - let (first_argument, second_argument) = format_arguments(); - - format!("if {first_argument} {comparison_symbol} {second_argument} {{ JUMP +1 }}") - } - Operation::Less => { - let comparison_symbol = if a != 0 { "<" } else { ">=" }; - let (first_argument, second_argument) = format_arguments(); - - format!("if {first_argument} {comparison_symbol} {second_argument} {{ JUMP +1 }}") - } - Operation::LessEqual => { - let comparison_symbol = if a != 0 { "<=" } else { ">" }; - let (first_argument, second_argument) = format_arguments(); - - format!("if {first_argument} {comparison_symbol} {second_argument} {{ JUMP +1 }}") - } - Operation::Negate => { - let argument = if b_is_constant { - format!("C{b}") - } else { - format!("R{b}") - }; - - format!("R{a} = -{argument}") - } - Operation::Not => { - let argument = if b_is_constant { - format!("C{b}") - } else { - format!("R{b}") - }; - - format!("R{a} = !{argument}") - } - Operation::Jump => { - let is_positive = c != 0; - - if is_positive { - format!("JUMP +{b}") - } else { - format!("JUMP -{b}") - } - } - Operation::Call => { - let argument_count = c; - - let mut output = format!("R{a} = R{b}("); - - if argument_count != 0 { - let first_argument = b + 1; - - for (index, register) in - (first_argument..first_argument + argument_count).enumerate() - { - if index > 0 { - output.push_str(", "); - } - - output.push_str(&format!("R{}", register)); - } - } - - output.push(')'); - - output - } - Operation::CallNative => { - let native_function = NativeFunction::from(b); - let argument_count = c; - let mut output = String::new(); - let native_function_name = native_function.as_str(); - - output.push_str(&format!("R{a} = {}(", native_function_name)); - - if argument_count != 0 { - let first_argument = a.saturating_sub(argument_count); - - for register in first_argument..a { - if register != first_argument { - output.push_str(", "); - } - - output.push_str(&format!("R{}", register)); - } - } - - output.push(')'); - - output - } - Operation::Return => { - let should_return_value = b != 0; - - if should_return_value { - "RETURN".to_string() - } else { - "".to_string() - } - } - } - } -} - -impl From<&Instruction> for u64 { - fn from(instruction: &Instruction) -> Self { - instruction.0 - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn builder() { - let instruction_from_builder = Instruction::builder(Operation::Add) - .a(1) - .b(2) - .c(3) - .b_is_constant(true) - .c_is_constant(true) - .a_is_local(true) - .b_is_local(true) - .c_is_local(true) - .build(); - let instruction = *Instruction::add(1, 2, 3) - .set_b_is_constant() - .set_c_is_constant() - .set_a_is_local() - .set_b_is_local() - .set_c_is_local(); - - assert_eq!(instruction_from_builder, instruction); - } - - #[test] - fn r#move() { - let instruction = Instruction::r#move(4, 1); - - assert_eq!(instruction.operation(), Operation::Move); - assert_eq!(instruction.b(), 4); - assert_eq!(instruction.c(), 1); - } - - #[test] - fn close() { - let instruction = Instruction::close(1, 2); - - assert_eq!(instruction.operation(), Operation::Close); - assert_eq!(instruction.b(), 1); - assert_eq!(instruction.c(), 2); - } - - #[test] - fn load_boolean() { - let instruction = Instruction::load_boolean(4, true, true); - - assert_eq!(instruction.operation(), Operation::LoadBoolean); - assert_eq!(instruction.a(), 4); - assert!(instruction.a_as_boolean()); - assert!(instruction.c_as_boolean()); - } - - #[test] - fn load_constant() { - let mut instruction = Instruction::load_constant(4, 1, true); - - instruction.set_b_is_constant(); - instruction.set_c_is_constant(); - - assert_eq!(instruction.operation(), Operation::LoadConstant); - assert_eq!(instruction.a(), 4); - assert_eq!(instruction.b(), 1); - assert!(instruction.b_is_constant()); - assert!(instruction.b_is_constant()); - assert!(instruction.c_as_boolean()); - } - - #[test] - fn load_list() { - let instruction = Instruction::load_list(4, 1); - - assert_eq!(instruction.operation(), Operation::LoadList); - assert_eq!(instruction.a(), 4); - assert_eq!(instruction.b(), 1); - } - - #[test] - fn load_self() { - let instruction = Instruction::load_self(10); - - assert_eq!(instruction.operation(), Operation::LoadSelf); - assert_eq!(instruction.a(), 10); - } - - #[test] - fn declare_local() { - let instruction = *Instruction::define_local(4, 1, true).set_b_is_constant(); - - assert_eq!(instruction.operation(), Operation::DefineLocal); - assert_eq!(instruction.a(), 4); - assert_eq!(instruction.b(), 1); - assert!(instruction.c_as_boolean()); - assert!(instruction.b_is_constant()); - } - - #[test] - fn add() { - let instruction = *Instruction::add(1, 1, 4).set_b_is_constant(); - - assert_eq!(instruction.operation(), Operation::Add); - assert_eq!(instruction.a(), 1); - assert_eq!(instruction.b(), 1); - assert_eq!(instruction.c(), 4); - assert!(instruction.b_is_constant()); - } - - #[test] - fn subtract() { - let mut instruction = Instruction::subtract(4, 1, 2); - - instruction.set_b_is_constant(); - instruction.set_c_is_constant(); - - assert_eq!(instruction.operation(), Operation::Subtract); - assert_eq!(instruction.a(), 4); - assert_eq!(instruction.b(), 1); - assert_eq!(instruction.c(), 2); - assert!(instruction.b_is_constant()); - assert!(instruction.b_is_constant()); - } - - #[test] - fn multiply() { - let mut instruction = Instruction::multiply(4, 1, 2); - - instruction.set_b_is_constant(); - instruction.set_c_is_constant(); - - assert_eq!(instruction.operation(), Operation::Multiply); - assert_eq!(instruction.a(), 4); - assert_eq!(instruction.b(), 1); - assert_eq!(instruction.c(), 2); - assert!(instruction.b_is_constant()); - assert!(instruction.b_is_constant()); - } - - #[test] - fn divide() { - let mut instruction = Instruction::divide(4, 1, 2); - - instruction.set_b_is_constant(); - instruction.set_c_is_constant(); - - assert_eq!(instruction.operation(), Operation::Divide); - assert_eq!(instruction.a(), 4); - assert_eq!(instruction.b(), 1); - assert_eq!(instruction.c(), 2); - assert!(instruction.b_is_constant()); - assert!(instruction.b_is_constant()); - } - - #[test] - fn test() { - let instruction = Instruction::test(42, true); - - assert_eq!(instruction.operation(), Operation::Test); - assert_eq!(instruction.b(), 42); - assert!(instruction.c_as_boolean()); - } - - #[test] - fn test_set() { - let instruction = Instruction::test_set(4, 1, true); - - assert_eq!(instruction.operation(), Operation::TestSet); - assert_eq!(instruction.a(), 4); - assert_eq!(instruction.b(), 1); - assert!(instruction.c_as_boolean()); - } - - #[test] - fn equal() { - let mut instruction = Instruction::equal(true, 1, 2); - - instruction.set_b_is_constant(); - instruction.set_c_is_constant(); - - assert_eq!(instruction.operation(), Operation::Equal); - assert!(instruction.a_as_boolean()); - assert_eq!(instruction.b(), 1); - assert_eq!(instruction.c(), 2); - assert!(instruction.b_is_constant()); - assert!(instruction.b_is_constant()); - } - - #[test] - fn negate() { - let mut instruction = Instruction::negate(4, 1); - - instruction.set_b_is_constant(); - instruction.set_c_is_constant(); - - assert_eq!(instruction.operation(), Operation::Negate); - assert_eq!(instruction.a(), 4); - assert_eq!(instruction.b(), 1); - assert!(instruction.b_is_constant()); - assert!(instruction.b_is_constant()); - } - - #[test] - fn not() { - let mut instruction = Instruction::not(4, 1); - - instruction.set_b_is_constant(); - instruction.set_c_is_constant(); - - assert_eq!(instruction.operation(), Operation::Not); - assert_eq!(instruction.a(), 4); - assert_eq!(instruction.b(), 1); - assert!(instruction.b_is_constant()); - assert!(instruction.b_is_constant()); - } - - #[test] - fn jump() { - let instruction = Instruction::jump(4, true); - - assert_eq!(instruction.operation(), Operation::Jump); - - assert_eq!(instruction.b(), 4); - assert!(instruction.c_as_boolean()); - } - - #[test] - fn call() { - let instruction = Instruction::call(1, 3, 4); - - assert_eq!(instruction.operation(), Operation::Call); - assert_eq!(instruction.a(), 1); - assert_eq!(instruction.b(), 3); - assert_eq!(instruction.c(), 4); - } - - #[test] - fn r#return() { - let instruction = Instruction::r#return(true); - - assert_eq!(instruction.operation(), Operation::Return); - assert!(instruction.b_as_boolean()); - } -} diff --git a/dust-lang/src/instruction/add.rs b/dust-lang/src/instruction/add.rs new file mode 100644 index 0000000..10a6ef5 --- /dev/null +++ b/dust-lang/src/instruction/add.rs @@ -0,0 +1,32 @@ +use crate::{Argument, Instruction, Operation}; + +pub struct Add { + pub destination: u16, + pub left: Argument, + pub right: Argument, +} + +impl From<&Instruction> for Add { + fn from(instruction: &Instruction) -> Self { + let (left, right) = instruction.b_and_c_as_arguments(); + + Add { + destination: instruction.a(), + left, + right, + } + } +} + +impl From for Instruction { + fn from(add: Add) -> Self { + *Instruction::new(Operation::Add) + .set_a(add.destination) + .set_b(add.left.index()) + .set_b_is_constant(add.left.is_constant()) + .set_b_is_local(add.left.is_local()) + .set_c(add.right.index()) + .set_c_is_constant(add.right.is_constant()) + .set_c_is_local(add.right.is_local()) + } +} diff --git a/dust-lang/src/instruction/call.rs b/dust-lang/src/instruction/call.rs new file mode 100644 index 0000000..fd377f0 --- /dev/null +++ b/dust-lang/src/instruction/call.rs @@ -0,0 +1,28 @@ +use crate::{Argument, Instruction, Operation}; + +pub struct Call { + pub destination: u16, + pub function: Argument, + pub argument_count: u16, +} + +impl From<&Instruction> for Call { + fn from(instruction: &Instruction) -> Self { + Call { + destination: instruction.a(), + function: instruction.b_as_argument(), + argument_count: instruction.c(), + } + } +} + +impl From for Instruction { + fn from(call: Call) -> Self { + *Instruction::new(Operation::Call) + .set_a(call.destination) + .set_b(call.function.index()) + .set_b_is_constant(call.function.is_constant()) + .set_b_is_local(call.function.is_local()) + .set_c(call.argument_count) + } +} diff --git a/dust-lang/src/instruction/call_native.rs b/dust-lang/src/instruction/call_native.rs new file mode 100644 index 0000000..2df039d --- /dev/null +++ b/dust-lang/src/instruction/call_native.rs @@ -0,0 +1,26 @@ +use crate::{Instruction, NativeFunction, Operation}; + +pub struct CallNative { + pub destination: u16, + pub function: NativeFunction, + pub argument_count: u16, +} + +impl From<&Instruction> for CallNative { + fn from(instruction: &Instruction) -> Self { + CallNative { + destination: instruction.a(), + function: NativeFunction::from(instruction.b()), + argument_count: instruction.c(), + } + } +} + +impl From for Instruction { + fn from(call_native: CallNative) -> Self { + *Instruction::new(Operation::CallNative) + .set_a(call_native.destination) + .set_b(call_native.function as u16) + .set_c(call_native.argument_count) + } +} diff --git a/dust-lang/src/instruction/close.rs b/dust-lang/src/instruction/close.rs new file mode 100644 index 0000000..a09c233 --- /dev/null +++ b/dust-lang/src/instruction/close.rs @@ -0,0 +1,23 @@ +use crate::{Instruction, Operation}; + +pub struct Close { + pub from: u16, + pub to: u16, +} + +impl From<&Instruction> for Close { + fn from(instruction: &Instruction) -> Self { + Close { + from: instruction.b(), + to: instruction.a(), + } + } +} + +impl From for Instruction { + fn from(r#move: Close) -> Self { + *Instruction::new(Operation::Move) + .set_b(r#move.from) + .set_c(r#move.to) + } +} diff --git a/dust-lang/src/instruction/define_local.rs b/dust-lang/src/instruction/define_local.rs new file mode 100644 index 0000000..021fae9 --- /dev/null +++ b/dust-lang/src/instruction/define_local.rs @@ -0,0 +1,26 @@ +use crate::{Instruction, Operation}; + +pub struct DefineLocal { + pub register: u16, + pub local_index: u16, + pub is_mutable: bool, +} + +impl From<&Instruction> for DefineLocal { + fn from(instruction: &Instruction) -> Self { + DefineLocal { + register: instruction.a(), + local_index: instruction.b(), + is_mutable: instruction.c_as_boolean(), + } + } +} + +impl From for Instruction { + fn from(define_local: DefineLocal) -> Self { + *Instruction::new(Operation::DefineLocal) + .set_a(define_local.register) + .set_b(define_local.local_index) + .set_c_to_boolean(define_local.is_mutable) + } +} diff --git a/dust-lang/src/instruction/divide.rs b/dust-lang/src/instruction/divide.rs new file mode 100644 index 0000000..b86dbb3 --- /dev/null +++ b/dust-lang/src/instruction/divide.rs @@ -0,0 +1,32 @@ +use crate::{Argument, Instruction, Operation}; + +pub struct Divide { + pub destination: u16, + pub left: Argument, + pub right: Argument, +} + +impl From<&Instruction> for Divide { + fn from(instruction: &Instruction) -> Self { + let (left, right) = instruction.b_and_c_as_arguments(); + + Divide { + destination: instruction.a(), + left, + right, + } + } +} + +impl From for Instruction { + fn from(divide: Divide) -> Self { + *Instruction::new(Operation::Divide) + .set_a(divide.destination) + .set_b(divide.left.index()) + .set_b_is_constant(divide.left.is_constant()) + .set_b_is_local(divide.left.is_local()) + .set_c(divide.right.index()) + .set_c_is_constant(divide.right.is_constant()) + .set_c_is_local(divide.right.is_local()) + } +} diff --git a/dust-lang/src/instruction/equal.rs b/dust-lang/src/instruction/equal.rs new file mode 100644 index 0000000..2400d87 --- /dev/null +++ b/dust-lang/src/instruction/equal.rs @@ -0,0 +1,32 @@ +use crate::{Argument, Instruction, Operation}; + +pub struct Equal { + pub value: bool, + pub left: Argument, + pub right: Argument, +} + +impl From<&Instruction> for Equal { + fn from(instruction: &Instruction) -> Self { + let (left, right) = instruction.b_and_c_as_arguments(); + + Equal { + value: instruction.a_as_boolean(), + left, + right, + } + } +} + +impl From for Instruction { + fn from(equal: Equal) -> Self { + *Instruction::new(Operation::Equal) + .set_a_to_boolean(equal.value) + .set_b(equal.left.index()) + .set_b_is_constant(equal.left.is_constant()) + .set_b_is_local(equal.left.is_local()) + .set_c(equal.right.index()) + .set_c_is_constant(equal.right.is_constant()) + .set_c_is_local(equal.right.is_local()) + } +} diff --git a/dust-lang/src/instruction/get_local.rs b/dust-lang/src/instruction/get_local.rs new file mode 100644 index 0000000..b2447cd --- /dev/null +++ b/dust-lang/src/instruction/get_local.rs @@ -0,0 +1,23 @@ +use crate::{Instruction, Operation}; + +pub struct GetLocal { + pub destination: u16, + pub local_index: u16, +} + +impl From<&Instruction> for GetLocal { + fn from(instruction: &Instruction) -> Self { + GetLocal { + destination: instruction.a(), + local_index: instruction.b(), + } + } +} + +impl From for Instruction { + fn from(get_local: GetLocal) -> Self { + *Instruction::new(Operation::GetLocal) + .set_a(get_local.destination) + .set_b(get_local.local_index) + } +} diff --git a/dust-lang/src/instruction/jump.rs b/dust-lang/src/instruction/jump.rs new file mode 100644 index 0000000..0bd866c --- /dev/null +++ b/dust-lang/src/instruction/jump.rs @@ -0,0 +1,23 @@ +use crate::{Instruction, Operation}; + +pub struct Jump { + pub offset: u16, + pub is_positive: bool, +} + +impl From<&Instruction> for Jump { + fn from(instruction: &Instruction) -> Self { + Jump { + offset: instruction.b(), + is_positive: instruction.c_as_boolean(), + } + } +} + +impl From for Instruction { + fn from(jump: Jump) -> Self { + *Instruction::new(Operation::Jump) + .set_b(jump.offset) + .set_c_to_boolean(jump.is_positive) + } +} diff --git a/dust-lang/src/instruction/less.rs b/dust-lang/src/instruction/less.rs new file mode 100644 index 0000000..d2e5463 --- /dev/null +++ b/dust-lang/src/instruction/less.rs @@ -0,0 +1,32 @@ +use crate::{Argument, Instruction, Operation}; + +pub struct Less { + pub value: bool, + pub left: Argument, + pub right: Argument, +} + +impl From<&Instruction> for Less { + fn from(instruction: &Instruction) -> Self { + let (left, right) = instruction.b_and_c_as_arguments(); + + Less { + value: instruction.a_as_boolean(), + left, + right, + } + } +} + +impl From for Instruction { + fn from(less: Less) -> Self { + *Instruction::new(Operation::Less) + .set_a_to_boolean(less.value) + .set_b(less.left.index()) + .set_b_is_constant(less.left.is_constant()) + .set_b_is_local(less.left.is_local()) + .set_c(less.right.index()) + .set_c_is_constant(less.right.is_constant()) + .set_c_is_local(less.right.is_local()) + } +} diff --git a/dust-lang/src/instruction/less_equal.rs b/dust-lang/src/instruction/less_equal.rs new file mode 100644 index 0000000..698ef5d --- /dev/null +++ b/dust-lang/src/instruction/less_equal.rs @@ -0,0 +1,32 @@ +use crate::{Argument, Instruction, Operation}; + +pub struct LessEqual { + pub value: bool, + pub left: Argument, + pub right: Argument, +} + +impl From<&Instruction> for LessEqual { + fn from(instruction: &Instruction) -> Self { + let (left, right) = instruction.b_and_c_as_arguments(); + + LessEqual { + value: instruction.a_as_boolean(), + left, + right, + } + } +} + +impl From for Instruction { + fn from(less_equal: LessEqual) -> Self { + *Instruction::new(Operation::LessEqual) + .set_a_to_boolean(less_equal.value) + .set_b(less_equal.left.index()) + .set_b_is_constant(less_equal.left.is_constant()) + .set_b_is_local(less_equal.left.is_local()) + .set_c(less_equal.right.index()) + .set_c_is_constant(less_equal.right.is_constant()) + .set_c_is_local(less_equal.right.is_local()) + } +} diff --git a/dust-lang/src/instruction/load_boolean.rs b/dust-lang/src/instruction/load_boolean.rs new file mode 100644 index 0000000..5025dc1 --- /dev/null +++ b/dust-lang/src/instruction/load_boolean.rs @@ -0,0 +1,26 @@ +use crate::{Instruction, Operation}; + +pub struct LoadBoolean { + pub destination: u16, + pub value: bool, + pub jump_next: bool, +} + +impl From<&Instruction> for LoadBoolean { + fn from(instruction: &Instruction) -> Self { + LoadBoolean { + destination: instruction.a(), + value: instruction.b_as_boolean(), + jump_next: instruction.c_as_boolean(), + } + } +} + +impl From for Instruction { + fn from(load_boolean: LoadBoolean) -> Self { + *Instruction::new(Operation::LoadBoolean) + .set_a(load_boolean.destination) + .set_b_to_boolean(load_boolean.value) + .set_c_to_boolean(load_boolean.jump_next) + } +} diff --git a/dust-lang/src/instruction/load_constant.rs b/dust-lang/src/instruction/load_constant.rs new file mode 100644 index 0000000..a8aef2c --- /dev/null +++ b/dust-lang/src/instruction/load_constant.rs @@ -0,0 +1,26 @@ +use crate::{Instruction, Operation}; + +pub struct LoadConstant { + pub destination: u16, + pub constant_index: u16, + pub jump_next: bool, +} + +impl From<&Instruction> for LoadConstant { + fn from(instruction: &Instruction) -> Self { + LoadConstant { + destination: instruction.a(), + constant_index: instruction.b(), + jump_next: instruction.c_as_boolean(), + } + } +} + +impl From for Instruction { + fn from(load_constant: LoadConstant) -> Self { + *Instruction::new(Operation::LoadConstant) + .set_a(load_constant.destination) + .set_b(load_constant.constant_index) + .set_c_to_boolean(load_constant.jump_next) + } +} diff --git a/dust-lang/src/instruction/load_list.rs b/dust-lang/src/instruction/load_list.rs new file mode 100644 index 0000000..6dcfb39 --- /dev/null +++ b/dust-lang/src/instruction/load_list.rs @@ -0,0 +1,23 @@ +use crate::{Instruction, Operation}; + +pub struct LoadList { + pub destination: u16, + pub start_register: u16, +} + +impl From<&Instruction> for LoadList { + fn from(instruction: &Instruction) -> Self { + LoadList { + destination: instruction.a(), + start_register: instruction.b(), + } + } +} + +impl From for Instruction { + fn from(load_list: LoadList) -> Self { + *Instruction::new(Operation::LoadList) + .set_a(load_list.destination) + .set_b(load_list.start_register) + } +} diff --git a/dust-lang/src/instruction/load_self.rs b/dust-lang/src/instruction/load_self.rs new file mode 100644 index 0000000..04cabbd --- /dev/null +++ b/dust-lang/src/instruction/load_self.rs @@ -0,0 +1,19 @@ +use crate::{Instruction, Operation}; + +pub struct LoadSelf { + pub destination: u16, +} + +impl From<&Instruction> for LoadSelf { + fn from(instruction: &Instruction) -> Self { + LoadSelf { + destination: instruction.a(), + } + } +} + +impl From for Instruction { + fn from(load_self: LoadSelf) -> Self { + *Instruction::new(Operation::LoadSelf).set_a(load_self.destination) + } +} diff --git a/dust-lang/src/instruction/mod.rs b/dust-lang/src/instruction/mod.rs new file mode 100644 index 0000000..121f354 --- /dev/null +++ b/dust-lang/src/instruction/mod.rs @@ -0,0 +1,544 @@ +//! An operation and its arguments for the Dust virtual machine. +//! +//! Each instruction is a 64-bit unsigned integer that is divided into five fields: +//! - Bits 0-8: The operation code. +//! - Bit 9: Boolean flag indicating whether the B argument is a constant. +//! - Bit 10: Boolean flag indicating whether the C argument is a constant. +//! - Bit 11: Boolean flag indicating whether the A argument is a local. +//! - Bit 12: Boolean flag indicating whether the B argument is a local. +//! - Bit 13: Boolean flag indicating whether the C argument is a local. +//! - Bits 17-32: The A argument, +//! - Bits 33-48: The B argument. +//! - Bits 49-63: The C argument. +//! +//! Be careful when working with instructions directly. When modifying an instruction, be sure to +//! account for the fact that setting the A, B, or C arguments to 0 will have no effect. It is +//! usually best to remove instructions and insert new ones in their place instead of mutating them. +mod add; +mod call; +mod call_native; +mod close; +mod define_local; +mod divide; +mod equal; +mod get_local; +mod jump; +mod less; +mod less_equal; +mod load_boolean; +mod load_constant; +mod load_list; +mod load_self; +mod modulo; +mod r#move; +mod multiply; +mod negate; +mod not; +mod r#return; +mod set_local; +mod subtract; +mod test; +mod test_set; + +use std::fmt::{self, Display, Formatter}; + +pub use add::Add; +pub use call::Call; +pub use call_native::CallNative; +pub use close::Close; +pub use define_local::DefineLocal; +pub use divide::Divide; +pub use equal::Equal; +pub use get_local::GetLocal; +pub use jump::Jump; +pub use less::Less; +pub use less_equal::LessEqual; +pub use load_boolean::LoadBoolean; +pub use load_constant::LoadConstant; +pub use load_list::LoadList; +pub use load_self::LoadSelf; +pub use modulo::Modulo; +pub use multiply::Multiply; +pub use negate::Negate; +pub use not::Not; +pub use r#move::Move; +pub use r#return::Return; +pub use set_local::SetLocal; +pub use subtract::Subtract; +pub use test::Test; +pub use test_set::TestSet; + +use serde::{Deserialize, Serialize}; + +use crate::{Chunk, NativeFunction, Operation}; + +pub enum Argument { + Constant(u16), + Local(u16), + Register(u16), +} + +impl Argument { + pub fn index(&self) -> u16 { + match self { + Argument::Constant(index) => *index, + Argument::Local(index) => *index, + Argument::Register(index) => *index, + } + } + + pub fn is_constant(&self) -> bool { + matches!(self, Argument::Constant(_)) + } + + pub fn is_local(&self) -> bool { + matches!(self, Argument::Local(_)) + } + + pub fn is_register(&self) -> bool { + matches!(self, Argument::Register(_)) + } +} + +impl Display for Argument { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + match self { + Argument::Constant(index) => write!(f, "C{index}"), + Argument::Local(index) => write!(f, "L{index}"), + Argument::Register(index) => write!(f, "R{index}"), + } + } +} + +/// An operation and its arguments for the Dust virtual machine. +/// +/// See the [module-level documentation](index.html) for more information. +#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize)] +pub struct Instruction(u64); + +impl Instruction { + pub fn new(operation: Operation) -> Instruction { + Instruction(operation as u64) + } + + pub fn destination_as_argument(&self) -> Option { + if self.yields_value() { + Some(Argument::Register(self.a())) + } else { + None + } + } + + pub fn b_as_argument(&self) -> Argument { + if self.b_is_constant() { + Argument::Constant(self.b()) + } else if self.b_is_local() { + Argument::Local(self.b()) + } else { + Argument::Register(self.b()) + } + } + + pub fn b_and_c_as_arguments(&self) -> (Argument, Argument) { + let left = if self.b_is_constant() { + Argument::Constant(self.b()) + } else if self.b_is_local() { + Argument::Local(self.b()) + } else { + Argument::Register(self.b()) + }; + let right = if self.c_is_constant() { + Argument::Constant(self.c()) + } else if self.c_is_local() { + Argument::Local(self.c()) + } else { + Argument::Register(self.c()) + }; + + (left, right) + } + + pub fn operation(&self) -> Operation { + Operation::from((self.0 & 0b11111111) as u8) + } + + pub fn set_b_is_constant(&mut self, boolean: bool) -> &mut Self { + self.0 = (self.0 & !(1 << 9)) | ((boolean as u64) << 9); + + self + } + + pub fn set_c_is_constant(&mut self, boolean: bool) -> &mut Self { + self.0 = (self.0 & !(1 << 10)) | ((boolean as u64) << 10); + + self + } + + pub fn set_a_is_local(&mut self, boolean: bool) -> &mut Self { + self.0 = (self.0 & !(1 << 11)) | ((boolean as u64) << 11); + + self + } + + pub fn set_b_is_local(&mut self, boolean: bool) -> &mut Self { + self.0 = (self.0 & !(1 << 12)) | ((boolean as u64) << 12); + + self + } + + pub fn set_c_is_local(&mut self, boolean: bool) -> &mut Self { + self.0 = (self.0 & !(1 << 13)) | ((boolean as u64) << 13); + + self + } + + pub fn a(&self) -> u16 { + ((self.0 >> 16) & 0b1111111111111111) as u16 + } + + pub fn a_as_boolean(&self) -> bool { + self.a() != 0 + } + + pub fn set_a(&mut self, a: u16) -> &mut Self { + self.0 = (self.0 & !(0b1111111111111111 << 16)) | ((a as u64) << 16); + + self + } + + pub fn set_a_to_boolean(&mut self, boolean: bool) -> &mut Self { + self.0 = (self.0 & !(0b1111111111111111 << 16)) | ((boolean as u64) << 16); + + self + } + + pub fn b(&self) -> u16 { + ((self.0 >> 32) & 0b1111111111111111) as u16 + } + + pub fn b_as_boolean(&self) -> bool { + self.b() != 0 + } + + pub fn set_b(&mut self, b: u16) -> &mut Self { + self.0 = (self.0 & !(0b1111111111111111 << 32)) | ((b as u64) << 32); + + self + } + + pub fn set_b_to_boolean(&mut self, boolean: bool) -> &mut Self { + self.0 = (self.0 & !(0b1111111111111111 << 32)) | ((boolean as u64) << 32); + + self + } + + pub fn c(&self) -> u16 { + ((self.0 >> 48) & 0b1111111111111111) as u16 + } + + pub fn c_as_boolean(&self) -> bool { + self.c() != 0 + } + + pub fn set_c(&mut self, c: u16) -> &mut Self { + self.0 = (self.0 & !(0b1111111111111111 << 48)) | ((c as u64) << 48); + + self + } + + pub fn set_c_to_boolean(&mut self, boolean: bool) -> &mut Self { + self.0 = (self.0 & !(0b1111111111111111 << 48)) | ((boolean as u64) << 48); + + self + } + + pub fn b_is_constant(&self) -> bool { + (self.0 >> 9) & 1 == 1 + } + + pub fn c_is_constant(&self) -> bool { + (self.0 >> 10) & 1 == 1 + } + + pub fn a_is_local(&self) -> bool { + (self.0 >> 11) & 1 == 1 + } + + pub fn b_is_local(&self) -> bool { + (self.0 >> 12) & 1 == 1 + } + + pub fn c_is_local(&self) -> bool { + (self.0 >> 13) & 1 == 1 + } + + pub fn yields_value(&self) -> bool { + match self.operation() { + Operation::LoadBoolean + | Operation::LoadConstant + | Operation::LoadList + | Operation::LoadSelf + | Operation::GetLocal + | Operation::Add + | Operation::Subtract + | Operation::Multiply + | Operation::Divide + | Operation::Modulo + | Operation::Equal + | Operation::Less + | Operation::LessEqual + | Operation::Negate + | Operation::Not + | Operation::Call => true, + + Operation::CallNative => { + let function = NativeFunction::from(self.b()); + + function.returns_value() + } + + Operation::Move + | Operation::Close + | Operation::DefineLocal + | Operation::SetLocal + | Operation::Test + | Operation::TestSet + | Operation::Jump + | Operation::Return => true, + } + } + + pub fn disassembly_info(&self, chunk: &Chunk) -> String { + match self.operation() { + Operation::Move => { + let Move { from, to } = Move::from(self); + + format!("{to} = {from}") + } + Operation::Close => { + let Close { from, to } = Close::from(self); + + format!("{from}..={to}") + } + Operation::LoadBoolean => { + let LoadBoolean { + destination, + value, + jump_next, + } = LoadBoolean::from(self); + + if jump_next { + format!("R{destination} = {value} && JUMP +1") + } else { + format!("R{destination} = {value}") + } + } + Operation::LoadConstant => { + let LoadConstant { + destination, + constant_index: constant, + jump_next, + } = LoadConstant::from(self); + + if jump_next { + format!("R{destination} = C{constant} JUMP +1") + } else { + format!("R{destination} = C{constant}") + } + } + Operation::LoadList => { + let LoadList { + destination, + start_register, + } = LoadList::from(self); + let end_register = destination - 1; + + format!("R{destination} = [R{start_register}..=R{end_register}]",) + } + Operation::LoadSelf => { + let LoadSelf { destination } = LoadSelf::from(self); + let name = chunk + .name() + .map(|idenifier| idenifier.as_str()) + .unwrap_or("self"); + + format!("R{destination} = {name}") + } + Operation::DefineLocal => { + let DefineLocal { + register, + local_index, + is_mutable, + } = DefineLocal::from(self); + + if is_mutable { + format!("mut L{local_index} = R{register}") + } else { + format!("L{local_index} = R{register}") + } + } + Operation::GetLocal => { + let GetLocal { + destination, + local_index, + } = GetLocal::from(self); + + format!("R{destination} = L{local_index}") + } + Operation::SetLocal => { + let SetLocal { + register, + local_index, + } = SetLocal::from(self); + + format!("L{local_index} = R{register}") + } + Operation::Add => { + let Add { + destination, + left, + right, + } = Add::from(self); + + format!("R{destination} = {left} + {right}") + } + Operation::Subtract => { + let Subtract { + destination, + left, + right, + } = Subtract::from(self); + + format!("R{destination} = {left} - {right}") + } + Operation::Multiply => { + let Multiply { + destination, + left, + right, + } = Multiply::from(self); + + format!("R{destination} = {left} * {right}") + } + Operation::Divide => { + let Divide { + destination, + left, + right, + } = Divide::from(self); + + format!("R{destination} = {left} / {right}") + } + Operation::Modulo => { + let Modulo { + destination, + left, + right, + } = Modulo::from(self); + + format!("R{destination} = {left} % {right}") + } + Operation::Test => { + let Test { argument, value } = Test::from(self); + let bang = if value { "" } else { "!" }; + + format!("if {bang}{argument} {{ JUMP +1 }}",) + } + Operation::TestSet => { + let TestSet { + destination, + argument, + value, + } = TestSet::from(self); + let bang = if value { "" } else { "!" }; + + format!("if {bang}{argument} {{ JUMP +1 }} else {{ R{destination} = {argument} }}") + } + Operation::Equal => { + let Equal { value, left, right } = Equal::from(self); + let comparison_symbol = if value { "==" } else { "!=" }; + + format!("if {left} {comparison_symbol} {right} {{ JUMP +1 }}") + } + Operation::Less => { + let Equal { value, left, right } = Equal::from(self); + let comparison_symbol = if value { "<" } else { ">=" }; + + format!("if {left} {comparison_symbol} {right} {{ JUMP +1 }}") + } + Operation::LessEqual => { + let LessEqual { value, left, right } = LessEqual::from(self); + let comparison_symbol = if value { "<=" } else { ">" }; + + format!("if {left} {comparison_symbol} {right} {{ JUMP +1 }}") + } + Operation::Negate => { + let Negate { + destination, + argument, + } = Negate::from(self); + + format!("R{destination} = -{argument}") + } + Operation::Not => { + let Not { + destination, + argument, + } = Not::from(self); + + format!("R{destination} = !{argument}") + } + Operation::Jump => { + let Jump { + offset, + is_positive, + } = Jump::from(self); + + if is_positive { + format!("JUMP +{offset}") + } else { + format!("JUMP -{offset}") + } + } + Operation::Call => { + let Call { + destination, + function, + argument_count, + } = Call::from(self); + let first_argument = destination.saturating_sub(argument_count); + let last_argument = destination - 1; + + format!("R{destination} = {function}(R{first_argument}..=R{last_argument})") + } + Operation::CallNative => { + let CallNative { + destination, + function, + argument_count, + } = CallNative::from(self); + let first_argument = destination.saturating_sub(argument_count); + let last_argument = destination - 1; + + format!("R{destination} = {function}(R{first_argument}..=R{last_argument})") + } + Operation::Return => { + let Return { + should_return_value, + } = Return::from(self); + + if should_return_value { + "RETURN".to_string() + } else { + "".to_string() + } + } + } + } +} + +impl From<&Instruction> for u64 { + fn from(instruction: &Instruction) -> Self { + instruction.0 + } +} diff --git a/dust-lang/src/instruction/modulo.rs b/dust-lang/src/instruction/modulo.rs new file mode 100644 index 0000000..298017e --- /dev/null +++ b/dust-lang/src/instruction/modulo.rs @@ -0,0 +1,32 @@ +use crate::{Argument, Instruction, Operation}; + +pub struct Modulo { + pub destination: u16, + pub left: Argument, + pub right: Argument, +} + +impl From<&Instruction> for Modulo { + fn from(instruction: &Instruction) -> Self { + let (left, right) = instruction.b_and_c_as_arguments(); + + Modulo { + destination: instruction.a(), + left, + right, + } + } +} + +impl From for Instruction { + fn from(modulo: Modulo) -> Self { + *Instruction::new(Operation::Modulo) + .set_a(modulo.destination) + .set_b(modulo.left.index()) + .set_b_is_constant(modulo.left.is_constant()) + .set_b_is_local(modulo.left.is_local()) + .set_c(modulo.right.index()) + .set_c_is_constant(modulo.right.is_constant()) + .set_c_is_local(modulo.right.is_local()) + } +} diff --git a/dust-lang/src/instruction/move.rs b/dust-lang/src/instruction/move.rs new file mode 100644 index 0000000..fe00943 --- /dev/null +++ b/dust-lang/src/instruction/move.rs @@ -0,0 +1,23 @@ +use crate::{Instruction, Operation}; + +pub struct Move { + pub from: u16, + pub to: u16, +} + +impl From<&Instruction> for Move { + fn from(instruction: &Instruction) -> Self { + Move { + from: instruction.b(), + to: instruction.a(), + } + } +} + +impl From for Instruction { + fn from(r#move: Move) -> Self { + *Instruction::new(Operation::Move) + .set_b(r#move.from) + .set_c(r#move.to) + } +} diff --git a/dust-lang/src/instruction/multiply.rs b/dust-lang/src/instruction/multiply.rs new file mode 100644 index 0000000..45ddea6 --- /dev/null +++ b/dust-lang/src/instruction/multiply.rs @@ -0,0 +1,32 @@ +use crate::{Argument, Instruction, Operation}; + +pub struct Multiply { + pub destination: u16, + pub left: Argument, + pub right: Argument, +} + +impl From<&Instruction> for Multiply { + fn from(instruction: &Instruction) -> Self { + let (left, right) = instruction.b_and_c_as_arguments(); + + Multiply { + destination: instruction.a(), + left, + right, + } + } +} + +impl From for Instruction { + fn from(multiply: Multiply) -> Self { + *Instruction::new(Operation::Multiply) + .set_a(multiply.destination) + .set_b(multiply.left.index()) + .set_b_is_constant(multiply.left.is_constant()) + .set_b_is_local(multiply.left.is_local()) + .set_c(multiply.right.index()) + .set_c_is_constant(multiply.right.is_constant()) + .set_c_is_local(multiply.right.is_local()) + } +} diff --git a/dust-lang/src/instruction/negate.rs b/dust-lang/src/instruction/negate.rs new file mode 100644 index 0000000..ccb1059 --- /dev/null +++ b/dust-lang/src/instruction/negate.rs @@ -0,0 +1,25 @@ +use crate::{Argument, Instruction, Operation}; + +pub struct Negate { + pub destination: u16, + pub argument: Argument, +} + +impl From<&Instruction> for Negate { + fn from(instruction: &Instruction) -> Self { + Negate { + destination: instruction.a(), + argument: instruction.b_as_argument(), + } + } +} + +impl From for Instruction { + fn from(negate: Negate) -> Self { + *Instruction::new(Operation::Negate) + .set_a(negate.destination) + .set_b(negate.argument.index()) + .set_b_is_constant(negate.argument.is_constant()) + .set_b_is_local(negate.argument.is_local()) + } +} diff --git a/dust-lang/src/instruction/not.rs b/dust-lang/src/instruction/not.rs new file mode 100644 index 0000000..0e16176 --- /dev/null +++ b/dust-lang/src/instruction/not.rs @@ -0,0 +1,25 @@ +use crate::{Argument, Instruction, Operation}; + +pub struct Not { + pub destination: u16, + pub argument: Argument, +} + +impl From<&Instruction> for Not { + fn from(instruction: &Instruction) -> Self { + Not { + destination: instruction.a(), + argument: instruction.b_as_argument(), + } + } +} + +impl From for Instruction { + fn from(not: Not) -> Self { + *Instruction::new(Operation::Not) + .set_a(not.destination) + .set_b(not.argument.index()) + .set_b_is_constant(not.argument.is_constant()) + .set_b_is_local(not.argument.is_local()) + } +} diff --git a/dust-lang/src/instruction/return.rs b/dust-lang/src/instruction/return.rs new file mode 100644 index 0000000..557d045 --- /dev/null +++ b/dust-lang/src/instruction/return.rs @@ -0,0 +1,19 @@ +use crate::{Instruction, Operation}; + +pub struct Return { + pub should_return_value: bool, +} + +impl From<&Instruction> for Return { + fn from(instruction: &Instruction) -> Self { + Return { + should_return_value: instruction.b_as_boolean(), + } + } +} + +impl From for Instruction { + fn from(r#return: Return) -> Self { + *Instruction::new(Operation::Return).set_b_to_boolean(r#return.should_return_value) + } +} diff --git a/dust-lang/src/instruction/set_local.rs b/dust-lang/src/instruction/set_local.rs new file mode 100644 index 0000000..6f1c747 --- /dev/null +++ b/dust-lang/src/instruction/set_local.rs @@ -0,0 +1,23 @@ +use crate::{Instruction, Operation}; + +pub struct SetLocal { + pub register: u16, + pub local_index: u16, +} + +impl From<&Instruction> for SetLocal { + fn from(instruction: &Instruction) -> Self { + SetLocal { + register: instruction.a(), + local_index: instruction.b(), + } + } +} + +impl From for Instruction { + fn from(set_local: SetLocal) -> Self { + *Instruction::new(Operation::SetLocal) + .set_a(set_local.register) + .set_b(set_local.local_index) + } +} diff --git a/dust-lang/src/instruction/subtract.rs b/dust-lang/src/instruction/subtract.rs new file mode 100644 index 0000000..b8d8a67 --- /dev/null +++ b/dust-lang/src/instruction/subtract.rs @@ -0,0 +1,32 @@ +use crate::{Argument, Instruction}; + +pub struct Subtract { + pub destination: u16, + pub left: Argument, + pub right: Argument, +} + +impl From<&Instruction> for Subtract { + fn from(instruction: &Instruction) -> Self { + let (left, right) = instruction.b_and_c_as_arguments(); + + Subtract { + destination: instruction.a(), + left, + right, + } + } +} + +impl From for Instruction { + fn from(subtract: Subtract) -> Self { + *Instruction::new(crate::Operation::Subtract) + .set_a(subtract.destination) + .set_b(subtract.left.index()) + .set_b_is_constant(subtract.left.is_constant()) + .set_b_is_local(subtract.left.is_local()) + .set_c(subtract.right.index()) + .set_c_is_constant(subtract.right.is_constant()) + .set_c_is_local(subtract.right.is_local()) + } +} diff --git a/dust-lang/src/instruction/test.rs b/dust-lang/src/instruction/test.rs new file mode 100644 index 0000000..7c32ebf --- /dev/null +++ b/dust-lang/src/instruction/test.rs @@ -0,0 +1,25 @@ +use crate::{Argument, Instruction, Operation}; + +pub struct Test { + pub argument: Argument, + pub value: bool, +} + +impl From<&Instruction> for Test { + fn from(instruction: &Instruction) -> Self { + Test { + argument: instruction.b_as_argument(), + value: instruction.c_as_boolean(), + } + } +} + +impl From for Instruction { + fn from(test: Test) -> Self { + *Instruction::new(Operation::Test) + .set_b(test.argument.index()) + .set_b_is_constant(test.argument.is_constant()) + .set_b_is_local(test.argument.is_local()) + .set_c_to_boolean(test.value) + } +} diff --git a/dust-lang/src/instruction/test_set.rs b/dust-lang/src/instruction/test_set.rs new file mode 100644 index 0000000..cac4096 --- /dev/null +++ b/dust-lang/src/instruction/test_set.rs @@ -0,0 +1,28 @@ +use crate::{Argument, Instruction, Operation}; + +pub struct TestSet { + pub destination: u16, + pub argument: Argument, + pub value: bool, +} + +impl From<&Instruction> for TestSet { + fn from(instruction: &Instruction) -> Self { + TestSet { + destination: instruction.a(), + argument: instruction.b_as_argument(), + value: instruction.c_as_boolean(), + } + } +} + +impl From for Instruction { + fn from(test_set: TestSet) -> Self { + *Instruction::new(Operation::TestSet) + .set_a(test_set.destination) + .set_b(test_set.argument.index()) + .set_b_is_constant(test_set.argument.is_constant()) + .set_b_is_local(test_set.argument.is_local()) + .set_c_to_boolean(test_set.value) + } +} diff --git a/dust-lang/src/lib.rs b/dust-lang/src/lib.rs index 736452d..d8e510c 100644 --- a/dust-lang/src/lib.rs +++ b/dust-lang/src/lib.rs @@ -21,7 +21,7 @@ pub use crate::compiler::{compile, CompileError, Compiler}; pub use crate::disassembler::Disassembler; pub use crate::dust_error::{AnnotatedError, DustError}; pub use crate::formatter::{format, Formatter}; -pub use crate::instruction::{Instruction, InstructionBuilder}; +pub use crate::instruction::{Argument, Instruction}; pub use crate::lexer::{lex, LexError, Lexer}; pub use crate::native_function::{NativeFunction, NativeFunctionError}; pub use crate::operation::Operation; diff --git a/dust-lang/src/optimizer.rs b/dust-lang/src/optimizer.rs index a660a9e..baa1ee2 100644 --- a/dust-lang/src/optimizer.rs +++ b/dust-lang/src/optimizer.rs @@ -1,6 +1,6 @@ //! Tool used by the compiler to optimize a chunk's bytecode. -use crate::{instruction::InstructionBuilder, Chunk, Instruction, Operation, Span}; +use crate::{Chunk, Instruction, Operation, Span}; /// An instruction optimizer that mutably borrows instructions from a chunk. #[derive(Debug)] @@ -58,11 +58,7 @@ impl<'a> Optimizer<'a> { let first_loader_register = first_loader.a(); let second_loader = &mut instructions.last_mut().unwrap().0; - let second_loader_new = Instruction::builder(second_loader.operation()) - .a(first_loader_register) - .b(second_loader.b()) - .c(second_loader.c()) - .build(); + let second_loader_new = *second_loader.clone().set_a(first_loader_register); *second_loader = second_loader_new; @@ -90,9 +86,7 @@ impl<'a> Optimizer<'a> { let set_local = instructions.pop().unwrap().0; let set_local_register = set_local.a(); let math_instruction = instructions.last_mut().unwrap().0; - let math_instruction_new = InstructionBuilder::from(&math_instruction) - .a(set_local_register) - .build(); + let math_instruction_new = *math_instruction.clone().set_a(set_local_register); instructions.last_mut().unwrap().0 = math_instruction_new;