Files
el/elp/src/morphology-grc.el
T
will.anderson c2cd5e01e1
El SDK CI - dev / build-and-test (pull_request) Successful in 3m34s
fix: elb macOS OpenSSL + C master declarations header; add ELP missing imports
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
2026-05-08 19:44:31 -05:00

757 lines
31 KiB
EmacsLisp
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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
import "morphology.el"
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
}