use crate::netlist::{ElabError, SymbolKey, TemplateKey};
use crate::symid::SymbolUid;
use std::collections::HashMap;
use std::hash::{Hash, Hasher};
#[derive(Default)]
pub(crate) struct DefinitionTable {
by_id: HashMap<crate::symid::SymbolId, u32>,
}
impl DefinitionTable {
pub(crate) fn intern(&mut self, def: sv_lang::Symbol<'_>) -> u32 {
let next_id = self.by_id.len() as u32;
*self.by_id.entry(def.uid()).or_insert(next_id)
}
}
pub(crate) fn template_key_of(
defs: &mut DefinitionTable,
instance: sv_lang::Symbol<'_>,
body: sv_lang::Symbol<'_>,
) -> TemplateKey {
let definition = body
.instance_body_definition()
.map(|def| defs.intern(def))
.unwrap_or(u32::MAX);
let mut hasher = std::collections::hash_map::DefaultHasher::new();
for param in instance.parameters() {
param.name().hash(&mut hasher);
param.parameter_value().hash(&mut hasher);
}
let params_hash = hasher.finish();
TemplateKey {
definition,
params_hash,
}
}
pub fn merge_groups_within_bucket<'d>(
tagged: &[(TemplateKey, sv_lang::Symbol<'d>)],
) -> Vec<Vec<usize>> {
let mut bucket_order: Vec<TemplateKey> = Vec::new();
let mut buckets: HashMap<TemplateKey, Vec<usize>> = HashMap::new();
for (index, (key, _symbol)) in tagged.iter().enumerate() {
if !buckets.contains_key(key) {
bucket_order.push(*key);
}
buckets.entry(*key).or_default().push(index);
}
let mut groups: Vec<Vec<usize>> = Vec::new();
for key in bucket_order {
let indices = &buckets[&key];
let mut assigned = vec![false; indices.len()];
for a in 0..indices.len() {
if assigned[a] {
continue;
}
let canonical = &tagged[indices[a]].1;
let mut group = vec![indices[a]];
assigned[a] = true;
for b in (a + 1)..indices.len() {
if assigned[b] {
continue;
}
let candidate = &tagged[indices[b]].1;
if canonical.instance_body_has_same_type(candidate) {
group.push(indices[b]);
assigned[b] = true;
}
}
groups.push(group);
}
}
groups
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct MemberShape {
pub kind: sv_lang_kinds::SymbolKind,
pub name: String,
}
pub struct ConfirmedMembers<'d> {
pub shapes: Vec<(SymbolKey, MemberShape)>,
pub per_body_symbols: Vec<Vec<sv_lang::Symbol<'d>>>,
}
pub fn confirm_and_assign_symbol_keys<'d>(
canonical: sv_lang::Symbol<'d>,
others: &[sv_lang::Symbol<'d>],
) -> Result<ConfirmedMembers<'d>, ElabError> {
let canonical_members: Vec<sv_lang::Symbol<'d>> = canonical.members().collect();
let shapes: Vec<(SymbolKey, MemberShape)> = canonical_members
.iter()
.enumerate()
.map(|(i, m)| {
(
SymbolKey(i as u32),
MemberShape {
kind: m.kind(),
name: m.name().to_string(),
},
)
})
.collect();
let mut per_body_symbols: Vec<Vec<sv_lang::Symbol<'d>>> = Vec::with_capacity(others.len() + 1);
per_body_symbols.push(canonical_members.clone());
for other in others {
let other_members: Vec<sv_lang::Symbol<'d>> = other.members().collect();
for (i, canonical_member) in canonical_members.iter().enumerate() {
let Some(other_member) = other_members.get(i) else {
return Err(ElabError::MergeInvariantViolated {
canonical_hier_path: canonical.hierarchical_path(),
other_hier_path: other.hierarchical_path(),
member_index: i,
detail: format!(
"other body has only {} member(s), canonical has member {} (`{}`, {:?})",
other_members.len(),
i,
canonical_member.name(),
canonical_member.kind(),
),
});
};
if other_member.kind() != canonical_member.kind()
|| other_member.name() != canonical_member.name()
{
return Err(ElabError::MergeInvariantViolated {
canonical_hier_path: canonical.hierarchical_path(),
other_hier_path: other.hierarchical_path(),
member_index: i,
detail: format!(
"member {i} mismatch: canonical `{}` ({:?}) vs other `{}` ({:?})",
canonical_member.name(),
canonical_member.kind(),
other_member.name(),
other_member.kind(),
),
});
}
}
if other_members.len() != canonical_members.len() {
return Err(ElabError::MergeInvariantViolated {
canonical_hier_path: canonical.hierarchical_path(),
other_hier_path: other.hierarchical_path(),
member_index: canonical_members.len(),
detail: format!(
"other body has {} member(s), canonical has only {}",
other_members.len(),
canonical_members.len(),
),
});
}
per_body_symbols.push(other_members);
}
Ok(ConfirmedMembers {
shapes,
per_body_symbols,
})
}