use boreal_parser::regex::AssertionKind;
use crate::regex::{visit, Hir, VisitAction, Visitor};
pub fn widen_hir(hir: &Hir) -> Hir {
visit(hir, HirWidener::new())
}
#[derive(Debug)]
struct HirWidener {
hir: Option<Hir>,
stack: Vec<StackLevel>,
}
#[derive(Debug)]
struct StackLevel {
hirs: Vec<Hir>,
in_concat: bool,
}
impl StackLevel {
fn new(in_concat: bool) -> Self {
Self {
hirs: Vec::new(),
in_concat,
}
}
fn push(&mut self, hir: Hir) {
self.hirs.push(hir);
}
}
impl HirWidener {
fn new() -> Self {
Self {
hir: None,
stack: Vec::new(),
}
}
fn add(&mut self, hir: Hir) {
if self.stack.is_empty() {
let res = self.hir.replace(hir);
assert!(res.is_none(), "top level HIR hir already set");
} else {
let pos = self.stack.len() - 1;
self.stack[pos].push(hir);
}
}
fn add_wide(&mut self, hir: Hir) {
let nul_byte = Hir::Literal(b'\0');
if self.stack.is_empty() {
let res = self.hir.replace(Hir::Concat(vec![hir, nul_byte]));
assert!(res.is_none(), "top level HIR hir already set");
} else {
let pos = self.stack.len() - 1;
let level = &mut self.stack[pos];
if level.in_concat {
level.hirs.push(hir);
level.hirs.push(nul_byte);
} else {
level
.hirs
.push(Hir::Group(Box::new(Hir::Concat(vec![hir, nul_byte]))));
}
}
}
}
impl Visitor for HirWidener {
type Output = Hir;
fn finish(self) -> Hir {
self.hir.unwrap()
}
fn visit_pre(&mut self, node: &Hir) -> VisitAction {
match node {
Hir::Dot
| Hir::Empty
| Hir::Literal(_)
| Hir::Mask { .. }
| Hir::Class(_)
| Hir::Assertion(_) => (),
Hir::Repetition { .. } | Hir::Group(_) | Hir::Alternation(_) => {
self.stack.push(StackLevel::new(false));
}
Hir::Concat(_) => {
self.stack.push(StackLevel::new(true));
}
}
VisitAction::Continue
}
fn visit_post(&mut self, hir: &Hir) {
match hir {
Hir::Empty => self.add(Hir::Empty),
Hir::Dot => self.add_wide(Hir::Dot),
Hir::Literal(lit) => self.add_wide(Hir::Literal(*lit)),
Hir::Mask { .. } => self.add_wide(hir.clone()),
Hir::Class(cls) => self.add_wide(Hir::Class(cls.clone())),
Hir::Assertion(AssertionKind::StartLine) | Hir::Assertion(AssertionKind::EndLine) => {
self.add(hir.clone());
}
Hir::Assertion(AssertionKind::WordBoundary)
| Hir::Assertion(AssertionKind::NonWordBoundary) => {
self.add(Hir::Empty);
}
Hir::Repetition {
hir: _,
kind,
greedy,
} => {
let hir = self.stack.pop().unwrap().hirs.pop().unwrap();
self.add(Hir::Repetition {
kind: kind.clone(),
greedy: *greedy,
hir: Box::new(hir),
});
}
Hir::Group(_) => {
let node = self.stack.pop().unwrap().hirs.pop().unwrap();
self.add(Hir::Group(Box::new(node)));
}
Hir::Concat(_) => {
let vec = self.stack.pop().unwrap().hirs;
self.add(Hir::Concat(vec));
}
Hir::Alternation(_) => {
let vec = self.stack.pop().unwrap().hirs;
self.add(Hir::Alternation(vec));
}
}
}
}
#[cfg(test)]
mod tests {
use crate::test_helpers::test_type_traits_non_clonable;
use super::*;
#[test]
fn test_types_traits() {
test_type_traits_non_clonable(HirWidener::new());
test_type_traits_non_clonable(StackLevel::new(false));
}
}