// morphology-ru.el - Russian morphology: noun declension and verb conjugation. // // Russian is a fusional language with: // - 6 grammatical cases: nominative, accusative, genitive, dative, // instrumental, prepositional // - 3 genders: masculine (m), feminine (f), neuter (n) // - 2 numbers: singular (sg), plural (pl) // - Aspect: perfective / imperfective (caller feature) // - No articles // - Cyrillic script (El strings are Unicode-transparent) // // Conventions: // gender: "m" | "f" | "n" // case: "nom" | "acc" | "gen" | "dat" | "ins" | "pre" // number: "sg" | "pl" // person: "1" | "2" | "3" // tense: "present" | "past" | "future" // // Stem-type classification: // "hard" - stem ends in a hard consonant // "soft" - stem ends in a soft consonant (+ ь) or a hushing consonant // "special" - lexically irregular // // Depends on: language-profile (str_eq, str_len, str_slice, str_drop_last, // str_ends_with) // ── Gender heuristic from ending ────────────────────────────────────────────── // // Russian gender is mostly predictable from the nominative singular ending: // ends in consonant → masculine (стол, дом) // ends in -ь → masculine or feminine (must be lexical) // ends in -а / -я → feminine (женщина, земля) // ends in -о / -е → neuter (слово, море) // // The heuristic returns the most probable gender. Caller should override // for known exceptions (путь, рубль are masc despite -ь). import "morphology.el" fn ru_gender(noun: String) -> String { let n: Int = str_len(noun) if n == 0 { return "m" } let last: String = str_slice(noun, n - 1, n) // Neuter: -о or -е if str_eq(last, "о") { return "n" } if str_eq(last, "е") { return "n" } if str_eq(last, "ё") { return "n" } // Feminine: -а or -я if str_eq(last, "а") { return "f" } if str_eq(last, "я") { return "f" } // Soft sign: ambiguous — default feminine (most common case) if str_eq(last, "ь") { return "f" } // Consonant or й: masculine return "m" } // ── Stem-type classifier ────────────────────────────────────────────────────── // // "hard" — ends in a plain consonant (стол) or -а (женщина) or -о (слово) // "soft" — ends in -ь, -й, -я, -е, -ие, -ия // "sibilant" — ends in ж/ш/ч/щ (triggers spelling rules) fn ru_stem_type(noun: String, gender: String) -> String { let n: Int = str_len(noun) if n == 0 { return "hard" } let last: String = str_slice(noun, n - 1, n) if str_eq(last, "ь") { return "soft" } if str_eq(last, "й") { return "soft" } if str_eq(last, "я") { return "soft" } if str_eq(last, "е") { return "soft" } // Sibilants (hushing consonants: ж ш ч щ) — use sibilant rules if str_eq(last, "ж") { return "sibilant" } if str_eq(last, "ш") { return "sibilant" } if str_eq(last, "ч") { return "sibilant" } if str_eq(last, "щ") { return "sibilant" } return "hard" } // ── Noun declension ─────────────────────────────────────────────────────────── // // Main entry point. Handles irregulars by lemma, then dispatches to the // paradigm appropriate for (gender × stem_type). fn ru_noun_case(noun: String, gender: String, gram_case: String, number: String) -> String { // ── Lexical irregulars ────────────────────────────────────────────────── // человек (person) — plural suppletive: люди if str_eq(noun, "человек") { if str_eq(number, "sg") { if str_eq(gram_case, "nom") { return "человек" } if str_eq(gram_case, "acc") { return "человека" } if str_eq(gram_case, "gen") { return "человека" } if str_eq(gram_case, "dat") { return "человеку" } if str_eq(gram_case, "ins") { return "человеком" } if str_eq(gram_case, "pre") { return "человеке" } } // plural: люди paradigm if str_eq(gram_case, "nom") { return "люди" } if str_eq(gram_case, "acc") { return "людей" } if str_eq(gram_case, "gen") { return "людей" } if str_eq(gram_case, "dat") { return "людям" } if str_eq(gram_case, "ins") { return "людьми" } if str_eq(gram_case, "pre") { return "людях" } return "люди" } // ребёнок (child) — plural suppletive: дети if str_eq(noun, "ребёнок") { if str_eq(number, "sg") { if str_eq(gram_case, "nom") { return "ребёнок" } if str_eq(gram_case, "acc") { return "ребёнка" } if str_eq(gram_case, "gen") { return "ребёнка" } if str_eq(gram_case, "dat") { return "ребёнку" } if str_eq(gram_case, "ins") { return "ребёнком" } if str_eq(gram_case, "pre") { return "ребёнке" } } if str_eq(gram_case, "nom") { return "дети" } if str_eq(gram_case, "acc") { return "детей" } if str_eq(gram_case, "gen") { return "детей" } if str_eq(gram_case, "dat") { return "детям" } if str_eq(gram_case, "ins") { return "детьми" } if str_eq(gram_case, "pre") { return "детях" } return "дети" } // время (time) — neuter in -мя (heteroclitic declension) if str_eq(noun, "время") { if str_eq(number, "sg") { if str_eq(gram_case, "nom") { return "время" } if str_eq(gram_case, "acc") { return "время" } if str_eq(gram_case, "gen") { return "времени" } if str_eq(gram_case, "dat") { return "времени" } if str_eq(gram_case, "ins") { return "временем" } if str_eq(gram_case, "pre") { return "времени" } } if str_eq(gram_case, "nom") { return "времена" } if str_eq(gram_case, "acc") { return "времена" } if str_eq(gram_case, "gen") { return "времён" } if str_eq(gram_case, "dat") { return "временам" } if str_eq(gram_case, "ins") { return "временами" } if str_eq(gram_case, "pre") { return "временах" } return "времена" } // имя (name) — neuter in -мя if str_eq(noun, "имя") { if str_eq(number, "sg") { if str_eq(gram_case, "nom") { return "имя" } if str_eq(gram_case, "acc") { return "имя" } if str_eq(gram_case, "gen") { return "имени" } if str_eq(gram_case, "dat") { return "имени" } if str_eq(gram_case, "ins") { return "именем" } if str_eq(gram_case, "pre") { return "имени" } } if str_eq(gram_case, "nom") { return "имена" } if str_eq(gram_case, "acc") { return "имена" } if str_eq(gram_case, "gen") { return "имён" } if str_eq(gram_case, "dat") { return "именам" } if str_eq(gram_case, "ins") { return "именами" } if str_eq(gram_case, "pre") { return "именах" } return "имена" } // путь (way/path) — masculine soft with unique instrumental if str_eq(noun, "путь") { if str_eq(number, "sg") { if str_eq(gram_case, "nom") { return "путь" } if str_eq(gram_case, "acc") { return "путь" } if str_eq(gram_case, "gen") { return "пути" } if str_eq(gram_case, "dat") { return "пути" } if str_eq(gram_case, "ins") { return "путём" } if str_eq(gram_case, "pre") { return "пути" } } if str_eq(gram_case, "nom") { return "пути" } if str_eq(gram_case, "acc") { return "пути" } if str_eq(gram_case, "gen") { return "путей" } if str_eq(gram_case, "dat") { return "путям" } if str_eq(gram_case, "ins") { return "путями" } if str_eq(gram_case, "pre") { return "путях" } return "пути" } // мать (mother) — feminine with -ер- insert if str_eq(noun, "мать") { if str_eq(number, "sg") { if str_eq(gram_case, "nom") { return "мать" } if str_eq(gram_case, "acc") { return "мать" } if str_eq(gram_case, "gen") { return "матери" } if str_eq(gram_case, "dat") { return "матери" } if str_eq(gram_case, "ins") { return "матерью" } if str_eq(gram_case, "pre") { return "матери" } } if str_eq(gram_case, "nom") { return "матери" } if str_eq(gram_case, "acc") { return "матерей" } if str_eq(gram_case, "gen") { return "матерей" } if str_eq(gram_case, "dat") { return "матерям" } if str_eq(gram_case, "ins") { return "матерями" } if str_eq(gram_case, "pre") { return "матерях" } return "матери" } // дочь (daughter) — same insert pattern if str_eq(noun, "дочь") { if str_eq(number, "sg") { if str_eq(gram_case, "nom") { return "дочь" } if str_eq(gram_case, "acc") { return "дочь" } if str_eq(gram_case, "gen") { return "дочери" } if str_eq(gram_case, "dat") { return "дочери" } if str_eq(gram_case, "ins") { return "дочерью" } if str_eq(gram_case, "pre") { return "дочери" } } if str_eq(gram_case, "nom") { return "дочери" } if str_eq(gram_case, "acc") { return "дочерей" } if str_eq(gram_case, "gen") { return "дочерей" } if str_eq(gram_case, "dat") { return "дочерям" } if str_eq(gram_case, "ins") { return "дочерями" } if str_eq(gram_case, "pre") { return "дочерях" } return "дочери" } // ── Regular paradigm dispatch ──────────────────────────────────────────── let stype: String = ru_stem_type(noun, gender) return ru_decline_regular(noun, gender, stype, gram_case, number) } // ── Regular declension paradigms ───────────────────────────────────────────── // // Handles hard, soft, and sibilant stems across all three genders. fn ru_decline_regular(noun: String, gender: String, stype: String, gram_case: String, number: String) -> String { if str_eq(gender, "m") { return ru_decline_masc(noun, stype, gram_case, number) } if str_eq(gender, "f") { return ru_decline_fem(noun, stype, gram_case, number) } return ru_decline_neut(noun, stype, gram_case, number) } // ── Masculine declension ────────────────────────────────────────────────────── // // Hard (стол): stem = noun itself (zero ending in nom sg) // Sg: стол, стол, стола, столу, столом, столе // Pl: столы, столы, столов, столам, столами, столах // // Soft ending in -й (трамвай → трамва-): // Sg: трамвай, трамвай, трамвая, трамваю, трамваем, трамвае // Pl: трамваи, трамваи, трамваев, трамваям, трамваями, трамваях // // Soft ending in -ь (рубль → рубл-): // Sg: рубль, рубль, рубля, рублю, рублём, рубле // Pl: рубли, рубли, рублей, рублям, рублями, рублях // // Sibilant (нож → нож-): // Same as hard but ins sg = -ом only when stressed; unstressed = -ем // Here we assume stressed stem and use -ом. fn ru_decline_masc(noun: String, stype: String, gram_case: String, number: String) -> String { let n: Int = str_len(noun) if str_eq(stype, "soft") { let last: String = str_slice(noun, n - 1, n) // Stem in -й (трамвай) if str_eq(last, "й") { let stem: String = str_drop_last(noun, 1) if str_eq(number, "sg") { if str_eq(gram_case, "nom") { return noun } if str_eq(gram_case, "acc") { return noun } // inanimate if str_eq(gram_case, "gen") { return stem + "я" } if str_eq(gram_case, "dat") { return stem + "ю" } if str_eq(gram_case, "ins") { return stem + "ем" } if str_eq(gram_case, "pre") { return stem + "е" } return noun } if str_eq(gram_case, "nom") { return stem + "и" } if str_eq(gram_case, "acc") { return stem + "и" } if str_eq(gram_case, "gen") { return stem + "ев" } if str_eq(gram_case, "dat") { return stem + "ям" } if str_eq(gram_case, "ins") { return stem + "ями" } if str_eq(gram_case, "pre") { return stem + "ях" } return stem + "и" } // Stem in -ь (рубль, гость) if str_eq(last, "ь") { let stem: String = str_drop_last(noun, 1) if str_eq(number, "sg") { if str_eq(gram_case, "nom") { return noun } if str_eq(gram_case, "acc") { return noun } if str_eq(gram_case, "gen") { return stem + "я" } if str_eq(gram_case, "dat") { return stem + "ю" } if str_eq(gram_case, "ins") { return stem + "ём" } if str_eq(gram_case, "pre") { return stem + "е" } return noun } if str_eq(gram_case, "nom") { return stem + "и" } if str_eq(gram_case, "acc") { return stem + "и" } if str_eq(gram_case, "gen") { return stem + "ей" } if str_eq(gram_case, "dat") { return stem + "ям" } if str_eq(gram_case, "ins") { return stem + "ями" } if str_eq(gram_case, "pre") { return stem + "ях" } return stem + "и" } } // Hard and sibilant: stem = noun (zero ending in nom sg) let stem: String = noun if str_eq(number, "sg") { if str_eq(gram_case, "nom") { return stem } if str_eq(gram_case, "acc") { return stem } // inanimate; animate = gen if str_eq(gram_case, "gen") { return stem + "а" } if str_eq(gram_case, "dat") { return stem + "у" } if str_eq(gram_case, "ins") { return stem + "ом" } if str_eq(gram_case, "pre") { return stem + "е" } return stem } // Plural if str_eq(gram_case, "nom") { return stem + "ы" } if str_eq(gram_case, "acc") { return stem + "ы" } if str_eq(gram_case, "gen") { return stem + "ов" } if str_eq(gram_case, "dat") { return stem + "ам" } if str_eq(gram_case, "ins") { return stem + "ами" } if str_eq(gram_case, "pre") { return stem + "ах" } return stem + "ы" } // ── Feminine declension ─────────────────────────────────────────────────────── // // Hard in -а (женщина): // Sg: женщина, женщину, женщины, женщине, женщиной, женщине // Pl: женщины, женщин, женщин, женщинам, женщинами, женщинах // // Soft in -я (земля): // Sg: земля, землю, земли, земле, землёй, земле // Pl: земли, земли, земель, землям, землями, землях // // Soft in -ь (тетрадь, ночь): // Sg: тетрадь, тетрадь, тетради, тетради, тетрадью, тетради // Pl: тетради, тетради, тетрадей, тетрадям, тетрадями, тетрадях fn ru_decline_fem(noun: String, stype: String, gram_case: String, number: String) -> String { let n: Int = str_len(noun) let last: String = str_slice(noun, n - 1, n) // Soft in -ь if str_eq(last, "ь") { let stem: String = str_drop_last(noun, 1) if str_eq(number, "sg") { if str_eq(gram_case, "nom") { return noun } if str_eq(gram_case, "acc") { return noun } if str_eq(gram_case, "gen") { return stem + "и" } if str_eq(gram_case, "dat") { return stem + "и" } if str_eq(gram_case, "ins") { return stem + "ью" } if str_eq(gram_case, "pre") { return stem + "и" } return noun } if str_eq(gram_case, "nom") { return stem + "и" } if str_eq(gram_case, "acc") { return stem + "и" } if str_eq(gram_case, "gen") { return stem + "ей" } if str_eq(gram_case, "dat") { return stem + "ям" } if str_eq(gram_case, "ins") { return stem + "ями" } if str_eq(gram_case, "pre") { return stem + "ях" } return stem + "и" } // Soft in -я (земля, семья) if str_eq(last, "я") { let stem: String = str_drop_last(noun, 1) if str_eq(number, "sg") { if str_eq(gram_case, "nom") { return noun } if str_eq(gram_case, "acc") { return stem + "ю" } if str_eq(gram_case, "gen") { return stem + "и" } if str_eq(gram_case, "dat") { return stem + "е" } if str_eq(gram_case, "ins") { return stem + "ей" } if str_eq(gram_case, "pre") { return stem + "е" } return noun } if str_eq(gram_case, "nom") { return stem + "и" } if str_eq(gram_case, "acc") { return stem + "и" } // Genitive plural: zero ending (with possible fleeting vowel) — use -ей for soft if str_eq(gram_case, "gen") { return stem + "ей" } if str_eq(gram_case, "dat") { return stem + "ям" } if str_eq(gram_case, "ins") { return stem + "ями" } if str_eq(gram_case, "pre") { return stem + "ях" } return stem + "и" } // Hard in -а (женщина, страна, рука) if str_eq(last, "а") { let stem: String = str_drop_last(noun, 1) if str_eq(number, "sg") { if str_eq(gram_case, "nom") { return noun } if str_eq(gram_case, "acc") { return stem + "у" } if str_eq(gram_case, "gen") { return stem + "ы" } if str_eq(gram_case, "dat") { return stem + "е" } if str_eq(gram_case, "ins") { return stem + "ой" } if str_eq(gram_case, "pre") { return stem + "е" } return noun } // Plural: gen pl = zero ending (stem only) for most -а feminines if str_eq(gram_case, "nom") { return stem + "ы" } if str_eq(gram_case, "acc") { return stem + "ы" } if str_eq(gram_case, "gen") { return stem } if str_eq(gram_case, "dat") { return stem + "ам" } if str_eq(gram_case, "ins") { return stem + "ами" } if str_eq(gram_case, "pre") { return stem + "ах" } return stem + "ы" } // Fallback: treat as hard -а (unexpected ending) return noun } // ── Neuter declension ───────────────────────────────────────────────────────── // // Hard in -о (слово): // Sg: слово, слово, слова, слову, словом, слове // Pl: слова, слова, слов, словам, словами, словах // // Soft in -е (море, поле): // Sg: море, море, моря, морю, морем, море // Pl: моря, моря, морей, морям, морями, морях // // Soft in -ие (здание): // Sg: здание, здание, здания, зданию, зданием, здании // Pl: здания, здания, зданий, зданиям, зданиями, зданиях fn ru_decline_neut(noun: String, stype: String, gram_case: String, number: String) -> String { let n: Int = str_len(noun) let last: String = str_slice(noun, n - 1, n) // Nouns in -ие (здание, здравоохранение) if str_ends_with(noun, "ие") { let stem: String = str_drop_last(noun, 2) if str_eq(number, "sg") { if str_eq(gram_case, "nom") { return noun } if str_eq(gram_case, "acc") { return noun } if str_eq(gram_case, "gen") { return stem + "ия" } if str_eq(gram_case, "dat") { return stem + "ию" } if str_eq(gram_case, "ins") { return stem + "ием" } if str_eq(gram_case, "pre") { return stem + "ии" } return noun } if str_eq(gram_case, "nom") { return stem + "ия" } if str_eq(gram_case, "acc") { return stem + "ия" } if str_eq(gram_case, "gen") { return stem + "ий" } if str_eq(gram_case, "dat") { return stem + "иям" } if str_eq(gram_case, "ins") { return stem + "иями" } if str_eq(gram_case, "pre") { return stem + "иях" } return stem + "ия" } // Soft in -е (море, поле) if str_eq(last, "е") { let stem: String = str_drop_last(noun, 1) if str_eq(number, "sg") { if str_eq(gram_case, "nom") { return noun } if str_eq(gram_case, "acc") { return noun } if str_eq(gram_case, "gen") { return stem + "я" } if str_eq(gram_case, "dat") { return stem + "ю" } if str_eq(gram_case, "ins") { return stem + "ем" } if str_eq(gram_case, "pre") { return noun } return noun } if str_eq(gram_case, "nom") { return stem + "я" } if str_eq(gram_case, "acc") { return stem + "я" } if str_eq(gram_case, "gen") { return stem + "ей" } if str_eq(gram_case, "dat") { return stem + "ям" } if str_eq(gram_case, "ins") { return stem + "ями" } if str_eq(gram_case, "pre") { return stem + "ях" } return stem + "я" } // Hard in -о (слово, окно, место) if str_eq(last, "о") { let stem: String = str_drop_last(noun, 1) if str_eq(number, "sg") { if str_eq(gram_case, "nom") { return noun } if str_eq(gram_case, "acc") { return noun } if str_eq(gram_case, "gen") { return stem + "а" } if str_eq(gram_case, "dat") { return stem + "у" } if str_eq(gram_case, "ins") { return stem + "ом" } if str_eq(gram_case, "pre") { return stem + "е" } return noun } if str_eq(gram_case, "nom") { return stem + "а" } if str_eq(gram_case, "acc") { return stem + "а" } // Genitive plural: zero ending (слов, окон, мест) if str_eq(gram_case, "gen") { return stem } if str_eq(gram_case, "dat") { return stem + "ам" } if str_eq(gram_case, "ins") { return stem + "ами" } if str_eq(gram_case, "pre") { return stem + "ах" } return stem + "а" } // Fallback return noun } // ── Past-tense agreement ────────────────────────────────────────────────────── // // Russian past tense agrees with the subject in gender and number. // The past stem is derived from the infinitive (drop -ть/-ти/-чь). // // Endings: masc → (bare stem), fem → -а, neut → -о, pl → -и // // Special: verbs in -чь (мочь → мог), -ти (идти → шёл) are irregular. fn ru_past_agree(verb_stem: String, gender: String, number: String) -> String { if str_eq(number, "pl") { return verb_stem + "и" } if str_eq(gender, "f") { return verb_stem + "а" } if str_eq(gender, "n") { return verb_stem + "о" } return verb_stem } // ── First-conjugation present tense ────────────────────────────────────────── // // Model: читать (чита-) → читаю, читаешь, читает, читаем, читаете, читают // Model: писать (пиш-) — stem changes in conjugation (handled by caller) // // This function takes an already-computed present stem (e.g. "чита") and // applies first-conjugation endings. // // 1sg: stem + -ю (after vowel) or -у (after consonant) // 2sg: stem + -ешь // 3sg: stem + -ет // 1pl: stem + -ем // 2pl: stem + -ете // 3pl: stem + -ют (after vowel) or -ут (after consonant) fn ru_conjugate_1st(stem: String, tense: String, person: String, number: String) -> String { if str_eq(tense, "present") { let n: Int = str_len(stem) let last: String = str_slice(stem, n - 1, n) let vowels: Bool = false let vowels = str_eq(last, "а") || str_eq(last, "е") || str_eq(last, "и") || str_eq(last, "о") || str_eq(last, "у") || str_eq(last, "ю") || str_eq(last, "я") || str_eq(last, "э") || str_eq(last, "ё") || str_eq(last, "ы") if str_eq(number, "sg") { if str_eq(person, "1") { if vowels { return stem + "ю" } return stem + "у" } if str_eq(person, "2") { return stem + "ешь" } return stem + "ет" } if str_eq(person, "1") { return stem + "ем" } if str_eq(person, "2") { return stem + "ете" } if vowels { return stem + "ют" } return stem + "ут" } // Past and future handled by ru_conjugate return stem } // ── Second-conjugation present tense ───────────────────────────────────────── // // Model: говорить (говор-) → говорю, говоришь, говорит, говорим, говорите, говорят // // 1sg: stem + -ю / -у // 2sg: stem + -ишь // 3sg: stem + -ит // 1pl: stem + -им // 2pl: stem + -ите // 3pl: stem + -ят / -ат fn ru_conjugate_2nd(stem: String, tense: String, person: String, number: String) -> String { if str_eq(tense, "present") { let n: Int = str_len(stem) let last: String = str_slice(stem, n - 1, n) let after_vowel: Bool = str_eq(last, "а") || str_eq(last, "е") || str_eq(last, "и") || str_eq(last, "о") || str_eq(last, "у") || str_eq(last, "ю") || str_eq(last, "я") || str_eq(last, "э") || str_eq(last, "ё") || str_eq(last, "ы") if str_eq(number, "sg") { if str_eq(person, "1") { if after_vowel { return stem + "ю" } return stem + "у" } if str_eq(person, "2") { return stem + "ишь" } return stem + "ит" } if str_eq(person, "1") { return stem + "им" } if str_eq(person, "2") { return stem + "ите" } if after_vowel { return stem + "ят" } return stem + "ат" } return stem } // ── Irregular verb forms ────────────────────────────────────────────────────── // // Returns the inflected form for known irregular verbs, or "" if unknown. // gender is used for past tense agreement. fn ru_irregular(verb: String, tense: String, person: String, number: String) -> String { // быть (to be) — present tense: есть (invariant) / rarely conjugated if str_eq(verb, "быть") { if str_eq(tense, "present") { // Modern Russian uses invariant "есть" for all persons in present return "есть" } // Future: буду, будешь, будет, будем, будете, будут if str_eq(tense, "future") { if str_eq(number, "sg") { if str_eq(person, "1") { return "буду" } if str_eq(person, "2") { return "будешь" } return "будет" } if str_eq(person, "1") { return "будем" } if str_eq(person, "2") { return "будете" } return "будут" } // Past: returned without gender agreement — caller must call ru_past_agree return "" } // идти (to go on foot) — present: иду, идёшь, идёт, идём, идёте, идут if str_eq(verb, "идти") { if str_eq(tense, "present") { if str_eq(number, "sg") { if str_eq(person, "1") { return "иду" } if str_eq(person, "2") { return "идёшь" } return "идёт" } if str_eq(person, "1") { return "идём" } if str_eq(person, "2") { return "идёте" } return "идут" } // Past stem: шёл (m), шла (f), шло (n), шли (pl) return "" } // ехать (to go by vehicle) — present: еду, едешь, едет, едем, едете, едут if str_eq(verb, "ехать") { if str_eq(tense, "present") { if str_eq(number, "sg") { if str_eq(person, "1") { return "еду" } if str_eq(person, "2") { return "едешь" } return "едет" } if str_eq(person, "1") { return "едем" } if str_eq(person, "2") { return "едете" } return "едут" } return "" } // говорить (to speak) — 2nd conjugation if str_eq(verb, "говорить") { if str_eq(tense, "present") { return ru_conjugate_2nd("говор", "present", person, number) } return "" } // знать (to know) — 1st conjugation, regular: зна- if str_eq(verb, "знать") { if str_eq(tense, "present") { return ru_conjugate_1st("зна", "present", person, number) } return "" } // видеть (to see) — 2nd conjugation, 1sg mutation: вижу if str_eq(verb, "видеть") { if str_eq(tense, "present") { if str_eq(number, "sg") { if str_eq(person, "1") { return "вижу" } if str_eq(person, "2") { return "видишь" } return "видит" } if str_eq(person, "1") { return "видим" } if str_eq(person, "2") { return "видите" } return "видят" } return "" } // делать (to do) — 1st conjugation: дела- if str_eq(verb, "делать") { if str_eq(tense, "present") { return ru_conjugate_1st("дела", "present", person, number) } return "" } // хотеть (to want) — mixed conjugation (1st pl, 2nd sg/3sg) if str_eq(verb, "хотеть") { if str_eq(tense, "present") { if str_eq(number, "sg") { if str_eq(person, "1") { return "хочу" } if str_eq(person, "2") { return "хочешь" } return "хочет" } if str_eq(person, "1") { return "хотим" } if str_eq(person, "2") { return "хотите" } return "хотят" } return "" } // мочь (can/be able) — present: могу, можешь, может, можем, можете, могут if str_eq(verb, "мочь") { if str_eq(tense, "present") { if str_eq(number, "sg") { if str_eq(person, "1") { return "могу" } if str_eq(person, "2") { return "можешь" } return "может" } if str_eq(person, "1") { return "можем" } if str_eq(person, "2") { return "можете" } return "могут" } return "" } // сказать (to say, pfv) — present (= future perfective): // скажу, скажешь, скажет, скажем, скажете, скажут if str_eq(verb, "сказать") { if str_eq(tense, "present") { if str_eq(number, "sg") { if str_eq(person, "1") { return "скажу" } if str_eq(person, "2") { return "скажешь" } return "скажет" } if str_eq(person, "1") { return "скажем" } if str_eq(person, "2") { return "скажете" } return "скажут" } return "" } return "" } // ── Past stem table ─────────────────────────────────────────────────────────── // // Returns the masculine past stem (without agreement ending) for known verbs. // The caller must run ru_past_agree(stem, gender, number) to get the full form. fn ru_past_stem(verb: String) -> String { // Regular -ать: drop -ть, add nothing (past stem = infinitive minus -ть) if str_eq(verb, "читать") { return "чита" } if str_eq(verb, "знать") { return "зна" } if str_eq(verb, "делать") { return "дела" } if str_eq(verb, "сказать") { return "сказа" } if str_eq(verb, "думать") { return "дума" } if str_eq(verb, "работать") { return "работа" } if str_eq(verb, "писать") { return "писа" } if str_eq(verb, "слушать") { return "слуша" } if str_eq(verb, "отвечать") { return "отвеча" } // Regular -ить/-еть if str_eq(verb, "говорить") { return "говори" } if str_eq(verb, "видеть") { return "виде" } if str_eq(verb, "смотреть") { return "смотре" } if str_eq(verb, "иметь") { return "име" } if str_eq(verb, "хотеть") { return "хоте" } // Irregular pasts if str_eq(verb, "быть") { return "бы" } // был/была/было/были if str_eq(verb, "идти") { return "шё" } // шёл — caller must handle -л suffix if str_eq(verb, "ехать") { return "еха" } // ехал if str_eq(verb, "мочь") { return "мо" } // мог (m: мог, not мол) if str_eq(verb, "нести") { return "нё" } // нёс if str_eq(verb, "вести") { return "вё" } // вёл // Derive from infinitive: drop -ть let n: Int = str_len(verb) if n > 2 { let last2: String = str_slice(verb, n - 2, n) if str_eq(last2, "ть") { return str_drop_last(verb, 2) } } return verb } // ── Unified Russian verb conjugation ───────────────────────────────────────── // // tense: "present" | "past" | "future" // person: "1" | "2" | "3" // number: "sg" | "pl" // gender: "m" | "f" | "n" — used for past tense agreement only fn ru_conjugate(verb: String, tense: String, person: String, number: String, gender: String) -> String { // ── Copula "byt" (transliterated быть) ──────────────────────────────── // Russian present tense has zero copula — "Кошка большая" not "Кошка есть большая". // Return empty string so gram_order_constituents skips the verb slot. if str_eq(verb, "byt") { if str_eq(tense, "present") { return "" } // Future: буду/будешь/будет... Past: был/была/было/были // For now return transliterated placeholder; full Unicode tables TBD. if str_eq(tense, "future") { return "budet" } return "byl" } // ── Past tense ────────────────────────────────────────────────────────── if str_eq(tense, "past") { // Special-case verbs with non-standard past forms if str_eq(verb, "идти") { if str_eq(number, "pl") { return "шли" } if str_eq(gender, "f") { return "шла" } if str_eq(gender, "n") { return "шло" } return "шёл" } if str_eq(verb, "мочь") { if str_eq(number, "pl") { return "могли" } if str_eq(gender, "f") { return "могла" } if str_eq(gender, "n") { return "могло" } return "мог" } if str_eq(verb, "нести") { if str_eq(number, "pl") { return "несли" } if str_eq(gender, "f") { return "несла" } if str_eq(gender, "n") { return "несло" } return "нёс" } if str_eq(verb, "вести") { if str_eq(number, "pl") { return "вели" } if str_eq(gender, "f") { return "вела" } if str_eq(gender, "n") { return "вело" } return "вёл" } let ps: String = ru_past_stem(verb) return ru_past_agree(ps, gender, number) } // ── Future tense (imperfective) ───────────────────────────────────────── // // Imperfective future = быть (conjugated) + infinitive if str_eq(tense, "future") { let aux: String = ru_irregular("быть", "future", person, number) return aux + " " + verb } // ── Present tense ─────────────────────────────────────────────────────── // Try the irregular table first let irr: String = ru_irregular(verb, tense, person, number) if !str_eq(irr, "") { return irr } // Derive conjugation class from infinitive ending let n: Int = str_len(verb) if n > 4 { let last4: String = str_slice(verb, n - 4, n) // -ить verbs → 2nd conjugation (most) if str_eq(last4, "ить ") { // remove trailing space if any — defensive } } // Detect -ить suffix → 2nd conjugation if str_ends_with(verb, "ить") { let stem: String = str_drop_last(verb, 3) return ru_conjugate_2nd(stem, "present", person, number) } // Detect -еть suffix → 2nd conjugation if str_ends_with(verb, "еть") { let stem: String = str_drop_last(verb, 3) return ru_conjugate_2nd(stem, "present", person, number) } // Detect -ать suffix → 1st conjugation if str_ends_with(verb, "ать") { let stem: String = str_drop_last(verb, 2) return ru_conjugate_1st(stem, "present", person, number) } // Detect -ять suffix → 1st conjugation if str_ends_with(verb, "ять") { let stem: String = str_drop_last(verb, 2) return ru_conjugate_1st(stem, "present", person, number) } // Detect -овать suffix → 1st conjugation with -у- stem if str_ends_with(verb, "овать") { let stem: String = str_drop_last(verb, 5) + "у" return ru_conjugate_1st(stem, "present", person, number) } // Detect -нуть suffix → 1st conjugation if str_ends_with(verb, "нуть") { let stem: String = str_drop_last(verb, 4) + "н" return ru_conjugate_1st(stem, "present", person, number) } // Fallback: return the infinitive return verb }