use crate::ast::NodeId; use crate::diagnostic::Diagnostic; use crate::diagnostic_factories::symbol_not_found; use crate::semantic_analysis::diagnostic_helpers::symbol_already_declared; use crate::semantic_analysis::scope::{Scope, ScopeId}; use crate::semantic_analysis::symbol::{Symbol, SymbolId}; use crate::semantic_analysis::type_info::{TypeInfo, TypeInfoId}; use crate::source_range::SourceRange; use std::collections::HashMap; use std::rc::Rc; pub struct AnalysisContext { fqn_stack: Vec>, scopes: Vec, current_scope_id: Option, nodes_to_scopes: HashMap, symbols: Vec, nodes_to_symbols: HashMap, type_infos: Vec, symbols_to_type_infos: HashMap, nodes_to_type_infos: HashMap, } impl AnalysisContext { pub fn new() -> Self { Self { fqn_stack: Vec::new(), scopes: Vec::new(), current_scope_id: None, nodes_to_scopes: HashMap::new(), symbols: Vec::new(), nodes_to_symbols: HashMap::new(), type_infos: Vec::new(), symbols_to_type_infos: HashMap::new(), nodes_to_type_infos: HashMap::new(), } } pub fn commit(&mut self) { todo!() } pub fn rollback(&mut self) { todo!() } pub fn scopes(&self) -> &[Scope] { &self.scopes } pub fn scopes_mut(&mut self) -> &mut Vec { &mut self.scopes } pub fn symbols(&self) -> &[Symbol] { &self.symbols } pub fn nodes_to_symbols(&self) -> &HashMap { &self.nodes_to_symbols } pub fn type_infos(&self) -> &[TypeInfo] { &self.type_infos } pub fn symbols_to_type_infos(&self) -> &HashMap { &self.symbols_to_type_infos } pub fn nodes_to_type_infos(&self) -> &HashMap { &self.nodes_to_type_infos } pub fn push_fqn_part(&mut self, part: Rc) { self.fqn_stack.push(part); } pub fn pop_fqn_part(&mut self) { self.fqn_stack.pop(); } pub fn resolve_fqn(&self, suffix: &str) -> String { let mut fqn = self.fqn_stack.clone(); fqn.push(suffix.into()); fqn.join("::") } pub fn push_scope(&mut self, _debug_name: &str) { let scope = Scope::new(self.current_scope_id); self.scopes.push(scope); self.current_scope_id = Some(self.scopes.len() - 1); } fn get_scope(&self, scope_id: ScopeId) -> &Scope { self.scopes.get(scope_id).expect(&format!( "scope_id {} is not a valid index of self.scopes", scope_id )) } fn get_scope_mut(&mut self, scope_id: ScopeId) -> &mut Scope { self.scopes.get_mut(scope_id).expect(&format!( "scope_id {} is not a valid index of self.scopes", scope_id )) } fn get_current_scope(&self) -> &Scope { self.get_scope(self.current_scope_id.expect("current_scope_id is None")) } fn get_current_scope_mut(&mut self) -> &mut Scope { self.get_scope_mut(self.current_scope_id.expect("current_scope_id is None")) } pub fn pop_scope(&mut self) { self.current_scope_id = self.get_current_scope().parent_id(); } pub fn associate_node_to_current_scope(&mut self, node_id: NodeId) { self.nodes_to_scopes.insert( node_id, self.current_scope_id.expect("current_scope_id is None"), ); } fn insert_symbol_in_scope(&mut self, scope_id: ScopeId, symbol: Symbol) -> SymbolId { // get declared name let declared_name = symbol.declared_name_owned(); // push the symbol self.symbols.push(symbol); let symbol_id = self.symbols.len() - 1; // put it in scope self.get_scope_mut(scope_id) .insert_symbol(declared_name, symbol_id); symbol_id } pub fn associate_node_to_symbol(&mut self, node_id: NodeId, symbol_id: SymbolId) { self.nodes_to_symbols.insert(node_id, symbol_id); } fn get_symbol_in_scope(&self, scope: &Scope, declared_name: &str) -> Option<&Symbol> { scope.get_symbol_id(declared_name).map(|symbol_id| { self.symbols.get(symbol_id).expect(&format!( "symbol_id {} is not a valid index of self.symbols", symbol_id )) }) } /// Use this function only when there is no node associated with the symbol. Otherwise use /// `try_insert_symbol_in_node_scope`. pub fn try_insert_symbol_in_scope( &mut self, to_insert: Symbol, scope_id: ScopeId, ) -> Result<(), Diagnostic> { let scope = self.get_scope(scope_id); if let Some(already_declared) = self.get_symbol_in_scope(scope, to_insert.declared_name()) { Err(symbol_already_declared(already_declared, &to_insert)) } else { self.insert_symbol_in_scope(scope_id, to_insert); // no associated node, so no need to update self.nodes_to_symbols Ok(()) } } pub fn try_insert_associate_symbol_in_node_scope( &mut self, to_insert: Symbol, owner_node_id: NodeId, ) -> Result<(), Diagnostic> { let node_scope_id = self.nodes_to_scopes.get(&owner_node_id).expect(&format!( "owner_node_id {} is not in self.nodes_to_scopes", owner_node_id )); let node_scope = self.get_scope(*node_scope_id); if let Some(already_declared) = self.get_symbol_in_scope(node_scope, to_insert.declared_name()) { Err(symbol_already_declared(already_declared, &to_insert)) } else { let symbol_id = self.insert_symbol_in_scope(*node_scope_id, to_insert); self.associate_node_to_symbol(owner_node_id, symbol_id); Ok(()) } } fn get_symbol_id_by_declared_name_in_node_scope( &self, node_id: NodeId, declared_name: &str, ) -> Option { let scope_id = self .nodes_to_scopes .get(&node_id) .expect(&format!("node_id {} has no associated scope", node_id)); let scope = self.get_scope(*scope_id); scope.get_symbol_id(declared_name) } pub fn associate_node_with_self_symbol(&mut self, node_id: NodeId, declared_name: &str) { let symbol_id = self .get_symbol_id_by_declared_name_in_node_scope(node_id, declared_name) .expect(&format!( "could not get symbol_id for node_id {} and declared_name {}", node_id, declared_name )); self.associate_node_to_symbol(node_id, symbol_id); } pub fn lookup_and_associate_symbol_to_node( &mut self, node_id: NodeId, declared_name: &str, node_source_range: &SourceRange, ) -> Result<(), Diagnostic> { let scope_id = self .nodes_to_scopes .get(&node_id) .expect(&format!("node_id {} has no associated scope", node_id)); let mut maybe_scope = Some(self.get_scope(*scope_id)); let mut found_symbol_id: Option = None; while let Some(scope) = maybe_scope { match scope.get_symbol_id(declared_name) { None => { maybe_scope = match scope.parent_id() { None => None, Some(parent_id) => Some(self.get_scope(parent_id)), } } Some(symbol_id) => { found_symbol_id = Some(symbol_id); break; } } } match found_symbol_id { None => Err(symbol_not_found(declared_name, node_source_range)), Some(symbol_id) => { self.associate_node_to_symbol(node_id, symbol_id); Ok(()) } } } pub fn insert_type_info(&mut self, type_info: TypeInfo) -> TypeInfoId { self.type_infos.push(type_info); self.type_infos.len() - 1 } pub fn insert_type_info_for_node(&mut self, type_info: TypeInfo, node_id: NodeId) { self.type_infos.push(type_info); self.nodes_to_type_infos .insert(node_id, self.type_infos.len() - 1); } pub fn associate_node_and_symbol_to_type(&mut self, node_id: NodeId, type_info_id: TypeInfoId) { let symbol_id = self .nodes_to_symbols .get(&node_id) .expect(&format!("node_id {} has no associated symbol", node_id)); self.symbols_to_type_infos.insert(*symbol_id, type_info_id); self.nodes_to_type_infos.insert(node_id, type_info_id); } pub fn get_type_info_id_for_node(&self, node_id: NodeId) -> TypeInfoId { *self .nodes_to_type_infos .get(&node_id) .expect(&format!("node_id {} has no associated type", node_id)) } pub fn get_type_info_for_node(&self, node_id: NodeId) -> &TypeInfo { let type_info_id = self.get_type_info_id_for_node(node_id); self.type_infos .get(type_info_id) .expect(&format!("invalid type_info_id {}", type_info_id)) } pub fn get_type_info_by_id(&self, type_info_id: TypeInfoId) -> &TypeInfo { self.type_infos .get(type_info_id) .expect(&format!("invalid type_info_id {}", type_info_id)) } pub fn associate_node_to_type(&mut self, node_id: NodeId, type_info_id: TypeInfoId) { self.nodes_to_type_infos.insert(node_id, type_info_id); } pub fn lookup_type_for_node_via_symbol(&self, node_id: NodeId) -> TypeInfoId { let symbol_id = self .nodes_to_symbols .get(&node_id) .expect(&format!("node_id {} has no associated symbol", node_id)); let symbol_type_info_id = self.symbols_to_type_infos.get(symbol_id).expect(&format!( "symbol_id {} has no associated type_info", symbol_id )); *symbol_type_info_id } }