Skip to main content

harper_core/
dict_word_metadata.rs

1use harper_brill::UPOS;
2use is_macro::Is;
3use itertools::Itertools;
4use paste::paste;
5use serde::{Deserialize, Serialize};
6use smallvec::SmallVec;
7use strum::{EnumCount as _, VariantArray as _};
8use strum_macros::{Display, EnumCount, EnumIter, EnumString, VariantArray};
9
10use std::convert::TryFrom;
11
12use crate::dict_word_metadata_orthography::OrthFlags;
13use crate::spell::WordId;
14use crate::{Document, TokenKind, TokenStringExt};
15
16/// This represents a "lexeme" or "headword" which is case-folded but affix-expanded.
17/// So not only lemmata but also inflected forms are stored here, with "horn" and "horns" each
18/// having their own lexeme, but "Ivy" and "ivy" sharing the same lexeme.
19#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize, PartialOrd, Hash)]
20pub struct DictWordMetadata {
21    /// The main parts of speech which have extra data.
22    pub noun: Option<NounData>,
23    pub pronoun: Option<PronounData>,
24    pub verb: Option<VerbData>,
25    pub adjective: Option<AdjectiveData>,
26    pub adverb: Option<AdverbData>,
27    pub conjunction: Option<ConjunctionData>,
28    pub determiner: Option<DeterminerData>,
29    pub affix: Option<AffixData>,
30    /// Parts of speech which don't have extra data.
31    /// Whether the word is a [preposition](https://www.merriam-webster.com/dictionary/preposition).
32    #[serde(default = "default_false")]
33    pub preposition: bool,
34    /// Whether the word is an offensive word.
35    pub swear: Option<bool>,
36    /// Whether the word is an abbreviation of any kind.
37    pub abbreviation: Option<bool>,
38    /// The dialects this word belongs to.
39    /// If no dialects are defined, it can be assumed that the word is
40    /// valid in all dialects of English.
41    #[serde(default = "default_default")]
42    pub dialects: DialectFlags,
43    /// Orthographic information: letter case, spaces, hyphens, etc.
44    #[serde(default = "OrthFlags::empty")]
45    pub orth_info: OrthFlags,
46    /// Whether the word is considered especially common.
47    #[serde(default = "default_false")]
48    pub common: bool,
49    #[serde(default = "default_none")]
50    pub derived_from: Option<WordId>,
51    /// Generated by a chunker. Declares whether the word is a member of a nominal phrase. Using
52    /// this should be preferred over the similarly named `Pattern`.
53    ///
54    /// For more details, see [the announcement blog post](https://elijahpotter.dev/articles/training_a_chunker_with_burn).
55    pub np_member: Option<bool>,
56    /// Generated by a POS tagger. Declares what it inferred the word's part of speech to be.
57    pub pos_tag: Option<UPOS>,
58}
59
60/// Needed for `serde`
61fn default_false() -> bool {
62    false
63}
64
65/// Needed for `serde`
66fn default_none<T>() -> Option<T> {
67    None
68}
69
70/// Needed for `serde`
71fn default_default<T: Default>() -> T {
72    T::default()
73}
74
75macro_rules! generate_metadata_queries {
76    ($($category:ident has $($sub:ident),*).*) => {
77        paste! {
78            pub fn is_likely_homograph(&self) -> bool {
79                [self.is_determiner(), self.preposition, $(
80                    self.[< is_ $category >](),
81                )*].iter().map(|b| *b as u8).sum::<u8>() > 1
82            }
83
84            /// How different is this word from another?
85            pub fn difference(&self, other: &Self) -> u32 {
86                [
87                    $(
88                        Self::[< is_ $category >],
89                        $(
90                            Self::[< is_ $sub _ $category >],
91                            Self::[< is_non_ $sub _ $category >],
92                        )*
93                    )*
94                ]
95                .iter()
96                .fold(0, |acc, func| acc + (func(self) ^ func(other)) as u32)
97            }
98
99            $(
100                #[doc = concat!("Checks if the word is definitely a ", stringify!($category), ".")]
101                pub fn [< is_ $category >](&self) -> bool {
102                    self.$category.is_some()
103                }
104
105                $(
106                    #[doc = concat!("Checks if the word is definitely a ", stringify!($category), " and more specifically is labeled as (a) ", stringify!($sub), ".")]
107                    pub fn [< is_ $sub _ $category >](&self) -> bool {
108                        matches!(
109                            self.$category,
110                            Some([< $category:camel Data >]{
111                                [< is_ $sub >]: Some(true),
112                                ..
113                            })
114                        ) }
115
116                    #[doc = concat!("Checks if the word is definitely a ", stringify!($category), " and more specifically is labeled as __not__ (a) ", stringify!($sub), ".")]
117                    pub fn [< is_non_ $sub _ $category >](&self) -> bool {
118                        matches!(
119                            self.$category,
120                            Some([< $category:camel Data >]{
121                                [< is_ $sub >]: None | Some(false),
122                                ..
123                            })
124                        )
125                    }
126                )*
127            )*
128        }
129    };
130}
131
132impl DictWordMetadata {
133    /// If there is only one possible interpretation of the metadata, infer its UPOS tag.
134    pub fn infer_pos_tag(&self) -> Option<UPOS> {
135        // If an explicit POS tag exists, return it immediately.
136        if let Some(pos) = self.pos_tag {
137            return Some(pos);
138        }
139
140        // Collect all possible POS tags from metadata
141        let mut candidates = SmallVec::<[UPOS; 14]>::with_capacity(14);
142
143        if self.is_proper_noun() {
144            candidates.push(UPOS::PROPN);
145        }
146
147        if self.is_pronoun() {
148            candidates.push(UPOS::PRON);
149        }
150        if self.is_noun() {
151            candidates.push(UPOS::NOUN);
152        }
153        if self.is_verb() {
154            // Distinguish auxiliary verbs
155            if let Some(data) = &self.verb {
156                if data.is_auxiliary == Some(true) {
157                    candidates.push(UPOS::AUX);
158                } else {
159                    candidates.push(UPOS::VERB);
160                }
161            } else {
162                candidates.push(UPOS::VERB);
163            }
164        }
165        if self.is_adjective() {
166            candidates.push(UPOS::ADJ);
167        }
168        if self.is_adverb() {
169            candidates.push(UPOS::ADV);
170        }
171        if self.is_conjunction() {
172            candidates.push(UPOS::CCONJ);
173        }
174        if self.is_determiner() {
175            candidates.push(UPOS::DET);
176        }
177        if self.preposition {
178            candidates.push(UPOS::ADP);
179        }
180
181        // Remove duplicates
182        candidates.sort();
183        candidates.dedup();
184
185        candidates.into_iter().exactly_one().ok()
186    }
187
188    /// Produce a copy of `self` with the known properties of `other` set.
189    pub fn or(&self, other: &Self) -> Self {
190        let mut clone = self.clone();
191        clone.merge(other);
192        clone
193    }
194
195    /// Given a UPOS tag, discard any metadata that would disagree with the given POS tag.
196    /// For example, if the metadata suggests a word could either be a noun or an adjective, and we
197    /// provide a [`UPOS::NOUN`], this function will remove the adjective data.
198    ///
199    /// Additionally, if the metadata does not currently declare the potential of the word to be
200    /// the specific POS, it becomes so. That means if we provide a [`UPOS::ADJ`] to the function
201    /// for a metadata whose `Self::adjective = None`, it will become `Some`.
202    pub fn enforce_pos_exclusivity(&mut self, pos: &UPOS) {
203        use UPOS::*;
204        match pos {
205            NOUN => {
206                if let Some(noun) = self.noun {
207                    self.noun = Some(NounData {
208                        is_proper: Some(false),
209                        ..noun
210                    })
211                } else {
212                    self.noun = Some(NounData {
213                        is_proper: Some(false),
214                        is_singular: None,
215                        is_plural: None,
216                        is_countable: None,
217                        is_mass: None,
218                        is_possessive: None,
219                    })
220                }
221
222                self.pronoun = None;
223                self.verb = None;
224                self.adjective = None;
225                self.adverb = None;
226                self.conjunction = None;
227                self.determiner = None;
228                self.affix = None;
229                self.preposition = false;
230            }
231            PROPN => {
232                if let Some(noun) = self.noun {
233                    self.noun = Some(NounData {
234                        is_proper: Some(true),
235                        ..noun
236                    })
237                } else {
238                    self.noun = Some(NounData {
239                        is_proper: Some(true),
240                        is_singular: None,
241                        is_plural: None,
242                        is_countable: None,
243                        is_mass: None,
244                        is_possessive: None,
245                    })
246                }
247
248                self.pronoun = None;
249                self.verb = None;
250                self.adjective = None;
251                self.adverb = None;
252                self.conjunction = None;
253                self.determiner = None;
254                self.affix = None;
255                self.preposition = false;
256            }
257            PRON => {
258                if self.pronoun.is_none() {
259                    self.pronoun = Some(PronounData::default())
260                }
261
262                self.noun = None;
263                self.verb = None;
264                self.adjective = None;
265                self.adverb = None;
266                self.conjunction = None;
267                self.determiner = None;
268                self.affix = None;
269                self.preposition = false;
270            }
271            VERB => {
272                if let Some(verb) = self.verb {
273                    self.verb = Some(VerbData {
274                        is_auxiliary: Some(false),
275                        ..verb
276                    })
277                } else {
278                    self.verb = Some(VerbData {
279                        is_auxiliary: Some(false),
280                        ..Default::default()
281                    })
282                }
283
284                self.noun = None;
285                self.pronoun = None;
286                self.adjective = None;
287                self.adverb = None;
288                self.conjunction = None;
289                self.determiner = None;
290                self.affix = None;
291                self.preposition = false;
292            }
293            AUX => {
294                if let Some(verb) = self.verb {
295                    self.verb = Some(VerbData {
296                        is_auxiliary: Some(true),
297                        ..verb
298                    })
299                } else {
300                    self.verb = Some(VerbData {
301                        is_auxiliary: Some(true),
302                        ..Default::default()
303                    })
304                }
305
306                self.noun = None;
307                self.pronoun = None;
308                self.adjective = None;
309                self.adverb = None;
310                self.conjunction = None;
311                self.determiner = None;
312                self.affix = None;
313                self.preposition = false;
314            }
315            ADJ => {
316                if self.adjective.is_none() {
317                    self.adjective = Some(AdjectiveData::default())
318                }
319
320                self.noun = None;
321                self.pronoun = None;
322                self.verb = None;
323                self.adverb = None;
324                self.conjunction = None;
325                self.determiner = None;
326                self.affix = None;
327                self.preposition = false;
328            }
329            ADV => {
330                if self.adverb.is_none() {
331                    self.adverb = Some(AdverbData::default())
332                }
333
334                self.noun = None;
335                self.pronoun = None;
336                self.verb = None;
337                self.adjective = None;
338                self.conjunction = None;
339                self.determiner = None;
340                self.affix = None;
341                self.preposition = false;
342            }
343            ADP => {
344                self.noun = None;
345                self.pronoun = None;
346                self.verb = None;
347                self.adjective = None;
348                self.adverb = None;
349                self.conjunction = None;
350                self.determiner = None;
351                self.affix = None;
352                self.preposition = true;
353            }
354            DET => {
355                self.noun = None;
356                self.pronoun = None;
357                self.verb = None;
358                self.adjective = None;
359                self.adverb = None;
360                self.conjunction = None;
361                self.affix = None;
362                self.preposition = false;
363                self.determiner = Some(DeterminerData::default());
364            }
365            CCONJ | SCONJ => {
366                if self.conjunction.is_none() {
367                    self.conjunction = Some(ConjunctionData::default())
368                }
369
370                self.noun = None;
371                self.pronoun = None;
372                self.verb = None;
373                self.adjective = None;
374                self.adverb = None;
375                self.determiner = None;
376                self.affix = None;
377                self.preposition = false;
378            }
379            _ => {}
380        }
381    }
382
383    // Generates boolean query methods for word metadata fields.
384    //
385    // Note: `singular` and `countable` default to true and are skipped here.
386    generate_metadata_queries!(
387        // Singular and countable default to true, so their metadata queries are not generated.
388        noun has proper, plural, mass, possessive.
389        pronoun has personal, singular, plural, possessive, reflexive, subject, object.
390        determiner has demonstrative, possessive, quantifier.
391        verb has linking, auxiliary.
392        conjunction has.
393        adjective has.
394        adverb has manner, frequency, degree
395    );
396
397    // Manual metadata queries
398
399    // Pronoun metadata queries
400
401    /// Returns the grammatical person for personal pronouns.
402    pub fn get_person(&self) -> Option<Person> {
403        self.pronoun.as_ref().and_then(|p| p.person)
404    }
405
406    /// Checks if the pronoun is both first-person and plural (we/us).
407    pub fn is_first_person_plural_pronoun(&self) -> bool {
408        matches!(
409            self.pronoun,
410            Some(PronounData {
411                person: Some(Person::First),
412                is_plural: Some(true),
413                ..
414            })
415        )
416    }
417
418    /// Checks if the pronoun is both first-person and singular (I/me).
419    pub fn is_first_person_singular_pronoun(&self) -> bool {
420        matches!(
421            self.pronoun,
422            Some(PronounData {
423                person: Some(Person::First),
424                is_singular: Some(true),
425                ..
426            })
427        )
428    }
429
430    /// Checks if the pronoun is both third-person and plural (they/them).
431    pub fn is_third_person_plural_pronoun(&self) -> bool {
432        matches!(
433            self.pronoun,
434            Some(PronounData {
435                person: Some(Person::Third),
436                is_plural: Some(true),
437                ..
438            })
439        )
440    }
441
442    /// Checks if the pronoun is both third-person and singular (he/him/she/her/it).
443    pub fn is_third_person_singular_pronoun(&self) -> bool {
444        matches!(
445            self.pronoun,
446            Some(PronounData {
447                person: Some(Person::Third),
448                is_singular: Some(true),
449                ..
450            })
451        )
452    }
453
454    /// Checks if the pronoun is third-person (he/him/she/her/it/they/them).
455    pub fn is_third_person_pronoun(&self) -> bool {
456        matches!(
457            self.pronoun,
458            Some(PronounData {
459                person: Some(Person::Third),
460                ..
461            })
462        )
463    }
464
465    /// Checks if the pronoun is second-person (you).
466    pub fn is_second_person_pronoun(&self) -> bool {
467        matches!(
468            self.pronoun,
469            Some(PronounData {
470                person: Some(Person::Second),
471                ..
472            })
473        )
474    }
475
476    /// Lemma is default if no verb form is specified in the dictionary
477    pub fn is_verb_lemma(&self) -> bool {
478        if let Some(verb) = self.verb {
479            if let Some(forms) = verb.verb_forms {
480                return forms.is_empty() || forms.contains(VerbFormFlags::LEMMA);
481            } else {
482                return true;
483            }
484        }
485        false
486    }
487
488    /// WARNING! This is intended for regular verb past form used for both preterite and past participle.
489    /// WARNING! But for now it's a bit fuzzy and may be missing from some regular verbs and may be
490    /// WARNING! misapplied to some irregular verbs that happen to have the same form for preterite and
491    /// WARNING! past participle such as "bought", "caught",
492    pub fn is_verb_past_form(&self) -> bool {
493        self.verb.is_some_and(|v| {
494            v.verb_forms
495                .is_some_and(|vf| vf.contains(VerbFormFlags::PAST))
496        })
497    }
498
499    /// WARNING! This method was formerly named `is_verb_regular_past_form` and seems to have been an
500    /// WARNING! attempt to make a new method that achieved what `is_verb_past_form` above was intended
501    /// WARNING! to to achieve. But it fell into the trap mentioned above of assuming that only regular
502    /// WARNING! verbs had the same form for preterite and past participle, overlooking "bought", "caught", etc.
503    pub fn is_verb_preterite_and_participle_form(&self) -> bool {
504        self.verb.is_some_and(|v| {
505            v.verb_forms.is_some_and(|vf| {
506                vf.contains(VerbFormFlags::PRETERITE) && vf.contains(VerbFormFlags::PAST_PARTICIPLE)
507            })
508        })
509    }
510
511    /// Checks if the verb is a simple past form, technically known as the "preterite" form.
512    /// WARNING! This should be working for irregular verbs but may not be working for regular verbs.
513    /// WARNING! e.g., it is more likely to match `ate` than `walked` for now.
514    pub fn is_verb_simple_past_form(&self) -> bool {
515        self.verb.is_some_and(|v| {
516            v.verb_forms
517                .is_some_and(|vf| vf.contains(VerbFormFlags::PRETERITE))
518        })
519    }
520
521    /// Checks if the verb is a past participle form.
522    /// WARNING! This should be working for irregular verbs but may not be working for regular verbs.
523    /// WARNING! e.g., it is more likely to match `eaten` than `walked` for now.
524    pub fn is_verb_past_participle_form(&self) -> bool {
525        self.verb.is_some_and(|v| {
526            v.verb_forms
527                .is_some_and(|vf| vf.contains(VerbFormFlags::PAST_PARTICIPLE))
528        })
529    }
530
531    /// Checks if the verb is only the simple past aka preterite form and not also marked as
532    /// `past` or `past participle`.
533    pub fn is_verb_simple_past_only(&self) -> bool {
534        self.verb.is_some_and(|v| {
535            v.verb_forms.is_some_and(|vf| {
536                vf.contains(VerbFormFlags::PRETERITE)
537                    && !vf.intersects(VerbFormFlags::PAST | VerbFormFlags::PAST_PARTICIPLE)
538            })
539        })
540    }
541
542    /// Checks if the verb is only the past participle form and not also marked as
543    /// `past` or `preterite` (aka simple past).
544    pub fn is_verb_past_participle_only(&self) -> bool {
545        self.verb.is_some_and(|v| {
546            v.verb_forms.is_some_and(|vf| {
547                vf.contains(VerbFormFlags::PAST_PARTICIPLE)
548                    && !vf.intersects(VerbFormFlags::PAST | VerbFormFlags::PRETERITE)
549            })
550        })
551    }
552
553    /// Checks if the verb is a progressive/continuous form (e.g., ends in "-ing").
554    pub fn is_verb_progressive_form(&self) -> bool {
555        self.verb.is_some_and(|v| {
556            v.verb_forms
557                .is_some_and(|vf| vf.contains(VerbFormFlags::PROGRESSIVE))
558        })
559    }
560
561    /// Checks if the verb is a third-person singular present form (e.g., "walks").
562    pub fn is_verb_third_person_singular_present_form(&self) -> bool {
563        self.verb.is_some_and(|v| {
564            v.verb_forms
565                .is_some_and(|vf| vf.contains(VerbFormFlags::THIRD_PERSON_SINGULAR))
566        })
567    }
568
569    // Noun metadata queries
570
571    /// Matches a noun either specifically marked as singular or not marked for number
572    /// since singular is the default. Note that this will also match nouns which are
573    /// both singular and plural such as "aircraft" or "sheep".
574    pub fn is_singular_noun(&self) -> bool {
575        if let Some(noun) = self.noun {
576            matches!(
577                (noun.is_singular, noun.is_plural),
578                (Some(true), _) | (None | Some(false), None | Some(false))
579            )
580        } else {
581            false
582        }
583    }
584
585    /// Matches a noun that is neither specifically marked as singular nor unmarked for
586    /// number since singular is the default. This will not match nouns which are both plural
587    /// and singular such as "aircraft" or "sheep".
588    pub fn is_non_singular_noun(&self) -> bool {
589        if let Some(noun) = self.noun {
590            !matches!(
591                (noun.is_singular, noun.is_plural),
592                (Some(true), _) | (None | Some(false), None | Some(false))
593            )
594        } else {
595            false
596        }
597    }
598
599    /// Matches a noun either specifically marked as a count noun or not marked for countability
600    /// since count is the default. This will match nouns which are both count and mass nouns
601    /// such as "beer" and "property".
602    pub fn is_countable_noun(&self) -> bool {
603        if let Some(noun) = self.noun {
604            matches!(
605                (noun.is_countable, noun.is_mass),
606                (Some(true), _) | (None | Some(false), None | Some(false))
607            )
608        } else {
609            false
610        }
611    }
612
613    /// Matches a noun that is neither specifically marked as a count noun nor unmarked for
614    /// countability since countable is the default. This will not match nouns which are both count
615    /// nouns and mass nouns such as "beer" or "property".
616    pub fn is_non_countable_noun(&self) -> bool {
617        if let Some(noun) = self.noun {
618            !matches!(
619                (noun.is_countable, noun.is_mass),
620                (Some(true), _) | (None | Some(false), None | Some(false))
621            )
622        } else {
623            false
624        }
625    }
626
627    /// Matches a noun either specifically marked as singular or not marked for number
628    /// since singular is the default, but only if it is not also marked as plural.
629    /// This will match nouns which are singular only like "balloon" or "cow"
630    /// but not both singular and plural such as "aircraft" or "sheep".
631    pub fn is_singular_noun_only(&self) -> bool {
632        if let Some(noun) = self.noun {
633            matches!(
634                (noun.is_singular, noun.is_plural),
635                (Some(true), None | Some(false))
636            )
637        } else {
638            false
639        }
640    }
641
642    /// Matches a noun specifically marked as plural, but only if it is not also
643    /// marked as singular.
644    /// This will match nouns which are plural only like "cars" or "men"
645    /// but not both singular and plural such as "aircraft" or "sheep".
646    pub fn is_plural_noun_only(&self) -> bool {
647        if let Some(noun) = self.noun {
648            matches!(
649                (noun.is_singular, noun.is_plural),
650                (None | Some(false), Some(true))
651            )
652        } else {
653            false
654        }
655    }
656
657    /// Most mass nouns also have countable senses. Match those that are only mass nouns.
658    /// This will match nouns like "information" and "furniture" but not ones like "beer" or "property".
659    pub fn is_mass_noun_only(&self) -> bool {
660        if let Some(noun) = self.noun {
661            matches!(
662                (noun.is_countable, noun.is_mass),
663                (None | Some(false), Some(true))
664            )
665        } else {
666            false
667        }
668    }
669
670    // Nominal metadata queries (noun + pronoun)
671
672    /// Checks if the word is definitely nominal.
673    pub fn is_nominal(&self) -> bool {
674        self.is_noun() || self.is_pronoun()
675    }
676
677    /// Checks if the word is definitely a nominal and more specifically is labeled as (a) singular.
678    pub fn is_singular_nominal(&self) -> bool {
679        self.is_singular_noun() || self.is_singular_pronoun()
680    }
681
682    /// Checks if the word is definitely a nominal and more specifically is labeled as (a) plural.
683    pub fn is_plural_nominal(&self) -> bool {
684        self.is_plural_noun() || self.is_plural_pronoun()
685    }
686
687    /// Checks if the word is definitely a nominal and more specifically is labeled as (a) possessive.
688    /// NOTE: `possessive pronoun`s are not qualifiers, but words like `mine`, `yours`, etc.
689    /// The terminology of `possessive noun`, `possessive pronoun` and `possessive determiner` only
690    /// tends to reinforce this confusion.
691    pub fn is_possessive_nominal(&self) -> bool {
692        self.is_possessive_noun() || self.is_possessive_determiner()
693    }
694
695    /// Checks if the word is definitely a nominal and more specifically is labeled as __not__ (a) singular.
696    pub fn is_non_singular_nominal(&self) -> bool {
697        self.is_non_singular_noun() || self.is_non_singular_pronoun()
698    }
699
700    /// Checks if the word is definitely a nominal and more specifically is labeled as __not__ (a) plural.
701    pub fn is_non_plural_nominal(&self) -> bool {
702        self.is_non_plural_noun() || self.is_non_plural_pronoun()
703    }
704
705    // Adjective metadata queries
706
707    pub fn get_degree(&self) -> Option<Degree> {
708        self.adjective.as_ref().and_then(|a| a.degree)
709    }
710
711    pub fn is_comparative_adjective(&self) -> bool {
712        matches!(
713            self.adjective,
714            Some(AdjectiveData {
715                degree: Some(Degree::Comparative)
716            })
717        )
718    }
719
720    pub fn is_superlative_adjective(&self) -> bool {
721        matches!(
722            self.adjective,
723            Some(AdjectiveData {
724                degree: Some(Degree::Superlative)
725            })
726        )
727    }
728
729    // Degree::Positive is the default if degree is not marked in the dictionary.
730    pub fn is_positive_adjective(&self) -> bool {
731        match self.adjective {
732            Some(AdjectiveData {
733                degree: Some(Degree::Positive),
734            }) => true,
735            Some(AdjectiveData { degree: None }) => true,
736            Some(AdjectiveData {
737                degree: Some(degree),
738            }) => !matches!(degree, Degree::Comparative | Degree::Superlative),
739            _ => false,
740        }
741    }
742
743    // Determiner metadata queries
744
745    // Checks if the word is definitely a determiner and more specifically is labeled as (a) quantifier.
746    pub fn is_quantifier(&self) -> bool {
747        self.is_quantifier_determiner()
748    }
749
750    // Non-POS queries
751
752    /// Checks whether a word is _definitely_ a swear.
753    pub fn is_swear(&self) -> bool {
754        matches!(self.swear, Some(true))
755    }
756
757    /// Abbreviation is orthogonal to POS
758    pub fn is_abbreviation(&self) -> bool {
759        matches!(self.abbreviation, Some(true))
760    }
761
762    // Orthographic queries
763
764    /// Does the metadata for this word cover an all-lowercase variant? (e.g., "hello")
765    ///
766    /// This returns true if all letters in the word are lowercase. Words containing
767    /// non-letter characters (like numbers or symbols) are only considered if all
768    /// letter characters are lowercase.
769    pub fn is_lowercase(&self) -> bool {
770        self.orth_info.contains(OrthFlags::LOWERCASE)
771    }
772    /// Does the metadata for this word cover a titlecase variant? (e.g., "Hello")
773    ///
774    /// This returns true if the word is in titlecase form, which means:
775    /// - The first letter is uppercase
776    /// - All other letters are lowercase
777    /// - The word is at least 2 characters long
778    ///
779    /// Examples: "Hello", "World"
780    ///
781    /// Note: Words with internal capital letters (like "McDonald") or apostrophes (like "O'Reilly")
782    /// are not considered titlecase - they are classified as UPPER_CAMEL instead.
783    pub fn is_titlecase(&self) -> bool {
784        self.orth_info.contains(OrthFlags::TITLECASE)
785    }
786    /// Does the metadata for this word cover an all-uppercase variant? (e.g., "HELLO")
787    ///
788    /// This returns true if all letters in the word are uppercase. Words containing
789    /// non-letter characters (like numbers or symbols) are only considered if all
790    /// letter characters are uppercase.
791    ///
792    /// Examples: "HELLO", "NASA", "I"
793    pub fn is_allcaps(&self) -> bool {
794        self.orth_info.contains(OrthFlags::ALLCAPS)
795    }
796    /// Does the metadata for this word cover a lower camel case variant? (e.g., "helloWorld")
797    ///
798    /// This returns true if the word is in lower camel case, which means:
799    /// - The first letter is lowercase
800    /// - There is at least one uppercase letter after the first character
801    /// - The word must be at least 2 characters long
802    ///
803    /// Examples: "helloWorld", "getHTTPResponse", "eBay"
804    ///
805    /// Note: Single words that are all lowercase will return false.
806    /// Words starting with an uppercase letter will return false (those would be UpperCamel).
807    pub fn is_lower_camel(&self) -> bool {
808        self.orth_info.contains(OrthFlags::LOWER_CAMEL)
809    }
810    /// Does the metadata for this word cover an upper camel case / pascal case variant? (e.g., "HelloWorld")
811    ///
812    /// This returns true if the word is in upper camel case (also known as Pascal case), which means:
813    /// - The first letter is uppercase
814    /// - There is at least one other uppercase letter after the first character
815    /// - There is at least one lowercase letter after the first uppercase letter
816    /// - The word must be at least 3 characters long
817    ///
818    /// Examples:
819    /// - "HelloWorld" (standard Pascal case)
820    /// - "McDonald" (name with internal caps)
821    /// - "O'Reilly" (name with apostrophe and internal caps)
822    /// - "HttpRequest" (initialism followed by word)
823    ///
824    /// Note: Single words that are titlecase (like "Hello") will return false.
825    /// Words that are all uppercase (like "NASA") will also return false.
826    pub fn is_upper_camel(&self) -> bool {
827        self.orth_info.contains(OrthFlags::UPPER_CAMEL)
828    }
829
830    /// Does the metadata for this word cover an apostrophized variant? (e.g., "doesn't")
831    pub fn is_apostrophized(&self) -> bool {
832        self.orth_info.contains(OrthFlags::APOSTROPHE)
833    }
834
835    pub fn is_roman_numerals(&self) -> bool {
836        self.orth_info.contains(OrthFlags::ROMAN_NUMERALS)
837    }
838
839    /// Same thing as [`Self::or`], except in-place rather than a clone.
840    pub fn merge(&mut self, other: &Self) -> &mut Self {
841        macro_rules! merge {
842            ($a:expr, $b:expr) => {
843                match ($a, $b) {
844                    (Some(a), Some(b)) => Some(a.or(&b)),
845                    (Some(a), None) => Some(a),
846                    (None, Some(b)) => Some(b),
847                    (None, None) => None,
848                }
849            };
850        }
851
852        self.noun = merge!(self.noun, other.noun);
853        self.pronoun = merge!(self.pronoun, other.pronoun);
854        self.verb = merge!(self.verb, other.verb);
855        self.adjective = merge!(self.adjective, other.adjective);
856        self.adverb = merge!(self.adverb, other.adverb);
857        self.conjunction = merge!(self.conjunction, other.conjunction);
858        self.determiner = merge!(self.determiner, other.determiner);
859        self.affix = merge!(self.affix, other.affix);
860        self.preposition |= other.preposition;
861        self.dialects |= other.dialects;
862        self.orth_info |= other.orth_info;
863        self.swear = self.swear.or(other.swear);
864        self.abbreviation = self.abbreviation.or(other.abbreviation);
865        self.common |= other.common;
866        self.derived_from = self.derived_from.or(other.derived_from);
867        self.pos_tag = self.pos_tag.or(other.pos_tag);
868        self.np_member = self.np_member.or(other.np_member);
869
870        self
871    }
872}
873
874// These verb forms are morphological variations, distinct from TAM (Tense-Aspect-Mood)
875// Each form can be used in various TAM combinations:
876// - Lemma form (infinitive, citation form, dictionary form)
877//   Used in infinitives (e.g., "to sleep"), imperatives (e.g., "sleep!"), and with modals (e.g., "will sleep")
878// - Past form (past participle and simple past)
879//   Used as verbs (e.g., "slept") or adjectives (e.g., "closed door")
880// - Progressive form (present participle and gerund)
881//   Used as verbs (e.g., "sleeping"), nouns (e.g., "sleeping is important"), or adjectives (e.g., "sleeping dog")
882// - Third person singular present (-s/-es)
883//   Used for third person singular subjects (e.g., "he sleeps", "she reads")
884//
885// Important notes:
886// 1. English expresses time through auxiliary verbs, not verb form alone
887// 2. Irregular verbs can have different forms for past participle and simple past
888// 3. Future is always expressed through auxiliary verbs (e.g., "will sleep", "going to sleep")
889#[repr(u32)]
890pub enum VerbForm {
891    /// The uninflected verb form: "walk", "eat"
892    LemmaForm = 1 << 0,
893    /// The past form for regular verbs: "walked"
894    PastForm = 1 << 1,
895    /// The simple past/preterite form for irregular verbs: "ate"
896    SimplePastForm = 1 << 2,
897    /// The past participle form for irregular verbs: "eaten"
898    PastParticipleForm = 1 << 3,
899    /// The progressive/continuous/gerund/present participle form: "walking", "eating"
900    ProgressiveForm = 1 << 4,
901    /// The third person singular present form: "walks", "eats"
902    ThirdPersonSingularPresentForm = 1 << 5,
903}
904
905/// The underlying type used for verb form flags.
906pub type VerbFormFlagsUnderlyingType = u32;
907
908bitflags::bitflags! {
909    /// A collection of bit flags used to represent verb forms.
910    ///
911    /// This allows a word to be tagged with multiple verb forms when applicable.
912    #[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, PartialOrd, Eq, Hash, Default)]
913    #[serde(transparent)]
914    pub struct VerbFormFlags: VerbFormFlagsUnderlyingType {
915        const LEMMA = VerbForm::LemmaForm as VerbFormFlagsUnderlyingType;
916        const PAST = VerbForm::PastForm as VerbFormFlagsUnderlyingType;
917        const PRETERITE = VerbForm::SimplePastForm as VerbFormFlagsUnderlyingType;
918        const PAST_PARTICIPLE = VerbForm::PastParticipleForm as VerbFormFlagsUnderlyingType;
919        const PROGRESSIVE = VerbForm::ProgressiveForm as VerbFormFlagsUnderlyingType;
920        const THIRD_PERSON_SINGULAR = VerbForm::ThirdPersonSingularPresentForm as VerbFormFlagsUnderlyingType;
921    }
922}
923
924#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, PartialOrd, Eq, Hash, Default)]
925pub struct VerbData {
926    pub is_linking: Option<bool>,
927    pub is_auxiliary: Option<bool>,
928    #[serde(rename = "verb_form", default)]
929    pub verb_forms: Option<VerbFormFlags>,
930}
931
932impl VerbData {
933    /// Produce a copy of `self` with the known properties of `other` set.
934    pub fn or(&self, other: &Self) -> Self {
935        let verb_forms = match (self.verb_forms, other.verb_forms) {
936            (Some(self_verb_forms), Some(other_verb_forms)) => {
937                Some(self_verb_forms | other_verb_forms)
938            }
939            (Some(self_verb_forms), None) => Some(self_verb_forms),
940            (None, Some(other_verb_forms)) => Some(other_verb_forms),
941            (None, None) => None,
942        };
943
944        Self {
945            is_linking: self.is_linking.or(other.is_linking),
946            is_auxiliary: self.is_auxiliary.or(other.is_auxiliary),
947            verb_forms,
948        }
949    }
950}
951
952// nouns can be both singular and plural: "aircraft", "biceps", "fish", "sheep"
953// TODO other noun properties may be worth adding: abstract
954#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, PartialOrd, Eq, Hash, Default)]
955pub struct NounData {
956    pub is_proper: Option<bool>,
957    pub is_singular: Option<bool>,
958    pub is_plural: Option<bool>,
959    pub is_countable: Option<bool>,
960    pub is_mass: Option<bool>,
961    pub is_possessive: Option<bool>,
962}
963
964impl NounData {
965    /// Produce a copy of `self` with the known properties of `other` set.
966    pub fn or(&self, other: &Self) -> Self {
967        Self {
968            is_proper: self.is_proper.or(other.is_proper),
969            is_singular: self.is_singular.or(other.is_singular),
970            is_plural: self.is_plural.or(other.is_plural),
971            is_countable: self.is_countable.or(other.is_countable),
972            is_mass: self.is_mass.or(other.is_mass),
973            is_possessive: self.is_possessive.or(other.is_possessive),
974        }
975    }
976}
977
978// Person is a property of pronouns; the verb 'be', plus all verbs reflect 3rd person singular with -s
979#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, PartialOrd, Is, Hash)]
980pub enum Person {
981    First,
982    Second,
983    Third,
984}
985
986// TODO for now focused on personal pronouns?
987#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, PartialOrd, Eq, Hash, Default)]
988pub struct PronounData {
989    pub is_personal: Option<bool>,
990    pub is_singular: Option<bool>,
991    pub is_plural: Option<bool>,
992    pub is_possessive: Option<bool>,
993    pub is_reflexive: Option<bool>,
994    pub person: Option<Person>,
995    pub is_subject: Option<bool>,
996    pub is_object: Option<bool>,
997}
998
999impl PronounData {
1000    /// Produce a copy of `self` with the known properties of `other` set.
1001    pub fn or(&self, other: &Self) -> Self {
1002        Self {
1003            is_personal: self.is_personal.or(other.is_personal),
1004            is_singular: self.is_singular.or(other.is_singular),
1005            is_plural: self.is_plural.or(other.is_plural),
1006            is_possessive: self.is_possessive.or(other.is_possessive),
1007            is_reflexive: self.is_reflexive.or(other.is_reflexive),
1008            person: self.person.or(other.person),
1009            is_subject: self.is_subject.or(other.is_subject),
1010            is_object: self.is_object.or(other.is_object),
1011        }
1012    }
1013}
1014
1015/// Additional metadata for determiners
1016#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, PartialOrd, Eq, Hash, Default)]
1017pub struct DeterminerData {
1018    pub is_demonstrative: Option<bool>,
1019    pub is_possessive: Option<bool>,
1020    pub is_quantifier: Option<bool>,
1021}
1022
1023impl DeterminerData {
1024    /// Produce a copy of `self` with the known properties of `other` set.
1025    pub fn or(&self, other: &Self) -> Self {
1026        Self {
1027            is_demonstrative: self.is_demonstrative.or(other.is_demonstrative),
1028            is_possessive: self.is_possessive.or(other.is_possessive),
1029            is_quantifier: self.is_quantifier.or(other.is_quantifier),
1030        }
1031    }
1032}
1033
1034/// Degree is a property of adjectives: positive is not inflected
1035/// Comparative is inflected with -er or comes after the word "more"
1036/// Superlative is inflected with -est or comes after the word "most"
1037#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, PartialOrd, Is, Hash)]
1038pub enum Degree {
1039    Positive,
1040    Comparative,
1041    Superlative,
1042}
1043
1044/// Some adjectives are not comparable so don't have -er or -est forms and can't be used with "more" or "most".
1045/// Some adjectives can only be used "attributively" (before a noun); some only predicatively (after "is" etc.).
1046/// In old grammars words like the articles and determiners are classified as adjectives but behave differently.
1047#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, PartialOrd, Eq, Hash, Default)]
1048pub struct AdjectiveData {
1049    pub degree: Option<Degree>,
1050}
1051
1052impl AdjectiveData {
1053    /// Produce a copy of `self` with the known properties of `other` set.
1054    pub fn or(&self, other: &Self) -> Self {
1055        Self {
1056            degree: self.degree.or(other.degree),
1057        }
1058    }
1059}
1060
1061/// Adverb can be a "junk drawer" category for words which don't fit the other major categories.
1062/// The typical adverbs are "adverbs of manner", those derived from adjectives in -ly
1063/// other adverbs (time, place, etc) should probably not be considered adverbs for Harper's purposes
1064#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, PartialOrd, Eq, Hash, Default)]
1065pub struct AdverbData {
1066    pub is_manner: Option<bool>,
1067    pub is_frequency: Option<bool>,
1068    pub is_degree: Option<bool>,
1069}
1070
1071impl AdverbData {
1072    /// Produce a copy of `self` with the known properties of `other` set.
1073    pub fn or(&self, _other: &Self) -> Self {
1074        Self {
1075            is_manner: self.is_manner.or(_other.is_manner),
1076            is_frequency: self.is_frequency.or(_other.is_frequency),
1077            is_degree: self.is_degree.or(_other.is_degree),
1078        }
1079    }
1080}
1081
1082#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, PartialOrd, Eq, Hash, Default)]
1083pub struct ConjunctionData {}
1084
1085impl ConjunctionData {
1086    /// Produce a copy of `self` with the known properties of `other` set.
1087    pub fn or(&self, _other: &Self) -> Self {
1088        Self {}
1089    }
1090}
1091
1092#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, PartialOrd, Eq, Hash, Default)]
1093pub struct AffixData {
1094    pub is_prefix: Option<bool>,
1095    pub is_suffix: Option<bool>,
1096}
1097
1098impl AffixData {
1099    /// Produce a copy of `self` with the known properties of `other` set.
1100    pub fn or(&self, _other: &Self) -> Self {
1101        Self {
1102            is_prefix: self.is_prefix.or(_other.is_prefix),
1103            is_suffix: self.is_suffix.or(_other.is_suffix),
1104        }
1105    }
1106}
1107
1108/// A regional dialect.
1109///
1110/// Note: these have bit-shifted values so that they can ergonomically integrate with
1111/// `DialectFlags`. Each value here must have a unique bit index inside
1112/// `DialectsUnderlyingType`.
1113#[derive(
1114    Debug,
1115    Clone,
1116    Copy,
1117    Serialize,
1118    Deserialize,
1119    PartialEq,
1120    PartialOrd,
1121    Eq,
1122    Hash,
1123    EnumCount,
1124    EnumString,
1125    EnumIter,
1126    Display,
1127    VariantArray,
1128)]
1129pub enum Dialect {
1130    American = 1 << 0,
1131    Canadian = 1 << 1,
1132    Australian = 1 << 2,
1133    British = 1 << 3,
1134    Indian = 1 << 4,
1135}
1136impl Dialect {
1137    /// Tries to guess the dialect used in the document by finding which dialect is used the most.
1138    /// Returns `None` if it fails to find a single dialect that is used the most.
1139    #[must_use]
1140    pub fn try_guess_from_document(document: &Document) -> Option<Self> {
1141        Self::try_from(DialectFlags::get_most_used_dialects_from_document(document)).ok()
1142    }
1143
1144    /// Tries to get a dialect from its abbreviation. Returns `None` if the abbreviation is not
1145    /// recognized.
1146    ///
1147    /// # Examples
1148    ///
1149    /// ```
1150    /// use harper_core::Dialect;
1151    ///
1152    /// let abbrs = ["US", "CA", "AU", "GB", "IN"];
1153    /// let mut dialects = abbrs.iter().map(|abbr| Dialect::try_from_abbr(abbr));
1154    ///
1155    /// assert_eq!(Some(Dialect::American), dialects.next().unwrap()); // US
1156    /// assert_eq!(Some(Dialect::Canadian), dialects.next().unwrap()); // CA
1157    /// assert_eq!(Some(Dialect::Australian), dialects.next().unwrap()); // AU
1158    /// assert_eq!(Some(Dialect::British), dialects.next().unwrap()); // GB
1159    /// assert_eq!(Some(Dialect::Indian), dialects.next().unwrap()); // IN
1160    /// ```
1161    #[must_use]
1162    pub fn try_from_abbr(abbr: &str) -> Option<Self> {
1163        match abbr {
1164            "US" => Some(Self::American),
1165            "CA" => Some(Self::Canadian),
1166            "AU" => Some(Self::Australian),
1167            "GB" => Some(Self::British),
1168            "IN" => Some(Self::Indian),
1169            _ => None,
1170        }
1171    }
1172    // BCP-47 https://www.rfc-editor.org/rfc/rfc5646
1173    pub fn try_from_bcp47(bcp47: &str) -> Option<Self> {
1174        bcp47.strip_prefix("en-").and_then(Self::try_from_abbr)
1175    }
1176}
1177impl TryFrom<DialectFlags> for Dialect {
1178    type Error = ();
1179
1180    /// Attempts to convert `DialectFlags` to a single `Dialect`.
1181    ///
1182    /// # Errors
1183    ///
1184    /// Will return `Err` if more than one dialect is enabled or if an undefined dialect is
1185    /// enabled.
1186    fn try_from(dialect_flags: DialectFlags) -> Result<Self, Self::Error> {
1187        // Ensure only one dialect is enabled before converting.
1188        if dialect_flags.bits().count_ones() == 1 {
1189            match dialect_flags {
1190                df if df.is_dialect_enabled_strict(Dialect::American) => Ok(Dialect::American),
1191                df if df.is_dialect_enabled_strict(Dialect::Canadian) => Ok(Dialect::Canadian),
1192                df if df.is_dialect_enabled_strict(Dialect::Australian) => Ok(Dialect::Australian),
1193                df if df.is_dialect_enabled_strict(Dialect::British) => Ok(Dialect::British),
1194                df if df.is_dialect_enabled_strict(Dialect::Indian) => Ok(Dialect::Indian),
1195                _ => Err(()),
1196            }
1197        } else {
1198            // More than one dialect enabled; can't soundly convert.
1199            Err(())
1200        }
1201    }
1202}
1203
1204// The underlying type used for DialectFlags.
1205// At the time of writing, this is currently a `u8`. If we want to define more than 8 dialects in
1206// the future, we will need to switch this to a larger type.
1207type DialectFlagsUnderlyingType = u8;
1208
1209bitflags::bitflags! {
1210    /// A collection of bit flags used to represent enabled dialects.
1211    ///
1212    /// This is generally used to allow a word (or similar) to be tagged with multiple dialects.
1213    #[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, PartialOrd, Eq, Hash)]
1214    #[serde(transparent)]
1215    pub struct DialectFlags: DialectFlagsUnderlyingType {
1216        const AMERICAN = Dialect::American as DialectFlagsUnderlyingType;
1217        const CANADIAN = Dialect::Canadian as DialectFlagsUnderlyingType;
1218        const AUSTRALIAN = Dialect::Australian as DialectFlagsUnderlyingType;
1219        const BRITISH = Dialect::British as DialectFlagsUnderlyingType;
1220        const INDIAN = Dialect::Indian as DialectFlagsUnderlyingType;
1221    }
1222}
1223impl DialectFlags {
1224    /// Checks if the provided dialect is enabled.
1225    /// If no dialect is explicitly enabled, it is assumed that all dialects are enabled.
1226    #[must_use]
1227    pub fn is_dialect_enabled(self, dialect: Dialect) -> bool {
1228        self.is_empty() || self.intersects(Self::from_dialect(dialect))
1229    }
1230
1231    /// Checks if the provided dialect is ***explicitly*** enabled.
1232    ///
1233    /// Unlike `is_dialect_enabled`, this will return false when no dialects are explicitly
1234    /// enabled.
1235    #[must_use]
1236    pub fn is_dialect_enabled_strict(self, dialect: Dialect) -> bool {
1237        self.intersects(Self::from_dialect(dialect))
1238    }
1239
1240    /// Constructs a `DialectFlags` from the provided `Dialect`, with only that dialect being
1241    /// enabled.
1242    ///
1243    /// # Panics
1244    ///
1245    /// This will panic if `dialect` represents a dialect that is not defined in
1246    /// `DialectFlags`.
1247    #[must_use]
1248    pub fn from_dialect(dialect: Dialect) -> Self {
1249        let Some(out) = Self::from_bits(dialect as DialectFlagsUnderlyingType) else {
1250            panic!("The '{dialect}' dialect isn't defined in DialectFlags!");
1251        };
1252        out
1253    }
1254
1255    /// Gets the most commonly used dialect(s) in the document.
1256    ///
1257    /// If multiple dialects are used equally often, they will all be enabled in the returned
1258    /// `DialectFlags`. On the other hand, if there is a single dialect that is used the most, it
1259    /// will be the only one enabled.
1260    #[must_use]
1261    pub fn get_most_used_dialects_from_document(document: &Document) -> Self {
1262        // Initialize counters.
1263        let mut dialect_counters: [(Dialect, usize); Dialect::COUNT] = Dialect::VARIANTS
1264            .iter()
1265            .map(|d| (*d, 0))
1266            .collect_array()
1267            .unwrap();
1268
1269        // Count word dialects.
1270        document.iter_words().for_each(|w| {
1271            if let TokenKind::Word(Some(lexeme_metadata)) = &w.kind {
1272                // If the token is a word, iterate though the dialects in `dialect_counters` and
1273                // increment those counters where the word has the respective dialect enabled.
1274                dialect_counters.iter_mut().for_each(|(dialect, count)| {
1275                    if lexeme_metadata.dialects.is_dialect_enabled(*dialect) {
1276                        *count += 1;
1277                    }
1278                });
1279            }
1280        });
1281
1282        // Find max counter.
1283        let max_counter = dialect_counters
1284            .iter()
1285            .map(|(_, count)| count)
1286            .max()
1287            .unwrap();
1288        // Get and convert the collection of most used dialects into a `DialectFlags`.
1289        dialect_counters
1290            .into_iter()
1291            .filter(|(_, count)| count == max_counter)
1292            .fold(DialectFlags::empty(), |acc, dialect| {
1293                // Fold most used dialects into `DialectFlags` via bitwise or.
1294                acc | Self::from_dialect(dialect.0)
1295            })
1296    }
1297}
1298impl Default for DialectFlags {
1299    /// A default value with no dialects explicitly enabled.
1300    /// Implicitly, this state corresponds to all dialects being enabled.
1301    fn default() -> Self {
1302        Self::empty()
1303    }
1304}
1305
1306#[cfg(test)]
1307pub mod tests {
1308    use crate::DictWordMetadata;
1309    use crate::spell::{Dictionary, FstDictionary};
1310
1311    // Helper function to get metadata from the curated dictionary
1312    pub fn md(word: &str) -> DictWordMetadata {
1313        FstDictionary::curated()
1314            .get_word_metadata_str(word)
1315            .unwrap_or_else(|| panic!("Word '{word}' not found in dictionary"))
1316            .into_owned()
1317    }
1318
1319    mod dialect {
1320        use super::super::{Dialect, DialectFlags};
1321        use crate::Document;
1322
1323        #[test]
1324        fn guess_british_dialect() {
1325            let document = Document::new_plain_english_curated("Aluminium was used.");
1326            let df = DialectFlags::get_most_used_dialects_from_document(&document);
1327            assert!(
1328                df.is_dialect_enabled_strict(Dialect::British)
1329                    && !df.is_dialect_enabled_strict(Dialect::American)
1330            );
1331        }
1332
1333        #[test]
1334        fn guess_american_dialect() {
1335            let document = Document::new_plain_english_curated("Aluminum was used.");
1336            let df = DialectFlags::get_most_used_dialects_from_document(&document);
1337            assert!(
1338                df.is_dialect_enabled_strict(Dialect::American)
1339                    && !df.is_dialect_enabled_strict(Dialect::British)
1340            );
1341        }
1342    }
1343
1344    mod noun {
1345        use crate::dict_word_metadata::tests::md;
1346
1347        #[test]
1348        fn puppy_is_noun() {
1349            assert!(md("puppy").is_noun());
1350        }
1351
1352        #[test]
1353        fn prepare_is_not_noun() {
1354            assert!(!md("prepare").is_noun());
1355        }
1356
1357        #[test]
1358        fn paris_is_proper_noun() {
1359            assert!(md("Paris").is_proper_noun());
1360        }
1361
1362        #[test]
1363        fn permit_is_non_proper_noun() {
1364            assert!(md("lapdog").is_non_proper_noun());
1365        }
1366
1367        #[test]
1368        fn hound_is_singular_noun() {
1369            assert!(md("hound").is_singular_noun());
1370        }
1371
1372        #[test]
1373        fn pooches_is_non_singular_noun() {
1374            assert!(md("pooches").is_non_singular_noun());
1375        }
1376
1377        // Make sure is_non_xxx_noun methods don't behave like is_not_xxx_noun.
1378        // In other words, make sure they don't return true for words that are not nouns.
1379        // They must only pass for words that are nouns but not singular etc.
1380        #[test]
1381        fn loyal_doesnt_pass_is_non_singular_noun() {
1382            assert!(!md("loyal").is_non_singular_noun());
1383        }
1384
1385        #[test]
1386        fn hounds_is_plural_noun() {
1387            assert!(md("hounds").is_plural_noun());
1388        }
1389
1390        #[test]
1391        fn pooch_is_non_plural_noun() {
1392            assert!(md("pooch").is_non_plural_noun());
1393        }
1394
1395        #[test]
1396        fn fish_is_singular_noun() {
1397            assert!(md("fish").is_singular_noun());
1398        }
1399
1400        #[test]
1401        fn fish_is_plural_noun() {
1402            assert!(md("fish").is_plural_noun());
1403        }
1404
1405        #[test]
1406        fn fishes_is_plural_noun() {
1407            assert!(md("fishes").is_plural_noun());
1408        }
1409
1410        #[test]
1411        fn sheep_is_singular_noun() {
1412            assert!(md("sheep").is_singular_noun());
1413        }
1414
1415        #[test]
1416        fn sheep_is_plural_noun() {
1417            assert!(md("sheep").is_plural_noun());
1418        }
1419
1420        #[test]
1421        #[should_panic]
1422        fn sheeps_is_not_word() {
1423            md("sheeps");
1424        }
1425
1426        #[test]
1427        fn bicep_is_singular_noun() {
1428            assert!(md("bicep").is_singular_noun());
1429        }
1430
1431        #[test]
1432        fn biceps_is_singular_noun() {
1433            assert!(md("biceps").is_singular_noun());
1434        }
1435
1436        #[test]
1437        fn biceps_is_plural_noun() {
1438            assert!(md("biceps").is_plural_noun());
1439        }
1440
1441        #[test]
1442        fn aircraft_is_singular_noun() {
1443            assert!(md("aircraft").is_singular_noun());
1444        }
1445
1446        #[test]
1447        fn aircraft_is_plural_noun() {
1448            assert!(md("aircraft").is_plural_noun());
1449        }
1450
1451        #[test]
1452        #[should_panic]
1453        fn aircrafts_is_not_word() {
1454            md("aircrafts");
1455        }
1456
1457        #[test]
1458        fn dog_apostrophe_s_is_possessive_noun() {
1459            assert!(md("dog's").is_possessive_noun());
1460        }
1461
1462        #[test]
1463        fn dogs_is_non_possessive_noun() {
1464            assert!(md("dogs").is_non_possessive_noun());
1465        }
1466
1467        // noun countability
1468
1469        #[test]
1470        fn dog_is_countable() {
1471            assert!(md("dog").is_countable_noun());
1472        }
1473        #[test]
1474        fn dog_is_non_mass_noun() {
1475            assert!(md("dog").is_non_mass_noun());
1476        }
1477
1478        #[test]
1479        fn furniture_is_mass_noun() {
1480            assert!(md("furniture").is_mass_noun());
1481        }
1482        #[test]
1483        fn furniture_is_non_countable_noun() {
1484            assert!(md("furniture").is_non_countable_noun());
1485        }
1486
1487        #[test]
1488        fn equipment_is_mass_noun() {
1489            assert!(md("equipment").is_mass_noun());
1490        }
1491        #[test]
1492        fn equipment_is_non_countable_noun() {
1493            assert!(md("equipment").is_non_countable_noun());
1494        }
1495
1496        #[test]
1497        fn beer_is_countable_noun() {
1498            assert!(md("beer").is_countable_noun());
1499        }
1500        #[test]
1501        fn beer_is_mass_noun() {
1502            assert!(md("beer").is_mass_noun());
1503        }
1504    }
1505
1506    mod pronoun {
1507        use crate::dict_word_metadata::tests::md;
1508
1509        mod i_me_myself {
1510            use crate::dict_word_metadata::tests::md;
1511
1512            #[test]
1513            fn i_is_pronoun() {
1514                assert!(md("I").is_pronoun());
1515            }
1516            #[test]
1517            fn i_is_personal_pronoun() {
1518                assert!(md("I").is_personal_pronoun());
1519            }
1520            #[test]
1521            fn i_is_singular_pronoun() {
1522                assert!(md("I").is_singular_pronoun());
1523            }
1524            #[test]
1525            fn i_is_subject_pronoun() {
1526                assert!(md("I").is_subject_pronoun());
1527            }
1528
1529            #[test]
1530            fn me_is_pronoun() {
1531                assert!(md("me").is_pronoun());
1532            }
1533            #[test]
1534            fn me_is_personal_pronoun() {
1535                assert!(md("me").is_personal_pronoun());
1536            }
1537            #[test]
1538            fn me_is_singular_pronoun() {
1539                assert!(md("me").is_singular_pronoun());
1540            }
1541            #[test]
1542            fn me_is_object_pronoun() {
1543                assert!(md("me").is_object_pronoun());
1544            }
1545
1546            #[test]
1547            fn myself_is_pronoun() {
1548                assert!(md("myself").is_pronoun());
1549            }
1550            #[test]
1551            fn myself_is_personal_pronoun() {
1552                assert!(md("myself").is_personal_pronoun());
1553            }
1554            #[test]
1555            fn myself_is_singular_pronoun() {
1556                assert!(md("myself").is_singular_pronoun());
1557            }
1558            #[test]
1559            fn myself_is_reflexive_pronoun() {
1560                assert!(md("myself").is_reflexive_pronoun());
1561            }
1562        }
1563
1564        mod we_us_ourselves {
1565            use crate::dict_word_metadata::tests::md;
1566
1567            #[test]
1568            fn we_is_pronoun() {
1569                assert!(md("we").is_pronoun());
1570            }
1571            #[test]
1572            fn we_is_personal_pronoun() {
1573                assert!(md("we").is_personal_pronoun());
1574            }
1575            #[test]
1576            fn we_is_plural_pronoun() {
1577                assert!(md("we").is_plural_pronoun());
1578            }
1579            #[test]
1580            fn we_is_subject_pronoun() {
1581                assert!(md("we").is_subject_pronoun());
1582            }
1583
1584            #[test]
1585            fn us_is_pronoun() {
1586                assert!(md("us").is_pronoun());
1587            }
1588            #[test]
1589            fn us_is_personal_pronoun() {
1590                assert!(md("us").is_personal_pronoun());
1591            }
1592            #[test]
1593            fn us_is_plural_pronoun() {
1594                assert!(md("us").is_plural_pronoun());
1595            }
1596            #[test]
1597            fn us_is_object_pronoun() {
1598                assert!(md("us").is_object_pronoun());
1599            }
1600
1601            #[test]
1602            fn ourselves_is_pronoun() {
1603                assert!(md("ourselves").is_pronoun());
1604            }
1605            #[test]
1606            fn ourselves_is_personal_pronoun() {
1607                assert!(md("ourselves").is_personal_pronoun());
1608            }
1609            #[test]
1610            fn ourselves_is_plural_pronoun() {
1611                assert!(md("ourselves").is_plural_pronoun());
1612            }
1613            #[test]
1614            fn ourselves_is_reflexive_pronoun() {
1615                assert!(md("ourselves").is_reflexive_pronoun());
1616            }
1617        }
1618
1619        mod you_yourself {
1620            use crate::dict_word_metadata::tests::md;
1621
1622            #[test]
1623            fn you_is_pronoun() {
1624                assert!(md("you").is_pronoun());
1625            }
1626            #[test]
1627            fn you_is_personal_pronoun() {
1628                assert!(md("you").is_personal_pronoun());
1629            }
1630            #[test]
1631            fn you_is_singular_pronoun() {
1632                assert!(md("you").is_singular_pronoun());
1633            }
1634            #[test]
1635            fn you_is_plural_pronoun() {
1636                assert!(md("you").is_plural_pronoun());
1637            }
1638            #[test]
1639            fn you_is_subject_pronoun() {
1640                assert!(md("you").is_subject_pronoun());
1641            }
1642            #[test]
1643            fn you_is_object_pronoun() {
1644                assert!(md("you").is_object_pronoun());
1645            }
1646            #[test]
1647            fn yourself_is_pronoun() {
1648                assert!(md("yourself").is_pronoun());
1649            }
1650            #[test]
1651            fn yourself_is_personal_pronoun() {
1652                assert!(md("yourself").is_personal_pronoun());
1653            }
1654            #[test]
1655            fn yourself_is_singular_pronoun() {
1656                assert!(md("yourself").is_singular_pronoun());
1657            }
1658            #[test]
1659            fn yourself_is_reflexive_pronoun() {
1660                assert!(md("yourself").is_reflexive_pronoun());
1661            }
1662        }
1663
1664        mod he_him_himself {
1665            use crate::dict_word_metadata::tests::md;
1666
1667            #[test]
1668            fn he_is_pronoun() {
1669                assert!(md("he").is_pronoun());
1670            }
1671            #[test]
1672            fn he_is_personal_pronoun() {
1673                assert!(md("he").is_personal_pronoun());
1674            }
1675            #[test]
1676            fn he_is_singular_pronoun() {
1677                assert!(md("he").is_singular_pronoun());
1678            }
1679            #[test]
1680            fn he_is_subject_pronoun() {
1681                assert!(md("he").is_subject_pronoun());
1682            }
1683
1684            #[test]
1685            fn him_is_pronoun() {
1686                assert!(md("him").is_pronoun());
1687            }
1688            #[test]
1689            fn him_is_personal_pronoun() {
1690                assert!(md("him").is_personal_pronoun());
1691            }
1692            #[test]
1693            fn him_is_singular_pronoun() {
1694                assert!(md("him").is_singular_pronoun());
1695            }
1696            #[test]
1697            fn him_is_object_pronoun() {
1698                assert!(md("him").is_object_pronoun());
1699            }
1700
1701            #[test]
1702            fn himself_is_pronoun() {
1703                assert!(md("himself").is_pronoun());
1704            }
1705            #[test]
1706            fn himself_is_personal_pronoun() {
1707                assert!(md("himself").is_personal_pronoun());
1708            }
1709            #[test]
1710            fn himself_is_singular_pronoun() {
1711                assert!(md("himself").is_singular_pronoun());
1712            }
1713            #[test]
1714            fn himself_is_reflexive_pronoun() {
1715                assert!(md("himself").is_reflexive_pronoun());
1716            }
1717        }
1718
1719        mod she_her_herself {
1720            use crate::dict_word_metadata::tests::md;
1721
1722            #[test]
1723            fn she_is_pronoun() {
1724                assert!(md("she").is_pronoun());
1725            }
1726            #[test]
1727            fn she_is_personal_pronoun() {
1728                assert!(md("she").is_personal_pronoun());
1729            }
1730            #[test]
1731            fn she_is_singular_pronoun() {
1732                assert!(md("she").is_singular_pronoun());
1733            }
1734            #[test]
1735            fn she_is_subject_pronoun() {
1736                assert!(md("she").is_subject_pronoun());
1737            }
1738
1739            #[test]
1740            fn her_is_pronoun() {
1741                assert!(md("her").is_pronoun());
1742            }
1743            #[test]
1744            fn her_is_personal_pronoun() {
1745                assert!(md("her").is_personal_pronoun());
1746            }
1747            #[test]
1748            fn her_is_singular_pronoun() {
1749                assert!(md("her").is_singular_pronoun());
1750            }
1751            #[test]
1752            fn her_is_object_pronoun() {
1753                assert!(md("her").is_object_pronoun());
1754            }
1755
1756            #[test]
1757            fn herself_is_pronoun() {
1758                assert!(md("herself").is_pronoun());
1759            }
1760            #[test]
1761            fn herself_is_personal_pronoun() {
1762                assert!(md("herself").is_personal_pronoun());
1763            }
1764            #[test]
1765            fn herself_is_singular_pronoun() {
1766                assert!(md("herself").is_singular_pronoun());
1767            }
1768            #[test]
1769            fn herself_is_reflexive_pronoun() {
1770                assert!(md("herself").is_reflexive_pronoun());
1771            }
1772        }
1773
1774        mod it_itself {
1775            use crate::dict_word_metadata::tests::md;
1776
1777            #[test]
1778            fn it_is_pronoun() {
1779                assert!(md("it").is_pronoun());
1780            }
1781            #[test]
1782            fn it_is_personal_pronoun() {
1783                assert!(md("it").is_personal_pronoun());
1784            }
1785            #[test]
1786            fn it_is_singular_pronoun() {
1787                assert!(md("it").is_singular_pronoun());
1788            }
1789            #[test]
1790            fn it_is_subject_pronoun() {
1791                assert!(md("it").is_subject_pronoun());
1792            }
1793            #[test]
1794            fn it_is_object_pronoun() {
1795                assert!(md("it").is_object_pronoun());
1796            }
1797
1798            #[test]
1799            fn itself_is_pronoun() {
1800                assert!(md("itself").is_pronoun());
1801            }
1802            #[test]
1803            fn itself_is_personal_pronoun() {
1804                assert!(md("itself").is_personal_pronoun());
1805            }
1806            #[test]
1807            fn itself_is_singular_pronoun() {
1808                assert!(md("itself").is_singular_pronoun());
1809            }
1810            #[test]
1811            fn itself_is_reflexive_pronoun() {
1812                assert!(md("itself").is_reflexive_pronoun());
1813            }
1814        }
1815
1816        mod they_them_themselves {
1817            use crate::dict_word_metadata::tests::md;
1818
1819            #[test]
1820            fn they_is_pronoun() {
1821                assert!(md("they").is_pronoun());
1822            }
1823            #[test]
1824            fn they_is_personal_pronoun() {
1825                assert!(md("they").is_personal_pronoun());
1826            }
1827            #[test]
1828            fn they_is_plural_pronoun() {
1829                assert!(md("they").is_plural_pronoun());
1830            }
1831            #[test]
1832            fn they_is_subject_pronoun() {
1833                assert!(md("they").is_subject_pronoun());
1834            }
1835
1836            #[test]
1837            fn them_is_pronoun() {
1838                assert!(md("them").is_pronoun());
1839            }
1840            #[test]
1841            fn them_is_personal_pronoun() {
1842                assert!(md("them").is_personal_pronoun());
1843            }
1844            #[test]
1845            fn them_is_plural_pronoun() {
1846                assert!(md("them").is_plural_pronoun());
1847            }
1848            #[test]
1849            fn them_is_object_pronoun() {
1850                assert!(md("them").is_object_pronoun());
1851            }
1852
1853            #[test]
1854            fn themselves_is_pronoun() {
1855                assert!(md("themselves").is_pronoun());
1856            }
1857            #[test]
1858            fn themselves_is_personal_pronoun() {
1859                assert!(md("themselves").is_personal_pronoun());
1860            }
1861            #[test]
1862            fn themselves_is_plural_pronoun() {
1863                assert!(md("themselves").is_plural_pronoun());
1864            }
1865            #[test]
1866            fn themselves_is_reflexive_pronoun() {
1867                assert!(md("themselves").is_reflexive_pronoun());
1868            }
1869        }
1870
1871        // Possessive pronouns (not to be confused with possessive adjectives/determiners)
1872        #[test]
1873        fn mine_is_pronoun() {
1874            assert!(md("mine").is_pronoun());
1875        }
1876        #[test]
1877        fn ours_is_pronoun() {
1878            assert!(md("ours").is_pronoun());
1879        }
1880        #[test]
1881        fn yours_is_pronoun() {
1882            assert!(md("yours").is_pronoun());
1883        }
1884        #[test]
1885        fn his_is_pronoun() {
1886            assert!(md("his").is_pronoun());
1887        }
1888        #[test]
1889        fn hers_is_pronoun() {
1890            assert!(md("hers").is_pronoun());
1891        }
1892        #[test]
1893        fn its_is_pronoun() {
1894            assert!(md("its").is_pronoun());
1895        }
1896        #[test]
1897        fn theirs_is_pronoun() {
1898            assert!(md("theirs").is_pronoun());
1899        }
1900
1901        // archaic pronouns
1902        #[test]
1903        fn archaic_pronouns() {
1904            assert!(md("thou").is_pronoun());
1905            assert!(md("thee").is_pronoun());
1906            assert!(md("thyself").is_pronoun());
1907            assert!(md("thine").is_pronoun());
1908        }
1909
1910        // generic pronouns
1911        #[test]
1912        fn generic_pronouns() {
1913            assert!(md("one").is_pronoun());
1914            assert!(md("oneself").is_pronoun());
1915        }
1916
1917        // relative and interrogative pronouns
1918        #[test]
1919        fn relative_and_interrogative_pronouns() {
1920            assert!(md("who").is_pronoun());
1921            assert!(md("whom").is_pronoun());
1922            assert!(md("whose").is_pronoun());
1923            assert!(md("which").is_pronoun());
1924            assert!(md("what").is_pronoun());
1925        }
1926
1927        // nonstandard pronouns
1928        #[test]
1929        #[ignore = "not in dictionary"]
1930        fn nonstandard_pronouns() {
1931            assert!(md("themself").pronoun.is_some());
1932            assert!(md("y'all'").pronoun.is_some());
1933        }
1934    }
1935
1936    mod nominal {
1937        use crate::dict_word_metadata::tests::md;
1938
1939        #[test]
1940        fn my_is_possessive_nominal() {
1941            assert!(md("my").is_possessive_nominal());
1942        }
1943
1944        #[test]
1945        fn mine_is_not_possessive_nominal() {
1946            assert!(!md("mine").is_possessive_nominal());
1947        }
1948
1949        #[test]
1950        fn freds_is_possessive_nominal() {
1951            assert!(md("Fred's").is_possessive_nominal());
1952        }
1953
1954        #[test]
1955        fn fred_is_not_possessive_nominal() {
1956            assert!(!md("Fred").is_possessive_nominal());
1957        }
1958
1959        #[test]
1960        fn dogs_is_possessive_nominal() {
1961            assert!(md("dog's").is_possessive_nominal());
1962        }
1963
1964        #[test]
1965        fn microsofts_is_possessive_nominal() {
1966            assert!(md("Microsoft's").is_possessive_nominal());
1967        }
1968    }
1969
1970    mod adjective {
1971        use crate::{Degree, dict_word_metadata::tests::md};
1972
1973        // Getting degrees
1974
1975        #[test]
1976        #[ignore = "not marked yet because it might not be reliable"]
1977        fn big_is_positive() {
1978            assert_eq!(md("big").get_degree(), Some(Degree::Positive));
1979        }
1980
1981        #[test]
1982        fn bigger_is_comparative() {
1983            assert_eq!(md("bigger").get_degree(), Some(Degree::Comparative));
1984        }
1985
1986        #[test]
1987        fn biggest_is_superlative() {
1988            assert_eq!(md("biggest").get_degree(), Some(Degree::Superlative));
1989        }
1990
1991        #[test]
1992        #[should_panic(expected = "Word 'bigly' not found in dictionary")]
1993        fn bigly_is_not_an_adjective_form_we_track() {
1994            assert_eq!(md("bigly").get_degree(), None);
1995        }
1996
1997        // Calling is_ methods
1998
1999        // TODO: positive degree not implemented
2000
2001        #[test]
2002        fn bigger_is_comparative_adjective() {
2003            assert!(md("bigger").is_comparative_adjective());
2004        }
2005
2006        #[test]
2007        fn biggest_is_superlative_adjective() {
2008            assert!(md("biggest").is_superlative_adjective());
2009        }
2010    }
2011
2012    #[test]
2013    fn the_is_determiner() {
2014        assert!(md("the").is_determiner());
2015    }
2016    #[test]
2017    fn this_is_demonstrative_determiner() {
2018        assert!(md("this").is_demonstrative_determiner());
2019    }
2020    #[test]
2021    fn your_is_possessive_determiner() {
2022        assert!(md("your").is_possessive_determiner());
2023    }
2024
2025    #[test]
2026    fn every_is_quantifier() {
2027        assert!(md("every").is_quantifier());
2028    }
2029
2030    #[test]
2031    fn the_isnt_quantifier() {
2032        assert!(!md("the").is_quantifier());
2033    }
2034
2035    #[test]
2036    fn equipment_is_mass_noun() {
2037        assert!(md("equipment").is_mass_noun());
2038    }
2039
2040    #[test]
2041    fn equipment_is_non_countable_noun() {
2042        assert!(md("equipment").is_non_countable_noun());
2043    }
2044
2045    #[test]
2046    fn equipment_isnt_countable_noun() {
2047        assert!(!md("equipment").is_countable_noun());
2048    }
2049
2050    #[test]
2051    fn infrastructure_is_mass_noun_only() {
2052        assert!(md("infrastructure").is_mass_noun_only());
2053    }
2054
2055    #[test]
2056    fn beer_is_not_mass_noun_only() {
2057        assert!(!md("beer").is_mass_noun_only());
2058    }
2059
2060    #[test]
2061    fn sheep_is_not_singular_only() {
2062        assert!(!md("sheep").is_singular_noun_only());
2063    }
2064
2065    #[test]
2066    fn sheep_is_not_plural_only() {
2067        assert!(!md("sheep").is_plural_noun_only());
2068    }
2069
2070    #[test]
2071    fn ox_is_singular_only() {
2072        assert!(md("ox").is_singular_noun_only());
2073    }
2074
2075    #[test]
2076    fn oxen_is_plural_only() {
2077        assert!(md("oxen").is_plural_noun_only());
2078    }
2079
2080    mod verb {
2081        use crate::dict_word_metadata::tests::md;
2082
2083        #[test]
2084        fn lemma_walk() {
2085            let md = md("walk");
2086            assert!(md.is_verb_lemma())
2087        }
2088
2089        #[test]
2090        fn lemma_fix() {
2091            let md = md("fix");
2092            assert!(md.is_verb_lemma())
2093        }
2094
2095        #[test]
2096        fn progressive_walking() {
2097            let md = md("walking");
2098            assert!(md.is_verb_progressive_form())
2099        }
2100
2101        #[test]
2102        fn past_walked() {
2103            let md = md("walked");
2104            assert!(md.is_verb_past_form())
2105        }
2106
2107        #[test]
2108        fn regular_past_thought() {
2109            let md = md("thought");
2110            assert!(md.is_verb_preterite_and_participle_form())
2111        }
2112
2113        #[test]
2114        fn simple_past_ate() {
2115            let md = md("ate");
2116            assert!(md.is_verb_simple_past_form())
2117        }
2118
2119        #[test]
2120        fn past_participle_eaten() {
2121            let md = md("eaten");
2122            assert!(md.is_verb_past_participle_form())
2123        }
2124
2125        #[test]
2126        fn ate_is_simple_past_only() {
2127            let md = md("ate");
2128            assert!(md.is_verb_simple_past_only());
2129            assert!(!md.is_verb_past_participle_only());
2130        }
2131
2132        #[test]
2133        fn eaten_is_past_participle_only() {
2134            let md = md("eaten");
2135            assert!(md.is_verb_past_participle_only());
2136            assert!(!md.is_verb_simple_past_only());
2137        }
2138
2139        #[test]
2140        fn thought_is_neither_past_form_only() {
2141            let md = md("thought");
2142            assert!(!md.is_verb_simple_past_only());
2143            assert!(!md.is_verb_past_participle_only());
2144        }
2145
2146        #[test]
2147        fn shared_past_forms_are_neither_past_form_only() {
2148            let md = md("thought");
2149            assert!(!md.is_verb_simple_past_only());
2150            assert!(!md.is_verb_past_participle_only());
2151            assert!(md.is_verb_preterite_and_participle_form());
2152        }
2153
2154        #[test]
2155        fn distinct_past_forms_are_not_regular_past() {
2156            assert!(!md("ate").is_verb_preterite_and_participle_form());
2157            assert!(!md("eaten").is_verb_preterite_and_participle_form());
2158            assert!(!md("walked").is_verb_preterite_and_participle_form());
2159        }
2160
2161        #[test]
2162        fn third_pers_sing_walks() {
2163            let md = md("walks");
2164            assert!(md.is_verb_third_person_singular_present_form())
2165        }
2166    }
2167}