use crate::ast::node::NodeId;
use crate::program::Program;
use std::collections::{HashMap, HashSet};
use std::ops::BitOr;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum ResolutionResultFlags {
#[default]
None = 0,
Resolved = 1 << 1,
Unknown = 1 << 2,
Ambiguous = 1 << 3,
NotFound = 1 << 4,
ResolutionFailed = (1 << 2) | (1 << 3) | (1 << 4), }
impl ResolutionResultFlags {
pub fn is_failed(&self) -> bool {
matches!(
self,
Self::NotFound | Self::Unknown | Self::Ambiguous | Self::ResolutionFailed
)
}
}
#[derive(Debug, Clone)]
pub struct ResolutionResult {
pub resolved_symbol: Option<Sym>,
pub final_symbol: Option<Sym>,
pub resolution_result: ResolutionResultFlags,
pub is_template_instantiation: bool,
pub ambiguous_symbols: Vec<Sym>,
}
impl Default for ResolutionResult {
fn default() -> Self {
Self {
resolved_symbol: None,
final_symbol: None,
resolution_result: ResolutionResultFlags::None,
is_template_instantiation: false,
ambiguous_symbols: Vec::new(),
}
}
}
impl ResolutionResult {
fn resolved(sym: Sym) -> Self {
Self {
resolved_symbol: Some(sym.clone()),
final_symbol: Some(sym),
resolution_result: ResolutionResultFlags::Resolved,
is_template_instantiation: false,
ambiguous_symbols: Vec::new(),
}
}
fn failed(flags: ResolutionResultFlags) -> Self {
Self {
resolved_symbol: None,
final_symbol: None,
resolution_result: flags,
is_template_instantiation: false,
ambiguous_symbols: Vec::new(),
}
}
#[allow(dead_code)]
fn ambiguous(symbols: Vec<Sym>) -> Self {
Self {
resolved_symbol: None,
final_symbol: None,
resolution_result: ResolutionResultFlags::Ambiguous,
is_template_instantiation: false,
ambiguous_symbols: symbols,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SymbolFlags(u32);
#[allow(non_upper_case_globals)]
impl SymbolFlags {
pub const None: Self = SymbolFlags(0);
pub const Model: Self = SymbolFlags(1 << 1);
pub const Scalar: Self = SymbolFlags(1 << 2);
pub const Operation: Self = SymbolFlags(1 << 3);
pub const Enum: Self = SymbolFlags(1 << 4);
pub const Interface: Self = SymbolFlags(1 << 5);
pub const Union: Self = SymbolFlags(1 << 6);
pub const Alias: Self = SymbolFlags(1 << 7);
pub const Namespace: Self = SymbolFlags(1 << 8);
pub const Decorator: Self = SymbolFlags(1 << 9);
pub const TemplateParameter: Self = SymbolFlags(1 << 10);
pub const Function: Self = SymbolFlags(1 << 11);
pub const FunctionParameter: Self = SymbolFlags(1 << 12);
pub const Using: Self = SymbolFlags(1 << 13);
pub const DuplicateUsing: Self = SymbolFlags(1 << 14);
pub const SourceFile: Self = SymbolFlags(1 << 15);
pub const Member: Self = SymbolFlags(1 << 16);
pub const Const: Self = SymbolFlags(1 << 17);
pub const Declaration: Self = SymbolFlags(1 << 20);
pub const Implementation: Self = SymbolFlags(1 << 21);
pub const LateBound: Self = SymbolFlags(1 << 22);
pub const Internal: Self = SymbolFlags(1 << 23);
pub const ExportContainer: Self = SymbolFlags((1 << 8) | (1 << 15)); pub const MemberContainer: Self =
SymbolFlags((1 << 1) | (1 << 4) | (1 << 6) | (1 << 5) | (1 << 2));
pub fn bits(&self) -> u32 {
self.0
}
pub fn from_bits(bits: u32) -> Self {
Self(bits)
}
pub fn contains(self, other: Self) -> bool {
(self.0 & other.0) == other.0
}
pub fn is_member_container(&self) -> bool {
(self.0 & Self::MemberContainer.0) != 0
}
pub fn is_export_container(&self) -> bool {
(self.0 & Self::ExportContainer.0) != 0
}
}
impl BitOr for SymbolFlags {
type Output = Self;
fn bitor(self, other: Self) -> Self::Output {
Self(self.0 | other.0)
}
}
#[derive(Debug, Clone)]
pub struct Sym {
pub id: Option<u32>,
pub name: String,
pub flags: SymbolFlags,
pub node: Option<NodeId>,
pub declarations: Vec<NodeId>,
pub exports: Option<HashMap<String, Sym>>,
pub members: Option<HashMap<String, Sym>>,
pub symbol_source: Option<Box<Sym>>,
pub metatype_members: Option<HashMap<String, Sym>>,
pub parent: Option<Box<Sym>>,
}
impl Sym {
pub fn new(name: &str, flags: SymbolFlags) -> Self {
Self {
id: None,
name: name.to_string(),
flags,
node: None,
declarations: Vec::new(),
exports: None,
members: None,
symbol_source: None,
metatype_members: None,
parent: None,
}
}
pub fn has_flag(&self, flag: SymbolFlags) -> bool {
self.flags.contains(flag)
}
}
pub type SymbolTable = HashMap<String, Sym>;
#[derive(Debug, Clone, Default)]
pub struct NodeLinks {
pub resolved_symbol: Option<Sym>,
pub resolution_result: ResolutionResultFlags,
pub is_template_instantiation: bool,
pub final_symbol: Option<Sym>,
}
impl NodeLinks {
pub fn new() -> Self {
Self::default()
}
}
#[derive(Debug, Clone, Default)]
pub struct SymbolLinks {
pub declared_type: Option<NodeId>,
pub type_id: Option<NodeId>,
pub members_bound: bool,
pub has_unknown_members: bool,
pub alias_resolution_result: ResolutionResultFlags,
pub aliased_symbol: Option<Sym>,
pub alias_resolution_is_template: bool,
pub constraint_resolution_result: ResolutionResultFlags,
pub constraint_symbol: Option<Sym>,
pub instantiations: Option<HashMap<Vec<NodeId>, NodeId>>,
}
impl SymbolLinks {
pub fn new() -> Self {
Self::default()
}
}
#[derive(Debug, Clone, Default)]
pub struct ResolveTypeReferenceOptions {
pub resolve_decorators: bool,
}
#[allow(dead_code)]
pub struct NameResolver {
program: Program,
merged_symbols: HashMap<NodeId, Sym>,
augmented_symbol_tables: HashMap<NodeId, SymbolTable>,
node_links: HashMap<NodeId, NodeLinks>,
symbol_links: HashMap<NodeId, SymbolLinks>,
visited_nodes: HashSet<NodeId>,
global_namespace_node: NodeId,
global_namespace_sym: Sym,
null_sym: Sym,
used_using_sym: HashMap<NodeId, HashSet<NodeId>>,
augment_decorators_for_sym: HashMap<NodeId, Vec<NodeId>>,
next_node_id: NodeId,
next_symbol_id: NodeId,
}
impl NameResolver {
pub fn new(program: Program) -> Self {
let global_namespace_sym =
Sym::new("global", SymbolFlags::Namespace | SymbolFlags::Declaration);
let null_sym = Sym::new("null", SymbolFlags::None);
Self {
program,
merged_symbols: HashMap::new(),
augmented_symbol_tables: HashMap::new(),
node_links: HashMap::new(),
symbol_links: HashMap::new(),
visited_nodes: HashSet::new(),
global_namespace_node: 0,
global_namespace_sym,
null_sym,
used_using_sym: HashMap::new(),
augment_decorators_for_sym: HashMap::new(),
next_node_id: 1,
next_symbol_id: 1,
}
}
pub fn resolve_program(&mut self) {
let file_ids: Vec<NodeId> = self
.program
.source_files
.values()
.map(|file| file.ast)
.collect();
for file_id in &file_ids {
self.bind_and_resolve_node(*file_id);
}
}
pub fn get_merged_symbol(&self, sym: &Sym) -> Sym {
match sym.id {
Some(id) => self
.merged_symbols
.get(&id)
.cloned()
.unwrap_or_else(|| sym.clone()),
None => sym.clone(),
}
}
pub fn get_augmented_symbol_table(&mut self, table_id: NodeId) -> &mut SymbolTable {
self.get_augmented_symbol_table_internal_by_id(table_id)
}
#[allow(dead_code)]
fn get_augmented_symbol_table_internal(&mut self, table: &SymbolTable) -> &mut SymbolTable {
let table_id = self.find_or_create_table_id(table);
self.get_augmented_symbol_table_internal_by_id(table_id)
}
#[allow(dead_code)]
fn find_or_create_table_id(&mut self, _table: &SymbolTable) -> NodeId {
let id = self.next_node_id;
self.next_node_id += 1;
self.augmented_symbol_tables.insert(id, SymbolTable::new());
id
}
fn get_augmented_symbol_table_internal_by_id(&mut self, table_id: NodeId) -> &mut SymbolTable {
self.augmented_symbol_tables.entry(table_id).or_default()
}
pub fn get_node_links(&mut self, node_id: NodeId) -> &mut NodeLinks {
self.node_links.entry(node_id).or_default()
}
pub fn get_symbol_links(&mut self, sym: &Sym) -> &mut SymbolLinks {
let id = sym.id.unwrap_or_else(|| {
let new_id = self.next_symbol_id;
self.next_symbol_id += 1;
new_id
});
self.symbol_links.entry(id).or_default()
}
pub fn get_augment_decorators_for_sym(&self, sym: &Sym) -> Vec<NodeId> {
sym.id
.and_then(|id| self.augment_decorators_for_sym.get(&id))
.cloned()
.unwrap_or_default()
}
pub fn get_unused_usings(&self) -> Vec<NodeId> {
Vec::new() }
pub fn resolve_type_reference(
&mut self,
node_id: NodeId,
options: ResolveTypeReferenceOptions,
) -> ResolutionResult {
let links = self.get_node_links(node_id);
if links.resolution_result != ResolutionResultFlags::None {
return ResolutionResult {
resolved_symbol: links.resolved_symbol.clone(),
final_symbol: links.final_symbol.clone(),
resolution_result: links.resolution_result,
is_template_instantiation: links.is_template_instantiation,
ambiguous_symbols: Vec::new(),
};
}
let result = self.resolve_type_reference_worker(node_id, options);
let resolved_sym = result.final_symbol.clone();
if let Some(ref sym) = resolved_sym {
if sym.has_flag(SymbolFlags::Alias) {
let alias_result = self.resolve_alias_internal(node_id);
return ResolutionResult {
resolved_symbol: alias_result.resolved_symbol.or(result.resolved_symbol),
final_symbol: alias_result.final_symbol,
resolution_result: alias_result.resolution_result,
is_template_instantiation: result.is_template_instantiation
|| alias_result.is_template_instantiation,
ambiguous_symbols: Vec::new(),
};
}
if sym.has_flag(SymbolFlags::TemplateParameter) {
let template_result = self.resolve_template_parameter_internal(node_id);
if template_result.resolution_result != ResolutionResultFlags::None {
return template_result;
}
}
}
if let Some(ref sym) = resolved_sym
&& sym.has_flag(SymbolFlags::Declaration)
&& !sym.has_flag(SymbolFlags::Namespace)
{
for decl in &sym.declarations {
self.bind_and_resolve_node(*decl);
}
}
result
}
fn resolve_type_reference_worker(
&mut self,
node_id: NodeId,
options: ResolveTypeReferenceOptions,
) -> ResolutionResult {
self.resolve_identifier(node_id, options)
}
#[allow(dead_code)]
fn resolve_member_expression(
&mut self,
_node_id: NodeId,
_options: ResolveTypeReferenceOptions,
) -> ResolutionResult {
ResolutionResult::failed(ResolutionResultFlags::Unknown)
}
fn resolve_identifier(
&mut self,
_node_id: NodeId,
_options: ResolveTypeReferenceOptions,
) -> ResolutionResult {
if let Some(sym) = self
.global_namespace_sym
.exports
.as_ref()
.and_then(|e| e.get("global"))
{
return ResolutionResult::resolved(sym.clone());
}
ResolutionResult::failed(ResolutionResultFlags::Unknown)
}
fn resolve_alias_internal(&mut self, _node_id: NodeId) -> ResolutionResult {
ResolutionResult::failed(ResolutionResultFlags::Unknown)
}
fn resolve_template_parameter_internal(&mut self, _node_id: NodeId) -> ResolutionResult {
ResolutionResult::failed(ResolutionResultFlags::Unknown)
}
pub fn resolve_member_expression_for_sym(
&mut self,
_sym: &Sym,
_node_id: NodeId,
_options: ResolveTypeReferenceOptions,
) -> ResolutionResult {
ResolutionResult::failed(ResolutionResultFlags::Unknown)
}
pub fn resolve_meta_member_by_name(&mut self, _sym: &Sym, _name: &str) -> ResolutionResult {
ResolutionResult::failed(ResolutionResultFlags::NotFound)
}
#[allow(dead_code)]
fn merge_symbol_table(&mut self, _target: &Sym, _source_id: NodeId) {
}
#[allow(dead_code)]
fn set_usings_for_file(&mut self, _file_id: NodeId) {
}
fn bind_and_resolve_node(&mut self, node_id: NodeId) {
if self.visited_nodes.contains(&node_id) {
return;
}
self.visited_nodes.insert(node_id);
}
pub fn global_namespace(&self) -> &Sym {
&self.global_namespace_sym
}
pub fn null_symbol(&self) -> &Sym {
&self.null_sym
}
}
pub fn create_resolver(program: Program) -> NameResolver {
NameResolver::new(program)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_resolution_result() {
let result = ResolutionResult::failed(ResolutionResultFlags::NotFound);
assert_eq!(result.resolution_result, ResolutionResultFlags::NotFound);
assert!(result.resolved_symbol.is_none());
}
#[test]
fn test_symbol_flags() {
let flags = SymbolFlags::Namespace | SymbolFlags::Declaration;
assert!(flags.contains(SymbolFlags::Namespace));
assert!(flags.contains(SymbolFlags::Declaration));
assert!(!flags.contains(SymbolFlags::Model));
}
#[test]
fn test_symbol_flags_is_member_container() {
let model = SymbolFlags::Model;
assert!(model.is_member_container());
assert!(!model.is_export_container());
let ns = SymbolFlags::Namespace;
assert!(ns.is_export_container());
assert!(!ns.is_member_container());
let source_file = SymbolFlags::SourceFile;
assert!(source_file.is_export_container());
assert!(!source_file.is_member_container());
let ns_decl = SymbolFlags::Namespace | SymbolFlags::Declaration;
assert!(ns_decl.is_export_container()); }
#[test]
fn test_resolution_result_flags_default() {
let flags = ResolutionResultFlags::default();
assert_eq!(flags, ResolutionResultFlags::None);
}
}