// morphology-got.el - Gothic morphology for the NLG engine. // // Implements Gothic verb conjugation and noun declension using standard // Gothic Latin romanisation. Designed as a companion to morphology.el and // called by the engine when the language profile code is "got". // // Language profile: code=got, name=Gothic, morph_type=fusional, // word_order=SOV, question_strategy=intonation, script=gothic-latin, // family=germanic. // // Historical note: Gothic is attested primarily in the 4th-century Bible // translation by Bishop Wulfila (Ulfilas). It is the earliest substantially // attested Germanic language and preserves many archaic features lost in later // branches (e.g. distinct dual, mediopassive voice, four-case system). // // Romanisation conventions used in this file: // þ — thorn (voiced/voiceless dental fricative; like "th" in "the/thin") // ƕ — hwair (Gothic hw-, like "wh" in older English "what") // q — Gothic q (labiovelar stop, like "qu") // ei — long /iː/ (Gothic digraph) // ai — short /ɛ/ (Gothic digraph, not a diphthong) // au — short /ɔ/ (Gothic digraph, not a diphthong) // Standard vowels: a e i u (short) // // Verb conjugation covered: // Tenses: present indicative active, past indicative active // Persons: first/second/third × singular/plural // Classes: weak class 1 (-jan verbs), weak class 2 (-on verbs) as // the regular productive paths // Irregulars: wisan (be), haban (have), gaggan (go), saihwan (see), // qiþan (say), niman (take) // Canonical map: "be" -> "wisan" // // Noun declension covered: // Cases: nominative, accusative, genitive, dative (all 4 Gothic cases) // Stem types: a-stem masc (paradigm: dags — day), // o-stem fem (paradigm: gibo — gift), // n-stem masc (paradigm: guma — man) // Numbers: singular, plural // // Demonstrative / article: // Gothic has no definite article proper. The demonstrative pronoun // sa (masc) / so (fem) / þata (neut) functions as a near-definite // determiner. got_noun_phrase prepends "sa" (masc) or "þo" (fem) when // definite=true. Gender must be inferred from stem class: a-stem → masc, // o-stem → fem, n-stem → masc. // // Depends on: morphology.el (str_eq, str_len, str_slice, str_ends_with) // ── String helpers ───────────────────────────────────────────────────────────── fn got_str_ends(s: String, suf: String) -> Bool { return str_ends_with(s, suf) } fn got_str_drop_last(s: String, n: Int) -> String { let len: Int = str_len(s) if n >= len { return "" } return str_slice(s, 0, len - n) } // ── Person/number slot ───────────────────────────────────────────────────────── // // Gothic has singular, dual, and plural. The dual is historically attested // for first and second person only (e.g. 1du wit, 2du jut) but is quite rare // even in the Gothic corpus. For NLG simplicity the dual is treated as plural. // // 0 = 1st singular // 1 = 2nd singular // 2 = 3rd singular // 3 = 1st plural // 4 = 2nd plural // 5 = 3rd plural fn got_slot(person: String, number: String) -> Int { if str_eq(person, "first") { if str_eq(number, "singular") { return 0 } return 3 } if str_eq(person, "second") { if str_eq(number, "singular") { return 1 } return 4 } // third person if str_eq(number, "singular") { return 2 } return 5 } // ── Canonical verb mapping ───────────────────────────────────────────────────── // // English semantic-layer labels are mapped to Gothic dictionary forms before // conjugation. Dictionary forms are the infinitive. fn got_map_canonical(verb: String) -> String { if str_eq(verb, "be") { return "wisan" } if str_eq(verb, "have") { return "haban" } if str_eq(verb, "go") { return "gaggan" } if str_eq(verb, "see") { return "saihwan" } if str_eq(verb, "say") { return "qiþan" } if str_eq(verb, "take") { return "niman" } if str_eq(verb, "come") { return "qiman" } if str_eq(verb, "give") { return "giban" } if str_eq(verb, "know") { return "kunnan" } if str_eq(verb, "want") { return "wiljan" } return verb } // ── Irregular verb: wisan (to be) ───────────────────────────────────────────── // // wisan is suppletive — the present is formed from im / is / ist / sijum… // and the past from was / wast / was / wesum… // // Present indicative active: // 1sg im 2sg is 3sg ist // 1pl sijum 2pl sijuþ 3pl sind // // Past indicative active: // 1sg was 2sg wast 3sg was // 1pl wesum 2pl wesuþ 3pl wesun fn got_wisan_present(slot: Int) -> String { if slot == 0 { return "im" } if slot == 1 { return "is" } if slot == 2 { return "ist" } if slot == 3 { return "sijum" } if slot == 4 { return "sijuþ" } return "sind" } fn got_wisan_past(slot: Int) -> String { if slot == 0 { return "was" } if slot == 1 { return "wast" } if slot == 2 { return "was" } if slot == 3 { return "wesum" } if slot == 4 { return "wesuþ" } return "wesun" } // ── Irregular verb: haban (to have) ─────────────────────────────────────────── // // Weak class 3 (-an preterite-present type). // // Present: // 1sg haba 2sg habais 3sg habaiþ // 1pl habam 2pl habaiþ 3pl haband // // Past (habida series — weak past in -ida): // 1sg habida 2sg habides 3sg habida // 1pl habidum 2pl habideþ 3pl habidedun fn got_haban_present(slot: Int) -> String { if slot == 0 { return "haba" } if slot == 1 { return "habais" } if slot == 2 { return "habaiþ" } if slot == 3 { return "habam" } if slot == 4 { return "habaiþ" } return "haband" } fn got_haban_past(slot: Int) -> String { if slot == 0 { return "habida" } if slot == 1 { return "habides" } if slot == 2 { return "habida" } if slot == 3 { return "habidum" } if slot == 4 { return "habideþ" } return "habidedun" } // ── Irregular verb: gaggan (to go) ──────────────────────────────────────────── // // Strong class 7 (reduplicating); suppletive in past with iddja- forms. // // Present: // 1sg gagga 2sg gaggis 3sg gaggiþ // 1pl gagam 2pl gagiþ 3pl gaggand // // Past (iddja series — suppletive): // 1sg iddja 2sg iddjēs 3sg iddja // 1pl iddjēdum 2pl iddjēduþ 3pl iddjēdun fn got_gaggan_present(slot: Int) -> String { if slot == 0 { return "gagga" } if slot == 1 { return "gaggis" } if slot == 2 { return "gaggiþ" } if slot == 3 { return "gagam" } if slot == 4 { return "gagiþ" } return "gaggand" } fn got_gaggan_past(slot: Int) -> String { if slot == 0 { return "iddja" } if slot == 1 { return "iddjēs" } if slot == 2 { return "iddja" } if slot == 3 { return "iddjēdum" } if slot == 4 { return "iddjēduþ" } return "iddjēdun" } // ── Irregular verb: saihwan (to see) ────────────────────────────────────────── // // Strong class 5 (saiƕan / saihwan). Present stem saihw-; past stem sahw-. // // Present: // 1sg saihwa 2sg saihwis 3sg saihwiþ // 1pl saihwam 2pl saihwiþ 3pl saihwand // // Past (ablaut: ai→a, strong past): // 1sg sahw 2sg sahwt 3sg sahw // 1pl sehwum 2pl sehwuþ 3pl sehwun fn got_saihwan_present(slot: Int) -> String { if slot == 0 { return "saihwa" } if slot == 1 { return "saihwis" } if slot == 2 { return "saihwiþ" } if slot == 3 { return "saihwam" } if slot == 4 { return "saihwiþ" } return "saihwand" } fn got_saihwan_past(slot: Int) -> String { if slot == 0 { return "sahw" } if slot == 1 { return "sahwt" } if slot == 2 { return "sahw" } if slot == 3 { return "sehwum" } if slot == 4 { return "sehwuþ" } return "sehwun" } // ── Irregular verb: qiþan (to say) ──────────────────────────────────────────── // // Strong class 5 (i-ablaut). Present stem qiþ-; past stem qaþ-. // // Present: // 1sg qiþa 2sg qiþis 3sg qiþiþ // 1pl qiþam 2pl qiþiþ 3pl qiþand // // Past (ablaut: i→a): // 1sg qaþ 2sg qast 3sg qaþ // 1pl qēþum 2pl qēþuþ 3pl qēþun fn got_qithan_present(slot: Int) -> String { if slot == 0 { return "qiþa" } if slot == 1 { return "qiþis" } if slot == 2 { return "qiþiþ" } if slot == 3 { return "qiþam" } if slot == 4 { return "qiþiþ" } return "qiþand" } fn got_qithan_past(slot: Int) -> String { if slot == 0 { return "qaþ" } if slot == 1 { return "qast" } if slot == 2 { return "qaþ" } if slot == 3 { return "qēþum" } if slot == 4 { return "qēþuþ" } return "qēþun" } // ── Irregular verb: niman (to take) ─────────────────────────────────────────── // // Strong class 4 (sonorant stem; i-ablaut in present, a-ablaut in past). // // Present: // 1sg nima 2sg nimis 3sg nimiþ // 1pl nimam 2pl nimiþ 3pl nimand // // Past (ablaut: i→a): // 1sg nam 2sg namt 3sg nam // 1pl nēmum 2pl nēmuþ 3pl nēmun fn got_niman_present(slot: Int) -> String { if slot == 0 { return "nima" } if slot == 1 { return "nimis" } if slot == 2 { return "nimiþ" } if slot == 3 { return "nimam" } if slot == 4 { return "nimiþ" } return "nimand" } fn got_niman_past(slot: Int) -> String { if slot == 0 { return "nam" } if slot == 1 { return "namt" } if slot == 2 { return "nam" } if slot == 3 { return "nēmum" } if slot == 4 { return "nēmuþ" } return "nēmun" } // ── Weak class 1 (-jan verbs): regular conjugation ──────────────────────────── // // Class 1 weak verbs are the most productive Gothic verb class. The // infinitive ends in -jan; the stem is obtained by stripping -jan. // // Present indicative active: // 1sg stem + -a (nasjan -> nasja) // 2sg stem + -is (nasjis) // 3sg stem + -iþ (nasjiþ) // 1pl stem + -jam (nasjam) // 2pl stem + -jiþ (nasjiþ) // 3pl stem + -jand (nasjand) // // Past indicative active (weak past -ida): // 1sg stem + -ida (nasjida) // 2sg stem + -ides (nasjides) // 3sg stem + -ida (nasjida) // 1pl stem + -idum (nasjidum) // 2pl stem + -ideþ (nasjideþ) // 3pl stem + -idedun (nasjidedun) fn got_wk1_present_ending(slot: Int) -> String { if slot == 0 { return "a" } if slot == 1 { return "is" } if slot == 2 { return "iþ" } if slot == 3 { return "jam" } if slot == 4 { return "jiþ" } return "jand" } fn got_wk1_past_ending(slot: Int) -> String { if slot == 0 { return "ida" } if slot == 1 { return "ides" } if slot == 2 { return "ida" } if slot == 3 { return "idum" } if slot == 4 { return "ideþ" } return "idedun" } fn got_wk1_conjugate(stem: String, tense: String, slot: Int) -> String { if str_eq(tense, "present") { return stem + got_wk1_present_ending(slot) } if str_eq(tense, "past") { return stem + got_wk1_past_ending(slot) } return stem } // ── Weak class 2 (-on verbs): regular conjugation ───────────────────────────── // // Class 2 weak verbs. Infinitive ends in -on; stem = drop -on. // This class corresponds roughly to OE -ian / OHG -on denominatives. // // Present indicative active: // 1sg stem + -o (salbon -> salbo) // 2sg stem + -os (salbos) // 3sg stem + -oþ (salboþ) // 1pl stem + -om (salbom) // 2pl stem + -oþ (salboþ) // 3pl stem + -ond (salbond) // // Past indicative active (weak past -oda): // 1sg stem + -oda (salboda) // 2sg stem + -odes (salbodes) // 3sg stem + -oda (salboda) // 1pl stem + -odum (salbodum) // 2pl stem + -odeþ (salbodeþ) // 3pl stem + -odedun (salbodedun) fn got_wk2_present_ending(slot: Int) -> String { if slot == 0 { return "o" } if slot == 1 { return "os" } if slot == 2 { return "oþ" } if slot == 3 { return "om" } if slot == 4 { return "oþ" } return "ond" } fn got_wk2_past_ending(slot: Int) -> String { if slot == 0 { return "oda" } if slot == 1 { return "odes" } if slot == 2 { return "oda" } if slot == 3 { return "odum" } if slot == 4 { return "odeþ" } return "odedun" } fn got_wk2_conjugate(stem: String, tense: String, slot: Int) -> String { if str_eq(tense, "present") { return stem + got_wk2_present_ending(slot) } if str_eq(tense, "past") { return stem + got_wk2_past_ending(slot) } return stem } // ── Infinitive class detection ───────────────────────────────────────────────── // // Identifies the verb class from the infinitive ending so that the correct // regular paradigm can be applied for verbs not in the irregular table. // // ends in -jan -> weak class 1 ("wk1") // ends in -on -> weak class 2 ("wk2") // otherwise -> default to weak class 1 (most productive class) fn got_verb_class(verb: String) -> String { if got_str_ends(verb, "jan") { return "wk1" } if got_str_ends(verb, "on") { return "wk2" } return "wk1" } // got_verb_stem: strip the infinitive suffix to expose the productive stem. // // wk1: strip -jan (3 bytes — all ASCII) // wk2: strip -on (2 bytes) fn got_verb_stem(verb: String, vclass: String) -> String { if str_eq(vclass, "wk1") { return got_str_drop_last(verb, 3) } if str_eq(vclass, "wk2") { return got_str_drop_last(verb, 2) } return got_str_drop_last(verb, 2) } // ── got_conjugate: main conjugation entry point ─────────────────────────────── // // verb: Gothic infinitive in Latin romanisation (e.g. "wisan", "nasjan") // or English canonical label ("be", "go", "have") // tense: "present" | "past" // person: "first" | "second" | "third" // number: "singular" | "plural" (or "dual" — treated as plural) // // Returns the inflected form. Falls back to the infinitive for unknown // tenses rather than crashing. fn got_conjugate(verb: String, tense: String, person: String, number: String) -> String { let v: String = got_map_canonical(verb) let slot: Int = got_slot(person, number) // ── Irregular: wisan (to be) ────────────────────────────────────────────── if str_eq(v, "wisan") { if str_eq(tense, "present") { return got_wisan_present(slot) } if str_eq(tense, "past") { return got_wisan_past(slot) } return v } // ── Irregular: haban (to have) ──────────────────────────────────────────── if str_eq(v, "haban") { if str_eq(tense, "present") { return got_haban_present(slot) } if str_eq(tense, "past") { return got_haban_past(slot) } return v } // ── Irregular: gaggan (to go) ───────────────────────────────────────────── if str_eq(v, "gaggan") { if str_eq(tense, "present") { return got_gaggan_present(slot) } if str_eq(tense, "past") { return got_gaggan_past(slot) } return v } // ── Irregular: saihwan (to see) ─────────────────────────────────────────── if str_eq(v, "saihwan") { if str_eq(tense, "present") { return got_saihwan_present(slot) } if str_eq(tense, "past") { return got_saihwan_past(slot) } return v } // ── Irregular: qiþan (to say) ───────────────────────────────────────────── if str_eq(v, "qiþan") { if str_eq(tense, "present") { return got_qithan_present(slot) } if str_eq(tense, "past") { return got_qithan_past(slot) } return v } // ── Irregular: niman (to take) ──────────────────────────────────────────── if str_eq(v, "niman") { if str_eq(tense, "present") { return got_niman_present(slot) } if str_eq(tense, "past") { return got_niman_past(slot) } return v } // ── Regular weak conjugation ────────────────────────────────────────────── let vclass: String = got_verb_class(v) let stem: String = got_verb_stem(v, vclass) if str_eq(vclass, "wk1") { return got_wk1_conjugate(stem, tense, slot) } if str_eq(vclass, "wk2") { return got_wk2_conjugate(stem, tense, slot) } // Final fallback: return the infinitive return v } // ── a-stem masculine paradigm (dags — day) ──────────────────────────────────── // // The a-stem masculine is the largest Gothic noun class, corresponding to the // Proto-Germanic *-a- stems (OE -as, German -e plurals). // // Nominative singular: dags // Stem: dag- (strip final -s from nom sg) // // Singular: nom dags acc dag gen dagis dat daga // Plural: nom dagos acc dagans gen dage dat dagam // // Note: some sources list acc sg as "dag" (without -n), consistent with // Gothic nom≠acc distinction in masculine a-stems. fn got_decline_a_stem_sg(stem: String, gram_case: String) -> String { if str_eq(gram_case, "nominative") { return stem + "s" } if str_eq(gram_case, "accusative") { return stem } if str_eq(gram_case, "genitive") { return stem + "is" } if str_eq(gram_case, "dative") { return stem + "a" } return stem + "s" } fn got_decline_a_stem_pl(stem: String, gram_case: String) -> String { if str_eq(gram_case, "nominative") { return stem + "os" } if str_eq(gram_case, "accusative") { return stem + "ans" } if str_eq(gram_case, "genitive") { return stem + "e" } if str_eq(gram_case, "dative") { return stem + "am" } return stem + "os" } // ── o-stem feminine paradigm (gibo — gift) ──────────────────────────────────── // // The o-stem feminines correspond to Proto-Germanic *-ō- stems. The // nominative singular ends in -o; nom and acc plural are identical. // // Singular: nom gibo acc giba gen gibos dat gibai // Plural: nom gibos acc gibos gen gibo dat gibom fn got_decline_o_stem_sg(stem: String, gram_case: String) -> String { if str_eq(gram_case, "nominative") { return stem + "o" } if str_eq(gram_case, "accusative") { return stem + "a" } if str_eq(gram_case, "genitive") { return stem + "os" } if str_eq(gram_case, "dative") { return stem + "ai" } return stem + "o" } fn got_decline_o_stem_pl(stem: String, gram_case: String) -> String { if str_eq(gram_case, "nominative") { return stem + "os" } if str_eq(gram_case, "accusative") { return stem + "os" } if str_eq(gram_case, "genitive") { return stem + "o" } if str_eq(gram_case, "dative") { return stem + "om" } return stem + "os" } // ── n-stem masculine paradigm (guma — man) ──────────────────────────────────── // // The n-stems (weak nouns) are characterised by -n- appearing in all cases // except the nominative singular. They are sometimes called "weak nouns" // by analogy with Old English grammar. // // Singular: nom guma acc guman gen gumins dat gumin // Plural: nom gumans acc gumans gen gumane dat gumam fn got_decline_n_stem_sg(stem: String, gram_case: String) -> String { if str_eq(gram_case, "nominative") { return stem + "a" } if str_eq(gram_case, "accusative") { return stem + "an" } if str_eq(gram_case, "genitive") { return stem + "ins" } if str_eq(gram_case, "dative") { return stem + "in" } return stem + "a" } fn got_decline_n_stem_pl(stem: String, gram_case: String) -> String { if str_eq(gram_case, "nominative") { return stem + "ans" } if str_eq(gram_case, "accusative") { return stem + "ans" } if str_eq(gram_case, "genitive") { return stem + "ane" } if str_eq(gram_case, "dative") { return stem + "am" } return stem + "ans" } // ── Stem type detection ──────────────────────────────────────────────────────── // // Infers the stem class from the nominative singular form. // // Strategy: // ends in -s and previous char is not a vowel -> a-stem masc (dags type) // ends in -o -> o-stem fem (gibo type) // ends in -a -> n-stem masc (guma type) // ends in -s and previous char is a vowel -> could be various; default a-stem // otherwise -> default to a-stem // // For the common case where the caller passes the paradigm lemma directly: // "dags" -> "a", "gibo" -> "o", "guma" -> "n" fn got_stem_type(noun: String) -> String { if got_str_ends(noun, "o") { return "o" } if got_str_ends(noun, "a") { return "n" } if got_str_ends(noun, "s") { return "a" } // Fallback for other forms return "a" } // got_extract_stem: strip the nom-sg suffix to obtain the bare stem. // // a-stem: strip final -s (dags -> dag) // o-stem: strip final -o (gibo -> gib) // n-stem: strip final -a (guma -> gum) // // All suffix bytes are single-byte ASCII, so str_len gives byte=char counts. fn got_extract_stem(noun: String, stype: String) -> String { let n: Int = str_len(noun) // All three suffixes are 1 byte each return str_slice(noun, 0, n - 1) } // ── Gender inference for article selection ───────────────────────────────────── // // The demonstrative-article used in got_noun_phrase depends on gender. // Gender correlates with stem class in Gothic (imperfect but useful heuristic): // a-stem -> masculine -> sa // o-stem -> feminine -> þo (nom sg of feminine demonstrative) // n-stem -> masculine -> sa // // Neuter a-stems (like "waurd" — word) exist but are not distinguished here; // the NLG layer is expected to pass explicit gender when the distinction matters. fn got_demo_article(stype: String) -> String { if str_eq(stype, "o") { return "þo" } // a-stem and n-stem both masculine return "sa" } // ── got_decline: main declension entry point ────────────────────────────────── // // noun: nominative singular in Gothic Latin romanisation // (e.g. "dags", "gibo", "guma") // gram_case: "nominative" | "accusative" | "genitive" | "dative" // number: "singular" | "plural" // // Returns the inflected form. Unknown stem types return the nominative // singular unchanged rather than producing garbage output. fn got_decline(noun: String, gram_case: String, number: String) -> String { let stype: String = got_stem_type(noun) let stem: String = got_extract_stem(noun, stype) if str_eq(stype, "a") { if str_eq(number, "singular") { return got_decline_a_stem_sg(stem, gram_case) } return got_decline_a_stem_pl(stem, gram_case) } if str_eq(stype, "o") { if str_eq(number, "singular") { return got_decline_o_stem_sg(stem, gram_case) } return got_decline_o_stem_pl(stem, gram_case) } if str_eq(stype, "n") { if str_eq(number, "singular") { return got_decline_n_stem_sg(stem, gram_case) } return got_decline_n_stem_pl(stem, gram_case) } // Unknown: return the nominative singular unchanged return noun } // ── got_noun_phrase: noun phrase builder ────────────────────────────────────── // // Gothic has no definite article. The demonstrative pronouns sa (masc) / // so (fem) / þata (neut) are used in contexts where a definite-article-like // meaning is required (closely following Greek ὁ/ἡ/τό in Wulfila's translation). // // When definite=true this function prepends the gender-appropriate demonstrative // in its nominative singular form. For non-nominative cases the demonstrative // should ideally be declined to match; this implementation prepends the nom-sg // form as a simplification suitable for NLG output. Callers requiring full // demonstrative agreement should call got_decline_demonstrative separately. // // noun: nominative singular Gothic noun // gram_case: "nominative" | "accusative" | "genitive" | "dative" // number: "singular" | "plural" // definite: "true" prepends the demonstrative; any other value omits it fn got_noun_phrase(noun: String, gram_case: String, number: String, definite: String) -> String { let declined: String = got_decline(noun, gram_case, number) if str_eq(definite, "true") { let stype: String = got_stem_type(noun) let article: String = got_demo_article(stype) return article + " " + declined } return declined }