// morphology-goh.el - Old High German morphology for the NLG engine. // // Implements Old High German verb conjugation, noun declension, and the // demonstrative determiner. Designed as a companion to morphology.el and // called by the engine when the language profile code is "goh". // // Language profile: code=goh, name=Old High German, morph_type=fusional, // word_order=V2, question_strategy=inversion, script=latin, // family=germanic. // // Historical note: Old High German (ca. 750–1050 CE) is the earliest // substantially attested stage of High German, preserved in texts such as the // Hildebrandslied, the Muspilli, and Notker's translations. It is the // ancestor of Middle High German and thence Modern German. The "High" refers // to the geographical highlands of southern Germany, Austria, and Switzerland — // distinct from the Low German / Old Saxon dialects spoken to the north. // // Defining feature — the Second Germanic Consonant Shift (Hochdeutsche // Lautverschiebung), which distinguishes OHG from Gothic and Old English: // p → ff/pf (Gothic "skip" → OHG "skif"; Goth "apan" → OHG "affo") // t → ss/z (Goth "watan" → OHG "wazzer"; OE "tid" → OHG "zit") // k → ch (Goth "mikan" → OHG "mihil") // This shift only applies to inherited consonants and not to recent loanwords. // // Three genders (masculine, feminine, neuter), four cases (nominative, // accusative, genitive, dative), and two numbers (singular, plural). // // Verb conjugation covered: // Tenses: present indicative, past indicative // Persons: first/second/third × singular/plural (slots 0-5) // Classes: weak verbs (dental -ta past suffix — the most productive class) // Irregulars: wesan/sīn (be), habēn (have), gān (go), sehan (see), // quethan (say), tuon (do) // Canonical map: "be" -> "wesan" // // Noun declension covered: // Strong masc a-stem (tag — day): 4 cases × sg/pl // Strong fem ō-stem (geba — gift): 4 cases × sg/pl // Strong neut a-stem (wort — word): 4 cases × sg/pl // Weak masc n-stem (boto — messenger): 4 cases × sg/pl // // Demonstrative / definite article: // OHG uses the demonstrative dër/diu/daz (the) which doubles as a definite // determiner. This implementation selects the correct nominative form by // inferred gender and passes it as a separate word before the noun. // // Depends on: morphology.el (str_eq, str_len, str_slice, str_ends_with) // ── String helpers ───────────────────────────────────────────────────────────── import "morphology.el" fn goh_str_ends(s: String, suf: String) -> Bool { return str_ends_with(s, suf) } fn goh_drop(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 ───────────────────────────────────────────────────────── // // Maps person × number to a 0-based paradigm index. // 0 = 1st singular (ih) // 1 = 2nd singular (dū) // 2 = 3rd singular (ër/siu/ez) // 3 = 1st plural (wir) // 4 = 2nd plural (ir) // 5 = 3rd plural (sie) fn goh_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 resolved to OHG dictionary infinitives // before conjugation. fn goh_map_canonical(verb: String) -> String { if str_eq(verb, "be") { return "wesan" } if str_eq(verb, "have") { return "haben" } if str_eq(verb, "go") { return "gan" } if str_eq(verb, "see") { return "sehan" } if str_eq(verb, "say") { return "quethan" } if str_eq(verb, "do") { return "tuon" } if str_eq(verb, "make") { return "tuon" } if str_eq(verb, "come") { return "queman" } if str_eq(verb, "give") { return "geban" } if str_eq(verb, "know") { return "wizzan" } if str_eq(verb, "want") { return "wellan" } return verb } // ── Irregular verb: wesan/sīn (to be) ──────────────────────────────────────── // // wesan is one of the most irregular verbs in OHG. The present uses the // bim/bin- forms (from Proto-Germanic *biju-); the past uses the was-/wāri- // forms (from the strong past of wesan). // // Present indicative: // 1sg bim/bin 2sg bist 3sg ist // 1pl birum 2pl biruț 3pl sint // // Note: 2pl "biruț" — the ț represents an old dental fricative; rendered here // as "birut" (common orthographic variant in OHG manuscripts). // // Past indicative: // 1sg was 2sg wāri 3sg was // 1pl wārum 2pl wāruț 3pl wārun fn goh_wesan_present(slot: Int) -> String { if slot == 0 { return "bim" } if slot == 1 { return "bist" } if slot == 2 { return "ist" } if slot == 3 { return "birum" } if slot == 4 { return "birut" } return "sint" } fn goh_wesan_past(slot: Int) -> String { if slot == 0 { return "was" } if slot == 1 { return "wari" } if slot == 2 { return "was" } if slot == 3 { return "warum" } if slot == 4 { return "warut" } return "warun" } // ── Irregular verb: habēn (to have) ─────────────────────────────────────────── // // habēn is an athematic preterite-present verb. // // Present indicative: // 1sg habem 2sg habest 3sg habet // 1pl habemes 2pl habet 3pl habent // // Past (weak past -ta on stem hab-): // 1sg habeta 2sg habetos 3sg habeta // 1pl habetom 2pl habetot 3pl habeton fn goh_haben_present(slot: Int) -> String { if slot == 0 { return "habem" } if slot == 1 { return "habest" } if slot == 2 { return "habet" } if slot == 3 { return "habemes" } if slot == 4 { return "habet" } return "habent" } fn goh_haben_past(slot: Int) -> String { if slot == 0 { return "habeta" } if slot == 1 { return "habetos" } if slot == 2 { return "habeta" } if slot == 3 { return "habetom" } if slot == 4 { return "habetot" } return "habeton" } // ── Irregular verb: gān (to go) ─────────────────────────────────────────────── // // gān is an anomalous contracted verb. // // Present indicative: // 1sg gan 2sg gest 3sg get // 1pl games 2pl gat 3pl gant // // Past (reduplicating/suppletive — giang- forms): // 1sg giang 2sg giangi 3sg giang // 1pl giangum 2pl giangun 3pl giangun fn goh_gan_present(slot: Int) -> String { if slot == 0 { return "gan" } if slot == 1 { return "gest" } if slot == 2 { return "get" } if slot == 3 { return "games" } if slot == 4 { return "gat" } return "gant" } fn goh_gan_past(slot: Int) -> String { if slot == 0 { return "giang" } if slot == 1 { return "giangi" } if slot == 2 { return "giang" } if slot == 3 { return "giangum" } if slot == 4 { return "giangun" } return "giangun" } // ── Irregular verb: sehan (to see) ──────────────────────────────────────────── // // Strong class 5 (ablaut e/a). Present has i-mutation in sg forms. // // Present indicative: // 1sg sihu 2sg sihist 3sg sihit // 1pl sehemes 2pl sehet 3pl sehent // // Past (strong ablaut: e → a): // 1sg sah 2sg sahi 3sg sah // 1pl sahum 2pl sahut 3pl sahun fn goh_sehan_present(slot: Int) -> String { if slot == 0 { return "sihu" } if slot == 1 { return "sihist" } if slot == 2 { return "sihit" } if slot == 3 { return "sehemes" } if slot == 4 { return "sehet" } return "sehent" } fn goh_sehan_past(slot: Int) -> String { if slot == 0 { return "sah" } if slot == 1 { return "sahi" } if slot == 2 { return "sah" } if slot == 3 { return "sahum" } if slot == 4 { return "sahut" } return "sahun" } // ── Irregular verb: quethan (to say) ────────────────────────────────────────── // // Strong class 5 (ablaut e/a). Present sg has i-mutation (quid-). // // Present indicative: // 1sg quidu 2sg quidist 3sg quidit // 1pl quethumes 2pl quethet 3pl quethent // // Past (strong ablaut: e → a): // 1sg quad 2sg quadi 3sg quad // 1pl quadum 2pl quadut 3pl quadun fn goh_quethan_present(slot: Int) -> String { if slot == 0 { return "quidu" } if slot == 1 { return "quidist" } if slot == 2 { return "quidit" } if slot == 3 { return "quethumes" } if slot == 4 { return "quethet" } return "quethent" } fn goh_quethan_past(slot: Int) -> String { if slot == 0 { return "quad" } if slot == 1 { return "quadi" } if slot == 2 { return "quad" } if slot == 3 { return "quadum" } if slot == 4 { return "quadut" } return "quadun" } // ── Irregular verb: tuon (to do/make) ───────────────────────────────────────── // // tuon is a contracted athematic verb. // // Present indicative: // 1sg tuom 2sg tuost 3sg tuot // 1pl tuomes 2pl tuot 3pl tuont // // Past (weak past -ta on stem tā-): // 1sg teta 2sg tetos 3sg teta // 1pl tetom 2pl tetot 3pl teton fn goh_tuon_present(slot: Int) -> String { if slot == 0 { return "tuom" } if slot == 1 { return "tuost" } if slot == 2 { return "tuot" } if slot == 3 { return "tuomes" } if slot == 4 { return "tuot" } return "tuont" } fn goh_tuon_past(slot: Int) -> String { if slot == 0 { return "teta" } if slot == 1 { return "tetos" } if slot == 2 { return "teta" } if slot == 3 { return "tetom" } if slot == 4 { return "tetot" } return "teton" } // ── Weak verb regular paradigm ───────────────────────────────────────────────── // // Weak verbs are the productive OHG class — new verbs are regularly formed on // this pattern. The infinitive ends in -en or -on; the weak past uses the // dental suffix -ta/-to (a reflex of Proto-Germanic *-dō-). // // Class 1 weak (most common; infinitive -en, stem ends in consonant): // // Present indicative (stem + ending): // 1sg -u 2sg -ist 3sg -it // 1pl -emēs 2pl -et 3pl -ent // // Past indicative (stem + dental suffix): // 1sg -ta 2sg -tōs 3sg -ta // 1pl -tōm 2pl -tōt 3pl -tōn // // The stem for a regular weak verb is obtained by dropping -en (2 bytes). fn goh_weak_present(stem: String, slot: Int) -> String { if slot == 0 { return stem + "u" } if slot == 1 { return stem + "ist" } if slot == 2 { return stem + "it" } if slot == 3 { return stem + "emes" } if slot == 4 { return stem + "et" } return stem + "ent" } fn goh_weak_past(stem: String, slot: Int) -> String { if slot == 0 { return stem + "ta" } if slot == 1 { return stem + "tos" } if slot == 2 { return stem + "ta" } if slot == 3 { return stem + "tom" } if slot == 4 { return stem + "tot" } return stem + "ton" } // goh_verb_stem: strip the infinitive ending to expose the productive stem. // -en endings: drop 2 bytes (most weak verbs: sagēn → sag-) // -on endings: drop 2 bytes (class 2 weak: lobōn → lob-) // -an endings: drop 2 bytes (strong verbs handled as irregular; fallback) fn goh_verb_stem(verb: String) -> String { return goh_drop(verb, 2) } // ── goh_conjugate: main conjugation entry point ─────────────────────────────── // // verb: OHG infinitive (e.g. "sagēn", "lobōn") or English canonical label // tense: "present" | "past" // person: "first" | "second" | "third" // number: "singular" | "plural" // // Returns the inflected form. Unknown tenses fall back to the infinitive. fn goh_conjugate(verb: String, tense: String, person: String, number: String) -> String { let v: String = goh_map_canonical(verb) let slot: Int = goh_slot(person, number) // ── Irregular: wesan (to be) ────────────────────────────────────────────── if str_eq(v, "wesan") { if str_eq(tense, "present") { return goh_wesan_present(slot) } if str_eq(tense, "past") { return goh_wesan_past(slot) } return v } // ── Irregular: habēn / haben (to have) ─────────────────────────────────── if str_eq(v, "haben") { if str_eq(tense, "present") { return goh_haben_present(slot) } if str_eq(tense, "past") { return goh_haben_past(slot) } return v } // Also match the macron-spelled infinitive if passed directly if str_eq(v, "haben") { if str_eq(tense, "present") { return goh_haben_present(slot) } if str_eq(tense, "past") { return goh_haben_past(slot) } return v } // ── Irregular: gān / gan (to go) ───────────────────────────────────────── if str_eq(v, "gan") { if str_eq(tense, "present") { return goh_gan_present(slot) } if str_eq(tense, "past") { return goh_gan_past(slot) } return v } // ── Irregular: sehan (to see) ───────────────────────────────────────────── if str_eq(v, "sehan") { if str_eq(tense, "present") { return goh_sehan_present(slot) } if str_eq(tense, "past") { return goh_sehan_past(slot) } return v } // ── Irregular: quethan (to say) ─────────────────────────────────────────── if str_eq(v, "quethan") { if str_eq(tense, "present") { return goh_quethan_present(slot) } if str_eq(tense, "past") { return goh_quethan_past(slot) } return v } // ── Irregular: tuon (to do/make) ────────────────────────────────────────── if str_eq(v, "tuon") { if str_eq(tense, "present") { return goh_tuon_present(slot) } if str_eq(tense, "past") { return goh_tuon_past(slot) } return v } // ── Regular weak conjugation ────────────────────────────────────────────── let stem: String = goh_verb_stem(v) if str_eq(tense, "present") { return goh_weak_present(stem, slot) } if str_eq(tense, "past") { return goh_weak_past(stem, slot) } // Final fallback: return the infinitive return v } // ── Noun stem type detection ─────────────────────────────────────────────────── // // OHG nouns are grouped by historical stem class. This engine supports four: // // "masc_a" — strong masculine a-stem (tag, fisc, arm) // "fem_o" — strong feminine ō-stem (geba, zala, burg) // "neut_a" — strong neuter a-stem (wort, kind, tier) // "masc_n" — weak masculine n-stem (boto, hazo, namo) // // Detection heuristic from the citation form (nominative singular): // ends in -o → masc_n (weak masc nouns: boto, hazo) // ends in -a → fem_o (strong fem: geba, zala) // ends in -t, -d, -n (common neuter endings) → neut_a // otherwise → masc_a (default strong masculine) // // Callers may override by passing the stem type directly when the heuristic // would produce the wrong class (e.g. for monosyllables with ambiguous endings). fn goh_stem_type(noun: String) -> String { if goh_str_ends(noun, "o") { return "masc_n" } if goh_str_ends(noun, "a") { return "fem_o" } if goh_str_ends(noun, "t") { return "neut_a" } if goh_str_ends(noun, "d") { return "neut_a" } if goh_str_ends(noun, "nd") { return "neut_a" } return "masc_a" } // goh_extract_stem: derive the bare stem used as the base for all case endings. // // masc_a: citation form is nom sg without ending (tag = tag, fisc = fisc) // — citation IS the stem; no stripping needed // fem_o: strip final -a (geba → geb-) // neut_a: citation form is nom/acc sg without ending (wort = wort) // — citation IS the stem; no stripping needed // masc_n: strip final -o (boto → bot-) fn goh_extract_stem(noun: String, stype: String) -> String { if str_eq(stype, "fem_o") { return goh_drop(noun, 1) } if str_eq(stype, "masc_n") { return goh_drop(noun, 1) } // masc_a and neut_a: citation IS the stem return noun } // ── Strong masculine a-stem declension (tag — day) ──────────────────────────── // // The strong masculine a-stem is the most common OHG masculine class. // It corresponds to the Gothic a-stem and the Latin 2nd-declension masculine. // // Paradigm (stem = tag-): // Singular: nom tag acc tag gen tages dat tage // Plural: nom taga acc taga gen tago dat tagum fn goh_decline_masc_a_sg(stem: String, gram_case: String) -> String { if str_eq(gram_case, "nominative") { return stem } if str_eq(gram_case, "accusative") { return stem } if str_eq(gram_case, "genitive") { return stem + "es" } if str_eq(gram_case, "dative") { return stem + "e" } return stem } fn goh_decline_masc_a_pl(stem: String, gram_case: String) -> String { if str_eq(gram_case, "nominative") { return stem + "a" } if str_eq(gram_case, "accusative") { return stem + "a" } if str_eq(gram_case, "genitive") { return stem + "o" } if str_eq(gram_case, "dative") { return stem + "um" } return stem + "a" } // ── Strong feminine ō-stem declension (geba — gift) ────────────────────────── // // The ō-stem feminines are the standard OHG feminine class. // They correspond to the Gothic o-stem and Latin 1st-declension nouns. // // Paradigm (stem = geb-): // Singular: nom geba acc geba gen gebā dat gebu // Plural: nom gebā acc gebā gen gebōno dat gebōm fn goh_decline_fem_o_sg(stem: String, gram_case: String) -> String { if str_eq(gram_case, "nominative") { return stem + "a" } if str_eq(gram_case, "accusative") { return stem + "a" } if str_eq(gram_case, "genitive") { return stem + "a" } if str_eq(gram_case, "dative") { return stem + "u" } return stem + "a" } fn goh_decline_fem_o_pl(stem: String, gram_case: String) -> String { if str_eq(gram_case, "nominative") { return stem + "a" } if str_eq(gram_case, "accusative") { return stem + "a" } if str_eq(gram_case, "genitive") { return stem + "ono" } if str_eq(gram_case, "dative") { return stem + "om" } return stem + "a" } // ── Strong neuter a-stem declension (wort — word) ───────────────────────────── // // Strong neuter nouns share the a-stem pattern but have identical nom/acc // throughout (a pan-Germanic neuter feature). The plural differs from the // masculine in the nom/acc: neuters use stem alone rather than stem + -a. // // Paradigm (stem = wort): // Singular: nom wort acc wort gen wortes dat worte // Plural: nom wort acc wort gen worto dat wortum fn goh_decline_neut_a_sg(stem: String, gram_case: String) -> String { if str_eq(gram_case, "nominative") { return stem } if str_eq(gram_case, "accusative") { return stem } if str_eq(gram_case, "genitive") { return stem + "es" } if str_eq(gram_case, "dative") { return stem + "e" } return stem } fn goh_decline_neut_a_pl(stem: String, gram_case: String) -> String { if str_eq(gram_case, "nominative") { return stem } if str_eq(gram_case, "accusative") { return stem } if str_eq(gram_case, "genitive") { return stem + "o" } if str_eq(gram_case, "dative") { return stem + "um" } return stem } // ── Weak masculine n-stem declension (boto — messenger) ─────────────────────── // // Weak nouns (n-stems) are characterised by the nasal -n- appearing in all // forms except the nominative singular. They correspond to the Gothic n-stem // and the Old English weak noun class. // // Paradigm (stem = bot-): // Singular: nom boto acc boton gen boton dat boton // Plural: nom boton acc boton gen botōno dat botōm fn goh_decline_masc_n_sg(stem: String, gram_case: String) -> String { if str_eq(gram_case, "nominative") { return stem + "o" } if str_eq(gram_case, "accusative") { return stem + "on" } if str_eq(gram_case, "genitive") { return stem + "on" } if str_eq(gram_case, "dative") { return stem + "on" } return stem + "o" } fn goh_decline_masc_n_pl(stem: String, gram_case: String) -> String { if str_eq(gram_case, "nominative") { return stem + "on" } if str_eq(gram_case, "accusative") { return stem + "on" } if str_eq(gram_case, "genitive") { return stem + "ono" } if str_eq(gram_case, "dative") { return stem + "om" } return stem + "on" } // ── goh_decline: main declension entry point ────────────────────────────────── // // noun: OHG nominative singular form (e.g. "tag", "geba", "wort", "boto") // gram_case: "nominative" | "accusative" | "genitive" | "dative" // number: "singular" | "plural" // // Returns the inflected form. Unknown stem types return the citation form // unchanged as a safe fallback. fn goh_decline(noun: String, gram_case: String, number: String) -> String { let stype: String = goh_stem_type(noun) let stem: String = goh_extract_stem(noun, stype) if str_eq(stype, "masc_a") { if str_eq(number, "singular") { return goh_decline_masc_a_sg(stem, gram_case) } return goh_decline_masc_a_pl(stem, gram_case) } if str_eq(stype, "fem_o") { if str_eq(number, "singular") { return goh_decline_fem_o_sg(stem, gram_case) } return goh_decline_fem_o_pl(stem, gram_case) } if str_eq(stype, "neut_a") { if str_eq(number, "singular") { return goh_decline_neut_a_sg(stem, gram_case) } return goh_decline_neut_a_pl(stem, gram_case) } if str_eq(stype, "masc_n") { if str_eq(number, "singular") { return goh_decline_masc_n_sg(stem, gram_case) } return goh_decline_masc_n_pl(stem, gram_case) } // Unknown: return citation form unchanged return noun } // ── Demonstrative article ────────────────────────────────────────────────────── // // OHG uses the demonstrative pronoun dër/diu/daz as a definite determiner. // Full declension of this pronoun is complex; this implementation provides the // nominative forms used as determiners before nouns. // // Nominative forms (the most common slot for a determiner): // Masculine sg: der Feminine sg: diu Neuter sg: daz // Plural (all genders): die // // Gender is inferred from the stem type: // masc_a → masculine → "der" // fem_o → feminine → "diu" // neut_a → neuter → "daz" // masc_n → masculine → "der" fn goh_demo_article(stype: String, number: String) -> String { if str_eq(number, "plural") { return "die" } if str_eq(stype, "fem_o") { return "diu" } if str_eq(stype, "neut_a") { return "daz" } return "der" } // ── goh_noun_phrase: noun phrase builder ────────────────────────────────────── // // Assembles a declined noun with an optional OHG demonstrative article. // // noun: OHG nominative singular (e.g. "tag", "geba", "wort", "boto") // gram_case: "nominative" | "accusative" | "genitive" | "dative" // number: "singular" | "plural" // definite: "true" to prepend the demonstrative determiner; any other value omits it // // Note: the demonstrative is given in its nominative singular form for // simplicity. Full agreement would require a separate declined demonstrative // paradigm; the NLG layer should implement that when case-agreement on the // determiner is required. fn goh_noun_phrase(noun: String, gram_case: String, number: String, definite: String) -> String { let stype: String = goh_stem_type(noun) let declined: String = goh_decline(noun, gram_case, number) if str_eq(definite, "true") { let art: String = goh_demo_article(stype, number) return art + " " + declined } return declined }