use std::collections::HashMap;
use sv_lang::kinds::SymbolKind;
use sv_lang::nodes::{
DataDeclarationSyntax, ImplicitAnsiPortSyntax, NetDeclarationSyntax, NetTypeDeclarationSyntax,
PortDeclarationSyntax, PortHeaderSyntax,
};
use sv_lang::{AstNode, EvalSession, Symbol, SyntaxTree};
use vj_codegen::process_lower::SignalSlot;
use vj_sim::netlist::InstanceId;
use vj_sim::symid::{SymbolId, SymbolUid};
use crate::nets::NetId;
pub(crate) struct UserNetType<'d> {
pub name: String,
pub func: Option<Symbol<'d>>,
pub init: Vec<sv_lang::Bit>,
pub data_array: bool,
}
#[derive(Default)]
pub(crate) struct UserNets<'d> {
pub by_net: HashMap<NetId, usize>,
pub types: Vec<UserNetType<'d>>,
}
pub(crate) fn declared_type_name(
net: &Symbol<'_>,
trees: &[SyntaxTree],
) -> Option<(String, usize)> {
for tree in trees {
let Some(mut node) = net.syntax(tree) else {
continue;
};
loop {
if NetDeclarationSyntax::cast(node).is_some() {
return None;
}
let name = net.name();
let (ty, dims) = if let Some(d) = DataDeclarationSyntax::cast(node) {
let dims = d
.declarators()
.iter()
.find(|x| x.name().is_some_and(|t| t.text() == name))
.map_or(0, |x| x.dimensions().len());
(Some(without_comments(&d.r#type().syntax().text())), dims)
} else if let Some(p) = ImplicitAnsiPortSyntax::cast(node) {
(header_type(p.header()), p.declarator().dimensions().len())
} else if let Some(p) = PortDeclarationSyntax::cast(node) {
let dims = p
.declarators()
.iter()
.find(|x| x.name().is_some_and(|t| t.text() == name))
.map_or(0, |x| x.dimensions().len());
(header_type(p.header()), dims)
} else {
node = node.parent()?;
continue;
};
let text = ty?.trim().to_string();
let ident = !text.is_empty()
&& text
.chars()
.all(|c| c.is_ascii_alphanumeric() || matches!(c, '_' | '$' | ':'))
&& text.chars().next().is_some_and(|c| !c.is_ascii_digit());
return ident.then_some((text, dims));
}
}
None
}
fn without_comments(text: &str) -> String {
let mut out = String::with_capacity(text.len());
let mut rest = text;
while let Some(i) = rest.find("/") {
out.push_str(&rest[..i]);
let tail = &rest[i..];
if tail.starts_with("//") {
rest = tail.find('\n').map_or("", |n| &tail[n..]);
} else if let Some(body) = tail.strip_prefix("/*") {
rest = body.find("*/").map_or("", |n| &body[n + 2..]);
} else {
out.push('/');
rest = &tail[1..];
}
}
out.push_str(rest);
out
}
fn header_type(h: PortHeaderSyntax<'_>) -> Option<String> {
match h {
PortHeaderSyntax::VariablePortHeader(h) => {
Some(without_comments(&h.data_type().syntax().text()))
}
PortHeaderSyntax::NetPortHeader(h) if h.net_type().is_none() => {
Some(without_comments(&h.data_type().syntax().text()))
}
_ => None,
}
}
fn declaration<'e>(
eval: &'e EvalSession<'_>,
scope: Symbol<'e>,
name: &str,
trees: &[SyntaxTree],
) -> Option<Symbol<'e>> {
let mut sym = eval.lookup_name(scope, name)?;
for _ in 0..32 {
if sym.kind() != SymbolKind::NetType || sym.net_type_is_built_in() {
return None;
}
if sym.net_type_resolution_function().is_some() {
return Some(sym);
}
let Some(target) = alias_target(&sym, trees) else {
return Some(sym);
};
let Some(scope) = sym.parent() else {
return Some(sym);
};
match eval.lookup_name(scope, &target) {
Some(next) if next.kind() == SymbolKind::NetType && !next.net_type_is_built_in() => {
sym = next
}
_ => return Some(sym),
}
}
Some(sym)
}
pub(crate) fn alias_target(nettype: &Symbol<'_>, trees: &[SyntaxTree]) -> Option<String> {
for tree in trees {
let Some(node) = nettype.syntax(tree) else {
continue;
};
let mut node = node;
loop {
if let Some(d) = NetTypeDeclarationSyntax::cast(node) {
return Some(
without_comments(&d.r#type().syntax().text())
.trim()
.to_string(),
);
}
node = node.parent()?;
}
}
None
}
pub(crate) fn user_net_types<'e>(
eval: &'e EvalSession<'_>,
instances: &[(InstanceId, Symbol<'e>, Symbol<'e>, Option<InstanceId>)],
signals: &HashMap<InstanceId, HashMap<SymbolId, SignalSlot>>,
trees: &[SyntaxTree],
) -> Result<UserNets<'e>, String> {
let mut out = UserNets::default();
let mut looked: HashMap<(SymbolId, String), Option<usize>> = HashMap::new();
let mut by_decl: HashMap<SymbolId, usize> = HashMap::new();
for (id, body, _, _) in instances {
let own = &signals[id];
for m in vj_sim::netlist::module_items(body) {
if m.kind() != SymbolKind::Net || !own.contains_key(&m.uid()) {
continue;
}
let Some((name, dims)) = declared_type_name(&m, trees) else {
continue;
};
let key = (body.uid(), name.clone());
let found = match looked.get(&key) {
Some(f) => *f,
None => {
let f = match declaration(eval, *body, &name, trees) {
Some(decl) => match by_decl.get(&decl.uid()) {
Some(&i) => Some(i),
None => {
let mut ty = m.value_type().ok_or_else(|| {
format!(
"{}: a net of nettype `{name}` has no data type",
m.hierarchical_path()
)
})?;
for _ in 0..dims {
ty = ty.element_type().ok_or_else(|| {
format!(
"{}: cannot find the element type of an array of `{name}` nets",
m.hierarchical_path()
)
})?;
}
let mut data_array = false;
while ty.is_unpacked_array() {
data_array = true;
ty = ty.element_type().ok_or_else(|| {
format!(
"{}: cannot find the element type of nettype `{name}`'s array data type",
m.hierarchical_path()
)
})?;
}
vj_sim::state::storage::storage_width(&ty).ok_or_else(|| {
format!(
"{}: the data type of nettype `{name}` has no stored form",
m.hierarchical_path()
)
})?;
let init = vj_sim::state::storage::default_bits(&ty)
.ok_or_else(|| {
format!(
"{}: the data type of nettype `{name}` has no default value",
m.hierarchical_path()
)
})?;
out.types.push(UserNetType {
name: name.clone(),
func: decl.net_type_resolution_function(),
init,
data_array,
});
by_decl.insert(decl.uid(), out.types.len() - 1);
Some(out.types.len() - 1)
}
},
None => None,
};
looked.insert(key, f);
f
}
};
if let Some(t) = found {
out.by_net.insert((*id, m.uid()), t);
}
}
}
Ok(out)
}