gioser

sergio/takana

> Una distribución Linux construida con un laboratorio funcional y hermético en el > sótano, y una terminal mutable, clásica y anárquica en el piso de arriba — diseñada > desde el suelo para que una IA programadora entienda, modifique y co…

TOML 1389Shell 137Markdown 80Rust 802602 Sutichaykuna

⎇ 7a0c1b7eae74e07b0b701ca8f674fc75884c6c3c scripts/awk-banco-diferencial.py Python · 221 siq'ikuna · 15.1 KB Ñawpa willay Pi imata llamkarqan ch'uya

el awk se llama akllay: `qillqa` estaba TOMADO en tawasuyu, y no lo comprobé 83720b303 · Sergio ·

1#!/usr/bin/env python3
2"""Banco DIFERENCIAL de awk: corre los mismos casos en varios motores y compara.
3
4    scripts/awk-banco-diferencial.py                      # akllay contra busybox awk y gawk --posix
5    scripts/awk-banco-diferencial.py --motor ruta/al/awk  # añade otro candidato
6
7Por qué diferencial y no de salidas esperadas escritas a mano: un banco escrito a mano mide lo que
8al autor se le ocurrió preguntar, y ahí es donde se cuela el sesgo — es la lección del §paso 3 de
9`docs/plan-botar-busybox.md`, donde brush pasó 56/57 casos POSIX y no construía una sola receta.
10Contra un oráculo que ya está en producción, cada divergencia es un hecho, no una opinión.
11
12⚠ Los oráculos NO coinciden entre sí en todo: busybox awk y gawk difieren en `substr` con índices
13raros, en `%c` y en el formato de algunos números. Por eso el veredicto no es «igual a X» sino
14«igual a LOS DOS», y cuando los dos difieren entre ellos el caso se marca `discrepan` y no cuenta
15como fallo de nadie — se mira a mano y se decide siguiendo POSIX.
16"""
17import argparse, json, os, subprocess, sys
18
19RAIZ = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
20
21
22def del_store(receta, rel):
23    est = json.load(open(os.path.join(RAIZ, "docs/state/build-state.json")))["nodes"]
24    if receta not in est:
25        return None
26    p = "/store/" + est[receta]["hash"].split(":")[1] + "-" + receta + "/" + rel
27    return p if os.path.exists(p) else None
28
29
30# (nombre, programa, entrada)
31CASOS = [
32    # ── campos, separadores, registros ──────────────────────────────────────────────────────────
33    ("campos-simple",        '{print $2}',                         "a b c\nd e f\n"),
34    ("campos-nf",            '{print NF, $NF}',                    "a b c\nx\n"),
35    ("campos-vacio",         '{print "["$5"]"}',                   "a b\n"),
36    ("campos-cero",          '{print $0}',                         "  hola   mundo  \n"),
37    ("fs-espacios-rachas",   '{print NF}',                         "   a    b   \n"),
38    ("fs-coma",              '-F, {print $2}',                     "a,b,c\n"),
39    ("fs-tab",               '-F\t {print $2}',                    "a\tb\tc\n"),
40    ("fs-ere",               '-F[0-9]+ {print $2}',                "ab123cd\n"),
41    ("fs-punto-literal",     '-F. {print NF}',                     "a.b.c\n"),
42    ("asignar-campo",        '{$2="X"; print}',                    "a b c\n"),
43    ("asignar-campo-lejos",  '{$5="X"; print; print NF}',          "a b\n"),
44    ("asignar-cero",         '{$0="p q r"; print $2, NF}',         "a b\n"),
45    ("nf-recorta",           '{NF=2; print; print NF}',            "a b c d\n"),
46    ("ofs-al-rehacer",       'BEGIN{OFS="-"} {$1=$1; print}',      "a b c\n"),
47    ("registros-nr-fnr",     '{print NR, FNR}',                    "x\ny\n"),
48    # ── valores: la parte que más se equivoca ───────────────────────────────────────────────────
49    ("strnum-numerico",      '{print ($1 > $2)}',                  "10 9\n"),
50    ("cadena-alfabetica",    'BEGIN{print ("10" > "9")}',          ""),
51    ("strnum-cero-falso",    '{if ($1) print "si"; else print "no"}', "0\n"),
52    ("cadena-cero-verdad",   'BEGIN{ if ("0") print "si"; else print "no"}', ""),
53    ("texto-se-conserva",    '{print $1}',                         "0012\n"),
54    ("suma-con-basura",      '{print $1+0}',                       "12x\n"),
55    ("entero-sin-decimales", 'BEGIN{print 2/1, 1/3}',              ""),
56    ("convfmt",              'BEGIN{CONVFMT="%.2g"; x=1/3; print x ""}', ""),
57    ("ofmt",                 'BEGIN{OFMT="%.2f"; print 1/3}',      ""),
58    ("uninit-es-cero-y-vacio", 'BEGIN{print x+0, "["x"]", (x==0), (x=="")}', ""),
59    # ── aritmética y operadores ─────────────────────────────────────────────────────────────────
60    ("potencia-derecha",     'BEGIN{print 2^3^2}',                 ""),
61    ("modulo-real",          'BEGIN{print 5.5 % 2}',               ""),
62    ("unario-vs-concat",     'BEGIN{a=1;b=2;print a-b; print a" "-b}', ""),
63    ("incremento",           'BEGIN{i=5; print i++, i, ++i, i}',   ""),
64    ("asigna-op",            'BEGIN{x=10; x-=3; x*=2; print x}',   ""),
65    ("ternario",             'BEGIN{print (1?"a":"b"), (0?"a":"b")}', ""),
66    ("comparacion-encadena", 'BEGIN{print (1<2), (2<=2), (3!=3)}', ""),
67    ("concatenacion",        'BEGIN{print "a" 1+1 "b"}',           ""),
68    # ── expresiones regulares ───────────────────────────────────────────────────────────────────
69    ("ere-patron",           '/b/{print "hay"}',                   "abc\nxyz\n"),
70    ("ere-tilde",            '{print ($0 ~ /^a/), ($0 !~ /^a/)}',  "abc\nxbc\n"),
71    ("ere-alternancia-larga", 'BEGIN{print match("ab", /a|ab/), RLENGTH}', ""),
72    ("ere-clase",            'BEGIN{print match("foo123", /[[:digit:]]+/), RSTART, RLENGTH}', ""),
73    ("ere-intervalo",        'BEGIN{print match("aaaa", /a{2,3}/), RLENGTH}', ""),
74    ("ere-anclas",           'BEGIN{print ("abc" ~ /^abc$/), ("abc" ~ /^b/)}', ""),
75    ("ere-punto",            'BEGIN{print ("a.c" ~ /a\\.c/), ("abc" ~ /a\\.c/)}', ""),
76    ("ere-estrella-vacia",   'BEGIN{print match("bbb", /a*/), RLENGTH}', ""),
77    ("ere-clase-negada",     'BEGIN{print match("abc1", /[^a-z]/), RSTART}', ""),
78    # ── funciones de cadena ─────────────────────────────────────────────────────────────────────
79    ("substr-normal",        'BEGIN{print substr("abcdef", 2, 3)}', ""),
80    ("substr-sin-largo",     'BEGIN{print substr("abcdef", 3)}',   ""),
81    ("substr-inicio-cero",   'BEGIN{print "["substr("abcdef", 0, 3)"]"}', ""),
82    ("substr-negativo",      'BEGIN{print "["substr("abcdef", -1, 3)"]"}', ""),
83    ("substr-pasado",        'BEGIN{print "["substr("abc", 10)"]"}', ""),
84    ("substr-redondeo",      'BEGIN{print substr("abcdef", 1.5, 2.5)}', ""),
85    ("index",                'BEGIN{print index("abcabc","bc"), index("abc","z"), index("abc","")}', ""),
86    ("length",               'BEGIN{print length("hola"), length()}', ""),
87    ("length-campo",         '{print length}',                     "abcd\n"),
88    ("toupper-tolower",      'BEGIN{print toupper("aBc"), tolower("AbC")}', ""),
89    ("sub-simple",           'BEGIN{s="aaa"; n=sub(/a/,"b",s); print n, s}', ""),
90    ("gsub-simple",          'BEGIN{s="aaa"; n=gsub(/a/,"b",s); print n, s}', ""),
91    ("gsub-ampersand",       'BEGIN{s="abc"; gsub(/b/,"[&]",s); print s}', ""),
92    ("gsub-ampersand-escapado", 'BEGIN{s="abc"; gsub(/b/,"[\\\\&]",s); print s}', ""),
93    ("gsub-vacio",           'BEGIN{s="xax"; n=gsub(/a*/,"-",s); print n, s}', ""),
94    ("gsub-en-campo",        '{gsub(/o/,"0"); print}',             "foo boo\n"),
95    ("split-fs",             'BEGIN{n=split("a:b:c",A,":"); print n, A[1], A[3]}', ""),
96    ("split-ere",            'BEGIN{n=split("a1b22c",A,/[0-9]+/); print n, A[2], A[3]}', ""),
97    ("split-vacio",          'BEGIN{n=split("",A,":"); print n}',  ""),
98    ("split-default",        'BEGIN{n=split("  a  b ",A); print n, A[1], A[2]}', ""),
99    ("sprintf",              'BEGIN{print sprintf("%03d-%s", 7, "x")}', ""),
100    ("match-sin-exito",      'BEGIN{print match("abc",/z/), RSTART, RLENGTH}', ""),
101    # ── printf ──────────────────────────────────────────────────────────────────────────────────
102    ("printf-enteros",       'BEGIN{printf "%d|%5d|%-5d|%05d\\n", 42, 42, 42, 42}', ""),
103    ("printf-negativo",      'BEGIN{printf "%05d|%+d|% d\\n", -1, 7, 7}', ""),
104    ("printf-flotante",      'BEGIN{printf "%.2f|%e|%g\\n", 3.14159, 150, 0.0000123}', ""),
105    ("printf-cadena",        'BEGIN{printf "%s|%10s|%-10s|%.2s|\\n", "ab","ab","ab","abcd"}', ""),
106    ("printf-caracter-num",  'BEGIN{printf "%c\\n", 65}',          ""),
107    ("printf-hex",           'BEGIN{printf "%x|%X|%o\\n", 255, 255, 8}', ""),
108    ("printf-asterisco",     'BEGIN{printf "%*d|%.*f\\n", 5, 42, 2, 3.14159}', ""),
109    ("printf-porciento",     'BEGIN{printf "100%%\\n"}',           ""),
110    ("printf-trunca",        'BEGIN{printf "%d\\n", 3.99}',        ""),
111    # ── control de flujo ────────────────────────────────────────────────────────────────────────
112    ("if-else",              'BEGIN{ if (1) print "a"; else print "b" }', ""),
113    ("while",                'BEGIN{i=0; while(i<3){printf "%d", i; i++}; print ""}', ""),
114    ("do-while",             'BEGIN{i=0; do {printf "%d", i; i++} while(i<3); print ""}', ""),
115    ("for-clasico",          'BEGIN{for(i=0;i<3;i++) printf "%d", i; print ""}', ""),
116    ("for-in",               'BEGIN{a[1];a[2];a[3]; n=0; for(k in a) n++; print n}', ""),
117    ("break-continue",       'BEGIN{for(i=0;i<5;i++){if(i==1)continue; if(i==3)break; printf "%d",i}; print ""}', ""),
118    ("next",                 '/skip/{next} {print}',               "uno\nskip esto\ndos\n"),
119    ("exit-con-end",         'BEGIN{exit 0} END{print "end corre"}', ""),
120    ("patron-rango",         '/ini/,/fin/{print}',                 "a\nini\nx\nfin\nb\n"),
121    ("patron-expresion",     'NR==2{print}',                       "a\nb\nc\n"),
122    ("patron-sin-accion",    'NR==2',                              "a\nb\nc\n"),
123    # ── arrays ──────────────────────────────────────────────────────────────────────────────────
124    ("array-subsep",         'BEGIN{a[1,2]="x"; for(k in a){split(k,p,SUBSEP); print p[1],p[2]}}', ""),
125    ("array-in-crea",        'BEGIN{ if (a["k"]=="") print "vacio"; print ("k" in a) }', ""),
126    ("array-delete",         'BEGIN{a[1]=1;a[2]=2; delete a[1]; n=0; for(k in a)n++; print n}', ""),
127    ("array-delete-todo",    'BEGIN{a[1]=1;a[2]=2; delete a; n=0; for(k in a)n++; print n}', ""),
128    ("array-length",         'BEGIN{a[1];a[2]; print length(a)}',  ""),
129    # ── funciones de usuario ────────────────────────────────────────────────────────────────────
130    ("funcion-simple",       'function f(x){return x*2} BEGIN{print f(21)}', ""),
131    ("funcion-recursiva",    'function f(n){return n<=1?1:n*f(n-1)} BEGIN{print f(6)}', ""),
132    ("funcion-array-ref",    'function g(a){a["k"]="puesto"} BEGIN{g(A); print A["k"]}', ""),
133    ("funcion-local",        'function f(x,  t){t=x*2; return t} BEGIN{t=9; print f(2), t}', ""),
134    ("funcion-sin-return",   'function f(){} BEGIN{print "["f()"]"}', ""),
135    # ── entrada/salida ──────────────────────────────────────────────────────────────────────────
136    ("print-redirigido",     'BEGIN{print "x" > "/dev/stderr"}',   ""),
137    ("print-coma-ofs",       'BEGIN{OFS=":"; print "a","b"}',      ""),
138    ("print-ors",            'BEGIN{ORS="|"} {print}',             "a\nb\n"),
139    ("print-parentesis",     'BEGIN{print (1), (2)}',              ""),
140    ("getline-tuberia",      'BEGIN{ "echo hola" | getline x; print x }', ""),
141    ("getline-simple",       'NR==1{ getline; print "leido:" $0 }', "a\nb\nc\n"),
142    ("rs-parrafo",           'BEGIN{RS=""} {print NR": "$1"-"$2}', "a\nb\n\nc\nd\n"),
143    ("rs-caracter",          'BEGIN{RS=":"} {print NR, $0}',       "a:b:c"),
144    # ── rincones del parser ─────────────────────────────────────────────────────────────────────
145    ("barra-division",       'BEGIN{a=6; b=3; print a / b}',       ""),
146    ("print-mayor-redirige", 'BEGIN{print (1>2)}',                 ""),
147    ("comentario",           'BEGIN{ # esto es comentario\nprint "ok"}', ""),
148    ("punto-y-coma",         'BEGIN{;;print "ok";;}',              ""),
149    ("continuacion-linea",   'BEGIN{print "a" \\\n "b"}',          ""),
150    ("cadena-escapes",       'BEGIN{print "a\\tb\\\\c"}',          ""),
151    ("expresion-regex-valor", '{print /b/}',                       "abc\nxyz\n"),
152]
153
154
155def corre(motor, caso, entrada):
156    """Devuelve (salida+error, código)."""
157    args = list(motor)
158    prog = caso
159    # Los casos que empiezan por `-F` traen la opción pegada al programa.
160    if prog.startswith("-F"):
161        i = prog.index(" ")
162        args += [prog[:i]]
163        prog = prog[i + 1:]
164    args.append(prog)
165    try:
166        r = subprocess.run(args, input=entrada, capture_output=True, text=True, timeout=10)
167        return (r.stdout + r.stderr).rstrip("\n"), r.returncode
168    except subprocess.TimeoutExpired:
169        return "@TIMEOUT", -1
170    except Exception as e:
171        return f"@ERROR {e}", -1
172
173
174def main():
175    ap = argparse.ArgumentParser()
176    ap.add_argument("--motor", default=os.path.join(RAIZ, "target/release/akllay"))
177    ap.add_argument("--verbose", action="store_true")
178    a = ap.parse_args()
179
180    bb = del_store("busybox", "usr/bin/awk")
181    gawk = del_store("gawk", "usr/bin/gawk")
182    oraculos = []
183    if bb: oraculos.append(("busybox", [bb]))
184    if gawk: oraculos.append(("gawk", [gawk, "--posix"]))
185    if not oraculos:
186        print("sin oráculos en el store: no se puede comparar", file=sys.stderr)
187        return 2
188
189    print(f"motor: {a.motor}")
190    print(f"oráculos: {', '.join(n for n, _ in oraculos)}\n")
191    igual = discrepan = distinto = 0
192    fallos = []
193    for nombre, prog, entrada in CASOS:
194        mio, _ = corre([a.motor], prog, entrada)
195        refs = [(n, corre(cmd, prog, entrada)[0]) for n, cmd in oraculos]
196        valores = {r for _, r in refs}
197        if len(valores) > 1:
198            discrepan += 1
199            if a.verbose:
200                print(f"  ~ {nombre}: los oráculos discrepan " +
201                      " · ".join(f"{n}=[{r}]" for n, r in refs) + f" · akllay=[{mio}]")
202            continue
203        esperado = refs[0][1]
204        if mio == esperado:
205            igual += 1
206        else:
207            distinto += 1
208            fallos.append((nombre, prog, entrada, esperado, mio))
209
210    print(f"  iguales a los oráculos : {igual}")
211    print(f"  DIVERGEN               : {distinto}")
212    print(f"  los oráculos discrepan : {discrepan}  (no cuentan: se deciden por POSIX a mano)")
213    if fallos:
214        print("\n── divergencias ──")
215        for n, p, e, esp, mio in fallos:
216            print(f"\n  ✗ {n}\n      programa: {p}\n      entrada : {e!r}\n      oráculos: [{esp}]\n      akllay  : [{mio}]")
217    return 1 if fallos else 0
218
219
220if __name__ == "__main__":
221    sys.exit(main())