421 lines
16 KiB
EmacsLisp
421 lines
16 KiB
EmacsLisp
// morphology-uga.el - Ugaritic morphology for the NLG engine.
|
||
// 𐎀𐎎𐎗𐎚 — Ugaritic, the language of the city-state of Ugarit.
|
||
//
|
||
// Implements Ugaritic verb conjugation (G-stem, suffix and prefix conjugations),
|
||
// noun declension (three cases, gender, number, dual), and noun-phrase construction.
|
||
//
|
||
// Ugaritic is a Northwest Semitic language (ca. 1400–1200 BCE), written in a
|
||
// 30-character alphabetic cuneiform. It is closely related to Biblical Hebrew
|
||
// and Phoenician. We work in standard Latin transliteration:
|
||
// ʼ = aleph (glottal stop) ʻ = ʿayin ḫ = het ġ = ghayin
|
||
// ṯ = thet ẓ = emphatic z š = shin ṣ = tsade
|
||
//
|
||
// Language profile:
|
||
// code=uga, name=Ugaritic, morph_type=semitic, word_order=VSO,
|
||
// script=alphabetic cuneiform (transliterated), family=semitic/northwest-semitic
|
||
//
|
||
// Key grammatical facts:
|
||
// - Semitic trilateral root system (nearly identical to Hebrew/Arabic)
|
||
// - Grammatical gender: masculine / feminine
|
||
// - Cases: nominative (-u), accusative (-a), genitive (-i)
|
||
// (mirrors Akkadian but with shorter endings — no mimation in Ugaritic)
|
||
// - Number: singular / plural / dual
|
||
// - Dual: nom -āma, gen/acc -ēma
|
||
// - Verb tenses:
|
||
// qtl (perfect, suffix conjugation) — completed action
|
||
// yqtl (imperfect, prefix conjugation) — ongoing / future action
|
||
// - Verb stems: G, D, Š, N (this file: G-stem throughout)
|
||
// - No separate definite article (unlike later Hebrew ha-)
|
||
// - Copula: root kn (to be)
|
||
//
|
||
// Verb conjugation conventions:
|
||
// person: "first" | "second" | "third"
|
||
// gender: "m" | "f"
|
||
// number: "singular" | "plural"
|
||
// tense: "perfect" | "imperfect"
|
||
//
|
||
// Noun declension conventions:
|
||
// gram_case: "nom" | "acc" | "gen"
|
||
// number: "singular" | "plural" | "dual"
|
||
//
|
||
// Verbs covered (G-stem root, transliterated):
|
||
// "kn" — to be (copula)
|
||
// "hlk" — to go
|
||
// "rʼy" — to see
|
||
// "ʼmr" — to say
|
||
//
|
||
// Depends on: morphology.el (str_eq, str_len, str_slice, str_ends_with)
|
||
|
||
// ── String helpers ─────────────────────────────────────────────────────────────
|
||
|
||
fn uga_str_ends(s: String, suf: String) -> Bool {
|
||
return str_ends_with(s, suf)
|
||
}
|
||
|
||
fn uga_str_len(s: String) -> Int {
|
||
return str_len(s)
|
||
}
|
||
|
||
fn uga_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 index.
|
||
// Gender is used separately for third-person disambiguation.
|
||
//
|
||
// Slot layout (6 primary cells, matching classic NW-Semitic paradigm):
|
||
// 0 = 1sg (I)
|
||
// 1 = 2sg (you sg — masc and fem conflate in many forms)
|
||
// 2 = 3sg m (he)
|
||
// 3 = 3sg f (she)
|
||
// 4 = 1pl (we)
|
||
// 5 = 3pl (they — masc default)
|
||
|
||
fn uga_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
|
||
}
|
||
|
||
// uga_slot_g: gender-sensitive slot for third-person singular.
|
||
fn uga_slot_g(person: String, gender: String, number: String) -> Int {
|
||
let base: Int = uga_slot(person, number)
|
||
if str_eq(person, "third") {
|
||
if str_eq(number, "singular") {
|
||
if str_eq(gender, "f") { return 3 }
|
||
}
|
||
}
|
||
return base
|
||
}
|
||
|
||
// ── Copula: kn — to be ────────────────────────────────────────────────────────
|
||
//
|
||
// qtl (perfect): kāna (3sg m), kānat (3sg f), kānu (3pl)
|
||
// yqtl (imperfect): yakūnu (3sg m), takūnu (3sg f / 2sg), ʼakūnu (1sg)
|
||
//
|
||
// The root kn is a weak verb (hollow: middle w/y). The perfect stem is kāna-;
|
||
// the imperfect stem is -kūn-.
|
||
|
||
fn uga_kn_perfect(slot: Int) -> String {
|
||
if slot == 0 { return "kāntu" } // 1sg (kān- + suffix -tu)
|
||
if slot == 1 { return "kānta" } // 2sg (-ta suffix)
|
||
if slot == 2 { return "kāna" } // 3sg m (base)
|
||
if slot == 3 { return "kānat" } // 3sg f (-at suffix)
|
||
if slot == 4 { return "kānnu" } // 1pl (-nu suffix)
|
||
return "kānu" // 3pl (-u suffix)
|
||
}
|
||
|
||
fn uga_kn_imperfect(slot: Int) -> String {
|
||
if slot == 0 { return "ʼakūnu" } // 1sg (ʼa- prefix)
|
||
if slot == 1 { return "takūnu" } // 2sg (ta- prefix)
|
||
if slot == 2 { return "yakūnu" } // 3sg m (ya- prefix)
|
||
if slot == 3 { return "takūnu" } // 3sg f (ta- prefix, same as 2sg)
|
||
if slot == 4 { return "nakūnu" } // 1pl (na- prefix)
|
||
return "yakūnuna" // 3pl (ya- + -ūna energic suffix)
|
||
}
|
||
|
||
fn uga_is_copula(verb: String) -> Bool {
|
||
if str_eq(verb, "kn") { return true }
|
||
if str_eq(verb, "kāna") { return true }
|
||
if str_eq(verb, "be") { return true }
|
||
return false
|
||
}
|
||
|
||
fn uga_conjugate_copula(tense: String, slot: Int) -> String {
|
||
if str_eq(tense, "perfect") { return uga_kn_perfect(slot) }
|
||
return uga_kn_imperfect(slot)
|
||
}
|
||
|
||
// ── hlk — to go ───────────────────────────────────────────────────────────────
|
||
//
|
||
// Regular strong verb. Perfect stem: halak-; imperfect stem: -hluk-.
|
||
|
||
fn uga_hlk_perfect(slot: Int) -> String {
|
||
if slot == 0 { return "halaktu" } // 1sg
|
||
if slot == 1 { return "halakta" } // 2sg
|
||
if slot == 2 { return "halaka" } // 3sg m
|
||
if slot == 3 { return "halakat" } // 3sg f
|
||
if slot == 4 { return "halaknu" } // 1pl
|
||
return "halaku" // 3pl
|
||
}
|
||
|
||
fn uga_hlk_imperfect(slot: Int) -> String {
|
||
if slot == 0 { return "ʼahluku" } // 1sg
|
||
if slot == 1 { return "tahluku" } // 2sg
|
||
if slot == 2 { return "yahluku" } // 3sg m
|
||
if slot == 3 { return "tahluku" } // 3sg f
|
||
if slot == 4 { return "nahluku" } // 1pl
|
||
return "yahlukuna" // 3pl
|
||
}
|
||
|
||
// ── rʼy — to see ──────────────────────────────────────────────────────────────
|
||
//
|
||
// Third-weak verb (final y). Perfect: raʼaya (3sg m); imperfect: yarʼā (3sg m).
|
||
|
||
fn uga_ray_perfect(slot: Int) -> String {
|
||
if slot == 0 { return "raʼaytu" } // 1sg
|
||
if slot == 1 { return "raʼayta" } // 2sg
|
||
if slot == 2 { return "raʼaya" } // 3sg m
|
||
if slot == 3 { return "raʼayat" } // 3sg f
|
||
if slot == 4 { return "raʼaynu" } // 1pl
|
||
return "raʼayu" // 3pl
|
||
}
|
||
|
||
fn uga_ray_imperfect(slot: Int) -> String {
|
||
if slot == 0 { return "ʼarʼā" } // 1sg
|
||
if slot == 1 { return "tarʼā" } // 2sg
|
||
if slot == 2 { return "yarʼā" } // 3sg m
|
||
if slot == 3 { return "tarʼā" } // 3sg f
|
||
if slot == 4 { return "narʼā" } // 1pl
|
||
return "yarʼayna" // 3pl (fem plural ending for final-weak)
|
||
}
|
||
|
||
// ── ʼmr — to say ──────────────────────────────────────────────────────────────
|
||
//
|
||
// Strong verb. Perfect: ʼamara (3sg m); imperfect: yaʼmuru (3sg m).
|
||
|
||
fn uga_amr_perfect(slot: Int) -> String {
|
||
if slot == 0 { return "ʼamartu" } // 1sg
|
||
if slot == 1 { return "ʼamarta" } // 2sg
|
||
if slot == 2 { return "ʼamara" } // 3sg m
|
||
if slot == 3 { return "ʼamarat" } // 3sg f
|
||
if slot == 4 { return "ʼamarnu" } // 1pl
|
||
return "ʼamaru" // 3pl
|
||
}
|
||
|
||
fn uga_amr_imperfect(slot: Int) -> String {
|
||
if slot == 0 { return "ʼaʼmuru" } // 1sg
|
||
if slot == 1 { return "taʼmuru" } // 2sg
|
||
if slot == 2 { return "yaʼmuru" } // 3sg m
|
||
if slot == 3 { return "taʼmuru" } // 3sg f
|
||
if slot == 4 { return "naʼmuru" } // 1pl
|
||
return "yaʼmuruna" // 3pl
|
||
}
|
||
|
||
// ── Generic G-stem paradigm (regular strong verbs) ────────────────────────────
|
||
//
|
||
// For verbs not in the lookup table, apply the regular pattern.
|
||
// Caller supplies the 3sg perfect form (qtl) and 3sg imperfect (yqtl) form.
|
||
// We derive person forms by suffix/prefix substitution.
|
||
|
||
fn uga_generic_perfect(base3sg: String, slot: Int) -> String {
|
||
// Perfect suffixes: 1sg -tu, 2sg -ta, 3sg m ∅, 3sg f -at, 1pl -nu, 3pl -u
|
||
if slot == 0 { return base3sg + "tu" }
|
||
if slot == 1 { return base3sg + "ta" }
|
||
if slot == 2 { return base3sg }
|
||
if slot == 3 { return base3sg + "at" }
|
||
if slot == 4 { return base3sg + "nu" }
|
||
return base3sg + "u"
|
||
}
|
||
|
||
fn uga_generic_imperfect(base3sg: String, slot: Int) -> String {
|
||
// Strip leading ya- to get the core, re-prefix per person.
|
||
// This is a heuristic for display purposes when the stem is unknown.
|
||
if slot == 0 { return "ʼa" + base3sg }
|
||
if slot == 1 { return "ta" + base3sg }
|
||
if slot == 2 { return "ya" + base3sg }
|
||
if slot == 3 { return "ta" + base3sg }
|
||
if slot == 4 { return "na" + base3sg }
|
||
return "ya" + base3sg + "una"
|
||
}
|
||
|
||
// ── Known-verb dispatcher ─────────────────────────────────────────────────────
|
||
|
||
fn uga_known_verb(verb: String, tense: String, slot: Int) -> String {
|
||
// kn — to be
|
||
if str_eq(verb, "kn") {
|
||
return uga_conjugate_copula(tense, slot)
|
||
}
|
||
if str_eq(verb, "kāna") {
|
||
return uga_conjugate_copula(tense, slot)
|
||
}
|
||
|
||
// hlk — to go
|
||
if str_eq(verb, "hlk") {
|
||
if str_eq(tense, "perfect") { return uga_hlk_perfect(slot) }
|
||
return uga_hlk_imperfect(slot)
|
||
}
|
||
if str_eq(verb, "halaka") {
|
||
if str_eq(tense, "perfect") { return uga_hlk_perfect(slot) }
|
||
return uga_hlk_imperfect(slot)
|
||
}
|
||
|
||
// rʼy — to see
|
||
if str_eq(verb, "rʼy") {
|
||
if str_eq(tense, "perfect") { return uga_ray_perfect(slot) }
|
||
return uga_ray_imperfect(slot)
|
||
}
|
||
if str_eq(verb, "raʼaya") {
|
||
if str_eq(tense, "perfect") { return uga_ray_perfect(slot) }
|
||
return uga_ray_imperfect(slot)
|
||
}
|
||
|
||
// ʼmr — to say
|
||
if str_eq(verb, "ʼmr") {
|
||
if str_eq(tense, "perfect") { return uga_amr_perfect(slot) }
|
||
return uga_amr_imperfect(slot)
|
||
}
|
||
if str_eq(verb, "ʼamara") {
|
||
if str_eq(tense, "perfect") { return uga_amr_perfect(slot) }
|
||
return uga_amr_imperfect(slot)
|
||
}
|
||
|
||
return ""
|
||
}
|
||
|
||
// ── Main conjugation entry point ──────────────────────────────────────────────
|
||
//
|
||
// uga_conjugate: conjugate a Ugaritic verb (G-stem).
|
||
//
|
||
// verb: root (2–3 consonants, e.g. "kn", "hlk") or 3sg perfect citation form
|
||
// tense: "perfect" | "imperfect"
|
||
// person: "first" | "second" | "third"
|
||
// number: "singular" | "plural"
|
||
//
|
||
// Returns:
|
||
// - Inflected form for known verbs
|
||
// - verb unchanged as safe fallback for unknown verbs
|
||
|
||
fn uga_conjugate(verb: String, tense: String, person: String, number: String) -> String {
|
||
let slot: Int = uga_slot(person, number)
|
||
|
||
if uga_is_copula(verb) {
|
||
return uga_conjugate_copula(tense, slot)
|
||
}
|
||
|
||
let known: String = uga_known_verb(verb, tense, slot)
|
||
if !str_eq(known, "") {
|
||
return known
|
||
}
|
||
|
||
return verb
|
||
}
|
||
|
||
// ── Noun declension ────────────────────────────────────────────────────────────
|
||
//
|
||
// uga_decline: decline a Ugaritic noun for gram_case and number.
|
||
//
|
||
// Ugaritic case endings (no mimation — unlike Akkadian):
|
||
// Masculine singular:
|
||
// Nominative: -u (e.g. malku "king")
|
||
// Accusative: -a (e.g. malka)
|
||
// Genitive: -i (e.g. malki)
|
||
// Masculine plural:
|
||
// Nominative: -ūma (e.g. malkūma)
|
||
// Genitive/Accusative: -īma (e.g. malkīma)
|
||
// Feminine singular (typically -atu base):
|
||
// Nominative: -atu (e.g. malaktu)
|
||
// Accusative: -ata (e.g. malakta)
|
||
// Genitive: -ati (e.g. malakti)
|
||
// Feminine plural:
|
||
// Nominative: -ātu (e.g. malakātu)
|
||
// Genitive/Accusative: -āti
|
||
// Dual:
|
||
// Nominative: -āma (e.g. malkāma)
|
||
// Genitive/Accusative: -ēma
|
||
//
|
||
// Strategy: strip the known nominative ending to get the bare stem,
|
||
// then apply the requested ending.
|
||
|
||
fn uga_strip_nom(noun: String) -> String {
|
||
// Strip masc sg -u
|
||
if uga_str_ends(noun, "u") {
|
||
let len: Int = uga_str_len(noun)
|
||
if len > 1 {
|
||
return uga_str_drop_last(noun, 1)
|
||
}
|
||
}
|
||
// Strip fem sg -atu
|
||
if uga_str_ends(noun, "atu") {
|
||
return uga_str_drop_last(noun, 3)
|
||
}
|
||
return noun
|
||
}
|
||
|
||
fn uga_is_fem(noun: String) -> Bool {
|
||
if uga_str_ends(noun, "atu") { return true }
|
||
if uga_str_ends(noun, "ata") { return true }
|
||
if uga_str_ends(noun, "ati") { return true }
|
||
if uga_str_ends(noun, "ātu") { return true }
|
||
if uga_str_ends(noun, "āti") { return true }
|
||
return false
|
||
}
|
||
|
||
fn uga_decline(noun: String, gram_case: String, number: String) -> String {
|
||
let fem: Bool = uga_is_fem(noun)
|
||
let stem: String = uga_strip_nom(noun)
|
||
|
||
if str_eq(number, "dual") {
|
||
if str_eq(gram_case, "nom") { return stem + "āma" }
|
||
return stem + "ēma"
|
||
}
|
||
|
||
if str_eq(number, "plural") {
|
||
if fem {
|
||
if str_eq(gram_case, "nom") { return stem + "ātu" }
|
||
return stem + "āti"
|
||
}
|
||
// Masculine plural
|
||
if str_eq(gram_case, "nom") { return stem + "ūma" }
|
||
return stem + "īma"
|
||
}
|
||
|
||
// Singular
|
||
if fem {
|
||
if str_eq(gram_case, "nom") { return stem + "atu" }
|
||
if str_eq(gram_case, "acc") { return stem + "ata" }
|
||
if str_eq(gram_case, "gen") { return stem + "ati" }
|
||
return stem + "atu"
|
||
}
|
||
// Masculine singular
|
||
if str_eq(gram_case, "nom") { return stem + "u" }
|
||
if str_eq(gram_case, "acc") { return stem + "a" }
|
||
if str_eq(gram_case, "gen") { return stem + "i" }
|
||
return stem + "u"
|
||
}
|
||
|
||
// ── Noun phrase ────────────────────────────────────────────────────────────────
|
||
//
|
||
// uga_noun_phrase: produce the surface noun phrase.
|
||
//
|
||
// Ugaritic has no separate definite article (unlike later Hebrew ha-).
|
||
// Determination is expressed through word order, the construct chain (genitive),
|
||
// and pronominal suffixes. The definite parameter is accepted for interface
|
||
// uniformity but has no surface effect.
|
||
//
|
||
// noun: base noun (nominative singular form, e.g. "malku")
|
||
// gram_case: "nom" | "acc" | "gen"
|
||
// number: "singular" | "plural" | "dual"
|
||
// definite: "true" | "false" (no surface effect in Ugaritic)
|
||
|
||
fn uga_noun_phrase(noun: String, gram_case: String, number: String, definite: String) -> String {
|
||
return uga_decline(noun, gram_case, number)
|
||
}
|
||
|
||
// ── Canonical verb mapping ─────────────────────────────────────────────────────
|
||
//
|
||
// uga_map_canonical: map cross-lingual English canonical verb labels to
|
||
// Ugaritic G-stem roots or citation forms.
|
||
|
||
fn uga_map_canonical(verb: String) -> String {
|
||
if str_eq(verb, "be") { return "kn" }
|
||
if str_eq(verb, "go") { return "hlk" }
|
||
if str_eq(verb, "see") { return "rʼy" }
|
||
if str_eq(verb, "say") { return "ʼmr" }
|
||
if str_eq(verb, "speak") { return "ʼmr" }
|
||
return verb
|
||
}
|