use std::iter::FusedIterator;
use oxc_ast::{AstKind, ast::Program};
use oxc_index::{IndexSlice, IndexVec};
use oxc_syntax::{
node::{NodeFlags, NodeId},
scope::ScopeId,
};
#[cfg(feature = "linter")]
use oxc_ast::AstType;
#[cfg(feature = "cfg")]
use oxc_cfg::BlockNodeId;
use super::AstNode;
#[cfg(feature = "linter")]
use crate::ast_types_bitset::AstTypesBitset;
#[derive(Debug, Default)]
pub struct AstNodes<'a> {
nodes: IndexVec<NodeId, AstNode<'a>>,
parent_ids: IndexVec<NodeId, NodeId>,
flags: IndexVec<NodeId, NodeFlags>,
#[cfg(feature = "cfg")]
cfg_ids: IndexVec<NodeId, BlockNodeId>,
#[cfg(feature = "linter")]
node_kinds_set: AstTypesBitset,
}
impl<'a> AstNodes<'a> {
pub fn iter(&self) -> impl Iterator<Item = &AstNode<'a>> + '_ {
self.nodes.iter()
}
pub fn iter_enumerated(&self) -> impl Iterator<Item = (NodeId, &AstNode<'a>)> + '_ {
self.nodes.iter_enumerated()
}
#[inline]
pub fn len(&self) -> usize {
self.nodes.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.nodes.is_empty()
}
#[inline]
pub fn ancestor_ids(&self, node_id: NodeId) -> impl Iterator<Item = NodeId> + Clone + '_ {
AstNodeIdAncestorsIter::new(node_id, self)
}
#[inline]
pub fn ancestor_kinds(
&self,
node_id: NodeId,
) -> impl Iterator<Item = AstKind<'a>> + Clone + '_ {
self.ancestor_ids(node_id).map(|id| self.kind(id))
}
#[inline]
pub fn ancestors(&self, node_id: NodeId) -> impl Iterator<Item = &AstNode<'a>> + Clone + '_ {
self.ancestor_ids(node_id).map(|id| self.get_node(id))
}
#[inline]
pub fn ancestors_enumerated(
&self,
node_id: NodeId,
) -> impl Iterator<Item = (NodeId, &AstNode<'a>)> + Clone + '_ {
self.ancestor_ids(node_id).map(|id| (id, self.get_node(id)))
}
#[inline]
pub fn kind(&self, node_id: NodeId) -> AstKind<'a> {
self.nodes[node_id].kind()
}
#[inline]
pub fn parent_id(&self, node_id: NodeId) -> NodeId {
self.parent_ids[node_id]
}
pub fn parent_kind(&self, node_id: NodeId) -> AstKind<'a> {
self.kind(self.parent_id(node_id))
}
pub fn parent_node(&self, node_id: NodeId) -> &AstNode<'a> {
self.get_node(self.parent_id(node_id))
}
#[inline]
pub fn get_node(&self, node_id: NodeId) -> &AstNode<'a> {
&self.nodes[node_id]
}
#[inline]
pub fn get_node_mut(&mut self, node_id: NodeId) -> &mut AstNode<'a> {
&mut self.nodes[node_id]
}
#[inline]
pub fn flags(&self, node_id: NodeId) -> NodeFlags {
self.flags[node_id]
}
#[inline]
pub fn flags_mut(&mut self, node_id: NodeId) -> &mut NodeFlags {
&mut self.flags[node_id]
}
#[inline]
#[cfg(feature = "cfg")]
pub fn cfg_id(&self, node_id: NodeId) -> BlockNodeId {
self.cfg_ids[node_id]
}
#[inline]
pub fn program(&self) -> &'a Program<'a> {
if let Some(node) = self.nodes.first()
&& let AstKind::Program(program) = node.kind()
{
return program;
}
unreachable!();
}
#[inline]
pub fn add_node(
&mut self,
kind: AstKind<'a>,
scope_id: ScopeId,
parent_node_id: NodeId,
#[cfg(feature = "cfg")] cfg_id: BlockNodeId,
flags: NodeFlags,
) -> NodeId {
let node_id = self.parent_ids.push(parent_node_id);
kind.set_node_id(node_id);
let node = AstNode::new(kind, scope_id);
self.nodes.push(node);
self.flags.push(flags);
#[cfg(feature = "cfg")]
self.cfg_ids.push(cfg_id);
#[cfg(feature = "linter")]
self.node_kinds_set.set(kind.ty());
node_id
}
pub fn add_program_node(
&mut self,
kind: AstKind<'a>,
scope_id: ScopeId,
#[cfg(feature = "cfg")] cfg_id: BlockNodeId,
flags: NodeFlags,
) -> NodeId {
assert!(self.parent_ids.is_empty(), "Program node must be the first node in the AST.");
debug_assert!(
matches!(kind, AstKind::Program(_)),
"Program node must be of kind `AstKind::Program`"
);
kind.set_node_id(NodeId::ROOT);
self.parent_ids.push(NodeId::ROOT);
self.nodes.push(AstNode::new(kind, scope_id));
self.flags.push(flags);
#[cfg(feature = "cfg")]
self.cfg_ids.push(cfg_id);
#[cfg(feature = "linter")]
self.node_kinds_set.set(AstType::Program);
NodeId::ROOT
}
pub fn reserve(&mut self, additional: usize) {
self.nodes.reserve(additional);
self.parent_ids.reserve(additional);
self.flags.reserve(additional);
#[cfg(feature = "cfg")]
self.cfg_ids.reserve(additional);
}
#[cfg(feature = "linter")]
pub fn contains_any(&self, bitset: &AstTypesBitset) -> bool {
self.node_kinds_set.intersects(bitset)
}
#[cfg(feature = "linter")]
pub fn contains_all(&self, bitset: &AstTypesBitset) -> bool {
self.node_kinds_set.contains(bitset)
}
#[cfg(feature = "linter")]
pub fn contains(&self, ty: AstType) -> bool {
self.node_kinds_set.has(ty)
}
}
impl<'a, 'n> IntoIterator for &'n AstNodes<'a> {
type IntoIter = std::slice::Iter<'n, AstNode<'a>>;
type Item = &'n AstNode<'a>;
fn into_iter(self) -> Self::IntoIter {
self.nodes.iter()
}
}
#[derive(Debug, Clone)]
pub struct AstNodeIdAncestorsIter<'n> {
current_node_id: NodeId,
parent_ids: &'n IndexSlice<NodeId, [NodeId]>,
}
impl<'n> AstNodeIdAncestorsIter<'n> {
fn new(node_id: NodeId, nodes: &'n AstNodes<'_>) -> Self {
Self { current_node_id: node_id, parent_ids: nodes.parent_ids.as_slice() }
}
}
impl Iterator for AstNodeIdAncestorsIter<'_> {
type Item = NodeId;
fn next(&mut self) -> Option<Self::Item> {
if self.current_node_id == NodeId::ROOT {
return None;
}
self.current_node_id = self.parent_ids[self.current_node_id];
Some(self.current_node_id)
}
}
impl FusedIterator for AstNodeIdAncestorsIter<'_> {}