use crate::ast::{GroupBehavior, NodeKind, RcNode};
use std::collections::HashMap;
pub(crate) struct NodeExtra {
pub(crate) capturing_group_to_index: HashMap<usize, u64>,
pub(crate) index_to_capturing_group: HashMap<u64, RcNode>,
pub(crate) node_to_lookahead_stack: HashMap<usize, Vec<RcNode>>,
pub(crate) reachable_references: Vec<RcNode>,
}
pub(crate) fn build_node_extra(root: &RcNode) -> NodeExtra {
let mut extra = NodeExtra {
capturing_group_to_index: HashMap::new(),
index_to_capturing_group: HashMap::new(),
node_to_lookahead_stack: HashMap::new(),
reachable_references: Vec::new(),
};
visit(root, &[], true, &mut extra);
extra
}
fn visit(node: &RcNode, lookahead_stack: &[RcNode], reachable: bool, extra: &mut NodeExtra) {
match &node.kind {
NodeKind::Anchor { .. }
| NodeKind::CharacterClass { .. }
| NodeKind::CharacterClassEscape { .. }
| NodeKind::CharacterClassRange { .. }
| NodeKind::UnicodePropertyEscape { .. }
| NodeKind::Value { .. }
| NodeKind::Dot => {}
NodeKind::Reference { .. } => {
extra
.node_to_lookahead_stack
.insert(node.id, lookahead_stack.to_vec());
if reachable {
extra.reachable_references.push(node.clone());
}
}
NodeKind::Alternative { body } | NodeKind::Disjunction { body } => {
for child in body {
visit(child, lookahead_stack, reachable, extra);
}
}
NodeKind::Group { behavior, body } => {
if *behavior == GroupBehavior::Normal {
let index = extra.capturing_group_to_index.len() as u64 + 1;
extra.capturing_group_to_index.insert(node.id, index);
extra.index_to_capturing_group.insert(index, node.clone());
extra
.node_to_lookahead_stack
.insert(node.id, lookahead_stack.to_vec());
}
let mut new_stack = lookahead_stack.to_vec();
if behavior.is_lookaround() {
new_stack.push(node.clone());
}
for child in body {
visit(child, &new_stack, reachable, extra);
}
}
NodeKind::Quantifier { max, body, .. } => {
let child_reachable = reachable && *max != Some(0);
visit(body, lookahead_stack, child_reachable, extra);
}
}
}