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
757 lines
31 KiB
EmacsLisp
757 lines
31 KiB
EmacsLisp
// 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
|
||
}
|