From a2e7a4e73e81d96b92cc9cd56b22ac7fc6721cff Mon Sep 17 00:00:00 2001 From: Jeff Date: Mon, 4 Nov 2024 15:38:58 -0500 Subject: [PATCH] Begin removing chunk errors; Use constants for identifiers --- dust-lang/src/chunk.rs | 259 ++++++----------------------- dust-lang/src/identifier.rs | 115 ------------- dust-lang/src/lib.rs | 4 +- dust-lang/src/native_function.rs | 8 +- dust-lang/src/parser.rs | 276 ++++++++++++++++++------------- dust-lang/src/type.rs | 80 ++------- dust-lang/src/value.rs | 10 +- dust-lang/src/vm.rs | 226 +++++++++++++++++-------- dust-lang/tests/control_flow.rs | 6 +- dust-lang/tests/expressions.rs | 18 +- dust-lang/tests/functions.rs | 34 ++-- dust-lang/tests/scopes.rs | 10 +- 12 files changed, 429 insertions(+), 617 deletions(-) delete mode 100644 dust-lang/src/identifier.rs diff --git a/dust-lang/src/chunk.rs b/dust-lang/src/chunk.rs index 3575b54..7d111ea 100644 --- a/dust-lang/src/chunk.rs +++ b/dust-lang/src/chunk.rs @@ -6,11 +6,11 @@ use std::{ use colored::Colorize; use serde::{Deserialize, Serialize}; -use crate::{AnnotatedError, Identifier, Instruction, Span, Type, Value}; +use crate::{Instruction, Span, Type, Value}; #[derive(Clone, PartialOrd, Ord, Serialize, Deserialize)] pub struct Chunk { - name: Option, + name: Option, instructions: Vec<(Instruction, Span)>, constants: Vec, locals: Vec, @@ -18,7 +18,7 @@ pub struct Chunk { } impl Chunk { - pub fn new(name: Option) -> Self { + pub fn new(name: Option) -> Self { Self { name, instructions: Vec::new(), @@ -29,7 +29,7 @@ impl Chunk { } pub fn with_data( - name: Option, + name: Option, instructions: Vec<(Instruction, Span)>, constants: Vec, locals: Vec, @@ -43,11 +43,11 @@ impl Chunk { } } - pub fn name(&self) -> Option<&Identifier> { + pub fn name(&self) -> Option<&String> { self.name.as_ref() } - pub fn set_name(&mut self, name: Identifier) { + pub fn set_name(&mut self, name: String) { self.name = Some(name); } @@ -59,7 +59,19 @@ impl Chunk { self.instructions.is_empty() } - pub fn instructions(&self) -> &[(Instruction, Span)] { + pub fn constants(&self) -> &Vec { + &self.constants + } + + pub fn constants_mut(&mut self) -> &mut Vec { + &mut self.constants + } + + pub fn take_constants(self) -> Vec { + self.constants + } + + pub fn instructions(&self) -> &Vec<(Instruction, Span)> { &self.instructions } @@ -67,151 +79,42 @@ impl Chunk { &mut self.instructions } - pub fn get_instruction( - &self, - offset: usize, - position: Span, - ) -> Result<&(Instruction, Span), ChunkError> { - self.instructions - .get(offset) - .ok_or(ChunkError::InstructionIndexOfBounds { offset, position }) + pub fn locals(&self) -> &Vec { + &self.locals } - pub fn push_instruction(&mut self, instruction: Instruction, position: Span) { - self.instructions.push((instruction, position)); + pub fn locals_mut(&mut self) -> &mut Vec { + &mut self.locals } - pub fn insert_instruction( - &mut self, - index: usize, - instruction: Instruction, - position: Span, - ) -> Result<(), ChunkError> { - if index > self.instructions.len() { - Err(ChunkError::InstructionIndexOfBounds { - offset: index, - position, - }) - } else { - self.instructions.insert(index, (instruction, position)); - - Ok(()) - } + pub fn scope_depth(&self) -> usize { + self.scope_depth } - pub fn take_constants(self) -> Vec { - self.constants + pub fn get_constant(&self, index: u8) -> Option<&Value> { + self.constants.get(index as usize) } - pub fn get_constant(&self, index: u8, position: Span) -> Result<&Value, ChunkError> { - let index = index as usize; - - self.constants - .get(index) - .ok_or(ChunkError::ConstantIndexOutOfBounds { index, position }) - } - - pub fn push_or_get_constant(&mut self, value: Value, position: Span) -> Result { + pub fn push_or_get_constant(&mut self, value: Value) -> u8 { if let Some(index) = self .constants .iter() .position(|constant| constant == &value) { - return Ok(index as u8); + return index as u8; } - let starting_length = self.constants.len(); + self.constants.push(value); - if starting_length + 1 > (u8::MAX as usize) { - Err(ChunkError::ConstantOverflow { position }) - } else { - self.constants.push(value); - - Ok(starting_length as u8) - } + (self.constants.len() - 1) as u8 } - pub fn locals(&self) -> &[Local] { - &self.locals - } - - pub fn get_local(&self, index: u8, position: Span) -> Result<&Local, ChunkError> { - let index = index as usize; - - self.locals - .get(index) - .ok_or(ChunkError::LocalIndexOutOfBounds { index, position }) - } - - pub fn get_identifier(&self, index: u8) -> Option<&Identifier> { - self.locals - .get(index as usize) - .map(|local| &local.identifier) - } - - pub fn get_local_index(&self, identifier_text: &str, position: Span) -> Result { - self.locals - .iter() - .enumerate() - .rev() - .find_map(|(index, local)| { - if local.identifier.as_str() == identifier_text { - Some(index as u8) - } else { - None - } - }) - .ok_or(ChunkError::IdentifierNotFound { - identifier: Identifier::new(identifier_text), - position, - }) - } - - pub fn declare_local( - &mut self, - identifier: Identifier, - r#type: Option, - is_mutable: bool, - register_index: u8, - position: Span, - ) -> Result { - log::debug!("Declare local {identifier}"); - - let starting_length = self.locals.len(); - - if starting_length + 1 > (u8::MAX as usize) { - Err(ChunkError::LocalOverflow { position }) - } else { - self.locals.push(Local::new( - identifier, - r#type, - is_mutable, - self.scope_depth, - register_index, - )); - - Ok(starting_length as u8) - } - } - - pub fn define_local( - &mut self, - local_index: u8, - register_index: u8, - position: Span, - ) -> Result<(), ChunkError> { - let local = self.locals.get_mut(local_index as usize).ok_or_else(|| { - ChunkError::LocalIndexOutOfBounds { - index: local_index as usize, - position, - } - })?; - - log::debug!("Define local {}", local.identifier); - - local.register_index = register_index; - - Ok(()) + pub fn get_identifier(&self, local_index: u8) -> Option { + self.locals.get(local_index as usize).and_then(|local| { + self.constants + .get(local.identifier_index as usize) + .map(|value| value.to_string()) + }) } pub fn begin_scope(&mut self) { @@ -257,7 +160,7 @@ impl PartialEq for Chunk { #[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] pub struct Local { - pub identifier: Identifier, + pub identifier_index: u8, pub r#type: Option, pub is_mutable: bool, pub depth: usize, @@ -266,14 +169,14 @@ pub struct Local { impl Local { pub fn new( - identifier: Identifier, + identifier_index: u8, r#type: Option, mutable: bool, depth: usize, register_index: u8, ) -> Self { Self { - identifier, + identifier_index, r#type, is_mutable: mutable, depth, @@ -524,7 +427,7 @@ impl<'a> ChunkDisassembler<'a> { for ( index, Local { - identifier, + identifier_index, r#type, depth, register_index, @@ -532,7 +435,12 @@ impl<'a> ChunkDisassembler<'a> { }, ) in self.chunk.locals.iter().enumerate() { - let identifier_display = identifier.as_str(); + let identifier_display = self + .chunk + .constants + .get(*identifier_index as usize) + .map(|value| value.to_string()) + .unwrap_or_else(|| "unknown".to_string()); let type_display = r#type .as_ref() .map(|r#type| r#type.to_string()) @@ -577,76 +485,3 @@ impl<'a> ChunkDisassembler<'a> { disassembly } } - -#[derive(Debug, Clone, PartialEq)] -pub enum ChunkError { - InstructionIndexOfBounds { - offset: usize, - position: Span, - }, - ConstantOverflow { - position: Span, - }, - ConstantIndexOutOfBounds { - index: usize, - position: Span, - }, - LocalIndexOutOfBounds { - index: usize, - position: Span, - }, - LocalOverflow { - position: Span, - }, - IdentifierNotFound { - identifier: Identifier, - position: Span, - }, -} - -impl AnnotatedError for ChunkError { - fn title() -> &'static str { - "Chunk Error" - } - - fn description(&self) -> &'static str { - match self { - ChunkError::InstructionIndexOfBounds { .. } => "Instruction index out of bounds", - ChunkError::ConstantOverflow { .. } => "Constant overflow", - ChunkError::ConstantIndexOutOfBounds { .. } => "Constant index out of bounds", - ChunkError::LocalIndexOutOfBounds { .. } => "Local index out of bounds", - ChunkError::LocalOverflow { .. } => "Local overflow", - ChunkError::IdentifierNotFound { .. } => "Identifier not found", - } - } - - fn details(&self) -> Option { - match self { - ChunkError::InstructionIndexOfBounds { offset, .. } => { - Some(format!("Instruction index: {}", offset)) - } - ChunkError::ConstantIndexOutOfBounds { index, .. } => { - Some(format!("Constant index: {}", index)) - } - ChunkError::LocalIndexOutOfBounds { index, .. } => { - Some(format!("Local index: {}", index)) - } - ChunkError::IdentifierNotFound { identifier, .. } => { - Some(format!("Identifier: {}", identifier)) - } - ChunkError::LocalOverflow { .. } => None, - ChunkError::ConstantOverflow { .. } => None, - } - } - - fn position(&self) -> Span { - match self { - ChunkError::InstructionIndexOfBounds { position, .. } => *position, - ChunkError::ConstantIndexOutOfBounds { position, .. } => *position, - ChunkError::IdentifierNotFound { position, .. } => *position, - ChunkError::LocalIndexOutOfBounds { position, .. } => *position, - ChunkError::LocalOverflow { position, .. } => *position, - ChunkError::ConstantOverflow { position, .. } => *position, - } - } -} diff --git a/dust-lang/src/identifier.rs b/dust-lang/src/identifier.rs deleted file mode 100644 index 55621c5..0000000 --- a/dust-lang/src/identifier.rs +++ /dev/null @@ -1,115 +0,0 @@ -//! Key used to identify a value or type. -//! -//! 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 = foo.clone(); -//! let another_foo = also_foo.clone(); -//! -//! assert_eq!(foo.strong_count(), 3); // One for each of the above. -//! ``` -use std::{ - fmt::{self, Display, Formatter}, - sync::Arc, -}; - -use serde::{de::Visitor, Deserialize, Serialize}; - -/// Key used to identify a value or type. -/// -/// See the [module-level documentation](index.html) for more information. -#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)] -pub struct Identifier(Arc); - -impl Identifier { - pub fn new(text: T) -> Self { - let string = text.to_string(); - - Identifier(Arc::new(string.clone())) - } - - pub fn as_str(&self) -> &str { - self.0.as_str() - } - - pub fn strong_count(&self) -> usize { - Arc::strong_count(&self.0) - } -} - -impl From for Identifier { - fn from(string: String) -> Self { - Identifier::new(string) - } -} - -impl From<&str> for Identifier { - fn from(slice: &str) -> Self { - Identifier::new(slice) - } -} - -impl Display for Identifier { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "{}", self.0) - } -} - -impl Serialize for Identifier { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - serializer.serialize_str(self.0.as_str()) - } -} - -impl<'de> Deserialize<'de> for Identifier { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - deserializer.deserialize_identifier(IdentifierVisitor) - } -} - -struct IdentifierVisitor; - -impl<'de> Visitor<'de> for IdentifierVisitor { - type Value = Identifier; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a UTF-8 string") - } - - fn visit_char(self, v: char) -> Result - where - E: serde::de::Error, - { - self.visit_str(v.encode_utf8(&mut [0u8; 4])) - } - - fn visit_str(self, v: &str) -> Result - where - E: serde::de::Error, - { - Ok(Identifier::new(v)) - } - - fn visit_borrowed_str(self, v: &'de str) -> Result - where - E: serde::de::Error, - { - self.visit_str(v) - } - - fn visit_string(self, v: String) -> Result - where - E: serde::de::Error, - { - self.visit_str(&v) - } -} diff --git a/dust-lang/src/lib.rs b/dust-lang/src/lib.rs index 40ed6f1..4f68d6b 100644 --- a/dust-lang/src/lib.rs +++ b/dust-lang/src/lib.rs @@ -1,7 +1,6 @@ mod chunk; mod dust_error; mod formatter; -mod identifier; mod instruction; mod lexer; mod native_function; @@ -12,10 +11,9 @@ mod r#type; mod value; mod vm; -pub use chunk::{Chunk, ChunkDisassembler, ChunkError, Local}; +pub use chunk::{Chunk, ChunkDisassembler, Local}; pub use dust_error::{AnnotatedError, DustError}; pub use formatter::{format, Formatter}; -pub use identifier::Identifier; pub use instruction::Instruction; pub use lexer::{lex, LexError, Lexer}; pub use native_function::{NativeFunction, NativeFunctionError}; diff --git a/dust-lang/src/native_function.rs b/dust-lang/src/native_function.rs index 701f479..7a9bcd8 100644 --- a/dust-lang/src/native_function.rs +++ b/dust-lang/src/native_function.rs @@ -174,7 +174,7 @@ impl NativeFunction { message.push(' '); } - let argument = vm.get(argument_index, position)?; + let argument = vm.get_register(argument_index, position)?; message.push_str(&argument.to_string()); } @@ -197,7 +197,7 @@ impl NativeFunction { let mut string = String::new(); for argument_index in 0..argument_count { - let argument = vm.get(argument_index, position)?; + let argument = vm.get_register(argument_index, position)?; string.push_str(&argument.to_string()); } @@ -238,7 +238,7 @@ impl NativeFunction { stdout.write(b" ").map_err(map_err)?; } - let argument_string = vm.get(argument_index, position)?.to_string(); + let argument_string = vm.get_register(argument_index, position)?.to_string(); stdout .write_all(argument_string.as_bytes()) @@ -264,7 +264,7 @@ impl NativeFunction { stdout.write(b" ").map_err(map_err)?; } - let argument_string = vm.get(argument_index, position)?.to_string(); + let argument_string = vm.get_register(argument_index, position)?.to_string(); stdout .write_all(argument_string.as_bytes()) diff --git a/dust-lang/src/parser.rs b/dust-lang/src/parser.rs index c9c55bf..6bf7aab 100644 --- a/dust-lang/src/parser.rs +++ b/dust-lang/src/parser.rs @@ -9,8 +9,8 @@ use colored::Colorize; use serde::{Deserialize, Serialize}; use crate::{ - AnnotatedError, Chunk, ChunkError, DustError, FunctionType, Identifier, Instruction, LexError, - Lexer, NativeFunction, Operation, Span, Token, TokenKind, TokenOwned, Type, Value, + AnnotatedError, Chunk, DustError, FunctionType, Instruction, LexError, Lexer, Local, + NativeFunction, Operation, Span, Token, TokenKind, TokenOwned, Type, Value, }; pub fn parse(source: &str) -> Result { @@ -107,6 +107,67 @@ impl<'src> Parser<'src> { Ok(()) } + fn get_local(&self, index: u8) -> Result<&Local, ParseError> { + let index = index as usize; + + self.chunk + .locals() + .get(index) + .ok_or(ParseError::LocalIndexOutOfBounds { + index, + position: self.current_position, + }) + } + + fn get_local_index(&self, identifier_text: &str) -> Result { + self.chunk + .locals() + .iter() + .enumerate() + .rev() + .find_map(|(index, local)| { + let identifier = self + .chunk + .constants() + .get(local.identifier_index as usize)? + .as_string()?; + + if identifier == identifier_text { + Some(index as u8) + } else { + None + } + }) + .ok_or(ParseError::UndeclaredVariable { + identifier: identifier_text.to_string(), + position: self.current_position, + }) + } + + pub fn declare_local( + &mut self, + identifier: &str, + r#type: Option, + is_mutable: bool, + register_index: u8, + ) -> (u8, u8) { + log::debug!("Declare local {identifier}"); + + let scope_depth = self.chunk.scope_depth(); + let identifier = Value::string(identifier); + let identifier_index = self.chunk.push_or_get_constant(identifier); + + self.chunk.locals_mut().push(Local::new( + identifier_index, + r#type, + is_mutable, + scope_depth, + register_index, + )); + + (self.chunk.locals().len() as u8 - 1, identifier_index) + } + fn allow(&mut self, allowed: Token) -> Result { if self.current_token == allowed { self.advance()?; @@ -138,7 +199,7 @@ impl<'src> Parser<'src> { self.current_statement_length += 1; - self.chunk.push_instruction(instruction, position); + self.chunk.instructions_mut().push((instruction, position)); } fn optimize_statement(&mut self) { @@ -236,7 +297,7 @@ impl<'src> Parser<'src> { } fn emit_constant(&mut self, value: Value, position: Span) -> Result<(), ParseError> { - let constant_index = self.chunk.push_or_get_constant(value, position)?; + let constant_index = self.chunk.push_or_get_constant(value); let register = self.next_register(); self.emit_instruction( @@ -476,7 +537,7 @@ impl<'src> Parser<'src> { let argument = match instruction.operation() { Operation::GetLocal => { let local_index = instruction.b(); - let local = self.chunk.get_local(local_index, self.current_position)?; + let local = self.get_local(local_index)?; is_mutable_local = local.is_mutable; local.register_index @@ -754,25 +815,17 @@ impl<'src> Parser<'src> { } fn parse_variable(&mut self, allowed: Allowed) -> Result<(), ParseError> { - let token = self.current_token; let start_position = self.current_position; - let local_index = if let Token::Identifier(text) = token { - if let Ok(local_index) = self.chunk.get_local_index(text, start_position) { + let local_index = if let Token::Identifier(text) = self.current_token { + if let Ok(local_index) = self.get_local_index(text) { local_index } else if let Some(name) = self.chunk.name() { if name.as_str() == text { let register = self.next_register(); self.emit_instruction(Instruction::load_self(register), start_position); - - self.chunk.declare_local( - Identifier::new(text), - None, - false, - register, - start_position, - )?; + self.declare_local(text, None, false, register); self.current_is_expression = true; @@ -783,7 +836,7 @@ impl<'src> Parser<'src> { self.parse_native_call(allowed) } else { Err(ParseError::UndeclaredVariable { - identifier: Identifier::new(text), + identifier: text.to_string(), position: start_position, }) }; @@ -792,7 +845,7 @@ impl<'src> Parser<'src> { self.parse_native_call(allowed) } else { Err(ParseError::UndeclaredVariable { - identifier: Identifier::new(text), + identifier: text.to_string(), position: start_position, }) }; @@ -807,12 +860,9 @@ impl<'src> Parser<'src> { self.advance()?; - let is_mutable = self - .chunk - .get_local(local_index, start_position)? - .is_mutable; - if self.allow(Token::Equal)? { + let is_mutable = self.get_local(local_index)?.is_mutable; + if !allowed.assignment { return Err(ParseError::InvalidAssignmentTarget { found: self.current_token.to_owned(), @@ -822,7 +872,7 @@ impl<'src> Parser<'src> { if !is_mutable { return Err(ParseError::CannotMutateImmutableVariable { - identifier: self.chunk.get_identifier(local_index).cloned().unwrap(), + identifier: self.chunk.get_identifier(local_index).unwrap(), position: start_position, }); } @@ -838,10 +888,7 @@ impl<'src> Parser<'src> { })?; if previous_instruction.operation().is_math() { - let register_index = self - .chunk - .get_local(local_index, start_position)? - .register_index; + let register_index = self.get_local(local_index)?.register_index; log::trace!("Condensing SET_LOCAL to binary math expression"); @@ -1019,18 +1066,22 @@ impl<'src> Parser<'src> { if let Some(skippable) = self.get_last_jumpable_mut() { skippable.set_c_to_boolean(true); } else { - self.chunk.insert_instruction( + self.chunk.instructions_mut().insert( else_start, - Instruction::jump(jump_distance, true), - self.current_position, - )?; + ( + Instruction::jump(jump_distance, true), + self.current_position, + ), + ); } } else { - self.chunk.insert_instruction( + self.chunk.instructions_mut().insert( else_start, - Instruction::jump(jump_distance, true), - self.current_position, - )?; + ( + Instruction::jump(jump_distance, true), + self.current_position, + ), + ); } } else { self.current_is_expression = false; @@ -1054,11 +1105,13 @@ impl<'src> Parser<'src> { if else_end - if_block_end > 1 { let jump_distance = (else_end - if_block_end) as u8; - self.chunk.insert_instruction( + self.chunk.instructions_mut().insert( if_block_end, - Instruction::jump(jump_distance, true), - self.current_position, - )?; + ( + Instruction::jump(jump_distance, true), + self.current_position, + ), + ); } } else { return Err(ParseError::ExpectedTokenMultiple { @@ -1096,11 +1149,13 @@ impl<'src> Parser<'src> { let block_end = self.chunk.len() as u8; - self.chunk.insert_instruction( + self.chunk.instructions_mut().insert( block_start, - Instruction::jump(block_end - block_start as u8 + 1, true), - self.current_position, - )?; + ( + Instruction::jump(block_end - block_start as u8 + 1, true), + self.current_position, + ), + ); let jump_back_distance = block_end - expression_start + 1; let jump_back = Instruction::jump(jump_back_distance, false); @@ -1267,7 +1322,7 @@ impl<'src> Parser<'src> { let identifier = if let Token::Identifier(text) = self.current_token { self.advance()?; - Identifier::new(text) + text } else { return Err(ParseError::ExpectedToken { expected: TokenKind::Identifier, @@ -1289,9 +1344,7 @@ impl<'src> Parser<'src> { self.expect(Token::Equal)?; self.parse_expression()?; - let local_index = self - .chunk - .declare_local(identifier, r#type, is_mutable, register, position)?; + let (local_index, _) = self.declare_local(identifier, r#type, is_mutable, register); let register = self.next_register().saturating_sub(1); self.emit_instruction( @@ -1310,27 +1363,25 @@ impl<'src> Parser<'src> { let mut function_parser = Parser::new(self.lexer)?; let identifier = if let Token::Identifier(text) = function_parser.current_token { let position = function_parser.current_position; - let identifier = Identifier::new(text); function_parser.advance()?; - function_parser.chunk.set_name(identifier.clone()); + function_parser.chunk.set_name(text.to_string()); - Some((identifier, position)) + Some((text, position)) } else { None }; function_parser.expect(Token::LeftParenthesis)?; - let mut value_parameters: Option> = None; + let mut value_parameters: Option> = None; while function_parser.current_token != Token::RightParenthesis { - let start = function_parser.current_position.0; let is_mutable = function_parser.allow(Token::Mut)?; let parameter = if let Token::Identifier(text) = function_parser.current_token { function_parser.advance()?; - Identifier::new(text) + text } else { return Err(ParseError::ExpectedToken { expected: TokenKind::Identifier, @@ -1348,26 +1399,22 @@ impl<'src> Parser<'src> { function_parser.advance()?; - let end = function_parser.current_position.1; - - if let Some(value_parameters) = value_parameters.as_mut() { - value_parameters.push((parameter.clone(), r#type.clone())); - } else { - value_parameters = Some(vec![(parameter.clone(), r#type.clone())]); - }; - let register = value_parameters .as_ref() .map(|values| values.len() as u8 - 1) .unwrap_or(0); - - function_parser.chunk.declare_local( + let (_, identifier_index) = function_parser.declare_local( parameter, - Some(r#type), + Some(r#type.clone()), is_mutable, register, - Span(start, end), - )?; + ); + + if let Some(value_parameters) = value_parameters.as_mut() { + value_parameters.push((identifier_index, r#type)); + } else { + value_parameters = Some(vec![(identifier_index, r#type)]); + }; function_parser.minimum_register += 1; @@ -1409,13 +1456,12 @@ impl<'src> Parser<'src> { if let Some((identifier, identifier_position)) = identifier { let register = self.next_register(); - let local_index = self.chunk.declare_local( + let (local_index, _) = self.declare_local( identifier, Some(Type::Function(function_type)), false, register, - Span(function_start, function_end), - )?; + ); self.emit_constant(function, Span(function_start, function_end))?; self.emit_instruction( @@ -1867,17 +1913,7 @@ impl From<&Token<'_>> for ParseRule<'_> { #[derive(Debug, PartialEq)] pub enum ParseError { - CannotChainComparison { - position: Span, - }, - CannotMutateImmutableVariable { - identifier: Identifier, - position: Span, - }, - ExpectedExpression { - found: TokenOwned, - position: Span, - }, + // Token errors ExpectedToken { expected: TokenKind, found: TokenOwned, @@ -1888,19 +1924,42 @@ pub enum ParseError { found: TokenOwned, position: Span, }, + + // Expression errors + CannotChainComparison { + position: Span, + }, + ExpectedExpression { + found: TokenOwned, + position: Span, + }, + + // Variable errors + CannotMutateImmutableVariable { + identifier: String, + position: Span, + }, ExpectedMutableVariable { found: TokenOwned, position: Span, }, + UndeclaredVariable { + identifier: String, + position: Span, + }, + + // Statement errors InvalidAssignmentTarget { found: TokenOwned, position: Span, }, - UndeclaredVariable { - identifier: Identifier, + UnexpectedReturn { position: Span, }, - UnexpectedReturn { + + // Chunk errors + LocalIndexOutOfBounds { + index: usize, position: Span, }, RegisterOverflow { @@ -1911,7 +1970,6 @@ pub enum ParseError { }, // Wrappers around foreign errors - Chunk(ChunkError), Lex(LexError), ParseFloatError { error: ParseFloatError, @@ -1923,12 +1981,6 @@ pub enum ParseError { }, } -impl From for ParseError { - fn from(error: ChunkError) -> Self { - Self::Chunk(error) - } -} - impl AnnotatedError for ParseError { fn title() -> &'static str { "Parse Error" @@ -1939,18 +1991,18 @@ impl AnnotatedError for ParseError { Self::CannotChainComparison { .. } => "Cannot chain comparison", Self::CannotMutateImmutableVariable { .. } => "Cannot mutate immutable variable", Self::ExpectedExpression { .. } => "Expected an expression", + Self::ExpectedMutableVariable { .. } => "Expected a mutable variable", Self::ExpectedToken { .. } => "Expected a specific token", Self::ExpectedTokenMultiple { .. } => "Expected one of multiple tokens", - Self::ExpectedMutableVariable { .. } => "Expected a mutable variable", Self::InvalidAssignmentTarget { .. } => "Invalid assignment target", - Self::UndeclaredVariable { .. } => "Undeclared variable", - Self::UnexpectedReturn { .. } => "Unexpected return", - Self::RegisterOverflow { .. } => "Register overflow", - Self::RegisterUnderflow { .. } => "Register underflow", + Self::Lex(error) => error.description(), + Self::LocalIndexOutOfBounds { .. } => "Local index out of bounds", Self::ParseFloatError { .. } => "Failed to parse float", Self::ParseIntError { .. } => "Failed to parse integer", - Self::Chunk(error) => error.description(), - Self::Lex(error) => error.description(), + Self::RegisterOverflow { .. } => "Register overflow", + Self::RegisterUnderflow { .. } => "Register underflow", + Self::UndeclaredVariable { .. } => "Undeclared variable", + Self::UnexpectedReturn { .. } => "Unexpected return", } } @@ -1993,16 +2045,18 @@ impl AnnotatedError for ParseError { Self::InvalidAssignmentTarget { found, .. } => { Some(format!("Invalid assignment target, found {found}")) } + Self::Lex(error) => error.details(), + Self::LocalIndexOutOfBounds { index, .. } => { + Some(format!("Local index {index} out of bounds")) + } + Self::ParseFloatError { error, .. } => Some(error.to_string()), + Self::ParseIntError { error, .. } => Some(error.to_string()), + Self::RegisterOverflow { .. } => None, + Self::RegisterUnderflow { .. } => None, Self::UndeclaredVariable { identifier, .. } => { Some(format!("Undeclared variable {identifier}")) } Self::UnexpectedReturn { .. } => None, - Self::RegisterOverflow { .. } => None, - Self::RegisterUnderflow { .. } => None, - Self::ParseFloatError { error, .. } => Some(error.to_string()), - Self::ParseIntError { error, .. } => Some(error.to_string()), - Self::Chunk(error) => error.details(), - Self::Lex(error) => error.details(), } } @@ -2011,18 +2065,18 @@ impl AnnotatedError for ParseError { Self::CannotChainComparison { position } => *position, Self::CannotMutateImmutableVariable { position, .. } => *position, Self::ExpectedExpression { position, .. } => *position, + Self::ExpectedMutableVariable { position, .. } => *position, Self::ExpectedToken { position, .. } => *position, Self::ExpectedTokenMultiple { position, .. } => *position, - Self::ExpectedMutableVariable { position, .. } => *position, Self::InvalidAssignmentTarget { position, .. } => *position, - Self::UndeclaredVariable { position, .. } => *position, - Self::UnexpectedReturn { position } => *position, - Self::RegisterOverflow { position } => *position, - Self::RegisterUnderflow { position } => *position, + Self::Lex(error) => error.position(), + Self::LocalIndexOutOfBounds { position, .. } => *position, Self::ParseFloatError { position, .. } => *position, Self::ParseIntError { position, .. } => *position, - Self::Chunk(error) => error.position(), - Self::Lex(error) => error.position(), + Self::RegisterOverflow { position } => *position, + Self::RegisterUnderflow { position } => *position, + Self::UndeclaredVariable { position, .. } => *position, + Self::UnexpectedReturn { position } => *position, } } } diff --git a/dust-lang/src/type.rs b/dust-lang/src/type.rs index c7e49b5..4223372 100644 --- a/dust-lang/src/type.rs +++ b/dust-lang/src/type.rs @@ -17,8 +17,6 @@ use std::{ use serde::{Deserialize, Serialize}; -use crate::Identifier; - /// Description of a kind of value. /// /// See the [module documentation](index.html) for more information. @@ -32,7 +30,7 @@ pub enum Type { Float, Function(FunctionType), Generic { - identifier: Identifier, + identifier_index: u8, concrete_type: Option>, }, Integer, @@ -45,7 +43,7 @@ pub enum Type { item_type: Box, }, Map { - pairs: HashMap, + pairs: HashMap, }, Number, Range { @@ -230,47 +228,6 @@ impl Type { expected: self.clone(), }) } - - pub fn has_field(&self, field: &Identifier) -> bool { - match field.as_str() { - "to_string" => true, - "length" => { - matches!( - self, - Type::List { .. } - | Type::ListOf { .. } - | Type::ListEmpty - | Type::Map { .. } - | Type::String { .. } - ) - } - "is_even" | "is_odd" => matches!(self, Type::Integer | Type::Float), - _ => match self { - Type::Struct(StructType::Fields { fields, .. }) => fields.contains_key(field), - Type::Map { pairs } => pairs.contains_key(field), - _ => false, - }, - } - } - - pub fn get_field_type(&self, field: &Identifier) -> Option { - match field.as_str() { - "length" => match self { - Type::List { .. } => Some(Type::Integer), - Type::ListOf { .. } => Some(Type::Integer), - Type::ListEmpty => Some(Type::Integer), - Type::Map { .. } => Some(Type::Integer), - Type::String { .. } => Some(Type::Integer), - _ => None, - }, - "is_even" | "is_odd" => Some(Type::Boolean), - _ => match self { - Type::Struct(StructType::Fields { fields, .. }) => fields.get(field).cloned(), - Type::Map { pairs } => pairs.get(field).cloned(), - _ => None, - }, - } - } } impl Display for Type { @@ -285,7 +242,10 @@ impl Display for Type { Type::Function(function_type) => write!(f, "{function_type}"), Type::Generic { concrete_type, .. } => { match concrete_type.clone().map(|r#box| *r#box) { - Some(Type::Generic { identifier, .. }) => write!(f, "{identifier}"), + Some(Type::Generic { + identifier_index: identifier, + .. + }) => write!(f, "{identifier}"), Some(concrete_type) => write!(f, "implied to be {concrete_type}"), None => write!(f, "unknown"), } @@ -416,8 +376,8 @@ impl Ord for Type { #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] pub struct FunctionType { - pub type_parameters: Option>, - pub value_parameters: Option>, + pub type_parameters: Option>, + pub value_parameters: Option>, pub return_type: Option>, } @@ -463,25 +423,17 @@ impl Display for FunctionType { #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] pub enum StructType { - Unit { - name: Identifier, - }, - Tuple { - name: Identifier, - fields: Vec, - }, - Fields { - name: Identifier, - fields: HashMap, - }, + Unit { name: u8 }, + Tuple { name: u8, fields: Vec }, + Fields { name: u8, fields: HashMap }, } impl StructType { - pub fn name(&self) -> &Identifier { + pub fn name(&self) -> u8 { match self { - StructType::Unit { name } => name, - StructType::Tuple { name, .. } => name, - StructType::Fields { name, .. } => name, + StructType::Unit { name } => *name, + StructType::Tuple { name, .. } => *name, + StructType::Fields { name, .. } => *name, } } } @@ -583,7 +535,7 @@ impl Ord for StructType { #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] pub struct EnumType { - pub name: Identifier, + pub name: u8, pub variants: Vec, } diff --git a/dust-lang/src/value.rs b/dust-lang/src/value.rs index 93ee212..cad4e11 100644 --- a/dust-lang/src/value.rs +++ b/dust-lang/src/value.rs @@ -83,6 +83,14 @@ impl Value { Value::Primitive(Primitive::String(to_string.to_string())) } + pub fn as_string(&self) -> Option<&String> { + if let Value::Primitive(Primitive::String(string)) = self { + Some(string) + } else { + None + } + } + pub fn is_function(&self) -> bool { matches!(self, Value::Function(_)) } @@ -894,7 +902,7 @@ impl Object { display.push_str(", "); } - let value_display = match vm.get(register, position) { + let value_display = match vm.get_register(register, position) { Ok(value) => value.display(vm, position)?, Err(error) => { return Err(ValueError::CannotDisplay { diff --git a/dust-lang/src/vm.rs b/dust-lang/src/vm.rs index 339fe49..4932179 100644 --- a/dust-lang/src/vm.rs +++ b/dust-lang/src/vm.rs @@ -1,9 +1,8 @@ use std::{cmp::Ordering, mem::replace}; use crate::{ - parse, value::Primitive, AnnotatedError, Chunk, ChunkError, DustError, FunctionType, - Identifier, Instruction, NativeFunction, NativeFunctionError, Operation, Span, Type, Value, - ValueError, + parse, value::Primitive, AnnotatedError, Chunk, DustError, FunctionType, Instruction, Local, + NativeFunction, NativeFunctionError, Operation, Span, Type, Value, ValueError, }; pub fn run(source: &str) -> Result, DustError> { @@ -42,14 +41,24 @@ impl Vm { position: Span, ) -> Result<(&Value, &Value), VmError> { let left = if instruction.b_is_constant() { - vm.chunk.get_constant(instruction.b(), position)? + vm.chunk.get_constant(instruction.b()).ok_or_else(|| { + VmError::ConstantIndexOutOfBounds { + index: instruction.b() as usize, + position, + } + })? } else { - vm.get(instruction.b(), position)? + vm.get_register(instruction.b(), position)? }; let right = if instruction.c_is_constant() { - vm.chunk.get_constant(instruction.c(), position)? + vm.chunk.get_constant(instruction.c()).ok_or_else(|| { + VmError::ConstantIndexOutOfBounds { + index: instruction.c() as usize, + position, + } + })? } else { - vm.get(instruction.c(), position)? + vm.get_register(instruction.c(), position)? }; Ok((left, right)) @@ -89,7 +98,7 @@ impl Vm { let jump = instruction.c_as_boolean(); let value = Value::boolean(boolean); - self.set(to_register, value, position)?; + self.set_register(to_register, value, position)?; if jump { self.ip += 1; @@ -115,11 +124,11 @@ impl Vm { let item_type = if is_empty { Type::Any } else { - self.get(first_register, position)?.r#type() + self.get_register(first_register, position)?.r#type() }; let value = Value::list(first_register, last_register, item_type); - self.set(to_register, value, position)?; + self.set_register(to_register, value, position)?; } Operation::LoadSelf => { let to_register = instruction.a(); @@ -132,34 +141,37 @@ impl Vm { }, ); - self.set(to_register, value, position)?; + self.set_register(to_register, value, position)?; } Operation::DefineLocal => { let from_register = instruction.a(); let to_local = instruction.b(); - self.chunk.define_local(to_local, from_register, position)?; + self.define_local(to_local, from_register, position)?; } Operation::GetLocal => { let to_register = instruction.a(); let local_index = instruction.b(); - let local = self.chunk.get_local(local_index, position)?; + let local = self.get_local(local_index, position)?; self.set_pointer(to_register, local.register_index, position)?; } Operation::SetLocal => { let register = instruction.a(); let local_index = instruction.b(); - let local = self.chunk.get_local(local_index, position)?; + let local = self.get_local(local_index, position)?; if !local.is_mutable { return Err(VmError::CannotMutateImmutableLocal { - identifier: local.identifier.clone(), + identifier: self + .chunk + .get_identifier(local.identifier_index) + .unwrap_or_else(|| "unknown".to_string()), position, }); } - self.chunk.define_local(local_index, register, position)?; + self.define_local(local_index, register, position)?; } Operation::Add => { let (left, right) = get_arguments(&mut self, instruction, position)?; @@ -167,7 +179,7 @@ impl Vm { .add(right) .map_err(|error| VmError::Value { error, position })?; - self.set(instruction.a(), sum, position)?; + self.set_register(instruction.a(), sum, position)?; } Operation::Subtract => { let (left, right) = get_arguments(&mut self, instruction, position)?; @@ -175,7 +187,7 @@ impl Vm { .subtract(right) .map_err(|error| VmError::Value { error, position })?; - self.set(instruction.a(), difference, position)?; + self.set_register(instruction.a(), difference, position)?; } Operation::Multiply => { let (left, right) = get_arguments(&mut self, instruction, position)?; @@ -183,7 +195,7 @@ impl Vm { .multiply(right) .map_err(|error| VmError::Value { error, position })?; - self.set(instruction.a(), product, position)?; + self.set_register(instruction.a(), product, position)?; } Operation::Divide => { let (left, right) = get_arguments(&mut self, instruction, position)?; @@ -191,7 +203,7 @@ impl Vm { .divide(right) .map_err(|error| VmError::Value { error, position })?; - self.set(instruction.a(), quotient, position)?; + self.set_register(instruction.a(), quotient, position)?; } Operation::Modulo => { let (left, right) = get_arguments(&mut self, instruction, position)?; @@ -199,12 +211,12 @@ impl Vm { .modulo(right) .map_err(|error| VmError::Value { error, position })?; - self.set(instruction.a(), remainder, position)?; + self.set_register(instruction.a(), remainder, position)?; } Operation::Test => { let register = instruction.a(); let test_value = instruction.c_as_boolean(); - let value = self.get(register, position)?; + let value = self.get_register(register, position)?; let boolean = if let Value::Primitive(Primitive::Boolean(boolean)) = value { *boolean } else { @@ -221,7 +233,7 @@ impl Vm { Operation::TestSet => todo!(), Operation::Equal => { debug_assert_eq!( - self.chunk.get_instruction(self.ip, position)?.0.operation(), + self.get_instruction(self.ip, position)?.0.operation(), Operation::Jump ); @@ -243,7 +255,7 @@ impl Vm { if boolean == compare_to { self.ip += 1; } else { - let (jump, _) = *self.chunk.get_instruction(self.ip, position)?; + let (jump, _) = self.get_instruction(self.ip, position)?; let jump_distance = jump.a(); let is_positive = jump.b_as_boolean(); let new_ip = if is_positive { @@ -257,7 +269,7 @@ impl Vm { } Operation::Less => { debug_assert_eq!( - self.chunk.get_instruction(self.ip, position)?.0.operation(), + self.get_instruction(self.ip, position)?.0.operation(), Operation::Jump ); @@ -279,7 +291,7 @@ impl Vm { if boolean == compare_to { self.ip += 1; } else { - let jump = self.chunk.get_instruction(self.ip, position)?.0; + let jump = self.get_instruction(self.ip, position)?.0; let jump_distance = jump.a(); let is_positive = jump.b_as_boolean(); let new_ip = if is_positive { @@ -293,7 +305,7 @@ impl Vm { } Operation::LessEqual => { debug_assert_eq!( - self.chunk.get_instruction(self.ip, position)?.0.operation(), + self.get_instruction(self.ip, position)?.0.operation(), Operation::Jump ); @@ -316,7 +328,7 @@ impl Vm { if boolean == compare_to { self.ip += 1; } else { - let jump = self.chunk.get_instruction(self.ip, position)?.0; + let jump = self.get_instruction(self.ip, position)?.0; let jump_distance = jump.a(); let is_positive = jump.b_as_boolean(); let new_ip = if is_positive { @@ -330,27 +342,37 @@ impl Vm { } Operation::Negate => { let value = if instruction.b_is_constant() { - self.chunk.get_constant(instruction.b(), position)? + self.chunk.get_constant(instruction.b()).ok_or_else(|| { + VmError::ConstantIndexOutOfBounds { + index: instruction.b() as usize, + position, + } + })? } else { - self.get(instruction.b(), position)? + self.get_register(instruction.b(), position)? }; let negated = value .negate() .map_err(|error| VmError::Value { error, position })?; - self.set(instruction.a(), negated, position)?; + self.set_register(instruction.a(), negated, position)?; } Operation::Not => { let value = if instruction.b_is_constant() { - self.chunk.get_constant(instruction.b(), position)? + self.chunk.get_constant(instruction.b()).ok_or_else(|| { + VmError::ConstantIndexOutOfBounds { + index: instruction.b() as usize, + position, + } + })? } else { - self.get(instruction.b(), position)? + self.get_register(instruction.b(), position)? }; let not = value .not() .map_err(|error| VmError::Value { error, position })?; - self.set(instruction.a(), not, position)?; + self.set_register(instruction.a(), not, position)?; } Operation::Jump => { let jump_distance = instruction.b(); @@ -366,7 +388,7 @@ impl Vm { let to_register = instruction.a(); let function_register = instruction.b(); let argument_count = instruction.c(); - let value = self.get(function_register, position)?.clone(); + let value = self.get_register(function_register, position)?.clone(); let function = if let Value::Function(function) = value { function } else { @@ -381,20 +403,20 @@ impl Vm { for argument_index in first_argument_index..first_argument_index + argument_count { - let argument = match self.get(argument_index, position) { + let argument = match self.get_register(argument_index, position) { Ok(value) => value.clone(), Err(VmError::EmptyRegister { .. }) => continue, Err(error) => return Err(error), }; let top_of_stack = function_vm.stack.len() as u8; - function_vm.set(top_of_stack, argument, position)?; + function_vm.set_register(top_of_stack, argument, position)?; } let return_value = function_vm.run()?; if let Some(value) = return_value { - self.set(to_register, value, position)?; + self.set_register(to_register, value, position)?; } } Operation::CallNative => { @@ -404,7 +426,7 @@ impl Vm { if let Some(value) = return_value { let to_register = instruction.a(); - self.set(to_register, value, position)?; + self.set_register(to_register, value, position)?; } } Operation::Return => { @@ -415,7 +437,7 @@ impl Vm { } if let Some(register) = self.last_assigned_register { - let value = self.empty(register, position)?; + let value = self.empty_register(register, position)?; return Ok(Some(value)); } else { @@ -428,7 +450,12 @@ impl Vm { Ok(None) } - fn set(&mut self, to_register: u8, value: Value, position: Span) -> Result<(), VmError> { + fn set_register( + &mut self, + to_register: u8, + value: Value, + position: Span, + ) -> Result<(), VmError> { let length = self.stack.len(); self.last_assigned_register = Some(to_register); let to_register = to_register as usize; @@ -564,7 +591,7 @@ impl Vm { } } - pub fn get(&self, index: u8, position: Span) -> Result<&Value, VmError> { + pub fn get_register(&self, index: u8, position: Span) -> Result<&Value, VmError> { let index = index as usize; let register = self .stack @@ -573,21 +600,19 @@ impl Vm { match register { Register::Value(value) => Ok(value), - Register::Pointer(register_index) => { - let value = self.get(*register_index, position)?; - - Ok(value) - } - Register::Constant(constant_index) => { - let value = self.chunk.get_constant(*constant_index, position)?; - - Ok(value) - } + Register::Pointer(register_index) => self.get_register(*register_index, position), + Register::Constant(constant_index) => self + .chunk + .get_constant(*constant_index) + .ok_or_else(|| VmError::ConstantIndexOutOfBounds { + index: *constant_index as usize, + position, + }), Register::Empty => Err(VmError::EmptyRegister { index, position }), } } - fn empty(mut self, index: u8, position: Span) -> Result { + fn empty_register(mut self, index: u8, position: Span) -> Result { let index = index as usize; if index >= self.stack.len() { @@ -599,7 +624,7 @@ impl Vm { match register { Register::Value(value) => Ok(value), Register::Pointer(register_index) => { - let value = self.empty(register_index, position)?; + let value = self.empty_register(register_index, position)?; Ok(value) } @@ -617,12 +642,62 @@ impl Vm { self.ip = self.chunk.len() - 1; } - let current = self.chunk.get_instruction(self.ip, position)?; + let current = self.chunk.instructions().get(self.ip).ok_or_else(|| { + VmError::InstructionIndexOutOfBounds { + index: self.ip, + position, + } + })?; self.ip += 1; Ok(current) } + + fn define_local( + &mut self, + local_index: u8, + register_index: u8, + position: Span, + ) -> Result<(), VmError> { + let local = self + .chunk + .locals_mut() + .get_mut(local_index as usize) + .ok_or_else(|| VmError::LocalIndexOutOfBounds { + index: local_index as usize, + position, + })?; + + log::debug!("Define local L{}", local_index); + + local.register_index = register_index; + + Ok(()) + } + + fn get_local(&self, local_index: u8, position: Span) -> Result<&Local, VmError> { + let local_index = local_index as usize; + + self.chunk + .locals() + .get(local_index) + .ok_or_else(|| VmError::LocalIndexOutOfBounds { + index: local_index, + position, + }) + } + + fn get_instruction( + &self, + index: usize, + position: Span, + ) -> Result<&(Instruction, Span), VmError> { + self.chunk + .instructions() + .get(index) + .ok_or_else(|| VmError::InstructionIndexOutOfBounds { index, position }) + } } #[derive(Debug, Eq, PartialEq)] @@ -636,7 +711,7 @@ enum Register { #[derive(Debug, Clone, PartialEq)] pub enum VmError { CannotMutateImmutableLocal { - identifier: Identifier, + identifier: String, position: Span, }, EmptyRegister { @@ -651,10 +726,22 @@ pub enum VmError { found: Value, position: Span, }, + ConstantIndexOutOfBounds { + index: usize, + position: Span, + }, RegisterIndexOutOfBounds { index: usize, position: Span, }, + InstructionIndexOutOfBounds { + index: usize, + position: Span, + }, + LocalIndexOutOfBounds { + index: usize, + position: Span, + }, InvalidInstruction { instruction: Instruction, position: Span, @@ -666,25 +753,18 @@ pub enum VmError { position: Span, }, UndefinedVariable { - identifier: Identifier, + identifier: String, position: Span, }, // Wrappers for foreign errors NativeFunction(NativeFunctionError), - Chunk(ChunkError), Value { error: ValueError, position: Span, }, } -impl From for VmError { - fn from(error: ChunkError) -> Self { - Self::Chunk(error) - } -} - impl AnnotatedError for VmError { fn title() -> &'static str { "Runtime Error" @@ -693,15 +773,17 @@ impl AnnotatedError for VmError { fn description(&self) -> &'static str { match self { Self::CannotMutateImmutableLocal { .. } => "Cannot mutate immutable variable", + Self::ConstantIndexOutOfBounds { .. } => "Constant index out of bounds", Self::EmptyRegister { .. } => "Empty register", Self::ExpectedBoolean { .. } => "Expected boolean", Self::ExpectedFunction { .. } => "Expected function", Self::RegisterIndexOutOfBounds { .. } => "Register index out of bounds", + Self::InstructionIndexOutOfBounds { .. } => "Instruction index out of bounds", Self::InvalidInstruction { .. } => "Invalid instruction", + Self::LocalIndexOutOfBounds { .. } => "Local index out of bounds", Self::StackOverflow { .. } => "Stack overflow", Self::StackUnderflow { .. } => "Stack underflow", Self::UndefinedVariable { .. } => "Undefined variable", - Self::Chunk(error) => error.description(), Self::NativeFunction(error) => error.description(), Self::Value { .. } => "Value error", } @@ -709,15 +791,23 @@ impl AnnotatedError for VmError { fn details(&self) -> Option { match self { + Self::ConstantIndexOutOfBounds { index, .. } => { + Some(format!("Constant C{index} does not exist")) + } Self::EmptyRegister { index, .. } => Some(format!("Register {index} is empty")), Self::ExpectedFunction { found, .. } => Some(format!("{found} is not a function")), Self::RegisterIndexOutOfBounds { index, .. } => { Some(format!("Register {index} does not exist")) } + Self::InstructionIndexOutOfBounds { index, .. } => { + Some(format!("Instruction {index} does not exist")) + } + Self::LocalIndexOutOfBounds { index, .. } => { + Some(format!("Local L{index} does not exist")) + } Self::UndefinedVariable { identifier, .. } => { Some(format!("{identifier} is not in scope")) } - Self::Chunk(error) => error.details(), Self::NativeFunction(error) => error.details(), Self::Value { error, .. } => Some(error.to_string()), _ => None, @@ -727,15 +817,17 @@ impl AnnotatedError for VmError { fn position(&self) -> Span { match self { Self::CannotMutateImmutableLocal { position, .. } => *position, + Self::ConstantIndexOutOfBounds { position, .. } => *position, Self::EmptyRegister { position, .. } => *position, Self::ExpectedBoolean { position, .. } => *position, Self::ExpectedFunction { position, .. } => *position, Self::RegisterIndexOutOfBounds { position, .. } => *position, + Self::InstructionIndexOutOfBounds { position, .. } => *position, Self::InvalidInstruction { position, .. } => *position, + Self::LocalIndexOutOfBounds { position, .. } => *position, Self::StackUnderflow { position } => *position, Self::StackOverflow { position } => *position, Self::UndefinedVariable { position, .. } => *position, - Self::Chunk(error) => error.position(), Self::NativeFunction(error) => error.position(), Self::Value { position, .. } => *position, } diff --git a/dust-lang/tests/control_flow.rs b/dust-lang/tests/control_flow.rs index 7b8e20f..130bd69 100644 --- a/dust-lang/tests/control_flow.rs +++ b/dust-lang/tests/control_flow.rs @@ -23,7 +23,7 @@ fn equality_assignment_long() { (Instruction::r#return(true), Span(44, 44)), ], vec![Value::integer(4)], - vec![Local::new(Identifier::new("a"), None, false, 0, 0)] + vec![Local::new(0, None, false, 0, 0)] )), ); @@ -53,7 +53,7 @@ fn equality_assignment_short() { (Instruction::r#return(true), Span(16, 16)), ], vec![Value::integer(4)], - vec![Local::new(Identifier::new("a"), None, false, 0, 0)] + vec![Local::new(0, None, false, 0, 0)] )), ); @@ -111,7 +111,7 @@ fn if_else_assigment() { Value::integer(3), Value::integer(42), ], - vec![Local::new(Identifier::new("a"), None, false, 0, 0)] + vec![Local::new(0, None, false, 0, 0)] )), ); diff --git a/dust-lang/tests/expressions.rs b/dust-lang/tests/expressions.rs index f3347f5..4609c98 100644 --- a/dust-lang/tests/expressions.rs +++ b/dust-lang/tests/expressions.rs @@ -41,7 +41,7 @@ fn add_assign() { (Instruction::r#return(true), Span(24, 24)) ], vec![Value::integer(1), Value::integer(2)], - vec![Local::new(Identifier::new("a"), None, true, 0, 0)] + vec![Local::new(0, None, true, 0, 0)] )) ); @@ -82,7 +82,7 @@ fn define_local() { (Instruction::r#return(false), Span(11, 11)) ], vec![Value::integer(42)], - vec![Local::new(Identifier::new("x"), None, false, 0, 0)] + vec![Local::new(0, None, false, 0, 0)] )), ); @@ -133,7 +133,7 @@ fn divide_assign() { (Instruction::r#return(true), Span(24, 24)) ], vec![Value::integer(2)], - vec![Local::new(Identifier::new("a"), None, true, 0, 0)] + vec![Local::new(0, None, true, 0, 0)] )) ); @@ -359,7 +359,7 @@ fn multiply_assign() { (Instruction::r#return(true), Span(23, 23)) ], vec![Value::integer(2), Value::integer(3)], - vec![Local::new(Identifier::new("a"), None, true, 0, 0),] + vec![Local::new(0, None, true, 0, 0),] )) ); @@ -453,7 +453,7 @@ fn set_local() { (Instruction::r#return(true), Span(25, 25)), ], vec![Value::integer(41), Value::integer(42)], - vec![Local::new(Identifier::new("x"), None, true, 0, 0)] + vec![Local::new(0, None, true, 0, 0)] )), ); @@ -504,7 +504,7 @@ fn subtract_assign() { (Instruction::r#return(true), Span(25, 25)), ], vec![Value::integer(42), Value::integer(2)], - vec![Local::new(Identifier::new("x"), None, true, 0, 0)] + vec![Local::new(0, None, true, 0, 0)] )), ); @@ -532,8 +532,8 @@ fn variable_and() { ], vec![], vec![ - Local::new(Identifier::new("a"), None, false, 0, 0), - Local::new(Identifier::new("b"), None, false, 0, 1), + Local::new(0, None, false, 0, 0), + Local::new(0, None, false, 0, 1), ] )) ); @@ -563,7 +563,7 @@ fn r#while() { (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, 0),] + vec![Local::new(0, None, true, 0, 0),] )), ); diff --git a/dust-lang/tests/functions.rs b/dust-lang/tests/functions.rs index 0953b3e..36044e7 100644 --- a/dust-lang/tests/functions.rs +++ b/dust-lang/tests/functions.rs @@ -15,16 +15,13 @@ fn function() { ], vec![], vec![ - Local::new(Identifier::new("a"), Some(Type::Integer), false, 0, 0), - Local::new(Identifier::new("b"), Some(Type::Integer), false, 0, 1) + Local::new(0, Some(Type::Integer), false, 0, 0), + Local::new(0, Some(Type::Integer), false, 0, 1) ] ), FunctionType { type_parameters: None, - value_parameters: Some(vec![ - (Identifier::new("a"), Type::Integer), - (Identifier::new("b"), Type::Integer) - ]), + value_parameters: Some(vec![(0, Type::Integer), (0, Type::Integer)]), return_type: Some(Box::new(Type::Integer)), } ))) @@ -53,27 +50,21 @@ fn function_declaration() { ], vec![], vec![ - Local::new(Identifier::new("a"), Some(Type::Integer), false, 0, 0), - Local::new(Identifier::new("b"), Some(Type::Integer), false, 0, 1) + Local::new(0, Some(Type::Integer), false, 0, 0), + Local::new(0, Some(Type::Integer), false, 0, 1) ] ), FunctionType { type_parameters: None, - value_parameters: Some(vec![ - (Identifier::new("a"), Type::Integer), - (Identifier::new("b"), Type::Integer) - ]), + value_parameters: Some(vec![(0, Type::Integer), (0, Type::Integer)]), return_type: Some(Box::new(Type::Integer)), }, )], vec![Local::new( - Identifier::new("add"), + 0, Some(Type::Function(FunctionType { type_parameters: None, - value_parameters: Some(vec![ - (Identifier::new("a"), Type::Integer), - (Identifier::new("b"), Type::Integer) - ]), + value_parameters: Some(vec![(0, Type::Integer), (0, Type::Integer)]), return_type: Some(Box::new(Type::Integer)), })), false, @@ -111,16 +102,13 @@ fn function_call() { ], vec![], vec![ - Local::new(Identifier::new("a"), Some(Type::Integer), false, 0, 0), - Local::new(Identifier::new("b"), Some(Type::Integer), false, 0, 1) + Local::new(0, Some(Type::Integer), false, 0, 0), + Local::new(0, Some(Type::Integer), false, 0, 1) ] ), FunctionType { type_parameters: None, - value_parameters: Some(vec![ - (Identifier::new("a"), Type::Integer), - (Identifier::new("b"), Type::Integer) - ]), + value_parameters: Some(vec![(0, Type::Integer), (0, Type::Integer)]), return_type: Some(Box::new(Type::Integer)), } ), diff --git a/dust-lang/tests/scopes.rs b/dust-lang/tests/scopes.rs index 768c85c..7ce4486 100644 --- a/dust-lang/tests/scopes.rs +++ b/dust-lang/tests/scopes.rs @@ -50,11 +50,11 @@ fn block_scope() { Value::integer(2), ], vec![ - Local::new(Identifier::new("a"), None, false, 0, 0), - Local::new(Identifier::new("b"), None, false, 1, 1), - Local::new(Identifier::new("c"), None, false, 2, 2), - Local::new(Identifier::new("d"), None, false, 1, 3), - Local::new(Identifier::new("e"), None, false, 0, 4), + Local::new(0, None, false, 0, 0), + Local::new(0, None, false, 1, 1), + Local::new(0, None, false, 2, 2), + Local::new(0, None, false, 1, 3), + Local::new(0, None, false, 0, 4), ] )), );