use std::collections::{HashMap, HashSet, VecDeque};
use std::sync::Arc;
use crate::ts_syn::abi::SpanIR;
use crate::ts_syn::declarative::{BodyToken, MacroDef};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum RegistryError {
DuplicateName(String),
}
impl std::fmt::Display for RegistryError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
RegistryError::DuplicateName(name) => {
write!(
f,
"declarative macro `${}` is defined more than once in this file",
name
)
}
}
}
}
impl std::error::Error for RegistryError {}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CycleError {
pub names: Vec<String>,
}
impl std::fmt::Display for CycleError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"declarative macros form a cycle: {}",
self.names
.iter()
.map(|n| format!("${}", n))
.collect::<Vec<_>>()
.join(" → ")
)
}
}
impl std::error::Error for CycleError {}
#[derive(Debug, Default, Clone)]
pub struct DeclarativeMacroRegistry {
entries: Vec<ScopedEntry>,
}
#[derive(Debug, Clone)]
struct ScopedEntry {
def: Arc<MacroDef>,
scope_span: SpanIR,
}
impl DeclarativeMacroRegistry {
pub fn new() -> Self {
Self::default()
}
pub fn register(&mut self, def: MacroDef) -> Result<(), RegistryError> {
self.register_scoped(def, SpanIR::new(1, u32::MAX))
}
pub fn register_scoped(
&mut self,
def: MacroDef,
scope_span: SpanIR,
) -> Result<(), RegistryError> {
for existing in &self.entries {
if existing.def.name == def.name
&& !spans_are_disjoint_or_nested(existing.scope_span, scope_span)
{
return Err(RegistryError::DuplicateName(def.name));
}
}
self.entries.push(ScopedEntry {
def: Arc::new(def),
scope_span,
});
Ok(())
}
pub fn lookup(&self, name: &str) -> Option<&Arc<MacroDef>> {
self.entries
.iter()
.filter(|e| e.def.name == name)
.max_by_key(|e| (e.scope_span.end as u64).saturating_sub(e.scope_span.start as u64))
.map(|e| &e.def)
}
pub fn lookup_at(&self, name: &str, pos: u32) -> Option<&Arc<MacroDef>> {
self.entries
.iter()
.filter(|e| e.def.name == name && span_contains(e.scope_span, pos))
.min_by_key(|e| (e.scope_span.end as u64).saturating_sub(e.scope_span.start as u64))
.map(|e| &e.def)
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
pub fn len(&self) -> usize {
self.entries.len()
}
pub fn iter(&self) -> impl Iterator<Item = (&String, &Arc<MacroDef>)> {
self.entries.iter().map(|e| (&e.def.name, &e.def))
}
pub fn topological_order(&self) -> Result<Vec<Arc<MacroDef>>, CycleError> {
let mut by_name: HashMap<String, Arc<MacroDef>> = HashMap::new();
for entry in &self.entries {
by_name
.entry(entry.def.name.clone())
.and_modify(|existing| {
let existing_size = existing.span.end.saturating_sub(existing.span.start);
let candidate_size =
entry.scope_span.end.saturating_sub(entry.scope_span.start);
if candidate_size > existing_size {
*existing = entry.def.clone();
}
})
.or_insert_with(|| entry.def.clone());
}
let mut edges: HashMap<String, HashSet<String>> = HashMap::new();
let mut reverse: HashMap<String, HashSet<String>> = HashMap::new();
for (name, def) in &by_name {
let callees = collect_macro_call_names(&def.arms);
edges
.entry(name.clone())
.or_default()
.extend(callees.iter().cloned());
for callee in &callees {
if by_name.contains_key(callee) {
reverse
.entry(callee.clone())
.or_default()
.insert(name.clone());
}
}
}
let mut in_degree: HashMap<String, usize> = HashMap::new();
for name in by_name.keys() {
let out = edges
.get(name)
.map(|s| s.iter().filter(|c| by_name.contains_key(*c)).count())
.unwrap_or(0);
in_degree.insert(name.clone(), out);
}
let mut queue: VecDeque<String> = in_degree
.iter()
.filter(|&(_, d)| *d == 0)
.map(|(n, _)| n.clone())
.collect();
let mut sorted: Vec<Arc<MacroDef>> = Vec::with_capacity(by_name.len());
let mut visited: HashSet<String> = HashSet::new();
while let Some(name) = queue.pop_front() {
if !visited.insert(name.clone()) {
continue;
}
if let Some(def) = by_name.get(&name) {
sorted.push(def.clone());
}
if let Some(dependents) = reverse.get(&name) {
for dep in dependents {
if let Some(d) = in_degree.get_mut(dep) {
*d = d.saturating_sub(1);
if *d == 0 {
queue.push_back(dep.clone());
}
}
}
}
}
if sorted.len() != by_name.len() {
let cycle_names: Vec<String> = by_name
.keys()
.filter(|n| !visited.contains(*n))
.cloned()
.collect();
return Err(CycleError { names: cycle_names });
}
Ok(sorted)
}
}
fn span_contains(span: SpanIR, pos: u32) -> bool {
pos >= span.start && pos < span.end
}
fn spans_are_disjoint_or_nested(a: SpanIR, b: SpanIR) -> bool {
if a.start == b.start && a.end == b.end {
return false;
}
if a.start <= b.start && b.end <= a.end {
return true;
}
if b.start <= a.start && a.end <= b.end {
return true;
}
if a.end <= b.start || b.end <= a.start {
return true;
}
false
}
fn collect_macro_call_names(arms: &[crate::ts_syn::declarative::MacroArm]) -> HashSet<String> {
let mut out = HashSet::new();
for arm in arms {
collect_from_tokens(&arm.body.0, &mut out);
}
out
}
fn collect_from_tokens(tokens: &[BodyToken], out: &mut HashSet<String>) {
for t in tokens {
match t {
BodyToken::MacroCall { name, args } => {
out.insert(name.clone());
collect_from_tokens(args, out);
}
BodyToken::Repetition { body, .. } => collect_from_tokens(body, out),
BodyToken::Literal(_) | BodyToken::Substitution(_) => {}
}
}
}