309 lines
10 KiB
Rust
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
|
|
}
|
|
}
|