pub enum Regex {
Literal(char),
AnyChar,
Anchor(Anchor),
EscapeClass(EscapeClass),
CharClass(CharClass),
Group(Box<Regex>, GroupKind),
Quantifier(Box<Regex>, QuantKind, bool),
Concat(Vec<Regex>),
Alternation(Vec<Regex>),
}Expand description
A parsed regex AST node.
Variants§
Literal(char)
Matches a single literal character, e.g. a.
AnyChar
Matches any character except newline (or including it with s flag): .
Anchor(Anchor)
Zero-width assertion: ^, $, \b, \B.
EscapeClass(EscapeClass)
Predefined character class shorthand: \d, \w, \s and their negations.
CharClass(CharClass)
A character class expression: [abc], [^a-z], [\d\w].
Group(Box<Regex>, GroupKind)
A group: capturing, non-capturing, or a lookaround assertion.
Quantifier(Box<Regex>, QuantKind, bool)
A quantifier applied to a sub-expression. The bool is true for greedy.
Concat(Vec<Regex>)
A sequence of sub-expressions matched one after the other.
Alternation(Vec<Regex>)
An alternation of sub-expressions: a|b|c.
Implementations§
Source§impl Regex
impl Regex
Sourcepub fn min_width(&self) -> usize
pub fn min_width(&self) -> usize
Returns the minimum number of characters this node can match.
Anchors (^, $, \b) and lookarounds contribute zero because
they are zero-width assertions.
use re_parser::parse;
assert_eq!(parse(r"\d{2,4}").unwrap().min_width(), 2);
assert_eq!(parse(r"^abc$").unwrap().min_width(), 3); // anchors are zero-width
assert_eq!(parse(r"a*").unwrap().min_width(), 0);Examples found in repository?
13fn report(pattern: &str) {
14 match parse(pattern) {
15 Err(e) => println!(" {pattern:40} ERROR: {e}"),
16 Ok(ast) => {
17 let min = ast.min_width();
18 let max = ast.max_width();
19 let max_str = max.map_or("∞".to_owned(), |n| n.to_string());
20 println!(" {pattern:40} min={min} max={max_str}");
21 }
22 }
23}
24
25fn main() {
26 println!("=== Fixed-width patterns ===");
27 report("abc");
28 report(r"\d{4}-\d{2}-\d{2}"); // ISO date → 10
29 report(r"#[0-9a-fA-F]{6}"); // CSS hex colour → 7
30 report(r"[A-Z]{2}[0-9]{4}"); // plate → 6
31
32 println!("\n=== Variable but bounded patterns ===");
33 report(r"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}"); // IPv4 → 7..=15
34 report(r"https?://"); // 7..=8
35 report(r"[a-zA-Z]{2,8}"); // 2..=8
36 report(r"colou?r"); // 5..=6
37
38 println!("\n=== Unbounded patterns ===");
39 report(r"\w+"); // 1..∞
40 report(r".*"); // 0..∞
41 report(r"\S+@\S+\.\S+"); // 5..∞
42 report(r"(?:ab)+"); // 2..∞
43
44 println!("\n=== Anchors and lookarounds are zero-width ===");
45 report(r"^hello$"); // anchors → still 5
46 report(r"foo(?=bar)"); // lookahead → 3
47 report(r"(?<=\d)px"); // lookbehind → 2
48 report(r"\bword\b"); // \b → 4
49
50 println!("\n=== Alternation ===");
51 report(r"cat|dog"); // exactly 3
52 report(r"a|bb|ccc"); // 1..=3
53 report(r"yes|no"); // 2..=3
54 report(r"x|\d+"); // 1..∞
55
56 // ── using pattern_width convenience function ───────────────────────────
57 println!("\n=== pattern_width() convenience wrapper ===");
58 let w = pattern_width(r"a{2,5}").unwrap();
59 println!(" a{{2,5}} => {w}");
60 println!(" is_fixed: {}", w.is_fixed());
61 println!(" is_nullable: {}", w.is_nullable());
62 println!(" is_unbounded: {}", w.is_unbounded());
63
64 // ── calling methods on a sub-node ──────────────────────────────────────
65 println!("\n=== Inspecting sub-nodes ===");
66 let ast = parse(r"(\d{4})-(\d{2})-(\d{2})").unwrap();
67 println!(" full pattern min={} max={:?}", ast.min_width(), ast.max_width());
68
69 // Walk the top-level Concat children manually
70 if let re_parser::ast::Regex::Concat(nodes) = &ast {
71 for (i, node) in nodes.iter().enumerate() {
72 println!(
73 " child[{i}] min={} max={:?} node={node:?}",
74 node.min_width(),
75 node.max_width(),
76 );
77 }
78 }
79}Sourcepub fn max_width(&self) -> Option<usize>
pub fn max_width(&self) -> Option<usize>
Returns the maximum number of characters this node can match, or None
if the match length is unbounded (the node contains *, +, or
{n,}).
use re_parser::parse;
assert_eq!(parse(r"\d{2,4}").unwrap().max_width(), Some(4));
assert_eq!(parse(r"a+").unwrap().max_width(), None); // unbounded
assert_eq!(parse(r"foo(?=bar)").unwrap().max_width(), Some(3)); // lookahead is zero-widthExamples found in repository?
13fn report(pattern: &str) {
14 match parse(pattern) {
15 Err(e) => println!(" {pattern:40} ERROR: {e}"),
16 Ok(ast) => {
17 let min = ast.min_width();
18 let max = ast.max_width();
19 let max_str = max.map_or("∞".to_owned(), |n| n.to_string());
20 println!(" {pattern:40} min={min} max={max_str}");
21 }
22 }
23}
24
25fn main() {
26 println!("=== Fixed-width patterns ===");
27 report("abc");
28 report(r"\d{4}-\d{2}-\d{2}"); // ISO date → 10
29 report(r"#[0-9a-fA-F]{6}"); // CSS hex colour → 7
30 report(r"[A-Z]{2}[0-9]{4}"); // plate → 6
31
32 println!("\n=== Variable but bounded patterns ===");
33 report(r"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}"); // IPv4 → 7..=15
34 report(r"https?://"); // 7..=8
35 report(r"[a-zA-Z]{2,8}"); // 2..=8
36 report(r"colou?r"); // 5..=6
37
38 println!("\n=== Unbounded patterns ===");
39 report(r"\w+"); // 1..∞
40 report(r".*"); // 0..∞
41 report(r"\S+@\S+\.\S+"); // 5..∞
42 report(r"(?:ab)+"); // 2..∞
43
44 println!("\n=== Anchors and lookarounds are zero-width ===");
45 report(r"^hello$"); // anchors → still 5
46 report(r"foo(?=bar)"); // lookahead → 3
47 report(r"(?<=\d)px"); // lookbehind → 2
48 report(r"\bword\b"); // \b → 4
49
50 println!("\n=== Alternation ===");
51 report(r"cat|dog"); // exactly 3
52 report(r"a|bb|ccc"); // 1..=3
53 report(r"yes|no"); // 2..=3
54 report(r"x|\d+"); // 1..∞
55
56 // ── using pattern_width convenience function ───────────────────────────
57 println!("\n=== pattern_width() convenience wrapper ===");
58 let w = pattern_width(r"a{2,5}").unwrap();
59 println!(" a{{2,5}} => {w}");
60 println!(" is_fixed: {}", w.is_fixed());
61 println!(" is_nullable: {}", w.is_nullable());
62 println!(" is_unbounded: {}", w.is_unbounded());
63
64 // ── calling methods on a sub-node ──────────────────────────────────────
65 println!("\n=== Inspecting sub-nodes ===");
66 let ast = parse(r"(\d{4})-(\d{2})-(\d{2})").unwrap();
67 println!(" full pattern min={} max={:?}", ast.min_width(), ast.max_width());
68
69 // Walk the top-level Concat children manually
70 if let re_parser::ast::Regex::Concat(nodes) = &ast {
71 for (i, node) in nodes.iter().enumerate() {
72 println!(
73 " child[{i}] min={} max={:?} node={node:?}",
74 node.min_width(),
75 node.max_width(),
76 );
77 }
78 }
79}