c2cd5e01e1
El SDK CI - dev / build-and-test (pull_request) Successful in 3m34s
elb.el: - Auto-detect Homebrew OpenSSL (-L$(brew --prefix openssl)/lib) so -lssl resolves on macOS without manual flags; no-op on Linux - Add -include elp-c-decls.h when present in out_dir: resolves undeclared cross-module calls in packages like ELP that lack explicit imports ELP source: - Add import "morphology.el" to all 29 language morphology modules - Add language module imports to morphology.el (all langs it dispatches to) These were missing since ELP was originally built as a monolithic unit
638 lines
25 KiB
EmacsLisp
638 lines
25 KiB
EmacsLisp
// morphology-goh.el - Old High German morphology for the NLG engine.
|
||
//
|
||
// Implements Old High German verb conjugation, noun declension, and the
|
||
// demonstrative determiner. Designed as a companion to morphology.el and
|
||
// called by the engine when the language profile code is "goh".
|
||
//
|
||
// Language profile: code=goh, name=Old High German, morph_type=fusional,
|
||
// word_order=V2, question_strategy=inversion, script=latin,
|
||
// family=germanic.
|
||
//
|
||
// Historical note: Old High German (ca. 750–1050 CE) is the earliest
|
||
// substantially attested stage of High German, preserved in texts such as the
|
||
// Hildebrandslied, the Muspilli, and Notker's translations. It is the
|
||
// ancestor of Middle High German and thence Modern German. The "High" refers
|
||
// to the geographical highlands of southern Germany, Austria, and Switzerland —
|
||
// distinct from the Low German / Old Saxon dialects spoken to the north.
|
||
//
|
||
// Defining feature — the Second Germanic Consonant Shift (Hochdeutsche
|
||
// Lautverschiebung), which distinguishes OHG from Gothic and Old English:
|
||
// p → ff/pf (Gothic "skip" → OHG "skif"; Goth "apan" → OHG "affo")
|
||
// t → ss/z (Goth "watan" → OHG "wazzer"; OE "tid" → OHG "zit")
|
||
// k → ch (Goth "mikan" → OHG "mihil")
|
||
// This shift only applies to inherited consonants and not to recent loanwords.
|
||
//
|
||
// Three genders (masculine, feminine, neuter), four cases (nominative,
|
||
// accusative, genitive, dative), and two numbers (singular, plural).
|
||
//
|
||
// Verb conjugation covered:
|
||
// Tenses: present indicative, past indicative
|
||
// Persons: first/second/third × singular/plural (slots 0-5)
|
||
// Classes: weak verbs (dental -ta past suffix — the most productive class)
|
||
// Irregulars: wesan/sīn (be), habēn (have), gān (go), sehan (see),
|
||
// quethan (say), tuon (do)
|
||
// Canonical map: "be" -> "wesan"
|
||
//
|
||
// Noun declension covered:
|
||
// Strong masc a-stem (tag — day): 4 cases × sg/pl
|
||
// Strong fem ō-stem (geba — gift): 4 cases × sg/pl
|
||
// Strong neut a-stem (wort — word): 4 cases × sg/pl
|
||
// Weak masc n-stem (boto — messenger): 4 cases × sg/pl
|
||
//
|
||
// Demonstrative / definite article:
|
||
// OHG uses the demonstrative dër/diu/daz (the) which doubles as a definite
|
||
// determiner. This implementation selects the correct nominative form by
|
||
// inferred gender and passes it as a separate word before the noun.
|
||
//
|
||
// Depends on: morphology.el (str_eq, str_len, str_slice, str_ends_with)
|
||
|
||
// ── String helpers ─────────────────────────────────────────────────────────────
|
||
|
||
import "morphology.el"
|
||
fn goh_str_ends(s: String, suf: String) -> Bool {
|
||
return str_ends_with(s, suf)
|
||
}
|
||
|
||
fn goh_drop(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 ─────────────────────────────────────────────────────────
|
||
//
|
||
// Maps person × number to a 0-based paradigm index.
|
||
// 0 = 1st singular (ih)
|
||
// 1 = 2nd singular (dū)
|
||
// 2 = 3rd singular (ër/siu/ez)
|
||
// 3 = 1st plural (wir)
|
||
// 4 = 2nd plural (ir)
|
||
// 5 = 3rd plural (sie)
|
||
|
||
fn goh_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 resolved to OHG dictionary infinitives
|
||
// before conjugation.
|
||
|
||
fn goh_map_canonical(verb: String) -> String {
|
||
if str_eq(verb, "be") { return "wesan" }
|
||
if str_eq(verb, "have") { return "haben" }
|
||
if str_eq(verb, "go") { return "gan" }
|
||
if str_eq(verb, "see") { return "sehan" }
|
||
if str_eq(verb, "say") { return "quethan" }
|
||
if str_eq(verb, "do") { return "tuon" }
|
||
if str_eq(verb, "make") { return "tuon" }
|
||
if str_eq(verb, "come") { return "queman" }
|
||
if str_eq(verb, "give") { return "geban" }
|
||
if str_eq(verb, "know") { return "wizzan" }
|
||
if str_eq(verb, "want") { return "wellan" }
|
||
return verb
|
||
}
|
||
|
||
// ── Irregular verb: wesan/sīn (to be) ────────────────────────────────────────
|
||
//
|
||
// wesan is one of the most irregular verbs in OHG. The present uses the
|
||
// bim/bin- forms (from Proto-Germanic *biju-); the past uses the was-/wāri-
|
||
// forms (from the strong past of wesan).
|
||
//
|
||
// Present indicative:
|
||
// 1sg bim/bin 2sg bist 3sg ist
|
||
// 1pl birum 2pl biruț 3pl sint
|
||
//
|
||
// Note: 2pl "biruț" — the ț represents an old dental fricative; rendered here
|
||
// as "birut" (common orthographic variant in OHG manuscripts).
|
||
//
|
||
// Past indicative:
|
||
// 1sg was 2sg wāri 3sg was
|
||
// 1pl wārum 2pl wāruț 3pl wārun
|
||
|
||
fn goh_wesan_present(slot: Int) -> String {
|
||
if slot == 0 { return "bim" }
|
||
if slot == 1 { return "bist" }
|
||
if slot == 2 { return "ist" }
|
||
if slot == 3 { return "birum" }
|
||
if slot == 4 { return "birut" }
|
||
return "sint"
|
||
}
|
||
|
||
fn goh_wesan_past(slot: Int) -> String {
|
||
if slot == 0 { return "was" }
|
||
if slot == 1 { return "wari" }
|
||
if slot == 2 { return "was" }
|
||
if slot == 3 { return "warum" }
|
||
if slot == 4 { return "warut" }
|
||
return "warun"
|
||
}
|
||
|
||
// ── Irregular verb: habēn (to have) ───────────────────────────────────────────
|
||
//
|
||
// habēn is an athematic preterite-present verb.
|
||
//
|
||
// Present indicative:
|
||
// 1sg habem 2sg habest 3sg habet
|
||
// 1pl habemes 2pl habet 3pl habent
|
||
//
|
||
// Past (weak past -ta on stem hab-):
|
||
// 1sg habeta 2sg habetos 3sg habeta
|
||
// 1pl habetom 2pl habetot 3pl habeton
|
||
|
||
fn goh_haben_present(slot: Int) -> String {
|
||
if slot == 0 { return "habem" }
|
||
if slot == 1 { return "habest" }
|
||
if slot == 2 { return "habet" }
|
||
if slot == 3 { return "habemes" }
|
||
if slot == 4 { return "habet" }
|
||
return "habent"
|
||
}
|
||
|
||
fn goh_haben_past(slot: Int) -> String {
|
||
if slot == 0 { return "habeta" }
|
||
if slot == 1 { return "habetos" }
|
||
if slot == 2 { return "habeta" }
|
||
if slot == 3 { return "habetom" }
|
||
if slot == 4 { return "habetot" }
|
||
return "habeton"
|
||
}
|
||
|
||
// ── Irregular verb: gān (to go) ───────────────────────────────────────────────
|
||
//
|
||
// gān is an anomalous contracted verb.
|
||
//
|
||
// Present indicative:
|
||
// 1sg gan 2sg gest 3sg get
|
||
// 1pl games 2pl gat 3pl gant
|
||
//
|
||
// Past (reduplicating/suppletive — giang- forms):
|
||
// 1sg giang 2sg giangi 3sg giang
|
||
// 1pl giangum 2pl giangun 3pl giangun
|
||
|
||
fn goh_gan_present(slot: Int) -> String {
|
||
if slot == 0 { return "gan" }
|
||
if slot == 1 { return "gest" }
|
||
if slot == 2 { return "get" }
|
||
if slot == 3 { return "games" }
|
||
if slot == 4 { return "gat" }
|
||
return "gant"
|
||
}
|
||
|
||
fn goh_gan_past(slot: Int) -> String {
|
||
if slot == 0 { return "giang" }
|
||
if slot == 1 { return "giangi" }
|
||
if slot == 2 { return "giang" }
|
||
if slot == 3 { return "giangum" }
|
||
if slot == 4 { return "giangun" }
|
||
return "giangun"
|
||
}
|
||
|
||
// ── Irregular verb: sehan (to see) ────────────────────────────────────────────
|
||
//
|
||
// Strong class 5 (ablaut e/a). Present has i-mutation in sg forms.
|
||
//
|
||
// Present indicative:
|
||
// 1sg sihu 2sg sihist 3sg sihit
|
||
// 1pl sehemes 2pl sehet 3pl sehent
|
||
//
|
||
// Past (strong ablaut: e → a):
|
||
// 1sg sah 2sg sahi 3sg sah
|
||
// 1pl sahum 2pl sahut 3pl sahun
|
||
|
||
fn goh_sehan_present(slot: Int) -> String {
|
||
if slot == 0 { return "sihu" }
|
||
if slot == 1 { return "sihist" }
|
||
if slot == 2 { return "sihit" }
|
||
if slot == 3 { return "sehemes" }
|
||
if slot == 4 { return "sehet" }
|
||
return "sehent"
|
||
}
|
||
|
||
fn goh_sehan_past(slot: Int) -> String {
|
||
if slot == 0 { return "sah" }
|
||
if slot == 1 { return "sahi" }
|
||
if slot == 2 { return "sah" }
|
||
if slot == 3 { return "sahum" }
|
||
if slot == 4 { return "sahut" }
|
||
return "sahun"
|
||
}
|
||
|
||
// ── Irregular verb: quethan (to say) ──────────────────────────────────────────
|
||
//
|
||
// Strong class 5 (ablaut e/a). Present sg has i-mutation (quid-).
|
||
//
|
||
// Present indicative:
|
||
// 1sg quidu 2sg quidist 3sg quidit
|
||
// 1pl quethumes 2pl quethet 3pl quethent
|
||
//
|
||
// Past (strong ablaut: e → a):
|
||
// 1sg quad 2sg quadi 3sg quad
|
||
// 1pl quadum 2pl quadut 3pl quadun
|
||
|
||
fn goh_quethan_present(slot: Int) -> String {
|
||
if slot == 0 { return "quidu" }
|
||
if slot == 1 { return "quidist" }
|
||
if slot == 2 { return "quidit" }
|
||
if slot == 3 { return "quethumes" }
|
||
if slot == 4 { return "quethet" }
|
||
return "quethent"
|
||
}
|
||
|
||
fn goh_quethan_past(slot: Int) -> String {
|
||
if slot == 0 { return "quad" }
|
||
if slot == 1 { return "quadi" }
|
||
if slot == 2 { return "quad" }
|
||
if slot == 3 { return "quadum" }
|
||
if slot == 4 { return "quadut" }
|
||
return "quadun"
|
||
}
|
||
|
||
// ── Irregular verb: tuon (to do/make) ─────────────────────────────────────────
|
||
//
|
||
// tuon is a contracted athematic verb.
|
||
//
|
||
// Present indicative:
|
||
// 1sg tuom 2sg tuost 3sg tuot
|
||
// 1pl tuomes 2pl tuot 3pl tuont
|
||
//
|
||
// Past (weak past -ta on stem tā-):
|
||
// 1sg teta 2sg tetos 3sg teta
|
||
// 1pl tetom 2pl tetot 3pl teton
|
||
|
||
fn goh_tuon_present(slot: Int) -> String {
|
||
if slot == 0 { return "tuom" }
|
||
if slot == 1 { return "tuost" }
|
||
if slot == 2 { return "tuot" }
|
||
if slot == 3 { return "tuomes" }
|
||
if slot == 4 { return "tuot" }
|
||
return "tuont"
|
||
}
|
||
|
||
fn goh_tuon_past(slot: Int) -> String {
|
||
if slot == 0 { return "teta" }
|
||
if slot == 1 { return "tetos" }
|
||
if slot == 2 { return "teta" }
|
||
if slot == 3 { return "tetom" }
|
||
if slot == 4 { return "tetot" }
|
||
return "teton"
|
||
}
|
||
|
||
// ── Weak verb regular paradigm ─────────────────────────────────────────────────
|
||
//
|
||
// Weak verbs are the productive OHG class — new verbs are regularly formed on
|
||
// this pattern. The infinitive ends in -en or -on; the weak past uses the
|
||
// dental suffix -ta/-to (a reflex of Proto-Germanic *-dō-).
|
||
//
|
||
// Class 1 weak (most common; infinitive -en, stem ends in consonant):
|
||
//
|
||
// Present indicative (stem + ending):
|
||
// 1sg -u 2sg -ist 3sg -it
|
||
// 1pl -emēs 2pl -et 3pl -ent
|
||
//
|
||
// Past indicative (stem + dental suffix):
|
||
// 1sg -ta 2sg -tōs 3sg -ta
|
||
// 1pl -tōm 2pl -tōt 3pl -tōn
|
||
//
|
||
// The stem for a regular weak verb is obtained by dropping -en (2 bytes).
|
||
|
||
fn goh_weak_present(stem: String, slot: Int) -> String {
|
||
if slot == 0 { return stem + "u" }
|
||
if slot == 1 { return stem + "ist" }
|
||
if slot == 2 { return stem + "it" }
|
||
if slot == 3 { return stem + "emes" }
|
||
if slot == 4 { return stem + "et" }
|
||
return stem + "ent"
|
||
}
|
||
|
||
fn goh_weak_past(stem: String, slot: Int) -> String {
|
||
if slot == 0 { return stem + "ta" }
|
||
if slot == 1 { return stem + "tos" }
|
||
if slot == 2 { return stem + "ta" }
|
||
if slot == 3 { return stem + "tom" }
|
||
if slot == 4 { return stem + "tot" }
|
||
return stem + "ton"
|
||
}
|
||
|
||
// goh_verb_stem: strip the infinitive ending to expose the productive stem.
|
||
// -en endings: drop 2 bytes (most weak verbs: sagēn → sag-)
|
||
// -on endings: drop 2 bytes (class 2 weak: lobōn → lob-)
|
||
// -an endings: drop 2 bytes (strong verbs handled as irregular; fallback)
|
||
|
||
fn goh_verb_stem(verb: String) -> String {
|
||
return goh_drop(verb, 2)
|
||
}
|
||
|
||
// ── goh_conjugate: main conjugation entry point ───────────────────────────────
|
||
//
|
||
// verb: OHG infinitive (e.g. "sagēn", "lobōn") or English canonical label
|
||
// tense: "present" | "past"
|
||
// person: "first" | "second" | "third"
|
||
// number: "singular" | "plural"
|
||
//
|
||
// Returns the inflected form. Unknown tenses fall back to the infinitive.
|
||
|
||
fn goh_conjugate(verb: String, tense: String, person: String, number: String) -> String {
|
||
let v: String = goh_map_canonical(verb)
|
||
let slot: Int = goh_slot(person, number)
|
||
|
||
// ── Irregular: wesan (to be) ──────────────────────────────────────────────
|
||
if str_eq(v, "wesan") {
|
||
if str_eq(tense, "present") { return goh_wesan_present(slot) }
|
||
if str_eq(tense, "past") { return goh_wesan_past(slot) }
|
||
return v
|
||
}
|
||
|
||
// ── Irregular: habēn / haben (to have) ───────────────────────────────────
|
||
if str_eq(v, "haben") {
|
||
if str_eq(tense, "present") { return goh_haben_present(slot) }
|
||
if str_eq(tense, "past") { return goh_haben_past(slot) }
|
||
return v
|
||
}
|
||
|
||
// Also match the macron-spelled infinitive if passed directly
|
||
if str_eq(v, "haben") {
|
||
if str_eq(tense, "present") { return goh_haben_present(slot) }
|
||
if str_eq(tense, "past") { return goh_haben_past(slot) }
|
||
return v
|
||
}
|
||
|
||
// ── Irregular: gān / gan (to go) ─────────────────────────────────────────
|
||
if str_eq(v, "gan") {
|
||
if str_eq(tense, "present") { return goh_gan_present(slot) }
|
||
if str_eq(tense, "past") { return goh_gan_past(slot) }
|
||
return v
|
||
}
|
||
|
||
// ── Irregular: sehan (to see) ─────────────────────────────────────────────
|
||
if str_eq(v, "sehan") {
|
||
if str_eq(tense, "present") { return goh_sehan_present(slot) }
|
||
if str_eq(tense, "past") { return goh_sehan_past(slot) }
|
||
return v
|
||
}
|
||
|
||
// ── Irregular: quethan (to say) ───────────────────────────────────────────
|
||
if str_eq(v, "quethan") {
|
||
if str_eq(tense, "present") { return goh_quethan_present(slot) }
|
||
if str_eq(tense, "past") { return goh_quethan_past(slot) }
|
||
return v
|
||
}
|
||
|
||
// ── Irregular: tuon (to do/make) ──────────────────────────────────────────
|
||
if str_eq(v, "tuon") {
|
||
if str_eq(tense, "present") { return goh_tuon_present(slot) }
|
||
if str_eq(tense, "past") { return goh_tuon_past(slot) }
|
||
return v
|
||
}
|
||
|
||
// ── Regular weak conjugation ──────────────────────────────────────────────
|
||
let stem: String = goh_verb_stem(v)
|
||
|
||
if str_eq(tense, "present") { return goh_weak_present(stem, slot) }
|
||
if str_eq(tense, "past") { return goh_weak_past(stem, slot) }
|
||
|
||
// Final fallback: return the infinitive
|
||
return v
|
||
}
|
||
|
||
// ── Noun stem type detection ───────────────────────────────────────────────────
|
||
//
|
||
// OHG nouns are grouped by historical stem class. This engine supports four:
|
||
//
|
||
// "masc_a" — strong masculine a-stem (tag, fisc, arm)
|
||
// "fem_o" — strong feminine ō-stem (geba, zala, burg)
|
||
// "neut_a" — strong neuter a-stem (wort, kind, tier)
|
||
// "masc_n" — weak masculine n-stem (boto, hazo, namo)
|
||
//
|
||
// Detection heuristic from the citation form (nominative singular):
|
||
// ends in -o → masc_n (weak masc nouns: boto, hazo)
|
||
// ends in -a → fem_o (strong fem: geba, zala)
|
||
// ends in -t, -d, -n (common neuter endings) → neut_a
|
||
// otherwise → masc_a (default strong masculine)
|
||
//
|
||
// Callers may override by passing the stem type directly when the heuristic
|
||
// would produce the wrong class (e.g. for monosyllables with ambiguous endings).
|
||
|
||
fn goh_stem_type(noun: String) -> String {
|
||
if goh_str_ends(noun, "o") { return "masc_n" }
|
||
if goh_str_ends(noun, "a") { return "fem_o" }
|
||
if goh_str_ends(noun, "t") { return "neut_a" }
|
||
if goh_str_ends(noun, "d") { return "neut_a" }
|
||
if goh_str_ends(noun, "nd") { return "neut_a" }
|
||
return "masc_a"
|
||
}
|
||
|
||
// goh_extract_stem: derive the bare stem used as the base for all case endings.
|
||
//
|
||
// masc_a: citation form is nom sg without ending (tag = tag, fisc = fisc)
|
||
// — citation IS the stem; no stripping needed
|
||
// fem_o: strip final -a (geba → geb-)
|
||
// neut_a: citation form is nom/acc sg without ending (wort = wort)
|
||
// — citation IS the stem; no stripping needed
|
||
// masc_n: strip final -o (boto → bot-)
|
||
|
||
fn goh_extract_stem(noun: String, stype: String) -> String {
|
||
if str_eq(stype, "fem_o") { return goh_drop(noun, 1) }
|
||
if str_eq(stype, "masc_n") { return goh_drop(noun, 1) }
|
||
// masc_a and neut_a: citation IS the stem
|
||
return noun
|
||
}
|
||
|
||
// ── Strong masculine a-stem declension (tag — day) ────────────────────────────
|
||
//
|
||
// The strong masculine a-stem is the most common OHG masculine class.
|
||
// It corresponds to the Gothic a-stem and the Latin 2nd-declension masculine.
|
||
//
|
||
// Paradigm (stem = tag-):
|
||
// Singular: nom tag acc tag gen tages dat tage
|
||
// Plural: nom taga acc taga gen tago dat tagum
|
||
|
||
fn goh_decline_masc_a_sg(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, "genitive") { return stem + "es" }
|
||
if str_eq(gram_case, "dative") { return stem + "e" }
|
||
return stem
|
||
}
|
||
|
||
fn goh_decline_masc_a_pl(stem: String, gram_case: String) -> String {
|
||
if str_eq(gram_case, "nominative") { return stem + "a" }
|
||
if str_eq(gram_case, "accusative") { return stem + "a" }
|
||
if str_eq(gram_case, "genitive") { return stem + "o" }
|
||
if str_eq(gram_case, "dative") { return stem + "um" }
|
||
return stem + "a"
|
||
}
|
||
|
||
// ── Strong feminine ō-stem declension (geba — gift) ──────────────────────────
|
||
//
|
||
// The ō-stem feminines are the standard OHG feminine class.
|
||
// They correspond to the Gothic o-stem and Latin 1st-declension nouns.
|
||
//
|
||
// Paradigm (stem = geb-):
|
||
// Singular: nom geba acc geba gen gebā dat gebu
|
||
// Plural: nom gebā acc gebā gen gebōno dat gebōm
|
||
|
||
fn goh_decline_fem_o_sg(stem: String, gram_case: String) -> String {
|
||
if str_eq(gram_case, "nominative") { return stem + "a" }
|
||
if str_eq(gram_case, "accusative") { return stem + "a" }
|
||
if str_eq(gram_case, "genitive") { return stem + "a" }
|
||
if str_eq(gram_case, "dative") { return stem + "u" }
|
||
return stem + "a"
|
||
}
|
||
|
||
fn goh_decline_fem_o_pl(stem: String, gram_case: String) -> String {
|
||
if str_eq(gram_case, "nominative") { return stem + "a" }
|
||
if str_eq(gram_case, "accusative") { return stem + "a" }
|
||
if str_eq(gram_case, "genitive") { return stem + "ono" }
|
||
if str_eq(gram_case, "dative") { return stem + "om" }
|
||
return stem + "a"
|
||
}
|
||
|
||
// ── Strong neuter a-stem declension (wort — word) ─────────────────────────────
|
||
//
|
||
// Strong neuter nouns share the a-stem pattern but have identical nom/acc
|
||
// throughout (a pan-Germanic neuter feature). The plural differs from the
|
||
// masculine in the nom/acc: neuters use stem alone rather than stem + -a.
|
||
//
|
||
// Paradigm (stem = wort):
|
||
// Singular: nom wort acc wort gen wortes dat worte
|
||
// Plural: nom wort acc wort gen worto dat wortum
|
||
|
||
fn goh_decline_neut_a_sg(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, "genitive") { return stem + "es" }
|
||
if str_eq(gram_case, "dative") { return stem + "e" }
|
||
return stem
|
||
}
|
||
|
||
fn goh_decline_neut_a_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, "genitive") { return stem + "o" }
|
||
if str_eq(gram_case, "dative") { return stem + "um" }
|
||
return stem
|
||
}
|
||
|
||
// ── Weak masculine n-stem declension (boto — messenger) ───────────────────────
|
||
//
|
||
// Weak nouns (n-stems) are characterised by the nasal -n- appearing in all
|
||
// forms except the nominative singular. They correspond to the Gothic n-stem
|
||
// and the Old English weak noun class.
|
||
//
|
||
// Paradigm (stem = bot-):
|
||
// Singular: nom boto acc boton gen boton dat boton
|
||
// Plural: nom boton acc boton gen botōno dat botōm
|
||
|
||
fn goh_decline_masc_n_sg(stem: String, gram_case: String) -> String {
|
||
if str_eq(gram_case, "nominative") { return stem + "o" }
|
||
if str_eq(gram_case, "accusative") { return stem + "on" }
|
||
if str_eq(gram_case, "genitive") { return stem + "on" }
|
||
if str_eq(gram_case, "dative") { return stem + "on" }
|
||
return stem + "o"
|
||
}
|
||
|
||
fn goh_decline_masc_n_pl(stem: String, gram_case: String) -> String {
|
||
if str_eq(gram_case, "nominative") { return stem + "on" }
|
||
if str_eq(gram_case, "accusative") { return stem + "on" }
|
||
if str_eq(gram_case, "genitive") { return stem + "ono" }
|
||
if str_eq(gram_case, "dative") { return stem + "om" }
|
||
return stem + "on"
|
||
}
|
||
|
||
// ── goh_decline: main declension entry point ──────────────────────────────────
|
||
//
|
||
// noun: OHG nominative singular form (e.g. "tag", "geba", "wort", "boto")
|
||
// gram_case: "nominative" | "accusative" | "genitive" | "dative"
|
||
// number: "singular" | "plural"
|
||
//
|
||
// Returns the inflected form. Unknown stem types return the citation form
|
||
// unchanged as a safe fallback.
|
||
|
||
fn goh_decline(noun: String, gram_case: String, number: String) -> String {
|
||
let stype: String = goh_stem_type(noun)
|
||
let stem: String = goh_extract_stem(noun, stype)
|
||
|
||
if str_eq(stype, "masc_a") {
|
||
if str_eq(number, "singular") { return goh_decline_masc_a_sg(stem, gram_case) }
|
||
return goh_decline_masc_a_pl(stem, gram_case)
|
||
}
|
||
|
||
if str_eq(stype, "fem_o") {
|
||
if str_eq(number, "singular") { return goh_decline_fem_o_sg(stem, gram_case) }
|
||
return goh_decline_fem_o_pl(stem, gram_case)
|
||
}
|
||
|
||
if str_eq(stype, "neut_a") {
|
||
if str_eq(number, "singular") { return goh_decline_neut_a_sg(stem, gram_case) }
|
||
return goh_decline_neut_a_pl(stem, gram_case)
|
||
}
|
||
|
||
if str_eq(stype, "masc_n") {
|
||
if str_eq(number, "singular") { return goh_decline_masc_n_sg(stem, gram_case) }
|
||
return goh_decline_masc_n_pl(stem, gram_case)
|
||
}
|
||
|
||
// Unknown: return citation form unchanged
|
||
return noun
|
||
}
|
||
|
||
// ── Demonstrative article ──────────────────────────────────────────────────────
|
||
//
|
||
// OHG uses the demonstrative pronoun dër/diu/daz as a definite determiner.
|
||
// Full declension of this pronoun is complex; this implementation provides the
|
||
// nominative forms used as determiners before nouns.
|
||
//
|
||
// Nominative forms (the most common slot for a determiner):
|
||
// Masculine sg: der Feminine sg: diu Neuter sg: daz
|
||
// Plural (all genders): die
|
||
//
|
||
// Gender is inferred from the stem type:
|
||
// masc_a → masculine → "der"
|
||
// fem_o → feminine → "diu"
|
||
// neut_a → neuter → "daz"
|
||
// masc_n → masculine → "der"
|
||
|
||
fn goh_demo_article(stype: String, number: String) -> String {
|
||
if str_eq(number, "plural") { return "die" }
|
||
if str_eq(stype, "fem_o") { return "diu" }
|
||
if str_eq(stype, "neut_a") { return "daz" }
|
||
return "der"
|
||
}
|
||
|
||
// ── goh_noun_phrase: noun phrase builder ──────────────────────────────────────
|
||
//
|
||
// Assembles a declined noun with an optional OHG demonstrative article.
|
||
//
|
||
// noun: OHG nominative singular (e.g. "tag", "geba", "wort", "boto")
|
||
// gram_case: "nominative" | "accusative" | "genitive" | "dative"
|
||
// number: "singular" | "plural"
|
||
// definite: "true" to prepend the demonstrative determiner; any other value omits it
|
||
//
|
||
// Note: the demonstrative is given in its nominative singular form for
|
||
// simplicity. Full agreement would require a separate declined demonstrative
|
||
// paradigm; the NLG layer should implement that when case-agreement on the
|
||
// determiner is required.
|
||
|
||
fn goh_noun_phrase(noun: String, gram_case: String, number: String, definite: String) -> String {
|
||
let stype: String = goh_stem_type(noun)
|
||
let declined: String = goh_decline(noun, gram_case, number)
|
||
|
||
if str_eq(definite, "true") {
|
||
let art: String = goh_demo_article(stype, number)
|
||
return art + " " + declined
|
||
}
|
||
|
||
return declined
|
||
}
|