Rework how scopes work to support excluding sibling scopes
This commit is contained in:
parent
0dcfcd5375
commit
8c72e921dc
@ -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,
|
||||
},
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
);
|
||||
|
||||
|
@ -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)]
|
||||
)),
|
||||
);
|
||||
|
||||
|
@ -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),]
|
||||
)),
|
||||
);
|
||||
|
||||
|
@ -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
|
||||
),],
|
||||
)),
|
||||
|
@ -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#"
|
||||
|
Loading…
Reference in New Issue
Block a user