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::{
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<Value>,
locals: Vec<Local>,
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<Type>,
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<Type>,
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,
},

View File

@ -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();

View File

@ -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;

View File

@ -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<Chunk, DustError> {
@ -149,11 +149,11 @@ impl<'src> Parser<'src> {
identifier: &str,
r#type: Option<Type>,
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,
);

View File

@ -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)]
)),
);

View File

@ -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),]
)),
);

View File

@ -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
),],
)),

View File

@ -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#"