// morphology-txb.el - Tocharian B morphology for the NLG engine. // // Implements Tocharian B verb conjugation and noun declension for the ca. 500-1000 CE // period. Designed as a companion to morphology.el and called by the engine when // the language profile code is "txb". // // Language profile: code=txb, name=Tocharian B, morph_type=fusional, word_order=SOV, // question_strategy=particle, script=latin, family=tocharian. // // Tocharian B is an extinct Indo-European language attested in the Tarim Basin // (modern Xinjiang, China). Most surviving texts are Buddhist in content. // The transliteration used here follows standard scholarly convention (Sieg, Siegling, // Winter); subscript dots are omitted in identifiers but retained in string literals. // // Verb conjugation covered: // Tenses: present (class I endings), imperfect not implemented // Persons: first/second/third x singular/plural (slots 0-5) // Irregulars: käm- (to come), yä- (to go), wes-/ste (to be), // lyut- (to see), wak- (to speak) // Canonical map: "be" -> "ste" (3sg) / "wes" (other) // // Noun declension covered: // Masculine o-stem: nom/acc/gen/dat x sg/pl // Feminine ā-stem: nom/acc/gen/dat x sg/pl // (The full 8-case paradigm is simplified to 4 cases for the engine.) // Number: singular, plural // Articles: none — Tocharian B has no article system // // Depends on: morphology.el (str_eq, str_len, str_slice, str_ends_with) // ── String helpers ───────────────────────────────────────────────────────────── fn txb_drop(s: String, n: Int) -> String { let len: Int = str_len(s) if n >= len { return "" } return str_slice(s, 0, len - n) } fn txb_ends(s: String, suf: String) -> Bool { return str_ends_with(s, suf) } // ── Person/number slot ───────────────────────────────────────────────────────── // // Maps person x number to a 0-based paradigm slot. // 0 = 1st singular // 1 = 2nd singular // 2 = 3rd singular (most frequently attested) // 3 = 1st plural // 4 = 2nd plural // 5 = 3rd plural fn txb_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 class I endings ──────────────────────────────────────────────────── // // Tocharian B present class I (most regular verbs). The full paradigm is // complex with active/medio-passive distinction; only the active series is // implemented here. // // Active present class I endings applied to stem: // Sg: 1st -au 2nd -ät 3rd -em // Pl: 1st -emane 2nd -em 3rd -em // // Note: the plural endings show extensive syncretism in the attested corpus. fn txb_pres1_suffix(slot: Int) -> String { if slot == 0 { return "au" } if slot == 1 { return "ät" } if slot == 2 { return "em" } if slot == 3 { return "emane" } if slot == 4 { return "em" } return "em" } // ── Irregular verb tables ────────────────────────────────────────────────────── // käm- (to come) — suppletive present paradigm // kam, käm, käm, kamnäṃ, kamnäṃ, kamnäṃ fn txb_kam_present(slot: Int) -> String { if slot == 0 { return "kam" } if slot == 1 { return "käm" } if slot == 2 { return "käm" } if slot == 3 { return "kamnäṃ" } if slot == 4 { return "kamnäṃ" } return "kamnäṃ" } // yä- (to go) — class IV verb; short stem // yau, yät, yäm, ymäṃ, ymäṃ, yänmäṃ fn txb_ya_present(slot: Int) -> String { if slot == 0 { return "yau" } if slot == 1 { return "yät" } if slot == 2 { return "yäm" } if slot == 3 { return "ymäṃ" } if slot == 4 { return "ymäṃ" } return "yänmäṃ" } // wes- / ste (to be) — the copula/existential "be" verb // 3sg form "ste" is by far the most attested; elsewhere "wes" is used. // Simplified paradigm: "ste" for 3sg, "wes" for all other slots. fn txb_wes_present(slot: Int) -> String { if slot == 2 { return "ste" } return "wes" } // lyut- (to see) — class I regular stem, fully attested // lyutau, lyutät, lyutem, lyutemane, lyutem, lyutem fn txb_lyut_present(slot: Int) -> String { if slot == 0 { return "lyutau" } if slot == 1 { return "lyutät" } if slot == 2 { return "lyutem" } if slot == 3 { return "lyutemane" } if slot == 4 { return "lyutem" } return "lyutem" } // wak- (to speak) — class I regular stem // wakau, wakät, wakem, wakemane, wakem, wakem fn txb_wak_present(slot: Int) -> String { if slot == 0 { return "wakau" } if slot == 1 { return "wakät" } if slot == 2 { return "wakem" } if slot == 3 { return "wakemane" } if slot == 4 { return "wakem" } return "wakem" } // ── Canonical verb mapping ───────────────────────────────────────────────────── // // Maps English semantic labels to Tocharian B citation forms (verbal stems). fn txb_map_canonical(verb: String) -> String { if str_eq(verb, "be") { return "wes" } if str_eq(verb, "come") { return "käm" } if str_eq(verb, "go") { return "yä" } if str_eq(verb, "see") { return "lyut" } if str_eq(verb, "speak") { return "wak" } if str_eq(verb, "say") { return "wak" } return verb } // ── txb_conjugate: main conjugation entry point ─────────────────────────────── // // verb: Tocharian B stem or English canonical label // tense: "present" (only present is implemented; others return base form) // person: "first" | "second" | "third" // number: "singular" | "plural" // // Returns the inflected form. For unknown verbs the stem is returned as-is — // the corpus is small enough that most productive verbs are known to the engine. fn txb_conjugate(verb: String, tense: String, person: String, number: String) -> String { let v: String = txb_map_canonical(verb) let slot: Int = txb_slot(person, number) // ── wes-/ste (to be) ────────────────────────────────────────────────────── if str_eq(v, "wes") { if str_eq(tense, "present") { return txb_wes_present(slot) } return v } // ── käm- (to come) ──────────────────────────────────────────────────────── if str_eq(v, "käm") { if str_eq(tense, "present") { return txb_kam_present(slot) } return v } // ── yä- (to go) ─────────────────────────────────────────────────────────── if str_eq(v, "yä") { if str_eq(tense, "present") { return txb_ya_present(slot) } return v } // ── lyut- (to see) ──────────────────────────────────────────────────────── if str_eq(v, "lyut") { if str_eq(tense, "present") { return txb_lyut_present(slot) } return v } // ── wak- (to speak) ─────────────────────────────────────────────────────── if str_eq(v, "wak") { if str_eq(tense, "present") { return txb_wak_present(slot) } return v } // ── Regular class I verb ────────────────────────────────────────────────── // // Apply present class I endings directly to the stem. if str_eq(tense, "present") { return v + txb_pres1_suffix(slot) } // Unknown tense: return base form return v } // ── Masculine o-stem declension ─────────────────────────────────────────────── // // Tocharian B masculine o-stem endings (simplified 4-case system): // Singular: nom -e, acc -e, gen -entse, dat -ene // Plural: nom -i, acc -i, gen -entwetse, dat -ene // // The o-stem masculine is the most common nominal class. The nom/acc syncretism // in the singular is a characteristic Tocharian B feature. fn txb_decline_masc(noun: String, gram_case: String, number: String) -> String { if str_eq(number, "singular") { if str_eq(gram_case, "nominative") { return noun + "e" } if str_eq(gram_case, "accusative") { return noun + "e" } if str_eq(gram_case, "genitive") { return noun + "entse" } if str_eq(gram_case, "dative") { return noun + "ene" } return noun + "e" } // plural if str_eq(gram_case, "nominative") { return noun + "i" } if str_eq(gram_case, "accusative") { return noun + "i" } if str_eq(gram_case, "genitive") { return noun + "entwetse" } if str_eq(gram_case, "dative") { return noun + "ene" } return noun + "i" } // ── Feminine ā-stem declension ──────────────────────────────────────────────── // // Tocharian B feminine ā-stem endings (simplified 4-case system): // Singular: nom -a, acc -a, gen -antse, dat -ane // Plural: nom -ä, acc -ä, gen -antse, dat -ane // // The ā-stem feminine shows the same nom/acc syncretism as the masculine. fn txb_decline_fem(noun: String, gram_case: String, number: String) -> String { if str_eq(number, "singular") { if str_eq(gram_case, "nominative") { return noun + "a" } if str_eq(gram_case, "accusative") { return noun + "a" } if str_eq(gram_case, "genitive") { return noun + "antse" } if str_eq(gram_case, "dative") { return noun + "ane" } return noun + "a" } // plural if str_eq(gram_case, "nominative") { return noun + "ä" } if str_eq(gram_case, "accusative") { return noun + "ä" } if str_eq(gram_case, "genitive") { return noun + "antse" } if str_eq(gram_case, "dative") { return noun + "ane" } return noun + "ä" } // ── Gender detection heuristic ───────────────────────────────────────────────── // // In Tocharian B the gender of a noun must ideally be supplied by the lexicon. // Gender was originally inherited from Proto-Indo-European but many neuters // merged into masculine. This heuristic defaults to masculine; known feminine // items should be supplied via the vocabulary layer. fn txb_detect_gender(noun: String) -> String { // No reliable phonological gender markers in Tocharian B stems. // Default: masculine. return "masculine" } // ── txb_decline: main declension entry point ────────────────────────────────── // // noun: Tocharian B noun stem (uninflected base form) // gram_case: "nominative" | "accusative" | "genitive" | "dative" // number: "singular" | "plural" // // Returns the inflected form. Falls back to the bare stem on unknown input. fn txb_decline(noun: String, gram_case: String, number: String) -> String { let gender: String = txb_detect_gender(noun) if str_eq(gender, "feminine") { return txb_decline_fem(noun, gram_case, number) } // masculine (default) return txb_decline_masc(noun, gram_case, number) } // ── txb_noun_phrase: noun phrase builder ────────────────────────────────────── // // Tocharian B has no article system. Definiteness was not grammatically marked // by a separate morpheme — context and word order served that function. // This function therefore ignores the "definite" parameter and returns the // declined noun form directly. // // noun: Tocharian B noun stem // gram_case: "nominative" | "accusative" | "genitive" | "dative" // number: "singular" | "plural" // definite: "true" | "false" (ignored — no articles in Tocharian B) // // Returns the declined noun form. fn txb_noun_phrase(noun: String, gram_case: String, number: String, definite: String) -> String { return txb_decline(noun, gram_case, number) }