deimos-lang/dmc-lib/src/semantic_analysis/analysis_context.rs
2026-07-16 18:33:15 -05:00

309 lines
10 KiB
Rust

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<Rc<str>>,
scopes: Vec<Scope>,
current_scope_id: Option<ScopeId>,
nodes_to_scopes: HashMap<NodeId, ScopeId>,
symbols: Vec<Symbol>,
nodes_to_symbols: HashMap<NodeId, SymbolId>,
type_infos: Vec<TypeInfo>,
symbols_to_type_infos: HashMap<SymbolId, TypeInfoId>,
nodes_to_type_infos: HashMap<NodeId, TypeInfoId>,
}
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<Scope> {
&mut self.scopes
}
pub fn symbols(&self) -> &[Symbol] {
&self.symbols
}
pub fn nodes_to_symbols(&self) -> &HashMap<NodeId, SymbolId> {
&self.nodes_to_symbols
}
pub fn type_infos(&self) -> &[TypeInfo] {
&self.type_infos
}
pub fn symbols_to_type_infos(&self) -> &HashMap<SymbolId, TypeInfoId> {
&self.symbols_to_type_infos
}
pub fn nodes_to_type_infos(&self) -> &HashMap<NodeId, TypeInfoId> {
&self.nodes_to_type_infos
}
pub fn push_fqn_part(&mut self, part: Rc<str>) {
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<SymbolId> {
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<SymbolId> = 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
}
}