use crate::symbol_table::{SymbolTable, ScopeSegment, ScopeSegmentKind, FullyQualifiedSymbolName}; use crate::ast::*; pub struct ScopeResolver<'a> { symbol_table: &'a mut SymbolTable } impl<'a> ScopeResolver<'a> { pub fn new(symbol_table: &'a mut SymbolTable) -> ScopeResolver { ScopeResolver { symbol_table } } pub fn resolve(&mut self, ast: &mut AST) -> Result<(), String> { for statement in ast.statements.iter_mut() { match statement.mut_node().kind { StatementKind::Declaration(ref mut decl) => self.decl(decl), StatementKind::Expression(ref mut expr) => self.expr(expr), }?; } Ok(()) } fn decl(&mut self, decl: &mut Declaration) -> Result<(), String> { use Declaration::*; match decl { Binding { expr, .. } => self.expr(expr), FuncDecl(_, block) => self.block(block), _ => Ok(()), } } fn block(&mut self, block: &mut Block) -> Result<(), String> { for statement in block.iter_mut() { match statement.mut_node().kind { StatementKind::Declaration(ref mut decl) => self.decl(decl), StatementKind::Expression(ref mut expr) => self.expr(expr), }?; } Ok(()) } fn expr(&mut self, expr: &mut Meta) -> Result<(), String> { use ExpressionKind::*; let inner_expr = expr.mut_node(); match &mut inner_expr.kind { ExpressionKind::Value(qualified_name) => { let fqsn = lookup_name_in_scope(&qualified_name.node()); let ref id = qualified_name.node().id; self.symbol_table.map_id_to_fqsn(id, fqsn); }, NamedStruct { name, .. } => { let ref id = name.node().id; let fqsn = lookup_name_in_scope(&name.node()); self.symbol_table.map_id_to_fqsn(id, fqsn); }, BinExp(_, ref mut lhs, ref mut rhs) => { self.expr(lhs)?; self.expr(rhs)?; }, PrefixExp(_, ref mut arg) => { self.expr(arg)?; }, TupleLiteral(exprs) => { for expr in exprs.iter_mut() { self.expr(expr)?; } }, Call { ref mut f, arguments } => { self.expr(f)?; for arg in arguments.iter_mut() { self.invoc(arg)?; } }, Lambda { params, body, .. } => { self.block(body)?; for param in params.iter_mut() { if let Some(ref mut expr) = param.default { self.expr(expr)?; } } }, IfExpression { ref mut body, ref mut discriminator } => { match &mut **discriminator { Discriminator::Simple(expr) | Discriminator::BinOp(expr, _) => self.expr(expr)? }; match &mut **body { IfExpressionBody::SimplePatternMatch(ref mut pat, ref mut alt1, ref mut alt2) => { self.pattern(pat)?; self.block(alt1)?; if let Some(alt) = alt2.as_mut() { self.block(alt)?; } }, IfExpressionBody::GuardList(guardarms) => { for arm in guardarms.iter_mut() { if let Guard::Pat(ref mut pat) = arm.guard { self.pattern(pat)?; } self.block(&mut arm.body)?; } } _ => () } }, _ => () }; Ok(()) } fn invoc(&mut self, invoc: &mut InvocationArgument) -> Result<(), String> { use InvocationArgument::*; match invoc { Positional(expr) => self.expr(expr), Keyword { expr, .. } => self.expr(expr), _ => Ok(()) } } fn pattern(&mut self, pat: &mut Pattern) -> Result<(), String> { use Pattern::*; match pat { Ignored => (), TuplePattern(patterns) => { for pat in patterns { self.pattern(pat)?; } }, Literal(_) => (), TupleStruct(name, patterns) => { self.qualified_name_in_pattern(name); for pat in patterns { self.pattern(pat)?; } }, Record(name, key_patterns) => { self.qualified_name_in_pattern(name); for (_, pat) in key_patterns { self.pattern(pat)?; } }, VarOrName(name) => { self.qualified_name_in_pattern(name); }, }; Ok(()) } /// this might be a variable or a pattern. if a variable, set to none fn qualified_name_in_pattern(&mut self, meta_qualified_name: &mut Meta, ) { let inner_name = meta_qualified_name.node(); let fqsn = lookup_name_in_scope(inner_name); meta_qualified_name.fqsn = if self.symbol_table.lookup_by_fqsn(&fqsn).is_some() { Some(fqsn) } else { None }; } } //TODO this is incomplete fn lookup_name_in_scope(sym_name: &QualifiedName) -> FullyQualifiedSymbolName { let QualifiedName { components: vec, .. } = sym_name; let len = vec.len(); let new_vec: Vec = vec.iter().enumerate().map(|(i, name)| { let kind = if i == (len - 1) { ScopeSegmentKind::Terminal } else { ScopeSegmentKind::Type }; ScopeSegment { name: name.clone(), kind } }).collect(); FullyQualifiedSymbolName(new_vec) } #[cfg(test)] mod tests { #[test] fn basic_scope() { } }