1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
use crate::{
    affix::RegularAffix,
    category::{AffixualAdjunctMode, AffixualAdjunctScope, Stress},
    gloss::{Gloss, GlossFlags, GlossHelpers, GlossStatic},
    prelude::{token::HForm, IntoTokens, TokenList},
    romanize::{
        flags::{FromTokenFlags, IntoTokensFlags},
        segment::{CsVxCz, VxCs, Vz},
        stream::{ParseError, TokenStream},
        token::Schwa,
        traits::{FromTokens, IntoVxCs},
    },
};
use vec1::Vec1;

/// An affixual adjunct containing multiple affixes.
#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct MultipleAffixAdjunct {
    /// The first affix of this adjunct.
    pub first_affix: RegularAffix,

    /// The scope of this adjunct (dominant over slot V, subordinate to slot VII, etc.).
    pub first_scope: AffixualAdjunctScope,

    /// The other affixes of this adjunct.
    pub other_affixes: Vec1<RegularAffix>,

    /// The scope of this adjunct (dominant over slot V, subordinate to slot VII, etc.).
    pub other_scope: Option<AffixualAdjunctScope>,

    /// The mode of this adjunct (full or concatenated stem only).
    pub mode: AffixualAdjunctMode,
}

impl Gloss for MultipleAffixAdjunct {
    fn gloss(&self, flags: GlossFlags) -> String {
        let mut output = self.first_affix.gloss(flags);
        output.add_dashed(self.first_scope.gloss_static_non_default(flags));
        for affix in &self.other_affixes {
            output.add_dashed(&affix.gloss(flags));
        }
        if let Some(other_scope) = self.other_scope {
            output.add_dashed(other_scope.gloss_static_non_default(flags));
        }
        output.add_dashed(self.mode.gloss_static_non_default(flags));
        output
    }
}

impl FromTokens for MultipleAffixAdjunct {
    fn parse_volatile(stream: &mut TokenStream, flags: FromTokenFlags) -> Result<Self, ParseError> {
        let _ = stream.next::<Schwa>();

        let CsVxCz {
            affix: first_affix,
            scope: first_scope,
        } = stream.parse(flags)?;

        let VxCs {
            affix: second_affix,
        } = stream.parse(flags)?;

        let mut other_affixes = Vec1::new(second_affix);

        while let Ok(VxCs { affix }) = stream.parse(flags) {
            other_affixes.push(affix);
        }

        let Vz { scope: other_scope } = stream.parse(flags)?;

        let mode = match stream.stress() {
            Some(Stress::Ultimate) => AffixualAdjunctMode::Concatenated,
            Some(Stress::Antepenultimate) => return Err(ParseError::AntepenultimateStress),
            _ => AffixualAdjunctMode::Full,
        };

        Ok(MultipleAffixAdjunct {
            first_affix,
            first_scope,
            other_affixes,
            other_scope,
            mode,
        })
    }
}

impl IntoTokens for MultipleAffixAdjunct {
    fn append_tokens_to(&self, list: &mut TokenList, flags: IntoTokensFlags) {
        let (mut first_vx, first_cs) = self.first_affix.into_vx_cs();
        if flags.matches(IntoTokensFlags::WORD_INITIAL_VOWEL) || !first_cs.is_valid_word_initial() {
            list.push(Schwa);
        }
        let (needs_glottal_stop, first_cz) = match self.first_scope {
            AffixualAdjunctScope::VDom => (false, HForm::H),
            AffixualAdjunctScope::Formative => (false, HForm::HW),
            AffixualAdjunctScope::VSub => (true, HForm::H),
            AffixualAdjunctScope::VIIDom => (true, HForm::HL),
            AffixualAdjunctScope::VIISub => (true, HForm::HR),
            AffixualAdjunctScope::OverAdj => (true, HForm::HW),
        };
        first_vx.has_glottal_stop = needs_glottal_stop;
        list.push(first_cs);
        list.push(first_vx);
        list.push(first_cz);
        let vz = if self.other_scope.is_some()
            || !self
                .other_affixes
                .last()
                .into_vx_cs()
                .1
                .is_valid_word_final()
        {
            Some(self.other_scope)
        } else {
            None
        };
        for affix in &self.other_affixes {
            let (vx, cs) = affix.into_vx_cs();
            list.push(vx);
            list.push(cs);
        }
        if let Some(vz) = vz {
            list.push(Vz { scope: vz });
        } else if flags.matches(IntoTokensFlags::WORD_FINAL_VOWEL) {
            list.push(Vz { scope: None });
        }
    }
}