gramma/string_matcher/
operators.rs

1use core::{
2    fmt,
3    ops::{Add, BitAnd, BitOr, Not},
4};
5
6use crate::utils::default;
7
8use super::{
9    char_matcher::MatchChar,
10    machine::StackItem,
11    traits::{IntersectPattern, IntoMatchString, NegatePattern},
12    DebugPrecedence, Link, MatchString, StringPattern,
13};
14
15pub struct MatchPlus<A, B> {
16    matcher1: A,
17    matcher2: B,
18}
19
20impl<A, B> IntoMatchString for MatchPlus<A, B>
21where
22    A: IntoMatchString,
23    B: IntoMatchString,
24{
25    type Matcher<'m> = MatchPlus<A::Matcher<'m>, B::Matcher<'m>> where Self: 'm;
26
27    fn into_match_string<'m>(self) -> Self::Matcher<'m>
28    where
29        Self: 'm,
30    {
31        Self::Matcher {
32            matcher1: self.matcher1.into_match_string(),
33            matcher2: self.matcher2.into_match_string(),
34        }
35    }
36}
37
38impl<'m, A, B> MatchString<'m> for MatchPlus<A, B>
39where
40    A: MatchString<'m>,
41    B: MatchString<'m>,
42{
43    fn match_string(&'m self, cx: &mut super::StringMatcherContext<'m, '_>) -> Option<bool> {
44        if cx.is_reversed() {
45            cx.run_matcher(&self.matcher2)
46        } else {
47            cx.run_matcher(&self.matcher1)
48        }
49    }
50
51    fn backward_matcher(&'m self) -> Option<super::Matcher<'m>> {
52        self.matcher1.backward_matcher()
53    }
54
55    fn forward_matcher(&'m self) -> Option<super::Matcher<'m>> {
56        self.matcher2.forward_matcher()
57    }
58
59    fn set_backward(&'m self, matcher: Option<super::Matcher<'m>>) {
60        self.matcher1.set_backward(matcher)
61    }
62
63    fn set_forward(&'m self, matcher: Option<super::Matcher<'m>>) {
64        self.matcher2.set_forward(matcher)
65    }
66
67    fn initialize(&'m self) {
68        self.matcher1.set_forward(self.matcher2.first().into());
69        self.matcher1.initialize();
70
71        self.matcher2.set_backward(self.matcher1.last().into());
72        self.matcher2.initialize();
73    }
74
75    fn fmt_matcher(&self, f: &mut fmt::Formatter, prec: DebugPrecedence) -> fmt::Result {
76        prec.wrap_below(DebugPrecedence::Add, f, |f| {
77            self.matcher1.fmt_matcher(f, DebugPrecedence::Add)?;
78            f.write_str(" + ")?;
79            self.matcher2.fmt_matcher(f, DebugPrecedence::Add)?;
80            Ok(())
81        })
82    }
83
84    fn quick_test(&'m self, cx: &mut super::StringMatcherContext<'m, '_>) -> Option<bool> {
85        if cx.is_reversed() {
86            self.matcher2.quick_test(cx)
87        } else {
88            self.matcher1.quick_test(cx)
89        }
90    }
91}
92
93impl<'m, A, B> Add<StringPattern<B>> for StringPattern<A> {
94    type Output = StringPattern<MatchPlus<A, B>>;
95
96    fn add(self, rhs: StringPattern<B>) -> Self::Output {
97        StringPattern::new(MatchPlus {
98            matcher1: self.inner,
99            matcher2: rhs.inner,
100        })
101    }
102}
103
104pub struct MatchOr<A, B> {
105    matcher1: A,
106    matcher2: B,
107}
108
109impl<A, B> IntoMatchString for MatchOr<A, B>
110where
111    A: IntoMatchString,
112    B: IntoMatchString,
113{
114    type Matcher<'m> = MatchOr<A::Matcher<'m>, B::Matcher<'m>> where Self: 'm;
115
116    fn into_match_string<'m>(self) -> Self::Matcher<'m>
117    where
118        Self: 'm,
119    {
120        Self::Matcher {
121            matcher1: self.matcher1.into_match_string(),
122            matcher2: self.matcher2.into_match_string(),
123        }
124    }
125}
126
127impl<'m, A, B> MatchString<'m> for MatchOr<A, B>
128where
129    A: MatchString<'m>,
130    B: MatchString<'m>,
131{
132    fn match_string(&'m self, cx: &mut super::StringMatcherContext<'m, '_>) -> Option<bool> {
133        if let Some(char_matcher) = self.as_char_matcher() {
134            if !cx.match_char(&char_matcher) {
135                return Some(false);
136            }
137
138            return cx.run_next(self);
139        }
140
141        let alt = cx.push_alternate(&self.matcher2);
142        let primary = cx.run_matcher(&self.matcher1);
143        cx.select_alternate(primary, alt)
144    }
145
146    fn quick_test(&'m self, cx: &mut super::StringMatcherContext<'m, '_>) -> Option<bool> {
147        let state = cx.state();
148        let test1 = self.matcher1.quick_test(cx);
149        if test1 == Some(true) {
150            return Some(true);
151        }
152        cx.set_state(state);
153        let test2 = self.matcher2.quick_test(cx);
154        match (test1, test2) {
155            (_, Some(true)) => Some(true),
156            (Some(false), Some(false)) => Some(false),
157            _ => None,
158        }
159    }
160
161    fn backward_matcher(&'m self) -> Option<super::Matcher<'m>> {
162        self.matcher1.backward_matcher()
163    }
164
165    fn forward_matcher(&'m self) -> Option<super::Matcher<'m>> {
166        self.matcher2.forward_matcher()
167    }
168
169    fn set_backward(&'m self, matcher: Option<super::Matcher<'m>>) {
170        self.matcher1.set_backward(matcher);
171        self.matcher2.set_backward(matcher);
172    }
173
174    fn set_forward(&'m self, matcher: Option<super::Matcher<'m>>) {
175        self.matcher1.set_forward(matcher);
176        self.matcher2.set_forward(matcher);
177    }
178
179    fn as_char_matcher(&'m self) -> Option<impl MatchChar + 'm> {
180        Some((
181            self.matcher1.as_char_matcher()?,
182            self.matcher2.as_char_matcher()?,
183        ))
184    }
185
186    fn initialize(&'m self) {
187        self.matcher1.initialize();
188        self.matcher2.initialize();
189    }
190
191    fn fmt_matcher(&self, f: &mut fmt::Formatter, prec: DebugPrecedence) -> fmt::Result {
192        prec.wrap_below(DebugPrecedence::Or, f, |f| {
193            self.matcher1.fmt_matcher(f, DebugPrecedence::Or)?;
194            f.write_str(" | ")?;
195            self.matcher2.fmt_matcher(f, DebugPrecedence::Or)?;
196            Ok(())
197        })
198    }
199}
200
201impl<A, B, AB> NegatePattern for MatchOr<A, B>
202where
203    A: NegatePattern,
204    B: NegatePattern,
205    A::Output: IntersectPattern<B::Output, Output = AB>,
206{
207    type Output = AB;
208
209    fn negate_pattern(self) -> Self::Output {
210        self.matcher1
211            .negate_pattern()
212            .intersect_pattern(self.matcher2.negate_pattern())
213    }
214}
215
216impl<A, B> BitOr<StringPattern<B>> for StringPattern<A> {
217    type Output = StringPattern<MatchOr<A, B>>;
218
219    fn bitor(self, rhs: StringPattern<B>) -> Self::Output {
220        StringPattern::new(MatchOr {
221            matcher1: self.inner,
222            matcher2: rhs.inner,
223        })
224    }
225}
226
227pub struct Lookaround<'m, M, const REVERSE: bool, const NEGATE: bool> {
228    inner: M,
229    links: (Link<'m>, Link<'m>),
230}
231
232impl<'m, M, const REVERSE: bool, const NEGATE: bool> Lookaround<'m, M, REVERSE, NEGATE> {
233    fn pop_pair(&self, next_len: usize) -> (u16, u16) {
234        if NEGATE {
235            (next_len as u16, 0)
236        } else {
237            (0, next_len as u16)
238        }
239    }
240}
241
242impl<M, const REVERSE: bool, const NEGATE: bool> IntoMatchString
243    for Lookaround<'_, M, REVERSE, NEGATE>
244where
245    M: IntoMatchString,
246{
247    type Matcher<'m> = Lookaround<'m, M::Matcher<'m>, REVERSE, NEGATE> where Self: 'm;
248
249    fn into_match_string<'m>(self) -> Self::Matcher<'m>
250    where
251        Self: 'm,
252    {
253        Self::Matcher {
254            inner: self.inner.into_match_string(),
255            links: default(),
256        }
257    }
258}
259
260impl<'m, M, const REVERSE: bool, const NEGATE: bool> MatchString<'m>
261    for Lookaround<'m, M, REVERSE, NEGATE>
262where
263    M: MatchString<'m>,
264{
265    fn match_string(&'m self, cx: &mut super::StringMatcherContext<'m, '_>) -> Option<bool> {
266        let pre_stack_len = cx.stack.len();
267        let pre_state = cx.state();
268
269        let next = self.next_matcher(cx.is_reversed());
270
271        let next_accepted;
272        let next_state;
273
274        if let Some(next) = next {
275            let (out, state) = cx.push_alternate(*next);
276            next_state = state;
277            next_accepted = match out {
278                Some(true) => true,
279                Some(false) => return Some(false),
280                None => false,
281            };
282        } else {
283            cx.push(StackItem::Accept).push_reset();
284            next_accepted = true;
285            next_state = cx.state();
286        };
287
288        let post_stack_len = cx.stack.len();
289
290        let (pop_ok, pop_err) = self.pop_pair(post_stack_len - pre_stack_len);
291        cx.push_frame(pop_ok, pop_err).reverse(REVERSE);
292
293        let matched = cx.run_matcher(&self.inner)? ^ NEGATE;
294
295        if matched {
296            cx.truncate_stack(post_stack_len).set_state(next_state);
297            if next_accepted {
298                Some(true)
299            } else {
300                None
301            }
302        } else {
303            cx.truncate_stack(pre_stack_len).set_state(pre_state);
304            Some(false)
305        }
306    }
307
308    fn links(&'m self) -> Option<super::Links<'m>> {
309        Some((&self.links).into())
310    }
311
312    fn initialize(&'m self) {
313        self.inner.initialize()
314    }
315
316    fn fmt_matcher(&self, f: &mut fmt::Formatter, _: DebugPrecedence) -> fmt::Result {
317        f.debug_tuple(if REVERSE { "follows" } else { "precedes" })
318            .field(&self.inner.as_debug(default()))
319            .finish()
320    }
321
322    fn quick_test(&'m self, cx: &mut super::StringMatcherContext<'m, '_>) -> Option<bool> {
323        let old_state = cx.state();
324        let test_inner = self
325            .inner
326            .quick_test(cx.reverse(REVERSE))
327            .map(|b| b ^ NEGATE);
328        cx.set_state(old_state);
329
330        if test_inner == Some(false) {
331            return Some(false);
332        }
333
334        let next = self.next_matcher(cx.is_reversed());
335
336        match (test_inner, next.map(|next| cx.quick_test_matcher(next, 1))) {
337            (inner, None) | (inner @ None, _) => inner,
338            (_, Some(next)) => next,
339        }
340    }
341}
342
343/// Tests whether the given matcher matches starting at the current location
344/// without changing the current location.
345///
346/// #Example
347///
348/// ```
349/// # use gramma::string_matcher;
350///
351/// string_matcher!(precedes(exactly("bar"))).match_string(3, "foobar").unwrap();
352/// // `precedes` can be negated:
353/// string_matcher!(!precedes(exactly("foo"))).match_string(3, "foobar").unwrap();
354/// ```
355pub fn precedes<'m, M: IntoMatchString>(
356    matcher: StringPattern<M>,
357) -> StringPattern<Lookaround<'m, M, false, false>> {
358    StringPattern::new(Lookaround {
359        inner: matcher.inner,
360        links: default(),
361    })
362}
363
364/// Tests whether the given matcher matches ending at the current location
365/// without changing the current location.
366///
367/// #Example
368///
369/// ```
370/// # use gramma::string_matcher;
371///
372/// string_matcher!(follows(exactly("foo"))).match_string(3, "foobar").unwrap();
373/// // `follows` can be negated:
374/// string_matcher!(!follows(exactly("bar"))).match_string(3, "foobar").unwrap();
375/// ```
376pub fn follows<'m, M: IntoMatchString>(
377    matcher: StringPattern<M>,
378) -> StringPattern<Lookaround<'m, M, true, false>> {
379    StringPattern::new(Lookaround {
380        inner: matcher.inner,
381        links: default(),
382    })
383}
384
385impl<'m, M, const REVERSE: bool> NegatePattern for Lookaround<'m, M, REVERSE, false> {
386    type Output = Lookaround<'m, M, REVERSE, true>;
387
388    fn negate_pattern(self) -> Self::Output {
389        Lookaround {
390            inner: self.inner,
391            links: self.links,
392        }
393    }
394}
395
396impl<'m, M, const REVERSE: bool> NegatePattern for Lookaround<'m, M, REVERSE, true> {
397    type Output = Lookaround<'m, M, REVERSE, false>;
398
399    fn negate_pattern(self) -> Self::Output {
400        Lookaround {
401            inner: self.inner,
402            links: self.links,
403        }
404    }
405}
406
407impl<M> Not for StringPattern<M>
408where
409    M: NegatePattern,
410{
411    type Output = StringPattern<M::Output>;
412
413    fn not(self) -> Self::Output {
414        StringPattern::new(self.inner.negate_pattern())
415    }
416}
417
418impl<M> StringPattern<M>
419where
420    M: NegatePattern,
421{
422    pub fn not(self) -> StringPattern<M::Output> {
423        !self
424    }
425}
426
427impl<M1, M2> BitAnd<StringPattern<M2>> for StringPattern<M1>
428where
429    M1: IntersectPattern<M2>,
430{
431    type Output = StringPattern<M1::Output>;
432
433    fn bitand(self, rhs: StringPattern<M2>) -> Self::Output {
434        StringPattern::new(self.inner.intersect_pattern(rhs.inner))
435    }
436}