harper_core/expr/
sequence_expr.rs

1use paste::paste;
2
3use crate::{
4    Span, Token, TokenKind,
5    patterns::{AnyPattern, IndefiniteArticle, WhitespacePattern, Word},
6};
7
8use super::{Expr, Optional, Repeating, Step, UnlessStep};
9
10#[derive(Default)]
11pub struct SequenceExpr {
12    exprs: Vec<Box<dyn Expr>>,
13}
14
15/// Generate a `then_*` method from an available `is_*` function on [`TokenKind`].
16macro_rules! gen_then_from_is {
17    ($quality:ident) => {
18        paste! {
19            pub fn [< then_$quality >] (self) -> Self{
20                self.then(|tok: &Token, _source: &[char]| {
21                    tok.kind.[< is_$quality >]()
22                })
23            }
24
25            pub fn [< then_one_or_more_$quality s >] (self) -> Self{
26                self.then_one_or_more(Box::new(|tok: &Token, _source: &[char]| {
27                    tok.kind.[< is_$quality >]()
28                }))
29            }
30
31            pub fn [< then_anything_but_$quality >] (self) -> Self{
32                self.then(|tok: &Token, _source: &[char]| {
33                    if tok.kind.[< is_$quality >](){
34                        false
35                    }else{
36                        true
37                    }
38                })
39            }
40        }
41    };
42}
43
44impl Expr for SequenceExpr {
45    /// Run the expression starting at an index, returning the total matched window.
46    ///
47    /// If any step returns `None`, the entire expression does as well.
48    fn run(&self, mut cursor: usize, tokens: &[Token], source: &[char]) -> Option<Span> {
49        let mut window = Span::new_with_len(cursor, 0);
50
51        for cur_expr in &self.exprs {
52            let out = cur_expr.run(cursor, tokens, source)?;
53
54            // Only expand the window if the match actually covers some tokens
55            if out.end > out.start {
56                window.expand_to_include(out.start);
57                window.expand_to_include(out.end.checked_sub(1).unwrap_or(out.start));
58            }
59
60            // Only advance cursor if we actually matched something
61            if out.end > cursor {
62                cursor = out.end;
63            } else if out.start < cursor {
64                cursor = out.start;
65            }
66            // If both start and end are equal to cursor, don't move the cursor
67        }
68
69        Some(window)
70    }
71}
72
73impl SequenceExpr {
74    pub fn then(mut self, expr: impl Expr + 'static) -> Self {
75        self.exprs.push(Box::new(expr));
76        self
77    }
78
79    /// Pushes an expression that could move the cursor to the sequence, but does not require it.
80    pub fn then_optional(mut self, expr: impl Expr + 'static) -> Self {
81        self.exprs.push(Box::new(Optional::new(expr)));
82        self
83    }
84
85    /// Appends the steps in `other` onto the end of `self`.
86    pub fn then_expr(mut self, mut other: Self) -> Self {
87        self.exprs.append(&mut other.exprs);
88        self
89    }
90
91    pub fn then_indefinite_article(self) -> Self {
92        self.then(IndefiniteArticle::default())
93    }
94
95    /// Match examples of `word` case-sensitively.
96    pub fn then_exact_word(self, word: &'static str) -> Self {
97        self.then(Word::new_exact(word))
98    }
99
100    /// Shorthand for [`Self::any_capitalization_of`].
101    pub fn aco(word: &'static str) -> Self {
102        Self::any_capitalization_of(word)
103    }
104
105    pub fn any_capitalization_of(word: &'static str) -> Self {
106        Self::default().then_any_capitalization_of(word)
107    }
108
109    /// Shorthand for [`Self::then_any_capitalization_of`].
110    pub fn t_aco(self, word: &'static str) -> Self {
111        self.then_any_capitalization_of(word)
112    }
113
114    /// Match examples of `word` that have any capitalization.
115    pub fn then_any_capitalization_of(self, word: &'static str) -> Self {
116        self.then(Word::new(word))
117    }
118
119    /// Matches any word.
120    pub fn then_any_word(self) -> Self {
121        self.then(|tok: &Token, _source: &[char]| tok.kind.is_word())
122    }
123
124    /// Matches any token whose `Kind` exactly matches.
125    pub fn then_strict(self, kind: TokenKind) -> Self {
126        self.then(move |tok: &Token, _source: &[char]| tok.kind == kind)
127    }
128
129    /// Shorthand for [`Self::then_whitespace`].
130    pub fn t_ws(self) -> Self {
131        self.then_whitespace()
132    }
133
134    /// Match against one or more whitespace tokens.
135    pub fn then_whitespace(self) -> Self {
136        self.then(WhitespacePattern)
137    }
138
139    pub fn then_one_or_more(self, expr: impl Expr + 'static) -> Self {
140        self.then(Repeating::new(Box::new(expr), 1))
141    }
142
143    /// Create a new condition that will step one token forward if met.
144    pub fn if_not_then_step_one(self, condition: impl Expr + 'static) -> Self {
145        self.then(UnlessStep::new(condition, |_tok: &Token, _src: &[char]| {
146            true
147        }))
148    }
149
150    pub fn t_any(self) -> Self {
151        self.then_anything()
152    }
153
154    pub fn then_anything(self) -> Self {
155        self.then(AnyPattern)
156    }
157
158    gen_then_from_is!(nominal);
159    gen_then_from_is!(noun);
160    gen_then_from_is!(possessive_nominal);
161    gen_then_from_is!(plural_nominal);
162    gen_then_from_is!(verb);
163    gen_then_from_is!(auxiliary_verb);
164    gen_then_from_is!(linking_verb);
165    gen_then_from_is!(pronoun);
166    gen_then_from_is!(punctuation);
167    gen_then_from_is!(conjunction);
168    gen_then_from_is!(comma);
169    gen_then_from_is!(period);
170    gen_then_from_is!(number);
171    gen_then_from_is!(case_separator);
172    gen_then_from_is!(adverb);
173    gen_then_from_is!(adjective);
174    gen_then_from_is!(apostrophe);
175    gen_then_from_is!(hyphen);
176    gen_then_from_is!(determiner);
177    gen_then_from_is!(proper_noun);
178    gen_then_from_is!(preposition);
179    gen_then_from_is!(third_person_pronoun);
180    gen_then_from_is!(third_person_singular_pronoun);
181    gen_then_from_is!(third_person_plural_pronoun);
182    gen_then_from_is!(first_person_singular_pronoun);
183    gen_then_from_is!(first_person_plural_pronoun);
184    gen_then_from_is!(second_person_pronoun);
185    gen_then_from_is!(non_plural_nominal);
186}
187
188impl<S> From<S> for SequenceExpr
189where
190    S: Step + 'static,
191{
192    fn from(step: S) -> Self {
193        Self {
194            exprs: vec![Box::new(step)],
195        }
196    }
197}