// morphology-enm.el - Middle English morphology for the NLG engine. // // Implements Middle English verb conjugation and noun declension for the // ca. 1100-1500 CE period (Chaucerian English). Designed as a companion to // morphology.el and called by the engine when the language profile code is "enm". // // Language profile: code=enm, name=Middle English, morph_type=analytic, // word_order=SVO, question_strategy=inversion, script=latin, family=germanic. // // Verb conjugation covered: // Tenses: present, past // Persons: first/second/third x singular/plural (slots 0-5) // Classes: weak (productive: -est 2sg, -eth 3sg, -en pl; past: -ede/-de/-te) // Irregulars: been/ben (be), han/haven (have), goon (go), seen (see), // seyn/seyen (say), comen (come), maken (make) // Canonical map: "be" -> "been" // // Noun declension covered: // Middle English has largely lost case endings. This module handles: // - nominative (base form) // - genitive singular (+es) // - plural (+es default; irregular forms for common words) // Common irregulars: man->men, child->children, ox->oxen, foot->feet, // tooth->teeth // // Article formation: // Definite: "the" prepended // Indefinite: "a" or "an" based on the first letter of the noun phrase // // Depends on: morphology.el (str_ends_with, str_len, str_slice, str_eq) // ── String helpers ───────────────────────────────────────────────────────────── fn enm_str_ends(s: String, suf: String) -> Bool { return str_ends_with(s, suf) } fn enm_drop(s: String, n: Int) -> String { let len: Int = str_len(s) if n >= len { return "" } return str_slice(s, 0, len - n) } fn enm_first_char(s: String) -> String { if str_len(s) == 0 { return "" } return str_slice(s, 0, 1) } // ── Person/number slot ───────────────────────────────────────────────────────── // // Maps person x number to a 0-based paradigm slot. // 0 = 1st singular (I / ich) // 1 = 2nd singular (thou) // 2 = 3rd singular (he / she / it) // 3 = 1st plural (we) // 4 = 2nd plural (ye) // 5 = 3rd plural (they) fn enm_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 if str_eq(number, "singular") { return 2 } return 5 } // ── Irregular verb tables ────────────────────────────────────────────────────── // // Each irregular verb has a present and past paradigm of six forms (slots 0-5). // Forms are in Middle English spelling, close to Chaucerian usage. fn enm_been_present(slot: Int) -> String { if slot == 0 { return "am" } if slot == 1 { return "art" } if slot == 2 { return "is" } if slot == 3 { return "aren" } if slot == 4 { return "been" } return "been" } fn enm_been_past(slot: Int) -> String { if slot == 0 { return "was" } if slot == 1 { return "were" } if slot == 2 { return "was" } if slot == 3 { return "were" } if slot == 4 { return "were" } return "were" } fn enm_haven_present(slot: Int) -> String { if slot == 0 { return "have" } if slot == 1 { return "hast" } if slot == 2 { return "hath" } if slot == 3 { return "have" } if slot == 4 { return "have" } return "have" } fn enm_haven_past(slot: Int) -> String { if slot == 0 { return "hadde" } if slot == 1 { return "haddest" } if slot == 2 { return "hadde" } if slot == 3 { return "hadden" } if slot == 4 { return "hadden" } return "hadden" } fn enm_goon_present(slot: Int) -> String { if slot == 0 { return "go" } if slot == 1 { return "goost" } if slot == 2 { return "gooth" } if slot == 3 { return "goon" } if slot == 4 { return "goon" } return "goon" } fn enm_goon_past(slot: Int) -> String { if slot == 0 { return "wente" } if slot == 1 { return "wentest" } if slot == 2 { return "wente" } if slot == 3 { return "wenten" } if slot == 4 { return "wenten" } return "wenten" } fn enm_seen_present(slot: Int) -> String { if slot == 0 { return "see" } if slot == 1 { return "seest" } if slot == 2 { return "seeth" } if slot == 3 { return "seen" } if slot == 4 { return "seen" } return "seen" } fn enm_seen_past(slot: Int) -> String { if slot == 0 { return "saugh" } if slot == 1 { return "sawest" } if slot == 2 { return "saugh" } if slot == 3 { return "sawen" } if slot == 4 { return "sawen" } return "sawen" } fn enm_seyen_present(slot: Int) -> String { if slot == 0 { return "seye" } if slot == 1 { return "seyst" } if slot == 2 { return "seith" } if slot == 3 { return "seyen" } if slot == 4 { return "seyen" } return "seyen" } fn enm_seyen_past(slot: Int) -> String { if slot == 0 { return "seide" } if slot == 1 { return "seidest" } if slot == 2 { return "seide" } if slot == 3 { return "seiden" } if slot == 4 { return "seiden" } return "seiden" } fn enm_comen_present(slot: Int) -> String { if slot == 0 { return "come" } if slot == 1 { return "comest" } if slot == 2 { return "cometh" } if slot == 3 { return "comen" } if slot == 4 { return "comen" } return "comen" } fn enm_comen_past(slot: Int) -> String { if slot == 0 { return "cam" } if slot == 1 { return "come" } if slot == 2 { return "cam" } if slot == 3 { return "comen" } if slot == 4 { return "comen" } return "comen" } fn enm_maken_present(slot: Int) -> String { if slot == 0 { return "make" } if slot == 1 { return "makest" } if slot == 2 { return "maketh" } if slot == 3 { return "maken" } if slot == 4 { return "maken" } return "maken" } fn enm_maken_past(slot: Int) -> String { if slot == 0 { return "made" } if slot == 1 { return "madest" } if slot == 2 { return "made" } if slot == 3 { return "maden" } if slot == 4 { return "maden" } return "maden" } // ── Canonical verb mapping ───────────────────────────────────────────────────── // // Maps English semantic labels to Middle English infinitives so the semantic // layer can request forms without knowing the target-language lexeme. fn enm_map_canonical(verb: String) -> String { if str_eq(verb, "be") { return "been" } if str_eq(verb, "have") { return "haven" } if str_eq(verb, "go") { return "goon" } if str_eq(verb, "see") { return "seen" } if str_eq(verb, "say") { return "seyen" } if str_eq(verb, "come") { return "comen" } if str_eq(verb, "make") { return "maken" } return verb } // ── Weak verb stem derivation ────────────────────────────────────────────────── // // For weak verbs the present stem is the infinitive minus any trailing -en or -e. // Past tense suffix: most common is -ede (after unvoiced consonants often -te, // after voiced -de). This module uses -ede as the default productive past suffix. // // Present: // slot 0: stem (I love) // slot 1: stem + est (thou lovest) // slot 2: stem + eth (he loveth) // slot 3: stem + en (we loven) // slot 4: stem + en (ye loven) // slot 5: stem + en (they loven) // // Past: // slot 0: stem + ede (I lovede) // slot 1: stem + edest (thou lovedest) // slot 2: stem + ede (he lovede) // slot 3..5: stem + eden (we loveden) fn enm_weak_stem(verb: String) -> String { if enm_str_ends(verb, "en") { return enm_drop(verb, 2) } if enm_str_ends(verb, "e") { return enm_drop(verb, 1) } return verb } fn enm_weak_present(stem: String, slot: Int) -> String { if slot == 0 { return stem + "e" } if slot == 1 { return stem + "est" } if slot == 2 { return stem + "eth" } if slot == 3 { return stem + "en" } if slot == 4 { return stem + "en" } return stem + "en" } fn enm_weak_past(stem: String, slot: Int) -> String { if slot == 0 { return stem + "ede" } if slot == 1 { return stem + "edest" } if slot == 2 { return stem + "ede" } if slot == 3 { return stem + "eden" } if slot == 4 { return stem + "eden" } return stem + "eden" } // ── enm_conjugate: main conjugation entry point ─────────────────────────────── // // verb: Middle English infinitive (e.g. "loven", "been") or English canonical // tense: "present" | "past" // person: "first" | "second" | "third" // number: "singular" | "plural" // // Returns the inflected form. Unknown tenses fall back to the infinitive rather // than crashing. fn enm_conjugate(verb: String, tense: String, person: String, number: String) -> String { let v: String = enm_map_canonical(verb) let slot: Int = enm_slot(person, number) // ── Irregulars ──────────────────────────────────────────────────────────── if str_eq(v, "been") { if str_eq(tense, "present") { return enm_been_present(slot) } if str_eq(tense, "past") { return enm_been_past(slot) } return v } if str_eq(v, "haven") { if str_eq(tense, "present") { return enm_haven_present(slot) } if str_eq(tense, "past") { return enm_haven_past(slot) } return v } if str_eq(v, "goon") { if str_eq(tense, "present") { return enm_goon_present(slot) } if str_eq(tense, "past") { return enm_goon_past(slot) } return v } if str_eq(v, "seen") { if str_eq(tense, "present") { return enm_seen_present(slot) } if str_eq(tense, "past") { return enm_seen_past(slot) } return v } if str_eq(v, "seyen") { if str_eq(tense, "present") { return enm_seyen_present(slot) } if str_eq(tense, "past") { return enm_seyen_past(slot) } return v } if str_eq(v, "comen") { if str_eq(tense, "present") { return enm_comen_present(slot) } if str_eq(tense, "past") { return enm_comen_past(slot) } return v } if str_eq(v, "maken") { if str_eq(tense, "present") { return enm_maken_present(slot) } if str_eq(tense, "past") { return enm_maken_past(slot) } return v } // ── Regular weak verb ───────────────────────────────────────────────────── let stem: String = enm_weak_stem(v) if str_eq(tense, "present") { return enm_weak_present(stem, slot) } if str_eq(tense, "past") { return enm_weak_past(stem, slot) } // Unknown tense: return infinitive unchanged return v } // ── Noun plural irregulars ───────────────────────────────────────────────────── // // Returns the suppletive plural form for nouns with non-productive plurals, or "" // if the noun takes the regular -es plural. // // Covered: man, woman, child, ox, foot, tooth, goose, mouse, louse // These mirror patterns still visible in Modern English, present in ME too. fn enm_irregular_plural(noun: String) -> String { if str_eq(noun, "man") { return "men" } if str_eq(noun, "woman") { return "wommen" } if str_eq(noun, "child") { return "children" } if str_eq(noun, "ox") { return "oxen" } if str_eq(noun, "foot") { return "feet" } if str_eq(noun, "tooth") { return "teeth" } if str_eq(noun, "goose") { return "gees" } if str_eq(noun, "mouse") { return "mees" } if str_eq(noun, "louse") { return "lees" } return "" } // ── Regular plural formation ─────────────────────────────────────────────────── // // Default: append -es. For nouns already ending in -e, append just -s. // For nouns ending in -s, -x, -sh, -ch: the -es is still appropriate but // in ME spelling the forms vary; we use the simple +es rule uniformly. fn enm_make_plural(noun: String) -> String { // Check suppletive irregular first let irreg: String = enm_irregular_plural(noun) if !str_eq(irreg, "") { return irreg } // Noun ends in -e: just add -s to avoid double vowel if enm_str_ends(noun, "e") { return noun + "s" } // Default: +es return noun + "es" } // ── enm_decline: main declension entry point ────────────────────────────────── // // Middle English has largely lost case morphology. This function handles the // three practically relevant categories: // nominative — base form (used also for accusative and dative) // genitive — base form + es (possessive) // plural — irregular or base + es // // noun: base nominative form (e.g. "knyght", "man", "lond") // gram_case: "nominative" | "accusative" | "dative" | "genitive" | "plural" // ("accusative" and "dative" return the nominative form) // number: "singular" | "plural" // // Returns the inflected form. fn enm_decline(noun: String, gram_case: String, number: String) -> String { // Plural number overrides gram_case for the plural form if str_eq(number, "plural") { return enm_make_plural(noun) } // Singular if str_eq(gram_case, "genitive") { // Genitive singular: +es (even after -e: "the kinges court") return noun + "es" } // Nominative, accusative, dative — all the same in ME return noun } // ── Article selection ────────────────────────────────────────────────────────── // // Middle English uses "the" (definite) and "a" / "an" (indefinite). // The indefinite article is "an" before a vowel-initial word, "a" otherwise. // Vowel check is on the first character of the noun phrase word. fn enm_is_vowel_initial(s: String) -> Bool { let c: String = enm_first_char(s) if str_eq(c, "a") { return true } if str_eq(c, "e") { return true } if str_eq(c, "i") { return true } if str_eq(c, "o") { return true } if str_eq(c, "u") { return true } // ME also treated initial h as effectively silent in some dialects; // we conservatively treat h-initial as consonant-initial. return false } fn enm_indef_article(noun_phrase: String) -> String { if enm_is_vowel_initial(noun_phrase) { return "an" } return "a" } // ── enm_noun_phrase: noun phrase builder ───────────────────────────────────── // // Constructs a full noun phrase with the appropriate article. // // noun: base nominative singular form (e.g. "knyght", "man", "lond") // gram_case: "nominative" | "accusative" | "dative" | "genitive" | "plural" // number: "singular" | "plural" // definite: "true" | "false" (string comparison) // // Returns the complete noun phrase string (article + declined noun). fn enm_noun_phrase(noun: String, gram_case: String, number: String, definite: String) -> String { let form: String = enm_decline(noun, gram_case, number) if str_eq(definite, "true") { return "the " + form } // Indefinite article only makes sense for singular; plural takes no article if str_eq(number, "plural") { return form } let art: String = enm_indef_article(form) return art + " " + form }