use crate::contract;
use zerodds_ast::{Ast, TypeKind, TypeNode};
use zerodds_ast_core::Id;
pub fn recursive_aggregates(ast: &Ast, root: Id<TypeNode>) -> Vec<Id<TypeNode>> {
let mut all: Vec<Id<TypeNode>> = Vec::new();
let mut queue = Vec::new();
collect_aggregates(ast, root, &mut queue, &mut Vec::new());
while let Some(id) = queue.pop() {
if all.contains(&id) {
continue;
}
all.push(id);
queue.extend(aggregate_successors(ast, id));
}
all.into_iter()
.filter(|id| reaches(ast, *id, *id, &mut Vec::new()))
.collect()
}
fn reaches(
ast: &Ast,
from: Id<TypeNode>,
target: Id<TypeNode>,
seen: &mut Vec<Id<TypeNode>>,
) -> bool {
for next in aggregate_successors(ast, from) {
if next == target {
return true;
}
if !seen.contains(&next) {
seen.push(next);
if reaches(ast, next, target, seen) {
return true;
}
}
}
false
}
fn aggregate_successors(ast: &Ast, type_: Id<TypeNode>) -> Vec<Id<TypeNode>> {
let mut found = Vec::new();
let Ok(node) = ast.type_(type_) else {
return found;
};
match &node.kind {
TypeKind::Structure { .. } | TypeKind::Valuetype { .. } => {
for member in contract::members(ast, type_) {
collect_aggregates(ast, member.type_, &mut found, &mut Vec::new());
}
}
TypeKind::Union { .. } => {
for case in contract::union_cases(ast, type_) {
collect_aggregates(ast, case.type_, &mut found, &mut Vec::new());
}
}
_ => {}
}
found
}
fn collect_aggregates(
ast: &Ast,
type_: Id<TypeNode>,
found: &mut Vec<Id<TypeNode>>,
open: &mut Vec<Id<TypeNode>>,
) {
let Ok(node) = ast.type_(type_) else {
return;
};
if open.contains(&type_) {
return;
}
open.push(type_);
match &node.kind {
TypeKind::Alias { aliased } => {
if let Some(target) = aliased.resolved() {
collect_aggregates(ast, target, found, open);
}
}
TypeKind::Sequence { element, .. } | TypeKind::Array { element, .. } => {
if let Some(target) = element.resolved() {
collect_aggregates(ast, target, found, open);
}
}
TypeKind::Map { key, value, .. } => {
if let Some(target) = key.resolved() {
collect_aggregates(ast, target, found, open);
}
if let Some(target) = value.resolved() {
collect_aggregates(ast, target, found, open);
}
}
TypeKind::Structure { .. } | TypeKind::Valuetype { .. } | TypeKind::Union { .. } => {
if !found.contains(&type_) {
found.push(type_);
}
}
_ => {}
}
}