From e4204c1b0d5ea95cf9396a18103c30ab9af55f58 Mon Sep 17 00:00:00 2001 From: Jeff Date: Wed, 11 Sep 2024 03:10:12 -0400 Subject: [PATCH] Get variable scopes working --- dust-lang/src/chunk.rs | 191 ++++++++--------- dust-lang/src/identifier_stack.rs | 160 --------------- dust-lang/src/instruction.rs | 22 +- dust-lang/src/lib.rs | 4 +- dust-lang/src/parser.rs | 100 +++++---- dust-lang/src/vm.rs | 326 ++++++++++++------------------ 6 files changed, 284 insertions(+), 519 deletions(-) delete mode 100644 dust-lang/src/identifier_stack.rs diff --git a/dust-lang/src/chunk.rs b/dust-lang/src/chunk.rs index 5b9287e..4a1a8f9 100644 --- a/dust-lang/src/chunk.rs +++ b/dust-lang/src/chunk.rs @@ -2,16 +2,14 @@ use std::fmt::{self, Debug, Display, Formatter}; use serde::{Deserialize, Serialize}; -use crate::{ - identifier_stack::Local, AnnotatedError, Identifier, IdentifierStack, Instruction, Span, Value, - ValueLocation, -}; +use crate::{AnnotatedError, Identifier, Instruction, Span, Value}; #[derive(Clone, Eq, PartialEq, Serialize, Deserialize)] pub struct Chunk { code: Vec<(u8, Span)>, constants: Vec, - identifiers: IdentifierStack, + identifiers: Vec, + scope_depth: usize, } impl Chunk { @@ -19,7 +17,8 @@ impl Chunk { Self { code: Vec::new(), constants: Vec::new(), - identifiers: IdentifierStack::new(), + identifiers: Vec::new(), + scope_depth: 0, } } @@ -31,7 +30,8 @@ impl Chunk { Self { code, constants, - identifiers: IdentifierStack::with_data(identifiers, 0), + identifiers, + scope_depth: 0, } } @@ -44,7 +44,7 @@ impl Chunk { } pub fn scope_depth(&self) -> usize { - self.identifiers.scope_depth() + self.scope_depth } pub fn get_code(&self, offset: usize, position: Span) -> Result<&(u8, Span), ChunkError> { @@ -53,8 +53,8 @@ impl Chunk { .ok_or(ChunkError::CodeIndexOfBounds { offset, position }) } - pub fn push_code(&mut self, instruction: u8, position: Span) { - self.code.push((instruction, position)); + pub fn push_code>(&mut self, into_byte: T, position: Span) { + self.code.push((into_byte.into(), position)); } pub fn get_constant(&self, index: u8, position: Span) -> Result<&Value, ChunkError> { @@ -76,11 +76,11 @@ impl Chunk { } } - pub fn push_constant(&mut self, value: Value) -> Result { + pub fn push_constant(&mut self, value: Value, position: Span) -> Result { let starting_length = self.constants.len(); if starting_length + 1 > (u8::MAX as usize) { - Err(ChunkError::ConstantOverflow) + Err(ChunkError::ConstantOverflow { position }) } else { self.constants.push(value); @@ -89,28 +89,22 @@ impl Chunk { } pub fn contains_identifier(&self, identifier: &Identifier) -> bool { - self.identifiers.contains(identifier) + self.identifiers + .iter() + .any(|local| &local.identifier == identifier) } - pub fn get_local(&self, index: u8) -> Result<&Local, ChunkError> { + pub fn get_local(&self, index: u8, position: Span) -> Result<&Local, ChunkError> { self.identifiers .get(index as usize) - .ok_or(ChunkError::IdentifierIndexOutOfBounds(index)) + .ok_or(ChunkError::IdentifierIndexOutOfBounds { index, position }) } - pub fn resolve_local(&self, identifier: &Identifier) -> Option { - self.identifiers.resolve(self, identifier) - } - - pub fn resolve_local_index(&self, identifier: &Identifier) -> Option { - self.identifiers.resolve_index(identifier) - } - - pub fn get_identifier(&self, index: u8) -> Result<&Identifier, ChunkError> { + pub fn get_identifier(&self, index: u8, position: Span) -> Result<&Identifier, ChunkError> { self.identifiers .get(index as usize) .map(|local| &local.identifier) - .ok_or(ChunkError::IdentifierIndexOutOfBounds(index)) + .ok_or(ChunkError::IdentifierIndexOutOfBounds { index, position }) } pub fn get_identifier_index( @@ -119,59 +113,47 @@ impl Chunk { position: Span, ) -> Result { self.identifiers - .get_index(identifier) - .map(|index| index as u8) + .iter() + .enumerate() + .rev() + .find_map(|(index, local)| { + if &local.identifier == identifier { + Some(index as u8) + } else { + None + } + }) .ok_or(ChunkError::IdentifierNotFound { identifier: identifier.clone(), position, }) } - pub fn push_constant_identifier(&mut self, identifier: Identifier) -> Result { - let starting_length = self.identifiers.local_count(); - - if starting_length + 1 > (u8::MAX as usize) { - Err(ChunkError::IdentifierOverflow) - } else { - self.identifiers - .define(identifier, ValueLocation::ConstantStack); - - Ok(starting_length as u8) - } - } - - pub fn push_runtime_identifier(&mut self, identifier: Identifier) -> Result { - let starting_length = self.identifiers.local_count(); - - if starting_length + 1 > (u8::MAX as usize) { - Err(ChunkError::IdentifierOverflow) - } else { - self.identifiers - .define(identifier, ValueLocation::RuntimeStack); - - Ok(starting_length as u8) - } - } - - pub fn redefine_as_runtime_identifier( + pub fn declare_variable( &mut self, - identifier: &Identifier, + identifier: Identifier, position: Span, - ) -> Result { - self.identifiers - .redefine(identifier, ValueLocation::RuntimeStack) - .ok_or_else(|| ChunkError::IdentifierNotFound { - identifier: identifier.clone(), - position, - }) + ) -> Result { + let starting_length = self.identifiers.len(); + + if starting_length + 1 > (u8::MAX as usize) { + Err(ChunkError::IdentifierOverflow { position }) + } else { + self.identifiers.push(Local { + identifier, + depth: self.scope_depth, + }); + + Ok(starting_length as u8) + } } pub fn begin_scope(&mut self) { - self.identifiers.begin_scope(); + self.scope_depth += 1; } pub fn end_scope(&mut self) { - self.identifiers.end_scope(); + self.scope_depth -= 1; } pub fn clear(&mut self) { @@ -180,6 +162,14 @@ impl Chunk { self.identifiers.clear(); } + pub fn identifiers(&self) -> &[Local] { + &self.identifiers + } + + pub fn pop_identifier(&mut self) -> Option { + self.identifiers.pop() + } + pub fn disassemble(&self, name: &str) -> String { let mut output = String::new(); @@ -199,7 +189,7 @@ impl Chunk { for (offset, (byte, position)) in self.code.iter().enumerate() { if let Some( Instruction::Constant - | Instruction::DefineVariable + | Instruction::DeclareVariable | Instruction::GetVariable | Instruction::SetVariable, ) = previous @@ -237,24 +227,13 @@ impl Chunk { output.push_str(&display); } - output.push_str("\n Identifiers \n"); - output.push_str("----- ---------- -------- -----\n"); - output.push_str("INDEX IDENTIFIER LOCATION DEPTH\n"); - output.push_str("----- ---------- -------- -----\n"); + output.push_str("\n Identifiers \n"); + output.push_str("----- ---------- -----\n"); + output.push_str("INDEX IDENTIFIER DEPTH\n"); + output.push_str("----- ---------- -----\n"); - for ( - index, - Local { - identifier, - depth, - value_location, - }, - ) in self.identifiers.iter().enumerate() - { - let display = format!( - "{index:3} {:10} {value_location} {depth}\n", - identifier.as_str() - ); + for (index, Local { identifier, depth }) in self.identifiers.iter().enumerate() { + let display = format!("{index:3} {:10} {depth}\n", identifier.as_str()); output.push_str(&display); } @@ -280,19 +259,32 @@ impl Debug for Chunk { } } +#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] +pub struct Local { + pub identifier: Identifier, + pub depth: usize, +} + #[derive(Debug, Clone, PartialEq)] pub enum ChunkError { CodeIndexOfBounds { offset: usize, position: Span, }, - ConstantOverflow, + ConstantOverflow { + position: Span, + }, ConstantIndexOutOfBounds { index: u8, position: Span, }, - IdentifierIndexOutOfBounds(u8), - IdentifierOverflow, + IdentifierIndexOutOfBounds { + index: u8, + position: Span, + }, + IdentifierOverflow { + position: Span, + }, IdentifierNotFound { identifier: Identifier, position: Span, @@ -307,10 +299,10 @@ impl AnnotatedError for ChunkError { fn description(&self) -> &'static str { match self { ChunkError::CodeIndexOfBounds { .. } => "Code index out of bounds", - ChunkError::ConstantOverflow => "Constant overflow", + ChunkError::ConstantOverflow { .. } => "Constant overflow", ChunkError::ConstantIndexOutOfBounds { .. } => "Constant index out of bounds", - ChunkError::IdentifierIndexOutOfBounds(_) => "Identifier index out of bounds", - ChunkError::IdentifierOverflow => "Identifier overflow", + ChunkError::IdentifierIndexOutOfBounds { .. } => "Identifier index out of bounds", + ChunkError::IdentifierOverflow { .. } => "Identifier overflow", ChunkError::IdentifierNotFound { .. } => "Identifier not found", } } @@ -321,7 +313,7 @@ impl AnnotatedError for ChunkError { ChunkError::ConstantIndexOutOfBounds { index, .. } => { Some(format!("Constant index: {}", index)) } - ChunkError::IdentifierIndexOutOfBounds(index) => { + ChunkError::IdentifierIndexOutOfBounds { index, .. } => { Some(format!("Identifier index: {}", index)) } ChunkError::IdentifierNotFound { identifier, .. } => { @@ -340,24 +332,3 @@ impl AnnotatedError for ChunkError { } } } - -impl Display for ChunkError { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - match self { - ChunkError::CodeIndexOfBounds { offset, .. } => { - write!(f, "Code index out of bounds: {}", offset) - } - ChunkError::ConstantOverflow => write!(f, "Constant overflow"), - ChunkError::ConstantIndexOutOfBounds { index, .. } => { - write!(f, "Constant index out of bounds: {}", index) - } - ChunkError::IdentifierIndexOutOfBounds(index) => { - write!(f, "Identifier index out of bounds: {}", index) - } - ChunkError::IdentifierOverflow => write!(f, "Identifier overflow"), - ChunkError::IdentifierNotFound { identifier, .. } => { - write!(f, "Identifier not found: {}", identifier) - } - } - } -} diff --git a/dust-lang/src/identifier_stack.rs b/dust-lang/src/identifier_stack.rs deleted file mode 100644 index ab4ba12..0000000 --- a/dust-lang/src/identifier_stack.rs +++ /dev/null @@ -1,160 +0,0 @@ -use std::fmt::Display; - -use serde::{Deserialize, Serialize}; - -use crate::{Chunk, Identifier}; - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct IdentifierStack { - locals: Vec, - scope_depth: usize, -} - -impl IdentifierStack { - pub fn new() -> Self { - Self { - locals: Vec::new(), - scope_depth: 0, - } - } - - pub fn with_data(locals: Vec, scope_depth: usize) -> Self { - Self { - locals, - scope_depth, - } - } - - pub fn clear(&mut self) { - self.locals.clear(); - self.scope_depth = 0; - } - - pub fn local_count(&self) -> usize { - self.locals.len() - } - - pub fn contains(&self, identifier: &Identifier) -> bool { - self.locals - .iter() - .rev() - .any(|local| &local.identifier == identifier) - } - - pub fn resolve(&self, chunk: &Chunk, identifier: &Identifier) -> Option { - for (index, local) in self.locals.iter().rev().enumerate() { - if &local.identifier == identifier { - let offset = index; - - return Some(offset as u8); - } - } - - None - } - - pub fn resolve_index(&self, identifier: &Identifier) -> Option { - self.locals.iter().enumerate().find_map(|(index, local)| { - if &local.identifier == identifier && local.depth <= self.scope_depth { - Some(index as u8) - } else { - None - } - }) - } - - pub fn get(&self, index: usize) -> Option<&Local> { - self.locals.get(index) - } - - pub fn get_index(&self, identifier: &Identifier) -> Option { - self.locals.iter().enumerate().rev().find_map( - |( - index, - Local { - identifier: local, .. - }, - )| { - if local == identifier { - Some(index) - } else { - None - } - }, - ) - } - - pub fn scope_depth(&self) -> usize { - self.scope_depth - } - - pub fn begin_scope(&mut self) { - self.scope_depth += 1; - } - - pub fn end_scope(&mut self) { - self.scope_depth -= 1; - } - - pub fn define(&mut self, identifier: Identifier, value_location: ValueLocation) { - self.locals.push(Local { - identifier, - depth: self.scope_depth, - value_location, - }); - } - - pub fn redefine( - &mut self, - identifier: &Identifier, - value_location: ValueLocation, - ) -> Option { - if let Some(index) = self.get_index(identifier) { - self.locals[index].value_location = value_location; - - Some(index) - } else { - None - } - } - - pub fn iter(&self) -> impl Iterator { - self.locals.iter() - } -} - -impl Default for IdentifierStack { - fn default() -> Self { - Self::new() - } -} - -impl Eq for IdentifierStack {} - -impl PartialEq for IdentifierStack { - fn eq(&self, other: &Self) -> bool { - self.locals == other.locals - } -} - -#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] -pub struct Local { - pub identifier: Identifier, - pub depth: usize, - pub value_location: ValueLocation, -} - -#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] -pub enum ValueLocation { - ConstantStack, - RuntimeStack, -} - -impl Display for ValueLocation { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - ValueLocation::ConstantStack => write!(f, "constant"), - ValueLocation::RuntimeStack => write!(f, "runtime "), - } - } -} diff --git a/dust-lang/src/instruction.rs b/dust-lang/src/instruction.rs index 6f1293a..5dc5a8e 100644 --- a/dust-lang/src/instruction.rs +++ b/dust-lang/src/instruction.rs @@ -11,7 +11,7 @@ pub enum Instruction { Pop = 2, // Variables - DefineVariable = 3, + DeclareVariable = 3, GetVariable = 4, SetVariable = 5, @@ -40,7 +40,7 @@ impl Instruction { 0 => Some(Instruction::Constant), 1 => Some(Instruction::Return), 2 => Some(Instruction::Pop), - 3 => Some(Instruction::DefineVariable), + 3 => Some(Instruction::DeclareVariable), 4 => Some(Instruction::GetVariable), 5 => Some(Instruction::SetVariable), 6 => Some(Instruction::Negate), @@ -70,7 +70,7 @@ impl Instruction { let value_display = chunk .get_constant(argument, position) .map(|value| value.to_string()) - .unwrap_or_else(|error| error.to_string()); + .unwrap_or_else(|error| format!("{error:?}")); format!("CONSTANT {value_display}") } @@ -78,20 +78,20 @@ impl Instruction { Instruction::Pop => "POP".to_string(), // Variables - Instruction::DefineVariable => { + Instruction::DeclareVariable => { let (argument, _) = chunk.get_code(offset + 1, dummy_position).unwrap(); - let identifier_display = match chunk.get_identifier(*argument) { + let identifier_display = match chunk.get_identifier(*argument, dummy_position) { Ok(identifier) => identifier.to_string(), - Err(error) => error.to_string(), + Err(error) => format!("{error:?}"), }; - format!("DEFINE_VARIABLE {identifier_display}") + format!("DECLARE_VARIABLE {identifier_display}") } Instruction::GetVariable => { let (argument, _) = chunk.get_code(offset + 1, dummy_position).unwrap(); - let identifier_display = match chunk.get_identifier(*argument) { + let identifier_display = match chunk.get_identifier(*argument, dummy_position) { Ok(identifier) => identifier.to_string(), - Err(error) => error.to_string(), + Err(error) => format!("{error:?}"), }; format!("GET_VARIABLE {identifier_display}") @@ -99,9 +99,9 @@ impl Instruction { Instruction::SetVariable => { let (argument, _) = chunk.get_code(offset + 1, dummy_position).unwrap(); - let identifier_display = match chunk.get_identifier(*argument) { + let identifier_display = match chunk.get_identifier(*argument, dummy_position) { Ok(identifier) => identifier.to_string(), - Err(error) => error.to_string(), + Err(error) => format!("{error:?}"), }; format!("SET_VARIABLE {identifier_display}") diff --git a/dust-lang/src/lib.rs b/dust-lang/src/lib.rs index e9bd64f..8357e15 100644 --- a/dust-lang/src/lib.rs +++ b/dust-lang/src/lib.rs @@ -19,7 +19,6 @@ pub mod chunk; pub mod constructor; pub mod dust_error; pub mod identifier; -pub mod identifier_stack; pub mod instruction; pub mod lexer; pub mod parser; @@ -28,11 +27,10 @@ pub mod r#type; pub mod value; pub mod vm; -pub use chunk::{Chunk, ChunkError}; +pub use chunk::{Chunk, ChunkError, Local}; pub use constructor::{ConstructError, Constructor}; pub use dust_error::{AnnotatedError, DustError}; pub use identifier::Identifier; -pub use identifier_stack::{IdentifierStack, Local, ValueLocation}; pub use instruction::Instruction; pub use lexer::{lex, LexError, Lexer}; pub use parser::{parse, ParseError, Parser}; diff --git a/dust-lang/src/parser.rs b/dust-lang/src/parser.rs index e302eb2..7ec0a0c 100644 --- a/dust-lang/src/parser.rs +++ b/dust-lang/src/parser.rs @@ -6,7 +6,7 @@ use std::{ use crate::{ dust_error::AnnotatedError, Chunk, ChunkError, DustError, Identifier, Instruction, LexError, - Lexer, Local, Span, Token, TokenKind, TokenOwned, Value, ValueLocation, + Lexer, Span, Token, TokenKind, TokenOwned, Value, }; pub fn parse(source: &str) -> Result { @@ -96,10 +96,7 @@ impl<'src> Parser<'src> { fn emit_constant(&mut self, value: Value) -> Result<(), ParseError> { let position = self.previous_position; - let constant_index = self - .chunk - .push_constant(value) - .map_err(|error| ParseError::Chunk { error, position })?; + let constant_index = self.chunk.push_constant(value, position)?; self.emit_byte(Instruction::Constant, position); self.emit_byte(constant_index, position); @@ -239,7 +236,7 @@ impl<'src> Parser<'src> { fn parse_named_variable(&mut self, allow_assignment: bool) -> Result<(), ParseError> { let token = self.previous_token.to_owned(); - let identifier_index = self.parse_identifier_from(token)?; + let identifier_index = self.parse_identifier_from(token, self.previous_position)?; if allow_assignment && self.allow(TokenKind::Equal)? { self.parse_expression()?; @@ -253,24 +250,27 @@ impl<'src> Parser<'src> { Ok(()) } - fn parse_identifier_from(&mut self, token: TokenOwned) -> Result { + fn parse_identifier_from( + &mut self, + token: TokenOwned, + position: Span, + ) -> Result { if let TokenOwned::Identifier(text) = token { let identifier = Identifier::new(text); - let identifier_index = - self.chunk - .push_constant_identifier(identifier) - .map_err(|error| ParseError::Chunk { - error, - position: self.previous_position, - })?; - - Ok(identifier_index) + if let Ok(identifier_index) = self.chunk.get_identifier_index(&identifier, position) { + Ok(identifier_index) + } else { + Err(ParseError::UndefinedVariable { + identifier, + position, + }) + } } else { Err(ParseError::ExpectedToken { expected: TokenKind::Identifier, found: self.current_token.to_owned(), - position: self.current_position, + position, }) } } @@ -284,6 +284,18 @@ impl<'src> Parser<'src> { self.chunk.end_scope(); + while self + .chunk + .identifiers() + .iter() + .rev() + .next() + .map_or(false, |local| local.depth > self.chunk.scope_depth()) + { + self.emit_byte(Instruction::Pop, self.current_position); + self.chunk.pop_identifier(); + } + Ok(()) } @@ -295,7 +307,7 @@ impl<'src> Parser<'src> { let start = self.current_position.0; let (is_expression_statement, contains_block) = match self.current_token { Token::Let => { - self.parse_let_assignment(true)?; + self.parse_let_statement(true)?; (false, false) } @@ -321,7 +333,7 @@ impl<'src> Parser<'src> { Ok(()) } - fn parse_let_assignment(&mut self, _allow_assignment: bool) -> Result<(), ParseError> { + fn parse_let_statement(&mut self, _allow_assignment: bool) -> Result<(), ParseError> { self.expect(TokenKind::Let)?; let position = self.current_position; @@ -340,12 +352,9 @@ impl<'src> Parser<'src> { self.expect(TokenKind::Equal)?; self.parse_expression()?; - let identifier_index = self - .chunk - .push_constant_identifier(identifier) - .map_err(|error| ParseError::Chunk { error, position })?; + let identifier_index = self.chunk.declare_variable(identifier, position)?; - self.emit_byte(Instruction::DefineVariable, position); + self.emit_byte(Instruction::DeclareVariable, position); self.emit_byte(identifier_index, position); Ok(()) @@ -504,7 +513,7 @@ impl From<&TokenKind> for ParseRule<'_> { TokenKind::If => todo!(), TokenKind::Int => todo!(), TokenKind::Let => ParseRule { - prefix: Some(Parser::parse_let_assignment), + prefix: Some(Parser::parse_let_statement), infix: None, precedence: Precedence::None, }, @@ -606,12 +615,13 @@ pub enum ParseError { found: TokenOwned, position: Span, }, - - // Wrappers around foreign errors - Chunk { - error: ChunkError, + UndefinedVariable { + identifier: Identifier, position: Span, }, + + // Wrappers around foreign errors + Chunk(ChunkError), Lex(LexError), ParseIntError { error: ParseIntError, @@ -619,6 +629,12 @@ 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" @@ -630,6 +646,7 @@ impl AnnotatedError for ParseError { Self::ExpectedToken { .. } => "Expected a specific token", Self::ExpectedTokenMultiple { .. } => "Expected one of multiple tokens", Self::InvalidAssignmentTarget { .. } => "Invalid assignment target", + Self::UndefinedVariable { .. } => "Undefined variable", Self::Chunk { .. } => "Chunk error", Self::Lex(_) => "Lex error", Self::ParseIntError { .. } => "Failed to parse integer", @@ -650,7 +667,10 @@ impl AnnotatedError for ParseError { Self::InvalidAssignmentTarget { found, .. } => { Some(format!("Invalid assignment target \"{found}\"")) } - Self::Chunk { error, .. } => Some(error.to_string()), + Self::UndefinedVariable { identifier, .. } => { + Some(format!("Undefined variable \"{identifier}\"")) + } + Self::Chunk(error) => error.details(), Self::Lex(error) => Some(error.to_string()), Self::ParseIntError { error, .. } => Some(error.to_string()), } @@ -662,7 +682,8 @@ impl AnnotatedError for ParseError { Self::ExpectedToken { position, .. } => *position, Self::ExpectedTokenMultiple { position, .. } => *position, Self::InvalidAssignmentTarget { position, .. } => *position, - Self::Chunk { position, .. } => *position, + Self::UndefinedVariable { position, .. } => *position, + Self::Chunk(error) => error.position(), Self::Lex(error) => error.position(), Self::ParseIntError { position, .. } => *position, } @@ -677,7 +698,7 @@ impl From for ParseError { #[cfg(test)] mod tests { - use crate::{identifier_stack::Local, ValueLocation}; + use crate::Local; use super::*; @@ -711,14 +732,14 @@ mod tests { test_chunk, Ok(Chunk::with_data( vec![ - (Instruction::DefineVariable as u8, Span(4, 5)), - (0, Span(4, 5)), (Instruction::Constant as u8, Span(8, 10)), (0, Span(8, 10)), - (Instruction::DefineVariable as u8, Span(16, 17)), - (1, Span(16, 17)), + (Instruction::DeclareVariable as u8, Span(4, 5)), + (0, Span(4, 5)), (Instruction::Constant as u8, Span(20, 22)), (1, Span(20, 22)), + (Instruction::DeclareVariable as u8, Span(16, 17)), + (1, Span(16, 17)), (Instruction::GetVariable as u8, Span(24, 25)), (0, Span(24, 25)), (Instruction::GetVariable as u8, Span(28, 29)), @@ -731,12 +752,10 @@ mod tests { Local { identifier: Identifier::new("x"), depth: 0, - value_location: ValueLocation::ConstantStack, }, Local { identifier: Identifier::new("y"), depth: 0, - value_location: ValueLocation::ConstantStack, }, ], )) @@ -752,16 +771,15 @@ mod tests { test_chunk, Ok(Chunk::with_data( vec![ - (Instruction::DefineVariable as u8, Span(4, 5)), - (0, Span(4, 5)), (Instruction::Constant as u8, Span(8, 10)), (0, Span(8, 10)), + (Instruction::DeclareVariable as u8, Span(4, 5)), + (0, Span(4, 5)), ], vec![Value::integer(42)], vec![Local { identifier: Identifier::new("x"), depth: 0, - value_location: ValueLocation::ConstantStack, }], )) ); diff --git a/dust-lang/src/vm.rs b/dust-lang/src/vm.rs index 28f3c88..3c0ac90 100644 --- a/dust-lang/src/vm.rs +++ b/dust-lang/src/vm.rs @@ -1,8 +1,8 @@ -use std::rc::Rc; +use std::rc::{Rc, Weak}; use crate::{ dust_error::AnnotatedError, parse, Chunk, ChunkError, DustError, Identifier, Instruction, Span, - Value, ValueError, ValueLocation, + Value, ValueError, }; pub fn run(source: &str) -> Result, DustError> { @@ -18,7 +18,7 @@ pub fn run(source: &str) -> Result, DustError> { pub struct Vm { chunk: Rc, ip: usize, - stack: Vec, + stack: Vec, } impl Vm { @@ -50,16 +50,10 @@ impl Vm { log::trace!("Pushing constant {value}"); - self.push_runtime_value(value, position)?; + self.push(value, position)?; } Instruction::Return => { - let stacked = self.pop(position)?; - let value = match stacked { - StackedValue::Runtime(value) => value, - StackedValue::Constant(index) => Rc::get_mut(&mut self.chunk) - .unwrap() - .remove_constant(index, position)?, - }; + let value = self.pop(position)?; log::trace!("Returning {value}"); @@ -72,221 +66,168 @@ impl Vm { } // Variables - Instruction::DefineVariable => { + Instruction::DeclareVariable => { let (argument, _) = *self.read(position)?; - let identifier = self.chunk.get_identifier(argument)?.clone(); - let stack_index_option = self.chunk.resolve_local(&identifier); + let identifier = self.chunk.get_identifier(argument, position)?; + let value = self.stack.remove(argument as usize); - if let Some(index) = stack_index_option { - let value = self.stack[index as usize] - .to_value(&self.chunk, position)? - .clone(); + log::trace!("Declaring {identifier} as value {value}",); - log::trace!("Defining {identifier} as value {value}"); - - self.push_runtime_value(value, position)?; - } else { - return Err(VmError::UndefinedVariable { - identifier, - position, - }); - } + self.push(value, position)?; } Instruction::GetVariable => { let (argument, _) = *self.read(position)?; - let value = self.pop(position)?.to_value(&self.chunk, position)?.clone(); + let identifier = self.chunk.get_identifier(argument, position)?; + let value = self.stack.remove(argument as usize); - log::trace!( - "Getting {} as value {value}", - self.chunk.get_identifier(argument)?, - ); + log::trace!("Getting {identifier} as value {value}",); - self.push_runtime_value(value, position)?; + self.push(value, position)?; } Instruction::SetVariable => { let (argument, _) = *self.read(position)?; - let identifier = self.chunk.get_identifier(argument)?.clone(); + let identifier = self.chunk.get_identifier(argument, position)?.clone(); if !self.chunk.contains_identifier(&identifier) { return Err(VmError::UndefinedVariable { - identifier, + identifier: identifier.clone(), position, }); } - let value = self.stack[argument as usize] - .to_value(&self.chunk, position)? - .clone(); + let value = self.pop(position)?; log::trace!("Setting {identifier} to {value}"); - self.push_runtime_value(value, position)?; + self.stack[argument as usize] = value; } // Unary Instruction::Negate => { let negated = self .pop(position)? - .to_value(&self.chunk, position)? .negate() .map_err(|error| VmError::Value { error, position })?; - self.push_runtime_value(negated, position)?; + self.push(negated, position)?; } Instruction::Not => { let not = self .pop(position)? - .to_value(&self.chunk, position)? .not() .map_err(|error| VmError::Value { error, position })?; - self.push_runtime_value(not, position)?; + self.push(not, position)?; } // Binary Instruction::Add => { - let chunk = self.chunk.clone(); - let right_stacked = self.pop(position)?; - let right = right_stacked.to_value(chunk.as_ref(), position)?; - let left_stacked = self.pop(position)?; - let left = left_stacked.to_value(&self.chunk, position)?; + let right = self.pop(position)?; + let left = self.pop(position)?; let sum = left - .add(right) + .add(&right) .map_err(|error| VmError::Value { error, position })?; - self.push_runtime_value(sum, position)?; + self.push(sum, position)?; } Instruction::Subtract => { - let chunk = self.chunk.clone(); - let right_stacked = self.pop(position)?; - let right = right_stacked.to_value(chunk.as_ref(), position)?; - let left_stacked = self.pop(position)?; - let left = left_stacked.to_value(&self.chunk, position)?; + let right = self.pop(position)?; + let left = self.pop(position)?; let difference = left - .subtract(right) + .subtract(&right) .map_err(|error| VmError::Value { error, position })?; - self.push_runtime_value(difference, position)?; + self.push(difference, position)?; } Instruction::Multiply => { - let chunk = self.chunk.clone(); - let right_stacked = self.pop(position)?; - let right = right_stacked.to_value(chunk.as_ref(), position)?; - let left_stacked = self.pop(position)?; - let left = left_stacked.to_value(&self.chunk, position)?; + let right = self.pop(position)?; + let left = self.pop(position)?; let product = left - .multiply(right) + .multiply(&right) .map_err(|error| VmError::Value { error, position })?; - self.push_runtime_value(product, position)?; + self.push(product, position)?; } Instruction::Divide => { - let chunk = self.chunk.clone(); - let right_stacked = self.pop(position)?; - let right = right_stacked.to_value(chunk.as_ref(), position)?; - let left_stacked = self.pop(position)?; - let left = left_stacked.to_value(&self.chunk, position)?; + let right = self.pop(position)?; + let left = self.pop(position)?; let quotient = left - .divide(right) + .divide(&right) .map_err(|error| VmError::Value { error, position })?; - self.push_runtime_value(quotient, position)?; + self.push(quotient, position)?; } Instruction::Greater => { - let chunk = self.chunk.clone(); - let right_stacked = self.pop(position)?; - let right = right_stacked.to_value(chunk.as_ref(), position)?; - let left_stacked = self.pop(position)?; - let left = left_stacked.to_value(&self.chunk, position)?; + let right = self.pop(position)?; + let left = self.pop(position)?; let greater = left - .greater_than(right) + .greater_than(&right) .map_err(|error| VmError::Value { error, position })?; - self.push_runtime_value(greater, position)?; + self.push(greater, position)?; } Instruction::Less => { - let chunk = self.chunk.clone(); - let right_stacked = self.pop(position)?; - let right = right_stacked.to_value(chunk.as_ref(), position)?; - let left_stacked = self.pop(position)?; - let left = left_stacked.to_value(&self.chunk, position)?; + let right = self.pop(position)?; + let left = self.pop(position)?; let less = left - .less_than(right) + .less_than(&right) .map_err(|error| VmError::Value { error, position })?; - self.push_runtime_value(less, position)?; + self.push(less, position)?; } Instruction::GreaterEqual => { - let chunk = self.chunk.clone(); - let right_stacked = self.pop(position)?; - let right = right_stacked.to_value(chunk.as_ref(), position)?; - let left_stacked = self.pop(position)?; - let left = left_stacked.to_value(&self.chunk, position)?; + let right = self.pop(position)?; + let left = self.pop(position)?; let greater_equal = left - .greater_than_or_equal(right) + .greater_than_or_equal(&right) .map_err(|error| VmError::Value { error, position })?; - self.push_runtime_value(greater_equal, position)?; + self.push(greater_equal, position)?; } Instruction::LessEqual => { - let chunk = self.chunk.clone(); - let right_stacked = self.pop(position)?; - let right = right_stacked.to_value(chunk.as_ref(), position)?; - let left_stacked = self.pop(position)?; - let left = left_stacked.to_value(&self.chunk, position)?; + let right = self.pop(position)?; + let left = self.pop(position)?; let less_equal = left - .less_than_or_equal(right) + .less_than_or_equal(&right) .map_err(|error| VmError::Value { error, position })?; - self.push_runtime_value(less_equal, position)?; + self.push(less_equal, position)?; } Instruction::Equal => { - let chunk = self.chunk.clone(); - let right_stacked = self.pop(position)?; - let right = right_stacked.to_value(chunk.as_ref(), position)?; - let left_stacked = self.pop(position)?; - let left = left_stacked.to_value(&self.chunk, position)?; + let right = self.pop(position)?; + let left = self.pop(position)?; let equal = left - .equal(right) + .equal(&right) .map_err(|error| VmError::Value { error, position })?; - self.push_runtime_value(equal, position)?; + self.push(equal, position)?; } Instruction::NotEqual => { - let chunk = self.chunk.clone(); - let right_stacked = self.pop(position)?; - let right = right_stacked.to_value(chunk.as_ref(), position)?; - let left_stacked = self.pop(position)?; - let left = left_stacked.to_value(&self.chunk, position)?; + let right = self.pop(position)?; + let left = self.pop(position)?; let not_equal = left - .not_equal(right) + .not_equal(&right) .map_err(|error| VmError::Value { error, position })?; - self.push_runtime_value(not_equal, position)?; + self.push(not_equal, position)?; } Instruction::And => { - let chunk = self.chunk.clone(); - let right_stacked = self.pop(position)?; - let right = right_stacked.to_value(chunk.as_ref(), position)?; - let left_stacked = self.pop(position)?; - let left = left_stacked.to_value(&self.chunk, position)?; + let right = self.pop(position)?; + let left = self.pop(position)?; let and = left - .and(right) + .and(&right) .map_err(|error| VmError::Value { error, position })?; - self.push_runtime_value(and, position)?; + self.push(and, position)?; } Instruction::Or => { - let chunk = self.chunk.clone(); - let right_stacked = self.pop(position)?; - let right = right_stacked.to_value(chunk.as_ref(), position)?; - let left_stacked = self.pop(position)?; - let left = left_stacked.to_value(&self.chunk, position)?; + let right = self.pop(position)?; + let left = self.pop(position)?; let or = left - .or(right) + .or(&right) .map_err(|error| VmError::Value { error, position })?; - self.push_runtime_value(or, position)?; + self.push(or, position)?; } } } @@ -294,7 +235,7 @@ impl Vm { Ok(None) } - fn push_runtime_value(&mut self, value: Value, position: Span) -> Result<(), VmError> { + fn push(&mut self, value: Value, position: Span) -> Result<(), VmError> { if self.stack.len() == Self::STACK_SIZE { Err(VmError::StackOverflow(position)) } else { @@ -304,23 +245,13 @@ impl Vm { value }; - self.stack.push(StackedValue::Runtime(value)); + self.stack.push(value); Ok(()) } } - fn push_constant_value(&mut self, index: u8, position: Span) -> Result<(), VmError> { - if self.stack.len() == Self::STACK_SIZE { - Err(VmError::StackOverflow(position)) - } else { - self.stack.push(StackedValue::Constant(index)); - - Ok(()) - } - } - - fn pop(&mut self, position: Span) -> Result { + fn pop(&mut self, position: Span) -> Result { if let Some(stacked) = self.stack.pop() { Ok(stacked) } else { @@ -337,21 +268,6 @@ impl Vm { } } -#[derive(Debug, Clone, Eq, PartialEq)] -pub enum StackedValue { - Runtime(Value), - Constant(u8), -} - -impl StackedValue { - fn to_value<'a>(&'a self, chunk: &'a Chunk, position: Span) -> Result<&'a Value, VmError> { - match self { - Self::Runtime(value) => Ok(value), - Self::Constant(index) => Ok(chunk.get_constant(*index, position)?), - } - } -} - #[derive(Debug, Clone, PartialEq)] pub enum VmError { InvalidInstruction(u8, Span), @@ -402,8 +318,7 @@ impl AnnotatedError for VmError { Self::UndefinedVariable { identifier, .. } => { Some(format!("{identifier} is not in scope")) } - - Self::Chunk(error) => Some(error.to_string()), + Self::Chunk(error) => error.details(), Self::Value { error, .. } => Some(error.to_string()), } } @@ -427,12 +342,15 @@ pub mod tests { #[test] fn negation() { let mut chunk = Chunk::new(); - let constant = chunk.push_constant(Value::integer(42)).unwrap(); + let dummy_position = Span(0, 0); + let constant = chunk + .push_constant(Value::integer(42), dummy_position) + .unwrap(); - chunk.push_code(Instruction::Constant as u8, Span(0, 1)); - chunk.push_code(constant, Span(2, 3)); - chunk.push_code(Instruction::Negate as u8, Span(4, 5)); - chunk.push_code(Instruction::Return as u8, Span(2, 3)); + chunk.push_code(Instruction::Constant as u8, dummy_position); + chunk.push_code(constant, dummy_position); + chunk.push_code(Instruction::Negate as u8, dummy_position); + chunk.push_code(Instruction::Return as u8, dummy_position); let mut vm = Vm::new(chunk); let result = vm.run(); @@ -443,15 +361,20 @@ pub mod tests { #[test] fn addition() { let mut chunk = Chunk::new(); - let left = chunk.push_constant(Value::integer(42)).unwrap(); - let right = chunk.push_constant(Value::integer(23)).unwrap(); + let dummy_position = Span(0, 0); + let left = chunk + .push_constant(Value::integer(42), dummy_position) + .unwrap(); + let right = chunk + .push_constant(Value::integer(23), dummy_position) + .unwrap(); - chunk.push_code(Instruction::Constant as u8, Span(0, 1)); - chunk.push_code(left, Span(2, 3)); - chunk.push_code(Instruction::Constant as u8, Span(4, 5)); - chunk.push_code(right, Span(6, 7)); - chunk.push_code(Instruction::Add as u8, Span(8, 9)); - chunk.push_code(Instruction::Return as u8, Span(10, 11)); + chunk.push_code(Instruction::Constant, dummy_position); + chunk.push_code(left, dummy_position); + chunk.push_code(Instruction::Constant, dummy_position); + chunk.push_code(right, dummy_position); + chunk.push_code(Instruction::Add, dummy_position); + chunk.push_code(Instruction::Return, dummy_position); let mut vm = Vm::new(chunk); let result = vm.run(); @@ -462,15 +385,20 @@ pub mod tests { #[test] fn subtraction() { let mut chunk = Chunk::new(); - let left = chunk.push_constant(Value::integer(42)).unwrap(); - let right = chunk.push_constant(Value::integer(23)).unwrap(); + let dummy_position = Span(0, 0); + let left = chunk + .push_constant(Value::integer(42), dummy_position) + .unwrap(); + let right = chunk + .push_constant(Value::integer(23), dummy_position) + .unwrap(); - chunk.push_code(Instruction::Constant as u8, Span(0, 1)); - chunk.push_code(left, Span(2, 3)); - chunk.push_code(Instruction::Constant as u8, Span(4, 5)); - chunk.push_code(right, Span(6, 7)); - chunk.push_code(Instruction::Subtract as u8, Span(8, 9)); - chunk.push_code(Instruction::Return as u8, Span(10, 11)); + chunk.push_code(Instruction::Constant, dummy_position); + chunk.push_code(left, dummy_position); + chunk.push_code(Instruction::Constant, dummy_position); + chunk.push_code(right, dummy_position); + chunk.push_code(Instruction::Subtract, dummy_position); + chunk.push_code(Instruction::Return, dummy_position); let mut vm = Vm::new(chunk); let result = vm.run(); @@ -481,15 +409,20 @@ pub mod tests { #[test] fn multiplication() { let mut chunk = Chunk::new(); - let left = chunk.push_constant(Value::integer(42)).unwrap(); - let right = chunk.push_constant(Value::integer(23)).unwrap(); + let dummy_position = Span(0, 0); + let left = chunk + .push_constant(Value::integer(42), dummy_position) + .unwrap(); + let right = chunk + .push_constant(Value::integer(23), dummy_position) + .unwrap(); - chunk.push_code(Instruction::Constant as u8, Span(0, 1)); - chunk.push_code(left, Span(2, 3)); - chunk.push_code(Instruction::Constant as u8, Span(4, 5)); - chunk.push_code(right, Span(6, 7)); - chunk.push_code(Instruction::Multiply as u8, Span(8, 9)); - chunk.push_code(Instruction::Return as u8, Span(10, 11)); + chunk.push_code(Instruction::Constant, dummy_position); + chunk.push_code(left, dummy_position); + chunk.push_code(Instruction::Constant, dummy_position); + chunk.push_code(right, dummy_position); + chunk.push_code(Instruction::Multiply, dummy_position); + chunk.push_code(Instruction::Return, dummy_position); let mut vm = Vm::new(chunk); let result = vm.run(); @@ -501,15 +434,20 @@ pub mod tests { fn division() { let mut chunk = Chunk::new(); - let left = chunk.push_constant(Value::integer(42)).unwrap(); - let right = chunk.push_constant(Value::integer(23)).unwrap(); + let dummy_position = Span(0, 0); + let left = chunk + .push_constant(Value::integer(42), dummy_position) + .unwrap(); + let right = chunk + .push_constant(Value::integer(23), dummy_position) + .unwrap(); - chunk.push_code(Instruction::Constant as u8, Span(0, 1)); - chunk.push_code(left, Span(2, 3)); - chunk.push_code(Instruction::Constant as u8, Span(4, 5)); - chunk.push_code(right, Span(6, 7)); - chunk.push_code(Instruction::Divide as u8, Span(8, 9)); - chunk.push_code(Instruction::Return as u8, Span(10, 11)); + chunk.push_code(Instruction::Constant, dummy_position); + chunk.push_code(left, dummy_position); + chunk.push_code(Instruction::Constant, dummy_position); + chunk.push_code(right, dummy_position); + chunk.push_code(Instruction::Divide, dummy_position); + chunk.push_code(Instruction::Return, dummy_position); let mut vm = Vm::new(chunk); let result = vm.run();