# -*- coding: utf-8 -*- """gen_parity_es.py — emit an El parity program that checks the .el Spanish morphology against the validated Python (UniMorph-backed) realizer. Python is the ORACLE. For every lemma in the held-out inventory we embed the Python-produced form, call the corresponding .el function, and the El program prints PASS/FAIL per category. Aggregation is done in bash (grep -c), so no El-side mutable counters are needed. Categories: Vpres verb present-ind-3sg es_conjugate(v,present,third,singular) Vpret verb preterite-3sg es_conjugate(v,past,third,singular) Vpart past participle es_participle(v) Npl noun plural es_pluralize(n) Gheur noun gender HEURISTIC es_gender(n) [exposes the bug] Aheur def article via heuristic es_agree_article(n,true,sg) [inherits the bug] Avocab def article via REAL g es_article_for_gender(realg,...) [the fix] Ampl adj masc-plural es_inflect_adj(a,m,plural) Afsg adj fem-singular es_inflect_adj(a,f,singular) Ctr contraction del/al es_contract(prep, np) """ import morphology_es_full as M import realizer_es as R from gen_elp_es import _collect_from_specs, _CORE_VERBS, _CORE_NOUNS, _CORE_ADJS def _esc(s): return str(s).replace('\\', '\\\\').replace('"', '\\"') def _check(cat, lemma, el_call, expected): return (f' es_check("{cat}", "{_esc(lemma)}", {el_call}, "{_esc(expected)}")') def main(out_path): v_specs, n_specs, a_specs = _collect_from_specs() verbs = sorted(set(_CORE_VERBS) | v_specs) nouns = sorted(set(_CORE_NOUNS) | n_specs) adjs = sorted(set(_CORE_ADJS) | a_specs) lines = [] lines.append("// gen'd parity checks — Python oracle embedded, El functions called.") lines.append("fn es_check(cat: String, lemma: String, got: String, exp: String) {") lines.append(' if str_eq(got, exp) {') lines.append(' println("PASS " + cat)') lines.append(' } else {') lines.append(' println("FAIL " + cat + " " + lemma + " got=" + got + " exp=" + exp)') lines.append(' }') lines.append("}") lines.append("") lines.append("fn es_parity() {") # verbs for v in verbs: p0 = M.conjugate(v, "ind", "present", "third", "singular")[0] p1 = M.conjugate(v, "ind", "preterite", "third", "singular")[0] pp = M.participle(v)[0] lines.append(_check("Vpres", v, f'es_conjugate("{_esc(v)}", "present", "third", "singular")', p0)) lines.append(_check("Vpret", v, f'es_conjugate("{_esc(v)}", "past", "third", "singular")', p1)) lines.append(_check("Vpart", v, f'es_participle("{_esc(v)}")', pp)) # nouns for n in nouns: pl = M.inflect_noun(n, "plural")[0] g = M.noun_gender(n) # REAL lexicon gender art = R._article(g, "singular", "def", n) # oracle article from real gender lines.append(_check("Npl", n, f'es_pluralize("{_esc(n)}")', pl)) lines.append(_check("Gheur", n, f'es_gender("{_esc(n)}")', g)) lines.append(_check("Aheur", n, f'es_agree_article("{_esc(n)}", "true", "singular")', art)) lines.append(_check("Avocab", n, f'es_article_for_gender("{_esc(g)}", "{_esc(n)}", "true", "singular")', art)) # adjectives for a in adjs: mpl = M.inflect_adj(a, "m", "plural")[0] fsg = M.inflect_adj(a, "f", "singular")[0] lines.append(_check("Ampl", a, f'es_inflect_adj("{_esc(a)}", "m", "plural")', mpl)) lines.append(_check("Afsg", a, f'es_inflect_adj("{_esc(a)}", "f", "singular")', fsg)) # contractions (mandatory) ctr_cases = [("de", "el día"), ("a", "el hombre"), ("de", "el mundo"), ("a", "el país"), ("en", "el mar"), ("de", "el año"), ("a", "la casa"), ("de", "la ciudad")] for prep, np in ctr_cases: exp = R._contract(prep, np) lines.append(_check("Ctr", prep + "+" + np, f'es_contract("{_esc(prep)}", "{_esc(np)}")', exp)) lines.append("}") lines.append("") lines.append("fn main() {") lines.append(" es_parity()") lines.append("}") with open(out_path, "w", encoding="utf-8") as fh: fh.write("\n".join(lines) + "\n") print(f"wrote {out_path} ({len(verbs)} verbs, {len(nouns)} nouns, {len(adjs)} adjs)") if __name__ == "__main__": import sys main(sys.argv[1] if len(sys.argv) > 1 else "parity_es_checks.el")