// morphology-egy.el - Ancient Egyptian (Middle Egyptian) morphology for the NLG engine. // // Implements Middle Egyptian verb conjugation (sdm=f / sdm.n=f paradigm), // noun number marking, suffix pronouns, and noun phrase assembly. // Designed as a companion to morphology.el; called when language code is "egy". // // Language profile: code=egy, name=Ancient Egyptian, morph_type=agglutinative, // word_order=SVO (Middle Egyptian nominal sentences), question_strategy=particle, // script=hieroglyphic (transliterated here as ASCII), family=afro-asiatic-egyptian. // // Script note: Classical transliteration uses special characters (ꜣ ꜥ ḥ ḫ ẖ š q ṯ ḏ). // This engine uses a safe ASCII mapping: // A = ꜣ (aleph/glottal stop) a = ꜥ (ayin) // H = ḥ (h with dot) x = ḫ (velar fricative) // X = ẖ (emphatic h) sh = š (sh sound) // q = q (emphatic k) T = ṯ (tj sound) // D = ḏ (dj sound) // This mapping keeps all string literals ASCII-safe for the El runtime. // // Grammatical notes (Middle Egyptian, ca. 2000–1300 BCE): // - Aspectual system: Imperfective (sdm=f), Perfective (sdm.n=f), Prospective // - "tense" labels used here: "present" (imperfective), "past" (perfective), // "future" (prospective/sdm.xr=f), "infinitive" // - Two grammatical genders: masculine (unmarked) and feminine (suffix -t) // - Number: singular (unmarked), dual (-wy masc / -ty fem), plural (-w masc / -wt fem) // - No case endings — syntactic role expressed by word order and prepositions // - No definite/indefinite article in Middle Egyptian (Late Egyptian introduced pꜣ/tꜣ/nꜣ) // - Zero copula: adjectival predicates need no verb "to be" ("nfr sw" = "he is good") // - Suffix pronouns attach directly to the verb stem with = (e.g. sdm=f "he hears") // // Persons/numbers covered (suffix pronoun paradigm): // person: "first" | "second" | "third" // gender: "m" | "f" (relevant for 2sg, 3sg; 1sg and plurals often unmarked) // number: "singular" | "dual" | "plural" // // Verbs covered (ASCII transliteration → gloss): // wnn — to be/exist (copular auxiliary) // rdi / di — to give // mAA — to see // Dd — to say // Sm — to go // iri — to do / make // sdm — to hear (the paradigm verb for the sdm=f construction) // // Canonical English → Egyptian mapping: // "be" → wnn / zero copula "give" → rdi // "see" → mAA "say" → Dd // "go" → Sm "do" → iri // "make" → iri "hear" → sdm // // Depends on: morphology.el (str_eq, str_len, str_slice, str_ends_with) // ── String helpers ────────────────────────────────────────────────────────────── import "morphology.el" fn egy_str_ends(s: String, suf: String) -> Bool { return str_ends_with(s, suf) } fn egy_str_len(s: String) -> Int { return str_len(s) } fn egy_drop(s: String, n: Int) -> String { let len: Int = str_len(s) if n >= len { return "" } return str_slice(s, 0, len - n) } fn egy_last_char(s: String) -> String { let n: Int = str_len(s) if n == 0 { return "" } return str_slice(s, n - 1, n) } // ── Person/number slot ────────────────────────────────────────────────────────── // // Maps person × gender × number to a 0-based index used in paradigm tables. // Egyptian suffix pronouns distinguish gender in 2nd and 3rd person singular. // // Slot layout: // 0 = 1sg (=i) // 1 = 2sg masc (=k) // 2 = 2sg fem (=T) // 3 = 3sg masc (=f) // 4 = 3sg fem (=s) // 5 = 1pl (=n) // 6 = 2pl (=Tn) // 7 = 3pl (=sn) // 8 = 1du / 2du / 3du (=sny — simplified; dual pronouns are rare in sources) // // Dual falls through to slot 8 (a single dual pronoun slot for all persons). fn egy_slot(person: String, number: String) -> Int { if str_eq(number, "dual") { return 8 } if str_eq(person, "first") { if str_eq(number, "plural") { return 5 } return 0 } if str_eq(person, "second") { if str_eq(number, "plural") { return 6 } return 1 } // third person if str_eq(number, "plural") { return 7 } return 3 } // egy_slot_with_gender: slot variant that factors in gender for 2sg and 3sg. fn egy_slot_with_gender(person: String, gender: String, number: String) -> Int { if str_eq(number, "dual") { return 8 } if str_eq(person, "first") { if str_eq(number, "plural") { return 5 } return 0 } if str_eq(person, "second") { if str_eq(number, "plural") { return 6 } if str_eq(gender, "f") { return 2 } return 1 } // third person if str_eq(number, "plural") { return 7 } if str_eq(gender, "f") { return 4 } return 3 } // ── Suffix pronouns ───────────────────────────────────────────────────────────── // // Egyptian suffix pronouns attach to verbs, nouns, and prepositions. // Written with = before the pronoun in transliteration (e.g. =f = "his / he"). // // Standard Middle Egyptian paradigm: // 1sg: =i ("I / me / my") // 2sg m: =k ("you / your" masc) // 2sg f: =T ("you / your" fem, ṯ in classical) // 3sg m: =f ("he / him / his") // 3sg f: =s ("she / her") // 1pl: =n ("we / us / our") // 2pl: =Tn ("you all / your" plural) // 3pl: =sn ("they / them / their") // dual: =sny (simplified dual — rare in Middle Egyptian texts) fn egy_conjugate_pronoun(person: String, number: String) -> String { let slot: Int = egy_slot(person, number) if slot == 0 { return "=i" } if slot == 1 { return "=k" } if slot == 5 { return "=n" } if slot == 6 { return "=Tn" } if slot == 7 { return "=sn" } if slot == 8 { return "=sny" } // slots 2–4 need gender; default to masc for slot 3 return "=f" } fn egy_suffix_pronoun(slot: Int) -> String { if slot == 0 { return "=i" } if slot == 1 { return "=k" } if slot == 2 { return "=T" } if slot == 3 { return "=f" } if slot == 4 { return "=s" } if slot == 5 { return "=n" } if slot == 6 { return "=Tn" } if slot == 7 { return "=sn" } // dual (slot 8) return "=sny" } // ── Copula detection ──────────────────────────────────────────────────────────── // // In Middle Egyptian the verb "to be" as predicate is often omitted in the // present (zero copula for adjective predicates). The auxiliary wnn is used // for existence / substantive "to be" and in subordinate clauses. // Canonical English label "be" maps to zero copula in the present. fn egy_is_copula(verb: String) -> Bool { if str_eq(verb, "wnn") { return true } if str_eq(verb, "be") { return true } return false } // ── Copula conjugation ────────────────────────────────────────────────────────── // // Present ("imperfective"): zero copula for adjectival predicate — return "". // The auxiliary iw...wnn is used in certain syntactic environments but the // bare zero is the canonical Middle Egyptian form. // Past ("perfective"): wnn.n (with perfective suffix .n) // Future ("prospective"): wnn.xr (prospective form, simplified) fn egy_conjugate_copula(tense: String, slot: Int) -> String { if str_eq(tense, "present") { return "" } if str_eq(tense, "past") { return "wnn.n" + egy_suffix_pronoun(slot) } if str_eq(tense, "future") { return "wnn.xr" + egy_suffix_pronoun(slot) } if str_eq(tense, "infinitive") { return "wnn" } // Default: zero copula return "" } // ── Irregular verb: rdi / di (to give) ───────────────────────────────────────── // // rdi is the full form; di is the common abbreviated written form. // Imperfective (present): di=f (3sg m), with full pronoun for other persons. // Perfective (past): di.n=f // Prospective (future): di.xr=f // Infinitive: rdi fn egy_rdi_present(slot: Int) -> String { return "di" + egy_suffix_pronoun(slot) } fn egy_rdi_past(slot: Int) -> String { return "di.n" + egy_suffix_pronoun(slot) } fn egy_rdi_future(slot: Int) -> String { return "di.xr" + egy_suffix_pronoun(slot) } // ── Irregular verb: mAA (to see) ─────────────────────────────────────────────── // // mAA is a geminated root (m-AA). // Present: mAA=f; Past: mAA.n=f; Future: mAA.xr=f fn egy_mAA_present(slot: Int) -> String { return "mAA" + egy_suffix_pronoun(slot) } fn egy_mAA_past(slot: Int) -> String { return "mAA.n" + egy_suffix_pronoun(slot) } fn egy_mAA_future(slot: Int) -> String { return "mAA.xr" + egy_suffix_pronoun(slot) } // ── Irregular verb: Dd (to say) ───────────────────────────────────────────────── // // Present: Dd=f; Past: Dd.n=f; Future: Dd.xr=f // Infinitive: Dd fn egy_Dd_present(slot: Int) -> String { return "Dd" + egy_suffix_pronoun(slot) } fn egy_Dd_past(slot: Int) -> String { return "Dd.n" + egy_suffix_pronoun(slot) } fn egy_Dd_future(slot: Int) -> String { return "Dd.xr" + egy_suffix_pronoun(slot) } // ── Irregular verb: Sm (to go) ───────────────────────────────────────────────── // // Present: Sm=f; Past: Sm.n=f; Future: Sm.xr=f // (Note: the verb Smt "to go" appears in texts; Sm is the most common short form.) fn egy_Sm_present(slot: Int) -> String { return "Sm" + egy_suffix_pronoun(slot) } fn egy_Sm_past(slot: Int) -> String { return "Sm.n" + egy_suffix_pronoun(slot) } fn egy_Sm_future(slot: Int) -> String { return "Sm.xr" + egy_suffix_pronoun(slot) } // ── Irregular verb: iri (to do / make) ───────────────────────────────────────── // // iri has a contracted 3-radical stem ir- before pronouns. // Present: ir=f; Past: ir.n=f; Future: ir.xr=f // Infinitive: iri fn egy_iri_present(slot: Int) -> String { return "ir" + egy_suffix_pronoun(slot) } fn egy_iri_past(slot: Int) -> String { return "ir.n" + egy_suffix_pronoun(slot) } fn egy_iri_future(slot: Int) -> String { return "ir.xr" + egy_suffix_pronoun(slot) } // ── Regular verb: sdm (to hear) ──────────────────────────────────────────────── // // sdm (to hear) is the paradigm verb used in grammar textbooks to illustrate // all Egyptian verb forms. The sdm=f construction names the imperfective suffix // verb pattern itself. // Present: sdm=f; Past: sdm.n=f; Future: sdm.xr=f // Infinitive: sdm fn egy_sdm_present(slot: Int) -> String { return "sdm" + egy_suffix_pronoun(slot) } fn egy_sdm_past(slot: Int) -> String { return "sdm.n" + egy_suffix_pronoun(slot) } fn egy_sdm_future(slot: Int) -> String { return "sdm.xr" + egy_suffix_pronoun(slot) } // ── Known-verb dispatcher ─────────────────────────────────────────────────────── // // Returns the inflected form for a known verb, or "" if unknown. // Accepts both canonical English labels and Egyptian transliterations. fn egy_known_verb(verb: String, tense: String, slot: Int) -> String { // ── rdi / di — to give ─────────────────────────────────────────────────────── if str_eq(verb, "rdi") { if str_eq(tense, "present") { return egy_rdi_present(slot) } if str_eq(tense, "past") { return egy_rdi_past(slot) } if str_eq(tense, "future") { return egy_rdi_future(slot) } if str_eq(tense, "infinitive") { return "rdi" } return egy_rdi_present(slot) } if str_eq(verb, "di") { if str_eq(tense, "present") { return egy_rdi_present(slot) } if str_eq(tense, "past") { return egy_rdi_past(slot) } if str_eq(tense, "future") { return egy_rdi_future(slot) } if str_eq(tense, "infinitive") { return "rdi" } return egy_rdi_present(slot) } if str_eq(verb, "give") { if str_eq(tense, "present") { return egy_rdi_present(slot) } if str_eq(tense, "past") { return egy_rdi_past(slot) } if str_eq(tense, "future") { return egy_rdi_future(slot) } if str_eq(tense, "infinitive") { return "rdi" } return egy_rdi_present(slot) } // ── mAA — to see ───────────────────────────────────────────────────────────── if str_eq(verb, "mAA") { if str_eq(tense, "present") { return egy_mAA_present(slot) } if str_eq(tense, "past") { return egy_mAA_past(slot) } if str_eq(tense, "future") { return egy_mAA_future(slot) } if str_eq(tense, "infinitive") { return "mAA" } return egy_mAA_present(slot) } if str_eq(verb, "see") { if str_eq(tense, "present") { return egy_mAA_present(slot) } if str_eq(tense, "past") { return egy_mAA_past(slot) } if str_eq(tense, "future") { return egy_mAA_future(slot) } if str_eq(tense, "infinitive") { return "mAA" } return egy_mAA_present(slot) } // ── Dd — to say ────────────────────────────────────────────────────────────── if str_eq(verb, "Dd") { if str_eq(tense, "present") { return egy_Dd_present(slot) } if str_eq(tense, "past") { return egy_Dd_past(slot) } if str_eq(tense, "future") { return egy_Dd_future(slot) } if str_eq(tense, "infinitive") { return "Dd" } return egy_Dd_present(slot) } if str_eq(verb, "say") { if str_eq(tense, "present") { return egy_Dd_present(slot) } if str_eq(tense, "past") { return egy_Dd_past(slot) } if str_eq(tense, "future") { return egy_Dd_future(slot) } if str_eq(tense, "infinitive") { return "Dd" } return egy_Dd_present(slot) } // ── Sm — to go ─────────────────────────────────────────────────────────────── if str_eq(verb, "Sm") { if str_eq(tense, "present") { return egy_Sm_present(slot) } if str_eq(tense, "past") { return egy_Sm_past(slot) } if str_eq(tense, "future") { return egy_Sm_future(slot) } if str_eq(tense, "infinitive") { return "Sm" } return egy_Sm_present(slot) } if str_eq(verb, "go") { if str_eq(tense, "present") { return egy_Sm_present(slot) } if str_eq(tense, "past") { return egy_Sm_past(slot) } if str_eq(tense, "future") { return egy_Sm_future(slot) } if str_eq(tense, "infinitive") { return "Sm" } return egy_Sm_present(slot) } // ── iri — to do / make ─────────────────────────────────────────────────────── if str_eq(verb, "iri") { if str_eq(tense, "present") { return egy_iri_present(slot) } if str_eq(tense, "past") { return egy_iri_past(slot) } if str_eq(tense, "future") { return egy_iri_future(slot) } if str_eq(tense, "infinitive") { return "iri" } return egy_iri_present(slot) } if str_eq(verb, "do") { if str_eq(tense, "present") { return egy_iri_present(slot) } if str_eq(tense, "past") { return egy_iri_past(slot) } if str_eq(tense, "future") { return egy_iri_future(slot) } if str_eq(tense, "infinitive") { return "iri" } return egy_iri_present(slot) } if str_eq(verb, "make") { if str_eq(tense, "present") { return egy_iri_present(slot) } if str_eq(tense, "past") { return egy_iri_past(slot) } if str_eq(tense, "future") { return egy_iri_future(slot) } if str_eq(tense, "infinitive") { return "iri" } return egy_iri_present(slot) } // ── sdm — to hear ──────────────────────────────────────────────────────────── if str_eq(verb, "sdm") { if str_eq(tense, "present") { return egy_sdm_present(slot) } if str_eq(tense, "past") { return egy_sdm_past(slot) } if str_eq(tense, "future") { return egy_sdm_future(slot) } if str_eq(tense, "infinitive") { return "sdm" } return egy_sdm_present(slot) } if str_eq(verb, "hear") { if str_eq(tense, "present") { return egy_sdm_present(slot) } if str_eq(tense, "past") { return egy_sdm_past(slot) } if str_eq(tense, "future") { return egy_sdm_future(slot) } if str_eq(tense, "infinitive") { return "sdm" } return egy_sdm_present(slot) } // Verb not in table return "" } // ── Regular verb conjugation ──────────────────────────────────────────────────── // // For verbs not in the explicit table, apply the productive suffix-verb pattern: // Present (imperfective sdm=f): stem + pronoun suffix // Past (perfective sdm.n=f): stem + ".n" + pronoun suffix // Future (prospective sdm.xr=f): stem + ".xr" + pronoun suffix // Infinitive: stem unchanged // // This covers the vast majority of strong (sound) verb roots. fn egy_regular_present(stem: String, slot: Int) -> String { return stem + egy_suffix_pronoun(slot) } fn egy_regular_past(stem: String, slot: Int) -> String { return stem + ".n" + egy_suffix_pronoun(slot) } fn egy_regular_future(stem: String, slot: Int) -> String { return stem + ".xr" + egy_suffix_pronoun(slot) } // ── egy_conjugate: main conjugation entry point ───────────────────────────────── // // verb: Egyptian verb (ASCII transliteration) or English canonical label // tense: "present" | "past" | "future" | "infinitive" // person: "first" | "second" | "third" // number: "singular" | "dual" | "plural" // // Returns: // - "" for present copula (zero copula — caller omits the verb) // - inflected form (stem + .n + suffix, etc.) for all other cases // - verb + regular suffix for unknown verbs (productive fallback) fn egy_conjugate(verb: String, tense: String, person: String, number: String) -> String { let slot: Int = egy_slot(person, number) // Handle copula (wnn / "be") if egy_is_copula(verb) { return egy_conjugate_copula(tense, slot) } // Try the known-verb table let known: String = egy_known_verb(verb, tense, slot) if !str_eq(known, "") { return known } // Infinitive: return unchanged if str_eq(tense, "infinitive") { return verb } // Regular verb: apply productive sdm=f / sdm.n=f pattern if str_eq(tense, "present") { return egy_regular_present(verb, slot) } if str_eq(tense, "past") { return egy_regular_past(verb, slot) } if str_eq(tense, "future") { return egy_regular_future(verb, slot) } // Unknown tense: return verb unchanged as safe fallback return verb } // ── Noun number marking ───────────────────────────────────────────────────────── // // Middle Egyptian nouns are invariant for case — syntactic role is expressed by // word order and prepositions, not noun endings. Number is marked by suffix: // // Singular: base form (no suffix) // Dual: masc + wy / fem + ty (wy and ty in ASCII transliteration) // Plural: masc + w / fem + wt // // Many common nouns have suppletive or irregular plurals (recorded in the // vocabulary layer). This function implements the productive regular pattern. // // gram_case: accepted for API symmetry but has no effect (Egyptian is caseless). fn egy_decline(noun: String, gram_case: String, number: String) -> String { if str_eq(number, "singular") { return noun } if str_eq(number, "dual") { // Feminine dual: if noun ends in t (feminine marker), replace with ty if egy_str_ends(noun, "t") { let stem: String = egy_drop(noun, 1) return stem + "ty" } return noun + "wy" } // Plural if egy_str_ends(noun, "t") { // Feminine noun: add wt return noun + "wt" } // Masculine noun: add w return noun + "w" } // ── Feminine derivation ───────────────────────────────────────────────────────── // // egy_fem: derive the feminine form of a noun or adjective by appending -t. // // In Middle Egyptian, the feminine gender marker is the suffix -t (written with // the bread-loaf hieroglyph, Gardiner X1). If the base already ends in -t the // form is returned unchanged to avoid double-suffixing. fn egy_fem(noun: String) -> String { if egy_str_ends(noun, "t") { return noun } return noun + "t" } // ── Noun phrase assembly ──────────────────────────────────────────────────────── // // egy_noun_phrase: return the surface form of a noun phrase. // // noun: base noun (ASCII transliteration) // gram_case: passed for API symmetry; has no effect (Egyptian is caseless) // number: "singular" | "dual" | "plural" // definite: "true" | "false" — Middle Egyptian has no article; parameter accepted // for API symmetry. Late Egyptian pꜣ/tꜣ/nꜣ articles are not implemented // here (they would require knowing the gender of each noun). // // Returns the noun in its correct number form. fn egy_noun_phrase(noun: String, gram_case: String, number: String, definite: String) -> String { return egy_decline(noun, gram_case, number) } // ── Canonical verb mapping ────────────────────────────────────────────────────── // // egy_map_canonical: map cross-lingual English canonical verb labels to their // Middle Egyptian equivalents before dispatching to egy_conjugate. fn egy_map_canonical(verb: String) -> String { if str_eq(verb, "be") { return "wnn" } if str_eq(verb, "give") { return "rdi" } if str_eq(verb, "see") { return "mAA" } if str_eq(verb, "say") { return "Dd" } if str_eq(verb, "go") { return "Sm" } if str_eq(verb, "do") { return "iri" } if str_eq(verb, "make") { return "iri" } if str_eq(verb, "hear") { return "sdm" } // Unknown: return as-is; egy_conjugate will apply the regular pattern return verb }