1
0

Rework how scopes work to support excluding sibling scopes

This commit is contained in:
Jeff 2024-11-05 12:44:16 -05:00
parent 0dcfcd5375
commit 8c72e921dc
8 changed files with 156 additions and 43 deletions

View File

@ -1,6 +1,7 @@
use std::{ use std::{
env::current_exe, env::current_exe,
fmt::{self, Debug, Display}, fmt::{self, Debug, Display},
rc::Weak,
}; };
use colored::Colorize; use colored::Colorize;
@ -14,7 +15,8 @@ pub struct Chunk {
instructions: Vec<(Instruction, Span)>, instructions: Vec<(Instruction, Span)>,
constants: Vec<Value>, constants: Vec<Value>,
locals: Vec<Local>, locals: Vec<Local>,
scope_depth: usize, current_scope: Scope,
block_count: usize,
} }
impl Chunk { impl Chunk {
@ -24,7 +26,8 @@ impl Chunk {
instructions: Vec::new(), instructions: Vec::new(),
constants: Vec::new(), constants: Vec::new(),
locals: Vec::new(), locals: Vec::new(),
scope_depth: 0, current_scope: Scope::default(),
block_count: 0,
} }
} }
@ -39,7 +42,8 @@ impl Chunk {
instructions, instructions,
constants, constants,
locals, locals,
scope_depth: 0, current_scope: Scope::default(),
block_count: 0,
} }
} }
@ -87,8 +91,8 @@ impl Chunk {
&mut self.locals &mut self.locals
} }
pub fn scope_depth(&self) -> usize { pub fn current_scope(&self) -> Scope {
self.scope_depth self.current_scope
} }
pub fn get_constant(&self, index: u8) -> Option<&Value> { pub fn get_constant(&self, index: u8) -> Option<&Value> {
@ -118,11 +122,19 @@ impl Chunk {
} }
pub fn begin_scope(&mut self) { 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) { 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 { pub fn disassembler(&self) -> ChunkDisassembler {
@ -163,7 +175,7 @@ pub struct Local {
pub identifier_index: u8, pub identifier_index: u8,
pub r#type: Option<Type>, pub r#type: Option<Type>,
pub is_mutable: bool, pub is_mutable: bool,
pub depth: usize, pub scope: Scope,
pub register_index: u8, pub register_index: u8,
} }
@ -172,19 +184,39 @@ impl Local {
identifier_index: u8, identifier_index: u8,
r#type: Option<Type>, r#type: Option<Type>,
mutable: bool, mutable: bool,
depth: usize, scope: Scope,
register_index: u8, register_index: u8,
) -> Self { ) -> Self {
Self { Self {
identifier_index, identifier_index,
r#type, r#type,
is_mutable: mutable, is_mutable: mutable,
depth, scope,
register_index, 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> { pub struct ChunkDisassembler<'a> {
chunk: &'a Chunk, chunk: &'a Chunk,
source: Option<&'a str>, source: Option<&'a str>,
@ -207,7 +239,7 @@ impl<'a> ChunkDisassembler<'a> {
const LOCAL_HEADER: [&'static str; 4] = [ const LOCAL_HEADER: [&'static str; 4] = [
"Locals", "Locals",
"------", "------",
"INDEX IDENTIFIER TYPE MUTABLE DEPTH REGISTER", "INDEX IDENTIFIER TYPE MUTABLE SCOPE REGISTER",
"----- ---------- -------- ------- ----- --------", "----- ---------- -------- ------- ----- --------",
]; ];
@ -429,7 +461,7 @@ impl<'a> ChunkDisassembler<'a> {
Local { Local {
identifier_index, identifier_index,
r#type, r#type,
depth, scope: depth,
register_index, register_index,
is_mutable: mutable, is_mutable: mutable,
}, },

View File

@ -454,7 +454,7 @@ impl Instruction {
}; };
let mutable_display = if self.c_as_boolean() { "mut" } else { "" }; 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 => { Operation::GetLocal => {
let local_index = self.b(); let local_index = self.b();

View File

@ -11,7 +11,7 @@ mod r#type;
mod value; mod value;
mod vm; mod vm;
pub use chunk::{Chunk, ChunkDisassembler, Local}; pub use chunk::{Chunk, ChunkDisassembler, Local, Scope};
pub use dust_error::{AnnotatedError, DustError}; pub use dust_error::{AnnotatedError, DustError};
pub use formatter::{format, Formatter}; pub use formatter::{format, Formatter};
pub use instruction::Instruction; pub use instruction::Instruction;

View File

@ -10,7 +10,7 @@ use serde::{Deserialize, Serialize};
use crate::{ use crate::{
AnnotatedError, Chunk, DustError, FunctionType, Instruction, LexError, Lexer, Local, 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<Chunk, DustError> { pub fn parse(source: &str) -> Result<Chunk, DustError> {
@ -149,11 +149,11 @@ impl<'src> Parser<'src> {
identifier: &str, identifier: &str,
r#type: Option<Type>, r#type: Option<Type>,
is_mutable: bool, is_mutable: bool,
scope: Scope,
register_index: u8, register_index: u8,
) -> (u8, u8) { ) -> (u8, u8) {
log::debug!("Declare local {identifier}"); log::debug!("Declare local {identifier}");
let scope_depth = self.chunk.scope_depth();
let identifier = Value::string(identifier); let identifier = Value::string(identifier);
let identifier_index = self.chunk.push_or_get_constant(identifier); let identifier_index = self.chunk.push_or_get_constant(identifier);
@ -161,7 +161,7 @@ impl<'src> Parser<'src> {
identifier_index, identifier_index,
r#type, r#type,
is_mutable, is_mutable,
scope_depth, scope,
register_index, register_index,
)); ));
@ -823,9 +823,10 @@ impl<'src> Parser<'src> {
} else if let Some(name) = self.chunk.name() { } else if let Some(name) = self.chunk.name() {
if name.as_str() == text { if name.as_str() == text {
let register = self.next_register(); let register = self.next_register();
let scope = self.chunk.current_scope();
self.emit_instruction(Instruction::load_self(register), start_position); 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; self.current_is_expression = true;
@ -1317,6 +1318,7 @@ impl<'src> Parser<'src> {
self.advance()?; self.advance()?;
let scope = self.chunk.current_scope();
let is_mutable = self.allow(Token::Mut)?; let is_mutable = self.allow(Token::Mut)?;
let position = self.current_position; let position = self.current_position;
let identifier = if let Token::Identifier(text) = self.current_token { let identifier = if let Token::Identifier(text) = self.current_token {
@ -1344,7 +1346,7 @@ impl<'src> Parser<'src> {
self.expect(Token::Equal)?; self.expect(Token::Equal)?;
self.parse_expression()?; 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); let register = self.next_register().saturating_sub(1);
self.emit_instruction( self.emit_instruction(
@ -1403,10 +1405,12 @@ impl<'src> Parser<'src> {
.as_ref() .as_ref()
.map(|values| values.len() as u8) .map(|values| values.len() as u8)
.unwrap_or(0); .unwrap_or(0);
let scope = function_parser.chunk.current_scope();
let (_, identifier_index) = function_parser.declare_local( let (_, identifier_index) = function_parser.declare_local(
parameter, parameter,
Some(r#type.clone()), Some(r#type.clone()),
is_mutable, is_mutable,
scope,
register, register,
); );
@ -1456,10 +1460,12 @@ impl<'src> Parser<'src> {
if let Some((identifier, identifier_position)) = identifier { if let Some((identifier, identifier_position)) = identifier {
let register = self.next_register(); let register = self.next_register();
let scope = self.chunk.current_scope();
let (local_index, _) = self.declare_local( let (local_index, _) = self.declare_local(
identifier, identifier,
Some(Type::Function(function_type)), Some(Type::Function(function_type)),
false, false,
scope,
register, register,
); );

View File

@ -23,7 +23,7 @@ fn equality_assignment_long() {
(Instruction::r#return(true), Span(44, 44)), (Instruction::r#return(true), Span(44, 44)),
], ],
vec![Value::integer(4), Value::string("a")], 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)), (Instruction::r#return(true), Span(16, 16)),
], ],
vec![Value::integer(4), Value::string("a")], 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::integer(42),
Value::string("a") Value::string("a")
], ],
vec![Local::new(5, None, false, 0, 0)] vec![Local::new(5, None, false, Scope::default(), 0)]
)), )),
); );

View File

@ -41,7 +41,7 @@ fn add_assign() {
(Instruction::r#return(true), Span(24, 24)) (Instruction::r#return(true), Span(24, 24))
], ],
vec![Value::integer(1), Value::string("a"), Value::integer(2)], 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)) (Instruction::r#return(false), Span(11, 11))
], ],
vec![Value::integer(42), Value::string("x")], 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)) (Instruction::r#return(true), Span(24, 24))
], ],
vec![Value::integer(2), Value::string("a")], 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)) (Instruction::r#return(true), Span(23, 23))
], ],
vec![Value::integer(2), Value::string("a"), Value::integer(3)], 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)), (Instruction::r#return(true), Span(25, 25)),
], ],
vec![Value::integer(41), Value::string("x"), Value::integer(42)], 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)), (Instruction::r#return(true), Span(25, 25)),
], ],
vec![Value::integer(42), Value::string("x"), Value::integer(2)], 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![Value::string("a"), Value::string("b"),],
vec![ vec![
Local::new(0, None, false, 0, 0), Local::new(0, None, false, Scope::default(), 0),
Local::new(1, None, false, 0, 1), Local::new(1, None, false, Scope::default(), 1),
] ]
)) ))
); );
@ -568,7 +568,7 @@ fn r#while() {
Value::integer(5), Value::integer(5),
Value::integer(1), Value::integer(1),
], ],
vec![Local::new(1, None, true, 0, 0),] vec![Local::new(1, None, true, Scope::default(), 0),]
)), )),
); );

View File

@ -15,8 +15,8 @@ fn function() {
], ],
vec![Value::string("a"), Value::string("b"),], vec![Value::string("a"), Value::string("b"),],
vec![ vec![
Local::new(0, Some(Type::Integer), false, 0, 0), Local::new(0, Some(Type::Integer), false, Scope::default(), 0),
Local::new(1, Some(Type::Integer), false, 0, 1) Local::new(1, Some(Type::Integer), false, Scope::default(), 1)
] ]
), ),
FunctionType { FunctionType {
@ -53,8 +53,8 @@ fn function_call() {
], ],
vec![Value::string("a"), Value::string("b"),], vec![Value::string("a"), Value::string("b"),],
vec![ vec![
Local::new(0, Some(Type::Integer), false, 0, 0), Local::new(0, Some(Type::Integer), false, Scope::default(), 0),
Local::new(1, Some(Type::Integer), false, 0, 1) Local::new(1, Some(Type::Integer), false, Scope::default(), 1)
] ]
), ),
FunctionType { FunctionType {
@ -97,8 +97,8 @@ fn function_declaration() {
], ],
vec![Value::string("a"), Value::string("b")], vec![Value::string("a"), Value::string("b")],
vec![ vec![
Local::new(0, Some(Type::Integer), false, 0, 0), Local::new(0, Some(Type::Integer), false, Scope::default(), 0),
Local::new(1, Some(Type::Integer), false, 0, 1) Local::new(1, Some(Type::Integer), false, Scope::default(), 1)
] ]
), ),
FunctionType { FunctionType {
@ -116,7 +116,7 @@ fn function_declaration() {
return_type: Some(Box::new(Type::Integer)), return_type: Some(Box::new(Type::Integer)),
})), })),
false, false,
0, Scope::default(),
0 0
),], ),],
)), )),

View File

@ -55,11 +55,11 @@ fn block_scope() {
Value::string("e"), Value::string("e"),
], ],
vec![ vec![
Local::new(1, None, false, 0, 0), Local::new(1, None, false, Scope::new(0, 0), 0),
Local::new(3, None, false, 1, 1), Local::new(3, None, false, Scope::new(1, 0), 1),
Local::new(5, None, false, 2, 2), Local::new(5, None, false, Scope::new(2, 0), 2),
Local::new(7, None, false, 1, 3), Local::new(7, None, false, Scope::new(1, 0), 3),
Local::new(8, None, false, 0, 4), Local::new(8, None, false, Scope::new(0, 0), 4),
] ]
)), )),
); );
@ -67,6 +67,81 @@ fn block_scope() {
assert_eq!(run(source), Ok(None)); 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] // #[test]
// fn disallow_access_to_child_scope() { // fn disallow_access_to_child_scope() {
// let source = r#" // let source = r#"