680 lines
26 KiB
EmacsLisp
680 lines
26 KiB
EmacsLisp
// morphology-got.el - Gothic morphology for the NLG engine.
|
||
//
|
||
// Implements Gothic verb conjugation and noun declension using standard
|
||
// Gothic Latin romanisation. Designed as a companion to morphology.el and
|
||
// called by the engine when the language profile code is "got".
|
||
//
|
||
// Language profile: code=got, name=Gothic, morph_type=fusional,
|
||
// word_order=SOV, question_strategy=intonation, script=gothic-latin,
|
||
// family=germanic.
|
||
//
|
||
// Historical note: Gothic is attested primarily in the 4th-century Bible
|
||
// translation by Bishop Wulfila (Ulfilas). It is the earliest substantially
|
||
// attested Germanic language and preserves many archaic features lost in later
|
||
// branches (e.g. distinct dual, mediopassive voice, four-case system).
|
||
//
|
||
// Romanisation conventions used in this file:
|
||
// þ — thorn (voiced/voiceless dental fricative; like "th" in "the/thin")
|
||
// ƕ — hwair (Gothic hw-, like "wh" in older English "what")
|
||
// q — Gothic q (labiovelar stop, like "qu")
|
||
// ei — long /iː/ (Gothic digraph)
|
||
// ai — short /ɛ/ (Gothic digraph, not a diphthong)
|
||
// au — short /ɔ/ (Gothic digraph, not a diphthong)
|
||
// Standard vowels: a e i u (short)
|
||
//
|
||
// Verb conjugation covered:
|
||
// Tenses: present indicative active, past indicative active
|
||
// Persons: first/second/third × singular/plural
|
||
// Classes: weak class 1 (-jan verbs), weak class 2 (-on verbs) as
|
||
// the regular productive paths
|
||
// Irregulars: wisan (be), haban (have), gaggan (go), saihwan (see),
|
||
// qiþan (say), niman (take)
|
||
// Canonical map: "be" -> "wisan"
|
||
//
|
||
// Noun declension covered:
|
||
// Cases: nominative, accusative, genitive, dative (all 4 Gothic cases)
|
||
// Stem types: a-stem masc (paradigm: dags — day),
|
||
// o-stem fem (paradigm: gibo — gift),
|
||
// n-stem masc (paradigm: guma — man)
|
||
// Numbers: singular, plural
|
||
//
|
||
// Demonstrative / article:
|
||
// Gothic has no definite article proper. The demonstrative pronoun
|
||
// sa (masc) / so (fem) / þata (neut) functions as a near-definite
|
||
// determiner. got_noun_phrase prepends "sa" (masc) or "þo" (fem) when
|
||
// definite=true. Gender must be inferred from stem class: a-stem → masc,
|
||
// o-stem → fem, n-stem → masc.
|
||
//
|
||
// Depends on: morphology.el (str_eq, str_len, str_slice, str_ends_with)
|
||
|
||
// ── String helpers ─────────────────────────────────────────────────────────────
|
||
|
||
fn got_str_ends(s: String, suf: String) -> Bool {
|
||
return str_ends_with(s, suf)
|
||
}
|
||
|
||
fn got_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)
|
||
}
|
||
|
||
// ── Person/number slot ─────────────────────────────────────────────────────────
|
||
//
|
||
// Gothic has singular, dual, and plural. The dual is historically attested
|
||
// for first and second person only (e.g. 1du wit, 2du jut) but is quite rare
|
||
// even in the Gothic corpus. For NLG simplicity the dual is treated as plural.
|
||
//
|
||
// 0 = 1st singular
|
||
// 1 = 2nd singular
|
||
// 2 = 3rd singular
|
||
// 3 = 1st plural
|
||
// 4 = 2nd plural
|
||
// 5 = 3rd plural
|
||
|
||
fn got_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 person
|
||
if str_eq(number, "singular") { return 2 }
|
||
return 5
|
||
}
|
||
|
||
// ── Canonical verb mapping ─────────────────────────────────────────────────────
|
||
//
|
||
// English semantic-layer labels are mapped to Gothic dictionary forms before
|
||
// conjugation. Dictionary forms are the infinitive.
|
||
|
||
fn got_map_canonical(verb: String) -> String {
|
||
if str_eq(verb, "be") { return "wisan" }
|
||
if str_eq(verb, "have") { return "haban" }
|
||
if str_eq(verb, "go") { return "gaggan" }
|
||
if str_eq(verb, "see") { return "saihwan" }
|
||
if str_eq(verb, "say") { return "qiþan" }
|
||
if str_eq(verb, "take") { return "niman" }
|
||
if str_eq(verb, "come") { return "qiman" }
|
||
if str_eq(verb, "give") { return "giban" }
|
||
if str_eq(verb, "know") { return "kunnan" }
|
||
if str_eq(verb, "want") { return "wiljan" }
|
||
return verb
|
||
}
|
||
|
||
// ── Irregular verb: wisan (to be) ─────────────────────────────────────────────
|
||
//
|
||
// wisan is suppletive — the present is formed from im / is / ist / sijum…
|
||
// and the past from was / wast / was / wesum…
|
||
//
|
||
// Present indicative active:
|
||
// 1sg im 2sg is 3sg ist
|
||
// 1pl sijum 2pl sijuþ 3pl sind
|
||
//
|
||
// Past indicative active:
|
||
// 1sg was 2sg wast 3sg was
|
||
// 1pl wesum 2pl wesuþ 3pl wesun
|
||
|
||
fn got_wisan_present(slot: Int) -> String {
|
||
if slot == 0 { return "im" }
|
||
if slot == 1 { return "is" }
|
||
if slot == 2 { return "ist" }
|
||
if slot == 3 { return "sijum" }
|
||
if slot == 4 { return "sijuþ" }
|
||
return "sind"
|
||
}
|
||
|
||
fn got_wisan_past(slot: Int) -> String {
|
||
if slot == 0 { return "was" }
|
||
if slot == 1 { return "wast" }
|
||
if slot == 2 { return "was" }
|
||
if slot == 3 { return "wesum" }
|
||
if slot == 4 { return "wesuþ" }
|
||
return "wesun"
|
||
}
|
||
|
||
// ── Irregular verb: haban (to have) ───────────────────────────────────────────
|
||
//
|
||
// Weak class 3 (-an preterite-present type).
|
||
//
|
||
// Present:
|
||
// 1sg haba 2sg habais 3sg habaiþ
|
||
// 1pl habam 2pl habaiþ 3pl haband
|
||
//
|
||
// Past (habida series — weak past in -ida):
|
||
// 1sg habida 2sg habides 3sg habida
|
||
// 1pl habidum 2pl habideþ 3pl habidedun
|
||
|
||
fn got_haban_present(slot: Int) -> String {
|
||
if slot == 0 { return "haba" }
|
||
if slot == 1 { return "habais" }
|
||
if slot == 2 { return "habaiþ" }
|
||
if slot == 3 { return "habam" }
|
||
if slot == 4 { return "habaiþ" }
|
||
return "haband"
|
||
}
|
||
|
||
fn got_haban_past(slot: Int) -> String {
|
||
if slot == 0 { return "habida" }
|
||
if slot == 1 { return "habides" }
|
||
if slot == 2 { return "habida" }
|
||
if slot == 3 { return "habidum" }
|
||
if slot == 4 { return "habideþ" }
|
||
return "habidedun"
|
||
}
|
||
|
||
// ── Irregular verb: gaggan (to go) ────────────────────────────────────────────
|
||
//
|
||
// Strong class 7 (reduplicating); suppletive in past with iddja- forms.
|
||
//
|
||
// Present:
|
||
// 1sg gagga 2sg gaggis 3sg gaggiþ
|
||
// 1pl gagam 2pl gagiþ 3pl gaggand
|
||
//
|
||
// Past (iddja series — suppletive):
|
||
// 1sg iddja 2sg iddjēs 3sg iddja
|
||
// 1pl iddjēdum 2pl iddjēduþ 3pl iddjēdun
|
||
|
||
fn got_gaggan_present(slot: Int) -> String {
|
||
if slot == 0 { return "gagga" }
|
||
if slot == 1 { return "gaggis" }
|
||
if slot == 2 { return "gaggiþ" }
|
||
if slot == 3 { return "gagam" }
|
||
if slot == 4 { return "gagiþ" }
|
||
return "gaggand"
|
||
}
|
||
|
||
fn got_gaggan_past(slot: Int) -> String {
|
||
if slot == 0 { return "iddja" }
|
||
if slot == 1 { return "iddjēs" }
|
||
if slot == 2 { return "iddja" }
|
||
if slot == 3 { return "iddjēdum" }
|
||
if slot == 4 { return "iddjēduþ" }
|
||
return "iddjēdun"
|
||
}
|
||
|
||
// ── Irregular verb: saihwan (to see) ──────────────────────────────────────────
|
||
//
|
||
// Strong class 5 (saiƕan / saihwan). Present stem saihw-; past stem sahw-.
|
||
//
|
||
// Present:
|
||
// 1sg saihwa 2sg saihwis 3sg saihwiþ
|
||
// 1pl saihwam 2pl saihwiþ 3pl saihwand
|
||
//
|
||
// Past (ablaut: ai→a, strong past):
|
||
// 1sg sahw 2sg sahwt 3sg sahw
|
||
// 1pl sehwum 2pl sehwuþ 3pl sehwun
|
||
|
||
fn got_saihwan_present(slot: Int) -> String {
|
||
if slot == 0 { return "saihwa" }
|
||
if slot == 1 { return "saihwis" }
|
||
if slot == 2 { return "saihwiþ" }
|
||
if slot == 3 { return "saihwam" }
|
||
if slot == 4 { return "saihwiþ" }
|
||
return "saihwand"
|
||
}
|
||
|
||
fn got_saihwan_past(slot: Int) -> String {
|
||
if slot == 0 { return "sahw" }
|
||
if slot == 1 { return "sahwt" }
|
||
if slot == 2 { return "sahw" }
|
||
if slot == 3 { return "sehwum" }
|
||
if slot == 4 { return "sehwuþ" }
|
||
return "sehwun"
|
||
}
|
||
|
||
// ── Irregular verb: qiþan (to say) ────────────────────────────────────────────
|
||
//
|
||
// Strong class 5 (i-ablaut). Present stem qiþ-; past stem qaþ-.
|
||
//
|
||
// Present:
|
||
// 1sg qiþa 2sg qiþis 3sg qiþiþ
|
||
// 1pl qiþam 2pl qiþiþ 3pl qiþand
|
||
//
|
||
// Past (ablaut: i→a):
|
||
// 1sg qaþ 2sg qast 3sg qaþ
|
||
// 1pl qēþum 2pl qēþuþ 3pl qēþun
|
||
|
||
fn got_qithan_present(slot: Int) -> String {
|
||
if slot == 0 { return "qiþa" }
|
||
if slot == 1 { return "qiþis" }
|
||
if slot == 2 { return "qiþiþ" }
|
||
if slot == 3 { return "qiþam" }
|
||
if slot == 4 { return "qiþiþ" }
|
||
return "qiþand"
|
||
}
|
||
|
||
fn got_qithan_past(slot: Int) -> String {
|
||
if slot == 0 { return "qaþ" }
|
||
if slot == 1 { return "qast" }
|
||
if slot == 2 { return "qaþ" }
|
||
if slot == 3 { return "qēþum" }
|
||
if slot == 4 { return "qēþuþ" }
|
||
return "qēþun"
|
||
}
|
||
|
||
// ── Irregular verb: niman (to take) ───────────────────────────────────────────
|
||
//
|
||
// Strong class 4 (sonorant stem; i-ablaut in present, a-ablaut in past).
|
||
//
|
||
// Present:
|
||
// 1sg nima 2sg nimis 3sg nimiþ
|
||
// 1pl nimam 2pl nimiþ 3pl nimand
|
||
//
|
||
// Past (ablaut: i→a):
|
||
// 1sg nam 2sg namt 3sg nam
|
||
// 1pl nēmum 2pl nēmuþ 3pl nēmun
|
||
|
||
fn got_niman_present(slot: Int) -> String {
|
||
if slot == 0 { return "nima" }
|
||
if slot == 1 { return "nimis" }
|
||
if slot == 2 { return "nimiþ" }
|
||
if slot == 3 { return "nimam" }
|
||
if slot == 4 { return "nimiþ" }
|
||
return "nimand"
|
||
}
|
||
|
||
fn got_niman_past(slot: Int) -> String {
|
||
if slot == 0 { return "nam" }
|
||
if slot == 1 { return "namt" }
|
||
if slot == 2 { return "nam" }
|
||
if slot == 3 { return "nēmum" }
|
||
if slot == 4 { return "nēmuþ" }
|
||
return "nēmun"
|
||
}
|
||
|
||
// ── Weak class 1 (-jan verbs): regular conjugation ────────────────────────────
|
||
//
|
||
// Class 1 weak verbs are the most productive Gothic verb class. The
|
||
// infinitive ends in -jan; the stem is obtained by stripping -jan.
|
||
//
|
||
// Present indicative active:
|
||
// 1sg stem + -a (nasjan -> nasja)
|
||
// 2sg stem + -is (nasjis)
|
||
// 3sg stem + -iþ (nasjiþ)
|
||
// 1pl stem + -jam (nasjam)
|
||
// 2pl stem + -jiþ (nasjiþ)
|
||
// 3pl stem + -jand (nasjand)
|
||
//
|
||
// Past indicative active (weak past -ida):
|
||
// 1sg stem + -ida (nasjida)
|
||
// 2sg stem + -ides (nasjides)
|
||
// 3sg stem + -ida (nasjida)
|
||
// 1pl stem + -idum (nasjidum)
|
||
// 2pl stem + -ideþ (nasjideþ)
|
||
// 3pl stem + -idedun (nasjidedun)
|
||
|
||
fn got_wk1_present_ending(slot: Int) -> String {
|
||
if slot == 0 { return "a" }
|
||
if slot == 1 { return "is" }
|
||
if slot == 2 { return "iþ" }
|
||
if slot == 3 { return "jam" }
|
||
if slot == 4 { return "jiþ" }
|
||
return "jand"
|
||
}
|
||
|
||
fn got_wk1_past_ending(slot: Int) -> String {
|
||
if slot == 0 { return "ida" }
|
||
if slot == 1 { return "ides" }
|
||
if slot == 2 { return "ida" }
|
||
if slot == 3 { return "idum" }
|
||
if slot == 4 { return "ideþ" }
|
||
return "idedun"
|
||
}
|
||
|
||
fn got_wk1_conjugate(stem: String, tense: String, slot: Int) -> String {
|
||
if str_eq(tense, "present") {
|
||
return stem + got_wk1_present_ending(slot)
|
||
}
|
||
if str_eq(tense, "past") {
|
||
return stem + got_wk1_past_ending(slot)
|
||
}
|
||
return stem
|
||
}
|
||
|
||
// ── Weak class 2 (-on verbs): regular conjugation ─────────────────────────────
|
||
//
|
||
// Class 2 weak verbs. Infinitive ends in -on; stem = drop -on.
|
||
// This class corresponds roughly to OE -ian / OHG -on denominatives.
|
||
//
|
||
// Present indicative active:
|
||
// 1sg stem + -o (salbon -> salbo)
|
||
// 2sg stem + -os (salbos)
|
||
// 3sg stem + -oþ (salboþ)
|
||
// 1pl stem + -om (salbom)
|
||
// 2pl stem + -oþ (salboþ)
|
||
// 3pl stem + -ond (salbond)
|
||
//
|
||
// Past indicative active (weak past -oda):
|
||
// 1sg stem + -oda (salboda)
|
||
// 2sg stem + -odes (salbodes)
|
||
// 3sg stem + -oda (salboda)
|
||
// 1pl stem + -odum (salbodum)
|
||
// 2pl stem + -odeþ (salbodeþ)
|
||
// 3pl stem + -odedun (salbodedun)
|
||
|
||
fn got_wk2_present_ending(slot: Int) -> String {
|
||
if slot == 0 { return "o" }
|
||
if slot == 1 { return "os" }
|
||
if slot == 2 { return "oþ" }
|
||
if slot == 3 { return "om" }
|
||
if slot == 4 { return "oþ" }
|
||
return "ond"
|
||
}
|
||
|
||
fn got_wk2_past_ending(slot: Int) -> String {
|
||
if slot == 0 { return "oda" }
|
||
if slot == 1 { return "odes" }
|
||
if slot == 2 { return "oda" }
|
||
if slot == 3 { return "odum" }
|
||
if slot == 4 { return "odeþ" }
|
||
return "odedun"
|
||
}
|
||
|
||
fn got_wk2_conjugate(stem: String, tense: String, slot: Int) -> String {
|
||
if str_eq(tense, "present") {
|
||
return stem + got_wk2_present_ending(slot)
|
||
}
|
||
if str_eq(tense, "past") {
|
||
return stem + got_wk2_past_ending(slot)
|
||
}
|
||
return stem
|
||
}
|
||
|
||
// ── Infinitive class detection ─────────────────────────────────────────────────
|
||
//
|
||
// Identifies the verb class from the infinitive ending so that the correct
|
||
// regular paradigm can be applied for verbs not in the irregular table.
|
||
//
|
||
// ends in -jan -> weak class 1 ("wk1")
|
||
// ends in -on -> weak class 2 ("wk2")
|
||
// otherwise -> default to weak class 1 (most productive class)
|
||
|
||
fn got_verb_class(verb: String) -> String {
|
||
if got_str_ends(verb, "jan") { return "wk1" }
|
||
if got_str_ends(verb, "on") { return "wk2" }
|
||
return "wk1"
|
||
}
|
||
|
||
// got_verb_stem: strip the infinitive suffix to expose the productive stem.
|
||
//
|
||
// wk1: strip -jan (3 bytes — all ASCII)
|
||
// wk2: strip -on (2 bytes)
|
||
|
||
fn got_verb_stem(verb: String, vclass: String) -> String {
|
||
if str_eq(vclass, "wk1") { return got_str_drop_last(verb, 3) }
|
||
if str_eq(vclass, "wk2") { return got_str_drop_last(verb, 2) }
|
||
return got_str_drop_last(verb, 2)
|
||
}
|
||
|
||
// ── got_conjugate: main conjugation entry point ───────────────────────────────
|
||
//
|
||
// verb: Gothic infinitive in Latin romanisation (e.g. "wisan", "nasjan")
|
||
// or English canonical label ("be", "go", "have")
|
||
// tense: "present" | "past"
|
||
// person: "first" | "second" | "third"
|
||
// number: "singular" | "plural" (or "dual" — treated as plural)
|
||
//
|
||
// Returns the inflected form. Falls back to the infinitive for unknown
|
||
// tenses rather than crashing.
|
||
|
||
fn got_conjugate(verb: String, tense: String, person: String, number: String) -> String {
|
||
let v: String = got_map_canonical(verb)
|
||
let slot: Int = got_slot(person, number)
|
||
|
||
// ── Irregular: wisan (to be) ──────────────────────────────────────────────
|
||
if str_eq(v, "wisan") {
|
||
if str_eq(tense, "present") { return got_wisan_present(slot) }
|
||
if str_eq(tense, "past") { return got_wisan_past(slot) }
|
||
return v
|
||
}
|
||
|
||
// ── Irregular: haban (to have) ────────────────────────────────────────────
|
||
if str_eq(v, "haban") {
|
||
if str_eq(tense, "present") { return got_haban_present(slot) }
|
||
if str_eq(tense, "past") { return got_haban_past(slot) }
|
||
return v
|
||
}
|
||
|
||
// ── Irregular: gaggan (to go) ─────────────────────────────────────────────
|
||
if str_eq(v, "gaggan") {
|
||
if str_eq(tense, "present") { return got_gaggan_present(slot) }
|
||
if str_eq(tense, "past") { return got_gaggan_past(slot) }
|
||
return v
|
||
}
|
||
|
||
// ── Irregular: saihwan (to see) ───────────────────────────────────────────
|
||
if str_eq(v, "saihwan") {
|
||
if str_eq(tense, "present") { return got_saihwan_present(slot) }
|
||
if str_eq(tense, "past") { return got_saihwan_past(slot) }
|
||
return v
|
||
}
|
||
|
||
// ── Irregular: qiþan (to say) ─────────────────────────────────────────────
|
||
if str_eq(v, "qiþan") {
|
||
if str_eq(tense, "present") { return got_qithan_present(slot) }
|
||
if str_eq(tense, "past") { return got_qithan_past(slot) }
|
||
return v
|
||
}
|
||
|
||
// ── Irregular: niman (to take) ────────────────────────────────────────────
|
||
if str_eq(v, "niman") {
|
||
if str_eq(tense, "present") { return got_niman_present(slot) }
|
||
if str_eq(tense, "past") { return got_niman_past(slot) }
|
||
return v
|
||
}
|
||
|
||
// ── Regular weak conjugation ──────────────────────────────────────────────
|
||
let vclass: String = got_verb_class(v)
|
||
let stem: String = got_verb_stem(v, vclass)
|
||
|
||
if str_eq(vclass, "wk1") {
|
||
return got_wk1_conjugate(stem, tense, slot)
|
||
}
|
||
if str_eq(vclass, "wk2") {
|
||
return got_wk2_conjugate(stem, tense, slot)
|
||
}
|
||
|
||
// Final fallback: return the infinitive
|
||
return v
|
||
}
|
||
|
||
// ── a-stem masculine paradigm (dags — day) ────────────────────────────────────
|
||
//
|
||
// The a-stem masculine is the largest Gothic noun class, corresponding to the
|
||
// Proto-Germanic *-a- stems (OE -as, German -e plurals).
|
||
//
|
||
// Nominative singular: dags
|
||
// Stem: dag- (strip final -s from nom sg)
|
||
//
|
||
// Singular: nom dags acc dag gen dagis dat daga
|
||
// Plural: nom dagos acc dagans gen dage dat dagam
|
||
//
|
||
// Note: some sources list acc sg as "dag" (without -n), consistent with
|
||
// Gothic nom≠acc distinction in masculine a-stems.
|
||
|
||
fn got_decline_a_stem_sg(stem: String, gram_case: String) -> String {
|
||
if str_eq(gram_case, "nominative") { return stem + "s" }
|
||
if str_eq(gram_case, "accusative") { return stem }
|
||
if str_eq(gram_case, "genitive") { return stem + "is" }
|
||
if str_eq(gram_case, "dative") { return stem + "a" }
|
||
return stem + "s"
|
||
}
|
||
|
||
fn got_decline_a_stem_pl(stem: String, gram_case: String) -> String {
|
||
if str_eq(gram_case, "nominative") { return stem + "os" }
|
||
if str_eq(gram_case, "accusative") { return stem + "ans" }
|
||
if str_eq(gram_case, "genitive") { return stem + "e" }
|
||
if str_eq(gram_case, "dative") { return stem + "am" }
|
||
return stem + "os"
|
||
}
|
||
|
||
// ── o-stem feminine paradigm (gibo — gift) ────────────────────────────────────
|
||
//
|
||
// The o-stem feminines correspond to Proto-Germanic *-ō- stems. The
|
||
// nominative singular ends in -o; nom and acc plural are identical.
|
||
//
|
||
// Singular: nom gibo acc giba gen gibos dat gibai
|
||
// Plural: nom gibos acc gibos gen gibo dat gibom
|
||
|
||
fn got_decline_o_stem_sg(stem: String, gram_case: String) -> String {
|
||
if str_eq(gram_case, "nominative") { return stem + "o" }
|
||
if str_eq(gram_case, "accusative") { return stem + "a" }
|
||
if str_eq(gram_case, "genitive") { return stem + "os" }
|
||
if str_eq(gram_case, "dative") { return stem + "ai" }
|
||
return stem + "o"
|
||
}
|
||
|
||
fn got_decline_o_stem_pl(stem: String, gram_case: String) -> String {
|
||
if str_eq(gram_case, "nominative") { return stem + "os" }
|
||
if str_eq(gram_case, "accusative") { return stem + "os" }
|
||
if str_eq(gram_case, "genitive") { return stem + "o" }
|
||
if str_eq(gram_case, "dative") { return stem + "om" }
|
||
return stem + "os"
|
||
}
|
||
|
||
// ── n-stem masculine paradigm (guma — man) ────────────────────────────────────
|
||
//
|
||
// The n-stems (weak nouns) are characterised by -n- appearing in all cases
|
||
// except the nominative singular. They are sometimes called "weak nouns"
|
||
// by analogy with Old English grammar.
|
||
//
|
||
// Singular: nom guma acc guman gen gumins dat gumin
|
||
// Plural: nom gumans acc gumans gen gumane dat gumam
|
||
|
||
fn got_decline_n_stem_sg(stem: String, gram_case: String) -> String {
|
||
if str_eq(gram_case, "nominative") { return stem + "a" }
|
||
if str_eq(gram_case, "accusative") { return stem + "an" }
|
||
if str_eq(gram_case, "genitive") { return stem + "ins" }
|
||
if str_eq(gram_case, "dative") { return stem + "in" }
|
||
return stem + "a"
|
||
}
|
||
|
||
fn got_decline_n_stem_pl(stem: String, gram_case: String) -> String {
|
||
if str_eq(gram_case, "nominative") { return stem + "ans" }
|
||
if str_eq(gram_case, "accusative") { return stem + "ans" }
|
||
if str_eq(gram_case, "genitive") { return stem + "ane" }
|
||
if str_eq(gram_case, "dative") { return stem + "am" }
|
||
return stem + "ans"
|
||
}
|
||
|
||
// ── Stem type detection ────────────────────────────────────────────────────────
|
||
//
|
||
// Infers the stem class from the nominative singular form.
|
||
//
|
||
// Strategy:
|
||
// ends in -s and previous char is not a vowel -> a-stem masc (dags type)
|
||
// ends in -o -> o-stem fem (gibo type)
|
||
// ends in -a -> n-stem masc (guma type)
|
||
// ends in -s and previous char is a vowel -> could be various; default a-stem
|
||
// otherwise -> default to a-stem
|
||
//
|
||
// For the common case where the caller passes the paradigm lemma directly:
|
||
// "dags" -> "a", "gibo" -> "o", "guma" -> "n"
|
||
|
||
fn got_stem_type(noun: String) -> String {
|
||
if got_str_ends(noun, "o") { return "o" }
|
||
if got_str_ends(noun, "a") { return "n" }
|
||
if got_str_ends(noun, "s") { return "a" }
|
||
// Fallback for other forms
|
||
return "a"
|
||
}
|
||
|
||
// got_extract_stem: strip the nom-sg suffix to obtain the bare stem.
|
||
//
|
||
// a-stem: strip final -s (dags -> dag)
|
||
// o-stem: strip final -o (gibo -> gib)
|
||
// n-stem: strip final -a (guma -> gum)
|
||
//
|
||
// All suffix bytes are single-byte ASCII, so str_len gives byte=char counts.
|
||
|
||
fn got_extract_stem(noun: String, stype: String) -> String {
|
||
let n: Int = str_len(noun)
|
||
// All three suffixes are 1 byte each
|
||
return str_slice(noun, 0, n - 1)
|
||
}
|
||
|
||
// ── Gender inference for article selection ─────────────────────────────────────
|
||
//
|
||
// The demonstrative-article used in got_noun_phrase depends on gender.
|
||
// Gender correlates with stem class in Gothic (imperfect but useful heuristic):
|
||
// a-stem -> masculine -> sa
|
||
// o-stem -> feminine -> þo (nom sg of feminine demonstrative)
|
||
// n-stem -> masculine -> sa
|
||
//
|
||
// Neuter a-stems (like "waurd" — word) exist but are not distinguished here;
|
||
// the NLG layer is expected to pass explicit gender when the distinction matters.
|
||
|
||
fn got_demo_article(stype: String) -> String {
|
||
if str_eq(stype, "o") { return "þo" }
|
||
// a-stem and n-stem both masculine
|
||
return "sa"
|
||
}
|
||
|
||
// ── got_decline: main declension entry point ──────────────────────────────────
|
||
//
|
||
// noun: nominative singular in Gothic Latin romanisation
|
||
// (e.g. "dags", "gibo", "guma")
|
||
// gram_case: "nominative" | "accusative" | "genitive" | "dative"
|
||
// number: "singular" | "plural"
|
||
//
|
||
// Returns the inflected form. Unknown stem types return the nominative
|
||
// singular unchanged rather than producing garbage output.
|
||
|
||
fn got_decline(noun: String, gram_case: String, number: String) -> String {
|
||
let stype: String = got_stem_type(noun)
|
||
let stem: String = got_extract_stem(noun, stype)
|
||
|
||
if str_eq(stype, "a") {
|
||
if str_eq(number, "singular") { return got_decline_a_stem_sg(stem, gram_case) }
|
||
return got_decline_a_stem_pl(stem, gram_case)
|
||
}
|
||
|
||
if str_eq(stype, "o") {
|
||
if str_eq(number, "singular") { return got_decline_o_stem_sg(stem, gram_case) }
|
||
return got_decline_o_stem_pl(stem, gram_case)
|
||
}
|
||
|
||
if str_eq(stype, "n") {
|
||
if str_eq(number, "singular") { return got_decline_n_stem_sg(stem, gram_case) }
|
||
return got_decline_n_stem_pl(stem, gram_case)
|
||
}
|
||
|
||
// Unknown: return the nominative singular unchanged
|
||
return noun
|
||
}
|
||
|
||
// ── got_noun_phrase: noun phrase builder ──────────────────────────────────────
|
||
//
|
||
// Gothic has no definite article. The demonstrative pronouns sa (masc) /
|
||
// so (fem) / þata (neut) are used in contexts where a definite-article-like
|
||
// meaning is required (closely following Greek ὁ/ἡ/τό in Wulfila's translation).
|
||
//
|
||
// When definite=true this function prepends the gender-appropriate demonstrative
|
||
// in its nominative singular form. For non-nominative cases the demonstrative
|
||
// should ideally be declined to match; this implementation prepends the nom-sg
|
||
// form as a simplification suitable for NLG output. Callers requiring full
|
||
// demonstrative agreement should call got_decline_demonstrative separately.
|
||
//
|
||
// noun: nominative singular Gothic noun
|
||
// gram_case: "nominative" | "accusative" | "genitive" | "dative"
|
||
// number: "singular" | "plural"
|
||
// definite: "true" prepends the demonstrative; any other value omits it
|
||
|
||
fn got_noun_phrase(noun: String, gram_case: String, number: String, definite: String) -> String {
|
||
let declined: String = got_decline(noun, gram_case, number)
|
||
|
||
if str_eq(definite, "true") {
|
||
let stype: String = got_stem_type(noun)
|
||
let article: String = got_demo_article(stype)
|
||
return article + " " + declined
|
||
}
|
||
|
||
return declined
|
||
}
|