9b63a2a23b
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.
436 lines
22 KiB
EmacsLisp
436 lines
22 KiB
EmacsLisp
// realizer.el - Universal syntactic realizer: GramSpec -> surface text.
|
|
//
|
|
// The realizer is now language-agnostic. It reads the "lang" field from the
|
|
// GramSpec to resolve a language profile, then dispatches word order, question
|
|
// formation, and morphology through the engine functions in grammar.el and
|
|
// morphology.el.
|
|
//
|
|
// English remains the default (backward compatible) when no "lang" key is set.
|
|
//
|
|
// Realization pipeline per call:
|
|
// 1. Extract lang code -> resolve profile
|
|
// 2. Extract agent, predicate, patient, location, tense, aspect, intent
|
|
// 3. Compute person/number from agent (English heuristic; other languages TBD)
|
|
// 4. Build VP: morph_conjugate with profile -> get verb and auxiliary surface
|
|
// 5. Choose question strategy from gram_question_strategy(profile)
|
|
// 6. Order constituents via gram_order_constituents(subj, verb, obj, profile)
|
|
// 7. Capitalize and terminate
|
|
//
|
|
// Depends on: morphology (morph_conjugate, agree_determiner)
|
|
// grammar (gram_order_constituents, gram_question_strategy,
|
|
// gram_build_vp, build_np, build_pp, slots_get)
|
|
// language-profile (lang_from_code, lang_get, ...)
|
|
|
|
// ── Agent agreement analysis ──────────────────────────────────────────────────
|
|
//
|
|
// Person and number are inferred from English pronouns. For other languages
|
|
// the grammatical person/number should come from the Engram vocabulary node
|
|
// for the subject; here we use a heuristic that is correct for English and
|
|
// passable for languages where the same pronoun strings are used.
|
|
|
|
fn agent_person(agent: String) -> String {
|
|
if str_eq(agent, "I") { return "first" }
|
|
if str_eq(agent, "me") { return "first" }
|
|
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"
|
|
}
|
|
|
|
fn agent_number(agent: String) -> String {
|
|
if str_eq(agent, "I") { return "singular" }
|
|
if str_eq(agent, "me") { return "singular" }
|
|
if str_eq(agent, "he") { return "singular" }
|
|
if str_eq(agent, "him") { return "singular" }
|
|
if str_eq(agent, "she") { return "singular" }
|
|
if str_eq(agent, "her") { return "singular" }
|
|
if str_eq(agent, "it") { return "singular" }
|
|
if str_eq(agent, "you") { return "singular" }
|
|
if str_eq(agent, "we") { return "plural" }
|
|
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"
|
|
}
|
|
|
|
// ── NP realization ────────────────────────────────────────────────────────────
|
|
|
|
fn realize_np(referent: String, number: String) -> String {
|
|
return referent
|
|
}
|
|
|
|
// ── VP realization ────────────────────────────────────────────────────────────
|
|
//
|
|
// Returns [main_verb_surface, aux_surface_or_empty].
|
|
// Delegates conjugation to morph_conjugate with the language profile.
|
|
|
|
fn realize_vp_lang(base_verb: String, tense: String, aspect: String, person: String, number: String, profile: [String]) -> [String] {
|
|
let empty_aux: String = ""
|
|
|
|
if str_eq(tense, "future") {
|
|
// Future: modal "will" + base (English) or language-specific future marker.
|
|
// For isolating/agglutinative languages the future marker is also the
|
|
// base form (morph_conjugate returns base); the surface "will" only appears
|
|
// for English because morph_conjugate("be", "future", ..., en_profile) = "will be".
|
|
let code: String = lang_get(profile, "code")
|
|
if str_eq(code, "en") {
|
|
let result: [String] = native_list_empty()
|
|
let result = native_list_append(result, base_verb)
|
|
let result = native_list_append(result, "will")
|
|
return result
|
|
}
|
|
// Other languages: conjugate normally (engine returns base form for
|
|
// languages without loaded Engram suffix data).
|
|
let surf: String = morph_conjugate(base_verb, tense, person, number, profile)
|
|
let result: [String] = native_list_empty()
|
|
let result = native_list_append(result, surf)
|
|
let result = native_list_append(result, empty_aux)
|
|
return result
|
|
}
|
|
|
|
if str_eq(aspect, "progressive") {
|
|
let gerund: String = morph_conjugate(base_verb, "progressive", person, number, profile)
|
|
let be_aux: String = morph_conjugate("be", tense, person, number, profile)
|
|
let result: [String] = native_list_empty()
|
|
let result = native_list_append(result, gerund)
|
|
let result = native_list_append(result, be_aux)
|
|
return result
|
|
}
|
|
|
|
if str_eq(aspect, "perfect") {
|
|
let pp: String = morph_conjugate(base_verb, "perfect", person, number, profile)
|
|
let have_form: String = morph_conjugate("have", tense, person, number, profile)
|
|
let result: [String] = native_list_empty()
|
|
let result = native_list_append(result, pp)
|
|
let result = native_list_append(result, have_form)
|
|
return result
|
|
}
|
|
|
|
let surf: String = morph_conjugate(base_verb, tense, person, number, profile)
|
|
let result: [String] = native_list_empty()
|
|
let result = native_list_append(result, surf)
|
|
let result = native_list_append(result, empty_aux)
|
|
return result
|
|
}
|
|
|
|
// ── Question formation ────────────────────────────────────────────────────────
|
|
//
|
|
// Strategy is resolved from gram_question_strategy(profile):
|
|
//
|
|
// "do-support" (en) - insert conjugated "do" before subject; verb stays base.
|
|
// "particle" (ja, hi, fi) - statement order + sentence-final question particle.
|
|
// "intonation" (zh, es, ar, ru, sw) - statement order + "?" punctuation only.
|
|
// "inversion" (fr, de) - subject-verb inversion.
|
|
|
|
// realize_question_lang: build the question surface string for any language.
|
|
// Returns the complete surface string (without final punctuation).
|
|
|
|
fn realize_question_lang(predicate: String, tense: String, aspect: String, person: String, number: String, agent: String, patient: String, location: String, profile: [String]) -> String {
|
|
let strategy: String = gram_question_strategy(profile)
|
|
let code: String = lang_get(profile, "code")
|
|
|
|
// ── do-support (English) ──────────────────────────────────────────────────
|
|
if str_eq(strategy, "do-support") {
|
|
if str_eq(aspect, "progressive") {
|
|
let vp_pair: [String] = realize_vp_lang(predicate, tense, "progressive", person, number, profile)
|
|
let gerund: String = native_list_get(vp_pair, 0)
|
|
let be_aux: String = native_list_get(vp_pair, 1)
|
|
let parts: [String] = native_list_empty()
|
|
let parts = native_list_append(parts, be_aux)
|
|
let parts = native_list_append(parts, agent)
|
|
let parts = native_list_append(parts, gerund)
|
|
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
|
|
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
|
|
return str_join(parts, " ")
|
|
}
|
|
|
|
if str_eq(aspect, "perfect") {
|
|
let vp_pair: [String] = realize_vp_lang(predicate, tense, "perfect", person, number, profile)
|
|
let pp: String = native_list_get(vp_pair, 0)
|
|
let have_aux: String = native_list_get(vp_pair, 1)
|
|
let parts: [String] = native_list_empty()
|
|
let parts = native_list_append(parts, have_aux)
|
|
let parts = native_list_append(parts, agent)
|
|
let parts = native_list_append(parts, pp)
|
|
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
|
|
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
|
|
return str_join(parts, " ")
|
|
}
|
|
|
|
// Simple: do-support
|
|
if str_eq(predicate, "be") {
|
|
let be_form: String = morph_conjugate("be", tense, person, number, profile)
|
|
let parts: [String] = native_list_empty()
|
|
let parts = native_list_append(parts, be_form)
|
|
let parts = native_list_append(parts, agent)
|
|
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
|
|
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
|
|
return str_join(parts, " ")
|
|
}
|
|
let do_form: String = morph_conjugate("do", tense, person, number, profile)
|
|
let parts: [String] = native_list_empty()
|
|
let parts = native_list_append(parts, do_form)
|
|
let parts = native_list_append(parts, agent)
|
|
let parts = native_list_append(parts, predicate)
|
|
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
|
|
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
|
|
return str_join(parts, " ")
|
|
}
|
|
|
|
// ── particle (ja, hi, fi) ─────────────────────────────────────────────────
|
|
// Build in statement order, then append the question particle.
|
|
if str_eq(strategy, "particle") {
|
|
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
|
|
let verb_s: String = native_list_get(vp_pair, 0)
|
|
let aux_s: String = native_list_get(vp_pair, 1)
|
|
let vp_str: String = gram_build_vp(verb_s, aux_s, profile)
|
|
let core: String = gram_order_constituents(agent, vp_str, patient, profile)
|
|
let loc_part: String = ""
|
|
if !str_eq(location, "") {
|
|
let loc_part = core + " " + location
|
|
} else {
|
|
let loc_part = core
|
|
}
|
|
// Language-specific question particles
|
|
if str_eq(code, "ja") { return loc_part + " か" }
|
|
if str_eq(code, "hi") { return loc_part + " क्या" }
|
|
if str_eq(code, "fi") { return loc_part + "-ko" }
|
|
return loc_part + "?"
|
|
}
|
|
|
|
// ── inversion (fr, de) ────────────────────────────────────────────────────
|
|
if str_eq(strategy, "inversion") {
|
|
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
|
|
let verb_s: String = native_list_get(vp_pair, 0)
|
|
let aux_s: String = native_list_get(vp_pair, 1)
|
|
// Inversion: Verb-Subject-Object order
|
|
let parts: [String] = native_list_empty()
|
|
if !str_eq(aux_s, "") {
|
|
let parts = native_list_append(parts, aux_s)
|
|
} else {
|
|
let parts = native_list_append(parts, verb_s)
|
|
}
|
|
let parts = native_list_append(parts, agent)
|
|
if !str_eq(aux_s, "") {
|
|
let parts = native_list_append(parts, verb_s)
|
|
}
|
|
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
|
|
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
|
|
return str_join(parts, " ")
|
|
}
|
|
|
|
// ── intonation (zh, es, ar, ru, sw) — statement order, "?" added by caller ─
|
|
let vp_pair: [String] = realize_vp_lang(predicate, tense, aspect, person, number, profile)
|
|
let verb_s: String = native_list_get(vp_pair, 0)
|
|
let aux_s: String = native_list_get(vp_pair, 1)
|
|
let vp_str: String = gram_build_vp(verb_s, aux_s, profile)
|
|
let core: String = gram_order_constituents(agent, vp_str, patient, profile)
|
|
if !str_eq(location, "") {
|
|
return core + " " + location
|
|
}
|
|
return core
|
|
}
|
|
|
|
// ── Capitalization and punctuation ────────────────────────────────────────────
|
|
|
|
fn capitalize_first(s: String) -> String {
|
|
let n: Int = str_len(s)
|
|
if n == 0 {
|
|
return s
|
|
}
|
|
let first: String = str_slice(s, 0, 1)
|
|
let rest: String = str_slice(s, 1, n)
|
|
return str_to_upper(first) + rest
|
|
}
|
|
|
|
fn add_punct(s: String, intent: String) -> String {
|
|
if str_eq(intent, "question") { return s + "?" }
|
|
return s + "."
|
|
}
|
|
|
|
// ── Polarity-aware negation (SACRED field honored on the generation side) ─────
|
|
//
|
|
// Negation must never be dropped between comprehension and realization. The
|
|
// meaning-spec carries an explicit "polarity" field ("aff"|"neg") and optional
|
|
// "neg_word" (standalone negative adverb, e.g. "never"). English uses
|
|
// do-support ("did not see") or preverbal adverb ("never fought"); copular "be"
|
|
// takes post-verbal "not"; other languages get a preverbal negator particle.
|
|
|
|
fn realize_negator(code: String) -> String {
|
|
if str_eq(code, "es") { return "no" }
|
|
if str_eq(code, "pt") { return "não" }
|
|
if str_eq(code, "ca") { return "no" }
|
|
if str_eq(code, "it") { return "non" }
|
|
if str_eq(code, "fr") { return "ne" }
|
|
if str_eq(code, "de") { return "nicht" }
|
|
if str_eq(code, "ro") { return "nu" }
|
|
return "not"
|
|
}
|
|
|
|
fn realize_assert_neg_en(predicate: String, tense: String, person: String, number: String, agent: String, patient: String, iobj: String, location: String, neg_word: String, profile: [String]) -> String {
|
|
let parts: [String] = native_list_empty()
|
|
let parts = native_list_append(parts, agent)
|
|
if !str_eq(neg_word, "") {
|
|
// adverbial negation: "I never fought the ocean."
|
|
let verb_surf: String = morph_conjugate(predicate, tense, person, number, profile)
|
|
let parts = native_list_append(parts, neg_word)
|
|
let parts = native_list_append(parts, verb_surf)
|
|
} else {
|
|
if str_eq(predicate, "be") {
|
|
// copular: "she was not a monster"
|
|
let be_form: String = morph_conjugate("be", tense, person, number, profile)
|
|
let parts = native_list_append(parts, be_form)
|
|
let parts = native_list_append(parts, "not")
|
|
} else {
|
|
// do-support: "she did not see the man"
|
|
let do_form: String = morph_conjugate("do", tense, person, number, profile)
|
|
let parts = native_list_append(parts, do_form)
|
|
let parts = native_list_append(parts, "not")
|
|
let parts = native_list_append(parts, predicate)
|
|
}
|
|
}
|
|
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
|
|
if !str_eq(iobj, "") {
|
|
let parts = native_list_append(parts, "to")
|
|
let parts = native_list_append(parts, iobj)
|
|
}
|
|
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
|
|
return str_join(parts, " ")
|
|
}
|
|
|
|
// ── Main realization entry point ──────────────────────────────────────────────
|
|
|
|
fn realize_lang(form: [String], profile: [String]) -> String {
|
|
let intent: String = slots_get(form, "intent")
|
|
let agent: String = slots_get(form, "agent")
|
|
let predicate: String = slots_get(form, "predicate")
|
|
let patient: String = slots_get(form, "patient")
|
|
let location: String = slots_get(form, "location")
|
|
let tense_raw: String = slots_get(form, "tense")
|
|
let aspect_raw: String= slots_get(form, "aspect")
|
|
|
|
let tense: String = tense_raw
|
|
if str_eq(tense, "") { let tense = "present" }
|
|
let aspect: String = aspect_raw
|
|
if str_eq(aspect, "") { let aspect = "simple" }
|
|
|
|
let person: String = agent_person(agent)
|
|
let number: String = agent_number(agent)
|
|
|
|
// ── Command (imperative) ──────────────────────────────────────────────────
|
|
if str_eq(intent, "command") {
|
|
let parts: [String] = native_list_empty()
|
|
let parts = native_list_append(parts, predicate)
|
|
if !str_eq(patient, "") { let parts = native_list_append(parts, patient) }
|
|
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
|
|
let sentence: String = str_join(parts, " ")
|
|
return add_punct(capitalize_first(sentence), "command")
|
|
}
|
|
|
|
// ── Question ──────────────────────────────────────────────────────────────
|
|
if str_eq(intent, "question") {
|
|
let surface: String = realize_question_lang(predicate, tense, aspect, person, number, agent, patient, location, profile)
|
|
return add_punct(capitalize_first(surface), "question")
|
|
}
|
|
|
|
// ── Assertion (declarative) ───────────────────────────────────────────────
|
|
let polarity: String = slots_get(form, "polarity")
|
|
let neg_word: String = slots_get(form, "neg_word")
|
|
let iobj: String = slots_get(form, "iobj")
|
|
let code: String = lang_get(profile, "code")
|
|
|
|
// Subordinate clause tail (SACRED completeness — the clause is carried, never
|
|
// dropped): "<conj> <subordinate surface>", e.g. "because he was a monster".
|
|
let subord_conj: String = slots_get(form, "subord_conj")
|
|
let subord_text: String = slots_get(form, "subord_text")
|
|
let subord_tail: String = ""
|
|
if !str_eq(subord_conj, "") {
|
|
if !str_eq(subord_text, "") {
|
|
let subord_tail = subord_conj + " " + subord_text
|
|
} else {
|
|
let subord_tail = subord_conj
|
|
}
|
|
}
|
|
|
|
// Negative polarity: SACRED — never dropped.
|
|
if str_eq(polarity, "neg") {
|
|
if str_eq(code, "en") {
|
|
let sentence: String = realize_assert_neg_en(predicate, tense, person, number, agent, patient, iobj, location, neg_word, profile)
|
|
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)
|
|
let vp_str: String = neg_particle + " " + gram_build_vp(verb_surf, aux_surf, profile)
|
|
let core: String = gram_order_constituents(agent, vp_str, patient, profile)
|
|
let parts: [String] = native_list_empty()
|
|
let parts = native_list_append(parts, core)
|
|
if !str_eq(iobj, "") {
|
|
let parts = native_list_append(parts, "to")
|
|
let parts = native_list_append(parts, iobj)
|
|
}
|
|
if !str_eq(location, "") { let parts = native_list_append(parts, location) }
|
|
if !str_eq(subord_tail, "") { let parts = native_list_append(parts, subord_tail) }
|
|
let sentence: String = str_join(parts, " ")
|
|
return add_punct(capitalize_first(sentence), "assert")
|
|
}
|
|
|
|
// Affirmative.
|
|
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)
|
|
|
|
let vp_str: String = gram_build_vp(verb_surf, aux_surf, profile)
|
|
let core: String = gram_order_constituents(agent, vp_str, patient, profile)
|
|
|
|
let parts: [String] = native_list_empty()
|
|
let parts = native_list_append(parts, core)
|
|
if !str_eq(iobj, "") {
|
|
let parts = native_list_append(parts, "to")
|
|
let parts = native_list_append(parts, iobj)
|
|
}
|
|
if !str_eq(location, "") {
|
|
let parts = native_list_append(parts, location)
|
|
}
|
|
if !str_eq(subord_tail, "") {
|
|
let parts = native_list_append(parts, subord_tail)
|
|
}
|
|
let sentence: String = str_join(parts, " ")
|
|
return add_punct(capitalize_first(sentence), "assert")
|
|
}
|
|
|
|
// realize: backward-compatible English entry point (original signature).
|
|
fn realize(form: [String]) -> String {
|
|
let lang_code: String = slots_get(form, "lang")
|
|
if str_eq(lang_code, "") {
|
|
return realize_lang(form, lang_default())
|
|
}
|
|
return realize_lang(form, lang_from_code(lang_code))
|
|
}
|