morph_rs/morph/
mod.rs

1use smallvec::SmallVec;
2
3use crate::{
4    analyzer::{dictionary::LemmaDict, Tag},
5    errors::DictionaryErr,
6    morph::vanga::{LemmaVanga, VangaVariant},
7    opencorpora::dictionary::GramWord,
8};
9
10use self::grammemes::{Grammem, Other, ParteSpeech};
11
12/// Содержит типы хранимых граммем слов
13/// в виде `unit enum`-ов для упрощения хранения.
14pub mod grammemes;
15/// Модуль сборки данных для Вангования
16/// на основе имеющегося словаря.
17pub(crate) mod vanga;
18
19// Взято из кода Pymorphy2.
20/// Непродуктивность - это в т.ч. невозможность образовывать от данных граммем префиксным-постфиксным образом новые слова.
21/// Например, мы не можем образовать слово от междометия.
22pub(crate) const UNPRODUCTIVE: [Grammem; 8] = [
23    Grammem::ParteSpeech(ParteSpeech::Number),
24    Grammem::ParteSpeech(ParteSpeech::NounPronoun),
25    Grammem::ParteSpeech(ParteSpeech::Predicative),
26    Grammem::ParteSpeech(ParteSpeech::Preposition),
27    Grammem::ParteSpeech(ParteSpeech::Conjunction),
28    Grammem::ParteSpeech(ParteSpeech::Particle),
29    Grammem::ParteSpeech(ParteSpeech::Interjection),
30    Grammem::Other(Other::Pronominal),
31];
32
33#[macro_export]
34macro_rules! gram {
35    ( $x:expr ) => {{
36        $crate::morph::grammemes::ToGrammem::to_grammem($x)
37    }};
38}
39
40#[macro_export]
41macro_rules! grams {
42    ( $($x:expr),* $(,)?  ) => {
43        {
44            vec![$(
45                $crate::gram![$x],
46            )*]
47        }
48    };
49}
50
51#[test]
52fn test_gram() {
53    let grammemes = vec![
54        Grammem::ParteSpeech(ParteSpeech::Number),
55        Grammem::Other(Other::Pronominal),
56    ];
57
58    let grams = grams![ParteSpeech::Number, Other::Pronominal];
59
60    assert_eq!(gram!(ParteSpeech::Number), *grammemes.first().unwrap());
61    assert_eq!(grammemes, grams);
62}
63
64impl LemmaDict {
65    /// Вычленение первых граммем из леммы словаря.
66    /// Первые граммемы наследуются всем остальным формам слова.
67    pub(crate) fn first_tags(&self) -> Result<Tag, DictionaryErr> {
68        let LemmaDict { normal_form, .. } = self;
69
70        match &normal_form.gram {
71            None => Err(DictionaryErr::LostFirstGrammemes(
72                normal_form.text.to_owned(),
73            )),
74            Some(gram) => Ok(SmallVec::from_iter(gram.iter().map(|g| g.v))),
75        }
76    }
77
78    /// Вычленение граммем остальных форм слова.
79    ///
80    /// Если к форме не было граммем, то вернется пустой вектор `Tag`.
81    pub(crate) fn forms(forms: Vec<GramWord>) -> impl Iterator<Item = (String, Tag)> {
82        forms.into_iter().map(|gram| match gram.gram {
83            None => (gram.text, SmallVec::from(vec![])),
84            Some(grams) => {
85                let tags = SmallVec::from_iter(grams.iter().map(|gram| gram.v));
86                (gram.text, tags)
87            }
88        })
89    }
90}
91
92impl LemmaVanga {
93    /// Вычленение первых граммем из леммы словаря.
94    /// Первые граммемы наследуются всем остальным формам слова.
95    pub(crate) fn first_tags(&self) -> Result<Tag, DictionaryErr> {
96        let LemmaVanga { variants } = self;
97
98        match variants.first() {
99            None => Err(DictionaryErr::EmptyVanga),
100            Some(VangaVariant { tag, .. }) => Ok(tag.to_owned()),
101        }
102    }
103
104    /// Вычленение граммем остальных форм слова.
105    ///
106    /// Если к форме не было граммем, то вернется пустой вектор `Tag`.
107    pub(crate) fn forms(forms: Vec<GramWord>) -> impl Iterator<Item = (String, Tag)> {
108        forms.into_iter().map(|gram| match gram.gram {
109            None => (gram.text, SmallVec::from(vec![])),
110            Some(grams) => {
111                let tags = SmallVec::from_iter(grams.iter().map(|gram| gram.v));
112                (gram.text, tags)
113            }
114        })
115    }
116}