// morphology-gez.el - Ge'ez morphology for the NLG engine. // ግዕዝ — Classical Ethiopic, the liturgical language of the // Ethiopian Orthodox Church and ancestor of Amharic/Tigrinya. // // Implements Ge'ez verb conjugation (perfect and imperfect, basic G-stem), // noun declension (nominative, accusative, construct; singular and plural), // and noun-phrase construction. // // Ge'ez (also: Classical Ethiopic, gǝʿǝz) is the ancient Semitic language // of the Kingdom of Axum (ca. 300–900 CE active liturgy; ca. 1st century BCE // epigraphic attestation). It remains the liturgical language of the // Ethiopian and Eritrean Orthodox Churches. Modern Ethiopian Semitic languages // (Amharic, Tigrinya, Tigre) descend from Ge'ez or a closely related ancestor. // // Script: Ge'ez Fidel (ፊደል) — an Ethiopic abugida (Unicode U+1200–U+137F). // Each Fidel character encodes a consonant + vowel combination (7 orders per // consonant). String literals in this file use actual Unicode characters. // // NOTE on El runtime: the El VM currently outputs non-ASCII as numeric hashes // (runtime limitation for non-Latin scripts). str_eq and string comparisons // work correctly internally. When the VM adds full UTF-8 output, all Ge'ez // strings will display correctly automatically. // // Language profile: // code=gez, name=Ge'ez, morph_type=semitic, word_order=SOV, // script=ethiopic-fidel, family=semitic/south-ethiopic-semitic // // Key grammatical facts: // - SOV word order — unusual for Semitic (Arabic, Hebrew, Akkadian are VSO); // Ge'ez shares SOV with modern Ethiopian Semitic descendants // - Semitic trilateral root system (root + vowel pattern = word) // - Gender: masculine (default) / feminine (often marked with -t suffix) // - Number: singular / plural; dual vestigial // - Cases: nominative (unmarked), accusative -a (animate masc nouns), // construct/genitive (various; simplified to base form here) // - Plural: no single rule; common patterns: -āt (fem/animate), -ān (masc), // broken (internal vowel change, unpredictable without lexicon) // - Verb system: // Perfect (suffix conjugation): completed action // Imperfect (prefix conjugation): ongoing / future / habitual // Four derived stems: basic (G), causative (ʾa-), intensive (doubling), // passive (te-); this file implements basic G-stem throughout // - No definite article (unlike Amharic which has suffixed -u/-wa/-itu etc.) // - Copula: root kwn (ሆነ honä) — "to be / become" // // Verb conjugation conventions: // person: "first" | "second" | "third" // gender: "m" | "f" // number: "singular" | "plural" // tense: "perfect" | "imperfect" // // Noun declension conventions: // gram_case: "nom" | "acc" | "construct" // number: "singular" | "plural" // // Verbs covered (root / Fidel form / transliteration): // "kwn" / ሆነ (honä) — to be / become (copula) // "hlw" / ሀሎ (hallo) — to exist / there is // "hbl" / ሰጠ (säṭṭä) — to give // "rʾy" / አየ (ʾayyä) — to see // "qwl" / ተናገረ (tänagärä) — to speak // // Depends on: morphology.el (str_eq, str_len, str_slice, str_ends_with) // ── String helpers ───────────────────────────────────────────────────────────── import "morphology.el" fn gez_str_ends(s: String, suf: String) -> Bool { return str_ends_with(s, suf) } fn gez_str_len(s: String) -> Int { return str_len(s) } fn gez_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) } // ── Slot index ───────────────────────────────────────────────────────────────── // // Maps person × number to a 0-based slot for table lookups. // Gender is handled separately for third-person disambiguation. // // Slot layout (6 primary cells): // 0 = 1sg (I) // 1 = 2sg (you sg — gender note: Ge'ez distinguishes 2sg m/f in perfect) // 2 = 3sg m (he) // 3 = 3sg f (she) // 4 = 1pl (we) // 5 = 3pl (they — default masc) fn gez_slot(person: String, number: String) -> Int { if str_eq(person, "first") { if str_eq(number, "plural") { return 4 } return 0 } if str_eq(person, "second") { return 1 } // third if str_eq(number, "plural") { return 5 } return 2 } // gez_slot_g: gender-sensitive slot for third-person singular. fn gez_slot_g(person: String, gender: String, number: String) -> Int { let base: Int = gez_slot(person, number) if str_eq(person, "third") { if str_eq(number, "singular") { if str_eq(gender, "f") { return 3 } } } return base } // ── Copula: kwn / ሆነ — to be / become ──────────────────────────────────────── // // Perfect paradigm (suffix forms): // 3sg m: ሆነ honä (base form) // 3sg f: ሆነት honät (-t suffix) // 2sg m: ሆንከ honkä (-kä suffix) // 2sg f: ሆንኪ honki (-ki suffix) // 1sg: ሆንኩ honku (-ku suffix) // 3pl m: ሆኑ honu (-u suffix) // 3pl f: ሆና honā (-ā suffix) // 2pl: ሆንክሙ honkǝmu (-kǝmu suffix) // 1pl: ሆንነ honna (-na suffix) // // Imperfect paradigm (prefix forms): // 3sg m: ይሆን yǝhon (yǝ- prefix) // 3sg f: ትሆን tǝhon (tǝ- prefix) // 2sg: ትሆን tǝhon (tǝ- prefix) // 1sg: እሆን ʾǝhon (ʾǝ- prefix) // 3pl m: ይሆኑ yǝhonu (yǝ- + -u suffix) // 1pl: ንሆን nǝhon (nǝ- prefix) fn gez_kwn_perfect(slot: Int) -> String { if slot == 0 { return "ሆንኩ" } // 1sg honku if slot == 1 { return "ሆንከ" } // 2sg m honkä (default; fem: ሆንኪ) if slot == 2 { return "ሆነ" } // 3sg m honä if slot == 3 { return "ሆነት" } // 3sg f honät if slot == 4 { return "ሆንነ" } // 1pl honna return "ሆኑ" // 3pl honu } fn gez_kwn_imperfect(slot: Int) -> String { if slot == 0 { return "እሆን" } // 1sg ʾǝhon if slot == 1 { return "ትሆን" } // 2sg tǝhon if slot == 2 { return "ይሆን" } // 3sg m yǝhon if slot == 3 { return "ትሆን" } // 3sg f tǝhon (same prefix as 2sg) if slot == 4 { return "ንሆን" } // 1pl nǝhon return "ይሆኑ" // 3pl yǝhonu } fn gez_is_copula(verb: String) -> Bool { if str_eq(verb, "kwn") { return true } if str_eq(verb, "ሆነ") { return true } if str_eq(verb, "hona") { return true } if str_eq(verb, "be") { return true } return false } fn gez_conjugate_copula(tense: String, slot: Int) -> String { if str_eq(tense, "imperfect") { return gez_kwn_imperfect(slot) } return gez_kwn_perfect(slot) } // ── hlw / ሀሎ — to exist / there is ─────────────────────────────────────────── // // hallo is an existential copula used for "there is/are". // In Ge'ez it is largely invariant in its classical usage (presentational). // We provide a minimal paradigm; for existential use, hallo is returned for // all slots in the "perfect" (existential present). fn gez_hlw_perfect(slot: Int) -> String { // Invariant existential for most purposes if slot == 0 { return "ሀሎኩ" } // 1sg halloku if slot == 1 { return "ሀሎከ" } // 2sg hallokä if slot == 2 { return "ሀሎ" } // 3sg m hallo if slot == 3 { return "ሀለወት" } // 3sg f halläwät if slot == 4 { return "ሀሎነ" } // 1pl hallonä return "ሀሉ" // 3pl hallu } fn gez_hlw_imperfect(slot: Int) -> String { if slot == 0 { return "እሀሉ" } // 1sg if slot == 1 { return "ትሀሉ" } // 2sg if slot == 2 { return "ይሀሉ" } // 3sg m if slot == 3 { return "ትሀሉ" } // 3sg f if slot == 4 { return "ንሀሉ" } // 1pl return "ይሀልዉ" // 3pl } // ── hbl / ሰጠ — to give ──────────────────────────────────────────────────────── // // Perfect: säṭṭä (3sg m base); standard G-stem suffix paradigm. // Imperfect: yǝsäṭ (3sg m). fn gez_hbl_perfect(slot: Int) -> String { if slot == 0 { return "ሰጠኩ" } // 1sg säṭṭäku if slot == 1 { return "ሰጠከ" } // 2sg säṭṭäkä if slot == 2 { return "ሰጠ" } // 3sg m säṭṭä if slot == 3 { return "ሰጠት" } // 3sg f säṭṭät if slot == 4 { return "ሰጠነ" } // 1pl säṭṭänä return "ሰጡ" // 3pl säṭṭu } fn gez_hbl_imperfect(slot: Int) -> String { if slot == 0 { return "እሰጥ" } // 1sg ʾǝsäṭ if slot == 1 { return "ትሰጥ" } // 2sg tǝsäṭ if slot == 2 { return "ይሰጥ" } // 3sg m yǝsäṭ if slot == 3 { return "ትሰጥ" } // 3sg f tǝsäṭ if slot == 4 { return "ንሰጥ" } // 1pl nǝsäṭ return "ይሰጡ" // 3pl yǝsäṭu } // ── rʾy / አየ — to see ───────────────────────────────────────────────────────── // // Third-weak verb (final ʾ). Perfect: ʾayyä (3sg m); imperfect: yāy (3sg m). fn gez_ray_perfect(slot: Int) -> String { if slot == 0 { return "አየኩ" } // 1sg ʾayyäku if slot == 1 { return "አየከ" } // 2sg ʾayyäkä if slot == 2 { return "አየ" } // 3sg m ʾayyä if slot == 3 { return "አየት" } // 3sg f ʾayyät if slot == 4 { return "አየነ" } // 1pl ʾayyänä return "አዩ" // 3pl ʾayyu } fn gez_ray_imperfect(slot: Int) -> String { if slot == 0 { return "እያይ" } // 1sg ʾǝyāy if slot == 1 { return "ትያይ" } // 2sg tǝyāy if slot == 2 { return "ያይ" } // 3sg m yāy if slot == 3 { return "ትያይ" } // 3sg f tǝyāy if slot == 4 { return "ንያይ" } // 1pl nǝyāy return "ያዩ" // 3pl yāyu } // ── qwl / ተናገረ — to speak ──────────────────────────────────────────────────── // // tänagärä is actually a derived (reciprocal / D-stem) form of the root ngrǝ, // used as the ordinary word for "to speak" in Classical Ge'ez. // Perfect: tänagärä (3sg m); imperfect: yǝnagär (3sg m). fn gez_qwl_perfect(slot: Int) -> String { if slot == 0 { return "ተናገርኩ" } // 1sg tänagärku if slot == 1 { return "ተናገርከ" } // 2sg tänagärkä if slot == 2 { return "ተናገረ" } // 3sg m tänagärä if slot == 3 { return "ተናገረት" } // 3sg f tänagärät if slot == 4 { return "ተናገርነ" } // 1pl tänagärnä return "ተናገሩ" // 3pl tänagäru } fn gez_qwl_imperfect(slot: Int) -> String { if slot == 0 { return "እናገር" } // 1sg ʾǝnagär if slot == 1 { return "ትናገር" } // 2sg tǝnagär if slot == 2 { return "ይናገር" } // 3sg m yǝnagär if slot == 3 { return "ትናገር" } // 3sg f tǝnagär if slot == 4 { return "ንናገር" } // 1pl nǝnagär return "ይናገሩ" // 3pl yǝnagäru } // ── Generic G-stem paradigm ──────────────────────────────────────────────────── // // For regular strong verbs not in the lookup table. // Ge'ez perfect suffixes: 1sg -ku, 2sg -kä, 3sg m ∅, 3sg f -at, 1pl -nä, 3pl -u // Ge'ez imperfect prefixes: 1sg ʾǝ-, 2sg tǝ-, 3sg m yǝ-, 3sg f tǝ-, 1pl nǝ- fn gez_generic_perfect(base3sg: String, slot: Int) -> String { if slot == 0 { return base3sg + "ኩ" } // -ku if slot == 1 { return base3sg + "ከ" } // -kä if slot == 2 { return base3sg } // ∅ if slot == 3 { return base3sg + "ት" } // -at (simplified: -t Fidel) if slot == 4 { return base3sg + "ነ" } // -nä return base3sg + "ኡ" // -u (3pl) } fn gez_generic_imperfect(base3sg: String, slot: Int) -> String { // base3sg is the 3sg m imperfect form (with yǝ- prefix) // We heuristically return the stem with different prefixes. if slot == 0 { return "እ" + base3sg } // ʾǝ- if slot == 1 { return "ት" + base3sg } // tǝ- if slot == 2 { return "ይ" + base3sg } // yǝ- if slot == 3 { return "ት" + base3sg } // tǝ- if slot == 4 { return "ን" + base3sg } // nǝ- return "ይ" + base3sg + "ኡ" // yǝ- + -u (3pl) } // ── Known-verb dispatcher ───────────────────────────────────────────────────── fn gez_known_verb(verb: String, tense: String, slot: Int) -> String { // kwn / ሆነ — to be if str_eq(verb, "kwn") { return gez_conjugate_copula(tense, slot) } if str_eq(verb, "ሆነ") { return gez_conjugate_copula(tense, slot) } if str_eq(verb, "hona") { return gez_conjugate_copula(tense, slot) } // hlw / ሀሎ — to exist if str_eq(verb, "hlw") { if str_eq(tense, "imperfect") { return gez_hlw_imperfect(slot) } return gez_hlw_perfect(slot) } if str_eq(verb, "ሀሎ") { if str_eq(tense, "imperfect") { return gez_hlw_imperfect(slot) } return gez_hlw_perfect(slot) } if str_eq(verb, "hallo") { if str_eq(tense, "imperfect") { return gez_hlw_imperfect(slot) } return gez_hlw_perfect(slot) } // hbl / ሰጠ — to give if str_eq(verb, "hbl") { if str_eq(tense, "imperfect") { return gez_hbl_imperfect(slot) } return gez_hbl_perfect(slot) } if str_eq(verb, "ሰጠ") { if str_eq(tense, "imperfect") { return gez_hbl_imperfect(slot) } return gez_hbl_perfect(slot) } if str_eq(verb, "sätta") { if str_eq(tense, "imperfect") { return gez_hbl_imperfect(slot) } return gez_hbl_perfect(slot) } // rʾy / አየ — to see if str_eq(verb, "rʾy") { if str_eq(tense, "imperfect") { return gez_ray_imperfect(slot) } return gez_ray_perfect(slot) } if str_eq(verb, "አየ") { if str_eq(tense, "imperfect") { return gez_ray_imperfect(slot) } return gez_ray_perfect(slot) } if str_eq(verb, "ʾayya") { if str_eq(tense, "imperfect") { return gez_ray_imperfect(slot) } return gez_ray_perfect(slot) } // qwl / ተናገረ — to speak if str_eq(verb, "qwl") { if str_eq(tense, "imperfect") { return gez_qwl_imperfect(slot) } return gez_qwl_perfect(slot) } if str_eq(verb, "ተናገረ") { if str_eq(tense, "imperfect") { return gez_qwl_imperfect(slot) } return gez_qwl_perfect(slot) } if str_eq(verb, "tänagärä") { if str_eq(tense, "imperfect") { return gez_qwl_imperfect(slot) } return gez_qwl_perfect(slot) } return "" } // ── Main conjugation entry point ────────────────────────────────────────────── // // gez_conjugate: conjugate a Ge'ez verb (G-stem / basic stem). // // verb: root (e.g. "kwn", "rʾy"), Fidel citation form (e.g. "ሆነ"), // or transliterated 3sg perfect (e.g. "hona", "ʾayya") // tense: "perfect" | "imperfect" // person: "first" | "second" | "third" // number: "singular" | "plural" // // Returns: // - Fidel string (Unicode) for known verbs // - verb unchanged as safe fallback for unknown verbs fn gez_conjugate(verb: String, tense: String, person: String, number: String) -> String { let slot: Int = gez_slot(person, number) if gez_is_copula(verb) { return gez_conjugate_copula(tense, slot) } let known: String = gez_known_verb(verb, tense, slot) if !str_eq(known, "") { return known } return verb } // ── Noun declension ──────────────────────────────────────────────────────────── // // gez_decline: decline a Ge'ez noun for gram_case and number. // // Case system (simplified for this engine): // Nominative: base form (unmarked — the Fidel form as given) // Accusative: base + -a (for animate masculine nouns; inanimate often same) // Construct: base form (genitive/construct; detailed vowel alternations // are lexically irregular — simplified to base here) // // Plural patterns (highly irregular in Ge'ez, like Arabic broken plurals): // Common productive suffixes: // Feminine/animate: -āt (ሀዋርያት hawāryāt — apostles) // Masculine: -ān (ነቢያን nabiyān — prophets) // Broken plurals: unpredictable — must come from vocabulary layer. // Fallback: base + "āt" (ア default) // // noun: base singular form (Fidel or transliterated) // gram_case: "nom" | "acc" | "construct" // number: "singular" | "plural" fn gez_is_fidel(noun: String) -> Bool { // Ethiopic Unicode block starts at U+1200 (ሀ). // We detect by checking a set of common Fidel first characters. let n: Int = gez_str_len(noun) if n == 0 { return false } let first: String = str_slice(noun, 0, 1) if str_eq(first, "ሀ") { return true } if str_eq(first, "ሁ") { return true } if str_eq(first, "ሂ") { return true } if str_eq(first, "ሃ") { return true } if str_eq(first, "ሄ") { return true } if str_eq(first, "ህ") { return true } if str_eq(first, "ሆ") { return true } if str_eq(first, "ለ") { return true } if str_eq(first, "መ") { return true } if str_eq(first, "ሰ") { return true } if str_eq(first, "ሸ") { return true } if str_eq(first, "ቀ") { return true } if str_eq(first, "በ") { return true } if str_eq(first, "ተ") { return true } if str_eq(first, "ነ") { return true } if str_eq(first, "አ") { return true } if str_eq(first, "እ") { return true } if str_eq(first, "ከ") { return true } if str_eq(first, "ወ") { return true } if str_eq(first, "ዘ") { return true } if str_eq(first, "የ") { return true } if str_eq(first, "ደ") { return true } if str_eq(first, "ገ") { return true } if str_eq(first, "ጠ") { return true } if str_eq(first, "ፀ") { return true } if str_eq(first, "ፈ") { return true } if str_eq(first, "ፐ") { return true } return false } fn gez_decline(noun: String, gram_case: String, number: String) -> String { if str_eq(number, "plural") { // Plural: default suffix -āt (Fidel: append transliterated marker for // non-Fidel input; for Fidel input append ት as the -āt marker) if gez_is_fidel(noun) { return noun + "ዎች" // simplified -woc plural marker (Ge'ez -āt ≈ ዎ) } return noun + "āt" } // Singular if str_eq(gram_case, "acc") { // Accusative: add -a suffix if gez_is_fidel(noun) { return noun + "ን" // accusative object marker (simplified) } return noun + "a" } // Nominative and construct: return base form return noun } // ── Noun phrase ──────────────────────────────────────────────────────────────── // // gez_noun_phrase: produce the surface noun phrase. // // Ge'ez has no definite article (unlike Amharic's suffix -u/-wa/-itu). // Definiteness is expressed through word order and context. // The definite parameter is accepted for interface uniformity but has no // surface effect. // // noun: base noun (Fidel string or transliteration) // gram_case: "nom" | "acc" | "construct" // number: "singular" | "plural" // definite: "true" | "false" (no surface effect in Ge'ez) fn gez_noun_phrase(noun: String, gram_case: String, number: String, definite: String) -> String { return gez_decline(noun, gram_case, number) } // ── Canonical verb mapping ───────────────────────────────────────────────────── // // gez_map_canonical: map cross-lingual English canonical verb labels to // Ge'ez roots or citation forms. fn gez_map_canonical(verb: String) -> String { if str_eq(verb, "be") { return "kwn" } if str_eq(verb, "exist") { return "hlw" } if str_eq(verb, "give") { return "hbl" } if str_eq(verb, "see") { return "rʾy" } if str_eq(verb, "speak") { return "qwl" } if str_eq(verb, "say") { return "qwl" } return verb }