// morphology-pi.el - Pali morphology for the NLG engine. // // Implements Pali verb conjugation and noun declension using standard IAST-subset // Latin transliteration. Pali is the liturgical language of Theravada Buddhism // (canonical form of Middle Indo-Aryan, ca. 3rd century BCE). // // Language profile: code=pi, name=Pali, morph_type=fusional, word_order=SOV, // question_strategy=particle, script=latin-iast, family=indo-aryan. // // Verb conjugation covered: // Tenses: present (bhū-ādi class), aorist (past), future // Persons: first/second/third x singular/plural (slots 0-5) // Endings: present -āmi/-asi/-ati/-āma/-atha/-anti // aorist -iṃ/-i/-i/-imhā/-ittha/-iṃsu // future -issāmi/-issasi/-issati/-issāma/-issatha/-issanti // Irregulars: atthi/hoti (be), gacchati (go), passati (see), // vadati (say), karoti (do) // Canonical map: "be" -> "hoti" // // Noun declension covered: // a-stem masculine (paradigm: "purisa" — man): 8 cases, sg + pl // ā-stem feminine (paradigm: "kaññā" — girl): 8 cases, sg (pl approx.) // Cases: nominative, accusative, instrumental, dative, ablative, // genitive, locative, vocative // // Articles: // Pali has no articles. pi_noun_phrase returns the declined noun directly. // // Depends on: morphology.el (str_ends_with, str_len, str_slice, str_eq) // ── String helpers ───────────────────────────────────────────────────────────── import "morphology.el" fn pi_str_ends(s: String, suf: String) -> Bool { return str_ends_with(s, suf) } fn pi_drop(s: String, n: Int) -> String { let len: Int = str_len(s) if n >= len { return "" } return str_slice(s, 0, len - n) } fn pi_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 x number to a 0-based paradigm slot. // 0 = 1st singular (ahaṃ) // 1 = 2nd singular (tvaṃ) // 2 = 3rd singular (so/sā/taṃ) // 3 = 1st plural (mayaṃ) // 4 = 2nd plural (tumhe) // 5 = 3rd plural (te/tā/tāni) fn pi_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 } // ── Present tense endings (bhū-ādi class) ───────────────────────────────────── // // The standard present active endings for the most common Pali verb class: // 1sg -āmi 2sg -asi 3sg -ati // 1pl -āma 2pl -atha 3pl -anti fn pi_present_ending(slot: Int) -> String { if slot == 0 { return "āmi" } if slot == 1 { return "asi" } if slot == 2 { return "ati" } if slot == 3 { return "āma" } if slot == 4 { return "atha" } return "anti" } // ── Aorist (past) tense endings ─────────────────────────────────────────────── // // The Pali aorist is the standard simple past. The most common set: // 1sg -iṃ 2sg -i 3sg -i // 1pl -imhā 2pl -ittha 3pl -iṃsu // // These are appended to the verb root (or a modified stem). For regular // weak aorists, the root is taken from the infinitive by stripping -ati. fn pi_aorist_ending(slot: Int) -> String { if slot == 0 { return "iṃ" } if slot == 1 { return "i" } if slot == 2 { return "i" } if slot == 3 { return "imhā" } if slot == 4 { return "ittha" } return "iṃsu" } // ── Future tense endings ─────────────────────────────────────────────────────── // // Future is formed with the suffix -issa- inserted between root and personal ending: // 1sg -issāmi 2sg -issasi 3sg -issati // 1pl -issāma 2pl -issatha 3pl -issanti fn pi_future_ending(slot: Int) -> String { if slot == 0 { return "issāmi" } if slot == 1 { return "issasi" } if slot == 2 { return "issati" } if slot == 3 { return "issāma" } if slot == 4 { return "issatha" } return "issanti" } // ── Irregular verb tables ────────────────────────────────────────────────────── // // atthi/hoti (to be): two parallel paradigms. // The "hoti" paradigm is used by default (canonical "be" maps here). // "atthi" paradigm is included for the commonly seen alternative. fn pi_hoti_present(slot: Int) -> String { if slot == 0 { return "homi" } if slot == 1 { return "hosi" } if slot == 2 { return "hoti" } if slot == 3 { return "homa" } if slot == 4 { return "hotha" } return "honti" } fn pi_atthi_present(slot: Int) -> String { if slot == 0 { return "amhi" } if slot == 1 { return "asi" } if slot == 2 { return "atthi" } if slot == 3 { return "amha" } if slot == 4 { return "attha" } return "santi" } // Past (aorist) of hoti/atthi: āsi paradigm (a-aorist) fn pi_hoti_aorist(slot: Int) -> String { if slot == 0 { return "āsiṃ" } if slot == 1 { return "āsi" } if slot == 2 { return "āsi" } if slot == 3 { return "āsimhā" } if slot == 4 { return "āsittha" } return "āsiṃsu" } // Future of hoti: regular -issa- on root "ho-" fn pi_hoti_future(slot: Int) -> String { if slot == 0 { return "hossāmi" } if slot == 1 { return "hossasi" } if slot == 2 { return "hossati" } if slot == 3 { return "hossāma" } if slot == 4 { return "hossatha" } return "hossanti" } // gacchati (to go): present is suppletive; aorist uses root "gam-"/"agamā-" fn pi_gacchati_present(slot: Int) -> String { if slot == 0 { return "gacchāmi" } if slot == 1 { return "gacchasi" } if slot == 2 { return "gacchati" } if slot == 3 { return "gacchāma" } if slot == 4 { return "gacchatha" } return "gacchanti" } fn pi_gacchati_aorist(slot: Int) -> String { if slot == 0 { return "agamāsiṃ" } if slot == 1 { return "agamāsi" } if slot == 2 { return "agamāsi" } if slot == 3 { return "agamāsimhā" } if slot == 4 { return "agamāsittha" } return "agamaṃsu" } fn pi_gacchati_future(slot: Int) -> String { if slot == 0 { return "gamissāmi" } if slot == 1 { return "gamissasi" } if slot == 2 { return "gamissati" } if slot == 3 { return "gamissāma" } if slot == 4 { return "gamissatha" } return "gamissanti" } // passati (to see): regular present; aorist root "dis-"/"addasā-" fn pi_passati_present(slot: Int) -> String { if slot == 0 { return "passāmi" } if slot == 1 { return "passasi" } if slot == 2 { return "passati" } if slot == 3 { return "passāma" } if slot == 4 { return "passatha" } return "passanti" } fn pi_passati_aorist(slot: Int) -> String { if slot == 0 { return "addasāsiṃ" } if slot == 1 { return "addasāsi" } if slot == 2 { return "addasāsi" } if slot == 3 { return "addasāsimhā" } if slot == 4 { return "addasāsittha" } return "addasāsiṃsu" } fn pi_passati_future(slot: Int) -> String { if slot == 0 { return "dakkhissāmi" } if slot == 1 { return "dakkhissasi" } if slot == 2 { return "dakkhissati" } if slot == 3 { return "dakkhissāma" } if slot == 4 { return "dakkhissatha" } return "dakkhissanti" } // vadati (to say): regular throughout; aorist uses avadi- fn pi_vadati_present(slot: Int) -> String { if slot == 0 { return "vadāmi" } if slot == 1 { return "vadasi" } if slot == 2 { return "vadati" } if slot == 3 { return "vadāma" } if slot == 4 { return "vadatha" } return "vadanti" } fn pi_vadati_aorist(slot: Int) -> String { if slot == 0 { return "avadāsiṃ" } if slot == 1 { return "avadāsi" } if slot == 2 { return "avadāsi" } if slot == 3 { return "avadāsimhā" } if slot == 4 { return "avadāsittha" } return "avadāsiṃsu" } fn pi_vadati_future(slot: Int) -> String { if slot == 0 { return "vadissāmi" } if slot == 1 { return "vadissasi" } if slot == 2 { return "vadissati" } if slot == 3 { return "vadissāma" } if slot == 4 { return "vadissatha" } return "vadissanti" } // karoti (to do/make): karo- present stem; kāri-/akāsi aorist fn pi_karoti_present(slot: Int) -> String { if slot == 0 { return "karomi" } if slot == 1 { return "karosi" } if slot == 2 { return "karoti" } if slot == 3 { return "karoma" } if slot == 4 { return "karotha" } return "karonti" } fn pi_karoti_aorist(slot: Int) -> String { if slot == 0 { return "akāsiṃ" } if slot == 1 { return "akāsi" } if slot == 2 { return "akāsi" } if slot == 3 { return "akāsimhā" } if slot == 4 { return "akāsittha" } return "akāsiṃsu" } fn pi_karoti_future(slot: Int) -> String { if slot == 0 { return "karissāmi" } if slot == 1 { return "karissasi" } if slot == 2 { return "karissati" } if slot == 3 { return "karissāma" } if slot == 4 { return "karissatha" } return "karissanti" } // ── Canonical verb mapping ───────────────────────────────────────────────────── // // Maps English semantic labels to Pali verb citation forms (3rd sg present). fn pi_map_canonical(verb: String) -> String { if str_eq(verb, "be") { return "hoti" } if str_eq(verb, "go") { return "gacchati" } if str_eq(verb, "see") { return "passati" } if str_eq(verb, "say") { return "vadati" } if str_eq(verb, "do") { return "karoti" } if str_eq(verb, "make") { return "karoti" } return verb } // ── Regular verb stem derivation ─────────────────────────────────────────────── // // For bhū-ādi class verbs, the present stem is derived by stripping -ati from // the 3sg present form (the citation form), then appending the appropriate ending. // The same root (without -ati) is used for the aorist and future. // // e.g. "bhavati" -> root "bhava" -> present "bhavāmi", future "bhavissāmi" // -> aorist root = "bhavi" -> "bhaviṃ" fn pi_regular_root(verb: String) -> String { // Strip -ati if present to get the thematic stem if pi_str_ends(verb, "ati") { return pi_drop(verb, 3) } // Some forms end in -eti (e-class verbs) if pi_str_ends(verb, "eti") { return pi_drop(verb, 3) } // Otherwise treat the whole form as the root return verb } // ── pi_conjugate: main conjugation entry point ──────────────────────────────── // // verb: Pali 3sg present (citation form, e.g. "bhavati", "hoti") or English // canonical label // tense: "present" | "past" | "future" // person: "first" | "second" | "third" // number: "singular" | "plural" // // Returns the inflected form. Falls back to the citation form on unknown input. fn pi_conjugate(verb: String, tense: String, person: String, number: String) -> String { let v: String = pi_map_canonical(verb) let slot: Int = pi_slot(person, number) // ── Irregulars ──────────────────────────────────────────────────────────── if str_eq(v, "hoti") { if str_eq(tense, "present") { return pi_hoti_present(slot) } if str_eq(tense, "past") { return pi_hoti_aorist(slot) } if str_eq(tense, "future") { return pi_hoti_future(slot) } return v } if str_eq(v, "atthi") { if str_eq(tense, "present") { return pi_atthi_present(slot) } if str_eq(tense, "past") { return pi_hoti_aorist(slot) } if str_eq(tense, "future") { return pi_hoti_future(slot) } return v } if str_eq(v, "gacchati") { if str_eq(tense, "present") { return pi_gacchati_present(slot) } if str_eq(tense, "past") { return pi_gacchati_aorist(slot) } if str_eq(tense, "future") { return pi_gacchati_future(slot) } return v } if str_eq(v, "passati") { if str_eq(tense, "present") { return pi_passati_present(slot) } if str_eq(tense, "past") { return pi_passati_aorist(slot) } if str_eq(tense, "future") { return pi_passati_future(slot) } return v } if str_eq(v, "vadati") { if str_eq(tense, "present") { return pi_vadati_present(slot) } if str_eq(tense, "past") { return pi_vadati_aorist(slot) } if str_eq(tense, "future") { return pi_vadati_future(slot) } return v } if str_eq(v, "karoti") { if str_eq(tense, "present") { return pi_karoti_present(slot) } if str_eq(tense, "past") { return pi_karoti_aorist(slot) } if str_eq(tense, "future") { return pi_karoti_future(slot) } return v } // ── Regular bhū-ādi class verb ──────────────────────────────────────────── let root: String = pi_regular_root(v) if str_eq(tense, "present") { return root + pi_present_ending(slot) } if str_eq(tense, "past") { // Aorist: root + i-aorist ending return root + pi_aorist_ending(slot) } if str_eq(tense, "future") { return root + pi_future_ending(slot) } // Unknown tense: return citation form unchanged return v } // ── a-stem masculine declension ("purisa" — man) ───────────────────────────── // // The a-stem masculine is the most frequent Pali declension class. // Paradigm based on "puriso" (nominative singular). // // Eight cases: nominative, accusative, instrumental, dative, ablative, // genitive, locative, vocative. // // Singular: // nom puriso acc purisaṃ instr purisena dat purisāya (or purissā) // abl purisā gen purisassa loc purisasmiṃ voc purisa // // Plural: // nom purisā acc purise instr purisehi dat purisānaṃ // abl purisānaṃ gen purisānaṃ loc purisesu voc purisā // // The caller passes the stem (without final vowel), e.g. "purisa". // Detection: strip the final -o (nom sg) or use the stem directly. fn pi_decline_a_masc_sg(stem: String, gram_case: String) -> String { if str_eq(gram_case, "nominative") { return stem + "o" } if str_eq(gram_case, "accusative") { return stem + "ṃ" } if str_eq(gram_case, "instrumental") { return stem + "ena" } if str_eq(gram_case, "dative") { return stem + "āya" } if str_eq(gram_case, "ablative") { return stem + "ā" } if str_eq(gram_case, "genitive") { return stem + "ssa" } if str_eq(gram_case, "locative") { return stem + "smiṃ" } if str_eq(gram_case, "vocative") { return stem } // Default: nominative return stem + "o" } fn pi_decline_a_masc_pl(stem: String, gram_case: String) -> String { if str_eq(gram_case, "nominative") { return stem + "ā" } if str_eq(gram_case, "accusative") { return stem + "e" } if str_eq(gram_case, "instrumental") { return stem + "ehi" } if str_eq(gram_case, "dative") { return stem + "ānaṃ" } if str_eq(gram_case, "ablative") { return stem + "ānaṃ" } if str_eq(gram_case, "genitive") { return stem + "ānaṃ" } if str_eq(gram_case, "locative") { return stem + "esu" } if str_eq(gram_case, "vocative") { return stem + "ā" } return stem + "ā" } // ── ā-stem feminine declension ("kaññā" — girl) ─────────────────────────────── // // ā-stem feminines are the second most common class. Paradigm based on "kaññā". // // Singular: // nom kaññā acc kaññaṃ instr kaññāya dat kaññāya // abl kaññāya gen kaññāya loc kaññāyaṃ voc kaññe // // Plural (approximated from standard tables; many forms merge): // nom kaññā acc kaññā instr kaññāhi dat kaññānaṃ // abl kaññānaṃ gen kaññānaṃ loc kaññāsu voc kaññā // // The caller passes the stem without the final ā, e.g. "kaññ". fn pi_decline_a_fem_sg(stem: String, gram_case: String) -> String { if str_eq(gram_case, "nominative") { return stem + "ā" } if str_eq(gram_case, "accusative") { return stem + "aṃ" } if str_eq(gram_case, "instrumental") { return stem + "āya" } if str_eq(gram_case, "dative") { return stem + "āya" } if str_eq(gram_case, "ablative") { return stem + "āya" } if str_eq(gram_case, "genitive") { return stem + "āya" } if str_eq(gram_case, "locative") { return stem + "āyaṃ" } if str_eq(gram_case, "vocative") { return stem + "e" } return stem + "ā" } fn pi_decline_a_fem_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, "instrumental") { return stem + "āhi" } if str_eq(gram_case, "dative") { return stem + "ānaṃ" } if str_eq(gram_case, "ablative") { return stem + "ānaṃ" } if str_eq(gram_case, "genitive") { return stem + "ānaṃ" } if str_eq(gram_case, "locative") { return stem + "āsu" } if str_eq(gram_case, "vocative") { return stem + "ā" } return stem + "ā" } // ── Declension class detection ───────────────────────────────────────────────── // // Detect whether a noun is an a-stem masculine or ā-stem feminine based on its // citation form (nominative singular). // // ends in -o -> a-stem masculine (puriso, devo, loko) // ends in -ā -> ā-stem feminine (kaññā, mātu[?], nadī) // ends in -a -> treat as a-stem masculine stem (some words cited without -o) // // For other stems the module falls back to a-stem masculine as the safest default. fn pi_detect_class(noun: String) -> String { if pi_str_ends(noun, "o") { return "a_masc" } if pi_str_ends(noun, "ā") { return "a_fem" } // Some a-masc nouns may be cited as stems ending in -a if pi_str_ends(noun, "a") { return "a_masc" } // Default to a-stem masculine return "a_masc" } // ── pi_decline: main declension entry point ─────────────────────────────────── // // noun: Pali nominative singular (citation form, e.g. "puriso", "kaññā") // gram_case: "nominative" | "accusative" | "instrumental" | "dative" | // "ablative" | "genitive" | "locative" | "vocative" // number: "singular" | "plural" // // Returns the inflected form. Falls back to the citation form on unknown input. fn pi_decline(noun: String, gram_case: String, number: String) -> String { let nclass: String = pi_detect_class(noun) if str_eq(nclass, "a_masc") { // Derive stem: strip final -o or -a to get the consonant stem let stem: String = noun if pi_str_ends(noun, "o") { let stem = pi_drop(noun, 1) } if pi_str_ends(noun, "a") { let stem = pi_drop(noun, 1) } if str_eq(number, "singular") { return pi_decline_a_masc_sg(stem, gram_case) } return pi_decline_a_masc_pl(stem, gram_case) } if str_eq(nclass, "a_fem") { // Derive stem: strip final -ā let stem: String = noun if pi_str_ends(noun, "ā") { let stem = pi_drop(noun, 1) } if str_eq(number, "singular") { return pi_decline_a_fem_sg(stem, gram_case) } return pi_decline_a_fem_pl(stem, gram_case) } // Unknown class: return citation form unchanged return noun } // ── pi_noun_phrase: noun phrase builder ────────────────────────────────────── // // Pali has no articles (definite or indefinite). The declined form is the // complete noun phrase. The definite parameter is accepted for interface // compatibility with other language modules but has no effect. // // noun: nominative singular Pali noun (e.g. "puriso", "kaññā") // gram_case: "nominative" | "accusative" | "instrumental" | "dative" | // "ablative" | "genitive" | "locative" | "vocative" // number: "singular" | "plural" // definite: ignored (Pali has no articles) // // Returns the inflected noun form. fn pi_noun_phrase(noun: String, gram_case: String, number: String, definite: String) -> String { return pi_decline(noun, gram_case, number) }