Resolver - Class definitions create a scope that contains "this"

This commit is contained in:
Emmanuel BENOîT 2023-01-09 10:48:50 +01:00
parent e46b399399
commit a2986a1342

View file

@ -1,7 +1,7 @@
use std::collections::HashMap; use std::collections::HashMap;
use crate::{ use crate::{
ast::{ExprNode, ProgramNode, StmtNode, VariableExpr}, ast::{ExprNode, FunDecl, ProgramNode, StmtNode, VariableExpr},
errors::{ErrorKind, SloxError, SloxResult}, errors::{ErrorKind, SloxError, SloxResult},
tokens::Token, tokens::Token,
}; };
@ -38,12 +38,13 @@ enum SymKind {
Variable, Variable,
Function, Function,
Class, Class,
This,
} }
/// General information about a symbol. /// General information about a symbol.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
struct SymInfo<'a> { struct SymInfo<'a> {
decl: &'a Token, decl: Option<&'a Token>,
kind: SymKind, kind: SymKind,
state: SymState, state: SymState,
} }
@ -77,10 +78,11 @@ impl<'a> ResolverState<'a> {
self.scopes[self.scopes.len() - 1] self.scopes[self.scopes.len() - 1]
.values() .values()
.filter(|v| v.state != SymState::Used) .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| { .map_or(Ok(()), |v| {
self.error( self.error(
v.decl, v.decl.unwrap(),
"unused symbol; prefix its name with '_' to avoid this error", "unused symbol; prefix its name with '_' to avoid this error",
) )
}) })
@ -105,7 +107,7 @@ impl<'a> ResolverState<'a> {
scope.insert( scope.insert(
name.lexeme.clone(), name.lexeme.clone(),
SymInfo { SymInfo {
decl: name, decl: Some(name),
kind, kind,
state: SymState::Declared, 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, /// Resolve a symbol when it is being used. If the symbol is local,
/// the lookup distance will be stored to the resolution map. /// the lookup distance will be stored to the resolution map.
fn resolve_use(&mut self, expr: &VariableExpr) -> ResolverResult { fn resolve_use(&mut self, expr: &VariableExpr) -> ResolverResult {
@ -208,6 +226,18 @@ where
rs.with_scope(|rs| body.resolve(rs)) 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. /// Helper trait used to visit the various AST nodes with the resolver.
trait VarResolver { trait VarResolver {
/// Try to resolve local variables under some AST node. /// Try to resolve local variables under some AST node.
@ -265,12 +295,7 @@ impl VarResolver for StmtNode {
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);
decl.methods rs.with_scope(|rs| resolve_class(rs, &decl.methods))
.iter()
.map(|method| {
rs.with_scope(|rs| resolve_function(rs, &method.params, &method.body))
})
.collect()
} }
StmtNode::If { StmtNode::If {