From 9b63a2a23b3c9005ddd07a16c00a6e47e4e85a54 Mon Sep 17 00:00:00 2001 From: Neuron Date: Fri, 14 Aug 2026 17:37:01 -0500 Subject: [PATCH] elp(translate): EN->ES/PT geometric-free translation faculty MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds the missing middle of the ELP: a deterministic EN-content-lemma -> target-lemma bridge (translate.el) on top of comprehend.el (parse) and realizer.el (inflect). English-only engram geometry cannot route cross-lingually and vocabulary-XX.el carries no en_translation glosses, so the honest no-LLM bridge is a wired lexicon (poem coverage; OOV passes through). SACRED polarity/neg_word are carried untouched: 'never' localizes to a negator ('nunca'), never to a content lemma. Additive realizer extensions: agent_person/agent_number recognize Romance target pronouns; the non-EN negation branch surfaces a carried neg_word instead of the generic negator. Verified on the real toolchain (elc->cc->run): 'You never fought the ocean.' -> ES 'Tú nunca luchaste el océano.' 'I never saw the breaking.' -> ES 'Yo nunca vi la ruptura.' nunca holds 3/3 negation lines. Known gaps: PT verb conjugation fallback (lutarred), irregular EN lemma (broke->break), adjunct/subordinator passthrough. --- elp/src/realizer.el | 24 ++++ elp/src/translate.el | 207 +++++++++++++++++++++++++++ elp/tests/translate_negation_gate.el | 44 ++++++ 3 files changed, 275 insertions(+) create mode 100644 elp/src/translate.el create mode 100644 elp/tests/translate_negation_gate.el diff --git a/elp/src/realizer.el b/elp/src/realizer.el index 9ef642b..3d3cb04 100644 --- a/elp/src/realizer.el +++ b/elp/src/realizer.el @@ -34,6 +34,13 @@ fn agent_person(agent: String) -> String { if str_eq(agent, "we") { return "first" } if str_eq(agent, "us") { return "first" } if str_eq(agent, "you") { return "second" } + // Romance target-language subject pronouns (translate.el sets these). + if str_eq(agent, "yo") { return "first" } + if str_eq(agent, "eu") { return "first" } + if str_eq(agent, "nosotros") { return "first" } + if str_eq(agent, "nós") { return "first" } + if str_eq(agent, "tú") { return "second" } + if str_eq(agent, "tu") { return "second" } return "third" } @@ -50,6 +57,19 @@ fn agent_number(agent: String) -> String { if str_eq(agent, "us") { return "plural" } if str_eq(agent, "they") { return "plural" } if str_eq(agent, "them") { return "plural" } + // Romance target-language subject pronouns. + if str_eq(agent, "yo") { return "singular" } + if str_eq(agent, "eu") { return "singular" } + if str_eq(agent, "tú") { return "singular" } + if str_eq(agent, "tu") { return "singular" } + if str_eq(agent, "él") { return "singular" } + if str_eq(agent, "ella") { return "singular" } + if str_eq(agent, "ele") { return "singular" } + if str_eq(agent, "ela") { return "singular" } + if str_eq(agent, "nosotros") { return "plural" } + if str_eq(agent, "nós") { return "plural" } + if str_eq(agent, "ellos") { return "plural" } + if str_eq(agent, "eles") { return "plural" } return "singular" } @@ -359,7 +379,11 @@ fn realize_lang(form: [String], profile: [String]) -> String { return add_punct(capitalize_first(sentence), "assert") } // Generic non-English: affirmative core with a preverbal negator particle. + // SACRED: when a standalone negative adverb was carried (e.g. "nunca", + // localized upstream from "never"), surface it rather than the generic + // negator — the specific negation must never be flattened away. let neg_particle: String = realize_negator(code) + if !str_eq(neg_word, "") { let neg_particle = neg_word } let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile) let verb_surf: String = native_list_get(vp_pair, 0) let aux_surf: String = native_list_get(vp_pair, 1) diff --git a/elp/src/translate.el b/elp/src/translate.el new file mode 100644 index 0000000..3346834 --- /dev/null +++ b/elp/src/translate.el @@ -0,0 +1,207 @@ +// translate.el - ELP geometric-free translation faculty (EN -> ES/PT/IT). +// +// The bridge from English meaning to a target-language surface, built as an +// EXPLICIT deterministic lexicon on top of the existing halves of the ELP: +// comprehend.el parse_spec(text) : EN surface -> meaning-spec (SACRED polarity) +// realizer.el realize(spec) : meaning-spec -> target surface (inflection) +// translate.el supplies the missing middle: EN content lemma -> target lemma. +// +// WHY an explicit table and not geometry: the live engram embedder +// (nomic-embed-text) is English-only — cross-lingual nearest-neighbour routing +// is near-random (ocean~dog 0.51 > ocean~oceano 0.43), and vocabulary-XX.el +// carries no en_translation glosses. So the honest, deterministic, no-LLM bridge +// is a wired lexicon. Coverage here targets the "Slowness" poem's content words; +// unknown lemmas pass through unchanged and are reported oov=true in routing[]. +// +// SACRED: polarity and neg_word are NEVER routed through the content bridge. +// "never" localizes to "nunca"/"nunca"/"mai" as a negator, never to a lemma. +// +// Depends on (concatenation order): language-profile, morphology, grammar, +// realizer, comprehend, multilingual. + +// ── EN verb lemma -> target infinitive ──────────────────────────────────────── +fn tr_pred(lemma: String, lang: String) -> String { + if str_eq(lang, "en") { return lemma } + if str_eq(lang, "es") { + if str_eq(lemma, "fight") { return "luchar" } + if str_eq(lemma, "touch") { return "tocar" } + if str_eq(lemma, "wait") { return "esperar" } + if str_eq(lemma, "see") { return "ver" } + if str_eq(lemma, "break") { return "romper" } + if str_eq(lemma, "stay") { return "quedar" } + if str_eq(lemma, "call") { return "llamar" } + if str_eq(lemma, "run") { return "correr" } + if str_eq(lemma, "chase") { return "perseguir" } + if str_eq(lemma, "take") { return "tomar" } + if str_eq(lemma, "carry") { return "llevar" } + return tr_pred_fallback(lemma, "es") + } + if str_eq(lang, "pt") { + if str_eq(lemma, "fight") { return "lutar" } + if str_eq(lemma, "touch") { return "tocar" } + if str_eq(lemma, "wait") { return "esperar" } + if str_eq(lemma, "see") { return "ver" } + if str_eq(lemma, "break") { return "quebrar" } + if str_eq(lemma, "stay") { return "ficar" } + if str_eq(lemma, "call") { return "chamar" } + if str_eq(lemma, "run") { return "correr" } + if str_eq(lemma, "chase") { return "perseguir" } + if str_eq(lemma, "take") { return "tomar" } + if str_eq(lemma, "carry") { return "levar" } + return tr_pred_fallback(lemma, "pt") + } + if str_eq(lang, "it") { + if str_eq(lemma, "fight") { return "lottare" } + if str_eq(lemma, "touch") { return "toccare" } + if str_eq(lemma, "wait") { return "aspettare" } + if str_eq(lemma, "see") { return "vedere" } + if str_eq(lemma, "break") { return "rompere" } + if str_eq(lemma, "stay") { return "restare" } + return tr_pred_fallback(lemma, "it") + } + return lemma +} + +// Second-tier verbs already covered by the older multilingual table. +fn tr_pred_fallback(lemma: String, lang: String) -> String { + return ml_translate_pred(lemma, lang) +} + +// ── EN noun head -> [target lemma, gender] ──────────────────────────────────── +// gender = "m" | "f"; used to pick the definite article. Empty lemma => unknown. +fn tr_noun_pair(head: String, lang: String) -> [String] { + let out: [String] = native_list_empty() + if str_eq(lang, "es") { + if str_eq(head, "ocean") { let out = native_list_append(out, "océano"); let out = native_list_append(out, "m"); return out } + if str_eq(head, "root") { let out = native_list_append(out, "raíz"); let out = native_list_append(out, "f"); return out } + if str_eq(head, "roots") { let out = native_list_append(out, "raíces"); let out = native_list_append(out, "f"); return out } + if str_eq(head, "breaking") { let out = native_list_append(out, "ruptura"); let out = native_list_append(out, "f"); return out } + if str_eq(head, "shoreline") { let out = native_list_append(out, "orilla"); let out = native_list_append(out, "f"); return out } + if str_eq(head, "patience") { let out = native_list_append(out, "paciencia"); let out = native_list_append(out, "f"); return out } + if str_eq(head, "wave") { let out = native_list_append(out, "ola"); let out = native_list_append(out, "f"); return out } + if str_eq(head, "truth") { let out = native_list_append(out, "verdad"); let out = native_list_append(out, "f"); return out } + if str_eq(head, "silence") { let out = native_list_append(out, "silencio"); let out = native_list_append(out, "m"); return out } + return out + } + if str_eq(lang, "pt") { + if str_eq(head, "ocean") { let out = native_list_append(out, "oceano"); let out = native_list_append(out, "m"); return out } + if str_eq(head, "root") { let out = native_list_append(out, "raiz"); let out = native_list_append(out, "f"); return out } + if str_eq(head, "roots") { let out = native_list_append(out, "raízes"); let out = native_list_append(out, "f"); return out } + if str_eq(head, "breaking") { let out = native_list_append(out, "ruptura"); let out = native_list_append(out, "f"); return out } + if str_eq(head, "shoreline") { let out = native_list_append(out, "costa"); let out = native_list_append(out, "f"); return out } + if str_eq(head, "patience") { let out = native_list_append(out, "paciência"); let out = native_list_append(out, "f"); return out } + if str_eq(head, "wave") { let out = native_list_append(out, "onda"); let out = native_list_append(out, "f"); return out } + if str_eq(head, "truth") { let out = native_list_append(out, "verdade"); let out = native_list_append(out, "f"); return out } + if str_eq(head, "silence") { let out = native_list_append(out, "silêncio"); let out = native_list_append(out, "m"); return out } + return out + } + return out +} + +// definite article for a gender/lang. +fn tr_def_article(gender: String, lang: String) -> String { + if str_eq(lang, "es") { if str_eq(gender, "f") { return "la" } return "el" } + if str_eq(lang, "pt") { if str_eq(gender, "f") { return "a" } return "o" } + if str_eq(lang, "it") { if str_eq(gender, "f") { return "la" } return "il" } + return "the" +} + +// strip a leading English determiner, return the bare head noun (lowercased). +fn tr_strip_det(np: String) -> String { + let s: String = str_to_lower(np) + let dets: [String] = native_list_empty() + let dets = native_list_append(dets, "the ") + let dets = native_list_append(dets, "a ") + let dets = native_list_append(dets, "an ") + let dets = native_list_append(dets, "my ") + let dets = native_list_append(dets, "your ") + let dets = native_list_append(dets, "his ") + let dets = native_list_append(dets, "her ") + let dets = native_list_append(dets, "its ") + let dets = native_list_append(dets, "our ") + let dets = native_list_append(dets, "their ") + let dets = native_list_append(dets, "every ") + let i: Int = 0 + let n: Int = native_list_len(dets) + while i < n { + let d: String = native_list_get(dets, i) + let dl: Int = str_len(d) + if str_len(s) > dl { + if str_eq(str_slice(s, 0, dl), d) { + return str_slice(s, dl, str_len(s)) + } + } + let i = i + 1 + } + return s +} + +// translate an English object NP into a target NP with definite article. +// Unknown head => pass the English head through (honest OOV), no article. +fn tr_np(np: String, lang: String) -> String { + if str_eq(np, "") { return "" } + let head: String = tr_strip_det(np) + let pair: [String] = tr_noun_pair(head, lang) + if native_list_len(pair) < 2 { return head } + let lemma: String = native_list_get(pair, 0) + let gender: String = native_list_get(pair, 1) + let art: String = tr_def_article(gender, lang) + return art + " " + lemma +} + +// translate an English subject pronoun to the target pronoun (keeps person). +fn tr_pronoun(agent: String, lang: String) -> String { + let a: String = str_to_lower(agent) + if str_eq(lang, "es") { + if str_eq(a, "i") { return "yo" } + if str_eq(a, "you") { return "tú" } + if str_eq(a, "he") { return "él" } + if str_eq(a, "she") { return "ella" } + if str_eq(a, "we") { return "nosotros" } + if str_eq(a, "they") { return "ellos" } + } + if str_eq(lang, "pt") { + if str_eq(a, "i") { return "eu" } + if str_eq(a, "you") { return "tu" } + if str_eq(a, "he") { return "ele" } + if str_eq(a, "she") { return "ela" } + if str_eq(a, "we") { return "nós" } + if str_eq(a, "they") { return "eles" } + } + return agent +} + +// localize a standalone negative adverb (SACRED). "never" -> preverbal negator. +fn tr_neg_word(neg_word: String, lang: String) -> String { + let w: String = str_to_lower(neg_word) + if str_eq(w, "never") { + if str_eq(lang, "es") { return "nunca" } + if str_eq(lang, "pt") { return "nunca" } + if str_eq(lang, "it") { return "mai" } + } + return "" +} + +// ── the faculty: EN text -> target surface ──────────────────────────────────── +// Parses EN (mature path), swaps content lemmas via the wired bridge, carries +// SACRED polarity/neg_word untouched, and hands a target-lang spec to realize(). +fn translate_spec(text: String, tgt: String) -> [String] { + let spec: [String] = parse_spec(text) + let pred: String = slots_get(spec, "predicate") + let pat: String = slots_get(spec, "patient") + let agent: String = slots_get(spec, "agent") + let negw: String = slots_get(spec, "neg_word") + + let spec = slots_set(spec, "predicate", tr_pred(pred, tgt)) + let spec = slots_set(spec, "patient", tr_np(pat, tgt)) + let spec = slots_set(spec, "agent", tr_pronoun(agent, tgt)) + // SACRED: neg_word localized to a negator, polarity left exactly as parsed. + let tw: String = tr_neg_word(negw, tgt) + if !str_eq(tw, "") { let spec = slots_set(spec, "neg_word", tw) } + let spec = slots_set(spec, "lang", tgt) + return spec +} + +fn translate_line(text: String, tgt: String) -> String { + return realize(translate_spec(text, tgt)) +} diff --git a/elp/tests/translate_negation_gate.el b/elp/tests/translate_negation_gate.el new file mode 100644 index 0000000..8d2f354 --- /dev/null +++ b/elp/tests/translate_negation_gate.el @@ -0,0 +1,44 @@ +// translate_negation_gate.el - EN -> ES/PT translation of the poem's negation +// lines. The bar (Aug-13 python reference): +// l11 "You never fought the ocean." -> "Tu nunca luchaste el oceano" (es) / +// "Tu nunca lutaste o oceano" (pt) +// l16 "but never touched my roots." -> "...nunca toco los raices" / +// "...nunca tocou os raizes" +// SACRED requirement: "never" must surface as "nunca" and polarity stay neg — +// negation is NEVER routed to a content lemma. + +fn tg_line(text: String) -> String { + let spec: [String] = parse_spec(text) + let pol: String = slots_get(spec, "polarity") + let negw: String = slots_get(spec, "neg_word") + let pred: String = slots_get(spec, "predicate") + let pat: String = slots_get(spec, "patient") + let es: String = translate_line(text, "es") + let pt: String = translate_line(text, "pt") + let out: String = "EN: " + text + "\n" + let out = out + " spec: pol=" + pol + " neg_word=" + negw + " pred=" + pred + " patient=" + pat + "\n" + let out = out + " ES: " + es + "\n" + let out = out + " PT: " + pt + "\n" + // SACRED check: if source is negative, "nunca" must appear in both surfaces. + let es_ok: String = "n/a" + if str_eq(pol, "neg") { + let es_ok = "NUNCA-LOST" + if str_contains(es, "nunca") { let es_ok = "nunca-ok" } + } + let out = out + " negation[es]: " + es_ok + "\n" + return out +} + +fn run_translate_negation_gate() -> String { + let rep: String = "==== ELP EN->ES/PT translation — negation lines ====\n" + // Negation lines (SACRED nunca must hold): + let rep = rep + tg_line("You never fought the ocean.") + let rep = rep + tg_line("but never touched my roots.") + let rep = rep + tg_line("I never saw the breaking.") + // Affirmative controls: + let rep = rep + tg_line("You waited like the shoreline.") + let rep = rep + tg_line("I broke against your truth.") + return rep +} + +println(run_translate_negation_gate())