use re_parser::parse;
use re_parser::pattern_width;
fn report(pattern: &str) {
match parse(pattern) {
Err(e) => println!(" {pattern:40} ERROR: {e}"),
Ok(ast) => {
let min = ast.min_width();
let max = ast.max_width();
let max_str = max.map_or("∞".to_owned(), |n| n.to_string());
println!(" {pattern:40} min={min} max={max_str}");
}
}
}
fn main() {
println!("=== Fixed-width patterns ===");
report("abc");
report(r"\d{4}-\d{2}-\d{2}"); report(r"#[0-9a-fA-F]{6}"); report(r"[A-Z]{2}[0-9]{4}");
println!("\n=== Variable but bounded patterns ===");
report(r"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}"); report(r"https?://"); report(r"[a-zA-Z]{2,8}"); report(r"colou?r");
println!("\n=== Unbounded patterns ===");
report(r"\w+"); report(r".*"); report(r"\S+@\S+\.\S+"); report(r"(?:ab)+");
println!("\n=== Anchors and lookarounds are zero-width ===");
report(r"^hello$"); report(r"foo(?=bar)"); report(r"(?<=\d)px"); report(r"\bword\b");
println!("\n=== Alternation ===");
report(r"cat|dog"); report(r"a|bb|ccc"); report(r"yes|no"); report(r"x|\d+");
println!("\n=== pattern_width() convenience wrapper ===");
let w = pattern_width(r"a{2,5}").unwrap();
println!(" a{{2,5}} => {w}");
println!(" is_fixed: {}", w.is_fixed());
println!(" is_nullable: {}", w.is_nullable());
println!(" is_unbounded: {}", w.is_unbounded());
println!("\n=== Inspecting sub-nodes ===");
let ast = parse(r"(\d{4})-(\d{2})-(\d{2})").unwrap();
println!(" full pattern min={} max={:?}", ast.min_width(), ast.max_width());
if let re_parser::ast::Regex::Concat(nodes) = &ast {
for (i, node) in nodes.iter().enumerate() {
println!(
" child[{i}] min={} max={:?} node={node:?}",
node.min_width(),
node.max_width(),
);
}
}
}