harper_core/expr/
repeating.rs1use super::Expr;
2use crate::{Span, Token};
3
4pub struct Repeating {
8 inner: Box<dyn Expr>,
9 required_repetitions: usize,
10}
11
12impl Repeating {
13 pub fn new(expr: Box<dyn Expr>, required_repetitions: usize) -> Self {
14 Self {
15 inner: expr,
16 required_repetitions,
17 }
18 }
19}
20
21impl Expr for Repeating {
22 fn run(&self, mut cursor: usize, tokens: &[Token], source: &[char]) -> Option<Span<Token>> {
23 let mut window = Span::new_with_len(cursor, 0);
24 let mut repetition = 0;
25
26 loop {
27 let res = self.inner.run(cursor, tokens, source);
28
29 if let Some(res) = res {
30 window.expand_to_include(res.start);
31 window.expand_to_include(res.end - 1);
32
33 if res.start < cursor {
34 cursor = res.start;
35 } else {
36 cursor = res.end;
37 }
38
39 if res.is_empty() {
40 return Some(window);
41 }
42
43 repetition += 1;
44 } else if repetition >= self.required_repetitions {
45 return Some(window);
46 } else {
47 return None;
48 }
49 }
50 }
51}
52
53#[cfg(test)]
54mod tests {
55
56 use super::Repeating;
57 use crate::expr::{ExprExt, SequenceExpr};
58 use crate::patterns::AnyPattern;
59 use crate::{Document, Span};
60
61 #[test]
62 fn matches_anything() {
63 let doc = Document::new_plain_english_curated(
64 "This matcher will match the entirety of any document!",
65 );
66 let pat = Repeating::new(Box::new(SequenceExpr::from(AnyPattern)), 0);
67
68 assert_eq!(
69 pat.iter_matches(doc.get_tokens(), doc.get_source()).next(),
70 Some(Span::new(0, doc.get_tokens().len()))
71 )
72 }
73
74 #[test]
75 fn does_not_match_short() {
76 let doc = Document::new_plain_english_curated("No match");
77 let pat = Repeating::new(Box::new(SequenceExpr::from(AnyPattern)), 4);
78
79 assert_eq!(
80 pat.iter_matches(doc.get_tokens(), doc.get_source()).next(),
81 None
82 )
83 }
84}