Skip to main content

groups/
groups.rs

1//! # Groups
2//!
3//! Shows every group variant the parser supports: capturing, named, non-capturing,
4//! and all four lookaround assertions.
5//!
6//! Run with:
7//!   cargo run -p re-parser --example groups
8
9use re_parser::ast::{GroupKind, Regex};
10use re_parser::parse;
11
12fn group_kind_label(kind: &GroupKind) -> &'static str {
13    match kind {
14        GroupKind::Capturing => "capturing",
15        GroupKind::Named(_) => "named capturing",
16        GroupKind::NonCapturing => "non-capturing",
17        GroupKind::LookaheadPos => "positive lookahead",
18        GroupKind::LookaheadNeg => "negative lookahead",
19        GroupKind::LookbehindPos => "positive lookbehind",
20        GroupKind::LookbehindNeg => "negative lookbehind",
21    }
22}
23
24fn print_group(pattern: &str) {
25    let ast = parse(pattern).unwrap();
26
27    // The outermost node might be a Concat if there is content outside the group.
28    // Walk one level to find the first Group node for display.
29    let group_node = match &ast {
30        Regex::Group(_, _) => Some(&ast),
31        Regex::Concat(nodes) => nodes.iter().find(|n| matches!(n, Regex::Group(_, _))),
32        _ => None,
33    };
34
35    match group_node {
36        Some(Regex::Group(inner, kind)) => {
37            let label = group_kind_label(kind);
38            let name_part = if let GroupKind::Named(n) = kind {
39                format!(" (name = \"{n}\")")
40            } else {
41                String::new()
42            };
43            println!(
44                "  {pattern:20}  =>  {label}{name_part}, inner = {:?}",
45                inner
46            );
47        }
48        _ => println!("  {pattern:20}  =>  {ast:?}"),
49    }
50}
51
52fn main() {
53    println!("=== Capturing & non-capturing ===");
54    print_group("(abc)");
55    print_group("(?:abc)");
56    print_group("(?P<word>\\w+)");
57
58    println!("\n=== Lookahead assertions ===");
59    print_group("foo(?=bar)");   // 'foo' only when followed by 'bar'
60    print_group("foo(?!bar)");   // 'foo' only when NOT followed by 'bar'
61
62    println!("\n=== Lookbehind assertions ===");
63    print_group("(?<=\\d)px");   // 'px' only when preceded by a digit
64    print_group("(?<!\\d)px");   // 'px' only when NOT preceded by a digit
65
66    println!("\n=== Nested groups (full pretty AST) ===");
67    let pattern = "(?P<date>(?P<year>\\d{4})-(?P<month>\\d{2}))";
68    println!("  Pattern: {pattern}");
69    println!("  AST:\n{:#?}", parse(pattern).unwrap());
70}