#[derive(Debug, Clone, PartialEq)]
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>),
}
#[derive(Debug, Clone, PartialEq)]
pub enum Anchor {
Start,
End,
WordBoundary,
NonWordBoundary,
}
#[derive(Debug, Clone, PartialEq)]
pub enum EscapeClass {
Digit,
NonDigit,
Word,
NonWord,
Space,
NonSpace,
}
#[derive(Debug, Clone, PartialEq)]
pub struct CharClass {
pub items: Vec<CharClassItem>,
pub negated: bool,
}
#[derive(Debug, Clone, PartialEq)]
pub enum CharClassItem {
Literal(char),
Range(char, char),
EscapeClass(EscapeClass),
}
#[derive(Debug, Clone, PartialEq)]
pub enum GroupKind {
Capturing,
Named(String),
NonCapturing,
LookaheadPos,
LookaheadNeg,
LookbehindPos,
LookbehindNeg,
}
#[derive(Debug, Clone, PartialEq)]
pub enum QuantKind {
ZeroOrMore,
OneOrMore,
ZeroOrOne,
Exactly(usize),
AtLeast(usize),
Between(usize, usize),
}
impl Regex {
pub fn min_width(&self) -> usize {
match self {
Regex::Literal(_) | Regex::AnyChar | Regex::EscapeClass(_) | Regex::CharClass(_) => 1,
Regex::Anchor(_) => 0,
Regex::Group(inner, kind) => match kind {
GroupKind::LookaheadPos
| GroupKind::LookaheadNeg
| GroupKind::LookbehindPos
| GroupKind::LookbehindNeg => 0,
_ => inner.min_width(),
},
Regex::Quantifier(inner, kind, _) => match kind {
QuantKind::ZeroOrMore | QuantKind::ZeroOrOne => 0,
QuantKind::OneOrMore => inner.min_width(),
QuantKind::Exactly(n) => inner.min_width().saturating_mul(*n),
QuantKind::AtLeast(n) => inner.min_width().saturating_mul(*n),
QuantKind::Between(n, _) => inner.min_width().saturating_mul(*n),
},
Regex::Concat(nodes) => nodes.iter().map(Regex::min_width).sum(),
Regex::Alternation(nodes) => {
nodes.iter().map(Regex::min_width).min().unwrap_or(0)
}
}
}
pub fn max_width(&self) -> Option<usize> {
match self {
Regex::Literal(_) | Regex::AnyChar | Regex::EscapeClass(_) | Regex::CharClass(_) => {
Some(1)
}
Regex::Anchor(_) => Some(0),
Regex::Group(inner, kind) => match kind {
GroupKind::LookaheadPos
| GroupKind::LookaheadNeg
| GroupKind::LookbehindPos
| GroupKind::LookbehindNeg => Some(0),
_ => inner.max_width(),
},
Regex::Quantifier(inner, kind, _) => match kind {
QuantKind::ZeroOrMore | QuantKind::OneOrMore | QuantKind::AtLeast(_) => None,
QuantKind::ZeroOrOne => inner.max_width(),
QuantKind::Exactly(n) => inner.max_width()?.checked_mul(*n),
QuantKind::Between(_, m) => inner.max_width()?.checked_mul(*m),
},
Regex::Concat(nodes) => nodes
.iter()
.try_fold(0usize, |acc, n| acc.checked_add(n.max_width()?)),
Regex::Alternation(nodes) => nodes
.iter()
.try_fold(0usize, |acc, n| Some(acc.max(n.max_width()?))),
}
}
}