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(words: &'static [&'static str]) -> Self {
115 Self::default().then_word_set(words)
116 }
117
118 pub fn any_word() -> Self {
120 Self::default().then_any_word()
121 }
122
123 pub fn number() -> Self {
125 Self::default().then_number()
126 }
127
128 pub fn optional(expr: impl Expr + 'static) -> Self {
132 Self::default().then_optional(expr)
133 }
134
135 pub fn word_seq(words: &'static [&'static str]) -> Self {
137 Self::default().then_word_seq(words)
138 }
139
140 pub fn fixed_phrase(phrase: &'static str) -> Self {
142 Self::default().then_fixed_phrase(phrase)
143 }
144
145 pub fn any_of(exprs: impl IntoIterator<Item = impl AsBoxedExpr>) -> Self {
149 Self::default().then_any_of(exprs)
150 }
151
152 pub fn longest_of(exprs: impl IntoIterator<Item = impl AsBoxedExpr>) -> Self {
154 Self::default().then_longest_of(exprs)
155 }
156
157 pub fn whitespace() -> Self {
158 Self::default().then_whitespace()
159 }
160
161 pub fn unless(condition: impl Expr + 'static) -> Self {
163 Self::default().then_unless(condition)
164 }
165
166 pub fn then(mut self, expr: impl Expr + 'static) -> Self {
170 self.exprs.push(Box::new(expr));
171 self
172 }
173
174 pub fn then_boxed(mut self, expr: Box<dyn Expr>) -> Self {
176 self.exprs.push(expr);
177 self
178 }
179
180 pub fn then_optional(mut self, expr: impl Expr + 'static) -> Self {
182 self.exprs.push(Box::new(Optional::new(expr)));
183 self
184 }
185
186 pub fn then_any_of(mut self, exprs: impl IntoIterator<Item = impl AsBoxedExpr>) -> Self {
192 self.exprs.push(Box::new(FirstMatchOf::new(exprs)));
193 self
194 }
195
196 pub fn then_longest_of(mut self, exprs: impl IntoIterator<Item = impl AsBoxedExpr>) -> Self {
201 self.exprs.push(Box::new(LongestMatchOf::new(exprs)));
202 self
203 }
204
205 pub fn then_seq(mut self, mut other: Self) -> Self {
208 self.exprs.append(&mut other.exprs);
209 self
210 }
211
212 pub fn then_word_set(self, words: &'static [&'static str]) -> Self {
214 self.then(WordSet::new(words))
215 }
216
217 pub fn t_set(self, words: &'static [&'static str]) -> Self {
219 self.then_word_set(words)
220 }
221
222 pub fn then_whitespace(self) -> Self {
224 self.then(WhitespacePattern)
225 }
226
227 pub fn t_ws(self) -> Self {
229 self.then_whitespace()
230 }
231
232 pub fn then_whitespace_or_hyphen(self) -> Self {
234 self.then(WhitespacePattern.or(|tok: &Token, _: &[char]| tok.kind.is_hyphen()))
235 }
236
237 pub fn t_ws_h(self) -> Self {
239 self.then_whitespace_or_hyphen()
240 }
241
242 pub fn then_optional_whitespace(self) -> Self {
244 self.then_optional(WhitespacePattern)
245 }
246
247 pub fn t_ows(self) -> Self {
249 self.then_optional_whitespace()
250 }
251
252 pub fn then_zero_or_more(self, expr: impl Expr + 'static) -> Self {
254 self.then(Repeating::new(Box::new(expr), 0))
255 }
256
257 pub fn then_one_or_more(self, expr: impl Expr + 'static) -> Self {
259 self.then(Repeating::new(Box::new(expr), 1))
260 }
261
262 pub fn then_zero_or_more_spaced(self, expr: impl Expr + 'static) -> Self {
264 let expr = Lrc::new(expr);
265 self.then(SequenceExpr::with(expr.clone()).then(Repeating::new(
266 Box::new(SequenceExpr::default().t_ws().then(expr)),
267 0,
268 )))
269 }
270
271 pub fn then_unless(self, condition: impl Expr + 'static) -> Self {
278 self.then(UnlessStep::new(condition, |_tok: &Token, _src: &[char]| {
279 true
280 }))
281 }
282
283 pub fn then_anything(self) -> Self {
287 self.then(AnyPattern)
288 }
289
290 pub fn t_any(self) -> Self {
294 self.then_anything()
295 }
296
297 pub fn then_any_word(self) -> Self {
301 self.then_kind_where(|kind| kind.is_word())
302 }
303
304 pub fn then_any_capitalization_of(self, word: &'static str) -> Self {
306 self.then(Word::new(word))
307 }
308
309 pub fn t_aco(self, word: &'static str) -> Self {
311 self.then_any_capitalization_of(word)
312 }
313
314 pub fn then_exact_word(self, word: &'static str) -> Self {
316 self.then(Word::new_exact(word))
317 }
318
319 pub fn then_word_seq(self, words: &'static [&'static str]) -> Self {
321 if let Some((first, rest)) = words.split_first() {
322 let mut expr = self.t_aco(first);
323 for word in rest {
324 expr = expr.t_ws().t_aco(word);
325 }
326 expr
327 } else {
328 self
329 }
330 }
331
332 pub fn then_fixed_phrase(self, phrase: &'static str) -> Self {
334 self.then(FixedPhrase::from_phrase(phrase))
335 }
336
337 pub fn then_word_except(self, words: &'static [&'static str]) -> Self {
339 self.then(move |tok: &Token, src: &[char]| {
340 !tok.kind.is_word() || !words.iter().any(|&word| tok.get_ch(src).eq_str(word))
341 })
342 }
343
344 pub fn then_kind(self, kind: TokenKind) -> Self {
350 self.then_kind_where(move |k| kind == *k)
351 }
352
353 pub fn then_kind_where<F>(mut self, predicate: F) -> Self
355 where
356 F: Fn(&TokenKind) -> bool + Send + Sync + 'static,
357 {
358 self.exprs
359 .push(Box::new(move |tok: &Token, _source: &[char]| {
360 predicate(&tok.kind)
361 }));
362 self
363 }
364
365 pub fn then_kind_except<F>(self, pred_is: F, ex: &'static [&'static str]) -> Self
367 where
368 F: Fn(&TokenKind) -> bool + Send + Sync + 'static,
369 {
370 self.then(move |tok: &Token, src: &[char]| {
371 pred_is(&tok.kind) && !ex.iter().any(|&word| tok.get_ch(src).eq_str(word))
372 })
373 }
374
375 pub fn then_kind_both<F1, F2>(self, pred_is_1: F1, pred_is_2: F2) -> Self
380 where
381 F1: Fn(&TokenKind) -> bool + Send + Sync + 'static,
382 F2: Fn(&TokenKind) -> bool + Send + Sync + 'static,
383 {
384 self.then_kind_where(move |k| pred_is_1(k) && pred_is_2(k))
385 }
386
387 pub fn then_kind_either<F1, F2>(self, pred_is_1: F1, pred_is_2: F2) -> Self
390 where
391 F1: Fn(&TokenKind) -> bool + Send + Sync + 'static,
392 F2: Fn(&TokenKind) -> bool + Send + Sync + 'static,
393 {
394 self.then_kind_where(move |k| pred_is_1(k) || pred_is_2(k))
395 }
396
397 pub fn then_kind_neither<F1, F2>(self, pred_isnt_1: F1, pred_isnt_2: F2) -> Self
400 where
401 F1: Fn(&TokenKind) -> bool + Send + Sync + 'static,
402 F2: Fn(&TokenKind) -> bool + Send + Sync + 'static,
403 {
404 self.then_kind_where(move |k| !pred_isnt_1(k) && !pred_isnt_2(k))
405 }
406
407 pub fn then_kind_is_but_is_not<F1, F2>(self, pred_is: F1, pred_not: F2) -> Self
410 where
411 F1: Fn(&TokenKind) -> bool + Send + Sync + 'static,
412 F2: Fn(&TokenKind) -> bool + Send + Sync + 'static,
413 {
414 self.then_kind_where(move |k| pred_is(k) && !pred_not(k))
415 }
416
417 pub fn then_kind_is_but_is_not_except<F1, F2>(
420 self,
421 pred_is: F1,
422 pred_not: F2,
423 ex: &'static [&'static str],
424 ) -> Self
425 where
426 F1: Fn(&TokenKind) -> bool + Send + Sync + 'static,
427 F2: Fn(&TokenKind) -> bool + Send + Sync + 'static,
428 {
429 self.then(move |tok: &Token, src: &[char]| {
430 pred_is(&tok.kind)
431 && !pred_not(&tok.kind)
432 && !ex.iter().any(|&word| tok.get_ch(src).eq_str(word))
433 })
434 }
435
436 pub fn then_kind_is_but_isnt_any_of<F1, F2>(
439 self,
440 pred_is: F1,
441 preds_isnt: &'static [F2],
442 ) -> Self
443 where
444 F1: Fn(&TokenKind) -> bool + Send + Sync + 'static,
445 F2: Fn(&TokenKind) -> bool + Send + Sync + 'static,
446 {
447 self.then_kind_where(move |k| pred_is(k) && !preds_isnt.iter().any(|pred| pred(k)))
448 }
449
450 pub fn then_kind_is_but_isnt_any_of_except<F1, F2>(
454 self,
455 pred_is: F1,
456 preds_isnt: &'static [F2],
457 ex: &'static [&'static str],
458 ) -> Self
459 where
460 F1: Fn(&TokenKind) -> bool + Send + Sync + 'static,
461 F2: Fn(&TokenKind) -> bool + Send + Sync + 'static,
462 {
463 self.then(move |tok: &Token, src: &[char]| {
464 pred_is(&tok.kind)
465 && !preds_isnt.iter().any(|pred| pred(&tok.kind))
466 && !ex.iter().any(|&word| tok.get_ch(src).eq_str(word))
467 })
468 }
469
470 pub fn then_kind_both_but_not<F1, F2, F3>(
476 self,
477 (pred_is_1, pred_is_2): (F1, F2),
478 pred_not: F3,
479 ) -> Self
480 where
481 F1: Fn(&TokenKind) -> bool + Send + Sync + 'static,
482 F2: Fn(&TokenKind) -> bool + Send + Sync + 'static,
483 F3: Fn(&TokenKind) -> bool + Send + Sync + 'static,
484 {
485 self.then_kind_where(move |k| pred_is_1(k) && pred_is_2(k) && !pred_not(k))
486 }
487
488 pub fn then_kind_any<F>(self, preds_is: &'static [F]) -> Self
491 where
492 F: Fn(&TokenKind) -> bool + Send + Sync + 'static,
493 {
494 self.then_kind_where(move |k| preds_is.iter().any(|pred| pred(k)))
495 }
496
497 pub fn then_kind_none_of<F>(self, preds_isnt: &'static [F]) -> Self
500 where
501 F: Fn(&TokenKind) -> bool + Send + Sync + 'static,
502 {
503 self.then_kind_where(move |k| preds_isnt.iter().all(|pred| !pred(k)))
504 }
505
506 pub fn then_kind_any_except<F>(
509 self,
510 preds_is: &'static [F],
511 ex: &'static [&'static str],
512 ) -> Self
513 where
514 F: Fn(&TokenKind) -> bool + Send + Sync + 'static,
515 {
516 self.then(move |tok: &Token, src: &[char]| {
517 preds_is.iter().any(|pred| pred(&tok.kind))
518 && !ex.iter().any(|&word| tok.get_ch(src).eq_str(word))
519 })
520 }
521
522 pub fn then_kind_any_or_words<F>(
525 self,
526 preds: &'static [F],
527 words: &'static [&'static str],
528 ) -> Self
529 where
530 F: Fn(&TokenKind) -> bool + Send + Sync + 'static,
531 {
532 self.then(move |tok: &Token, src: &[char]| {
533 preds.iter().any(|pred| pred(&tok.kind))
534 || words.iter().any(|&word| tok.get_ch(src).eq_str(word))
535 })
536 }
537
538 pub fn then_kind_any_but_not<F1, F2>(self, preds_is: &'static [F1], pred_not: F2) -> Self
541 where
542 F1: Fn(&TokenKind) -> bool + Send + Sync + 'static,
543 F2: Fn(&TokenKind) -> bool + Send + Sync + 'static,
544 {
545 self.then(move |tok: &Token, _src: &[char]| {
546 preds_is.iter().any(|pred| pred(&tok.kind)) && !pred_not(&tok.kind)
547 })
548 }
549
550 pub fn then_kind_any_but_not_except<F1, F2>(
553 self,
554 preds_is: &'static [F1],
555 pred_not: F2,
556 ex: &'static [&'static str],
557 ) -> Self
558 where
559 F1: Fn(&TokenKind) -> bool + Send + Sync + 'static,
560 F2: Fn(&TokenKind) -> bool + Send + Sync + 'static,
561 {
562 self.then(move |tok: &Token, src: &[char]| {
563 preds_is.iter().any(|pred| pred(&tok.kind))
564 && !pred_not(&tok.kind)
565 && !ex.iter().any(|&word| tok.get_ch(src).eq_str(word))
566 })
567 }
568
569 gen_then_from_is!(oov);
573 gen_then_from_is!(swear);
574
575 gen_then_from_is!(nominal);
580 gen_then_from_is!(plural_nominal);
581 gen_then_from_is!(non_plural_nominal);
582 gen_then_from_is!(possessive_nominal);
583
584 gen_then_from_is!(noun);
587 gen_then_from_is!(proper_noun);
588 gen_then_from_is!(plural_noun);
589 gen_then_from_is!(singular_noun);
590 gen_then_from_is!(mass_noun_only);
591
592 gen_then_from_is!(pronoun);
595 gen_then_from_is!(personal_pronoun);
596 gen_then_from_is!(first_person_singular_pronoun);
597 gen_then_from_is!(first_person_plural_pronoun);
598 gen_then_from_is!(second_person_pronoun);
599 gen_then_from_is!(third_person_pronoun);
600 gen_then_from_is!(third_person_singular_pronoun);
601 gen_then_from_is!(third_person_plural_pronoun);
602 gen_then_from_is!(subject_pronoun);
603 gen_then_from_is!(object_pronoun);
604
605 pub fn then_relative_pronoun(self) -> Self {
606 self.then(RelativePronoun::default())
607 }
608
609 gen_then_from_is!(verb);
612 gen_then_from_is!(auxiliary_verb);
613 gen_then_from_is!(linking_verb);
614 gen_then_from_is!(verb_lemma);
615 gen_then_from_is!(verb_simple_past_form);
616 gen_then_from_is!(verb_past_participle_form);
617 gen_then_from_is!(verb_progressive_form);
618 gen_then_from_is!(verb_third_person_singular_present_form);
619
620 gen_then_from_is!(adjective);
623 gen_then_from_is!(positive_adjective);
624 gen_then_from_is!(comparative_adjective);
625 gen_then_from_is!(superlative_adjective);
626
627 gen_then_from_is!(adverb);
630 gen_then_from_is!(frequency_adverb);
631 gen_then_from_is!(degree_adverb);
632
633 gen_then_from_is!(determiner);
636 gen_then_from_is!(demonstrative_determiner);
637 gen_then_from_is!(possessive_determiner);
638 gen_then_from_is!(quantifier);
639 gen_then_from_is!(non_quantifier_determiner);
640 gen_then_from_is!(non_demonstrative_determiner);
641
642 pub fn then_indefinite_article(self) -> Self {
644 self.then(IndefiniteArticle::default())
645 }
646
647 gen_then_from_is!(conjunction);
650 gen_then_from_is!(preposition);
651
652 gen_then_from_is!(number);
655 gen_then_from_is!(cardinal_number);
656 gen_then_from_is!(ordinal_number);
657
658 gen_then_from_is!(punctuation);
661 gen_then_from_is!(apostrophe);
662 gen_then_from_is!(comma);
663 gen_then_from_is!(hyphen);
664 gen_then_from_is!(period);
665 gen_then_from_is!(semicolon);
666 gen_then_from_is!(acute);
667 gen_then_from_is!(quote);
668 gen_then_from_is!(backslash);
669 gen_then_from_is!(slash);
670 gen_then_from_is!(percent);
671 gen_then_from_is!(degree);
672 gen_then_from_is!(open_single);
673 gen_then_from_is!(single_prime);
674 gen_then_from_is!(double_prime);
675 gen_then_from_is!(backtick);
676 gen_then_from_is!(plus);
677
678 gen_then_from_is!(case_separator);
681 gen_then_from_is!(likely_homograph);
682 gen_then_from_is!(sentence_terminator);
683}
684
685impl<S> From<S> for SequenceExpr
686where
687 S: Step + 'static,
688{
689 fn from(step: S) -> Self {
690 Self {
691 exprs: vec![Box::new(step)],
692 }
693 }
694}
695
696#[cfg(test)]
697mod tests {
698 use crate::{
699 Document, TokenKind,
700 expr::{AnchorEnd, Expr, ExprExt, SequenceExpr},
701 linting::tests::SpanVecExt,
702 };
703
704 #[test]
705 fn test_kind_both() {
706 let noun_and_verb =
707 SequenceExpr::default().then_kind_both(TokenKind::is_noun, TokenKind::is_verb);
708 let doc = Document::new_plain_english_curated("Use a good example.");
709 let matches = noun_and_verb.iter_matches_in_doc(&doc).collect::<Vec<_>>();
710 assert_eq!(matches.to_strings(&doc), vec!["Use", "good", "example"]);
711 }
712
713 #[test]
714 fn test_adjective_or_determiner() {
715 let expr = SequenceExpr::default()
716 .then_kind_either(TokenKind::is_adjective, TokenKind::is_determiner);
717 let doc = Document::new_plain_english_curated("Use a good example.");
718 let matches = expr.iter_matches_in_doc(&doc).collect::<Vec<_>>();
719 assert_eq!(matches.to_strings(&doc), vec!["a", "good"]);
720 }
721
722 #[test]
723 fn test_noun_but_not_adjective() {
724 let expr = SequenceExpr::default()
725 .then_kind_is_but_is_not(TokenKind::is_noun, TokenKind::is_adjective);
726 let doc = Document::new_plain_english_curated("Use a good example.");
727 let matches = expr.iter_matches_in_doc(&doc).collect::<Vec<_>>();
728 assert_eq!(matches.to_strings(&doc), vec!["Use", "example"]);
729 }
730
731 #[test]
732 fn flag_foo_followed_by_bar_or_at_end_1() {
733 let expr = SequenceExpr::aco("foo").then_any_of([
734 Box::new(SequenceExpr::whitespace().t_aco("bar").then(AnchorEnd)) as Box<dyn Expr>,
735 Box::new(AnchorEnd),
736 ]);
737
738 let doc_with_bar = Document::new_plain_english_curated("foo bar");
739
740 let matches_with_bar = expr.iter_matches_in_doc(&doc_with_bar).collect::<Vec<_>>();
741
742 eprintln!("matches_with_bar: {:#?}", matches_with_bar);
743
744 assert_eq!(matches_with_bar.len(), 1);
746 assert_eq!(matches_with_bar[0].start, 0);
747 assert_eq!(matches_with_bar[0].end, 3);
748 assert_eq!(matches_with_bar.to_strings(&doc_with_bar), vec!["foo bar"]);
749 }
750
751 #[test]
752 fn flag_foo_followed_by_bar_or_at_end_2() {
753 let expr = SequenceExpr::aco("foo").then_any_of([
754 Box::new(SequenceExpr::whitespace().t_aco("bar").then(AnchorEnd)) as Box<dyn Expr>,
755 Box::new(AnchorEnd),
756 ]);
757
758 let doc_with_end = Document::new_plain_english_curated("foo");
759
760 let matches_with_end = expr.iter_matches_in_doc(&doc_with_end).collect::<Vec<_>>();
761
762 eprintln!("matches_with_end: {:#?}", matches_with_end);
763
764 assert_eq!(matches_with_end.len(), 1);
766 assert_eq!(matches_with_end[0].start, 0);
767 assert_eq!(matches_with_end[0].end, 1);
768 assert_eq!(matches_with_end.to_strings(&doc_with_end), vec!["foo"]);
769 }
770
771 #[test]
772 fn flag_foo_followed_by_bar_or_at_end_3() {
773 let expr = SequenceExpr::aco("foo").then_any_of([
774 Box::new(SequenceExpr::whitespace().t_aco("bar").then(AnchorEnd)) as Box<dyn Expr>,
775 Box::new(AnchorEnd),
776 ]);
777
778 let doc_with_foo_bar_baz = Document::new_plain_english_curated("foo bar baz");
779
780 let matches_with_foo_bar_baz = expr
781 .iter_matches_in_doc(&doc_with_foo_bar_baz)
782 .collect::<Vec<_>>();
783
784 eprintln!("matches_with_foo_bar_baz: {:#?}", matches_with_foo_bar_baz);
785
786 assert_eq!(matches_with_foo_bar_baz.len(), 0);
788 assert_eq!(
789 matches_with_foo_bar_baz.to_strings(&doc_with_foo_bar_baz),
790 Vec::<String>::new()
791 );
792 }
793}