diff --git a/dust-lang/src/chunk.rs b/dust-lang/src/chunk.rs index 19c36cd..c2e8720 100644 --- a/dust-lang/src/chunk.rs +++ b/dust-lang/src/chunk.rs @@ -36,10 +36,6 @@ impl Chunk { } } - pub fn scope_depth(&self) -> usize { - self.scope_depth - } - pub fn len(&self) -> usize { self.instructions.len() } @@ -55,7 +51,7 @@ impl Chunk { ) -> Result<&(Instruction, Span), ChunkError> { self.instructions .get(offset) - .ok_or(ChunkError::CodeIndexOfBounds { offset, position }) + .ok_or(ChunkError::InstructionIndexOfBounds { offset, position }) } pub fn remove_instruction(&mut self, index: usize) -> (Instruction, Span) { @@ -410,7 +406,7 @@ impl<'a> ChunkDisassembler<'a> { pub fn disassemble(&self) -> String { let mut disassembly = String::with_capacity(self.predict_length()); let indent = "│ ".repeat(self.indent); - let top_border = "┌".to_string() + &"─".repeat(self.width) + "┐"; + let top_border = "┌".to_string() + &"─".repeat(self.width - 2) + "┐"; disassembly.push_str(&indent); disassembly.push_str(&top_border); @@ -418,9 +414,13 @@ impl<'a> ChunkDisassembler<'a> { let center_and_style = |line: &str, style: bool| { if style { - format!("│{line:^width$}│", line = line.bold(), width = self.width) + format!( + "│{line:^width$}│", + line = line.bold(), + width = self.width - 2 + ) } else { - format!("│{line:^width$}│", width = self.width) + format!("│{line:^width$}│", width = self.width - 2) } }; let mut push = |line: &str, style: bool| { @@ -576,7 +576,7 @@ impl<'a> ChunkDisassembler<'a> { } let indent = "│ ".repeat(self.indent); - let bottom_border = "└".to_string() + &"─".repeat(self.width) + "┘"; + let bottom_border = "└".to_string() + &"─".repeat(self.width - 2) + "┘"; disassembly.push_str(&indent); disassembly.push_str(&bottom_border); @@ -628,7 +628,7 @@ impl<'a> ChunkDisassembler<'a> { #[derive(Debug, Clone, PartialEq)] pub enum ChunkError { - CodeIndexOfBounds { + InstructionIndexOfBounds { offset: usize, position: Span, }, @@ -666,7 +666,7 @@ impl AnnotatedError for ChunkError { fn description(&self) -> &'static str { match self { - ChunkError::CodeIndexOfBounds { .. } => "Code index out of bounds", + ChunkError::InstructionIndexOfBounds { .. } => "Instruction index out of bounds", ChunkError::ConstantAlreadyUsed { .. } => "Constant already used", ChunkError::ConstantOverflow { .. } => "Constant overflow", ChunkError::ConstantIndexOutOfBounds { .. } => "Constant index out of bounds", @@ -679,7 +679,9 @@ impl AnnotatedError for ChunkError { fn details(&self) -> Option { match self { - ChunkError::CodeIndexOfBounds { offset, .. } => Some(format!("Code index: {}", offset)), + ChunkError::InstructionIndexOfBounds { offset, .. } => { + Some(format!("Instruction index: {}", offset)) + } ChunkError::ConstantAlreadyUsed { index, .. } => { Some(format!("Constant index: {}", index)) } @@ -700,7 +702,7 @@ impl AnnotatedError for ChunkError { fn position(&self) -> Span { match self { - ChunkError::CodeIndexOfBounds { position, .. } => *position, + ChunkError::InstructionIndexOfBounds { position, .. } => *position, ChunkError::ConstantAlreadyUsed { position, .. } => *position, ChunkError::ConstantIndexOutOfBounds { position, .. } => *position, ChunkError::IdentifierNotFound { position, .. } => *position, diff --git a/dust-lang/src/instruction.rs b/dust-lang/src/instruction.rs index 7bb6a04..f56cafe 100644 --- a/dust-lang/src/instruction.rs +++ b/dust-lang/src/instruction.rs @@ -208,8 +208,12 @@ impl Instruction { instruction } - pub fn r#return() -> Instruction { - Instruction(Operation::Return as u32) + pub fn r#return(should_return_value: bool) -> Instruction { + let mut instruction = Instruction(Operation::Return as u32); + + instruction.set_b(if should_return_value { 1 } else { 0 }); + + instruction } pub fn operation(&self) -> Operation { @@ -777,8 +781,9 @@ mod tests { #[test] fn r#return() { - let instruction = Instruction::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/parser.rs b/dust-lang/src/parser.rs index cf6cc6b..42f155f 100644 --- a/dust-lang/src/parser.rs +++ b/dust-lang/src/parser.rs @@ -41,6 +41,7 @@ pub struct Parser<'src> { current_position: Span, previous_token: Token<'src>, previous_position: Span, + parsed_expression: bool, } impl<'src> Parser<'src> { @@ -61,6 +62,7 @@ impl<'src> Parser<'src> { current_position, previous_token: Token::Eof, previous_position: Span(0, 0), + parsed_expression: false, }) } @@ -184,6 +186,8 @@ impl<'src> Parser<'src> { position, ); + self.parsed_expression = true; + Ok(()) } @@ -203,6 +207,8 @@ impl<'src> Parser<'src> { self.emit_constant(value, position)?; } + self.parsed_expression = true; + Ok(()) } @@ -217,6 +223,8 @@ impl<'src> Parser<'src> { self.emit_constant(value, position)?; } + self.parsed_expression = true; + Ok(()) } @@ -237,6 +245,8 @@ impl<'src> Parser<'src> { self.emit_constant(value, position)?; } + self.parsed_expression = true; + Ok(()) } @@ -257,6 +267,8 @@ impl<'src> Parser<'src> { self.emit_constant(value, position)?; } + self.parsed_expression = true; + Ok(()) } @@ -271,6 +283,8 @@ impl<'src> Parser<'src> { self.emit_constant(value, position)?; } + self.parsed_expression = true; + Ok(()) } @@ -284,7 +298,11 @@ impl<'src> Parser<'src> { }, Context::None, )?; - self.expect(TokenKind::RightParenthesis) + self.expect(TokenKind::RightParenthesis)?; + + self.parsed_expression = true; + + Ok(()) } fn parse_unary(&mut self, _: Allowed) -> Result<(), ParseError> { @@ -345,6 +363,8 @@ impl<'src> Parser<'src> { self.emit_instruction(instruction, operator_position); + self.parsed_expression = true; + Ok(()) } @@ -439,16 +459,16 @@ impl<'src> Parser<'src> { current }; - let mut new_instruction = match operator.kind() { - TokenKind::Plus => Instruction::add(register, left, right), - TokenKind::PlusEqual => Instruction::add(register, left, right), - TokenKind::Minus => Instruction::subtract(register, left, right), - TokenKind::MinusEqual => Instruction::subtract(register, left, right), - TokenKind::Star => Instruction::multiply(register, left, right), - TokenKind::StarEqual => Instruction::multiply(register, left, right), - TokenKind::Slash => Instruction::divide(register, left, right), - TokenKind::SlashEqual => Instruction::divide(register, left, right), - TokenKind::Percent => Instruction::modulo(register, left, right), + let (mut new_instruction, is_expression) = match operator.kind() { + TokenKind::Plus => (Instruction::add(register, left, right), true), + TokenKind::PlusEqual => (Instruction::add(register, left, right), false), + TokenKind::Minus => (Instruction::subtract(register, left, right), true), + TokenKind::MinusEqual => (Instruction::subtract(register, left, right), false), + TokenKind::Star => (Instruction::multiply(register, left, right), true), + TokenKind::StarEqual => (Instruction::multiply(register, left, right), false), + TokenKind::Slash => (Instruction::divide(register, left, right), true), + TokenKind::SlashEqual => (Instruction::divide(register, left, right), false), + TokenKind::Percent => (Instruction::modulo(register, left, right), true), _ => { return Err(ParseError::ExpectedTokenMultiple { expected: &[ @@ -468,6 +488,8 @@ impl<'src> Parser<'src> { } }; + self.parsed_expression = is_expression; + if left_is_constant { new_instruction.set_b_is_constant(); } @@ -549,6 +571,7 @@ impl<'src> Parser<'src> { Token::LessEqual => Instruction::less_equal(true, left.saturating_sub(1), right), Token::Greater => Instruction::less_equal(false, left.saturating_sub(1), right), Token::GreaterEqual => Instruction::less(false, left.saturating_sub(1), right), + _ => { return Err(ParseError::ExpectedTokenMultiple { expected: &[ @@ -565,6 +588,8 @@ impl<'src> Parser<'src> { } }; + self.parsed_expression = true; + if left_is_constant { instruction.set_b_is_constant(); } @@ -626,6 +651,8 @@ impl<'src> Parser<'src> { self.emit_instruction(Instruction::jump(1, true), operator_position); self.emit_instruction(right_instruction, right_position); + self.parsed_expression = true; + Ok(()) } @@ -690,11 +717,15 @@ impl<'src> Parser<'src> { Instruction::set_local(self.current_register, local_index), start_position, ); + + self.parsed_expression = false; } else { self.emit_instruction( Instruction::get_local(self.current_register, local_index), self.previous_position, ); + + self.parsed_expression = true; } Ok(()) @@ -793,6 +824,9 @@ impl<'src> Parser<'src> { Instruction::load_list(self.current_register, start_register, end_register), Span(start, end), ); + self.increment_register()?; + + self.parsed_expression = true; Ok(()) } @@ -847,7 +881,11 @@ impl<'src> Parser<'src> { if let Token::LeftCurlyBrace = self.current_token { self.parse_block(block_allowed)?; + + self.parsed_expression = true; } + } else { + self.parsed_expression = false; } Ok(()) @@ -888,6 +926,8 @@ impl<'src> Parser<'src> { self.chunk .insert_instruction(jump_end, jump_back, self.current_position); + self.parsed_expression = false; + Ok(()) } @@ -915,11 +955,10 @@ impl<'src> Parser<'src> { } } - let is_expression = self.chunk.get_last_instruction()?.0.is_expression(); let has_semicolon = self.allow(TokenKind::Semicolon)?; - if !has_semicolon && is_expression && allowed.implicit_return { - self.emit_instruction(Instruction::r#return(), self.current_position); + if !has_semicolon && self.parsed_expression && allowed.implicit_return { + self.emit_instruction(Instruction::r#return(true), self.current_position); } Ok(()) @@ -936,7 +975,7 @@ impl<'src> Parser<'src> { self.advance()?; - if !matches!( + let has_return_value = if !matches!( self.current_token, Token::Semicolon | Token::RightCurlyBrace ) { @@ -948,11 +987,17 @@ impl<'src> Parser<'src> { }, Context::None, )?; - } + + true + } else { + false + }; let end = self.current_position.1; - self.emit_instruction(Instruction::r#return(), Span(start, end)); + self.emit_instruction(Instruction::r#return(has_return_value), Span(start, end)); + + self.parsed_expression = false; Ok(()) } @@ -1015,6 +1060,8 @@ impl<'src> Parser<'src> { position, ); + self.parsed_expression = false; + Ok(()) } @@ -1093,6 +1140,8 @@ impl<'src> Parser<'src> { self.lexer.skip_to(function_end); self.emit_constant(function, Span(function_start, function_end))?; + self.parsed_expression = true; + Ok(()) } diff --git a/dust-lang/src/vm.rs b/dust-lang/src/vm.rs index d52034b..08fe6c0 100644 --- a/dust-lang/src/vm.rs +++ b/dust-lang/src/vm.rs @@ -1,6 +1,8 @@ +use std::mem::replace; + use crate::{ - parse, AnnotatedError, Chunk, ChunkError, DustError, Identifier, Instruction, Local, Operation, - Span, Value, ValueError, + parse, AnnotatedError, Chunk, ChunkError, DustError, Identifier, Instruction, Operation, Span, + Value, ValueError, }; pub fn run(source: &str) -> Result, DustError> { @@ -14,9 +16,9 @@ pub fn run(source: &str) -> Result, DustError> { #[derive(Debug, Eq, PartialEq)] pub struct Vm { - chunk: Chunk, ip: usize, - register_stack: Vec>, + chunk: Chunk, + stack: Vec, } impl Vm { @@ -24,9 +26,9 @@ impl Vm { pub fn new(chunk: Chunk) -> Self { Self { - chunk, ip: 0, - register_stack: Vec::new(), + chunk, + stack: Vec::new(), } } @@ -56,26 +58,35 @@ impl Vm { } while let Ok((instruction, position)) = self.read(Span(0, 0)).copied() { - let ip = self.ip - 1; - let operation = instruction.operation(); - let position_display = position.to_string(); - - log::info!("{ip:^3} | {position_display:^10} | {operation}"); + log::info!( + "{} | {} | {} | {}", + self.ip - 1, + position, + instruction.operation(), + instruction + .disassembly_info(Some(&self.chunk)) + .0 + .unwrap_or_default() + ); match instruction.operation() { Operation::Move => { - let from = instruction.b(); - let to = instruction.a(); - let value = self.take(from, position)?; + let to_register = instruction.a(); + let from_register = instruction.b(); + let value = self.take(from_register, to_register, position)?; - self.insert(value, to, position)?; + self.set(to_register, value, position)?; } Operation::Close => { - let from = instruction.b(); - let to = instruction.c(); + let from_register = instruction.b(); + let to_register = instruction.c(); - for register_index in from..to { - self.register_stack[register_index as usize] = None; + if self.stack.len() < to_register as usize { + return Err(VmError::StackUnderflow { position }); + } + + for register_index in from_register..to_register { + self.stack[register_index as usize] = Register::Empty; } } Operation::LoadBoolean => { @@ -84,7 +95,7 @@ impl Vm { let skip = instruction.c_as_boolean(); let value = Value::boolean(boolean); - self.insert(value, to_register, position)?; + self.set(to_register, value, position)?; if skip { self.ip += 1; @@ -94,9 +105,8 @@ impl Vm { let to_register = instruction.a(); let from_constant = instruction.b(); let jump = instruction.c_as_boolean(); - let value = self.chunk.take_constant(from_constant, position)?; - self.insert(value, to_register, position)?; + self.set_constant(to_register, from_constant, position)?; if jump { self.ip += 1; @@ -110,7 +120,7 @@ impl Vm { let mut list = Vec::with_capacity(length as usize); for register_index in first_register..=last_register { - let value = match self.take(register_index, position) { + let value = match self.take(register_index, to_register, position) { Ok(value) => value, Err(VmError::EmptyRegister { .. }) => continue, Err(error) => return Err(error), @@ -119,7 +129,7 @@ impl Vm { list.push(value); } - self.insert(Value::list(list), to_register, position)?; + self.set(to_register, Value::list(list), position)?; } Operation::DefineLocal => { let from_register = instruction.a(); @@ -128,22 +138,23 @@ impl Vm { self.chunk.define_local(to_local, from_register, position)?; } Operation::GetLocal => { - let register_index = instruction.a(); + let to_register = instruction.a(); let local_index = instruction.b(); let local = self.chunk.get_local(local_index, position)?; - let value = if let Some(index) = local.register_index { - self.take(index, position)? - } else { - return Err(VmError::UndefinedVariable { - identifier: local.identifier.clone(), - position, - }); - }; + let from_register = + local + .register_index + .ok_or_else(|| VmError::UndefinedVariable { + identifier: local.identifier.clone(), + position, + })?; - self.insert(value, register_index, position)?; + let value = self.take(from_register, to_register, position)?; + + self.set(to_register, value, position)?; } Operation::SetLocal => { - let register_index = instruction.a(); + let register = instruction.a(); let local_index = instruction.b(); let local = self.chunk.get_local(local_index, position)?; @@ -154,15 +165,7 @@ impl Vm { }); } - let new_value = if instruction.b_is_constant() { - self.chunk.take_constant(register_index, position)? - } else { - self.take(register_index, position)? - }; - - self.insert(new_value, register_index, position)?; - self.chunk - .define_local(local_index, register_index, position)?; + self.chunk.define_local(local_index, register, position)?; } Operation::Add => { let (left, right) = get_arguments(self, instruction, position)?; @@ -170,7 +173,7 @@ impl Vm { .add(right) .map_err(|error| VmError::Value { error, position })?; - self.insert(sum, instruction.a(), position)?; + self.set(instruction.a(), sum, position)?; } Operation::Subtract => { let (left, right) = get_arguments(self, instruction, position)?; @@ -178,16 +181,15 @@ impl Vm { .subtract(right) .map_err(|error| VmError::Value { error, position })?; - self.insert(difference, instruction.a(), position)?; + self.set(instruction.a(), difference, position)?; } Operation::Multiply => { let (left, right) = get_arguments(self, instruction, position)?; - let product = left .multiply(right) .map_err(|error| VmError::Value { error, position })?; - self.insert(product, instruction.a(), position)?; + self.set(instruction.a(), product, position)?; } Operation::Divide => { let (left, right) = get_arguments(self, instruction, position)?; @@ -195,7 +197,7 @@ impl Vm { .divide(right) .map_err(|error| VmError::Value { error, position })?; - self.insert(quotient, instruction.a(), position)?; + self.set(instruction.a(), quotient, position)?; } Operation::Modulo => { let (left, right) = get_arguments(self, instruction, position)?; @@ -203,7 +205,7 @@ impl Vm { .modulo(right) .map_err(|error| VmError::Value { error, position })?; - self.insert(remainder, instruction.a(), position)?; + self.set(instruction.a(), remainder, position)?; } Operation::Test => { let register = instruction.a(); @@ -222,22 +224,28 @@ impl Vm { let to_register = instruction.a(); let argument = instruction.b(); let test_value = instruction.c_as_boolean(); - let value = self.clone(argument, position)?; - let boolean = value.as_boolean().ok_or_else(|| VmError::ExpectedBoolean { - found: value.clone(), - position, - })?; + let borrowed_value = self.get(argument, position)?; + let boolean = + borrowed_value + .as_boolean() + .ok_or_else(|| VmError::ExpectedBoolean { + found: borrowed_value.clone(), + position, + })?; if boolean == test_value { - self.insert(value, to_register, position)?; + let value = self.take(argument, to_register, position)?; + + self.set(to_register, value, position)?; } else { self.ip += 1; } } Operation::Equal => { - let (jump, _) = *self.chunk.get_instruction(self.ip, position)?; - - debug_assert_eq!(jump.operation(), Operation::Jump); + debug_assert_eq!( + self.chunk.get_instruction(self.ip, position)?.0.operation(), + Operation::Jump + ); let (left, right) = get_arguments(self, instruction, position)?; @@ -254,6 +262,7 @@ impl Vm { if boolean == compare_to { self.ip += 1; } else { + let (jump, _) = *self.chunk.get_instruction(self.ip, position)?; let jump_distance = jump.a(); let is_positive = jump.b_as_boolean(); let new_ip = if is_positive { @@ -266,12 +275,12 @@ impl Vm { } } Operation::Less => { - let jump = self.chunk.get_instruction(self.ip, position)?.0; - - debug_assert_eq!(jump.operation(), Operation::Jump); + debug_assert_eq!( + self.chunk.get_instruction(self.ip, position)?.0.operation(), + Operation::Jump + ); let (left, right) = get_arguments(self, instruction, position)?; - let boolean = left .less_than(right) .map_err(|error| VmError::Value { error, position })? @@ -285,6 +294,7 @@ impl Vm { if boolean == compare_to { self.ip += 1; } else { + let jump = self.chunk.get_instruction(self.ip, position)?.0; let jump_distance = jump.a(); let is_positive = jump.b_as_boolean(); let new_ip = if is_positive { @@ -297,9 +307,10 @@ impl Vm { } } Operation::LessEqual => { - let jump = self.chunk.get_instruction(self.ip, position)?.0; - - debug_assert_eq!(jump.operation(), Operation::Jump); + debug_assert_eq!( + self.chunk.get_instruction(self.ip, position)?.0.operation(), + Operation::Jump + ); let (left, right) = get_arguments(self, instruction, position)?; let boolean = left @@ -315,6 +326,7 @@ impl Vm { if boolean == compare_to { self.ip += 1; } else { + let jump = self.chunk.get_instruction(self.ip, position)?.0; let jump_distance = jump.a(); let is_positive = jump.b_as_boolean(); let new_ip = if is_positive { @@ -336,7 +348,7 @@ impl Vm { .negate() .map_err(|error| VmError::Value { error, position })?; - self.insert(negated, instruction.a(), position)?; + self.set(instruction.a(), negated, position)?; } Operation::Not => { let value = if instruction.b_is_constant() { @@ -348,7 +360,7 @@ impl Vm { .not() .map_err(|error| VmError::Value { error, position })?; - self.insert(not, instruction.a(), position)?; + self.set(instruction.a(), not, position)?; } Operation::Jump => { let offset = instruction.b(); @@ -362,7 +374,11 @@ impl Vm { self.ip = new_ip; } Operation::Return => { - return if let Some(Some(value)) = self.register_stack.pop() { + let should_return_value = instruction.b_as_boolean(); + + return if should_return_value { + let value = self.empty(self.stack.len() - 1, position)?; + Ok(Some(value)) } else { Ok(None) @@ -374,124 +390,136 @@ impl Vm { Ok(None) } - fn insert(&mut self, value: Value, index: u8, position: Span) -> Result<(), VmError> { - if self.register_stack.len() == Self::STACK_LIMIT { - Err(VmError::StackOverflow { position }) - } else { - let index = index as usize; - - while index >= self.register_stack.len() { - self.register_stack.push(None); - } - - self.register_stack[index] = Some(value); - - Ok(()) - } - } - - fn take(&mut self, index: u8, position: Span) -> Result { + fn set(&mut self, index: u8, value: Value, position: Span) -> Result<(), VmError> { + let length = self.stack.len(); let index = index as usize; - if let Some(register) = self.register_stack.get_mut(index) { - let value = register - .take() - .ok_or_else(|| VmError::EmptyRegister { index, position })?; - - Ok(value) - } else { - Err(VmError::RegisterIndexOutOfBounds { position }) + if length == Self::STACK_LIMIT { + return Err(VmError::StackOverflow { position }); } + + if index == length { + log::trace!("Push register {index} with value {value}"); + + self.stack.push(Register::Value(value)); + + return Ok(()); + } + + if index < length { + log::trace!("Replace register {index} with value {value}"); + + self.stack[index] = Register::Value(value); + + return Ok(()); + } + + Err(VmError::SkippedRegister { + index, + length: self.stack.len(), + position, + }) + } + + fn set_constant( + &mut self, + index: u8, + constant_index: u8, + position: Span, + ) -> Result<(), VmError> { + let length = self.stack.len(); + let index = index as usize; + + if length == Self::STACK_LIMIT { + return Err(VmError::StackOverflow { position }); + } + + if index == length { + log::trace!("Push register {index} with constant {constant_index}"); + + self.stack.push(Register::Constant(constant_index)); + + return Ok(()); + } + + if index < length { + log::trace!("Replace register {index} with constant {constant_index}"); + + self.stack[index] = Register::Constant(constant_index); + + return Ok(()); + } + + Err(VmError::SkippedRegister { + index, + length: self.stack.len(), + position, + }) } fn get(&self, index: u8, position: Span) -> Result<&Value, VmError> { let index = index as usize; + let register = self + .stack + .get(index) + .ok_or_else(|| VmError::RegisterIndexOutOfBounds { position })?; - if let Some(register) = self.register_stack.get(index) { - let value = register - .as_ref() - .ok_or_else(|| VmError::EmptyRegister { index, position })?; + match register { + Register::Value(value) => Ok(value), + Register::Pointer(register_index) => { + let value = self.get(*register_index, position)?; - Ok(value) - } else { - Err(VmError::RegisterIndexOutOfBounds { position }) + Ok(value) + } + Register::Constant(constant_index) => { + let value = self.chunk.get_constant(*constant_index, position)?; + + Ok(value) + } + Register::Empty => Err(VmError::EmptyRegister { index, position }), } } - fn clone(&mut self, index: u8, position: Span) -> Result { + fn take(&mut self, index: u8, point_to: u8, position: Span) -> Result { let index = index as usize; + let register = replace(&mut self.stack[index], Register::Pointer(point_to)); - if let Some(register) = self.register_stack.get_mut(index) { - let cloneable = if let Some(value) = register.take() { - if value.is_raw() { - value.into_reference() - } else { - value - } - } else { - return Err(VmError::EmptyRegister { index, position }); - }; + match register { + Register::Value(value) => Ok(value), + Register::Pointer(register_index) => { + let value = self.take(register_index, point_to, position)?; - *register = Some(cloneable.clone()); + Ok(value) + } + Register::Constant(constant_index) => { + let value = self.chunk.take_constant(constant_index, position)?; - Ok(cloneable) - } else { - Err(VmError::RegisterIndexOutOfBounds { position }) + Ok(value) + } + Register::Empty => Err(VmError::EmptyRegister { index, position }), } } - fn _clone_mutable(&mut self, index: u8, position: Span) -> Result { - let index = index as usize; - - if let Some(register) = self.register_stack.get_mut(index) { - let cloneable = if let Some(value) = register.take() { - value.into_mutable() - } else { - return Err(VmError::EmptyRegister { index, position }); - }; - - *register = Some(cloneable.clone()); - - Ok(cloneable) - } else { - Err(VmError::RegisterIndexOutOfBounds { position }) + fn empty(&mut self, index: usize, position: Span) -> Result { + if index >= self.stack.len() { + return Err(VmError::RegisterIndexOutOfBounds { position }); } - } - fn _clone_as_variable(&mut self, local: Local, position: Span) -> Result { - let index = if let Some(index) = local.register_index { - index - } else { - return Err(VmError::UndefinedVariable { - identifier: local.identifier, - position, - }); - }; - let clone_result = if local.is_mutable { - self._clone_mutable(index, position) - } else { - self.clone(index, position) - }; + let register = replace(&mut self.stack[index], Register::Empty); - match clone_result { - Err(VmError::EmptyRegister { .. }) => Err(VmError::UndefinedVariable { - identifier: local.identifier, - position, - }), - _ => clone_result, - } - } + match register { + Register::Value(value) => Ok(value), + Register::Pointer(register_index) => { + let value = self.empty(register_index as usize, position)?; - fn _pop(&mut self, position: Span) -> Result { - if let Some(register) = self.register_stack.pop() { - let value = register.ok_or(VmError::EmptyRegister { - index: self.register_stack.len().saturating_sub(1), - position, - })?; + Ok(value) + } + Register::Constant(constant_index) => { + let value = self.chunk.take_constant(constant_index, position)?; - Ok(value) - } else { - Err(VmError::StackUnderflow { position }) + Ok(value) + } + Register::Empty => Err(VmError::EmptyRegister { index, position }), } } @@ -504,6 +532,14 @@ impl Vm { } } +#[derive(Debug, Eq, PartialEq)] +enum Register { + Empty, + Value(Value), + Pointer(u8), + Constant(u8), +} + #[derive(Debug, Clone, PartialEq)] pub enum VmError { CannotMutateImmutableLocal { @@ -525,6 +561,11 @@ pub enum VmError { instruction: Instruction, position: Span, }, + SkippedRegister { + index: usize, + length: usize, + position: Span, + }, StackOverflow { position: Span, }, @@ -562,11 +603,12 @@ impl AnnotatedError for VmError { Self::ExpectedBoolean { .. } => "Expected boolean", Self::RegisterIndexOutOfBounds { .. } => "Register index out of bounds", Self::InvalidInstruction { .. } => "Invalid instruction", + Self::SkippedRegister { .. } => "Skipped register", Self::StackOverflow { .. } => "Stack overflow", Self::StackUnderflow { .. } => "Stack underflow", Self::UndefinedVariable { .. } => "Undefined variable", - Self::Value { .. } => "Value error", Self::Chunk(error) => error.description(), + Self::Value { .. } => "Value error", } } @@ -589,6 +631,7 @@ impl AnnotatedError for VmError { Self::ExpectedBoolean { position, .. } => *position, Self::RegisterIndexOutOfBounds { position } => *position, Self::InvalidInstruction { position, .. } => *position, + Self::SkippedRegister { position, .. } => *position, Self::StackUnderflow { position } => *position, Self::StackOverflow { position } => *position, Self::UndefinedVariable { position, .. } => *position, diff --git a/dust-lang/tests/expressions.rs b/dust-lang/tests/expressions.rs index 7516cc1..53207bc 100644 --- a/dust-lang/tests/expressions.rs +++ b/dust-lang/tests/expressions.rs @@ -14,7 +14,7 @@ fn add() { .set_c_is_constant(), Span(2, 3) ), - (Instruction::r#return(), Span(5, 5)) + (Instruction::r#return(true), Span(5, 5)) ], vec![Value::integer(1), Value::integer(2)], vec![] @@ -36,7 +36,7 @@ fn add_assign() { (Instruction::define_local(0, 0, true), Span(8, 9)), (*Instruction::add(0, 0, 1).set_c_is_constant(), Span(17, 19)), (Instruction::get_local(1, 0), Span(23, 24)), - (Instruction::r#return(), Span(24, 24)) + (Instruction::r#return(true), Span(24, 24)) ], vec![Value::integer(1), Value::integer(2)], vec![Local::new(Identifier::new("a"), None, true, 0, Some(0))] @@ -58,7 +58,7 @@ fn and() { (Instruction::test(0, false), Span(5, 7)), (Instruction::jump(1, true), Span(5, 7)), (Instruction::load_boolean(1, false, false), Span(8, 13)), - (Instruction::r#return(), Span(13, 13)), + (Instruction::r#return(true), Span(13, 13)), ], vec![], vec![] @@ -126,7 +126,7 @@ fn constant() { Ok(Chunk::with_data( vec![ (Instruction::load_constant(0, 0, false), Span(0, 2)), - (Instruction::r#return(), Span(2, 2)) + (Instruction::r#return(true), Span(2, 2)) ], vec![Value::integer(42)], vec![] @@ -169,7 +169,7 @@ fn divide() { .set_c_is_constant(), Span(2, 3) ), - (Instruction::r#return(), Span(5, 5)) + (Instruction::r#return(true), Span(5, 5)) ], vec![Value::integer(2), Value::integer(2)], vec![] @@ -194,7 +194,7 @@ fn divide_assign() { Span(17, 19) ), (Instruction::get_local(1, 0), Span(23, 24)), - (Instruction::r#return(), Span(24, 24)) + (Instruction::r#return(true), Span(24, 24)) ], vec![Value::integer(2), Value::integer(2)], vec![Local::new(Identifier::new("a"), None, true, 0, Some(0))] @@ -229,7 +229,7 @@ fn equal() { (Instruction::jump(1, true), Span(2, 4)), (Instruction::load_boolean(0, true, true), Span(2, 4)), (Instruction::load_boolean(0, false, false), Span(2, 4)), - (Instruction::r#return(), Span(6, 6)), + (Instruction::r#return(true), Span(6, 6)), ], vec![Value::integer(1), Value::integer(2)], vec![] @@ -258,7 +258,7 @@ fn equality_assignment_long() { (Instruction::load_boolean(0, false, false), Span(34, 39)), (Instruction::define_local(0, 0, false), Span(4, 5)), (Instruction::get_local(1, 0), Span(43, 44)), - (Instruction::r#return(), Span(44, 44)), + (Instruction::r#return(true), Span(44, 44)), ], vec![Value::integer(4), Value::integer(4)], vec![Local::new(Identifier::new("a"), None, false, 0, Some(0))] @@ -287,7 +287,7 @@ fn equality_assignment_short() { (Instruction::load_boolean(0, false, false), Span(10, 12)), (Instruction::define_local(0, 0, false), Span(4, 5)), (Instruction::get_local(1, 0), Span(15, 16)), - (Instruction::r#return(), Span(16, 16)), + (Instruction::r#return(true), Span(16, 16)), ], vec![Value::integer(4), Value::integer(4)], vec![Local::new(Identifier::new("a"), None, false, 0, Some(0))] @@ -314,7 +314,7 @@ fn greater() { (Instruction::jump(1, true), Span(2, 3)), (Instruction::load_boolean(0, true, true), Span(2, 3)), (Instruction::load_boolean(0, false, false), Span(2, 3)), - (Instruction::r#return(), Span(5, 5)), + (Instruction::r#return(true), Span(5, 5)), ], vec![Value::integer(1), Value::integer(2)], vec![] @@ -341,7 +341,7 @@ fn greater_than_or_equal() { (Instruction::jump(1, true), Span(2, 4)), (Instruction::load_boolean(0, true, true), Span(2, 4)), (Instruction::load_boolean(0, false, false), Span(2, 4)), - (Instruction::r#return(), Span(6, 6)), + (Instruction::r#return(true), Span(6, 6)), ], vec![Value::integer(1), Value::integer(2)], vec![] @@ -368,7 +368,7 @@ fn if_else_expression() { (Instruction::jump(1, true), Span(5, 7)), (Instruction::load_constant(0, 2, true), Span(12, 13)), (Instruction::load_constant(0, 3, false), Span(23, 24)), - (Instruction::r#return(), Span(26, 26)), + (Instruction::r#return(true), Span(26, 26)), ], vec![ Value::integer(1), @@ -399,7 +399,6 @@ fn if_expression_false() { ), (Instruction::jump(1, true), Span(5, 7)), (Instruction::load_constant(0, 2, false), Span(12, 13)), - (Instruction::r#return(), Span(15, 15)), ], vec![Value::integer(1), Value::integer(2), Value::integer(2)], vec![] @@ -425,14 +424,13 @@ fn if_expression_true() { ), (Instruction::jump(1, true), Span(5, 7)), (Instruction::load_constant(0, 2, false), Span(12, 13)), - (Instruction::r#return(), Span(15, 15)), ], vec![Value::integer(1), Value::integer(1), Value::integer(2)], vec![] )), ); - assert_eq!(run(source), Ok(Some(Value::integer(2)))); + assert_eq!(run(source), Ok(None)); } #[test] @@ -452,7 +450,7 @@ fn less_than() { (Instruction::jump(1, true), Span(2, 3)), (Instruction::load_boolean(0, true, true), Span(2, 3)), (Instruction::load_boolean(0, false, false), Span(2, 3)), - (Instruction::r#return(), Span(5, 5)), + (Instruction::r#return(true), Span(5, 5)), ], vec![Value::integer(1), Value::integer(2)], vec![] @@ -479,7 +477,7 @@ fn less_than_or_equal() { (Instruction::jump(1, true), Span(2, 4)), (Instruction::load_boolean(0, true, true), Span(2, 4)), (Instruction::load_boolean(0, false, false), Span(2, 4)), - (Instruction::r#return(), Span(6, 6)), + (Instruction::r#return(true), Span(6, 6)), ], vec![Value::integer(1), Value::integer(2)], vec![] @@ -501,7 +499,7 @@ fn list() { (Instruction::load_constant(1, 1, false), Span(4, 5)), (Instruction::load_constant(2, 2, false), Span(7, 8)), (Instruction::load_list(3, 0, 2), Span(0, 9)), - (Instruction::r#return(), Span(9, 9)), + (Instruction::r#return(true), Span(9, 9)), ], vec![Value::integer(1), Value::integer(2), Value::integer(3),], vec![] @@ -542,7 +540,7 @@ fn list_with_complex_expression() { (Instruction::subtract(3, 1, 2), Span(10, 11)), (Instruction::close(1, 3), Span(17, 18)), (Instruction::load_list(4, 0, 3), Span(0, 18)), - (Instruction::r#return(), Span(18, 18)), + (Instruction::r#return(true), Span(18, 18)), ], vec![ Value::integer(1), @@ -581,7 +579,7 @@ fn list_with_simple_expression() { ), (Instruction::load_constant(2, 3, false), Span(11, 12)), (Instruction::load_list(3, 0, 2), Span(0, 13)), - (Instruction::r#return(), Span(13, 13)), + (Instruction::r#return(true), Span(13, 13)), ], vec![ Value::integer(1), @@ -628,7 +626,7 @@ fn math_operator_precedence() { Span(14, 15) ), (Instruction::subtract(3, 0, 2), Span(6, 7)), - (Instruction::r#return(), Span(17, 17)), + (Instruction::r#return(true), Span(17, 17)), ], vec![ Value::integer(1), @@ -658,7 +656,7 @@ fn multiply() { .set_c_is_constant(), Span(2, 3) ), - (Instruction::r#return(), Span(5, 5)), + (Instruction::r#return(true), Span(5, 5)), ], vec![Value::integer(1), Value::integer(2)], vec![] @@ -683,7 +681,7 @@ fn multiply_assign() { Span(17, 19) ), (Instruction::get_local(1, 0), Span(22, 23)), - (Instruction::r#return(), Span(23, 23)) + (Instruction::r#return(true), Span(23, 23)) ], vec![Value::integer(2), Value::integer(3)], vec![Local::new(Identifier::new("a"), None, true, 0, Some(0)),] @@ -702,7 +700,7 @@ fn negate() { Ok(Chunk::with_data( vec![ (*Instruction::negate(0, 0).set_b_is_constant(), Span(0, 1)), - (Instruction::r#return(), Span(5, 5)), + (Instruction::r#return(true), Span(5, 5)), ], vec![Value::integer(42)], vec![] @@ -722,7 +720,7 @@ fn not() { vec![ (Instruction::load_boolean(0, true, false), Span(1, 5)), (Instruction::not(1, 0), Span(0, 1)), - (Instruction::r#return(), Span(5, 5)), + (Instruction::r#return(true), Span(5, 5)), ], vec![], vec![] @@ -749,7 +747,7 @@ fn not_equal() { (Instruction::jump(1, true), Span(2, 4)), (Instruction::load_boolean(0, true, true), Span(2, 4)), (Instruction::load_boolean(0, false, false), Span(2, 4)), - (Instruction::r#return(), Span(6, 6)), + (Instruction::r#return(true), Span(6, 6)), ], vec![Value::integer(1), Value::integer(2)], vec![] @@ -771,7 +769,7 @@ fn or() { (Instruction::test(0, true), Span(5, 7)), (Instruction::jump(1, true), Span(5, 7)), (Instruction::load_boolean(1, false, false), Span(8, 13)), - (Instruction::r#return(), Span(13, 13)), + (Instruction::r#return(true), Span(13, 13)), ], vec![], vec![] @@ -799,7 +797,7 @@ fn parentheses_precedence() { *Instruction::multiply(1, 0, 2).set_c_is_constant(), Span(8, 9) ), - (Instruction::r#return(), Span(11, 11)), + (Instruction::r#return(true), Span(11, 11)), ], vec![Value::integer(1), Value::integer(2), Value::integer(3)], vec![] @@ -822,7 +820,7 @@ fn set_local() { (Instruction::load_constant(1, 1, false), Span(20, 22)), (Instruction::set_local(1, 0), Span(16, 17)), (Instruction::get_local(1, 0), Span(24, 25)), - (Instruction::r#return(), Span(25, 25)), + (Instruction::r#return(true), Span(25, 25)), ], vec![Value::integer(41), Value::integer(42)], vec![Local::new(Identifier::new("x"), None, true, 0, Some(0)),] @@ -846,7 +844,7 @@ fn subtract() { .set_c_is_constant(), Span(2, 3) ), - (Instruction::r#return(), Span(5, 5)), + (Instruction::r#return(true), Span(5, 5)), ], vec![Value::integer(1), Value::integer(2)], vec![] @@ -871,7 +869,7 @@ fn subtract_assign() { Span(18, 20) ), (Instruction::get_local(1, 0), Span(24, 25)), - (Instruction::r#return(), Span(25, 25)), + (Instruction::r#return(true), Span(25, 25)), ], vec![Value::integer(42), Value::integer(2)], vec![Local::new(Identifier::new("x"), None, true, 0, Some(0)),] @@ -897,7 +895,7 @@ fn variable_and() { (Instruction::test(2, false), Span(31, 33)), (Instruction::jump(1, true), Span(31, 33)), (Instruction::get_local(3, 1), Span(34, 35)), - (Instruction::r#return(), Span(35, 35)), + (Instruction::r#return(true), Span(35, 35)), ], vec![], vec![ @@ -928,7 +926,7 @@ fn r#while() { (*Instruction::add(0, 0, 2).set_c_is_constant(), Span(39, 40)), (Instruction::jump(3, false), Span(41, 42)), (Instruction::get_local(1, 0), Span(41, 42)), - (Instruction::r#return(), Span(42, 42)), + (Instruction::r#return(true), Span(42, 42)), ], vec![Value::integer(0), Value::integer(5), Value::integer(1),], vec![Local::new(Identifier::new("x"), None, true, 0, Some(0)),]