use bincode_next::{Decode, Encode};
use super::pointer::RelPtr;
use crate::dag::node::DagNodeId;
use crate::dag::symbol::SymbolKind;
#[derive(Debug, Clone, PartialEq, Eq, Hash, Encode, Decode)]
pub enum AstChildList {
Empty,
One(RelPtr<AstNode>),
Two([RelPtr<AstNode>; 2]),
Three([RelPtr<AstNode>; 3]),
Four([RelPtr<AstNode>; 4]),
Many {
start: u32,
len: u32,
},
}
impl AstChildList {
#[must_use]
pub const fn len(&self) -> usize {
match self {
Self::Empty => 0,
Self::One(_) => 1,
Self::Two(_) => 2,
Self::Three(_) => 3,
Self::Four(_) => 4,
Self::Many { len, .. } => *len as usize,
}
}
#[must_use]
pub const fn is_empty(&self) -> bool {
matches!(self, Self::Empty)
}
#[must_use]
pub fn as_slice_with_pool<'a>(&'a self, pool: &'a [RelPtr<AstNode>]) -> &'a [RelPtr<AstNode>] {
match self {
Self::Empty => &[],
Self::One(ptr) => std::slice::from_ref(ptr),
Self::Two(arr) => arr,
Self::Three(arr) => arr,
Self::Four(arr) => arr,
Self::Many { start, len } => &pool[*start as usize..*start as usize + *len as usize],
}
}
#[must_use]
pub fn as_slice(&self) -> &[RelPtr<AstNode>] {
match self {
Self::Empty => &[],
Self::One(ptr) => std::slice::from_ref(ptr),
Self::Two(arr) => arr,
Self::Three(arr) => arr,
Self::Four(arr) => arr,
Self::Many { .. } => panic!("AstChildList::Many: use as_slice_with_pool"),
}
}
}
#[derive(Debug, Clone, Encode, Decode)]
pub struct AstNode {
pub kind: SymbolKind,
pub value: f64,
pub dag_id: DagNodeId,
pub children: AstChildList,
}
#[derive(Debug, Clone, Encode, Decode, Default)]
pub struct AstProjection {
pub nodes: Vec<AstNode>,
pub children_pool: Vec<RelPtr<AstNode>>,
}
impl AstProjection {
#[must_use]
pub const fn new() -> Self {
Self {
nodes: Vec::new(),
children_pool: Vec::new(),
}
}
#[must_use]
pub fn root(&self) -> Option<&AstNode> {
self.nodes.first()
}
#[must_use]
pub fn resolve(&self, source_idx: usize, ptr: RelPtr<AstNode>) -> Option<&AstNode> {
ptr.resolve(source_idx).and_then(|idx| self.nodes.get(idx))
}
#[must_use]
pub fn children_pool(&self) -> &[RelPtr<AstNode>] {
&self.children_pool
}
pub fn clear(&mut self) {
self.nodes.clear();
self.children_pool.clear();
}
#[must_use]
pub const fn len(&self) -> usize {
self.nodes.len()
}
#[must_use]
pub const fn is_empty(&self) -> bool {
self.nodes.is_empty()
}
}
pub trait PostOrderVisitor {
fn visit(&mut self, node: &AstNode, node_idx: usize) -> bool;
}
impl AstProjection {
pub fn visit_post<V: PostOrderVisitor>(&self, visitor: &mut V) {
if self.nodes.is_empty() {
return;
}
let mut stack: Vec<(usize, bool)> = vec![(0, false)];
while let Some((idx, visited)) = stack.pop() {
let Some(node) = self.nodes.get(idx) else {
continue;
};
if visited {
if !visitor.visit(node, idx) {
return;
}
} else {
stack.push((idx, true));
for ptr in node
.children
.as_slice_with_pool(&self.children_pool)
.iter()
.rev()
{
if let Some(child_idx) = ptr.resolve(idx)
&& child_idx < self.nodes.len()
{
stack.push((child_idx, false));
}
}
}
}
}
}