577 lines
25 KiB
EmacsLisp
577 lines
25 KiB
EmacsLisp
// morphology-cop.el - Coptic (Sahidic dialect) morphology for the NLG engine.
|
||
//
|
||
// Implements Coptic verb conjugation (bipartite and tripartite patterns), noun
|
||
// phrase assembly with definite and indefinite articles, and noun number marking.
|
||
// Designed as a companion to morphology.el; called when language code is "cop".
|
||
//
|
||
// Language profile: code=cop, name=Coptic, morph_type=agglutinative,
|
||
// word_order=SVO, question_strategy=particle, script=coptic, family=afro-asiatic-egyptian.
|
||
//
|
||
// Script: Coptic uses the Greek alphabet plus seven additional letters borrowed
|
||
// from Demotic Egyptian. All Coptic-script characters in this file use their
|
||
// correct Unicode code points (Coptic block U+2C80–U+2CFF; Coptic letters also
|
||
// appear in the Greek block: ϣ U+03E3, ϥ U+03E5, ϩ U+03E9, ϫ U+03EB, ϭ U+03ED).
|
||
//
|
||
// The El runtime stores strings as byte arrays. String literals with Coptic
|
||
// Unicode characters are encoded as UTF-8 and compared via str_eq byte equality.
|
||
// The runtime limitation on non-ASCII *output display* does not affect internal
|
||
// string logic — str_eq and concatenation work correctly.
|
||
//
|
||
// Grammatical notes (Sahidic Coptic, ca. 200–1000 CE):
|
||
// - SVO word order (Greek influence; reversed from classical Egyptian)
|
||
// - Definite articles prefixed directly to the noun (no space):
|
||
// p- (masc sg), t- (fem sg), n- (plural) — definite
|
||
// ou- (sg indefinite), hen- (pl indefinite)
|
||
// - Grammatical gender: masculine / feminine (still active)
|
||
// - No case endings — grammatical role expressed by word order + prepositions
|
||
// - Verb tense/aspect expressed by conjugation base (bipartite pattern):
|
||
// Present I: pronoun prefix + verb stem ("f-bwk" = he goes)
|
||
// Perfect: a- + pronoun prefix + verb ("a-f-bwk" = he went)
|
||
// Future: pronoun prefix + na- + verb ("f-na-bwk" = he will go)
|
||
// - Pronoun prefixes (Sahidic — used as subject markers in bipartite conjugation):
|
||
// 1sg: a-/t- (full: ⲁⲛⲟⲕ) 2sg m: k- 2sg f: te-
|
||
// 3sg m: f- 3sg f: s-
|
||
// 1pl: n- 2pl: teten- 3pl: se-
|
||
// - Copula: "pe" (m sg), "te" (f sg), "ne" (pl); zero copula for adj predicates
|
||
// - "to be/become": ϣωπε (Sahidic; present: fϣoop / sϣoop; past: afϣwpe)
|
||
//
|
||
// Verbs covered (Sahidic transliteration / Coptic script):
|
||
// ϣωπε (shwpe) — to be / become bwk — to go
|
||
// nau — to see jw — to say / speak
|
||
// di — to give
|
||
//
|
||
// Canonical English → Coptic mapping:
|
||
// "be" → ϣωπε / zero copula "go" → bwk
|
||
// "see" → nau "say" → jw
|
||
// "give" → di
|
||
//
|
||
// Persons/numbers covered:
|
||
// person: "first" | "second" | "third"
|
||
// gender: "m" | "f" (relevant for 2sg and 3sg pronoun prefix selection)
|
||
// number: "singular" | "plural"
|
||
//
|
||
// Depends on: morphology.el (str_eq, str_len, str_slice, str_ends_with)
|
||
|
||
// ── String helpers ──────────────────────────────────────────────────────────────
|
||
|
||
fn cop_str_ends(s: String, suf: String) -> Bool {
|
||
return str_ends_with(s, suf)
|
||
}
|
||
|
||
fn cop_str_len(s: String) -> Int {
|
||
return str_len(s)
|
||
}
|
||
|
||
fn cop_drop(s: String, n: Int) -> String {
|
||
let len: Int = str_len(s)
|
||
if n >= len { return "" }
|
||
return str_slice(s, 0, len - n)
|
||
}
|
||
|
||
fn cop_last_char(s: String) -> String {
|
||
let n: Int = str_len(s)
|
||
if n == 0 { return "" }
|
||
return str_slice(s, n - 1, n)
|
||
}
|
||
|
||
// ── Person/number slot ──────────────────────────────────────────────────────────
|
||
//
|
||
// Maps person × number to a 0-based index used in paradigm tables.
|
||
// Gender is not encoded in the slot index here; it is passed separately to
|
||
// cop_subject_prefix where it matters (2sg and 3sg distinction).
|
||
//
|
||
// Slot layout:
|
||
// 0 = 1st singular (ⲁⲛⲟⲕ anok)
|
||
// 1 = 2nd singular (ⲛⲧⲟⲕ/ⲛⲧⲟ ntok/nto) — gender resolved in cop_subject_prefix
|
||
// 2 = 3rd singular (ⲛⲧⲟϥ/ⲛⲧⲟⲥ ntof/ntos) — gender resolved in cop_subject_prefix
|
||
// 3 = 1st plural (ⲁⲛⲟⲛ anon)
|
||
// 4 = 2nd plural (ⲛⲧⲱⲧⲉⲛ ntwten)
|
||
// 5 = 3rd plural (ⲛⲧⲟⲩ ntou)
|
||
|
||
fn cop_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
|
||
}
|
||
|
||
// ── Subject pronoun prefixes ─────────────────────────────────────────────────────
|
||
//
|
||
// Coptic bipartite conjugation uses short pronoun prefixes attached directly to
|
||
// the verb stem (or to the tense base in tripartite). These are the Sahidic
|
||
// bound subject pronouns.
|
||
//
|
||
// Full independent pronouns (for reference):
|
||
// 1sg: ⲁⲛⲟⲕ (anok) prefix: ⲁ- / ⲧ- (varies by tense base)
|
||
// 2sg m: ⲛⲧⲟⲕ (ntok) prefix: ⲕ-
|
||
// 2sg f: ⲛⲧⲟ (nto) prefix: ⲧⲉ-
|
||
// 3sg m: ⲛⲧⲟϥ (ntof) prefix: ϥ-
|
||
// 3sg f: ⲛⲧⲟⲥ (ntos) prefix: ⲥ-
|
||
// 1pl: ⲁⲛⲟⲛ (anon) prefix: ⲛ-
|
||
// 2pl: ⲛⲧⲱⲧⲉⲛ (ntwten) prefix: ⲧⲉⲧⲉⲛ-
|
||
// 3pl: ⲛⲧⲟⲩ (ntou) prefix: ⲥⲉ-
|
||
//
|
||
// cop_subject_prefix returns the short bound prefix used in bipartite conjugation.
|
||
// For the perfect (a-prefix tense base), the subject prefix follows "a-" directly.
|
||
|
||
fn cop_subject_prefix(person: String, number: String) -> String {
|
||
if str_eq(person, "first") {
|
||
if str_eq(number, "singular") { return "ⲁ" }
|
||
return "ⲛ"
|
||
}
|
||
if str_eq(person, "second") {
|
||
if str_eq(number, "singular") { return "ⲕ" }
|
||
return "ⲧⲉⲧⲉⲛ"
|
||
}
|
||
// third
|
||
if str_eq(number, "singular") { return "ϥ" }
|
||
return "ⲥⲉ"
|
||
}
|
||
|
||
// cop_subject_prefix_gendered: like cop_subject_prefix but handles the
|
||
// 2sg feminine (ⲧⲉ-) and 3sg feminine (ⲥ-) distinction.
|
||
|
||
fn cop_subject_prefix_gendered(person: String, gender: String, number: String) -> String {
|
||
if str_eq(person, "first") {
|
||
if str_eq(number, "singular") { return "ⲁ" }
|
||
return "ⲛ"
|
||
}
|
||
if str_eq(person, "second") {
|
||
if str_eq(number, "singular") {
|
||
if str_eq(gender, "f") { return "ⲧⲉ" }
|
||
return "ⲕ"
|
||
}
|
||
return "ⲧⲉⲧⲉⲛ"
|
||
}
|
||
// third person
|
||
if str_eq(number, "singular") {
|
||
if str_eq(gender, "f") { return "ⲥ" }
|
||
return "ϥ"
|
||
}
|
||
return "ⲥⲉ"
|
||
}
|
||
|
||
// ── Copula ──────────────────────────────────────────────────────────────────────
|
||
//
|
||
// The Coptic nominal/adjectival copula is a standalone particle that agrees with
|
||
// the gender and number of the subject:
|
||
// Masculine sg: ⲡⲉ (pe)
|
||
// Feminine sg: ⲧⲉ (te)
|
||
// Plural: ⲛⲉ (ne)
|
||
//
|
||
// For adjective predicates in the present tense, the copula is often zero
|
||
// (following the inherited Egyptian zero-copula rule). This engine returns ""
|
||
// for the present adjective predicate and the full copula particle otherwise.
|
||
|
||
fn cop_copula_particle(gender: String, number: String) -> String {
|
||
if str_eq(number, "plural") { return "ⲛⲉ" }
|
||
if str_eq(gender, "f") { return "ⲧⲉ" }
|
||
return "ⲡⲉ"
|
||
}
|
||
|
||
// ── Verb: ϣωπε (to be / become) ────────────────────────────────────────────────
|
||
//
|
||
// ϣωπε is the Sahidic verb meaning "to be" or "to become". It is used as a
|
||
// substantive/existential copula. For adjective predicate sentences the zero
|
||
// copula is preferred (inherited from Egyptian).
|
||
//
|
||
// Sahidic forms:
|
||
// Present I (bipartite): prefix + ϣⲟⲟⲡ (e.g. ϥϣⲟⲟⲡ "he is/exists")
|
||
// Perfect (a- base): ⲁ + prefix + ϣⲱⲡⲉ (e.g. ⲁϥϣⲱⲡⲉ "he became")
|
||
// Future (na- infix): prefix + ⲛⲁϣⲱⲡⲉ (e.g. ϥⲛⲁϣⲱⲡⲉ "he will become")
|
||
//
|
||
// Note: ϣⲟⲟⲡ (shoop) is the present stem; ϣⲱⲡⲉ (shwpe) is the infinitive/perfect stem.
|
||
|
||
fn cop_shwpe_present(prefix: String) -> String {
|
||
return prefix + "ϣⲟⲟⲡ"
|
||
}
|
||
|
||
fn cop_shwpe_perfect(prefix: String) -> String {
|
||
return "ⲁ" + prefix + "ϣⲱⲡⲉ"
|
||
}
|
||
|
||
fn cop_shwpe_future(prefix: String) -> String {
|
||
return prefix + "ⲛⲁϣⲱⲡⲉ"
|
||
}
|
||
|
||
// ── Verb: bwk (to go) — written ⲃⲱⲕ ───────────────────────────────────────────
|
||
//
|
||
// A common strong verb. The standard bipartite/tripartite pattern applies.
|
||
// Present: prefix + ⲃⲱⲕ (e.g. ϥⲃⲱⲕ "he goes")
|
||
// Perfect: ⲁ + prefix + ⲃⲱⲕ (e.g. ⲁϥⲃⲱⲕ "he went")
|
||
// Future: prefix + ⲛⲁⲃⲱⲕ (e.g. ϥⲛⲁⲃⲱⲕ "he will go")
|
||
|
||
fn cop_bwk_present(prefix: String) -> String {
|
||
return prefix + "ⲃⲱⲕ"
|
||
}
|
||
|
||
fn cop_bwk_perfect(prefix: String) -> String {
|
||
return "ⲁ" + prefix + "ⲃⲱⲕ"
|
||
}
|
||
|
||
fn cop_bwk_future(prefix: String) -> String {
|
||
return prefix + "ⲛⲁⲃⲱⲕ"
|
||
}
|
||
|
||
// ── Verb: nau (to see) — written ⲛⲁⲩ ──────────────────────────────────────────
|
||
//
|
||
// nau is a biconsonantal verb. Regular bipartite conjugation:
|
||
// Present: prefix + ⲛⲁⲩ (e.g. ϥⲛⲁⲩ "he sees")
|
||
// Perfect: ⲁ + prefix + ⲛⲁⲩ (e.g. ⲁϥⲛⲁⲩ "he saw")
|
||
// Future: prefix + ⲛⲁⲛⲁⲩ (e.g. ϥⲛⲁⲛⲁⲩ "he will see")
|
||
//
|
||
// Note: the future prefix "na-" followed by "nau" produces "nanau" — standard.
|
||
|
||
fn cop_nau_present(prefix: String) -> String {
|
||
return prefix + "ⲛⲁⲩ"
|
||
}
|
||
|
||
fn cop_nau_perfect(prefix: String) -> String {
|
||
return "ⲁ" + prefix + "ⲛⲁⲩ"
|
||
}
|
||
|
||
fn cop_nau_future(prefix: String) -> String {
|
||
return prefix + "ⲛⲁⲛⲁⲩ"
|
||
}
|
||
|
||
// ── Verb: jw (to say / speak) — written ϫⲱ ────────────────────────────────────
|
||
//
|
||
// ϫⲱ is the Sahidic verb for "to say". Bipartite pattern:
|
||
// Present: prefix + ϫⲱ (e.g. ϥϫⲱ "he says")
|
||
// Perfect: ⲁ + prefix + ϫⲱ (e.g. ⲁϥϫⲱ "he said")
|
||
// Future: prefix + ⲛⲁϫⲱ (e.g. ϥⲛⲁϫⲱ "he will say")
|
||
|
||
fn cop_jw_present(prefix: String) -> String {
|
||
return prefix + "ϫⲱ"
|
||
}
|
||
|
||
fn cop_jw_perfect(prefix: String) -> String {
|
||
return "ⲁ" + prefix + "ϫⲱ"
|
||
}
|
||
|
||
fn cop_jw_future(prefix: String) -> String {
|
||
return prefix + "ⲛⲁϫⲱ"
|
||
}
|
||
|
||
// ── Verb: di (to give) — written ϯ ─────────────────────────────────────────────
|
||
//
|
||
// ϯ (ti/di) is a monosyllabic verb meaning "to give". It is very common in
|
||
// Coptic texts. Bipartite pattern:
|
||
// Present: prefix + ϯ (e.g. ϥϯ "he gives")
|
||
// Perfect: ⲁ + prefix + ϯ (e.g. ⲁϥϯ "he gave")
|
||
// Future: prefix + ⲛⲁϯ (e.g. ϥⲛⲁϯ "he will give")
|
||
|
||
fn cop_di_present(prefix: String) -> String {
|
||
return prefix + "ϯ"
|
||
}
|
||
|
||
fn cop_di_perfect(prefix: String) -> String {
|
||
return "ⲁ" + prefix + "ϯ"
|
||
}
|
||
|
||
fn cop_di_future(prefix: String) -> String {
|
||
return prefix + "ⲛⲁϯ"
|
||
}
|
||
|
||
// ── Copula detection ─────────────────────────────────────────────────────────────
|
||
|
||
fn cop_is_copula(verb: String) -> Bool {
|
||
if str_eq(verb, "ϣωπε") { return true }
|
||
if str_eq(verb, "shwpe") { return true }
|
||
if str_eq(verb, "be") { return true }
|
||
return false
|
||
}
|
||
|
||
// ── Known-verb dispatcher ────────────────────────────────────────────────────────
|
||
//
|
||
// Returns the inflected form for a known verb given the subject prefix string
|
||
// and tense. Returns "" if the verb is not in the table.
|
||
|
||
fn cop_known_verb_prefixed(verb: String, tense: String, prefix: String) -> String {
|
||
// ── ϣωπε / shwpe / "be" — to be / become ────────────────────────────────────
|
||
if str_eq(verb, "ϣωπε") {
|
||
if str_eq(tense, "present") { return cop_shwpe_present(prefix) }
|
||
if str_eq(tense, "past") { return cop_shwpe_perfect(prefix) }
|
||
if str_eq(tense, "future") { return cop_shwpe_future(prefix) }
|
||
return cop_shwpe_present(prefix)
|
||
}
|
||
if str_eq(verb, "shwpe") {
|
||
if str_eq(tense, "present") { return cop_shwpe_present(prefix) }
|
||
if str_eq(tense, "past") { return cop_shwpe_perfect(prefix) }
|
||
if str_eq(tense, "future") { return cop_shwpe_future(prefix) }
|
||
return cop_shwpe_present(prefix)
|
||
}
|
||
|
||
// ── bwk / ⲃⲱⲕ — to go ────────────────────────────────────────────────────────
|
||
if str_eq(verb, "bwk") {
|
||
if str_eq(tense, "present") { return cop_bwk_present(prefix) }
|
||
if str_eq(tense, "past") { return cop_bwk_perfect(prefix) }
|
||
if str_eq(tense, "future") { return cop_bwk_future(prefix) }
|
||
return cop_bwk_present(prefix)
|
||
}
|
||
if str_eq(verb, "ⲃⲱⲕ") {
|
||
if str_eq(tense, "present") { return cop_bwk_present(prefix) }
|
||
if str_eq(tense, "past") { return cop_bwk_perfect(prefix) }
|
||
if str_eq(tense, "future") { return cop_bwk_future(prefix) }
|
||
return cop_bwk_present(prefix)
|
||
}
|
||
if str_eq(verb, "go") {
|
||
if str_eq(tense, "present") { return cop_bwk_present(prefix) }
|
||
if str_eq(tense, "past") { return cop_bwk_perfect(prefix) }
|
||
if str_eq(tense, "future") { return cop_bwk_future(prefix) }
|
||
return cop_bwk_present(prefix)
|
||
}
|
||
|
||
// ── nau / ⲛⲁⲩ — to see ───────────────────────────────────────────────────────
|
||
if str_eq(verb, "nau") {
|
||
if str_eq(tense, "present") { return cop_nau_present(prefix) }
|
||
if str_eq(tense, "past") { return cop_nau_perfect(prefix) }
|
||
if str_eq(tense, "future") { return cop_nau_future(prefix) }
|
||
return cop_nau_present(prefix)
|
||
}
|
||
if str_eq(verb, "ⲛⲁⲩ") {
|
||
if str_eq(tense, "present") { return cop_nau_present(prefix) }
|
||
if str_eq(tense, "past") { return cop_nau_perfect(prefix) }
|
||
if str_eq(tense, "future") { return cop_nau_future(prefix) }
|
||
return cop_nau_present(prefix)
|
||
}
|
||
if str_eq(verb, "see") {
|
||
if str_eq(tense, "present") { return cop_nau_present(prefix) }
|
||
if str_eq(tense, "past") { return cop_nau_perfect(prefix) }
|
||
if str_eq(tense, "future") { return cop_nau_future(prefix) }
|
||
return cop_nau_present(prefix)
|
||
}
|
||
|
||
// ── jw / ϫⲱ — to say / speak ─────────────────────────────────────────────────
|
||
if str_eq(verb, "jw") {
|
||
if str_eq(tense, "present") { return cop_jw_present(prefix) }
|
||
if str_eq(tense, "past") { return cop_jw_perfect(prefix) }
|
||
if str_eq(tense, "future") { return cop_jw_future(prefix) }
|
||
return cop_jw_present(prefix)
|
||
}
|
||
if str_eq(verb, "ϫⲱ") {
|
||
if str_eq(tense, "present") { return cop_jw_present(prefix) }
|
||
if str_eq(tense, "past") { return cop_jw_perfect(prefix) }
|
||
if str_eq(tense, "future") { return cop_jw_future(prefix) }
|
||
return cop_jw_present(prefix)
|
||
}
|
||
if str_eq(verb, "say") {
|
||
if str_eq(tense, "present") { return cop_jw_present(prefix) }
|
||
if str_eq(tense, "past") { return cop_jw_perfect(prefix) }
|
||
if str_eq(tense, "future") { return cop_jw_future(prefix) }
|
||
return cop_jw_present(prefix)
|
||
}
|
||
|
||
// ── di / ϯ — to give ──────────────────────────────────────────────────────────
|
||
if str_eq(verb, "di") {
|
||
if str_eq(tense, "present") { return cop_di_present(prefix) }
|
||
if str_eq(tense, "past") { return cop_di_perfect(prefix) }
|
||
if str_eq(tense, "future") { return cop_di_future(prefix) }
|
||
return cop_di_present(prefix)
|
||
}
|
||
if str_eq(verb, "ϯ") {
|
||
if str_eq(tense, "present") { return cop_di_present(prefix) }
|
||
if str_eq(tense, "past") { return cop_di_perfect(prefix) }
|
||
if str_eq(tense, "future") { return cop_di_future(prefix) }
|
||
return cop_di_present(prefix)
|
||
}
|
||
if str_eq(verb, "give") {
|
||
if str_eq(tense, "present") { return cop_di_present(prefix) }
|
||
if str_eq(tense, "past") { return cop_di_perfect(prefix) }
|
||
if str_eq(tense, "future") { return cop_di_future(prefix) }
|
||
return cop_di_present(prefix)
|
||
}
|
||
|
||
// Verb not in table
|
||
return ""
|
||
}
|
||
|
||
// ── Regular verb conjugation ─────────────────────────────────────────────────────
|
||
//
|
||
// For verbs not in the explicit table, apply the productive bipartite pattern:
|
||
// Present: prefix + stem
|
||
// Perfect: ⲁ + prefix + stem
|
||
// Future: prefix + ⲛⲁ + stem
|
||
|
||
fn cop_regular_present(prefix: String, stem: String) -> String {
|
||
return prefix + stem
|
||
}
|
||
|
||
fn cop_regular_perfect(prefix: String, stem: String) -> String {
|
||
return "ⲁ" + prefix + stem
|
||
}
|
||
|
||
fn cop_regular_future(prefix: String, stem: String) -> String {
|
||
return prefix + "ⲛⲁ" + stem
|
||
}
|
||
|
||
// ── cop_conjugate: main conjugation entry point ──────────────────────────────────
|
||
//
|
||
// verb: Coptic verb (Sahidic stem, transliterated, or English canonical label)
|
||
// tense: "present" | "past" | "future"
|
||
// person: "first" | "second" | "third"
|
||
// number: "singular" | "plural"
|
||
//
|
||
// Returns the fully conjugated form with subject prefix embedded.
|
||
// Zero copula ("") is returned for present "be" (adj predicate context).
|
||
// For unknown verbs the regular bipartite pattern is applied as a productive fallback.
|
||
|
||
fn cop_conjugate(verb: String, tense: String, person: String, number: String) -> String {
|
||
let prefix: String = cop_subject_prefix(person, number)
|
||
|
||
// Handle "be" canonical → zero copula in present; ϣωπε otherwise
|
||
if str_eq(verb, "be") {
|
||
if str_eq(tense, "present") { return "" }
|
||
if str_eq(tense, "past") { return cop_shwpe_perfect(prefix) }
|
||
if str_eq(tense, "future") { return cop_shwpe_future(prefix) }
|
||
return ""
|
||
}
|
||
|
||
// Try the known-verb table
|
||
let known: String = cop_known_verb_prefixed(verb, tense, prefix)
|
||
if !str_eq(known, "") {
|
||
return known
|
||
}
|
||
|
||
// Regular productive bipartite conjugation
|
||
if str_eq(tense, "present") { return cop_regular_present(prefix, verb) }
|
||
if str_eq(tense, "past") { return cop_regular_perfect(prefix, verb) }
|
||
if str_eq(tense, "future") { return cop_regular_future(prefix, verb) }
|
||
|
||
// Unknown tense: return verb as safe fallback
|
||
return verb
|
||
}
|
||
|
||
// ── Article system ────────────────────────────────────────────────────────────────
|
||
//
|
||
// cop_article: return the Coptic article string for the given gender/number/definiteness.
|
||
//
|
||
// Definite articles (prefixed directly to noun, no space):
|
||
// Masculine singular: ⲡ- (p-)
|
||
// Feminine singular: ⲧ- (t-)
|
||
// Plural (both): ⲛ- (n-)
|
||
//
|
||
// Indefinite articles:
|
||
// Singular (both genders): ⲟⲩ- (ou-)
|
||
// Plural: ϩⲉⲛ- (hen-)
|
||
//
|
||
// gender: "m" | "f"
|
||
// number: "singular" | "plural"
|
||
// definite: "true" | "false"
|
||
//
|
||
// Returns the article prefix string (to be concatenated with the noun).
|
||
|
||
fn cop_article(gender: String, number: String, definite: String) -> String {
|
||
if str_eq(definite, "true") {
|
||
if str_eq(number, "plural") { return "ⲛ" }
|
||
if str_eq(gender, "f") { return "ⲧ" }
|
||
return "ⲡ"
|
||
}
|
||
// Indefinite
|
||
if str_eq(number, "plural") { return "ϩⲉⲛ" }
|
||
return "ⲟⲩ"
|
||
}
|
||
|
||
// ── Noun number ───────────────────────────────────────────────────────────────────
|
||
//
|
||
// cop_decline: return the noun in the appropriate number form.
|
||
//
|
||
// Coptic nouns have no case endings. Grammatical role is expressed entirely by
|
||
// word order and prepositions. The gram_case parameter is accepted for API
|
||
// symmetry with other morphology modules but has no effect.
|
||
//
|
||
// Plural formation:
|
||
// Coptic plural morphology is highly irregular (inherited from Egyptian and
|
||
// influenced by Greek loanwords). Common patterns:
|
||
// - Many nouns show no suffix change — plurality is indicated only by the plural article ⲛ-.
|
||
// - Some nouns take -ⲟⲟⲩⲉ (-ooue): e.g. ϩⲟ (face) → ϩⲟⲟⲩⲉ
|
||
// - Greek loanwords often add -ⲟⲥ / -ⲟⲩ in Greek fashion
|
||
//
|
||
// This function implements:
|
||
// - No suffix change (base form) as the productive default — the article carries number.
|
||
// - Words ending in ⲉ (a common Coptic nominal ending) may take -ⲟⲟⲩⲉ in the plural;
|
||
// this suffix is applied only when the caller explicitly requests plural and the
|
||
// noun ends in ⲉ (productive pattern).
|
||
// Vocabulary-layer irregular plurals should be stored in vocabulary-cop.el and
|
||
// passed already inflected.
|
||
|
||
fn cop_decline(noun: String, gram_case: String, number: String) -> String {
|
||
if str_eq(number, "singular") { return noun }
|
||
// Plural: if noun ends in ⲉ, attempt -ooue suffix (common productive pattern)
|
||
if cop_str_ends(noun, "ⲉ") {
|
||
let stem: String = cop_drop(noun, 1)
|
||
return stem + "ⲟⲟⲩⲉ"
|
||
}
|
||
// Default: base form (article carries the plural signal)
|
||
return noun
|
||
}
|
||
|
||
// ── Noun phrase assembly ──────────────────────────────────────────────────────────
|
||
//
|
||
// cop_noun_phrase: build a complete Coptic noun phrase.
|
||
//
|
||
// noun: base noun (Coptic script or transliteration)
|
||
// gram_case: accepted for API symmetry; has no effect (Coptic is caseless)
|
||
// number: "singular" | "plural"
|
||
// definite: "true" | "false"
|
||
//
|
||
// The article is prefixed directly to the noun with no intervening space,
|
||
// following standard Coptic orthographic convention.
|
||
// Gender defaults to masculine when not determinable from context; the caller
|
||
// should supply the declined noun already in its correct form if gender-sensitive
|
||
// plural forms are needed.
|
||
|
||
fn cop_noun_phrase(noun: String, gram_case: String, number: String, definite: String) -> String {
|
||
let form: String = cop_decline(noun, gram_case, number)
|
||
// Infer gender from number: if plural, gender is moot for the article (always ⲛ-)
|
||
// For singular, default to masculine (caller provides gender via article if known)
|
||
let art: String = cop_article("m", number, definite)
|
||
if str_eq(definite, "true") {
|
||
return art + form
|
||
}
|
||
if str_eq(definite, "false") {
|
||
// Indefinite article + noun (no space — Coptic convention for proclitic articles)
|
||
return art + form
|
||
}
|
||
return form
|
||
}
|
||
|
||
// cop_noun_phrase_gendered: noun phrase with explicit gender for correct article selection.
|
||
//
|
||
// gender: "m" | "f"
|
||
|
||
fn cop_noun_phrase_gendered(noun: String, gram_case: String, number: String, definite: String, gender: String) -> String {
|
||
let form: String = cop_decline(noun, gram_case, number)
|
||
let art: String = cop_article(gender, number, definite)
|
||
if str_eq(definite, "true") {
|
||
return art + form
|
||
}
|
||
if str_eq(definite, "false") {
|
||
return art + form
|
||
}
|
||
return form
|
||
}
|
||
|
||
// ── Canonical verb mapping ────────────────────────────────────────────────────────
|
||
//
|
||
// cop_map_canonical: map cross-lingual English canonical verb labels to their
|
||
// Sahidic Coptic equivalents before dispatching to cop_conjugate.
|
||
|
||
fn cop_map_canonical(verb: String) -> String {
|
||
if str_eq(verb, "be") { return "be" }
|
||
if str_eq(verb, "go") { return "bwk" }
|
||
if str_eq(verb, "see") { return "nau" }
|
||
if str_eq(verb, "say") { return "jw" }
|
||
if str_eq(verb, "speak") { return "jw" }
|
||
if str_eq(verb, "give") { return "di" }
|
||
// Unknown: return as-is; cop_conjugate will apply the regular pattern
|
||
return verb
|
||
}
|