Rename: nlg → elp (Engram Language Protocol)

This commit is contained in:
Will Anderson
2026-05-02 22:15:25 -05:00
parent cbb27b8d87
commit 34725a3988
125 changed files with 129214 additions and 780 deletions
+420
View File
@@ -0,0 +1,420 @@
// 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. 14001200 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 (23 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
}