diff --git a/dust-lang/src/chunk.rs b/dust-lang/src/chunk.rs index 461227f..89bdd27 100644 --- a/dust-lang/src/chunk.rs +++ b/dust-lang/src/chunk.rs @@ -76,11 +76,18 @@ impl Chunk { .ok_or(ChunkError::InstructionUnderflow { position }) } - pub fn get_last_instruction(&self) -> Option<&(Instruction, Span)> { - self.instructions.last() + pub fn get_last_instruction(&self) -> Result<(&Instruction, &Span), ChunkError> { + let (instruction, position) = + self.instructions + .last() + .ok_or_else(|| ChunkError::InstructionUnderflow { + position: Span(0, 0), + })?; + + Ok((instruction, position)) } - pub fn get_last_operation(&self) -> Option { + pub fn get_last_operation(&self) -> Result { self.get_last_instruction() .map(|(instruction, _)| instruction.operation()) } diff --git a/dust-lang/src/identifier.rs b/dust-lang/src/identifier.rs index f6744eb..55621c5 100644 --- a/dust-lang/src/identifier.rs +++ b/dust-lang/src/identifier.rs @@ -1,17 +1,15 @@ //! Key used to identify a value or type. //! -//! Identifiers are used to uniquely identify values and types in Dust programs. They are -//! cached to avoid duplication. This means that two identifiers with the same text are the same -//! object in memory. +//! Identifiers are used to uniquely identify values and types in Dust programs. //! //! # Examples //! ``` //! # use dust_lang::Identifier; //! let foo = Identifier::new("foo"); -//! let also_foo = Identifier::new("foo"); -//! let another_foo = Identifier::new("foo"); +//! let also_foo = foo.clone(); +//! let another_foo = also_foo.clone(); //! -//! assert_eq!(foo.strong_count(), 4); // One for each of the above and one for the cache. +//! assert_eq!(foo.strong_count(), 3); // One for each of the above. //! ``` use std::{ fmt::{self, Display, Formatter}, @@ -27,7 +25,6 @@ use serde::{de::Visitor, Deserialize, Serialize}; pub struct Identifier(Arc); impl Identifier { - /// Creates a new identifier or returns a clone of an existing one from a cache. pub fn new(text: T) -> Self { let string = text.to_string(); diff --git a/dust-lang/src/instruction.rs b/dust-lang/src/instruction.rs index 06a9972..a29d2a9 100644 --- a/dust-lang/src/instruction.rs +++ b/dust-lang/src/instruction.rs @@ -41,12 +41,12 @@ impl Instruction { instruction } - pub fn load_list(to_register: u8, start_register: u8, list_length: u8) -> Instruction { + pub fn load_list(to_register: u8, start_register: u8, end_register: u8) -> Instruction { let mut instruction = Instruction(Operation::LoadList as u32); instruction.set_a(to_register); instruction.set_b(start_register); - instruction.set_c(list_length); + instruction.set_c(end_register); instruction } @@ -371,12 +371,9 @@ impl Instruction { Operation::LoadList => { let destination = self.a(); let first_index = self.b(); - let last_index = destination.saturating_sub(1); + let last_index = self.c(); - Some(format!( - "R{} = [R{}..=R{}]", - destination, first_index, last_index - )) + Some(format!("R{destination} = [R{first_index}..=R{last_index}]",)) } Operation::DefineLocal => { let destination = self.a(); diff --git a/dust-lang/src/parser/mod.rs b/dust-lang/src/parser/mod.rs index 9b695f2..1cb927d 100644 --- a/dust-lang/src/parser/mod.rs +++ b/dust-lang/src/parser/mod.rs @@ -40,7 +40,7 @@ impl<'src> Parser<'src> { pub fn new(mut lexer: Lexer<'src>) -> Result { let (current_token, current_position) = lexer.next_token()?; - log::trace!("Starting parser with token \"{current_token}\" at {current_position}"); + log::info!("Starting parser with token \"{current_token}\" at {current_position}"); Ok(Parser { lexer, @@ -96,7 +96,7 @@ impl<'src> Parser<'src> { let (new_token, position) = self.lexer.next_token()?; - log::trace!("Parsing \"{new_token}\" at {position}"); + log::info!("Parsing \"{new_token}\" at {position}"); self.previous_token = replace(&mut self.current_token, new_token); self.previous_position = replace(&mut self.current_position, position); @@ -438,7 +438,7 @@ impl<'src> Parser<'src> { ] }; - while let Some(operation) = self.chunk.get_last_operation() { + while let Ok(operation) = self.chunk.get_last_operation() { if operation.is_math() { let (instruction, position) = self.chunk.pop_instruction(self.current_position)?; @@ -688,14 +688,13 @@ impl<'src> Parser<'src> { self.advance()?; let start_register = self.current_register; - let mut length = 0; while !self.allow(TokenKind::RightSquareBrace)? && !self.is_eof() { let next_register = self.current_register; self.parse(Precedence::Assignment)?; // Do not allow assignment - if let Some(Operation::LoadConstant) = self.chunk.get_last_operation() { + if let Operation::LoadConstant = self.chunk.get_last_operation()? { self.increment_register()?; } @@ -706,19 +705,16 @@ impl<'src> Parser<'src> { ); } - length += 1; - if !self.allow(TokenKind::Comma)? { self.expect(TokenKind::RightSquareBrace)?; - - break; } } + let end_register = self.current_register - 1; let end = self.current_position.1; self.emit_instruction( - Instruction::load_list(self.current_register, start_register, length), + Instruction::load_list(self.current_register, start_register, end_register), Span(start, end), ); self.increment_register()?; @@ -864,17 +860,11 @@ impl<'src> Parser<'src> { self.parse_expression()?; self.increment_register()?; - let (previous_instruction, previous_position) = *self - .chunk - .get_last_instruction() - .ok_or_else(|| ParseError::ExpectedExpression { - found: self.current_token.to_owned(), - position, - })?; + let (previous_instruction, previous_position) = self.chunk.get_last_instruction()?; let register = previous_instruction.a(); let local_index = self.chunk - .declare_local(identifier, is_mutable, register, previous_position)?; + .declare_local(identifier, is_mutable, register, *previous_position)?; // Optimize for assignment to a comparison // if let Operation::Jump = previous_instruction.operation() { @@ -909,8 +899,8 @@ impl<'src> Parser<'src> { let allow_return = precedence == Precedence::None; if let Some(prefix_parser) = ParseRule::from(&self.current_token.kind()).prefix { - log::trace!( - "Parsing \"{}\" as prefix at precedence {precedence}", + log::debug!( + "Prefix \"{}\" has precedence {precedence}", self.current_token, ); @@ -921,8 +911,8 @@ impl<'src> Parser<'src> { while precedence <= infix_rule.precedence { if let Some(infix_parser) = infix_rule.infix { - log::trace!( - "Parsing \"{}\" as infix at precedence {precedence}", + log::debug!( + "Infix \"{}\" has precedence {precedence}", self.current_token, ); diff --git a/dust-lang/src/parser/tests.rs b/dust-lang/src/parser/tests.rs index 8b8d62d..2370385 100644 --- a/dust-lang/src/parser/tests.rs +++ b/dust-lang/src/parser/tests.rs @@ -321,7 +321,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, 2), Span(0, 18)), + (Instruction::load_list(4, 0, 3), Span(0, 18)), (Instruction::end(true), Span(18, 18)), ], vec![ @@ -352,7 +352,7 @@ fn list_with_simple_expression() { Span(6, 7) ), (Instruction::load_constant(2, 3), Span(11, 12)), - (Instruction::load_list(3, 0, 3), Span(0, 13)), + (Instruction::load_list(3, 0, 2), Span(0, 13)), (Instruction::end(true), Span(13, 13)), ], vec![ @@ -377,7 +377,7 @@ fn list() { (Instruction::load_constant(0, 0), Span(1, 2)), (Instruction::load_constant(1, 1), Span(4, 5)), (Instruction::load_constant(2, 2), Span(7, 8)), - (Instruction::load_list(3, 0, 3), Span(0, 9)), + (Instruction::load_list(3, 0, 2), Span(0, 9)), (Instruction::end(true), Span(9, 9)), ], vec![Value::integer(1), Value::integer(2), Value::integer(3),], diff --git a/dust-lang/src/vm.rs b/dust-lang/src/vm.rs index f82be12..0101f9f 100644 --- a/dust-lang/src/vm.rs +++ b/dust-lang/src/vm.rs @@ -56,7 +56,7 @@ impl Vm { } while let Ok((instruction, position)) = self.read(Span(0, 0)).copied() { - log::trace!( + log::info!( "Running IP {} {} at {position}", self.ip - 1, instruction.operation() @@ -68,6 +68,8 @@ impl Vm { let to = instruction.a(); let value = self.take(from, position)?; + log::debug!("R{from} -{{{value}}}-> R{to}"); + self.insert(value, to, position)?; } Operation::Close => { @@ -100,13 +102,14 @@ impl Vm { Operation::LoadList => { let to_register = instruction.a(); let first_register = instruction.b(); - let length = instruction.c(); - let last_register = first_register + length + 1; - + let last_register = instruction.c(); + let length = last_register - first_register + 1; let mut list = Vec::with_capacity(length as usize); + log::debug!("R{to_register} = [R{first_register}..=R{last_register}]"); + for register_index in first_register..=last_register { - let value = match self.clone(register_index, position) { + let value = match self.take(register_index, position) { Ok(value) => value, Err(VmError::EmptyRegister { .. }) => continue, Err(error) => return Err(error), @@ -126,81 +129,91 @@ impl Vm { Operation::GetLocal => { let register_index = instruction.a(); let local_index = instruction.b(); - let local = self.chunk.get_local(local_index, position)?.clone(); - let value = self.clone_as_variable(local, position)?; + 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, + }); + }; self.insert(value, register_index, position)?; } Operation::SetLocal => { let register_index = instruction.a(); let local_index = instruction.b(); - let local = self.chunk.get_local(local_index, position)?.clone(); - let value = self.clone_as_variable(local, position)?; + 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 new_value = if instruction.b_is_constant() { self.chunk.take_constant(register_index, position)? } else { - self.clone(register_index, position)? + self.take(register_index, position)? }; value .mutate(new_value) .map_err(|error| VmError::Value { error, position })?; - self.insert(value, register_index, position)?; } Operation::Add => { let (left, right) = get_arguments(self, instruction, position)?; - - log::debug!("{left} + {right}"); - let sum = left .add(right) .map_err(|error| VmError::Value { error, position })?; + log::debug!("{left} + {right} = {sum}"); + self.insert(sum, instruction.a(), position)?; } Operation::Subtract => { let (left, right) = get_arguments(self, instruction, position)?; - - log::debug!("{left} - {right}"); - let difference = left .subtract(right) .map_err(|error| VmError::Value { error, position })?; + log::debug!("{left} - {right} = {difference}"); + self.insert(difference, instruction.a(), position)?; } Operation::Multiply => { let (left, right) = get_arguments(self, instruction, position)?; - log::debug!("{left} * {right}"); - let product = left .multiply(right) .map_err(|error| VmError::Value { error, position })?; + log::debug!("{position} {left} * {right}"); + self.insert(product, instruction.a(), position)?; } Operation::Divide => { let (left, right) = get_arguments(self, instruction, position)?; - - log::debug!("{left} / {right}"); - let quotient = left .divide(right) .map_err(|error| VmError::Value { error, position })?; + log::debug!("{left} / {right} = {quotient}"); + self.insert(quotient, instruction.a(), position)?; } Operation::Modulo => { let (left, right) = get_arguments(self, instruction, position)?; - - log::debug!("{left} % {right}"); - let remainder = left .modulo(right) .map_err(|error| VmError::Value { error, position })?; + log::debug!("{left} % {right} = {remainder}"); + self.insert(remainder, instruction.a(), position)?; } Operation::Test => { @@ -238,6 +251,7 @@ impl Vm { debug_assert_eq!(jump.operation(), Operation::Jump); let (left, right) = get_arguments(self, instruction, position)?; + let boolean = left .equal(right) .map_err(|error| VmError::Value { error, position })? @@ -248,6 +262,8 @@ impl Vm { })?; let compare_to = instruction.a_as_boolean(); + log::debug!("{left} == {right} = {boolean} == {compare_to}"); + if boolean == compare_to { self.ip += 1; } else { @@ -263,11 +279,12 @@ impl Vm { } } Operation::Less => { - let (jump, _) = *self.chunk.get_instruction(self.ip, position)?; + let jump = self.chunk.get_instruction(self.ip, position)?.0; debug_assert_eq!(jump.operation(), Operation::Jump); let (left, right) = get_arguments(self, instruction, position)?; + let boolean = left .less_than(right) .map_err(|error| VmError::Value { error, position })? @@ -278,6 +295,8 @@ impl Vm { })?; let compare_to = instruction.a_as_boolean(); + log::debug!("{left} < {right} = {boolean} == {compare_to}"); + if boolean == compare_to { self.ip += 1; } else { @@ -293,7 +312,7 @@ impl Vm { } } Operation::LessEqual => { - let (jump, _) = *self.chunk.get_instruction(self.ip, position)?; + let jump = self.chunk.get_instruction(self.ip, position)?.0; debug_assert_eq!(jump.operation(), Operation::Jump); @@ -308,6 +327,8 @@ impl Vm { })?; let compare_to = instruction.a_as_boolean(); + log::debug!("{left} <= {right} = {boolean} == {compare_to}"); + if boolean == compare_to { self.ip += 1; } else { @@ -332,6 +353,8 @@ impl Vm { .negate() .map_err(|error| VmError::Value { error, position })?; + log::debug!("-({value}) = {negated}"); + self.insert(negated, instruction.a(), position)?; } Operation::Not => { @@ -344,6 +367,8 @@ impl Vm { .not() .map_err(|error| VmError::Value { error, position })?; + log::debug!("!{value} = {not}"); + self.insert(not, instruction.a(), position)?; } Operation::Jump => { diff --git a/dust-shell/src/main.rs b/dust-shell/src/main.rs index 5ea02cc..1618fba 100644 --- a/dust-shell/src/main.rs +++ b/dust-shell/src/main.rs @@ -24,21 +24,21 @@ fn main() { .parse_env("DUST_LOG") .format(|buf, record| { let level = match record.level() { - Level::Error => "ERROR".red(), - Level::Warn => "WARN".yellow(), - Level::Info => "INFO".white(), - Level::Debug => "DEBUG".blue(), - Level::Trace => "TRACE".purple(), + Level::Info => "INFO".white().bold(), + Level::Debug => "DEBUG".blue().bold(), + Level::Warn => "WARN".yellow().bold(), + Level::Error => "ERROR".red().bold(), + Level::Trace => "TRACE".purple().bold(), } .bold(); - let level_display = format!("[{level:^5}]").white().on_black(); + let level_display = format!("{level:<5}"); let module = record .module_path() - .map(|path| path.split("::").last().unwrap_or("unknown")) + .map(|path| path.split("::").last().unwrap_or(path)) .unwrap_or("unknown") .dimmed(); - writeln!(buf, "{level_display} {module:^6} {}", record.args()) + writeln!(buf, "{level_display:^10} {module:^6} {}", record.args()) }) .init();