use serde::{Deserialize, Serialize};
#[cfg(feature = "frontend")]
use sv_lang::Symbol;
#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
pub struct SymbolId {
path: String,
chain: Vec<u32>,
}
impl SymbolId {
pub fn synthetic(path: &str) -> SymbolId {
SymbolId {
path: path.to_string(),
chain: vec![u32::MAX],
}
}
pub fn path(&self) -> &str {
&self.path
}
pub fn synthetic_tagged(tag: &str) -> SymbolId {
SymbolId {
path: format!("${tag}"),
chain: vec![u32::MAX],
}
}
pub fn derive(&self, tag: &str) -> SymbolId {
SymbolId {
path: format!("{}${tag}", self.path),
chain: self.chain.clone(),
}
}
pub fn key_string(&self) -> String {
let chain: Vec<String> = self.chain.iter().map(u32::to_string).collect();
format!("{}#{}", self.path, chain.join("."))
}
}
impl core::fmt::Display for SymbolId {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str(&self.path)
}
}
#[cfg(feature = "frontend")]
pub fn is_value_name(kind: sv_lang_kinds::ExpressionKind) -> bool {
matches!(
kind,
sv_lang_kinds::ExpressionKind::NamedValue
| sv_lang_kinds::ExpressionKind::HierarchicalValue
| sv_lang_kinds::ExpressionKind::ArbitrarySymbol
)
}
#[cfg(feature = "frontend")]
pub trait SymbolUid {
fn uid(&self) -> SymbolId;
}
#[cfg(feature = "frontend")]
type UidMemo = std::collections::HashMap<(usize, usize), SymbolId>;
#[cfg(feature = "frontend")]
thread_local! {
static UID_MEMO: std::cell::RefCell<Option<UidMemo>> = const { std::cell::RefCell::new(None) };
}
#[cfg(feature = "frontend")]
pub struct UidMemoGuard;
#[cfg(feature = "frontend")]
impl UidMemoGuard {
pub fn enter() -> Self {
UID_MEMO.with(|m| *m.borrow_mut() = Some(UidMemo::new()));
UidMemoGuard
}
}
#[cfg(feature = "frontend")]
impl Drop for UidMemoGuard {
fn drop(&mut self) {
UID_MEMO.with(|m| *m.borrow_mut() = None);
}
}
#[cfg(feature = "frontend")]
pub fn symbol_identity(sym: &Symbol<'_>) -> (usize, usize) {
const _: () =
assert!(std::mem::size_of::<Symbol<'static>>() >= 2 * std::mem::size_of::<usize>());
unsafe {
let p = sym as *const Symbol<'_> as *const usize;
(*p, *p.add(1))
}
}
#[cfg(feature = "frontend")]
impl SymbolUid for Symbol<'_> {
fn uid(&self) -> SymbolId {
let key = symbol_identity(self);
if let Some(hit) = UID_MEMO.with(|m| m.borrow().as_ref().and_then(|m| m.get(&key).cloned()))
{
return hit;
}
let id = self.uid_uncached();
UID_MEMO.with(|m| {
if let Some(m) = m.borrow_mut().as_mut() {
m.insert(key, id.clone());
}
});
id
}
}
#[cfg(feature = "frontend")]
trait UidUncached {
fn uid_uncached(&self) -> SymbolId;
}
#[cfg(feature = "frontend")]
impl UidUncached for Symbol<'_> {
fn uid_uncached(&self) -> SymbolId {
let mut chain = Vec::new();
let mut cur = Some(*self);
while let Some(s) = cur {
let parent = s.parent();
if parent.is_none() {
break;
}
chain.push(s.lookup_location_before().index());
cur = parent;
}
chain.reverse();
SymbolId {
path: self.hierarchical_path(),
chain,
}
}
}