deimos-lang/dmc-lib/src/semantic_analysis/collect_symbols.rs

171 lines
5.3 KiB
Rust

use crate::ast::NodeId;
use crate::ast::compilation_unit::CompilationUnit;
use crate::ast::extern_function::ExternFunction;
use crate::ast::function::Function;
use crate::ast::parameter::Parameter;
use crate::ast::statement::Statement;
use crate::diagnostic::Diagnostics;
use crate::semantic_analysis::diagnostic_helpers::symbol_already_declared;
use crate::semantic_analysis::scope::{Scope, ScopeId};
use crate::semantic_analysis::symbol::{FunctionSymbol, ParameterSymbol, Symbol};
use std::collections::HashMap;
use std::rc::Rc;
pub struct SymbolCollectionResult(pub Diagnostics);
struct SymbolCollectionContext<'a> {
scopes: &'a mut Vec<Scope>,
nodes_to_scopes: &'a HashMap<NodeId, ScopeId>,
symbols: &'a mut Vec<Symbol>,
fqn_context: Vec<Rc<str>>,
diagnostics: Diagnostics,
}
impl<'a> SymbolCollectionContext<'a> {
pub fn new(
scopes: &'a mut Vec<Scope>,
nodes_to_scopes: &'a HashMap<NodeId, ScopeId>,
symbols: &'a mut Vec<Symbol>,
) -> Self {
Self {
scopes,
nodes_to_scopes,
symbols,
fqn_context: Vec::new(),
diagnostics: Diagnostics::new(),
}
}
pub fn find_symbol_in_scope_for(&self, name: &str, node_id: NodeId) -> Option<&Symbol> {
let scope_id = self.nodes_to_scopes[&node_id];
let scope = &self.scopes[scope_id];
if let Some(symbol_id) = scope.symbols().get(name) {
Some(&self.symbols[*symbol_id])
} else {
None
}
}
pub fn insert_symbol(&mut self, symbol: Symbol, node_id: NodeId) {
let declared_name = symbol.declared_name_owned();
self.symbols.push(symbol);
let symbol_id = self.symbols.len() - 1;
let scope_id = self.nodes_to_scopes[&node_id];
let scope = &mut self.scopes[scope_id];
scope.symbols_mut().insert(declared_name, symbol_id);
}
pub fn symbols_mut(&mut self) -> &mut Vec<Symbol> {
&mut self.symbols
}
pub fn diagnostics_mut(&mut self) -> &mut Diagnostics {
&mut self.diagnostics
}
fn join_fqn_parts(parts: &[Rc<str>]) -> String {
parts.join("::")
}
pub fn get_fqn_base(&self) -> String {
Self::join_fqn_parts(&self.fqn_context)
}
pub fn resolve_fqn(&self, suffix: &Rc<str>) -> String {
let base = self.get_fqn_base();
if base.is_empty() {
suffix.to_string()
} else {
Self::join_fqn_parts(&[base.into(), suffix.clone()])
}
}
}
pub fn collect_symbols_in_compilation_unit(
compilation_unit: &CompilationUnit,
scopes: &mut Vec<Scope>,
nodes_to_scopes: &HashMap<NodeId, ScopeId>,
symbols: &mut Vec<Symbol>,
) -> SymbolCollectionResult {
let mut ctx = SymbolCollectionContext::new(scopes, nodes_to_scopes, symbols);
for function in compilation_unit.functions() {
collect_symbols_function(function, &mut ctx);
}
for extern_function in compilation_unit.extern_functions() {
collect_symbols_extern_function(extern_function, &mut ctx);
}
SymbolCollectionResult(ctx.diagnostics)
}
fn collect_symbols_function(function: &Function, ctx: &mut SymbolCollectionContext) {
let fqn = ctx.resolve_fqn(&function.declared_name_owned()).into();
// function itself
let function_symbol = Symbol::Function(FunctionSymbol::new(
function.declared_name_owned(),
Some(function.declared_name_source_range()),
fqn,
false,
));
// insert
if let Some(already_declared) =
ctx.find_symbol_in_scope_for(function.declared_name(), function.node_id())
{
let diagnostic = symbol_already_declared(already_declared, &function_symbol);
ctx.diagnostics_mut().push(diagnostic);
} else {
ctx.insert_symbol(function_symbol, function.node_id());
}
// parameters
for parameter in function.parameters() {
collect_symbols_parameter(parameter, ctx);
}
// n.b. do not do statements yet, because variables are declared and resolved in the resolution pass
}
fn collect_symbols_extern_function(
extern_function: &ExternFunction,
ctx: &mut SymbolCollectionContext,
) {
let fqn = ctx
.resolve_fqn(&extern_function.declared_name_owned())
.into();
// function itself
let function_symbol = Symbol::Function(FunctionSymbol::new(
extern_function.declared_name_owned(),
Some(extern_function.declared_name_source_range()),
fqn,
true,
));
// insert function symbol
if let Some(already_declared) =
ctx.find_symbol_in_scope_for(extern_function.declared_name(), extern_function.node_id())
{
let diagnostic = symbol_already_declared(already_declared, &function_symbol);
ctx.diagnostics_mut().push(diagnostic);
} else {
ctx.insert_symbol(function_symbol, extern_function.node_id());
}
// parameters
for parameter in extern_function.parameters() {
collect_symbols_parameter(parameter, ctx);
}
}
fn collect_symbols_parameter(parameter: &Parameter, ctx: &mut SymbolCollectionContext) {
let parameter_symbol = ParameterSymbol::new(
parameter.declared_name_owned(),
Some(parameter.declared_name_source_range()),
);
ctx.insert_symbol(Symbol::Parameter(parameter_symbol), parameter.node_id());
}