tnil/romanize/
segment.rs

1//! Implements traits for conversion from and into tokens for many types, and provides wrappers on
2//! sequences of types (such as a VnCn pair) to aid with parsing them.
3
4use super::{
5    flags::{FromTokenFlags, IntoTokensFlags},
6    stream::ParseError,
7    stream::TokenStream,
8    token::{
9        GlottalStop, HForm, Hh, Hr, NumeralForm, OwnedConsonantForm, Schwa, Token, VowelForm,
10        WYForm, ÜA,
11    },
12    traits::{FromToken, FromTokens, IntoToken, IntoTokens, IntoVowelForm},
13};
14use crate::{
15    affix::RegularAffix,
16    category::{
17        AffixualAdjunctScope, ArbitraryMoodOrCaseScope, Aspect, Bias, Case, CaseScope, HFormDegree,
18        HFormSequence, ModularAdjunctMode, ModularAdjunctScope, Mood, MoodOrCaseScope,
19        NonAspectualVn, RegisterType, Stress, SuppletiveAdjunctMode, Vn, VowelFormDegree,
20        VowelFormSequence,
21    },
22    prelude::TokenList,
23};
24
25impl FromToken for OwnedConsonantForm {
26    fn from_token(token: &Token) -> Option<Self> {
27        match token {
28            Token::C(value) => Some(value.clone()),
29            _ => None,
30        }
31    }
32}
33
34impl IntoToken for OwnedConsonantForm {
35    fn into_token(self) -> Token {
36        Token::C(self)
37    }
38}
39
40impl IntoVowelForm for VowelForm {
41    fn into_vowel_form(self) -> VowelForm {
42        self
43    }
44}
45
46impl FromToken for ÜA {
47    fn from_token(token: &Token) -> Option<Self> {
48        match token {
49            Token::ÜA => Some(Self),
50            _ => None,
51        }
52    }
53}
54
55impl IntoToken for ÜA {
56    fn into_token(self) -> Token {
57        Token::ÜA
58    }
59}
60
61impl FromToken for Schwa {
62    fn from_token(token: &Token) -> Option<Self> {
63        match token {
64            Token::Schwa => Some(Self),
65            _ => None,
66        }
67    }
68}
69
70impl IntoToken for Schwa {
71    fn into_token(self) -> Token {
72        Token::Schwa
73    }
74}
75
76impl FromToken for HForm {
77    fn from_token(token: &Token) -> Option<Self> {
78        match token {
79            Token::H(value) => Some(*value),
80            _ => None,
81        }
82    }
83}
84
85impl IntoToken for HForm {
86    fn into_token(self) -> Token {
87        Token::H(self)
88    }
89}
90
91impl FromToken for WYForm {
92    fn from_token(token: &Token) -> Option<Self> {
93        match token {
94            Token::H(HForm::W) => Some(WYForm::W),
95            Token::H(HForm::Y) => Some(WYForm::Y),
96            _ => None,
97        }
98    }
99}
100
101impl IntoToken for WYForm {
102    fn into_token(self) -> Token {
103        match self {
104            Self::W => Token::H(HForm::W),
105            Self::Y => Token::H(HForm::Y),
106        }
107    }
108}
109
110impl FromToken for NumeralForm {
111    fn from_token(token: &Token) -> Option<Self> {
112        match token {
113            Token::N(value) => Some(*value),
114            _ => None,
115        }
116    }
117}
118
119impl IntoToken for NumeralForm {
120    fn into_token(self) -> Token {
121        Token::N(self)
122    }
123}
124
125impl FromToken for GlottalStop {
126    fn from_token(token: &Token) -> Option<Self> {
127        match token {
128            Token::GlottalStop => Some(Self),
129            _ => None,
130        }
131    }
132}
133
134impl IntoToken for GlottalStop {
135    fn into_token(self) -> Token {
136        Token::GlottalStop
137    }
138}
139
140impl IntoToken for Hh {
141    fn into_token(self) -> Token {
142        Token::H(HForm::H)
143    }
144}
145
146impl FromTokens for Hh {
147    fn parse_volatile(stream: &mut TokenStream, _: FromTokenFlags) -> Result<Self, ParseError> {
148        match stream.next_any() {
149            Some(Token::H(HForm::H)) => Ok(Self),
150            _ => Err(ParseError::ExpectedHh),
151        }
152    }
153}
154
155impl IntoToken for Hr {
156    fn into_token(self) -> Token {
157        Token::H(HForm::HR)
158    }
159}
160
161impl FromTokens for Hr {
162    fn parse_volatile(stream: &mut TokenStream, _: FromTokenFlags) -> Result<Self, ParseError> {
163        match stream.next_any() {
164            Some(Token::H(HForm::HR)) => Ok(Self),
165            _ => Err(ParseError::ExpectedHr),
166        }
167    }
168}
169
170impl IntoToken for Bias {
171    fn into_token(self) -> Token {
172        Token::C(self.as_cb().into())
173    }
174}
175
176impl FromTokens for Bias {
177    fn parse_volatile(stream: &mut TokenStream, _: FromTokenFlags) -> Result<Self, ParseError> {
178        match stream.next_any() {
179            Some(Token::C(value)) => match value.0.parse() {
180                Ok(value) => Ok(value),
181                Err(_) => Err(ParseError::ExpectedCb),
182            },
183            _ => Err(ParseError::ExpectedCb),
184        }
185    }
186}
187
188impl IntoToken for SuppletiveAdjunctMode {
189    fn into_token(self) -> Token {
190        Token::H(match self {
191            SuppletiveAdjunctMode::CAR => HForm::HL,
192            SuppletiveAdjunctMode::QUO => HForm::HM,
193            SuppletiveAdjunctMode::NAM => HForm::HN,
194            SuppletiveAdjunctMode::PHR => HForm::HŇ,
195        })
196    }
197}
198
199impl FromTokens for SuppletiveAdjunctMode {
200    fn parse_volatile(stream: &mut TokenStream, _: FromTokenFlags) -> Result<Self, ParseError> {
201        match stream.next_any() {
202            Some(Token::H(h)) => match *h {
203                HForm::HL => Ok(SuppletiveAdjunctMode::CAR),
204                HForm::HM => Ok(SuppletiveAdjunctMode::QUO),
205                HForm::HN => Ok(SuppletiveAdjunctMode::NAM),
206                HForm::HŇ => Ok(SuppletiveAdjunctMode::PHR),
207                _ => Err(ParseError::ExpectedCp),
208            },
209
210            _ => Err(ParseError::ExpectedCp),
211        }
212    }
213}
214
215impl IntoVowelForm for Case {
216    fn into_vowel_form(self) -> VowelForm {
217        let value = self as u8;
218
219        VowelForm {
220            has_glottal_stop: value >= 36,
221            sequence: match value / 9 {
222                0 | 4 => VowelFormSequence::S1,
223                1 | 5 => VowelFormSequence::S2,
224                2 | 6 => VowelFormSequence::S3,
225                3 | 7 => VowelFormSequence::S4,
226                _ => unreachable!(),
227            },
228            degree: match value % 9 {
229                0 => VowelFormDegree::D1,
230                1 => VowelFormDegree::D2,
231                2 => VowelFormDegree::D3,
232                3 => VowelFormDegree::D4,
233                4 => VowelFormDegree::D5,
234                5 => VowelFormDegree::D6,
235                6 => VowelFormDegree::D7,
236                7 => VowelFormDegree::D8,
237                8 => VowelFormDegree::D9,
238                _ => unreachable!(),
239            },
240        }
241    }
242}
243
244impl FromTokens for Case {
245    fn parse_volatile(
246        stream: &mut TokenStream,
247        _flags: FromTokenFlags,
248    ) -> Result<Self, ParseError> {
249        match stream.next_any() {
250            Some(Token::V(vc)) => Case::from_vc(*vc),
251            _ => Err(ParseError::ExpectedVc),
252        }
253    }
254}
255
256impl IntoVowelForm for RegisterType {
257    fn into_vowel_form(self) -> VowelForm {
258        match self {
259            RegisterType::DSV => VowelForm {
260                has_glottal_stop: false,
261                sequence: VowelFormSequence::S1,
262                degree: VowelFormDegree::D1,
263            },
264            RegisterType::PNT => VowelForm {
265                has_glottal_stop: false,
266                sequence: VowelFormSequence::S1,
267                degree: VowelFormDegree::D3,
268            },
269            RegisterType::SPF => VowelForm {
270                has_glottal_stop: false,
271                sequence: VowelFormSequence::S1,
272                degree: VowelFormDegree::D4,
273            },
274            RegisterType::EXM => VowelForm {
275                has_glottal_stop: false,
276                sequence: VowelFormSequence::S1,
277                degree: VowelFormDegree::D7,
278            },
279            RegisterType::CGT => VowelForm {
280                has_glottal_stop: false,
281                sequence: VowelFormSequence::S1,
282                degree: VowelFormDegree::D9,
283            },
284            RegisterType::DSV_END => VowelForm {
285                has_glottal_stop: false,
286                sequence: VowelFormSequence::S2,
287                degree: VowelFormDegree::D1,
288            },
289            RegisterType::PNT_END => VowelForm {
290                has_glottal_stop: false,
291                sequence: VowelFormSequence::S2,
292                degree: VowelFormDegree::D3,
293            },
294            RegisterType::SPF_END => VowelForm {
295                has_glottal_stop: false,
296                sequence: VowelFormSequence::S2,
297                degree: VowelFormDegree::D8,
298            },
299            RegisterType::EXM_END => VowelForm {
300                has_glottal_stop: false,
301                sequence: VowelFormSequence::S2,
302                degree: VowelFormDegree::D7,
303            },
304            RegisterType::CGT_END => VowelForm {
305                has_glottal_stop: false,
306                sequence: VowelFormSequence::S2,
307                degree: VowelFormDegree::D9,
308            },
309            RegisterType::END => VowelForm {
310                has_glottal_stop: false,
311                sequence: VowelFormSequence::S1,
312                degree: VowelFormDegree::D8,
313            },
314        }
315    }
316}
317
318impl FromTokens for RegisterType {
319    fn parse_volatile(stream: &mut TokenStream, _: FromTokenFlags) -> Result<Self, ParseError> {
320        match stream.next_any() {
321            Some(Token::V(VowelForm {
322                has_glottal_stop: false,
323                sequence,
324                degree,
325            })) => match (sequence, degree) {
326                (VowelFormSequence::S1, VowelFormDegree::D1) => Ok(RegisterType::DSV),
327                (VowelFormSequence::S1, VowelFormDegree::D3) => Ok(RegisterType::PNT),
328                (VowelFormSequence::S1, VowelFormDegree::D4) => Ok(RegisterType::SPF),
329                (VowelFormSequence::S1, VowelFormDegree::D7) => Ok(RegisterType::EXM),
330                (VowelFormSequence::S1, VowelFormDegree::D9) => Ok(RegisterType::CGT),
331                (VowelFormSequence::S2, VowelFormDegree::D1) => Ok(RegisterType::DSV_END),
332                (VowelFormSequence::S2, VowelFormDegree::D3) => Ok(RegisterType::PNT_END),
333                (VowelFormSequence::S2, VowelFormDegree::D8) => Ok(RegisterType::SPF_END),
334                (VowelFormSequence::S2, VowelFormDegree::D7) => Ok(RegisterType::EXM_END),
335                (VowelFormSequence::S2, VowelFormDegree::D9) => Ok(RegisterType::CGT_END),
336                (VowelFormSequence::S1, VowelFormDegree::D8) => Ok(RegisterType::END),
337                _ => Err(ParseError::ExpectedVm),
338            },
339            _ => Err(ParseError::ExpectedVm),
340        }
341    }
342}
343
344impl IntoVowelForm for Stress {
345    fn into_vowel_form(self) -> VowelForm {
346        VowelForm {
347            has_glottal_stop: false,
348            sequence: VowelFormSequence::S1,
349            degree: match self {
350                Stress::Monosyllabic => VowelFormDegree::D1,
351                Stress::Ultimate => VowelFormDegree::D3,
352                Stress::Penultimate => VowelFormDegree::D7,
353                Stress::Antepenultimate => VowelFormDegree::D9,
354            },
355        }
356    }
357}
358
359impl FromTokens for Stress {
360    fn parse_volatile(stream: &mut TokenStream, _: FromTokenFlags) -> Result<Self, ParseError> {
361        match stream.next_any() {
362            Some(Token::V(VowelForm {
363                has_glottal_stop: false,
364                sequence: VowelFormSequence::S1,
365                degree,
366            })) => match degree {
367                VowelFormDegree::D1 => Ok(Stress::Monosyllabic),
368                VowelFormDegree::D3 => Ok(Stress::Ultimate),
369                VowelFormDegree::D7 => Ok(Stress::Penultimate),
370                VowelFormDegree::D9 => Ok(Stress::Antepenultimate),
371                _ => Err(ParseError::ExpectedVp),
372            },
373            _ => Err(ParseError::ExpectedVp),
374        }
375    }
376}
377
378impl IntoVowelForm for MoodOrCaseScope {
379    fn into_vowel_form(self) -> VowelForm {
380        match self {
381            MoodOrCaseScope::Mood(Mood::FAC) => VowelForm {
382                has_glottal_stop: false,
383                sequence: VowelFormSequence::S1,
384                degree: VowelFormDegree::D1,
385            },
386            MoodOrCaseScope::Mood(Mood::SUB) => VowelForm {
387                has_glottal_stop: false,
388                sequence: VowelFormSequence::S1,
389                degree: VowelFormDegree::D3,
390            },
391            MoodOrCaseScope::Mood(Mood::ASM) => VowelForm {
392                has_glottal_stop: false,
393                sequence: VowelFormSequence::S1,
394                degree: VowelFormDegree::D4,
395            },
396            MoodOrCaseScope::Mood(Mood::SPC) => VowelForm {
397                has_glottal_stop: false,
398                sequence: VowelFormSequence::S1,
399                degree: VowelFormDegree::D7,
400            },
401            MoodOrCaseScope::Mood(Mood::COU) => VowelForm {
402                has_glottal_stop: false,
403                sequence: VowelFormSequence::S1,
404                degree: VowelFormDegree::D6,
405            },
406            MoodOrCaseScope::Mood(Mood::HYP) => VowelForm {
407                has_glottal_stop: false,
408                sequence: VowelFormSequence::S1,
409                degree: VowelFormDegree::D9,
410            },
411            MoodOrCaseScope::CaseScope(CaseScope::CCN) => VowelForm {
412                has_glottal_stop: false,
413                sequence: VowelFormSequence::S2,
414                degree: VowelFormDegree::D1,
415            },
416            MoodOrCaseScope::CaseScope(CaseScope::CCA) => VowelForm {
417                has_glottal_stop: false,
418                sequence: VowelFormSequence::S2,
419                degree: VowelFormDegree::D3,
420            },
421            MoodOrCaseScope::CaseScope(CaseScope::CCS) => VowelForm {
422                has_glottal_stop: false,
423                sequence: VowelFormSequence::S2,
424                degree: VowelFormDegree::D8,
425            },
426            MoodOrCaseScope::CaseScope(CaseScope::CCQ) => VowelForm {
427                has_glottal_stop: false,
428                sequence: VowelFormSequence::S2,
429                degree: VowelFormDegree::D7,
430            },
431            MoodOrCaseScope::CaseScope(CaseScope::CCP) => VowelForm {
432                has_glottal_stop: false,
433                sequence: VowelFormSequence::S1,
434                degree: VowelFormDegree::D8,
435            },
436            MoodOrCaseScope::CaseScope(CaseScope::CCV) => VowelForm {
437                has_glottal_stop: false,
438                sequence: VowelFormSequence::S2,
439                degree: VowelFormDegree::D9,
440            },
441        }
442    }
443}
444
445impl FromTokens for MoodOrCaseScope {
446    fn parse_volatile(stream: &mut TokenStream, _: FromTokenFlags) -> Result<Self, ParseError> {
447        use VowelFormDegree as D;
448        use VowelFormSequence as S;
449
450        match stream.next_any() {
451            Some(Token::V(VowelForm {
452                has_glottal_stop: false,
453                sequence,
454                degree,
455            })) => match (sequence, degree) {
456                (S::S1, D::D1) => Ok(MoodOrCaseScope::Mood(Mood::FAC)),
457                (S::S1, D::D3) => Ok(MoodOrCaseScope::Mood(Mood::SUB)),
458                (S::S1, D::D4) => Ok(MoodOrCaseScope::Mood(Mood::ASM)),
459                (S::S1, D::D7) => Ok(MoodOrCaseScope::Mood(Mood::SPC)),
460                (S::S1, D::D6) => Ok(MoodOrCaseScope::Mood(Mood::COU)),
461                (S::S1, D::D9) => Ok(MoodOrCaseScope::Mood(Mood::HYP)),
462
463                (S::S2, D::D1) => Ok(MoodOrCaseScope::CaseScope(CaseScope::CCN)),
464                (S::S2, D::D3) => Ok(MoodOrCaseScope::CaseScope(CaseScope::CCA)),
465                (S::S2, D::D8) => Ok(MoodOrCaseScope::CaseScope(CaseScope::CCS)),
466                (S::S2, D::D7) => Ok(MoodOrCaseScope::CaseScope(CaseScope::CCQ)),
467                (S::S1, D::D8) => Ok(MoodOrCaseScope::CaseScope(CaseScope::CCP)),
468                (S::S2, D::D9) => Ok(MoodOrCaseScope::CaseScope(CaseScope::CCV)),
469
470                _ => Err(ParseError::ExpectedCn),
471            },
472            _ => Err(ParseError::ExpectedCn),
473        }
474    }
475}
476
477impl IntoTokens for ModularAdjunctMode {
478    fn append_tokens_to(&self, list: &mut TokenList, _flags: IntoTokensFlags) {
479        match self {
480            ModularAdjunctMode::Parent => list.push(WYForm::W),
481            ModularAdjunctMode::Concatenated => list.push(WYForm::Y),
482            ModularAdjunctMode::Full => {}
483        }
484    }
485}
486
487impl FromTokens for ModularAdjunctMode {
488    fn parse_volatile(stream: &mut TokenStream, _: FromTokenFlags) -> Result<Self, ParseError> {
489        match stream.next() {
490            Some(WYForm::W) => Ok(ModularAdjunctMode::Parent),
491            Some(WYForm::Y) => Ok(ModularAdjunctMode::Concatenated),
492            None => Ok(ModularAdjunctMode::Full),
493        }
494    }
495}
496
497impl IntoVowelForm for NonAspectualVn {
498    fn into_vowel_form(self) -> VowelForm {
499        let (sequence, degree) = match self {
500            NonAspectualVn::Valence(value) => (VowelFormSequence::S1, value as u8),
501            NonAspectualVn::Phase(value) => (VowelFormSequence::S1, value as u8),
502            NonAspectualVn::Effect(value) => (VowelFormSequence::S1, value as u8),
503            NonAspectualVn::Level(value) => (VowelFormSequence::S1, value as u8),
504        };
505
506        VowelForm {
507            has_glottal_stop: false,
508            sequence,
509            degree: match degree {
510                0 => VowelFormDegree::D1,
511                1 => VowelFormDegree::D2,
512                2 => VowelFormDegree::D3,
513                3 => VowelFormDegree::D4,
514                4 => VowelFormDegree::D5,
515                5 => VowelFormDegree::D6,
516                6 => VowelFormDegree::D7,
517                7 => VowelFormDegree::D8,
518                8 => VowelFormDegree::D9,
519                _ => unreachable!(),
520            },
521        }
522    }
523}
524
525impl FromTokens for NonAspectualVn {
526    fn parse_volatile(stream: &mut TokenStream, flags: FromTokenFlags) -> Result<Self, ParseError> {
527        let vn: VowelForm = stream.next().ok_or(ParseError::ExpectedVn)?;
528        if vn.has_glottal_stop && !flags.matches(FromTokenFlags::PERMISSIVE) {
529            return Err(ParseError::GlottalizedVn);
530        }
531        Self::from_vowel_form(vn).ok_or(ParseError::ExpectedVn)
532    }
533}
534
535impl IntoVowelForm for Aspect {
536    fn into_vowel_form(self) -> VowelForm {
537        let value = self as u8;
538
539        VowelForm {
540            has_glottal_stop: false,
541            sequence: match value / 9 {
542                0 => VowelFormSequence::S1,
543                1 => VowelFormSequence::S2,
544                2 => VowelFormSequence::S3,
545                3 => VowelFormSequence::S4,
546                _ => unreachable!(),
547            },
548            degree: match value % 9 {
549                0 => VowelFormDegree::D1,
550                1 => VowelFormDegree::D2,
551                2 => VowelFormDegree::D3,
552                3 => VowelFormDegree::D4,
553                4 => VowelFormDegree::D5,
554                5 => VowelFormDegree::D6,
555                6 => VowelFormDegree::D7,
556                7 => VowelFormDegree::D8,
557                8 => VowelFormDegree::D9,
558                _ => unreachable!(),
559            },
560        }
561    }
562}
563
564impl FromTokens for Aspect {
565    fn parse_volatile(stream: &mut TokenStream, flags: FromTokenFlags) -> Result<Self, ParseError> {
566        let vn: VowelForm = stream.next().ok_or(ParseError::ExpectedVn)?;
567        if vn.has_glottal_stop && !flags.matches(FromTokenFlags::PERMISSIVE) {
568            return Err(ParseError::GlottalizedVn);
569        }
570        Self::from_vowel_form(vn).ok_or(ParseError::ExpectedVn)
571    }
572}
573
574impl IntoVowelForm for ModularAdjunctScope {
575    fn into_vowel_form(self) -> VowelForm {
576        VowelForm {
577            has_glottal_stop: false,
578            sequence: VowelFormSequence::S1,
579            degree: match self {
580                ModularAdjunctScope::Formative => VowelFormDegree::D1,
581                ModularAdjunctScope::MCS => VowelFormDegree::D3,
582                ModularAdjunctScope::OverAdj => VowelFormDegree::D4,
583                ModularAdjunctScope::UnderAdj => VowelFormDegree::D7,
584            },
585        }
586    }
587}
588
589impl FromTokens for ModularAdjunctScope {
590    fn parse_volatile(stream: &mut TokenStream, flags: FromTokenFlags) -> Result<Self, ParseError> {
591        let Some(VowelForm {
592            has_glottal_stop,
593            sequence: VowelFormSequence::S1,
594            degree,
595        }) = stream.next()
596        else {
597            return Err(ParseError::ExpectedVh);
598        };
599
600        if has_glottal_stop && !flags.matches(FromTokenFlags::PERMISSIVE) {
601            return Err(ParseError::GlottalizedVh);
602        }
603
604        match degree {
605            VowelFormDegree::D1 => Ok(ModularAdjunctScope::Formative),
606            VowelFormDegree::D3 => Ok(ModularAdjunctScope::MCS),
607            VowelFormDegree::D4 | VowelFormDegree::D9 => Ok(ModularAdjunctScope::OverAdj),
608            VowelFormDegree::D7 => Ok(ModularAdjunctScope::UnderAdj),
609            _ => Err(ParseError::ExpectedVh),
610        }
611    }
612}
613
614/// A parsed Cn that is arbitrarily either a mood or a case-scope and marks its corresponding Vn as
615/// aspectual or non-aspectual.
616#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
617pub struct Cn {
618    /// The mood/case-scope of this Cn form.
619    pub mcs: ArbitraryMoodOrCaseScope,
620
621    /// Whether the corresponding Vn is an aspect.
622    pub is_aspect: bool,
623}
624
625impl IntoToken for Cn {
626    fn into_token(self) -> Token {
627        Token::H(HForm {
628            sequence: match self.is_aspect {
629                false => HFormSequence::S0,
630                true => HFormSequence::SW,
631            },
632            degree: match self.mcs {
633                ArbitraryMoodOrCaseScope::FAC_CCN => HFormDegree::D1,
634                ArbitraryMoodOrCaseScope::SUB_CCA => HFormDegree::D2,
635                ArbitraryMoodOrCaseScope::ASM_CCS => HFormDegree::D3,
636                ArbitraryMoodOrCaseScope::SPC_CCQ => HFormDegree::D4,
637                ArbitraryMoodOrCaseScope::COU_CCP => HFormDegree::D5,
638                ArbitraryMoodOrCaseScope::HYP_CCV => HFormDegree::D6,
639            },
640        })
641    }
642}
643
644impl FromTokens for Cn {
645    fn parse_volatile(stream: &mut TokenStream, _: FromTokenFlags) -> Result<Self, ParseError> {
646        let cn: HForm = stream.next().ok_or(ParseError::ExpectedCn)?;
647
648        let is_aspect = matches!(cn.sequence, HFormSequence::SW | HFormSequence::SY);
649
650        let mcs = match cn.degree {
651            HFormDegree::D1 => ArbitraryMoodOrCaseScope::FAC_CCN,
652            HFormDegree::D2 => ArbitraryMoodOrCaseScope::SUB_CCA,
653            HFormDegree::D3 => ArbitraryMoodOrCaseScope::ASM_CCS,
654            HFormDegree::D4 => ArbitraryMoodOrCaseScope::SPC_CCQ,
655            HFormDegree::D5 => ArbitraryMoodOrCaseScope::COU_CCP,
656            HFormDegree::D6 => ArbitraryMoodOrCaseScope::HYP_CCV,
657        };
658
659        Ok(Self { mcs, is_aspect })
660    }
661}
662
663/// A parsed Cm that marks its corresponding Vn as aspectual or non-aspectual.
664#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
665pub struct Cm {
666    /// Whether the corresponding Vn is an aspect.
667    pub is_aspect: bool,
668}
669
670impl IntoToken for Cm {
671    fn into_token(self) -> Token {
672        Token::C(OwnedConsonantForm(
673            if self.is_aspect { "ň" } else { "n" }.to_owned(),
674        ))
675    }
676}
677
678impl FromTokens for Cm {
679    fn parse_volatile(stream: &mut TokenStream, _: FromTokenFlags) -> Result<Self, ParseError> {
680        match stream.next_any() {
681            Some(Token::C(OwnedConsonantForm(source))) => match &source[..] {
682                "n" => Ok(Self { is_aspect: false }),
683                "ň" => Ok(Self { is_aspect: true }),
684                _ => Err(ParseError::ExpectedCm),
685            },
686            _ => Err(ParseError::ExpectedCm),
687        }
688    }
689}
690
691/// A VnCn pair.
692#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
693pub struct VnCn {
694    /// The Vn of this pair.
695    pub vn: Vn,
696
697    /// The Cn of this pair.
698    pub cn: ArbitraryMoodOrCaseScope,
699}
700
701impl FromTokens for VnCn {
702    fn parse_volatile(stream: &mut TokenStream, flags: FromTokenFlags) -> Result<Self, ParseError> {
703        let vn: VowelForm = stream.next().ok_or(ParseError::ExpectedVn)?;
704
705        if vn.has_glottal_stop && !flags.matches(FromTokenFlags::PERMISSIVE) {
706            return Err(ParseError::GlottalizedVn);
707        }
708
709        let cn: Cn = stream.parse(flags)?;
710
711        Ok(Self {
712            vn: Vn::from_vowel_form(vn, cn.is_aspect).ok_or(ParseError::ExpectedVn)?,
713            cn: cn.mcs,
714        })
715    }
716}
717
718impl IntoTokens for VnCn {
719    fn append_tokens_to(&self, list: &mut TokenList, _flags: IntoTokensFlags) {
720        match self.vn.as_non_aspectual_vn() {
721            Ok(non_aspectual) => {
722                list.push(non_aspectual);
723
724                list.push(Cn {
725                    mcs: self.cn,
726                    is_aspect: false,
727                });
728            }
729            Err(aspect) => {
730                list.push(aspect);
731                list.push(Cn {
732                    mcs: self.cn,
733                    is_aspect: true,
734                });
735            }
736        }
737    }
738}
739
740/// A VnCm pair.
741#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
742pub struct VnCm {
743    /// The Vn of this pair.
744    pub vn: Vn,
745}
746
747impl FromTokens for VnCm {
748    fn parse_volatile(stream: &mut TokenStream, flags: FromTokenFlags) -> Result<Self, ParseError> {
749        let vn: VowelForm = stream.next().ok_or(ParseError::ExpectedVn)?;
750
751        if vn.has_glottal_stop && !flags.matches(FromTokenFlags::PERMISSIVE) {
752            return Err(ParseError::GlottalizedVn);
753        }
754
755        let cm: Cm = stream.parse(flags)?;
756
757        Ok(Self {
758            vn: Vn::from_vowel_form(vn, cm.is_aspect).ok_or(ParseError::ExpectedVn)?,
759        })
760    }
761}
762
763impl IntoTokens for VnCm {
764    fn append_tokens_to(&self, list: &mut TokenList, _flags: IntoTokensFlags) {
765        match self.vn.as_non_aspectual_vn() {
766            Ok(non_aspectual) => {
767                list.push(non_aspectual);
768                list.push(Cm { is_aspect: false });
769            }
770            Err(aspect) => {
771                list.push(aspect);
772                list.push(Cm { is_aspect: true });
773            }
774        }
775    }
776}
777
778/// A VnCn pair with an optional glottal stop.
779#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
780pub struct VnCnWithGlottalStop {
781    /// The Vn of this pair.
782    pub vn: Vn,
783
784    /// Whether the Vn form had a glottal stop.
785    pub has_glottal_stop: bool,
786
787    /// The Cn of this pair.
788    pub cn: ArbitraryMoodOrCaseScope,
789}
790
791impl FromTokens for VnCnWithGlottalStop {
792    fn parse_volatile(stream: &mut TokenStream, flags: FromTokenFlags) -> Result<Self, ParseError> {
793        let vn: VowelForm = stream.next().ok_or(ParseError::ExpectedVn)?;
794
795        let cn: Cn = stream.parse(flags)?;
796
797        Ok(Self {
798            vn: Vn::from_vowel_form(vn, cn.is_aspect).ok_or(ParseError::ExpectedVn)?,
799            has_glottal_stop: vn.has_glottal_stop,
800            cn: cn.mcs,
801        })
802    }
803}
804
805impl IntoTokens for VnCnWithGlottalStop {
806    fn append_tokens_to(&self, list: &mut TokenList, _flags: IntoTokensFlags) {
807        match self.vn.as_non_aspectual_vn() {
808            Ok(vn) => {
809                let mut vn = vn.into_vowel_form();
810                vn.has_glottal_stop = self.has_glottal_stop;
811                list.push(vn);
812                list.push(Cn {
813                    mcs: self.cn,
814                    is_aspect: false,
815                });
816            }
817            Err(vn) => {
818                let mut vn = vn.into_vowel_form();
819                vn.has_glottal_stop = self.has_glottal_stop;
820                list.push(vn);
821                list.push(Cn {
822                    mcs: self.cn,
823                    is_aspect: true,
824                });
825            }
826        }
827    }
828}
829
830/// A VxCs pair with no glottal stop.
831#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
832pub struct VxCs {
833    /// The affix represented by this VxCs form.
834    pub affix: RegularAffix,
835}
836
837impl FromTokens for VxCs {
838    fn parse_volatile(stream: &mut TokenStream, flags: FromTokenFlags) -> Result<Self, ParseError> {
839        let vx: VowelForm = stream.next().ok_or(ParseError::ExpectedVx)?;
840        if vx.has_glottal_stop && !flags.matches(FromTokenFlags::PERMISSIVE) {
841            return Err(ParseError::GlottalizedVx);
842        }
843        let cs = stream.next_cs().ok_or(ParseError::ExpectedCs)?;
844        Ok(VxCs {
845            affix: RegularAffix::from_vxcs(vx, &cs)?,
846        })
847    }
848}
849
850/// A VxCs pair with an optional glottal stop.
851#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
852pub struct VxCsWithGlottalStop {
853    /// The affix represented by this VxCs form.
854    pub affix: RegularAffix,
855
856    /// Whether the Vx form had a glottal stop.
857    pub has_glottal_stop: bool,
858}
859
860impl FromTokens for VxCsWithGlottalStop {
861    fn parse_volatile(stream: &mut TokenStream, flags: FromTokenFlags) -> Result<Self, ParseError> {
862        let vx: VowelForm = stream.next().ok_or(ParseError::ExpectedVx)?;
863        if vx.has_glottal_stop && !flags.matches(FromTokenFlags::PERMISSIVE) {
864            return Err(ParseError::GlottalizedVx);
865        }
866        let cs = stream.next_cs().ok_or(ParseError::ExpectedCs)?;
867        Ok(Self {
868            affix: RegularAffix::from_vxcs(vx, &cs)?,
869            has_glottal_stop: vx.has_glottal_stop,
870        })
871    }
872}
873
874/// A CsVx pair with an optional glottal stop.
875#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
876pub struct CsVxWithGlottalStop {
877    /// The affix represented by this CsVx form.
878    pub affix: RegularAffix,
879
880    /// Whether the Vx form had a glottal stop.
881    pub has_glottal_stop: bool,
882}
883
884impl FromTokens for CsVxWithGlottalStop {
885    fn parse_volatile(stream: &mut TokenStream, flags: FromTokenFlags) -> Result<Self, ParseError> {
886        let cs = stream.next_cs().ok_or(ParseError::ExpectedCs)?.to_owned();
887        let vx: VowelForm = stream.next().ok_or(ParseError::ExpectedVx)?;
888        if vx.has_glottal_stop && !flags.matches(FromTokenFlags::PERMISSIVE) {
889            return Err(ParseError::GlottalizedVx);
890        }
891        Ok(Self {
892            affix: RegularAffix::from_vxcs(vx, &cs)?,
893            has_glottal_stop: vx.has_glottal_stop,
894        })
895    }
896}
897
898/// A Vs form.
899#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
900pub struct Vs {
901    /// The scope marked by this Vs form.
902    pub scope: AffixualAdjunctScope,
903}
904
905impl FromTokens for Vs {
906    fn parse_volatile(stream: &mut TokenStream, flags: FromTokenFlags) -> Result<Self, ParseError> {
907        let Some(vowel_form): Option<VowelForm> = stream.next() else {
908            return Ok(Vs {
909                scope: AffixualAdjunctScope::VDom,
910            });
911        };
912
913        if vowel_form.has_glottal_stop && !flags.matches(FromTokenFlags::PERMISSIVE) {
914            return Err(ParseError::GlottalizedVs);
915        }
916
917        if vowel_form.sequence != VowelFormSequence::S1 {
918            return Err(ParseError::ExpectedVs);
919        }
920
921        match vowel_form.degree {
922            VowelFormDegree::D1 => Ok(Vs {
923                scope: AffixualAdjunctScope::VDom,
924            }),
925            VowelFormDegree::D9 => Ok(Vs {
926                scope: AffixualAdjunctScope::VSub,
927            }),
928            VowelFormDegree::D3 => Ok(Vs {
929                scope: AffixualAdjunctScope::VIIDom,
930            }),
931            VowelFormDegree::D4 => Ok(Vs {
932                scope: AffixualAdjunctScope::VIISub,
933            }),
934            VowelFormDegree::D7 => Ok(Vs {
935                scope: AffixualAdjunctScope::Formative,
936            }),
937            VowelFormDegree::D6 => Ok(Vs {
938                scope: AffixualAdjunctScope::OverAdj,
939            }),
940            _ => Err(ParseError::ExpectedVs),
941        }
942    }
943}
944
945impl IntoVowelForm for Vs {
946    fn into_vowel_form(self) -> VowelForm {
947        VowelForm {
948            has_glottal_stop: false,
949            sequence: VowelFormSequence::S1,
950            degree: match self.scope {
951                AffixualAdjunctScope::VDom => VowelFormDegree::D1,
952                AffixualAdjunctScope::VSub => VowelFormDegree::D9,
953                AffixualAdjunctScope::VIIDom => VowelFormDegree::D3,
954                AffixualAdjunctScope::VIISub => VowelFormDegree::D4,
955                AffixualAdjunctScope::Formative => VowelFormDegree::D7,
956                AffixualAdjunctScope::OverAdj => VowelFormDegree::D6,
957            },
958        }
959    }
960}
961
962/// A Vz form.
963#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
964pub struct Vz {
965    /// The scope marked by this Vz form.
966    pub scope: Option<AffixualAdjunctScope>,
967}
968
969impl FromTokens for Vz {
970    fn parse_volatile(stream: &mut TokenStream, flags: FromTokenFlags) -> Result<Self, ParseError> {
971        let Some(vowel_form): Option<VowelForm> = stream.next() else {
972            return Ok(Vz { scope: None });
973        };
974
975        if vowel_form.has_glottal_stop && !flags.matches(FromTokenFlags::PERMISSIVE) {
976            return Err(ParseError::GlottalizedVz);
977        }
978
979        if matches!(
980            vowel_form,
981            VowelForm {
982                has_glottal_stop: false,
983                sequence: VowelFormSequence::S2,
984                degree: VowelFormDegree::D1
985            }
986        ) {
987            return Ok(Vz { scope: None });
988        }
989
990        if vowel_form.sequence != VowelFormSequence::S1 {
991            return Err(ParseError::ExpectedVz);
992        }
993
994        match vowel_form.degree {
995            VowelFormDegree::D1 => Ok(Vz {
996                scope: Some(AffixualAdjunctScope::VDom),
997            }),
998            VowelFormDegree::D9 => Ok(Vz {
999                scope: Some(AffixualAdjunctScope::VSub),
1000            }),
1001            VowelFormDegree::D3 => Ok(Vz {
1002                scope: Some(AffixualAdjunctScope::VIIDom),
1003            }),
1004            VowelFormDegree::D4 => Ok(Vz {
1005                scope: Some(AffixualAdjunctScope::VIISub),
1006            }),
1007            VowelFormDegree::D7 => Ok(Vz {
1008                scope: Some(AffixualAdjunctScope::Formative),
1009            }),
1010            VowelFormDegree::D6 => Ok(Vz {
1011                scope: Some(AffixualAdjunctScope::OverAdj),
1012            }),
1013            _ => Err(ParseError::ExpectedVz),
1014        }
1015    }
1016}
1017
1018impl IntoVowelForm for Vz {
1019    fn into_vowel_form(self) -> VowelForm {
1020        match self.scope {
1021            None => VowelForm {
1022                has_glottal_stop: false,
1023                sequence: VowelFormSequence::S2,
1024                degree: VowelFormDegree::D1,
1025            },
1026            Some(AffixualAdjunctScope::VDom) => VowelForm {
1027                has_glottal_stop: false,
1028                sequence: VowelFormSequence::S1,
1029                degree: VowelFormDegree::D1,
1030            },
1031
1032            Some(AffixualAdjunctScope::VSub) => VowelForm {
1033                has_glottal_stop: false,
1034                sequence: VowelFormSequence::S1,
1035                degree: VowelFormDegree::D9,
1036            },
1037
1038            Some(AffixualAdjunctScope::VIIDom) => VowelForm {
1039                has_glottal_stop: false,
1040                sequence: VowelFormSequence::S1,
1041                degree: VowelFormDegree::D3,
1042            },
1043
1044            Some(AffixualAdjunctScope::VIISub) => VowelForm {
1045                has_glottal_stop: false,
1046                sequence: VowelFormSequence::S1,
1047                degree: VowelFormDegree::D4,
1048            },
1049
1050            Some(AffixualAdjunctScope::Formative) => VowelForm {
1051                has_glottal_stop: false,
1052                sequence: VowelFormSequence::S1,
1053                degree: VowelFormDegree::D7,
1054            },
1055
1056            Some(AffixualAdjunctScope::OverAdj) => VowelForm {
1057                has_glottal_stop: false,
1058                sequence: VowelFormSequence::S1,
1059                degree: VowelFormDegree::D6,
1060            },
1061        }
1062    }
1063}
1064
1065/// A CsVxCz triplet. These must be parsed together because the value of Cz is influenced by whether
1066/// the Vx form has a glottal stop or not.
1067#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
1068pub struct CsVxCz {
1069    /// The affix marked by the CsVx pair.
1070    pub affix: RegularAffix,
1071
1072    /// The scope marked by the Cz form.
1073    pub scope: AffixualAdjunctScope,
1074}
1075
1076impl FromTokens for CsVxCz {
1077    fn parse_volatile(stream: &mut TokenStream, _: FromTokenFlags) -> Result<Self, ParseError> {
1078        let cs = stream.next_cs().ok_or(ParseError::ExpectedCs)?.to_owned();
1079        let vx: VowelForm = stream.next().ok_or(ParseError::ExpectedVx)?;
1080        let cz: HForm = stream.next().ok_or(ParseError::ExpectedCz)?;
1081        Ok(Self {
1082            affix: RegularAffix::from_vxcs(vx, &cs)?,
1083            scope: match vx.has_glottal_stop {
1084                false => match cz {
1085                    HForm::H => AffixualAdjunctScope::VDom,
1086                    HForm::HW => AffixualAdjunctScope::Formative,
1087                    _ => return Err(ParseError::ExpectedCz),
1088                },
1089                true => match cz {
1090                    HForm::H => AffixualAdjunctScope::VSub,
1091                    HForm::HL => AffixualAdjunctScope::VIIDom,
1092                    HForm::HR => AffixualAdjunctScope::VIISub,
1093                    HForm::HW => AffixualAdjunctScope::OverAdj,
1094                    _ => return Err(ParseError::ExpectedCz),
1095                },
1096            },
1097        })
1098    }
1099}
1100
1101/// A case form found at the end of a combination referential.
1102#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
1103pub struct Vc2 {
1104    /// The case of this Vc2 form.
1105    pub case: Option<Case>,
1106}
1107
1108impl FromTokens for Vc2 {
1109    fn parse_volatile(
1110        stream: &mut TokenStream,
1111        _flags: FromTokenFlags,
1112    ) -> Result<Self, ParseError> {
1113        match stream.next_any() {
1114            Some(Token::V(VowelForm {
1115                has_glottal_stop: false,
1116                sequence: VowelFormSequence::S1,
1117                degree: VowelFormDegree::D1,
1118            })) => Ok(Vc2 { case: None }),
1119
1120            Some(Token::V(vc)) => Ok(Vc2 {
1121                case: Some(Case::from_vc(*vc)?),
1122            }),
1123
1124            Some(Token::ÜA) => Ok(Vc2 {
1125                case: Some(Case::THM),
1126            }),
1127
1128            None => Ok(Vc2 { case: None }),
1129
1130            _ => Err(ParseError::ExpectedVc2),
1131        }
1132    }
1133}