Resolver - Keep track of the container scope's type
This commit is contained in:
parent
47c4136c25
commit
0a581f042d
1 changed files with 57 additions and 17 deletions
|
@ -15,7 +15,7 @@ pub type ResolvedVariables = HashMap<usize, usize>;
|
||||||
pub fn resolve_variables(program: &ProgramNode) -> SloxResult<ResolvedVariables> {
|
pub fn resolve_variables(program: &ProgramNode) -> SloxResult<ResolvedVariables> {
|
||||||
let mut state = ResolverState::default();
|
let mut state = ResolverState::default();
|
||||||
state
|
state
|
||||||
.with_scope(|rs| program.resolve(rs))
|
.with_scope(|rs| program.resolve(rs), ScopeType::TopLevel)
|
||||||
.map(|_| state.resolved)
|
.map(|_| state.resolved)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,6 +41,14 @@ enum SymKind {
|
||||||
This,
|
This,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The type of a scope
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
enum ScopeType {
|
||||||
|
TopLevel,
|
||||||
|
Function,
|
||||||
|
Method,
|
||||||
|
}
|
||||||
|
|
||||||
/// General information about a symbol.
|
/// General information about a symbol.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
struct SymInfo<'a> {
|
struct SymInfo<'a> {
|
||||||
|
@ -49,33 +57,59 @@ struct SymInfo<'a> {
|
||||||
state: SymState,
|
state: SymState,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A resolver scope.
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
struct SymScope<'a> {
|
||||||
|
/// The type of scope we are in.
|
||||||
|
scope_type: ScopeType,
|
||||||
|
/// The symbols that are defined inside the current scope.
|
||||||
|
symbols: HashMap<String, SymInfo<'a>>,
|
||||||
|
}
|
||||||
|
|
||||||
/// The state of the resolver.
|
/// The state of the resolver.
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct ResolverState<'a> {
|
struct ResolverState<'a> {
|
||||||
/// The stack of scopes. Each scope maps symbols to information which
|
/// The stack of scopes. Each scope maps symbols to information which
|
||||||
/// includes the kind of symbol it is and its current state.
|
/// includes the kind of symbol it is and its current state.
|
||||||
scopes: Vec<HashMap<String, SymInfo<'a>>>,
|
scopes: Vec<SymScope<'a>>,
|
||||||
/// The result of the resolver pass.
|
/// The result of the resolver pass.
|
||||||
resolved: ResolvedVariables,
|
resolved: ResolvedVariables,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> SymScope<'a> {
|
||||||
|
/// Initialize a new scope of the specified type.
|
||||||
|
fn new(scope_type: ScopeType) -> Self {
|
||||||
|
Self {
|
||||||
|
scope_type,
|
||||||
|
symbols: HashMap::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> ResolverState<'a> {
|
impl<'a> ResolverState<'a> {
|
||||||
/// Execute some function with a new scope. The scope will be disposed
|
/// Execute some function with a new scope. The scope will be disposed
|
||||||
/// of after the function has been executed.
|
/// of after the function has been executed.
|
||||||
fn with_scope<F>(&mut self, f: F) -> ResolverResult
|
fn with_scope<F>(&mut self, f: F, scope_type: ScopeType) -> ResolverResult
|
||||||
where
|
where
|
||||||
F: FnOnce(&mut Self) -> ResolverResult,
|
F: FnOnce(&mut Self) -> ResolverResult,
|
||||||
{
|
{
|
||||||
self.scopes.push(HashMap::new());
|
self.scopes.push(SymScope::new(scope_type));
|
||||||
let result = f(self).and_then(|_| self.check_unused());
|
let result = f(self).and_then(|_| self.check_unused());
|
||||||
self.scopes.pop();
|
self.scopes.pop();
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the type of the current scope.
|
||||||
|
fn current_type(&self) -> ScopeType {
|
||||||
|
let pos = self.scopes.len() - 1;
|
||||||
|
self.scopes[pos].scope_type
|
||||||
|
}
|
||||||
|
|
||||||
/// Check for unused symbols in the scope. If an unused symbol is found and
|
/// Check for unused symbols in the scope. If an unused symbol is found and
|
||||||
/// its name does not begin with an underscore, generate an error.
|
/// its name does not begin with an underscore, generate an error.
|
||||||
fn check_unused(&self) -> ResolverResult {
|
fn check_unused(&self) -> ResolverResult {
|
||||||
self.scopes[self.scopes.len() - 1]
|
self.scopes[self.scopes.len() - 1]
|
||||||
|
.symbols
|
||||||
.values()
|
.values()
|
||||||
.filter(|v| v.state != SymState::Used)
|
.filter(|v| v.state != SymState::Used)
|
||||||
.filter(|v| v.decl.is_some())
|
.filter(|v| v.decl.is_some())
|
||||||
|
@ -97,14 +131,14 @@ impl<'a> ResolverState<'a> {
|
||||||
assert!(!self.scopes.is_empty());
|
assert!(!self.scopes.is_empty());
|
||||||
let idx = self.scopes.len() - 1;
|
let idx = self.scopes.len() - 1;
|
||||||
let scope = &mut self.scopes[idx];
|
let scope = &mut self.scopes[idx];
|
||||||
if scope.contains_key(&name.lexeme as &str) {
|
if scope.symbols.contains_key(&name.lexeme as &str) {
|
||||||
Err(SloxError::with_token(
|
Err(SloxError::with_token(
|
||||||
ErrorKind::Parse,
|
ErrorKind::Parse,
|
||||||
name,
|
name,
|
||||||
"already a symbol with this name in this scope".to_owned(),
|
"already a symbol with this name in this scope".to_owned(),
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
scope.insert(
|
scope.symbols.insert(
|
||||||
name.lexeme.clone(),
|
name.lexeme.clone(),
|
||||||
SymInfo {
|
SymInfo {
|
||||||
decl: Some(name),
|
decl: Some(name),
|
||||||
|
@ -122,7 +156,7 @@ impl<'a> ResolverState<'a> {
|
||||||
assert!(!self.scopes.is_empty());
|
assert!(!self.scopes.is_empty());
|
||||||
let idx = self.scopes.len() - 1;
|
let idx = self.scopes.len() - 1;
|
||||||
let top = &mut self.scopes[idx];
|
let top = &mut self.scopes[idx];
|
||||||
if let Some(info) = top.get_mut(&name.lexeme as &str) {
|
if let Some(info) = top.symbols.get_mut(&name.lexeme as &str) {
|
||||||
if info.state == SymState::Declared {
|
if info.state == SymState::Declared {
|
||||||
info.state = SymState::Defined;
|
info.state = SymState::Defined;
|
||||||
}
|
}
|
||||||
|
@ -134,8 +168,8 @@ impl<'a> ResolverState<'a> {
|
||||||
assert!(!self.scopes.is_empty());
|
assert!(!self.scopes.is_empty());
|
||||||
let idx = self.scopes.len() - 1;
|
let idx = self.scopes.len() - 1;
|
||||||
let scope = &mut self.scopes[idx];
|
let scope = &mut self.scopes[idx];
|
||||||
assert!(!scope.contains_key("this"));
|
assert!(!scope.symbols.contains_key("this"));
|
||||||
scope.insert(
|
scope.symbols.insert(
|
||||||
"this".to_owned(),
|
"this".to_owned(),
|
||||||
SymInfo {
|
SymInfo {
|
||||||
decl: None,
|
decl: None,
|
||||||
|
@ -151,7 +185,7 @@ impl<'a> ResolverState<'a> {
|
||||||
let mut i = self.scopes.len();
|
let mut i = self.scopes.len();
|
||||||
while i != 0 {
|
while i != 0 {
|
||||||
i -= 1;
|
i -= 1;
|
||||||
if let Some(info) = self.scopes[i].get_mut(&expr.token.lexeme as &str) {
|
if let Some(info) = self.scopes[i].symbols.get_mut(&expr.token.lexeme as &str) {
|
||||||
if info.state == SymState::Declared {
|
if info.state == SymState::Declared {
|
||||||
return self.error(&expr.token, "symbol accessed before definition");
|
return self.error(&expr.token, "symbol accessed before definition");
|
||||||
}
|
}
|
||||||
|
@ -170,7 +204,7 @@ impl<'a> ResolverState<'a> {
|
||||||
let mut i = self.scopes.len();
|
let mut i = self.scopes.len();
|
||||||
while i != 0 {
|
while i != 0 {
|
||||||
i -= 1;
|
i -= 1;
|
||||||
if let Some(info) = self.scopes[i].get_mut(&name.lexeme as &str) {
|
if let Some(info) = self.scopes[i].symbols.get_mut(&name.lexeme as &str) {
|
||||||
if info.kind != SymKind::Variable {
|
if info.kind != SymKind::Variable {
|
||||||
return self.error(name, "cannot assign to this symbol");
|
return self.error(name, "cannot assign to this symbol");
|
||||||
}
|
}
|
||||||
|
@ -223,7 +257,7 @@ where
|
||||||
}
|
}
|
||||||
// Unlike the original Lox, function arguments and function bodies do
|
// Unlike the original Lox, function arguments and function bodies do
|
||||||
// not use the same environment.
|
// not use the same environment.
|
||||||
rs.with_scope(|rs| body.resolve(rs))
|
rs.with_scope(|rs| body.resolve(rs), ScopeType::Function)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Process all method definitions in a class.
|
/// Process all method definitions in a class.
|
||||||
|
@ -233,7 +267,10 @@ where
|
||||||
{
|
{
|
||||||
rs.define_this();
|
rs.define_this();
|
||||||
methods.iter().try_for_each(|method| {
|
methods.iter().try_for_each(|method| {
|
||||||
rs.with_scope(|rs| resolve_function(rs, &method.params, &method.body))
|
rs.with_scope(
|
||||||
|
|rs| resolve_function(rs, &method.params, &method.body),
|
||||||
|
ScopeType::Method,
|
||||||
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -272,7 +309,7 @@ impl VarResolver for StmtNode {
|
||||||
'a: 'b,
|
'a: 'b,
|
||||||
{
|
{
|
||||||
match self {
|
match self {
|
||||||
StmtNode::Block(stmts) => rs.with_scope(|rs| stmts.resolve(rs)),
|
StmtNode::Block(stmts) => rs.with_scope(|rs| stmts.resolve(rs), rs.current_type()),
|
||||||
|
|
||||||
StmtNode::VarDecl(name, None) => {
|
StmtNode::VarDecl(name, None) => {
|
||||||
rs.declare(name, SymKind::Variable)?;
|
rs.declare(name, SymKind::Variable)?;
|
||||||
|
@ -288,13 +325,16 @@ impl VarResolver for StmtNode {
|
||||||
StmtNode::FunDecl(decl) => {
|
StmtNode::FunDecl(decl) => {
|
||||||
rs.declare(&decl.name, SymKind::Function)?;
|
rs.declare(&decl.name, SymKind::Function)?;
|
||||||
rs.define(&decl.name);
|
rs.define(&decl.name);
|
||||||
rs.with_scope(|rs| resolve_function(rs, &decl.params, &decl.body))
|
rs.with_scope(
|
||||||
|
|rs| resolve_function(rs, &decl.params, &decl.body),
|
||||||
|
ScopeType::Function,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
StmtNode::ClassDecl(decl) => {
|
StmtNode::ClassDecl(decl) => {
|
||||||
rs.declare(&decl.name, SymKind::Class)?;
|
rs.declare(&decl.name, SymKind::Class)?;
|
||||||
rs.define(&decl.name);
|
rs.define(&decl.name);
|
||||||
rs.with_scope(|rs| resolve_class(rs, &decl.methods))
|
rs.with_scope(|rs| resolve_class(rs, &decl.methods), rs.current_type())
|
||||||
}
|
}
|
||||||
|
|
||||||
StmtNode::If {
|
StmtNode::If {
|
||||||
|
@ -360,7 +400,7 @@ impl VarResolver for ExprNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
ExprNode::Lambda { params, body } => {
|
ExprNode::Lambda { params, body } => {
|
||||||
rs.with_scope(|rs| resolve_function(rs, params, body))
|
rs.with_scope(|rs| resolve_function(rs, params, body), ScopeType::Function)
|
||||||
}
|
}
|
||||||
|
|
||||||
ExprNode::Logical(binary_expr) | ExprNode::Binary(binary_expr) => binary_expr
|
ExprNode::Logical(binary_expr) | ExprNode::Binary(binary_expr) => binary_expr
|
||||||
|
|
Loading…
Reference in a new issue