1use paste::paste;
2
3use crate::{
4 CharStringExt, Lrc, Span, Token, TokenKind,
5 expr::{AsBoxedExpr, FirstMatchOf, FixedPhrase, LongestMatchOf},
6 patterns::{AnyPattern, IndefiniteArticle, RelativePronoun, WhitespacePattern, Word, WordSet},
7};
8
9use super::{Expr, Optional, OwnedExprExt, Repeating, Step, UnlessStep};
10
11#[derive(Default)]
12pub struct SequenceExpr {
13 exprs: Vec<Box<dyn Expr>>,
14}
15
16macro_rules! gen_then_from_is {
18 ($quality:ident) => {
19 paste! {
20 #[doc = concat!("Adds a step matching a token where [`TokenKind::is_", stringify!($quality), "()`] returns true.")]
21 pub fn [< then_$quality >] (self) -> Self{
22 self.then_kind_where(|kind| {
23 kind.[< is_$quality >]()
24 })
25 }
26
27 #[doc = concat!("Adds an optional step matching a token where [`TokenKind::is_", stringify!($quality), "()`] returns true.")]
28 pub fn [< then_optional_$quality >] (self) -> Self{
29 self.then_optional(|tok: &Token, _source: &[char]| {
30 tok.kind.[< is_$quality >]()
31 })
32 }
33
34 #[doc = concat!("Adds a step matching one or more consecutive tokens where [`TokenKind::is_", stringify!($quality), "()`] returns true.")]
35 pub fn [< then_one_or_more_$quality s >] (self) -> Self{
36 self.then_one_or_more(Box::new(|tok: &Token, _source: &[char]| {
37 tok.kind.[< is_$quality >]()
38 }))
39 }
40
41 #[doc = concat!("Adds a step matching a token where [`TokenKind::is_", stringify!($quality), "()`] returns false.")]
42 pub fn [< then_anything_but_$quality >] (self) -> Self{
43 self.then_kind_where(|kind| {
44 !kind.[< is_$quality >]()
45 })
46 }
47 }
48 };
49}
50
51impl Expr for SequenceExpr {
52 fn run(&self, mut cursor: usize, tokens: &[Token], source: &[char]) -> Option<Span<Token>> {
56 let mut window = Span::empty(cursor);
57
58 for cur_expr in &self.exprs {
59 let out = cur_expr.run(cursor, tokens, source)?;
60
61 let is_zero_width = out.end == out.start;
64
65 if !is_zero_width {
66 if out.end > out.start {
68 window.expand_to_include(out.start);
69 window.expand_to_include(out.end.checked_sub(1).unwrap_or(out.start));
70 }
71
72 if out.end > cursor {
74 cursor = out.end;
75 } else if out.start < cursor {
76 cursor = out.start;
77 }
78 }
79 }
81
82 Some(window)
83 }
84}
85
86impl SequenceExpr {
87 pub fn with(expr: impl Expr + 'static) -> Self {
91 Self::default().then(expr)
92 }
93
94 pub fn anything() -> Self {
98 Self::default().then_anything()
99 }
100
101 pub fn any_capitalization_of(word: &'static str) -> Self {
105 Self::default().then_any_capitalization_of(word)
106 }
107
108 pub fn aco(word: &'static str) -> Self {
110 Self::any_capitalization_of(word)
111 }
112
113 pub fn word_set<I, S>(words: I) -> Self
115 where
116 I: IntoIterator<Item = S>,
117 S: AsRef<str>,
118 {
119 Self::default().then_word_set(words)
120 }
121
122 pub fn any_word() -> Self {
124 Self::default().then_any_word()
125 }
126
127 pub fn number() -> Self {
129 Self::default().then_number()
130 }
131
132 pub fn optional(expr: impl Expr + 'static) -> Self {
136 Self::default().then_optional(expr)
137 }
138
139 pub fn word_seq(words: &'static [&'static str]) -> Self {
141 Self::default().then_word_seq(words)
142 }
143
144 pub fn fixed_phrase(phrase: &'static str) -> Self {
146 Self::default().then_fixed_phrase(phrase)
147 }
148
149 pub fn any_of(exprs: impl IntoIterator<Item = impl AsBoxedExpr>) -> Self {
153 Self::default().then_any_of(exprs)
154 }
155
156 pub fn longest_of(exprs: impl IntoIterator<Item = impl AsBoxedExpr>) -> Self {
158 Self::default().then_longest_of(exprs)
159 }
160
161 pub fn whitespace() -> Self {
162 Self::default().then_whitespace()
163 }
164
165 pub fn unless(condition: impl Expr + 'static) -> Self {
167 Self::default().then_unless(condition)
168 }
169
170 pub fn then(mut self, expr: impl Expr + 'static) -> Self {
174 self.exprs.push(Box::new(expr));
175 self
176 }
177
178 pub fn then_boxed(mut self, expr: Box<dyn Expr>) -> Self {
180 self.exprs.push(expr);
181 self
182 }
183
184 pub fn then_optional(mut self, expr: impl Expr + 'static) -> Self {
186 self.exprs.push(Box::new(Optional::new(expr)));
187 self
188 }
189
190 pub fn then_any_of(mut self, exprs: impl IntoIterator<Item = impl AsBoxedExpr>) -> Self {
196 self.exprs.push(Box::new(FirstMatchOf::new(exprs)));
197 self
198 }
199
200 pub fn then_longest_of(mut self, exprs: impl IntoIterator<Item = impl AsBoxedExpr>) -> Self {
205 self.exprs.push(Box::new(LongestMatchOf::new(exprs)));
206 self
207 }
208
209 pub fn then_seq(mut self, mut other: Self) -> Self {
212 self.exprs.append(&mut other.exprs);
213 self
214 }
215
216 pub fn then_word_set<I, S>(self, words: I) -> Self
218 where
219 I: IntoIterator<Item = S>,
220 S: AsRef<str>,
221 {
222 self.then(WordSet::new(words))
223 }
224
225 pub fn t_set<I, S>(self, words: I) -> Self
227 where
228 I: IntoIterator<Item = S>,
229 S: AsRef<str>,
230 {
231 self.then_word_set(words)
232 }
233
234 pub fn then_whitespace(self) -> Self {
236 self.then(WhitespacePattern)
237 }
238
239 pub fn t_ws(self) -> Self {
241 self.then_whitespace()
242 }
243
244 pub fn then_whitespace_or_hyphen(self) -> Self {
246 self.then(WhitespacePattern.or(|tok: &Token, _: &[char]| tok.kind.is_hyphen()))
247 }
248
249 pub fn t_ws_h(self) -> Self {
251 self.then_whitespace_or_hyphen()
252 }
253
254 pub fn then_optional_whitespace(self) -> Self {
256 self.then_optional(WhitespacePattern)
257 }
258
259 pub fn t_ows(self) -> Self {
261 self.then_optional_whitespace()
262 }
263
264 pub fn then_zero_or_more(self, expr: impl Expr + 'static) -> Self {
266 self.then(Repeating::new(Box::new(expr), 0))
267 }
268
269 pub fn then_one_or_more(self, expr: impl Expr + 'static) -> Self {
271 self.then(Repeating::new(Box::new(expr), 1))
272 }
273
274 pub fn then_zero_or_more_spaced(self, expr: impl Expr + 'static) -> Self {
276 let expr = Lrc::new(expr);
277 self.then(SequenceExpr::with(expr.clone()).then(Repeating::new(
278 Box::new(SequenceExpr::default().t_ws().then(expr)),
279 0,
280 )))
281 }
282
283 pub fn then_unless(self, condition: impl Expr + 'static) -> Self {
290 self.then(UnlessStep::new(condition, |_tok: &Token, _src: &[char]| {
291 true
292 }))
293 }
294
295 pub fn then_anything(self) -> Self {
299 self.then(AnyPattern)
300 }
301
302 pub fn t_any(self) -> Self {
306 self.then_anything()
307 }
308
309 pub fn then_any_word(self) -> Self {
313 self.then_kind_where(|kind| kind.is_word())
314 }
315
316 pub fn then_any_capitalization_of(self, word: &'static str) -> Self {
318 self.then(Word::new(word))
319 }
320
321 pub fn t_aco(self, word: &'static str) -> Self {
323 self.then_any_capitalization_of(word)
324 }
325
326 pub fn then_exact_word(self, word: &'static str) -> Self {
328 self.then(Word::new_exact(word))
329 }
330
331 pub fn then_word_seq(self, words: &'static [&'static str]) -> Self {
333 if let Some((first, rest)) = words.split_first() {
334 let mut expr = self.t_aco(first);
335 for word in rest {
336 expr = expr.t_ws().t_aco(word);
337 }
338 expr
339 } else {
340 self
341 }
342 }
343
344 pub fn then_fixed_phrase(self, phrase: &'static str) -> Self {
346 self.then(FixedPhrase::from_phrase(phrase))
347 }
348
349 pub fn then_word_except(self, words: &'static [&'static str]) -> Self {
351 self.then(move |tok: &Token, src: &[char]| {
352 !tok.kind.is_word() || !words.iter().any(|&word| tok.get_ch(src).eq_str(word))
353 })
354 }
355
356 pub fn then_kind(self, kind: TokenKind) -> Self {
362 self.then_kind_where(move |k| kind == *k)
363 }
364
365 pub fn then_kind_where<F>(mut self, predicate: F) -> Self
367 where
368 F: Fn(&TokenKind) -> bool + Send + Sync + 'static,
369 {
370 self.exprs
371 .push(Box::new(move |tok: &Token, _source: &[char]| {
372 predicate(&tok.kind)
373 }));
374 self
375 }
376
377 pub fn then_kind_except<F>(self, pred_is: F, ex: &'static [&'static str]) -> Self
379 where
380 F: Fn(&TokenKind) -> bool + Send + Sync + 'static,
381 {
382 self.then(move |tok: &Token, src: &[char]| {
383 pred_is(&tok.kind) && !ex.iter().any(|&word| tok.get_ch(src).eq_str(word))
384 })
385 }
386
387 pub fn then_kind_both<F1, F2>(self, pred_is_1: F1, pred_is_2: F2) -> Self
392 where
393 F1: Fn(&TokenKind) -> bool + Send + Sync + 'static,
394 F2: Fn(&TokenKind) -> bool + Send + Sync + 'static,
395 {
396 self.then_kind_where(move |k| pred_is_1(k) && pred_is_2(k))
397 }
398
399 pub fn then_kind_either<F1, F2>(self, pred_is_1: F1, pred_is_2: F2) -> Self
402 where
403 F1: Fn(&TokenKind) -> bool + Send + Sync + 'static,
404 F2: Fn(&TokenKind) -> bool + Send + Sync + 'static,
405 {
406 self.then_kind_where(move |k| pred_is_1(k) || pred_is_2(k))
407 }
408
409 pub fn then_kind_neither<F1, F2>(self, pred_isnt_1: F1, pred_isnt_2: F2) -> Self
412 where
413 F1: Fn(&TokenKind) -> bool + Send + Sync + 'static,
414 F2: Fn(&TokenKind) -> bool + Send + Sync + 'static,
415 {
416 self.then_kind_where(move |k| !pred_isnt_1(k) && !pred_isnt_2(k))
417 }
418
419 pub fn then_kind_is_but_is_not<F1, F2>(self, pred_is: F1, pred_not: F2) -> Self
422 where
423 F1: Fn(&TokenKind) -> bool + Send + Sync + 'static,
424 F2: Fn(&TokenKind) -> bool + Send + Sync + 'static,
425 {
426 self.then_kind_where(move |k| pred_is(k) && !pred_not(k))
427 }
428
429 pub fn then_kind_is_but_is_not_except<F1, F2>(
432 self,
433 pred_is: F1,
434 pred_not: F2,
435 ex: &'static [&'static str],
436 ) -> Self
437 where
438 F1: Fn(&TokenKind) -> bool + Send + Sync + 'static,
439 F2: Fn(&TokenKind) -> bool + Send + Sync + 'static,
440 {
441 self.then(move |tok: &Token, src: &[char]| {
442 pred_is(&tok.kind)
443 && !pred_not(&tok.kind)
444 && !ex.iter().any(|&word| tok.get_ch(src).eq_str(word))
445 })
446 }
447
448 pub fn then_kind_is_but_isnt_any_of<F1, F2>(
451 self,
452 pred_is: F1,
453 preds_isnt: &'static [F2],
454 ) -> Self
455 where
456 F1: Fn(&TokenKind) -> bool + Send + Sync + 'static,
457 F2: Fn(&TokenKind) -> bool + Send + Sync + 'static,
458 {
459 self.then_kind_where(move |k| pred_is(k) && !preds_isnt.iter().any(|pred| pred(k)))
460 }
461
462 pub fn then_kind_is_but_isnt_any_of_except<F1, F2>(
466 self,
467 pred_is: F1,
468 preds_isnt: &'static [F2],
469 ex: &'static [&'static str],
470 ) -> Self
471 where
472 F1: Fn(&TokenKind) -> bool + Send + Sync + 'static,
473 F2: Fn(&TokenKind) -> bool + Send + Sync + 'static,
474 {
475 self.then(move |tok: &Token, src: &[char]| {
476 pred_is(&tok.kind)
477 && !preds_isnt.iter().any(|pred| pred(&tok.kind))
478 && !ex.iter().any(|&word| tok.get_ch(src).eq_str(word))
479 })
480 }
481
482 pub fn then_kind_both_but_not<F1, F2, F3>(
488 self,
489 (pred_is_1, pred_is_2): (F1, F2),
490 pred_not: F3,
491 ) -> Self
492 where
493 F1: Fn(&TokenKind) -> bool + Send + Sync + 'static,
494 F2: Fn(&TokenKind) -> bool + Send + Sync + 'static,
495 F3: Fn(&TokenKind) -> bool + Send + Sync + 'static,
496 {
497 self.then_kind_where(move |k| pred_is_1(k) && pred_is_2(k) && !pred_not(k))
498 }
499
500 pub fn then_kind_any<F>(self, preds_is: &'static [F]) -> Self
503 where
504 F: Fn(&TokenKind) -> bool + Send + Sync + 'static,
505 {
506 self.then_kind_where(move |k| preds_is.iter().any(|pred| pred(k)))
507 }
508
509 pub fn then_kind_none_of<F>(self, preds_isnt: &'static [F]) -> Self
512 where
513 F: Fn(&TokenKind) -> bool + Send + Sync + 'static,
514 {
515 self.then_kind_where(move |k| preds_isnt.iter().all(|pred| !pred(k)))
516 }
517
518 pub fn then_kind_any_except<F>(
521 self,
522 preds_is: &'static [F],
523 ex: &'static [&'static str],
524 ) -> Self
525 where
526 F: Fn(&TokenKind) -> bool + Send + Sync + 'static,
527 {
528 self.then(move |tok: &Token, src: &[char]| {
529 preds_is.iter().any(|pred| pred(&tok.kind))
530 && !ex.iter().any(|&word| tok.get_ch(src).eq_str(word))
531 })
532 }
533
534 pub fn then_kind_any_or_words<F>(
537 self,
538 preds: &'static [F],
539 words: &'static [&'static str],
540 ) -> Self
541 where
542 F: Fn(&TokenKind) -> bool + Send + Sync + 'static,
543 {
544 self.then(move |tok: &Token, src: &[char]| {
545 preds.iter().any(|pred| pred(&tok.kind))
546 || words.iter().any(|&word| tok.get_ch(src).eq_str(word))
547 })
548 }
549
550 pub fn then_kind_any_but_not<F1, F2>(self, preds_is: &'static [F1], pred_not: F2) -> Self
553 where
554 F1: Fn(&TokenKind) -> bool + Send + Sync + 'static,
555 F2: Fn(&TokenKind) -> bool + Send + Sync + 'static,
556 {
557 self.then(move |tok: &Token, _src: &[char]| {
558 preds_is.iter().any(|pred| pred(&tok.kind)) && !pred_not(&tok.kind)
559 })
560 }
561
562 pub fn then_kind_any_but_not_except<F1, F2>(
565 self,
566 preds_is: &'static [F1],
567 pred_not: F2,
568 ex: &'static [&'static str],
569 ) -> Self
570 where
571 F1: Fn(&TokenKind) -> bool + Send + Sync + 'static,
572 F2: Fn(&TokenKind) -> bool + Send + Sync + 'static,
573 {
574 self.then(move |tok: &Token, src: &[char]| {
575 preds_is.iter().any(|pred| pred(&tok.kind))
576 && !pred_not(&tok.kind)
577 && !ex.iter().any(|&word| tok.get_ch(src).eq_str(word))
578 })
579 }
580
581 gen_then_from_is!(oov);
585 gen_then_from_is!(swear);
586
587 gen_then_from_is!(nominal);
592 gen_then_from_is!(plural_nominal);
593 gen_then_from_is!(non_plural_nominal);
594 gen_then_from_is!(possessive_nominal);
595
596 gen_then_from_is!(noun);
599 gen_then_from_is!(proper_noun);
600 gen_then_from_is!(singular_noun);
601 gen_then_from_is!(plural_noun);
602 gen_then_from_is!(singular_noun_only);
603 gen_then_from_is!(plural_noun_only);
604 gen_then_from_is!(mass_noun_only);
605
606 gen_then_from_is!(pronoun);
609 gen_then_from_is!(personal_pronoun);
610 gen_then_from_is!(first_person_singular_pronoun);
611 gen_then_from_is!(first_person_plural_pronoun);
612 gen_then_from_is!(second_person_pronoun);
613 gen_then_from_is!(third_person_pronoun);
614 gen_then_from_is!(third_person_singular_pronoun);
615 gen_then_from_is!(third_person_plural_pronoun);
616 gen_then_from_is!(subject_pronoun);
617 gen_then_from_is!(object_pronoun);
618
619 pub fn then_relative_pronoun(self) -> Self {
620 self.then(RelativePronoun::default())
621 }
622
623 gen_then_from_is!(verb);
626 gen_then_from_is!(auxiliary_verb);
627 gen_then_from_is!(linking_verb);
628 gen_then_from_is!(verb_lemma);
629 gen_then_from_is!(verb_simple_past_form);
630 gen_then_from_is!(verb_past_participle_form);
631 gen_then_from_is!(verb_progressive_form);
632 gen_then_from_is!(verb_third_person_singular_present_form);
633
634 gen_then_from_is!(adjective);
637 gen_then_from_is!(positive_adjective);
638 gen_then_from_is!(comparative_adjective);
639 gen_then_from_is!(superlative_adjective);
640
641 gen_then_from_is!(adverb);
644 gen_then_from_is!(frequency_adverb);
645 gen_then_from_is!(degree_adverb);
646
647 gen_then_from_is!(determiner);
650 gen_then_from_is!(demonstrative_determiner);
651 gen_then_from_is!(possessive_determiner);
652 gen_then_from_is!(quantifier);
653 gen_then_from_is!(non_quantifier_determiner);
654 gen_then_from_is!(non_demonstrative_determiner);
655
656 pub fn then_indefinite_article(self) -> Self {
658 self.then(IndefiniteArticle::default())
659 }
660
661 gen_then_from_is!(conjunction);
664 gen_then_from_is!(preposition);
665
666 gen_then_from_is!(number);
669 gen_then_from_is!(cardinal_number);
670 gen_then_from_is!(ordinal_number);
671
672 gen_then_from_is!(punctuation);
675 gen_then_from_is!(apostrophe);
676 gen_then_from_is!(comma);
677 gen_then_from_is!(hyphen);
678 gen_then_from_is!(period);
679 gen_then_from_is!(semicolon);
680 gen_then_from_is!(acute);
681 gen_then_from_is!(quote);
682 gen_then_from_is!(backslash);
683 gen_then_from_is!(slash);
684 gen_then_from_is!(percent);
685 gen_then_from_is!(degree);
686 gen_then_from_is!(open_single);
687 gen_then_from_is!(single_prime);
688 gen_then_from_is!(double_prime);
689 gen_then_from_is!(backtick);
690 gen_then_from_is!(plus);
691
692 gen_then_from_is!(case_separator);
695 gen_then_from_is!(likely_homograph);
696 gen_then_from_is!(sentence_terminator);
697}
698
699impl<S> From<S> for SequenceExpr
700where
701 S: Step + 'static,
702{
703 fn from(step: S) -> Self {
704 Self {
705 exprs: vec![Box::new(step)],
706 }
707 }
708}
709
710#[cfg(test)]
711mod tests {
712 use crate::{
713 Document, TokenKind,
714 expr::{AnchorEnd, Expr, ExprExt, SequenceExpr},
715 linting::tests::SpanVecExt,
716 };
717
718 #[test]
719 fn test_kind_both() {
720 let noun_and_verb =
721 SequenceExpr::default().then_kind_both(TokenKind::is_noun, TokenKind::is_verb);
722 let doc = Document::new_plain_english_curated("Use a good example.");
723 let matches = noun_and_verb.iter_matches_in_doc(&doc).collect::<Vec<_>>();
724 assert_eq!(matches.to_strings(&doc), vec!["Use", "good", "example"]);
725 }
726
727 #[test]
728 fn test_adjective_or_determiner() {
729 let expr = SequenceExpr::default()
730 .then_kind_either(TokenKind::is_adjective, TokenKind::is_determiner);
731 let doc = Document::new_plain_english_curated("Use a good example.");
732 let matches = expr.iter_matches_in_doc(&doc).collect::<Vec<_>>();
733 assert_eq!(matches.to_strings(&doc), vec!["a", "good"]);
734 }
735
736 #[test]
737 fn test_noun_but_not_adjective() {
738 let expr = SequenceExpr::default()
739 .then_kind_is_but_is_not(TokenKind::is_noun, TokenKind::is_adjective);
740 let doc = Document::new_plain_english_curated("Use a good example.");
741 let matches = expr.iter_matches_in_doc(&doc).collect::<Vec<_>>();
742 assert_eq!(matches.to_strings(&doc), vec!["Use", "example"]);
743 }
744
745 #[test]
746 fn flag_foo_followed_by_bar_or_at_end_1() {
747 let expr = SequenceExpr::aco("foo").then_any_of([
748 Box::new(SequenceExpr::whitespace().t_aco("bar").then(AnchorEnd)) as Box<dyn Expr>,
749 Box::new(AnchorEnd),
750 ]);
751
752 let doc_with_bar = Document::new_plain_english_curated("foo bar");
753
754 let matches_with_bar = expr.iter_matches_in_doc(&doc_with_bar).collect::<Vec<_>>();
755
756 eprintln!("matches_with_bar: {:#?}", matches_with_bar);
757
758 assert_eq!(matches_with_bar.len(), 1);
760 assert_eq!(matches_with_bar[0].start, 0);
761 assert_eq!(matches_with_bar[0].end, 3);
762 assert_eq!(matches_with_bar.to_strings(&doc_with_bar), vec!["foo bar"]);
763 }
764
765 #[test]
766 fn flag_foo_followed_by_bar_or_at_end_2() {
767 let expr = SequenceExpr::aco("foo").then_any_of([
768 Box::new(SequenceExpr::whitespace().t_aco("bar").then(AnchorEnd)) as Box<dyn Expr>,
769 Box::new(AnchorEnd),
770 ]);
771
772 let doc_with_end = Document::new_plain_english_curated("foo");
773
774 let matches_with_end = expr.iter_matches_in_doc(&doc_with_end).collect::<Vec<_>>();
775
776 eprintln!("matches_with_end: {:#?}", matches_with_end);
777
778 assert_eq!(matches_with_end.len(), 1);
780 assert_eq!(matches_with_end[0].start, 0);
781 assert_eq!(matches_with_end[0].end, 1);
782 assert_eq!(matches_with_end.to_strings(&doc_with_end), vec!["foo"]);
783 }
784
785 #[test]
786 fn flag_foo_followed_by_bar_or_at_end_3() {
787 let expr = SequenceExpr::aco("foo").then_any_of([
788 Box::new(SequenceExpr::whitespace().t_aco("bar").then(AnchorEnd)) as Box<dyn Expr>,
789 Box::new(AnchorEnd),
790 ]);
791
792 let doc_with_foo_bar_baz = Document::new_plain_english_curated("foo bar baz");
793
794 let matches_with_foo_bar_baz = expr
795 .iter_matches_in_doc(&doc_with_foo_bar_baz)
796 .collect::<Vec<_>>();
797
798 eprintln!("matches_with_foo_bar_baz: {:#?}", matches_with_foo_bar_baz);
799
800 assert_eq!(matches_with_foo_bar_baz.len(), 0);
802 assert_eq!(
803 matches_with_foo_bar_baz.to_strings(&doc_with_foo_bar_baz),
804 Vec::<String>::new()
805 );
806 }
807}