use sv_lang::{Expression, Symbol};
use sv_lang_kinds::{ExpressionKind, SymbolKind};
pub fn interface_instances<'d>(any: &Symbol<'d>, definition: &str) -> Vec<Symbol<'d>> {
fn visit<'d>(inst: Symbol<'d>, definition: &str, out: &mut Vec<Symbol<'d>>) {
if inst.kind() == SymbolKind::InstanceArray {
for e in inst.instance_array_elements() {
visit(e, definition, out);
}
return;
}
if inst.kind() != SymbolKind::Instance {
return;
}
if inst
.instance_definition()
.is_some_and(|d| d.name() == definition)
{
out.push(inst);
}
if let Some(body) = inst.instance_body() {
for m in super::module_items(&body) {
visit(m, definition, out);
}
}
}
let mut root = *any;
while let Some(p) = root.parent() {
root = p;
}
let mut out = Vec::new();
for top in root.members() {
visit(top, definition, &mut out);
}
out
}
pub fn instance_id(inst: &Symbol<'_>) -> Option<u64> {
let def = inst.instance_definition()?;
let pos = interface_instances(inst, def.name())
.iter()
.position(|c| c.hierarchical_path() == inst.hierarchical_path())?;
Some(pos as u64 + 1)
}
fn definition_of_member(member: &Symbol<'_>) -> Option<String> {
let mut cur = member.parent();
while let Some(s) = cur {
if s.kind() == SymbolKind::InstanceBody {
return Some(s.name().to_string());
}
cur = s.parent();
}
None
}
pub fn is_member_access(e: &Expression<'_>) -> bool {
e.kind() == ExpressionKind::MemberAccess
&& e.operand()
.or_else(|| e.select_value())
.and_then(|b| b.expr_type())
.is_some_and(|t| t.is_virtual_interface())
}
pub fn base<'d>(e: &Expression<'d>) -> Option<Expression<'d>> {
e.operand().or_else(|| e.select_value())
}
pub fn member_targets<'d>(e: &Expression<'d>) -> Option<Vec<Option<Symbol<'d>>>> {
if !is_member_access(e) {
return None;
}
let member = e.referenced_symbol()?;
let def = definition_of_member(&member)?;
let mut path = Vec::new();
let mut cur = Some(member);
while let Some(s) = cur {
if s.kind() == SymbolKind::InstanceBody {
break;
}
path.push(s.name().to_string());
cur = s.parent();
}
path.reverse();
Some(
interface_instances(&member, &def)
.into_iter()
.map(|inst| {
let mut at = inst.instance_body()?;
for name in &path {
at = at.find(name)?;
}
Some(at)
})
.map(|m| {
m.map(|s| {
if s.kind() == SymbolKind::ModportPort {
s.modport_port_internal_symbol().unwrap_or(s)
} else {
s
}
})
})
.collect(),
)
}
pub fn call_targets<'d>(e: &Expression<'d>) -> Option<(Expression<'d>, Vec<Option<Symbol<'d>>>)> {
if e.kind() != ExpressionKind::Call || e.is_system_call() == Some(true) {
return None;
}
let callee = e.call_subroutine()?;
let operands = e.operands();
let vif = *operands.first()?;
if !vif.expr_type().is_some_and(|t| t.is_virtual_interface())
|| callee.subroutine_arguments().count() + 1 != operands.len()
{
return None;
}
let def = definition_of_member(&callee)?;
Some((
vif,
interface_instances(&callee, &def)
.into_iter()
.map(|inst| inst.instance_body().and_then(|b| b.find(callee.name())))
.collect(),
))
}