Resolver - Class definitions create a scope that contains "this"
This commit is contained in:
parent
e46b399399
commit
a2986a1342
1 changed files with 36 additions and 11 deletions
|
@ -1,7 +1,7 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use crate::{
|
||||
ast::{ExprNode, ProgramNode, StmtNode, VariableExpr},
|
||||
ast::{ExprNode, FunDecl, ProgramNode, StmtNode, VariableExpr},
|
||||
errors::{ErrorKind, SloxError, SloxResult},
|
||||
tokens::Token,
|
||||
};
|
||||
|
@ -38,12 +38,13 @@ enum SymKind {
|
|||
Variable,
|
||||
Function,
|
||||
Class,
|
||||
This,
|
||||
}
|
||||
|
||||
/// General information about a symbol.
|
||||
#[derive(Clone, Debug)]
|
||||
struct SymInfo<'a> {
|
||||
decl: &'a Token,
|
||||
decl: Option<&'a Token>,
|
||||
kind: SymKind,
|
||||
state: SymState,
|
||||
}
|
||||
|
@ -77,10 +78,11 @@ impl<'a> ResolverState<'a> {
|
|||
self.scopes[self.scopes.len() - 1]
|
||||
.values()
|
||||
.filter(|v| v.state != SymState::Used)
|
||||
.find(|v| !v.decl.lexeme.starts_with('_'))
|
||||
.filter(|v| v.decl.is_some())
|
||||
.find(|v| !v.decl.unwrap().lexeme.starts_with('_'))
|
||||
.map_or(Ok(()), |v| {
|
||||
self.error(
|
||||
v.decl,
|
||||
v.decl.unwrap(),
|
||||
"unused symbol; prefix its name with '_' to avoid this error",
|
||||
)
|
||||
})
|
||||
|
@ -105,7 +107,7 @@ impl<'a> ResolverState<'a> {
|
|||
scope.insert(
|
||||
name.lexeme.clone(),
|
||||
SymInfo {
|
||||
decl: name,
|
||||
decl: Some(name),
|
||||
kind,
|
||||
state: SymState::Declared,
|
||||
},
|
||||
|
@ -127,6 +129,22 @@ impl<'a> ResolverState<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Declare and define the "this" value for the current scope.
|
||||
fn define_this(&mut self) {
|
||||
assert!(!self.scopes.is_empty());
|
||||
let idx = self.scopes.len() - 1;
|
||||
let scope = &mut self.scopes[idx];
|
||||
assert!(!scope.contains_key("this"));
|
||||
scope.insert(
|
||||
"this".to_owned(),
|
||||
SymInfo {
|
||||
decl: None,
|
||||
kind: SymKind::This,
|
||||
state: SymState::Defined,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// Resolve a symbol when it is being used. If the symbol is local,
|
||||
/// the lookup distance will be stored to the resolution map.
|
||||
fn resolve_use(&mut self, expr: &VariableExpr) -> ResolverResult {
|
||||
|
@ -208,6 +226,18 @@ where
|
|||
rs.with_scope(|rs| body.resolve(rs))
|
||||
}
|
||||
|
||||
/// Process all method definitions in a class.
|
||||
fn resolve_class<'a, 'b>(rs: &mut ResolverState<'a>, methods: &'b [FunDecl]) -> ResolverResult
|
||||
where
|
||||
'b: 'a,
|
||||
{
|
||||
rs.define_this();
|
||||
methods
|
||||
.iter()
|
||||
.map(|method| rs.with_scope(|rs| resolve_function(rs, &method.params, &method.body)))
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Helper trait used to visit the various AST nodes with the resolver.
|
||||
trait VarResolver {
|
||||
/// Try to resolve local variables under some AST node.
|
||||
|
@ -265,12 +295,7 @@ impl VarResolver for StmtNode {
|
|||
StmtNode::ClassDecl(decl) => {
|
||||
rs.declare(&decl.name, SymKind::Class)?;
|
||||
rs.define(&decl.name);
|
||||
decl.methods
|
||||
.iter()
|
||||
.map(|method| {
|
||||
rs.with_scope(|rs| resolve_function(rs, &method.params, &method.body))
|
||||
})
|
||||
.collect()
|
||||
rs.with_scope(|rs| resolve_class(rs, &decl.methods))
|
||||
}
|
||||
|
||||
StmtNode::If {
|
||||
|
|
Loading…
Reference in a new issue