ast_visitor/
ast_visitor.rs1use re_parser::ast::{CharClassItem, Regex};
13use re_parser::parse;
14
15fn count_nodes(node: &Regex) -> usize {
18 match node {
19 Regex::Literal(_) | Regex::AnyChar | Regex::Anchor(_) | Regex::EscapeClass(_) => 1,
20 Regex::CharClass(_) => 1,
21 Regex::Group(inner, _) => 1 + count_nodes(inner),
22 Regex::Quantifier(inner, _, _) => 1 + count_nodes(inner),
23 Regex::Concat(nodes) | Regex::Alternation(nodes) => {
24 1 + nodes.iter().map(count_nodes).sum::<usize>()
25 }
26 }
27}
28
29fn collect_literals(node: &Regex, out: &mut Vec<char>) {
32 match node {
33 Regex::Literal(c) => out.push(*c),
34 Regex::AnyChar | Regex::Anchor(_) | Regex::EscapeClass(_) => {}
35 Regex::CharClass(cls) => {
36 for item in &cls.items {
38 if let CharClassItem::Literal(c) = item {
39 out.push(*c);
40 }
41 }
42 }
43 Regex::Group(inner, _) => collect_literals(inner, out),
44 Regex::Quantifier(inner, _, _) => collect_literals(inner, out),
45 Regex::Concat(nodes) | Regex::Alternation(nodes) => {
46 for n in nodes {
47 collect_literals(n, out);
48 }
49 }
50 }
51}
52
53fn pretty_print(node: &Regex, indent: usize) {
56 let pad = " ".repeat(indent);
57 match node {
58 Regex::Literal(c) => println!("{pad}Literal({c:?})"),
59 Regex::AnyChar => println!("{pad}AnyChar"),
60 Regex::Anchor(a) => println!("{pad}Anchor({a:?})"),
61 Regex::EscapeClass(ec) => println!("{pad}EscapeClass({ec:?})"),
62 Regex::CharClass(cls) => {
63 let neg = if cls.negated { "negated" } else { "" };
64 println!("{pad}CharClass[{neg}] ({} items)", cls.items.len());
65 }
66 Regex::Group(inner, kind) => {
67 println!("{pad}Group({kind:?})");
68 pretty_print(inner, indent + 1);
69 }
70 Regex::Quantifier(inner, kind, greedy) => {
71 let mode = if *greedy { "greedy" } else { "lazy" };
72 println!("{pad}Quantifier({kind:?}, {mode})");
73 pretty_print(inner, indent + 1);
74 }
75 Regex::Concat(nodes) => {
76 println!("{pad}Concat ({} children)", nodes.len());
77 for n in nodes {
78 pretty_print(n, indent + 1);
79 }
80 }
81 Regex::Alternation(nodes) => {
82 println!("{pad}Alternation ({} branches)", nodes.len());
83 for n in nodes {
84 pretty_print(n, indent + 1);
85 }
86 }
87 }
88}
89
90fn analyse(pattern: &str) {
91 println!("┌─ Pattern: {pattern}");
92 let ast = parse(pattern).unwrap();
93
94 let total = count_nodes(&ast);
95 let mut lits = Vec::new();
96 collect_literals(&ast, &mut lits);
97 let lit_str: String = lits.iter().collect();
98
99 println!("│ nodes: {total}");
100 println!("│ literals: {lit_str:?}");
101 println!("└─ tree:");
102 pretty_print(&ast, 1);
103 println!();
104}
105
106fn main() {
107 analyse(r"\d{4}-\d{2}-\d{2}"); analyse(r"(?P<proto>https?)://\S+"); analyse(r"^[A-Z][a-z]+(?: [A-Z][a-z]+)*$"); }