use crate::parse::ExprTree;
use crate::Expr;
use crate::LookAround;
use alloc::boxed::Box;
use alloc::vec;
use core::mem;
pub fn optimize(tree: &mut ExprTree) -> bool {
if !tree.self_recursive {
optimize_trailing_lookahead(tree)
} else {
false
}
}
fn optimize_trailing_lookahead(tree: &mut ExprTree) -> bool {
if let Expr::Concat(ref mut root_concat_children) = tree.expr {
if let Some(Expr::LookAround(_, LookAround::LookAhead)) = root_concat_children.last() {
let lookahead_expr = root_concat_children
.pop()
.expect("lookaround should be popped");
let group0_children = mem::take(root_concat_children);
if let Expr::LookAround(inner, LookAround::LookAhead) = lookahead_expr {
let group0 = Expr::Group(Box::new(Expr::Concat(group0_children)));
let new_concat = Expr::Concat(vec![group0, *inner]);
tree.expr = new_concat;
return true;
} else {
unreachable!("already checked it is a lookahead");
}
}
} else if let Expr::LookAround(inner, LookAround::LookAhead) = &mut tree.expr {
let group0 = Expr::Group(Box::new(Expr::Empty));
let mut swap = Expr::Empty;
mem::swap(&mut swap, inner);
tree.expr = Expr::Concat(vec![group0, swap]);
return true;
}
false
}
#[cfg(test)]
mod tests {
use super::optimize;
use super::vec;
use super::Box;
use crate::parse::make_literal;
use crate::Expr;
use alloc::string::String;
#[test]
fn trailing_positive_lookahead_optimized() {
let mut tree = Expr::parse_tree("a(?=b)").unwrap();
let requires_capture_group_fixup = optimize(&mut tree);
assert_eq!(requires_capture_group_fixup, true);
let mut s = String::new();
tree.expr.to_str(&mut s, 0);
assert_eq!(s, "(a)b");
}
#[test]
fn standalone_positive_lookahead_optimized() {
let mut tree = Expr::parse_tree("(?=b)").unwrap();
let requires_capture_group_fixup = optimize(&mut tree);
assert_eq!(requires_capture_group_fixup, true);
let mut s = String::new();
tree.expr.to_str(&mut s, 0);
assert_eq!(s, "()b");
}
#[test]
fn trailing_positive_lookahead_with_alternative_optimized() {
let mut tree = Expr::parse_tree("a(?=b|c)").unwrap();
let requires_capture_group_fixup = optimize(&mut tree);
assert_eq!(requires_capture_group_fixup, true);
let mut s = String::new();
tree.expr.to_str(&mut s, 0);
assert_eq!(s, "(a)(?:b|c)");
}
#[test]
fn trailing_positive_lookahead_moved_even_if_not_easy() {
let mut tree = Expr::parse_tree(r"(a)\1(?=c)").unwrap();
let requires_capture_group_fixup = optimize(&mut tree);
assert_eq!(requires_capture_group_fixup, true);
assert_eq!(
tree.expr,
Expr::Concat(vec![
Expr::Group(Box::new(Expr::Concat(vec![
Expr::Group(Box::new(make_literal("a"))),
Expr::Backref {
group: 1,
casei: false
}
]))),
make_literal("c"),
])
);
}
#[test]
fn trailing_positive_lookahead_left_alone_when_self_recursive() {
let tree = Expr::parse_tree(r"ab?\g<0>?(?=a|$)").unwrap();
let mut optimized_tree = tree.clone();
let requires_capture_group_fixup = optimize(&mut optimized_tree);
assert_eq!(requires_capture_group_fixup, false);
assert_eq!(&optimized_tree.expr, &tree.expr);
}
#[test]
fn trailing_negative_lookahead_left_alone() {
let tree = Expr::parse_tree(r"a(?!b)").unwrap();
let mut optimized_tree = tree.clone();
let requires_capture_group_fixup = optimize(&mut optimized_tree);
assert_eq!(requires_capture_group_fixup, false);
assert_eq!(&optimized_tree.expr, &tree.expr);
}
#[test]
fn trailing_positive_lookbehind_left_alone() {
let tree = Expr::parse_tree(r"(?<=b)").unwrap();
let mut optimized_tree = tree.clone();
let requires_capture_group_fixup = optimize(&mut optimized_tree);
assert_eq!(requires_capture_group_fixup, false);
assert_eq!(&optimized_tree.expr, &tree.expr);
}
#[test]
fn non_trailing_positive_lookahead_left_alone() {
let tree = Expr::parse_tree(r"a(?=(b))\1").unwrap();
let mut optimized_tree = tree.clone();
let requires_capture_group_fixup = optimize(&mut optimized_tree);
assert_eq!(requires_capture_group_fixup, false);
assert_eq!(&optimized_tree.expr, &tree.expr);
let tree = Expr::parse_tree(r"(?=(b))\1").unwrap();
let mut optimized_tree = tree.clone();
let requires_capture_group_fixup = optimize(&mut optimized_tree);
assert_eq!(requires_capture_group_fixup, false);
assert_eq!(&optimized_tree.expr, &tree.expr);
}
}