use alloc::{borrow::Cow, vec::Vec};
pub(crate) type Text = Cow<'static, str>;
pub(crate) type Captures = Vec<(u32, Capture)>;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Rule(pub(crate) Kind);
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) enum Kind {
Match(Match),
BeginEnd(BeginEnd),
BeginWhile(BeginWhile),
Include(Include),
Patterns(Vec<Rule>),
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) enum Include {
Repository(Text),
SelfRef,
Base,
Scope(Text),
ScopeEntry(Text, Text),
}
impl Rule {
#[must_use]
pub fn include(name: impl Into<Text>) -> Rule {
Rule(Kind::Include(Include::Repository(name.into())))
}
#[must_use]
pub fn include_self() -> Rule {
Rule(Kind::Include(Include::SelfRef))
}
#[must_use]
pub fn include_base() -> Rule {
Rule(Kind::Include(Include::Base))
}
#[must_use]
pub fn include_scope(scope: impl Into<Text>) -> Rule {
Rule(Kind::Include(Include::Scope(scope.into())))
}
#[must_use]
pub fn include_scope_entry(scope: impl Into<Text>, name: impl Into<Text>) -> Rule {
Rule(Kind::Include(Include::ScopeEntry(
scope.into(),
name.into(),
)))
}
#[must_use]
pub fn patterns(rules: impl IntoIterator<Item = Rule>) -> Rule {
Rule(Kind::Patterns(rules.into_iter().collect()))
}
}
impl Drop for Rule {
fn drop(&mut self) {
let mut stack = Vec::new();
self.0.take_children(&mut stack);
while let Some(mut rule) = stack.pop() {
rule.0.take_children(&mut stack);
}
}
}
impl Kind {
fn take_children(&mut self, out: &mut Vec<Rule>) {
fn captures(list: &mut Captures, out: &mut Vec<Rule>) {
for (_, capture) in list.iter_mut() {
out.append(&mut capture.patterns);
}
}
match self {
Kind::Match(m) => captures(&mut m.captures, out),
Kind::BeginEnd(r) => {
out.append(&mut r.patterns);
captures(&mut r.captures, out);
captures(&mut r.begin_captures, out);
captures(&mut r.end_captures, out);
}
Kind::BeginWhile(r) => {
out.append(&mut r.patterns);
captures(&mut r.captures, out);
captures(&mut r.begin_captures, out);
captures(&mut r.while_captures, out);
}
Kind::Patterns(rules) => out.append(rules),
Kind::Include(_) => {}
}
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct Capture {
pub(crate) scope: Option<Text>,
pub(crate) patterns: Vec<Rule>,
}
impl Capture {
#[must_use]
pub fn new() -> Capture {
Capture::default()
}
#[must_use]
pub fn scope(mut self, scope: impl Into<Text>) -> Capture {
self.scope = Some(scope.into());
self
}
#[must_use]
pub fn pattern(mut self, rule: impl Into<Rule>) -> Capture {
self.patterns.push(rule.into());
self
}
}
impl From<&'static str> for Capture {
fn from(scope: &'static str) -> Capture {
Capture::new().scope(scope)
}
}
impl From<alloc::string::String> for Capture {
fn from(scope: alloc::string::String) -> Capture {
Capture::new().scope(scope)
}
}
impl From<Text> for Capture {
fn from(scope: Text) -> Capture {
Capture::new().scope(scope)
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Match {
pub(crate) regex: Text,
pub(crate) scope: Option<Text>,
pub(crate) captures: Captures,
}
impl Match {
#[must_use]
pub fn new(regex: impl Into<Text>) -> Match {
Match {
regex: regex.into(),
scope: None,
captures: Vec::new(),
}
}
#[must_use]
pub fn scope(mut self, scope: impl Into<Text>) -> Match {
self.scope = Some(scope.into());
self
}
#[must_use]
pub fn capture(mut self, group: u32, capture: impl Into<Capture>) -> Match {
self.captures.push((group, capture.into()));
self
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct BeginEnd {
pub(crate) begin: Text,
pub(crate) end: Text,
pub(crate) scope: Option<Text>,
pub(crate) content_scope: Option<Text>,
pub(crate) captures: Captures,
pub(crate) begin_captures: Captures,
pub(crate) end_captures: Captures,
pub(crate) patterns: Vec<Rule>,
pub(crate) apply_end_pattern_last: bool,
}
impl BeginEnd {
#[must_use]
pub fn new(begin: impl Into<Text>, end: impl Into<Text>) -> BeginEnd {
BeginEnd {
begin: begin.into(),
end: end.into(),
scope: None,
content_scope: None,
captures: Vec::new(),
begin_captures: Vec::new(),
end_captures: Vec::new(),
patterns: Vec::new(),
apply_end_pattern_last: false,
}
}
#[must_use]
pub fn scope(mut self, scope: impl Into<Text>) -> BeginEnd {
self.scope = Some(scope.into());
self
}
#[must_use]
pub fn content_scope(mut self, scope: impl Into<Text>) -> BeginEnd {
self.content_scope = Some(scope.into());
self
}
#[must_use]
pub fn capture(mut self, group: u32, capture: impl Into<Capture>) -> BeginEnd {
self.captures.push((group, capture.into()));
self
}
#[must_use]
pub fn begin_capture(mut self, group: u32, capture: impl Into<Capture>) -> BeginEnd {
self.begin_captures.push((group, capture.into()));
self
}
#[must_use]
pub fn end_capture(mut self, group: u32, capture: impl Into<Capture>) -> BeginEnd {
self.end_captures.push((group, capture.into()));
self
}
#[must_use]
pub fn pattern(mut self, rule: impl Into<Rule>) -> BeginEnd {
self.patterns.push(rule.into());
self
}
#[must_use]
pub fn apply_end_pattern_last(mut self, last: bool) -> BeginEnd {
self.apply_end_pattern_last = last;
self
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct BeginWhile {
pub(crate) begin: Text,
pub(crate) while_: Text,
pub(crate) scope: Option<Text>,
pub(crate) content_scope: Option<Text>,
pub(crate) captures: Captures,
pub(crate) begin_captures: Captures,
pub(crate) while_captures: Captures,
pub(crate) patterns: Vec<Rule>,
}
impl BeginWhile {
#[must_use]
pub fn new(begin: impl Into<Text>, while_: impl Into<Text>) -> BeginWhile {
BeginWhile {
begin: begin.into(),
while_: while_.into(),
scope: None,
content_scope: None,
captures: Vec::new(),
begin_captures: Vec::new(),
while_captures: Vec::new(),
patterns: Vec::new(),
}
}
#[must_use]
pub fn scope(mut self, scope: impl Into<Text>) -> BeginWhile {
self.scope = Some(scope.into());
self
}
#[must_use]
pub fn content_scope(mut self, scope: impl Into<Text>) -> BeginWhile {
self.content_scope = Some(scope.into());
self
}
#[must_use]
pub fn capture(mut self, group: u32, capture: impl Into<Capture>) -> BeginWhile {
self.captures.push((group, capture.into()));
self
}
#[must_use]
pub fn begin_capture(mut self, group: u32, capture: impl Into<Capture>) -> BeginWhile {
self.begin_captures.push((group, capture.into()));
self
}
#[must_use]
pub fn while_capture(mut self, group: u32, capture: impl Into<Capture>) -> BeginWhile {
self.while_captures.push((group, capture.into()));
self
}
#[must_use]
pub fn pattern(mut self, rule: impl Into<Rule>) -> BeginWhile {
self.patterns.push(rule.into());
self
}
}
impl From<Match> for Rule {
fn from(rule: Match) -> Rule {
Rule(Kind::Match(rule))
}
}
impl From<BeginEnd> for Rule {
fn from(rule: BeginEnd) -> Rule {
Rule(Kind::BeginEnd(rule))
}
}
impl From<BeginWhile> for Rule {
fn from(rule: BeginWhile) -> Rule {
Rule(Kind::BeginWhile(rule))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_deep_nesting_drops_without_recursion() {
let mut rule = Rule::include("leaf");
for _ in 0..200_000 {
rule = BeginEnd::new("a", "b")
.pattern(Rule::patterns([rule]))
.into();
}
drop(rule);
let mut capture_chain = Rule::include("leaf");
for _ in 0..100_000 {
capture_chain = Match::new("(a)")
.capture(1, Capture::new().pattern(capture_chain))
.into();
}
drop(capture_chain);
}
#[test]
fn test_conversions() {
let a: Rule = Match::new("x").into();
assert!(matches!(a.0, Kind::Match(_)));
let b: Rule = BeginWhile::new("x", "y").into();
assert!(matches!(b.0, Kind::BeginWhile(_)));
assert_eq!(
Capture::from(alloc::string::String::from("s"))
.scope
.as_deref(),
Some("s")
);
assert_eq!(
Capture::from(Text::Borrowed("t")).scope.as_deref(),
Some("t")
);
}
}