tawasuyu

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…

⎇ main crates/akllay/tests/divergencias_medidas.rs Rust · 106 lines · 4.6 KB History Who touched what raw

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

1//! Las divergencias que el banco diferencial destapó contra `busybox awk` y `gawk --posix`.
2//!
3//! Cada una estuvo ROTA y daba un resultado plausible: ninguna reventaba, ninguna imprimía un
4//! error. Por eso están acá y no sólo en el banco — el banco compara contra oráculos que hay que
5//! tener en el store, y esto corre con `cargo test` en cualquier sitio.
6
7use std::process::{Command, Stdio};
8use std::io::Write;
9
10fn awk(prog: &str, entrada: &str) -> String {
11    let exe = env!("CARGO_BIN_EXE_akllay");
12    let mut h = Command::new(exe).arg(prog)
13        .stdin(Stdio::piped()).stdout(Stdio::piped()).stderr(Stdio::piped())
14        .spawn().expect("no pude lanzar akllay");
15    h.stdin.as_mut().unwrap().write_all(entrada.as_bytes()).unwrap();
16    let s = h.wait_with_output().unwrap();
17    String::from_utf8_lossy(&s.stdout).trim_end_matches('\n').to_string()
18}
19
20/// Leer NF tiene que forzar el partido en campos. Daba 0 hasta que alguien tocara un campo, porque
21/// el partido es perezoso — y eso es lo que hace barato asignar a `$0`.
22#[test]
23fn nf_se_lee_sin_tocar_un_campo() {
24    assert_eq!(awk("{print NF}", "   a    b   \n"), "2");
25    assert_eq!(awk("{print NF, $NF}", "a b c\n"), "3 c");
26}
27
28/// `length` sin paréntesis es el único builtin al que POSIX se lo permite, y se leía como una
29/// variable llamada `length`, que siempre vale vacío.
30#[test]
31fn length_sin_parentesis() {
32    assert_eq!(awk("{print length}", "abcd\n"), "4");
33    assert_eq!(awk("BEGIN{print length(\"hola\")}", ""), "4");
34}
35
36/// Un array se pasa POR REFERENCIA. Se decide mirando si la función usa el parámetro como array, y
37/// `a["k"] = 1` tiene el nombre en el LUGAR de la asignación, no en el valor.
38#[test]
39fn los_arrays_se_pasan_por_referencia() {
40    assert_eq!(awk(r#"function g(a){a["k"]="puesto"} BEGIN{g(A); print A["k"]}"#, ""), "puesto");
41    // Y los escalares siguen yendo por valor.
42    assert_eq!(awk("function f(x){x=99} BEGIN{y=1; f(y); print y}", ""), "1");
43}
44
45/// `substr` con inicio ≤ 0: se recorta a 1 y **el largo no se ajusta**. La lectura estricta de
46/// POSIX daría `a`; ni busybox ni gawk hacen eso, y lo que hay que ejecutar son los guiones
47/// escritos contra ellos.
48#[test]
49fn substr_con_indices_fuera_de_rango() {
50    assert_eq!(awk(r#"BEGIN{print substr("abcdef", 0, 3)}"#, ""), "abc");
51    assert_eq!(awk(r#"BEGIN{print substr("abcdef", -1, 3)}"#, ""), "abc");
52    assert_eq!(awk(r#"BEGIN{print "["substr("abc", 10)"]"}"#, ""), "[]");
53    // Los no enteros se truncan hacia cero, no se redondean.
54    assert_eq!(awk(r#"BEGIN{print substr("abcdef", 1.5, 2.5)}"#, ""), "ab");
55}
56
57/// Una coincidencia VACÍA pegada al final de la anterior no sustituye. Es la única diferencia
58/// medida entre gawk y el awk de busybox en todo el banco, y gawk es quien sigue a POSIX.
59#[test]
60fn gsub_no_sustituye_el_vacio_pegado_a_la_coincidencia_anterior() {
61    assert_eq!(awk(r#"BEGIN{s="xax"; n=gsub(/a*/,"-",s); print n, s}"#, ""), "3 -x-x-");
62    assert_eq!(awk(r#"BEGIN{s="abc"; n=gsub(/x*/,"-",s); print n, s}"#, ""), "4 -a-b-c-");
63}
64
65/// El campo que viene de la entrada se compara como número si lo parece, y como cadena si no.
66#[test]
67fn strnum() {
68    assert_eq!(awk("{print ($1 > $2)}", "10 9\n"), "1");
69    assert_eq!(awk(r#"BEGIN{print ("10" > "9")}"#, ""), "0");
70    assert_eq!(awk("{print $1}", "0012\n"), "0012");
71    assert_eq!(awk("{print $1+0}", "0012\n"), "12");
72}
73
74/// `^` asocia a la DERECHA. El awk de busybox lo hace al revés y da 64.
75#[test]
76fn la_potencia_asocia_a_la_derecha() {
77    assert_eq!(awk("BEGIN{print 2^3^2}", ""), "512");
78}
79
80/// La ERE es leftmost-LONGEST, no leftmost-first: para `a|ab` sobre `"ab"` gana `ab`.
81#[test]
82fn la_regex_es_leftmost_longest() {
83    assert_eq!(awk(r#"BEGIN{print match("ab", /a|ab/), RLENGTH}"#, ""), "1 2");
84    assert_eq!(awk(r#"BEGIN{s="ab"; sub(/a|ab/, "X", s); print s}"#, ""), "X");
85}
86
87/// Un `exit` en el cuerpo ejecuta los END; uno dentro de END, no.
88#[test]
89fn exit_ejecuta_los_end() {
90    assert_eq!(awk(r#"BEGIN{exit 0} END{print "end corre"}"#, ""), "end corre");
91}
92
93/// El `%c` de printf mira el TIPO del argumento.
94#[test]
95fn printf_c_mira_el_tipo() {
96    assert_eq!(awk(r#"BEGIN{printf "%c|%c\n", 65, "65"}"#, ""), "A|6");
97}
98
99/// Los campos se rehacen con OFS al tocar cualquiera, y `$0` se reparte al asignarlo.
100#[test]
101fn campos_y_registro_son_dos_caras_del_mismo_dato() {
102    assert_eq!(awk(r#"BEGIN{OFS="-"} {$1=$1; print}"#, "a b c\n"), "a-b-c");
103    assert_eq!(awk(r#"{$5="X"; print; print NF}"#, "a b\n"), "a b   X\n5");
104    assert_eq!(awk(r#"{$0="p q r"; print $2, NF}"#, "a b\n"), "q 3");
105    assert_eq!(awk(r#"{NF=2; print}"#, "a b c d\n"), "a b");
106}