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
+755
View File
@@ -0,0 +1,755 @@
// morphology-grc.el - Ancient Greek morphology for the NLG engine.
//
// Implements polytonic Ancient Greek verb conjugation, noun declension, and
// the definite article. Designed as a companion to morphology.el and called
// by the engine when the language profile code is "grc".
//
// Language profile: code=grc, name=Ancient Greek, morph_type=fusional,
// word_order=SOV, question_strategy=particle, script=greek, family=hellenic.
//
// Typology note: Ancient Greek is a heavily inflected synthetic language with
// rich morphophonology. Nouns carry gender, case, and number in fused endings.
// Verbs encode tense, aspect, voice, mood, person, and number. The augment
// (ε-) marks past indicative. All polytonic diacritics are preserved in
// string literals; comparisons use exact Unicode equality.
//
// Verb conjugation covered:
// Tenses: present, imperfect, aorist, future
// Persons: first/second/third × singular/plural (slots 0-5)
// Class: thematic () verbs
// Irregulars: εἰναι (be), ἔχειν (have), λέγειν (say), ὁράω (see),
// ἔρχεσθαι (come/go)
// Canonical map: "be" -> "εἰναι"
//
// Noun declension covered:
// Cases: nominative, accusative, genitive, dative, vocative
// Declensions: 1st (-α/-η fem), 2nd masc (-ος), 2nd neut (-ον), 3rd (base)
//
// Article: full 24-form table for ὁ/ἡ/τό (gender × case × number).
//
// Depends on: morphology.el (str_ends_with, str_len, str_slice, str_eq)
// String helpers
fn grc_str_ends(s: String, suf: String) -> Bool {
return str_ends_with(s, suf)
}
fn grc_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)
}
fn grc_str_last_char(s: String) -> String {
let n: Int = str_len(s)
if n == 0 {
return ""
}
return str_slice(s, n - 1, n)
}
fn grc_str_last2(s: String) -> String {
let n: Int = str_len(s)
if n < 2 {
return s
}
return str_slice(s, n - 2, n)
}
fn grc_str_last3(s: String) -> String {
let n: Int = str_len(s)
if n < 3 {
return s
}
return str_slice(s, n - 3, n)
}
// Person/number slot
//
// Maps person × number to a 0-based index used in all paradigm tables.
// 0 = 1st singular (ἐγώ)
// 1 = 2nd singular (σύ)
// 2 = 3rd singular (αὐτός/αὐτή/αὐτό)
// 3 = 1st plural (ἡμεῖς)
// 4 = 2nd plural (ὑμεῖς)
// 5 = 3rd plural (αὐτοί)
//
// Dual number is not handled; dual inputs fall through to plural.
fn grc_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
}
// Canonical verb mapping
//
// The semantic layer may pass English canonical labels. Map these to the
// Ancient Greek infinitive (or dictionary citation form) before conjugation.
fn grc_map_canonical(verb: String) -> String {
if str_eq(verb, "be") { return "εἰναι" }
if str_eq(verb, "have") { return "ἔχειν" }
if str_eq(verb, "say") { return "λέγειν" }
if str_eq(verb, "see") { return "ὁράω" }
if str_eq(verb, "come") { return "ἔρχεσθαι" }
if str_eq(verb, "go") { return "ἔρχεσθαι" }
if str_eq(verb, "know") { return "γιγνώσκειν" }
if str_eq(verb, "write") { return "γράφειν" }
if str_eq(verb, "hear") { return "ἀκούειν" }
if str_eq(verb, "want") { return "βούλεσθαι" }
if str_eq(verb, "do") { return "ποιεῖν" }
if str_eq(verb, "make") { return "ποιεῖν" }
return verb
}
// Irregular: εἰναι (to be)
//
// Present indicative active: εἰμί εἶ ἐστί ἐσμέν ἐστέ εἰσί
// Imperfect: ἦν ἦσθα ἦν ἦμεν ἦτε ἦσαν
// Future: ἔσομαι ἔσῃ ἔσται ἐσόμεθα ἔσεσθε ἔσονται
// Aorist: no aorist of εἰμί; use imperfect as fallback
fn grc_einai_present(slot: Int) -> String {
if slot == 0 { return "εἰμί" }
if slot == 1 { return "εἶ" }
if slot == 2 { return "ἐστί" }
if slot == 3 { return "ἐσμέν" }
if slot == 4 { return "ἐστέ" }
return "εἰσί"
}
fn grc_einai_imperfect(slot: Int) -> String {
if slot == 0 { return "ἦν" }
if slot == 1 { return "ἦσθα" }
if slot == 2 { return "ἦν" }
if slot == 3 { return "ἦμεν" }
if slot == 4 { return "ἦτε" }
return "ἦσαν"
}
fn grc_einai_future(slot: Int) -> String {
if slot == 0 { return "ἔσομαι" }
if slot == 1 { return "ἔσῃ" }
if slot == 2 { return "ἔσται" }
if slot == 3 { return "ἐσόμεθα" }
if slot == 4 { return "ἔσεσθε" }
return "ἔσονται"
}
// Irregular: ἔχειν (to have)
//
// Present: ἔχω ἔχεις ἔχει ἔχομεν ἔχετε ἔχουσι
// Imperfect: εἶχον εἶχες εἶχε εἴχομεν εἴχετε εἶχον
// Aorist: ἔσχον ἔσχες ἔσχε ἔσχομεν ἔσχετε ἔσχον
// Future: ἕξω ἕξεις ἕξει ἕξομεν ἕξετε ἕξουσι
fn grc_echein_present(slot: Int) -> String {
if slot == 0 { return "ἔχω" }
if slot == 1 { return "ἔχεις" }
if slot == 2 { return "ἔχει" }
if slot == 3 { return "ἔχομεν" }
if slot == 4 { return "ἔχετε" }
return "ἔχουσι"
}
fn grc_echein_imperfect(slot: Int) -> String {
if slot == 0 { return "εἶχον" }
if slot == 1 { return "εἶχες" }
if slot == 2 { return "εἶχε" }
if slot == 3 { return "εἴχομεν" }
if slot == 4 { return "εἴχετε" }
return "εἶχον"
}
fn grc_echein_aorist(slot: Int) -> String {
if slot == 0 { return "ἔσχον" }
if slot == 1 { return "ἔσχες" }
if slot == 2 { return "ἔσχε" }
if slot == 3 { return "ἔσχομεν" }
if slot == 4 { return "ἔσχετε" }
return "ἔσχον"
}
fn grc_echein_future(slot: Int) -> String {
if slot == 0 { return "ἕξω" }
if slot == 1 { return "ἕξεις" }
if slot == 2 { return "ἕξει" }
if slot == 3 { return "ἕξομεν" }
if slot == 4 { return "ἕξετε" }
return "ἕξουσι"
}
// Irregular: λέγειν (to say)
//
// Present: λέγω λέγεις λέγει λέγομεν λέγετε λέγουσι
// Imperfect: ἔλεγον ἔλεγες ἔλεγε ἐλέγομεν ἐλέγετε ἔλεγον
// Aorist: εἶπον εἶπες εἶπε εἴπομεν εἴπετε εἶπον
// Future: λέξω λέξεις λέξει λέξομεν λέξετε λέξουσι
fn grc_legein_present(slot: Int) -> String {
if slot == 0 { return "λέγω" }
if slot == 1 { return "λέγεις" }
if slot == 2 { return "λέγει" }
if slot == 3 { return "λέγομεν" }
if slot == 4 { return "λέγετε" }
return "λέγουσι"
}
fn grc_legein_imperfect(slot: Int) -> String {
if slot == 0 { return "ἔλεγον" }
if slot == 1 { return "ἔλεγες" }
if slot == 2 { return "ἔλεγε" }
if slot == 3 { return "ἐλέγομεν" }
if slot == 4 { return "ἐλέγετε" }
return "ἔλεγον"
}
fn grc_legein_aorist(slot: Int) -> String {
if slot == 0 { return "εἶπον" }
if slot == 1 { return "εἶπες" }
if slot == 2 { return "εἶπε" }
if slot == 3 { return "εἴπομεν" }
if slot == 4 { return "εἴπετε" }
return "εἶπον"
}
fn grc_legein_future(slot: Int) -> String {
if slot == 0 { return "λέξω" }
if slot == 1 { return "λέξεις" }
if slot == 2 { return "λέξει" }
if slot == 3 { return "λέξομεν" }
if slot == 4 { return "λέξετε" }
return "λέξουσι"
}
// Irregular: ὁράω (to see)
//
// Present: ὁράω ὁράς ὁρᾷ ὁρῶμεν ὁρᾶτε ὁρῶσι
// Imperfect: ἑώρων ἑώρας ἑώρα ἑωρῶμεν ἑωρᾶτε ἑώρων
// Aorist: εἶδον εἶδες εἶδε εἴδομεν εἴδετε εἶδον
// Future: ὄψομαι ὄψῃ ὄψεται ὀψόμεθα ὄψεσθε ὄψονται
fn grc_horao_present(slot: Int) -> String {
if slot == 0 { return "ὁράω" }
if slot == 1 { return "ὁράς" }
if slot == 2 { return "ὁρᾷ" }
if slot == 3 { return "ὁρῶμεν" }
if slot == 4 { return "ὁρᾶτε" }
return "ὁρῶσι"
}
fn grc_horao_imperfect(slot: Int) -> String {
if slot == 0 { return "ἑώρων" }
if slot == 1 { return "ἑώρας" }
if slot == 2 { return "ἑώρα" }
if slot == 3 { return "ἑωρῶμεν" }
if slot == 4 { return "ἑωρᾶτε" }
return "ἑώρων"
}
fn grc_horao_aorist(slot: Int) -> String {
if slot == 0 { return "εἶδον" }
if slot == 1 { return "εἶδες" }
if slot == 2 { return "εἶδε" }
if slot == 3 { return "εἴδομεν" }
if slot == 4 { return "εἴδετε" }
return "εἶδον"
}
fn grc_horao_future(slot: Int) -> String {
if slot == 0 { return "ὄψομαι" }
if slot == 1 { return "ὄψῃ" }
if slot == 2 { return "ὄψεται" }
if slot == 3 { return "ὀψόμεθα" }
if slot == 4 { return "ὄψεσθε" }
return "ὄψονται"
}
// Irregular: ἔρχεσθαι (to come / to go)
//
// Present: ἔρχομαι ἔρχῃ ἔρχεται ἐρχόμεθα ἔρχεσθε ἔρχονται
// Imperfect: ἠρχόμην ἤρχου ἤρχετο ἠρχόμεθα ἤρχεσθε ἤρχοντο
// Aorist: ἦλθον ἦλθες ἦλθε ἤλθομεν ἤλθετε ἦλθον
// Future: εἶμι εἶ εἶσι ἴμεν ἴτε ἴασι
fn grc_erchesthai_present(slot: Int) -> String {
if slot == 0 { return "ἔρχομαι" }
if slot == 1 { return "ἔρχῃ" }
if slot == 2 { return "ἔρχεται" }
if slot == 3 { return "ἐρχόμεθα" }
if slot == 4 { return "ἔρχεσθε" }
return "ἔρχονται"
}
fn grc_erchesthai_imperfect(slot: Int) -> String {
if slot == 0 { return "ἠρχόμην" }
if slot == 1 { return "ἤρχου" }
if slot == 2 { return "ἤρχετο" }
if slot == 3 { return "ἠρχόμεθα" }
if slot == 4 { return "ἤρχεσθε" }
return "ἤρχοντο"
}
fn grc_erchesthai_aorist(slot: Int) -> String {
if slot == 0 { return "ἦλθον" }
if slot == 1 { return "ἦλθες" }
if slot == 2 { return "ἦλθε" }
if slot == 3 { return "ἤλθομεν" }
if slot == 4 { return "ἤλθετε" }
return "ἦλθον"
}
fn grc_erchesthai_future(slot: Int) -> String {
if slot == 0 { return "εἶμι" }
if slot == 1 { return "εἶ" }
if slot == 2 { return "εἶσι" }
if slot == 3 { return "ἴμεν" }
if slot == 4 { return "ἴτε" }
return "ἴασι"
}
// Thematic () present active endings
//
// Slot: 0 1 2 3 4 5
// -εις -ει -ομεν -ετε -ουσι
//
// These are attached to the present stem.
fn grc_thematic_present_ending(slot: Int) -> String {
if slot == 0 { return "ω" }
if slot == 1 { return "εις" }
if slot == 2 { return "ει" }
if slot == 3 { return "ομεν" }
if slot == 4 { return "ετε" }
return "ουσι"
}
// Thematic imperfect active endings
//
// Imperfect = augment (ἐ-) + stem + secondary endings
// Slot: 0 1 2 3 4 5
// -ον -ες -ομεν -ετε -ον
fn grc_thematic_imperfect_ending(slot: Int) -> String {
if slot == 0 { return "ον" }
if slot == 1 { return "ες" }
if slot == 2 { return "ε" }
if slot == 3 { return "ομεν" }
if slot == 4 { return "ετε" }
return "ον"
}
// Thematic future active endings
//
// Future = stem + σ + thematic vowel + primary endings.
// For most verbs: stem + σ serves as the future stem, then present endings.
// Slot: 0 1 2 3 4 5
// -σω -σεις -σει -σομεν -σετε -σουσι
fn grc_thematic_future_ending(slot: Int) -> String {
if slot == 0 { return "σω" }
if slot == 1 { return "σεις" }
if slot == 2 { return "σει" }
if slot == 3 { return "σομεν" }
if slot == 4 { return "σετε" }
return "σουσι"
}
// Weak (first) aorist active endings
//
// Weak aorist = augment (ἐ-) + stem + σ + aorist endings
// Slot: 0 1 2 3 4 5
// -σα -σας -σε -σαμεν -σατε -σαν
fn grc_weak_aorist_ending(slot: Int) -> String {
if slot == 0 { return "σα" }
if slot == 1 { return "σας" }
if slot == 2 { return "σε" }
if slot == 3 { return "σαμεν" }
if slot == 4 { return "σατε" }
return "σαν"
}
// Stem extraction
//
// Strip the citation-form ending to recover the present stem.
// -ειν -> strip 3 chars (λύειν -> λύ-)
// -> strip 1 char (λύω -> λύ-)
// -αω -> strip 2 chars (ὁράω -> ὁρ-; handled as irregular above)
// -εω -> strip 2 chars (contracted -εω verbs)
// otherwise: return as-is (already a stem or unrecognised form)
fn grc_present_stem(verb: String) -> String {
if grc_str_ends(verb, "ειν") {
return grc_str_drop_last(verb, 3)
}
if grc_str_ends(verb, "αω") {
return grc_str_drop_last(verb, 2)
}
if grc_str_ends(verb, "εω") {
return grc_str_drop_last(verb, 2)
}
if grc_str_ends(verb, "ω") {
return grc_str_drop_last(verb, 1)
}
return verb
}
// grc_conjugate: main conjugation entry point
//
// verb: Greek infinitive/citation form or English canonical label
// tense: "present" | "imperfect" | "aorist" | "future"
// person: "first" | "second" | "third"
// number: "singular" | "plural"
//
// Returns the inflected form. Falls back to the citation form for unknown
// tenses or unrecognised verbs rather than crashing.
fn grc_conjugate(verb: String, tense: String, person: String, number: String) -> String {
let v: String = grc_map_canonical(verb)
let slot: Int = grc_slot(person, number)
// Irregulars
if str_eq(v, "εἰναι") {
if str_eq(tense, "present") { return grc_einai_present(slot) }
if str_eq(tense, "imperfect") { return grc_einai_imperfect(slot) }
if str_eq(tense, "aorist") { return grc_einai_imperfect(slot) }
if str_eq(tense, "future") { return grc_einai_future(slot) }
return v
}
if str_eq(v, "ἔχειν") {
if str_eq(tense, "present") { return grc_echein_present(slot) }
if str_eq(tense, "imperfect") { return grc_echein_imperfect(slot) }
if str_eq(tense, "aorist") { return grc_echein_aorist(slot) }
if str_eq(tense, "future") { return grc_echein_future(slot) }
return v
}
if str_eq(v, "λέγειν") {
if str_eq(tense, "present") { return grc_legein_present(slot) }
if str_eq(tense, "imperfect") { return grc_legein_imperfect(slot) }
if str_eq(tense, "aorist") { return grc_legein_aorist(slot) }
if str_eq(tense, "future") { return grc_legein_future(slot) }
return v
}
if str_eq(v, "ὁράω") {
if str_eq(tense, "present") { return grc_horao_present(slot) }
if str_eq(tense, "imperfect") { return grc_horao_imperfect(slot) }
if str_eq(tense, "aorist") { return grc_horao_aorist(slot) }
if str_eq(tense, "future") { return grc_horao_future(slot) }
return v
}
if str_eq(v, "ἔρχεσθαι") {
if str_eq(tense, "present") { return grc_erchesthai_present(slot) }
if str_eq(tense, "imperfect") { return grc_erchesthai_imperfect(slot) }
if str_eq(tense, "aorist") { return grc_erchesthai_aorist(slot) }
if str_eq(tense, "future") { return grc_erchesthai_future(slot) }
return v
}
// Regular thematic conjugation
let stem: String = grc_present_stem(v)
if str_eq(tense, "present") {
return stem + grc_thematic_present_ending(slot)
}
if str_eq(tense, "imperfect") {
// Augment: prefix ἐ- to the stem
return "" + stem + grc_thematic_imperfect_ending(slot)
}
if str_eq(tense, "future") {
return stem + grc_thematic_future_ending(slot)
}
if str_eq(tense, "aorist") {
// Weak (sigmatic) aorist: ἐ- + stem + σ endings
return "" + stem + grc_weak_aorist_ending(slot)
}
// Unknown tense: return citation form unchanged
return v
}
// Declension detection
//
// Infer Greek declension class from the nominative singular ending.
//
// ends in -ος -> 2nd declension masculine
// ends in -ον -> 2nd declension neuter
// ends in -α -> 1st declension feminine (alpha-stem)
// ends in -> 1st declension feminine (eta-stem)
// otherwise -> 3rd declension (consonant stem; too varied for full tables)
fn grc_declension(noun: String) -> String {
if grc_str_ends(noun, "ος") { return "2m" }
if grc_str_ends(noun, "ον") { return "2n" }
if grc_str_ends(noun, "α") { return "1a" }
if grc_str_ends(noun, "η") { return "1e" }
return "3"
}
// 2nd declension masculine: -ος nouns
//
// Stem: remove -ος (2 chars)
// Singular: nom -ος gen -ου dat -ῳ acc -ον voc
// Plural: nom -οι gen -ων dat -οις acc -ους voc -οι
fn grc_decline_2m(stem: String, gram_case: String, number: String) -> String {
if str_eq(number, "singular") {
if str_eq(gram_case, "nominative") { return stem + "ος" }
if str_eq(gram_case, "genitive") { return stem + "ου" }
if str_eq(gram_case, "dative") { return stem + "" }
if str_eq(gram_case, "accusative") { return stem + "ον" }
if str_eq(gram_case, "vocative") { return stem + "ε" }
return stem + "ος"
}
// plural
if str_eq(gram_case, "nominative") { return stem + "οι" }
if str_eq(gram_case, "genitive") { return stem + "ων" }
if str_eq(gram_case, "dative") { return stem + "οις" }
if str_eq(gram_case, "accusative") { return stem + "ους" }
if str_eq(gram_case, "vocative") { return stem + "οι" }
return stem + "οι"
}
// 2nd declension neuter: -ον nouns
//
// Stem: remove -ον (2 chars)
// Singular: nom/acc/voc -ον gen -ου dat -ῳ
// Plural: nom/acc/voc -α gen -ων dat -οις
fn grc_decline_2n(stem: String, gram_case: String, number: String) -> String {
if str_eq(number, "singular") {
if str_eq(gram_case, "nominative") { return stem + "ον" }
if str_eq(gram_case, "genitive") { return stem + "ου" }
if str_eq(gram_case, "dative") { return stem + "" }
if str_eq(gram_case, "accusative") { return stem + "ον" }
if str_eq(gram_case, "vocative") { return stem + "ον" }
return stem + "ον"
}
// plural
if str_eq(gram_case, "nominative") { return stem + "α" }
if str_eq(gram_case, "genitive") { return stem + "ων" }
if str_eq(gram_case, "dative") { return stem + "οις" }
if str_eq(gram_case, "accusative") { return stem + "α" }
if str_eq(gram_case, "vocative") { return stem + "α" }
return stem + "α"
}
// 1st declension feminine: alpha-stem (-α) nouns
//
// Stem: remove -α (1 char)
// Singular: nom -α gen -ας dat -ᾳ acc -αν voc -α
// Plural: nom -αι gen -ων dat -αις acc -ας voc -αι
fn grc_decline_1a(stem: String, gram_case: String, number: String) -> String {
if str_eq(number, "singular") {
if str_eq(gram_case, "nominative") { return stem + "α" }
if str_eq(gram_case, "genitive") { return stem + "ας" }
if str_eq(gram_case, "dative") { return stem + "" }
if str_eq(gram_case, "accusative") { return stem + "αν" }
if str_eq(gram_case, "vocative") { return stem + "α" }
return stem + "α"
}
// plural
if str_eq(gram_case, "nominative") { return stem + "αι" }
if str_eq(gram_case, "genitive") { return stem + "ων" }
if str_eq(gram_case, "dative") { return stem + "αις" }
if str_eq(gram_case, "accusative") { return stem + "ας" }
if str_eq(gram_case, "vocative") { return stem + "αι" }
return stem + "αι"
}
// 1st declension feminine: eta-stem () nouns
//
// Stem: remove (1 char)
// Singular: nom gen -ης dat -ῃ acc -ην voc
// Plural: nom -αι gen -ων dat -αις acc -ας voc -αι
fn grc_decline_1e(stem: String, gram_case: String, number: String) -> String {
if str_eq(number, "singular") {
if str_eq(gram_case, "nominative") { return stem + "η" }
if str_eq(gram_case, "genitive") { return stem + "ης" }
if str_eq(gram_case, "dative") { return stem + "" }
if str_eq(gram_case, "accusative") { return stem + "ην" }
if str_eq(gram_case, "vocative") { return stem + "η" }
return stem + "η"
}
// plural (same as alpha-stem in the plural)
if str_eq(gram_case, "nominative") { return stem + "αι" }
if str_eq(gram_case, "genitive") { return stem + "ων" }
if str_eq(gram_case, "dative") { return stem + "αις" }
if str_eq(gram_case, "accusative") { return stem + "ας" }
if str_eq(gram_case, "vocative") { return stem + "αι" }
return stem + "αι"
}
// grc_decline: main declension entry point
//
// noun: nominative singular Greek noun (e.g. "λόγος", "ἔργον", "χώρα")
// gram_case: "nominative" | "accusative" | "genitive" | "dative" | "vocative"
// number: "singular" | "plural"
//
// 3rd declension consonant stems are too unpredictable without a full lexicon;
// for those the nominative is returned unchanged as a safe fallback.
fn grc_decline(noun: String, gram_case: String, number: String) -> String {
let decl: String = grc_declension(noun)
if str_eq(decl, "2m") {
let stem: String = grc_str_drop_last(noun, 2)
return grc_decline_2m(stem, gram_case, number)
}
if str_eq(decl, "2n") {
let stem: String = grc_str_drop_last(noun, 2)
return grc_decline_2n(stem, gram_case, number)
}
if str_eq(decl, "1a") {
let stem: String = grc_str_drop_last(noun, 1)
return grc_decline_1a(stem, gram_case, number)
}
if str_eq(decl, "1e") {
let stem: String = grc_str_drop_last(noun, 1)
return grc_decline_1e(stem, gram_case, number)
}
// 3rd declension: return the base form unchanged
return noun
}
// grc_article: definite article
//
// The Ancient Greek definite article ὁ/ἡ/τό is fully declined for gender,
// case, and number. There is no indefinite article.
//
// gender: "masculine" | "feminine" | "neuter"
// gram_case: "nominative" | "accusative" | "genitive" | "dative" | "vocative"
// number: "singular" | "plural"
//
// Masc Fem Neut
// Nom sg: τό
// Gen sg: τοῦ τῆς τοῦ
// Dat sg: τῷ τῇ τῷ
// Acc sg: τόν τήν τό
// Voc sg: (none; ὦ used as exclamatory particle, not article)
// Nom pl: οἱ αἱ τά
// Gen pl: τῶν τῶν τῶν
// Dat pl: τοῖς ταῖς τοῖς
// Acc pl: τούς τάς τά
// Voc pl: (same as nom pl in practice)
fn grc_article_masculine(gram_case: String, number: String) -> String {
if str_eq(number, "singular") {
if str_eq(gram_case, "nominative") { return "" }
if str_eq(gram_case, "genitive") { return "τοῦ" }
if str_eq(gram_case, "dative") { return "τῷ" }
if str_eq(gram_case, "accusative") { return "τόν" }
if str_eq(gram_case, "vocative") { return "" }
return ""
}
// plural
if str_eq(gram_case, "nominative") { return "οἱ" }
if str_eq(gram_case, "genitive") { return "τῶν" }
if str_eq(gram_case, "dative") { return "τοῖς" }
if str_eq(gram_case, "accusative") { return "τούς" }
if str_eq(gram_case, "vocative") { return "οἱ" }
return "οἱ"
}
fn grc_article_feminine(gram_case: String, number: String) -> String {
if str_eq(number, "singular") {
if str_eq(gram_case, "nominative") { return "" }
if str_eq(gram_case, "genitive") { return "τῆς" }
if str_eq(gram_case, "dative") { return "τῇ" }
if str_eq(gram_case, "accusative") { return "τήν" }
if str_eq(gram_case, "vocative") { return "" }
return ""
}
// plural
if str_eq(gram_case, "nominative") { return "αἱ" }
if str_eq(gram_case, "genitive") { return "τῶν" }
if str_eq(gram_case, "dative") { return "ταῖς" }
if str_eq(gram_case, "accusative") { return "τάς" }
if str_eq(gram_case, "vocative") { return "αἱ" }
return "αἱ"
}
fn grc_article_neuter(gram_case: String, number: String) -> String {
if str_eq(number, "singular") {
if str_eq(gram_case, "nominative") { return "τό" }
if str_eq(gram_case, "genitive") { return "τοῦ" }
if str_eq(gram_case, "dative") { return "τῷ" }
if str_eq(gram_case, "accusative") { return "τό" }
if str_eq(gram_case, "vocative") { return "τό" }
return "τό"
}
// plural
if str_eq(gram_case, "nominative") { return "τά" }
if str_eq(gram_case, "genitive") { return "τῶν" }
if str_eq(gram_case, "dative") { return "τοῖς" }
if str_eq(gram_case, "accusative") { return "τά" }
if str_eq(gram_case, "vocative") { return "τά" }
return "τά"
}
fn grc_article(gender: String, gram_case: String, number: String) -> String {
if str_eq(gender, "masculine") { return grc_article_masculine(gram_case, number) }
if str_eq(gender, "feminine") { return grc_article_feminine(gram_case, number) }
// neuter
return grc_article_neuter(gram_case, number)
}
// Infer gender from noun ending
//
// Used by grc_noun_phrase when no explicit gender is provided.
// -ος -> masculine (default 2nd), -ον -> neuter, -α/-η -> feminine.
fn grc_infer_gender(noun: String) -> String {
if grc_str_ends(noun, "ος") { return "masculine" }
if grc_str_ends(noun, "ον") { return "neuter" }
if grc_str_ends(noun, "α") { return "feminine" }
if grc_str_ends(noun, "η") { return "feminine" }
return "masculine"
}
// grc_noun_phrase: noun phrase builder
//
// Produces a declined noun with optional definite article prepended.
// Gender is inferred from the noun ending when not supplied explicitly.
//
// noun: nominative singular Greek noun
// gram_case: "nominative" | "accusative" | "genitive" | "dative" | "vocative"
// number: "singular" | "plural"
// definite: "true" | "false" (article is prepended when "true")
fn grc_noun_phrase(noun: String, gram_case: String, number: String, definite: String) -> String {
let declined: String = grc_decline(noun, gram_case, number)
if str_eq(definite, "true") {
let gender: String = grc_infer_gender(noun)
let art: String = grc_article(gender, gram_case, number)
return art + " " + declined
}
return declined
}