use super::Expr;
use crate::{Span, Token};
pub struct Repeating {
inner: Box<dyn Expr>,
required_repetitions: usize,
}
impl Repeating {
pub fn new(expr: Box<dyn Expr>, required_repetitions: usize) -> Self {
Self {
inner: expr,
required_repetitions,
}
}
}
impl Expr for Repeating {
fn run(&self, mut cursor: usize, tokens: &[Token], source: &[char]) -> Option<Span<Token>> {
let mut window = Span::empty(cursor);
let mut repetition = 0;
loop {
let res = self.inner.run(cursor, tokens, source);
if let Some(res) = res {
window.expand_to_include(res.start);
window.expand_to_include(res.end - 1);
if res.start < cursor {
cursor = res.start;
} else {
cursor = res.end;
}
if res.is_empty() {
return Some(window);
}
repetition += 1;
} else if repetition >= self.required_repetitions {
return Some(window);
} else {
return None;
}
}
}
}
#[cfg(test)]
mod tests {
use super::Repeating;
use crate::expr::{ExprExt, SequenceExpr};
use crate::patterns::AnyPattern;
use crate::{Document, Span};
#[test]
fn matches_anything() {
let doc = Document::new_plain_english_curated(
"This matcher will match the entirety of any document!",
);
let pat = Repeating::new(Box::new(SequenceExpr::from(AnyPattern)), 0);
assert_eq!(
pat.iter_matches(doc.get_tokens(), doc.get_source()).next(),
Some(Span::new(0, doc.get_tokens().len()))
)
}
#[test]
fn does_not_match_short() {
let doc = Document::new_plain_english_curated("No match");
let pat = Repeating::new(Box::new(SequenceExpr::from(AnyPattern)), 4);
assert_eq!(
pat.iter_matches(doc.get_tokens(), doc.get_source()).next(),
None
)
}
}