diff --git a/dust-lang/src/compiler/mod.rs b/dust-lang/src/compiler/mod.rs index 2434839..9e25224 100644 --- a/dust-lang/src/compiler/mod.rs +++ b/dust-lang/src/compiler/mod.rs @@ -610,12 +610,13 @@ impl<'src> Compiler<'src> { &mut self, instruction: &Instruction, ) -> Result<(Argument, bool), CompileError> { - let argument = instruction.destination_as_argument().ok_or_else(|| { - CompileError::ExpectedExpression { - found: self.previous_token.to_owned(), - position: self.previous_position, - } - })?; + let argument = + instruction + .as_argument() + .ok_or_else(|| CompileError::ExpectedExpression { + found: self.previous_token.to_owned(), + position: self.previous_position, + })?; let push_back = matches!(argument, Argument::Register(_)); Ok((argument, push_back)) @@ -867,7 +868,20 @@ impl<'src> Compiler<'src> { self.get_last_operations(), Some([Operation::Test, Operation::Jump, _]) ); - let (left_instruction, left_type, left_position) = self.pop_last_instruction()?; + + let (mut left_instruction, left_type, left_position) = self.pop_last_instruction()?; + + if is_logic_chain { + let destination = self + .instructions + .iter() + .rev() + .nth(2) + .map_or(0, |(instruction, _, _)| instruction.a); + + left_instruction.a = destination; + } + let jump_index = self.instructions.len().saturating_sub(1); let mut jump_distance = if is_logic_chain { self.instructions.pop().map_or(0, |(jump, _, _)| { @@ -886,13 +900,10 @@ impl<'src> Compiler<'src> { }); } - let (argument, push_back) = self.handle_binary_argument(&left_instruction)?; - let is_local = left_instruction.operation() == Operation::GetLocal; + let (left, _) = self.handle_binary_argument(&left_instruction)?; - if push_back || is_local { - self.instructions - .push((left_instruction, left_type.clone(), left_position)); - } + self.instructions + .push((left_instruction, left_type.clone(), left_position)); let operator = self.current_token; let operator_position = self.current_position; @@ -908,16 +919,16 @@ impl<'src> Compiler<'src> { }) } }; - let test = Instruction::from(Test { - argument, - test_value: test_boolean, - }); + let test = Instruction::test(left, test_boolean); self.advance()?; self.emit_instruction(test, Type::None, operator_position); self.emit_instruction(Instruction::jump(1, true), Type::None, operator_position); self.parse_sub_expression(&rule.precedence)?; + let (mut right_instruction, _, _) = self.instructions.last_mut().unwrap(); + right_instruction.a = left_instruction.a; + if is_logic_chain { let expression_length = self.instructions.len() - jump_index - 1; jump_distance += expression_length as u8; @@ -992,7 +1003,7 @@ impl<'src> Compiler<'src> { }); self.emit_instruction(set_local, Type::None, start_position); - optimize_set_local(self); + optimize_set_local(self)?; return Ok(()); } @@ -1092,7 +1103,7 @@ impl<'src> Compiler<'src> { self.parse_expression()?; if let Some((instruction, _, _)) = self.instructions.last() { - let argument = match instruction.destination_as_argument() { + let argument = match instruction.as_argument() { Some(argument) => argument, None => { return Err(CompileError::ExpectedExpression { @@ -1323,7 +1334,18 @@ impl<'src> Compiler<'src> { } fn parse_sub_expression(&mut self, precedence: &Precedence) -> Result<(), CompileError> { - self.parse(precedence.increment()) + self.parse(precedence.increment())?; + + let expression_type = self.get_last_instruction_type(); + + if expression_type == Type::None || self.instructions.is_empty() { + return Err(CompileError::ExpectedExpression { + found: self.previous_token.to_owned(), + position: self.current_position, + }); + } + + Ok(()) } fn parse_return_statement(&mut self) -> Result<(), CompileError> { @@ -1572,12 +1594,13 @@ impl<'src> Compiler<'src> { }); } - let function = last_instruction.destination_as_argument().ok_or_else(|| { - CompileError::ExpectedExpression { - found: self.previous_token.to_owned(), - position: self.previous_position, - } - })?; + let function = + last_instruction + .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, diff --git a/dust-lang/src/compiler/optimize.rs b/dust-lang/src/compiler/optimize.rs index 58ae274..bb6b107 100644 --- a/dust-lang/src/compiler/optimize.rs +++ b/dust-lang/src/compiler/optimize.rs @@ -1,7 +1,5 @@ //! Tools used by the compiler to optimize a chunk's bytecode. -use smallvec::SmallVec; - use crate::{instruction::SetLocal, CompileError, Compiler, Instruction, Operation, Span, Type}; fn get_last_operations( diff --git a/dust-lang/src/instruction/mod.rs b/dust-lang/src/instruction/mod.rs index dda70df..68514e9 100644 --- a/dust-lang/src/instruction/mod.rs +++ b/dust-lang/src/instruction/mod.rs @@ -40,15 +40,15 @@ //! write, this ensures that the instruction has the correct flags to represent the arguments. //! //! ``` -//! # use dust_lang::instruction::{Instruction, Add, u8, Argument}; +//! # use dust_lang::instruction::{Instruction, Add, Argument}; //! let add_1 = Instruction::add( -//! u8::Register(0), -//! Argument::Local(1), +//! 0, +//! Argument::Register(1), //! Argument::Constant(2) //! ); //! let add_2 = Instruction::from(Add { -//! destination: u8::Register(0), -//! left: Argument::Local(1), +//! destination: 0, +//! left: Argument::Register(1), //! right: Argument::Constant(2), //! }); //! @@ -57,21 +57,20 @@ //! //! ## Reading Instructions //! -//! To read an instruction, check its `operation` field, then convert the instruction to the struct -//! that corresponds to that operation. Like the example above, this removes the burden of dealing -//! with the options and handles the process of reading the options and the instruction's fields -//! into `u8` or `Argument` enums when appropriate. +//! To read an instruction, check its operation with [`Instruction::operation`], then convert the +//! instruction to the struct that corresponds to that operation. Like the example above, this +//! removes the burden of dealing with the options directly and automatically casts the A, B, C and +//! D fields as `u8`, `bool` or `Argument` values. //! //! ``` -//! # use dust_lang::instruction::{Instruction, Add, u8, Argument, Operation}; +//! # use dust_lang::instruction::{Instruction, Add, Argument, Operation}; //! # let mystery_instruction = Instruction::add( -//! u8::Local(1), -//! Argument::Local(1), -//! Argument::Constant(2) -//! ); -//! -//! // Let's read an instruction and see if it performs addition-assignment, like in one of the -//! // following examples: +//! # 1, +//! # Argument::Register(1), +//! # Argument::Constant(2) +//! # ); +//! // Let's read an instruction and see if it performs addition-assignment, +//! // like in one of the following examples: //! // - `a += 2` //! // - `a = a + 2` //! // - `a = 2 + a` @@ -79,7 +78,7 @@ //! let operation = mystery_instruction.operation(); //! //! match operation { -//! Operation::ADD => { +//! Operation::Add => { //! let Add { destination, left, right } = Add::from(&mystery_instruction); //! let is_add_assign = //! left == Argument::Register(destination) @@ -87,10 +86,7 @@ //! //! assert!(is_add_assign); //! } -//! // Handle other operations... -//! _ => { -//! panic!("Unknown operation code: {operation}"); -//! } +//! _ => {} // Handle other operations... //! } //! ``` mod add; @@ -357,7 +353,7 @@ impl Instruction { }) } - pub fn destination_as_argument(&self) -> Option { + pub fn as_argument(&self) -> Option { match self.operation() { Operation::LoadConstant => Some(Argument::Constant(self.b)), Operation::LoadBoolean @@ -592,7 +588,7 @@ impl Instruction { } = Less::from(self); let comparison_symbol = if value { "<" } else { ">=" }; - format!("R{destination} {left} {comparison_symbol} {right}") + format!("R{destination} = {left} {comparison_symbol} {right}") } Operation::LessEqual => { let LessEqual { @@ -603,7 +599,7 @@ impl Instruction { } = LessEqual::from(self); let comparison_symbol = if value { "<=" } else { ">" }; - format!("R{destination} {left} {comparison_symbol} {right}") + format!("R{destination} = {left} {comparison_symbol} {right}") } Operation::Negate => { let Negate { @@ -687,7 +683,7 @@ impl Debug for Instruction { } } -#[derive(Debug, Clone, Copy)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)] pub enum Argument { Constant(u8), Register(u8), @@ -740,15 +736,15 @@ mod tests { use super::*; #[test] - fn instruction_is_8_bytes() { - assert_eq!(size_of::(), 8); + fn instruction_is_4_bytes() { + assert_eq!(size_of::(), 4); } #[test] fn instruction_layout() { - assert_eq!(offset_of!(Instruction, a), 0); - assert_eq!(offset_of!(Instruction, b), 1); - assert_eq!(offset_of!(Instruction, c), 2); - assert_eq!(offset_of!(Instruction, metadata), 3); + assert_eq!(offset_of!(Instruction, metadata), 0); + assert_eq!(offset_of!(Instruction, a), 1); + assert_eq!(offset_of!(Instruction, b), 2); + assert_eq!(offset_of!(Instruction, c), 3); } } diff --git a/dust-lang/src/scope.rs b/dust-lang/src/scope.rs index 4e84988..be1e061 100644 --- a/dust-lang/src/scope.rs +++ b/dust-lang/src/scope.rs @@ -47,6 +47,6 @@ impl Scope { impl Display for Scope { fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "({}, {})", self.depth, self.block_index) + write!(f, "{}.{}", self.depth, self.block_index) } } diff --git a/dust-lang/src/value/abstract_value.rs b/dust-lang/src/value/abstract_value.rs index 5eb1bcf..7949faa 100644 --- a/dust-lang/src/value/abstract_value.rs +++ b/dust-lang/src/value/abstract_value.rs @@ -24,7 +24,7 @@ impl AbstractValue { let mut resolved_items = Vec::with_capacity(items.len()); for pointer in items { - let resolved_item = vm.follow_pointer(*pointer)?.to_concrete_owned(vm)?; + let resolved_item = vm.follow_pointer(*pointer)?.into_concrete_owned(vm)?; resolved_items.push(resolved_item); } diff --git a/dust-lang/src/value/mod.rs b/dust-lang/src/value/mod.rs index 3472687..e2d973a 100644 --- a/dust-lang/src/value/mod.rs +++ b/dust-lang/src/value/mod.rs @@ -25,10 +25,10 @@ impl Value { } } - pub fn to_concrete_owned(&self, vm: &Vm) -> Result { + pub fn into_concrete_owned(self, vm: &Vm) -> Result { match self { Value::Abstract(abstract_value) => abstract_value.to_concrete_owned(vm), - Value::Concrete(concrete_value) => Ok(concrete_value.clone()), + Value::Concrete(concrete_value) => Ok(concrete_value), } } @@ -64,10 +64,10 @@ impl ValueRef<'_> { } } - pub fn to_concrete_owned(&self, vm: &Vm) -> Result { + pub fn into_concrete_owned(self, vm: &Vm) -> Result { match self { ValueRef::Abstract(abstract_value) => abstract_value.to_concrete_owned(vm), - ValueRef::Concrete(concrete_value) => Ok((*concrete_value).clone()), + ValueRef::Concrete(concrete_value) => Ok(concrete_value.clone()), } } diff --git a/dust-lang/src/vm.rs b/dust-lang/src/vm.rs index a521548..3276d0d 100644 --- a/dust-lang/src/vm.rs +++ b/dust-lang/src/vm.rs @@ -169,7 +169,7 @@ impl<'a> Vm<'a> { let local_register_index = self.get_local_register(local_index)?; let register = Register::Pointer(Pointer::Stack(register_index)); - self.set_register(local_register_index, register); + self.set_register(local_register_index, register)?; } Operation::Add => { let Add { @@ -471,7 +471,7 @@ impl<'a> Vm<'a> { self.chunk } else { return Err(VmError::ExpectedFunction { - found: function.to_concrete_owned(self)?, + found: function.into_concrete_owned(self)?, position: self.current_position(), }); }; @@ -521,7 +521,7 @@ impl<'a> Vm<'a> { return if let Some(register_index) = self.last_assigned_register { let return_value = self .open_register(register_index)? - .to_concrete_owned(self)?; + .into_concrete_owned(self)?; Ok(Some(return_value)) } else {