diff --git a/dust-lang/src/chunk.rs b/dust-lang/src/chunk.rs index 7d111ea..7da8b96 100644 --- a/dust-lang/src/chunk.rs +++ b/dust-lang/src/chunk.rs @@ -1,6 +1,7 @@ use std::{ env::current_exe, fmt::{self, Debug, Display}, + rc::Weak, }; use colored::Colorize; @@ -14,7 +15,8 @@ pub struct Chunk { instructions: Vec<(Instruction, Span)>, constants: Vec, locals: Vec, - scope_depth: usize, + current_scope: Scope, + block_count: usize, } impl Chunk { @@ -24,7 +26,8 @@ impl Chunk { instructions: Vec::new(), constants: Vec::new(), locals: Vec::new(), - scope_depth: 0, + current_scope: Scope::default(), + block_count: 0, } } @@ -39,7 +42,8 @@ impl Chunk { instructions, constants, locals, - scope_depth: 0, + current_scope: Scope::default(), + block_count: 0, } } @@ -87,8 +91,8 @@ impl Chunk { &mut self.locals } - pub fn scope_depth(&self) -> usize { - self.scope_depth + pub fn current_scope(&self) -> Scope { + self.current_scope } pub fn get_constant(&self, index: u8) -> Option<&Value> { @@ -118,11 +122,19 @@ impl Chunk { } pub fn begin_scope(&mut self) { - self.scope_depth += 1; + self.current_scope.depth += 1; + self.current_scope.block = self.block_count; } pub fn end_scope(&mut self) { - self.scope_depth -= 1; + self.current_scope.depth -= 1; + + if self.current_scope.depth == 0 { + self.block_count += 1; + self.current_scope.block = 0; + } else { + self.current_scope.block = self.block_count; + }; } pub fn disassembler(&self) -> ChunkDisassembler { @@ -163,7 +175,7 @@ pub struct Local { pub identifier_index: u8, pub r#type: Option, pub is_mutable: bool, - pub depth: usize, + pub scope: Scope, pub register_index: u8, } @@ -172,19 +184,39 @@ impl Local { identifier_index: u8, r#type: Option, mutable: bool, - depth: usize, + scope: Scope, register_index: u8, ) -> Self { Self { identifier_index, r#type, is_mutable: mutable, - depth, + scope, register_index, } } } +#[derive(Debug, Clone, Copy, Default, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] +pub struct Scope { + /// The level of block nesting. + pub depth: usize, + /// The nth top-level block in the chunk. + pub block: usize, +} + +impl Scope { + pub fn new(depth: usize, block: usize) -> Self { + Self { depth, block } + } +} + +impl Display for Scope { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "({}, {})", self.depth, self.block) + } +} + pub struct ChunkDisassembler<'a> { chunk: &'a Chunk, source: Option<&'a str>, @@ -207,7 +239,7 @@ impl<'a> ChunkDisassembler<'a> { const LOCAL_HEADER: [&'static str; 4] = [ "Locals", "------", - "INDEX IDENTIFIER TYPE MUTABLE DEPTH REGISTER", + "INDEX IDENTIFIER TYPE MUTABLE SCOPE REGISTER", "----- ---------- -------- ------- ----- --------", ]; @@ -429,7 +461,7 @@ impl<'a> ChunkDisassembler<'a> { Local { identifier_index, r#type, - depth, + scope: depth, register_index, is_mutable: mutable, }, diff --git a/dust-lang/src/instruction.rs b/dust-lang/src/instruction.rs index a1262b2..4b0cfec 100644 --- a/dust-lang/src/instruction.rs +++ b/dust-lang/src/instruction.rs @@ -454,7 +454,7 @@ impl Instruction { }; let mutable_display = if self.c_as_boolean() { "mut" } else { "" }; - format!("L{local_index} = R{to_register} {mutable_display} {identifier_display}") + format!("R{to_register} = L{local_index} {mutable_display} {identifier_display}") } Operation::GetLocal => { let local_index = self.b(); diff --git a/dust-lang/src/lib.rs b/dust-lang/src/lib.rs index 4f68d6b..b92bde1 100644 --- a/dust-lang/src/lib.rs +++ b/dust-lang/src/lib.rs @@ -11,7 +11,7 @@ mod r#type; mod value; mod vm; -pub use chunk::{Chunk, ChunkDisassembler, Local}; +pub use chunk::{Chunk, ChunkDisassembler, Local, Scope}; pub use dust_error::{AnnotatedError, DustError}; pub use formatter::{format, Formatter}; pub use instruction::Instruction; diff --git a/dust-lang/src/parser.rs b/dust-lang/src/parser.rs index bd4f249..1c03b08 100644 --- a/dust-lang/src/parser.rs +++ b/dust-lang/src/parser.rs @@ -10,7 +10,7 @@ use serde::{Deserialize, Serialize}; use crate::{ AnnotatedError, Chunk, DustError, FunctionType, Instruction, LexError, Lexer, Local, - NativeFunction, Operation, Span, Token, TokenKind, TokenOwned, Type, Value, + NativeFunction, Operation, Scope, Span, Token, TokenKind, TokenOwned, Type, Value, }; pub fn parse(source: &str) -> Result { @@ -149,11 +149,11 @@ impl<'src> Parser<'src> { identifier: &str, r#type: Option, is_mutable: bool, + scope: Scope, 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); @@ -161,7 +161,7 @@ impl<'src> Parser<'src> { identifier_index, r#type, is_mutable, - scope_depth, + scope, register_index, )); @@ -823,9 +823,10 @@ impl<'src> Parser<'src> { } else if let Some(name) = self.chunk.name() { if name.as_str() == text { let register = self.next_register(); + let scope = self.chunk.current_scope(); self.emit_instruction(Instruction::load_self(register), start_position); - self.declare_local(text, None, false, register); + self.declare_local(text, None, false, scope, register); self.current_is_expression = true; @@ -1317,6 +1318,7 @@ impl<'src> Parser<'src> { self.advance()?; + let scope = self.chunk.current_scope(); let is_mutable = self.allow(Token::Mut)?; let position = self.current_position; let identifier = if let Token::Identifier(text) = self.current_token { @@ -1344,7 +1346,7 @@ impl<'src> Parser<'src> { self.expect(Token::Equal)?; self.parse_expression()?; - let (local_index, _) = self.declare_local(identifier, r#type, is_mutable, register); + let (local_index, _) = self.declare_local(identifier, r#type, is_mutable, scope, register); let register = self.next_register().saturating_sub(1); self.emit_instruction( @@ -1403,10 +1405,12 @@ impl<'src> Parser<'src> { .as_ref() .map(|values| values.len() as u8) .unwrap_or(0); + let scope = function_parser.chunk.current_scope(); let (_, identifier_index) = function_parser.declare_local( parameter, Some(r#type.clone()), is_mutable, + scope, register, ); @@ -1456,10 +1460,12 @@ impl<'src> Parser<'src> { if let Some((identifier, identifier_position)) = identifier { let register = self.next_register(); + let scope = self.chunk.current_scope(); let (local_index, _) = self.declare_local( identifier, Some(Type::Function(function_type)), false, + scope, register, ); diff --git a/dust-lang/tests/control_flow.rs b/dust-lang/tests/control_flow.rs index c4769f0..8f84bae 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), Value::string("a")], - vec![Local::new(1, None, false, 0, 0)] + vec![Local::new(1, None, false, Scope { depth: 0, block: 0 }, 0)] )), ); @@ -53,7 +53,7 @@ fn equality_assignment_short() { (Instruction::r#return(true), Span(16, 16)), ], vec![Value::integer(4), Value::string("a")], - vec![Local::new(1, None, false, 0, 0)] + vec![Local::new(1, None, false, Scope::default(), 0)] )), ); @@ -112,7 +112,7 @@ fn if_else_assigment() { Value::integer(42), Value::string("a") ], - vec![Local::new(5, None, false, 0, 0)] + vec![Local::new(5, None, false, Scope::default(), 0)] )), ); diff --git a/dust-lang/tests/expressions.rs b/dust-lang/tests/expressions.rs index fda1e82..a66fc2c 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::string("a"), Value::integer(2)], - vec![Local::new(1, None, true, 0, 0)] + vec![Local::new(1, None, true, Scope::default(), 0)] )) ); @@ -82,7 +82,7 @@ fn define_local() { (Instruction::r#return(false), Span(11, 11)) ], vec![Value::integer(42), Value::string("x")], - vec![Local::new(1, None, false, 0, 0)] + vec![Local::new(1, None, false, Scope::default(), 0)] )), ); @@ -133,7 +133,7 @@ fn divide_assign() { (Instruction::r#return(true), Span(24, 24)) ], vec![Value::integer(2), Value::string("a")], - vec![Local::new(1, None, true, 0, 0)] + vec![Local::new(1, None, true, Scope::default(), 0)] )) ); @@ -359,7 +359,7 @@ fn multiply_assign() { (Instruction::r#return(true), Span(23, 23)) ], vec![Value::integer(2), Value::string("a"), Value::integer(3)], - vec![Local::new(1, None, true, 0, 0),] + vec![Local::new(1, None, true, Scope::default(), 0),] )) ); @@ -453,7 +453,7 @@ fn set_local() { (Instruction::r#return(true), Span(25, 25)), ], vec![Value::integer(41), Value::string("x"), Value::integer(42)], - vec![Local::new(1, None, true, 0, 0)] + vec![Local::new(1, None, true, Scope::default(), 0)] )), ); @@ -504,7 +504,7 @@ fn subtract_assign() { (Instruction::r#return(true), Span(25, 25)), ], vec![Value::integer(42), Value::string("x"), Value::integer(2)], - vec![Local::new(1, None, true, 0, 0)] + vec![Local::new(1, None, true, Scope::default(), 0)] )), ); @@ -532,8 +532,8 @@ fn variable_and() { ], vec![Value::string("a"), Value::string("b"),], vec![ - Local::new(0, None, false, 0, 0), - Local::new(1, None, false, 0, 1), + Local::new(0, None, false, Scope::default(), 0), + Local::new(1, None, false, Scope::default(), 1), ] )) ); @@ -568,7 +568,7 @@ fn r#while() { Value::integer(5), Value::integer(1), ], - vec![Local::new(1, None, true, 0, 0),] + vec![Local::new(1, None, true, Scope::default(), 0),] )), ); diff --git a/dust-lang/tests/functions.rs b/dust-lang/tests/functions.rs index 0717f4e..792f885 100644 --- a/dust-lang/tests/functions.rs +++ b/dust-lang/tests/functions.rs @@ -15,8 +15,8 @@ fn function() { ], vec![Value::string("a"), Value::string("b"),], vec![ - Local::new(0, Some(Type::Integer), false, 0, 0), - Local::new(1, Some(Type::Integer), false, 0, 1) + Local::new(0, Some(Type::Integer), false, Scope::default(), 0), + Local::new(1, Some(Type::Integer), false, Scope::default(), 1) ] ), FunctionType { @@ -53,8 +53,8 @@ fn function_call() { ], vec![Value::string("a"), Value::string("b"),], vec![ - Local::new(0, Some(Type::Integer), false, 0, 0), - Local::new(1, Some(Type::Integer), false, 0, 1) + Local::new(0, Some(Type::Integer), false, Scope::default(), 0), + Local::new(1, Some(Type::Integer), false, Scope::default(), 1) ] ), FunctionType { @@ -97,8 +97,8 @@ fn function_declaration() { ], vec![Value::string("a"), Value::string("b")], vec![ - Local::new(0, Some(Type::Integer), false, 0, 0), - Local::new(1, Some(Type::Integer), false, 0, 1) + Local::new(0, Some(Type::Integer), false, Scope::default(), 0), + Local::new(1, Some(Type::Integer), false, Scope::default(), 1) ] ), FunctionType { @@ -116,7 +116,7 @@ fn function_declaration() { return_type: Some(Box::new(Type::Integer)), })), false, - 0, + Scope::default(), 0 ),], )), diff --git a/dust-lang/tests/scopes.rs b/dust-lang/tests/scopes.rs index fdc73c2..5603de3 100644 --- a/dust-lang/tests/scopes.rs +++ b/dust-lang/tests/scopes.rs @@ -55,11 +55,11 @@ fn block_scope() { Value::string("e"), ], vec![ - Local::new(1, None, false, 0, 0), - Local::new(3, None, false, 1, 1), - Local::new(5, None, false, 2, 2), - Local::new(7, None, false, 1, 3), - Local::new(8, None, false, 0, 4), + Local::new(1, None, false, Scope::new(0, 0), 0), + Local::new(3, None, false, Scope::new(1, 0), 1), + Local::new(5, None, false, Scope::new(2, 0), 2), + Local::new(7, None, false, Scope::new(1, 0), 3), + Local::new(8, None, false, Scope::new(0, 0), 4), ] )), ); @@ -67,6 +67,81 @@ fn block_scope() { assert_eq!(run(source), Ok(None)); } +#[test] +fn multiple_block_scopes() { + let source = " + let a = 0; + { + let b = 42; + { + let c = 1; + } + let d = 2; + } + let q = 42; + { + let b = 42; + { + let c = 1; + } + let d = 2; + } + let e = 1; + "; + + assert_eq!( + parse(source), + Ok(Chunk::with_data( + None, + vec![ + (Instruction::load_constant(0, 0, false), Span(17, 18)), + (Instruction::define_local(0, 0, false), Span(13, 14)), + (Instruction::load_constant(1, 2, false), Span(50, 52)), + (Instruction::define_local(1, 1, false), Span(46, 47)), + (Instruction::load_constant(2, 4, false), Span(92, 93)), + (Instruction::define_local(2, 2, false), Span(88, 89)), + (Instruction::load_constant(3, 6, false), Span(129, 130)), + (Instruction::define_local(3, 3, false), Span(125, 126)), + (Instruction::load_constant(4, 2, false), Span(158, 160)), + (Instruction::define_local(4, 4, false), Span(154, 155)), + (Instruction::load_constant(5, 2, false), Span(192, 194)), + (Instruction::define_local(5, 5, false), Span(188, 189)), + (Instruction::load_constant(6, 4, false), Span(234, 235)), + (Instruction::define_local(6, 6, false), Span(230, 231)), + (Instruction::load_constant(7, 6, false), Span(271, 272)), + (Instruction::define_local(7, 7, false), Span(267, 268)), + (Instruction::load_constant(8, 4, false), Span(300, 301)), + (Instruction::define_local(8, 8, false), Span(296, 297)), + (Instruction::r#return(false), Span(307, 307)) + ], + vec![ + Value::integer(0), + Value::string("a"), + Value::integer(42), + Value::string("b"), + Value::integer(1), + Value::string("c"), + Value::integer(2), + Value::string("d"), + Value::string("q"), + Value::string("e"), + ], + vec![ + Local::new(1, None, false, Scope::new(0, 0), 0), + Local::new(3, None, false, Scope::new(1, 0), 1), + Local::new(5, None, false, Scope::new(2, 0), 2), + Local::new(7, None, false, Scope::new(1, 0), 3), + Local::new(8, None, false, Scope::new(0, 0), 4), + Local::new(3, None, false, Scope::new(1, 1), 5), + Local::new(5, None, false, Scope::new(2, 1), 6), + Local::new(7, None, false, Scope::new(1, 1), 7), + Local::new(9, None, false, Scope::new(0, 0), 8), + ] + )), + ); + + assert_eq!(run(source), Ok(None)); +} // #[test] // fn disallow_access_to_child_scope() { // let source = r#"