use std::collections::{HashMap, HashSet};
use crate::ast::{Declaration, ExternalDecl, EnumSpec, TypeSpec, StorageClass};
use crate::intern::{InternedStr, StringInterner};
#[derive(Debug, Default)]
pub struct EnumDict {
variant_to_enum: HashMap<InternedStr, HashSet<InternedStr>>,
enum_to_variants: HashMap<InternedStr, Vec<InternedStr>>,
target_enums: HashSet<InternedStr>,
}
impl EnumDict {
pub fn new() -> Self {
Self::default()
}
pub fn collect_from_external_decl(
&mut self,
decl: &ExternalDecl,
is_target: bool,
_interner: &StringInterner,
) {
if let ExternalDecl::Declaration(d) = decl {
self.collect_from_declaration(d, is_target);
}
}
fn collect_from_declaration(&mut self, decl: &Declaration, is_target: bool) {
let typedef_name = if decl.specs.storage == Some(StorageClass::Typedef) {
decl.declarators.first().and_then(|init_decl| {
init_decl.declarator.name
})
} else {
None
};
for type_spec in &decl.specs.type_specs {
if let TypeSpec::Enum(spec) = type_spec {
self.collect_from_enum_spec(spec, typedef_name, is_target);
}
}
}
fn collect_from_enum_spec(
&mut self,
spec: &EnumSpec,
typedef_name: Option<InternedStr>,
is_target: bool,
) {
let enum_name = typedef_name.or(spec.name);
let enum_name = match enum_name {
Some(name) => name,
None => return,
};
let enumerators = match &spec.enumerators {
Some(e) => e,
None => return,
};
let variants: Vec<InternedStr> = enumerators.iter().map(|e| e.name).collect();
self.collect_enum(enum_name, &variants, is_target);
}
fn collect_enum(
&mut self,
enum_name: InternedStr,
variants: &[InternedStr],
is_target: bool,
) {
for &variant in variants {
self.variant_to_enum
.entry(variant)
.or_default()
.insert(enum_name);
}
self.enum_to_variants.insert(enum_name, variants.to_vec());
if is_target {
self.target_enums.insert(enum_name);
}
}
pub fn get_enum_for_variant(&self, variant: InternedStr) -> Option<InternedStr> {
self.variant_to_enum.get(&variant).and_then(|enums| {
if enums.len() == 1 {
enums.iter().next().copied()
} else {
None }
})
}
pub fn is_enum_variant(&self, name: InternedStr) -> bool {
self.variant_to_enum.contains_key(&name)
}
pub fn is_target_enum(&self, name: InternedStr) -> bool {
self.target_enums.contains(&name)
}
pub fn target_enums(&self) -> impl Iterator<Item = InternedStr> + '_ {
self.target_enums.iter().copied()
}
pub fn target_enum_names<'a>(&'a self, interner: &'a StringInterner) -> Vec<&'a str> {
let mut names: Vec<&str> = self.target_enums
.iter()
.map(|&name| interner.get(name))
.collect();
names.sort();
names
}
}