use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Span {
pub start: usize,
pub end: usize,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Shorthand {
Digit,
NotDigit,
Word,
NotWord,
Space,
NotSpace,
}
impl Shorthand {
fn letter(self) -> char {
match self {
Shorthand::Digit => 'd',
Shorthand::NotDigit => 'D',
Shorthand::Word => 'w',
Shorthand::NotWord => 'W',
Shorthand::Space => 's',
Shorthand::NotSpace => 'S',
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PropKind {
GeneralCategory,
Script,
ScriptExtensions,
Binary,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PropertyRef {
pub negated: bool,
pub kind: PropKind,
pub name: &'static str,
pub exact: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ClassItem {
Property(PropertyRef),
Range(u32, u32),
Shorthand(Shorthand),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Class {
pub negated: bool,
pub items: Vec<ClassItem>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Assertion {
LineStart,
LineEnd,
TextStart,
TextEnd,
WordBoundary,
NotWordBoundary,
WordStart,
WordEnd,
TextEndOptionalNewline,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SetOp {
Intersect,
Difference,
SymmetricDifference,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ClassExpr {
Items(Vec<ClassItem>),
Union(Vec<ClassExpr>),
Bracket {
negated: bool,
inner: Box<ClassExpr>,
},
Op(SetOp, Box<ClassExpr>, Box<ClassExpr>),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct FlagDelta {
pub on: crate::flags::Flags,
pub off: crate::flags::Flags,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum NodeKind {
Empty,
Literal(u32),
Any,
Class(Class),
ClassSet(ClassExpr),
Concat(Vec<Node>),
Alt(Vec<Node>),
Repeat {
node: Box<Node>,
min: u64,
max: Option<u64>,
greedy: bool,
},
Group {
capture: Option<u32>,
name: Option<String>,
node: Box<Node>,
},
FlagGroup {
delta: FlagDelta,
node: Box<Node>,
},
Assert(Assertion),
Look {
ahead: bool,
negate: bool,
node: Box<Node>,
},
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Node {
pub kind: NodeKind,
pub span: Span,
}
impl fmt::Display for Node {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.kind {
NodeKind::Empty => write!(f, "eps"),
NodeKind::Literal(c) => match char::from_u32(*c) {
Some(ch) if ch.is_ascii_graphic() => write!(f, "{ch}"),
_ => write!(f, "U+{c:04X}"),
},
NodeKind::Any => write!(f, "any"),
NodeKind::Class(c) => {
write!(f, "[{}", if c.negated { "^" } else { "" })?;
for (i, it) in c.items.iter().enumerate() {
if i > 0 {
write!(f, " ")?;
}
match it {
ClassItem::Range(a, b) if a == b => write!(f, "{a:X}")?,
ClassItem::Range(a, b) => write!(f, "{a:X}-{b:X}")?,
ClassItem::Shorthand(s) => write!(f, "\\{}", s.letter())?,
ClassItem::Property(p) => write!(
f,
"\\{}{{{:?}:{}}}",
if p.negated { "P" } else { "p" },
p.kind,
p.name
)?,
}
}
write!(f, "]")
}
NodeKind::ClassSet(e) => write!(f, "[set {e:?}]"),
NodeKind::Concat(v) | NodeKind::Alt(v) => {
let tag = if matches!(self.kind, NodeKind::Concat(_)) {
"cat"
} else {
"alt"
};
write!(f, "({tag}")?;
for n in v {
write!(f, " {n}")?;
}
write!(f, ")")
}
NodeKind::Repeat {
node,
min,
max,
greedy,
} => {
let max = max.map_or("inf".to_string(), |m| m.to_string());
write!(
f,
"(rep{} {min} {max} {node})",
if *greedy { "" } else { "?" }
)
}
NodeKind::Group {
capture,
name,
node,
} => match (capture, name) {
(Some(i), Some(n)) => write!(f, "(group#{i}:{n} {node})"),
(Some(i), None) => write!(f, "(group#{i} {node})"),
_ => write!(f, "(ncgroup {node})"),
},
NodeKind::FlagGroup { node, .. } => write!(f, "(flags {node})"),
NodeKind::Assert(a) => write!(f, "{a:?}"),
NodeKind::Look {
ahead,
negate,
node,
} => {
let d = if *ahead { "ahead" } else { "behind" };
write!(f, "(look{}{} {node})", d, if *negate { "!" } else { "" })
}
}
}
}