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 188Rust 55Shell 30Markdown 281211 Definiciones

⎇ ae6f0b45d94eb275e2464d898b35ee9a64595f43 crates/qillqa/src/interp.rs Rust · 1306 líneas · 58.1 KB Historia Quién tocó qué crudo

qillqa: el awk propio — 3531 líneas, sólo libc, y CERO divergencias contra dos oráculos ae6f0b45d · Sergio ·

1//! El intérprete: recorre el árbol y ejecuta.
2//!
3//! Tres piezas merecen leerse antes de tocar nada:
4//!
5//! * **Los campos y `$0` son un mismo dato con dos caras.** Escribir `$2` reconstruye `$0` uniendo
6//!   con OFS; escribir `$0` vuelve a partir en campos con FS; y asignar a `$5` cuando sólo hay tres
7//!   campos CREA los que faltan vacíos y sube NF. Todo eso vive en `partir` / `rehacer_registro`.
8//! * **El separador FS tiene cuatro comportamientos**, no uno: `" "` (el de por defecto) parte por
9//!   rachas de espacios/tabuladores/saltos y descarta los de los extremos; un carácter suelto parte
10//!   por ese carácter exacto; `"\t"` es ese caso; y cualquier cosa de más de un carácter es una ERE.
11//! * **Los arrays se pasan POR REFERENCIA a las funciones y los escalares por valor**, y eso no se
12//!   declara: se descubre al usar el parámetro. Por eso un parámetro puede empezar indefinido y
13//!   convertirse en array dentro de la llamada.
14
15use crate::ast::*;
16use crate::formato::{self, Arg};
17use crate::regex::Regex;
18use crate::valor::{self, Valor};
19use std::cell::RefCell;
20use std::collections::HashMap;
21use std::io::{BufRead, BufReader, Read, Write};
22use std::rc::Rc;
23
24/// Lo que puede haber en una variable: un escalar o un array. No hay un tercer caso, y confundirlos
25/// es un error en awk (`x[1]=1; x=2` es ilegal), pero no lo comprobamos: los awk reales tampoco lo
26/// hacen en todos los caminos y romper eso sólo rompe guiones que ya funcionaban.
27#[derive(Clone)]
28pub enum Celda {
29    Escalar(Valor),
30    Array(Rc<RefCell<HashMap<String, Valor>>>),
31}
32
33/// Por qué se interrumpió la ejecución de un bloque.
34pub enum Flujo {
35    Normal,
36    Corta,
37    Sigue,
38    Siguiente,
39    SiguienteFichero,
40    Retorna(Valor),
41    Sale(i32),
42}
43
44macro_rules! propaga {
45    ($e:expr) => {
46        match $e { Flujo::Normal => {}, otro => return Ok(otro) }
47    };
48}
49
50pub struct Salida {
51    escritor: Box<dyn Write>,
52    /// Si es una tubería, hay un hijo al que esperar en `close`.
53    hijo: Option<std::process::Child>,
54}
55
56pub struct Entrada {
57    lector: Box<dyn BufRead>,
58    hijo: Option<std::process::Child>,
59}
60
61pub struct Interprete {
62    pub prog: Programa,
63    globales: HashMap<String, Celda>,
64    /// Marcos de las funciones. El último es el actual.
65    marcos: Vec<HashMap<String, Celda>>,
66    campos: Vec<String>,
67    registro: String,
68    /// `$0` ya no refleja los campos (alguien escribió `$2`) y hay que rehacerlo antes de leerlo.
69    sucio_registro: bool,
70    /// Los campos ya no reflejan `$0`.
71    sucio_campos: bool,
72    rangos_activos: Vec<bool>,
73    salidas: HashMap<String, Salida>,
74    entradas: HashMap<String, Entrada>,
75    ere_cache: HashMap<String, Rc<Regex>>,
76    pub codigo_salida: i32,
77    semilla: u64,
78    semilla_previa: f64,
79    /// Ficheros de la línea de órdenes pendientes, y el que se está leyendo.
80    lector_actual: Option<Box<dyn BufRead>>,
81    indice_argv: usize,
82    pub stdout: Box<dyn Write>,
83}
84
85fn ahora_ms() -> u64 {
86    std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).map(|d| d.as_millis() as u64).unwrap_or(0)
87}
88
89impl Interprete {
90    pub fn nuevo(prog: Programa) -> Interprete {
91        let rangos = prog.rangos;
92        let mut it = Interprete {
93            prog,
94            globales: HashMap::new(),
95            marcos: Vec::new(),
96            campos: Vec::new(),
97            registro: String::new(),
98            sucio_registro: false,
99            sucio_campos: false,
100            rangos_activos: vec![false; rangos],
101            salidas: HashMap::new(),
102            entradas: HashMap::new(),
103            ere_cache: HashMap::new(),
104            codigo_salida: 0,
105            semilla: 0,
106            semilla_previa: 0.0,
107            lector_actual: None,
108            indice_argv: 1,
109            stdout: Box::new(std::io::BufWriter::new(std::io::stdout())),
110        };
111        for (k, v) in [("FS", " "), ("OFS", " "), ("ORS", "\n"), ("RS", "\n"),
112                       ("SUBSEP", "\u{1c}"), ("CONVFMT", "%.6g"), ("OFMT", "%.6g"), ("FILENAME", "")] {
113            it.globales.insert(k.into(), Celda::Escalar(Valor::Cad(v.into())));
114        }
115        for k in ["NR", "NF", "FNR", "RSTART", "RLENGTH"] {
116            let v = if k == "RLENGTH" { -1.0 } else { 0.0 };
117            it.globales.insert(k.into(), Celda::Escalar(Valor::Num(v)));
118        }
119        it.semilla = 0;
120        it
121    }
122
123    // ── Variables ────────────────────────────────────────────────────────────────────────────────
124
125    fn celda(&self, n: &str) -> Option<&Celda> {
126        if let Some(m) = self.marcos.last() {
127            if let Some(c) = m.get(n) { return Some(c); }
128        }
129        self.globales.get(n)
130    }
131
132    fn en_marco(&self, n: &str) -> bool {
133        self.marcos.last().map_or(false, |m| m.contains_key(n))
134    }
135
136    pub fn leer_var(&self, n: &str) -> Valor {
137        match self.celda(n) {
138            Some(Celda::Escalar(v)) => v.clone(),
139            Some(Celda::Array(_)) => Valor::Vacio,
140            None => Valor::Vacio,
141        }
142    }
143
144    pub fn poner_var(&mut self, n: &str, v: Valor) {
145        if self.en_marco(n) {
146            self.marcos.last_mut().unwrap().insert(n.into(), Celda::Escalar(v));
147            return;
148        }
149        // Escribir NF recorta o extiende el registro; escribir los demás no tiene efecto colateral.
150        if n == "NF" {
151            let nuevo = v.numero().max(0.0) as usize;
152            self.asegurar_campos();
153            self.campos.resize(nuevo, String::new());
154            self.globales.insert("NF".into(), Celda::Escalar(Valor::Num(nuevo as f64)));
155            self.sucio_registro = true;
156            return;
157        }
158        self.globales.insert(n.into(), Celda::Escalar(v));
159    }
160
161    fn cadena_de(&self, v: &Valor) -> String { v.cadena(&self.convfmt()) }
162    fn convfmt(&self) -> String { self.leer_var("CONVFMT").cadena("%.6g") }
163    fn ofmt(&self) -> String { self.leer_var("OFMT").cadena("%.6g") }
164
165    fn array(&mut self, n: &str) -> Rc<RefCell<HashMap<String, Valor>>> {
166        if let Some(Celda::Array(a)) = self.celda(n) { return a.clone(); }
167        let a = Rc::new(RefCell::new(HashMap::new()));
168        if self.en_marco(n) {
169            self.marcos.last_mut().unwrap().insert(n.into(), Celda::Array(a.clone()));
170        } else {
171            self.globales.insert(n.into(), Celda::Array(a.clone()));
172        }
173        a
174    }
175
176    // ── Campos ───────────────────────────────────────────────────────────────────────────────────
177
178    /// ⚠ Los cuatro comportamientos de FS.
179    fn partir(&mut self, linea: &str) -> Vec<String> {
180        let fs = self.leer_var("FS").cadena("%.6g");
181        Self::partir_con(linea, &fs, |p| self.ere(p))
182    }
183
184    fn partir_con<F: FnMut(&str) -> Rc<Regex>>(linea: &str, fs: &str, mut ere: F) -> Vec<String> {
185        if fs == " " {
186            return linea.split(|c: char| c == ' ' || c == '\t' || c == '\n')
187                .filter(|s| !s.is_empty()).map(|s| s.to_string()).collect();
188        }
189        if linea.is_empty() { return Vec::new(); }
190        if fs.chars().count() == 1 && fs != "\\" {
191            let c = fs.chars().next().unwrap();
192            // Un único carácter es literal, salvo que sea el espacio (ya tratado). En POSIX un
193            // metacarácter suelto —`.` o `|`— también es literal acá, y ése es el punto.
194            return linea.split(c).map(|s| s.to_string()).collect();
195        }
196        let re = ere(fs);
197        let cs: Vec<char> = linea.chars().collect();
198        let mut trozos = Vec::new();
199        let mut ini = 0;
200        let mut i = 0;
201        while i <= cs.len() {
202            match re.buscar(&cs, i) {
203                Some((a, b)) if b > a => {
204                    trozos.push(cs[ini..a].iter().collect::<String>());
205                    ini = b;
206                    i = b;
207                }
208                // Una coincidencia VACÍA no parte: si no, `FS="x*"` haría un trozo por carácter y
209                // se comería la cadena entera.
210                Some((a, _)) => { i = a + 1; }
211                None => break,
212            }
213        }
214        trozos.push(cs[ini.min(cs.len())..].iter().collect::<String>());
215        trozos
216    }
217
218    fn asegurar_campos(&mut self) {
219        if !self.sucio_campos { return; }
220        self.sucio_campos = false;
221        let reg = self.registro.clone();
222        self.campos = self.partir(&reg);
223        let nf = self.campos.len() as f64;
224        self.globales.insert("NF".into(), Celda::Escalar(Valor::Num(nf)));
225    }
226
227    fn asegurar_registro(&mut self) {
228        if !self.sucio_registro { return; }
229        self.sucio_registro = false;
230        let ofs = self.leer_var("OFS").cadena("%.6g");
231        self.registro = self.campos.join(&ofs);
232    }
233
234    pub fn poner_registro(&mut self, s: String) {
235        self.registro = s;
236        self.sucio_campos = true;
237        self.sucio_registro = false;
238    }
239
240    fn leer_campo(&mut self, i: usize) -> Valor {
241        if i == 0 {
242            self.asegurar_registro();
243            return Valor::de_entrada(&self.registro.clone());
244        }
245        self.asegurar_campos();
246        match self.campos.get(i - 1) {
247            Some(s) => Valor::de_entrada(s),
248            None => Valor::Vacio,
249        }
250    }
251
252    fn poner_campo(&mut self, i: usize, v: String) {
253        if i == 0 {
254            self.poner_registro(v);
255            self.asegurar_campos();
256            return;
257        }
258        self.asegurar_campos();
259        if i > self.campos.len() {
260            self.campos.resize(i, String::new());
261            self.globales.insert("NF".into(), Celda::Escalar(Valor::Num(self.campos.len() as f64)));
262        }
263        self.campos[i - 1] = v;
264        self.sucio_registro = true;
265    }
266
267    // ── Expresiones regulares ────────────────────────────────────────────────────────────────────
268
269    /// Compila y CACHEA. Sin la caché, un `gsub(/x/,"")` dentro de un bucle recompilaría el patrón
270    /// en cada vuelta; con ella se compila una vez por texto distinto.
271    fn ere(&self, patron: &str) -> Rc<Regex> {
272        // La caché es interior-mutable de hecho: se usa desde `&self` para poder llamarla desde
273        // `partir`, que ya tiene prestado el intérprete. Se resuelve compilando sin cachear cuando
274        // no se puede tomar prestado — es raro y el coste es de una compilación.
275        if let Some(r) = self.ere_cache.get(patron) { return r.clone(); }
276        Rc::new(Regex::nueva(patron).unwrap_or_else(|e| {
277            eprintln!("qillqa: expresión regular inválida /{patron}/: {e}");
278            std::process::exit(2);
279        }))
280    }
281
282    fn ere_mut(&mut self, patron: &str) -> Rc<Regex> {
283        if let Some(r) = self.ere_cache.get(patron) { return r.clone(); }
284        let r = self.ere(patron);
285        self.ere_cache.insert(patron.to_string(), r.clone());
286        r
287    }
288
289    /// La ERE de una expresión: si es un literal `/re/`, la suya; si no, se compila su valor.
290    fn ere_de(&mut self, e: &Expr) -> Result<Rc<Regex>, String> {
291        if let Expr::Ere(r) = e { return Ok(r.clone()); }
292        let v = self.eval(e)?;
293        let s = self.cadena_de(&v);
294        Ok(self.ere_mut(&s))
295    }
296
297    // ── Ejecución ────────────────────────────────────────────────────────────────────────────────
298
299    pub fn ejecutar_begin(&mut self) -> Result<Flujo, String> {
300        let reglas = self.prog.reglas.clone();
301        for r in reglas.iter().filter(|r| matches!(r.patron, Patron::Begin)) {
302            if let Some(a) = &r.accion {
303                propaga!(self.bloque(a)?);
304            }
305        }
306        Ok(Flujo::Normal)
307    }
308
309    pub fn ejecutar_end(&mut self) -> Result<Flujo, String> {
310        let reglas = self.prog.reglas.clone();
311        for r in reglas.iter().filter(|r| matches!(r.patron, Patron::End)) {
312            if let Some(a) = &r.accion {
313                propaga!(self.bloque(a)?);
314            }
315        }
316        Ok(Flujo::Normal)
317    }
318
319    pub fn hay_main(&self) -> bool {
320        self.prog.reglas.iter().any(|r| !matches!(r.patron, Patron::Begin))
321    }
322    pub fn hay_end(&self) -> bool {
323        self.prog.reglas.iter().any(|r| matches!(r.patron, Patron::End))
324    }
325
326    /// Corre las reglas normales para el registro actual.
327    pub fn ejecutar_reglas(&mut self) -> Result<Flujo, String> {
328        let reglas = self.prog.reglas.clone();
329        for r in &reglas {
330            let corre = match &r.patron {
331                Patron::Begin | Patron::End => continue,
332                Patron::Siempre => true,
333                Patron::Expr(e) => self.eval(e)?.verdad(),
334                Patron::Rango(a, b) => {
335                    let id = r.rango_id;
336                    if !self.rangos_activos[id] {
337                        if self.eval(a)?.verdad() {
338                            // Un rango cuyo final casa en la MISMA línea empieza y acaba ahí.
339                            self.rangos_activos[id] = !self.eval(b)?.verdad();
340                            true
341                        } else { false }
342                    } else {
343                        if self.eval(b)?.verdad() { self.rangos_activos[id] = false; }
344                        true
345                    }
346                }
347            };
348            if !corre { continue; }
349            match &r.accion {
350                None => { let s = self.leer_campo(0); let t = self.cadena_de(&s); self.escribir_linea(&t, &None)?; }
351                Some(a) => propaga!(self.bloque(a)?),
352            }
353        }
354        Ok(Flujo::Normal)
355    }
356
357    fn bloque(&mut self, ss: &[Sent]) -> Result<Flujo, String> {
358        for s in ss { propaga!(self.sentencia(s)?); }
359        Ok(Flujo::Normal)
360    }
361
362    fn sentencia(&mut self, s: &Sent) -> Result<Flujo, String> {
363        match s {
364            Sent::Nada => {}
365            Sent::Expr(e) => { self.eval(e)?; }
366            Sent::Bloque(b) => propaga!(self.bloque(b)?),
367            Sent::Print(args, redir) => {
368                let ofs = self.leer_var("OFS").cadena("%.6g");
369                let ofmt = self.ofmt();
370                let mut partes = Vec::new();
371                if args.is_empty() {
372                    let v = self.leer_campo(0);
373                    partes.push(self.cadena_de(&v));
374                } else {
375                    let planos = Self::aplanar(args);
376                    for a in &planos {
377                        let v = self.eval(a)?;
378                        // `print` usa OFMT para los no-enteros, no CONVFMT. Es la única diferencia
379                        // entre las dos variables y se nota en cuanto alguien toca OFMT.
380                        partes.push(match &v {
381                            Valor::Num(n) => valor::numero_a_cadena(*n, &ofmt),
382                            otro => self.cadena_de(otro),
383                        });
384                    }
385                }
386                let linea = partes.join(&ofs);
387                self.escribir_linea(&linea, redir)?;
388            }
389            Sent::Printf(args, redir) => {
390                let planos = Self::aplanar(args);
391                if planos.is_empty() { return Err("printf sin formato".into()); }
392                let fv = self.eval(&planos[0])?;
393                let fmt = self.cadena_de(&fv);
394                let mut vals = Vec::new();
395                for a in &planos[1..] {
396                    let v = self.eval(a)?;
397                    vals.push(match v {
398                        Valor::Num(n) => Arg::Numero(n),
399                        Valor::Cad(s) => Arg::Texto(s),
400                        Valor::StrNum(s, n) => Arg::Entrada(s, n),
401                        Valor::Vacio => Arg::Texto(String::new()),
402                    });
403                }
404                let texto = formato::printf(&fmt, &vals, &self.convfmt());
405                self.escribir_crudo(&texto, redir)?;
406            }
407            Sent::Si(c, a, b) => {
408                if self.eval(c)?.verdad() { propaga!(self.sentencia(a)?); }
409                else if let Some(b) = b { propaga!(self.sentencia(b)?); }
410            }
411            Sent::Mientras(c, cuerpo) => {
412                while self.eval(c)?.verdad() {
413                    match self.sentencia(cuerpo)? {
414                        Flujo::Corta => break,
415                        Flujo::Sigue | Flujo::Normal => {}
416                        otro => return Ok(otro),
417                    }
418                }
419            }
420            Sent::HazMientras(cuerpo, c) => {
421                loop {
422                    match self.sentencia(cuerpo)? {
423                        Flujo::Corta => break,
424                        Flujo::Sigue | Flujo::Normal => {}
425                        otro => return Ok(otro),
426                    }
427                    if !self.eval(c)?.verdad() { break; }
428                }
429            }
430            Sent::Para(init, cond, paso, cuerpo) => {
431                if let Some(i) = init { propaga!(self.sentencia(i)?); }
432                loop {
433                    if let Some(c) = cond { if !self.eval(c)?.verdad() { break; } }
434                    match self.sentencia(cuerpo)? {
435                        Flujo::Corta => break,
436                        Flujo::Sigue | Flujo::Normal => {}
437                        otro => return Ok(otro),
438                    }
439                    if let Some(p) = paso { propaga!(self.sentencia(p)?); }
440                }
441            }
442            Sent::ParaEn(k, arr, cuerpo) => {
443                let a = self.array(arr);
444                // Se copian las claves: el cuerpo puede borrar elementos, y recorrer el mapa
445                // mientras se modifica es justo lo que rompe en los awk que no lo hacen.
446                let claves: Vec<String> = a.borrow().keys().cloned().collect();
447                for c in claves {
448                    if !a.borrow().contains_key(&c) { continue; }
449                    self.poner_var(k, Valor::de_entrada(&c));
450                    match self.sentencia(cuerpo)? {
451                        Flujo::Corta => break,
452                        Flujo::Sigue | Flujo::Normal => {}
453                        otro => return Ok(otro),
454                    }
455                }
456            }
457            Sent::Corta => return Ok(Flujo::Corta),
458            Sent::Sigue => return Ok(Flujo::Sigue),
459            Sent::Siguiente => return Ok(Flujo::Siguiente),
460            Sent::SiguienteFichero => return Ok(Flujo::SiguienteFichero),
461            Sent::Sale(e) => {
462                let c = match e { Some(x) => self.eval(x)?.numero() as i32, None => self.codigo_salida };
463                return Ok(Flujo::Sale(c));
464            }
465            Sent::Retorna(e) => {
466                let v = match e { Some(x) => self.eval(x)?, None => Valor::Vacio };
467                return Ok(Flujo::Retorna(v));
468            }
469            Sent::Borra(n, idx) => {
470                let clave = self.clave(idx)?;
471                let a = self.array(n);
472                a.borrow_mut().remove(&clave);
473            }
474            Sent::BorraTodo(n) => { let a = self.array(n); a.borrow_mut().clear(); }
475        }
476        Ok(Flujo::Normal)
477    }
478
479    /// `print (a, b)` llega como una «llamada» sintética `@lista`: acá se desarma.
480    fn aplanar(args: &[Expr]) -> Vec<Expr> {
481        if args.len() == 1 {
482            if let Expr::Llamada(n, lista) = &args[0] {
483                if n == "@lista" { return lista.clone(); }
484            }
485        }
486        args.to_vec()
487    }
488
489    fn clave(&mut self, idx: &[Expr]) -> Result<String, String> {
490        let subsep = self.leer_var("SUBSEP").cadena("%.6g");
491        let mut partes = Vec::new();
492        for e in idx {
493            let v = self.eval(e)?;
494            partes.push(self.cadena_de(&v));
495        }
496        Ok(partes.join(&subsep))
497    }
498
499    // ── Salidas y entradas ───────────────────────────────────────────────────────────────────────
500
501    fn escribir_linea(&mut self, s: &str, r: &Option<Redir>) -> Result<(), String> {
502        let ors = self.leer_var("ORS").cadena("%.6g");
503        self.escribir_crudo(&format!("{s}{ors}"), r)
504    }
505
506    fn escribir_crudo(&mut self, s: &str, r: &Option<Redir>) -> Result<(), String> {
507        match r {
508            None => { let _ = self.stdout.write_all(s.as_bytes()); }
509            Some(red) => {
510                let (destino, modo) = match red {
511                    Redir::Fichero(e) => (self.eval(e)?, 'w'),
512                    Redir::Anexa(e) => (self.eval(e)?, 'a'),
513                    Redir::Tuberia(e) => (self.eval(e)?, 'p'),
514                };
515                let nombre = self.cadena_de(&destino);
516                self.abrir_salida(&nombre, modo)?;
517                if let Some(sal) = self.salidas.get_mut(&nombre) {
518                    let _ = sal.escritor.write_all(s.as_bytes());
519                }
520            }
521        }
522        Ok(())
523    }
524
525    fn abrir_salida(&mut self, nombre: &str, modo: char) -> Result<(), String> {
526        if self.salidas.contains_key(nombre) { return Ok(()); }
527        let sal = match modo {
528            'p' => {
529                // La tubería la abre un `sh -c`, como manda POSIX: el destino es una LÍNEA DE
530                // ÓRDENES, no un programa. Por eso `print | "sort -k2 | head"` funciona.
531                let mut hijo = std::process::Command::new("/bin/sh")
532                    .arg("-c").arg(nombre)
533                    .stdin(std::process::Stdio::piped())
534                    .spawn().map_err(|e| format!("no pude lanzar `{nombre}`: {e}"))?;
535                let entrada = hijo.stdin.take().unwrap();
536                Salida { escritor: Box::new(entrada), hijo: Some(hijo) }
537            }
538            _ => {
539                if nombre == "/dev/stdout" || nombre == "-" {
540                    Salida { escritor: Box::new(std::io::stdout()), hijo: None }
541                } else if nombre == "/dev/stderr" {
542                    Salida { escritor: Box::new(std::io::stderr()), hijo: None }
543                } else {
544                    let f = std::fs::OpenOptions::new()
545                        .write(true).create(true)
546                        .append(modo == 'a').truncate(modo == 'w')
547                        .open(nombre).map_err(|e| format!("no pude abrir `{nombre}`: {e}"))?;
548                    Salida { escritor: Box::new(std::io::BufWriter::new(f)), hijo: None }
549                }
550            }
551        };
552        self.salidas.insert(nombre.to_string(), sal);
553        Ok(())
554    }
555
556    pub fn cerrar_todo(&mut self) {
557        let _ = self.stdout.flush();
558        let nombres: Vec<String> = self.salidas.keys().cloned().collect();
559        for n in nombres { self.cerrar(&n); }
560        let nombres: Vec<String> = self.entradas.keys().cloned().collect();
561        for n in nombres { self.cerrar(&n); }
562    }
563
564    fn cerrar(&mut self, nombre: &str) -> i32 {
565        let mut r = -1;
566        if let Some(mut s) = self.salidas.remove(nombre) {
567            let _ = s.escritor.flush();
568            drop(s.escritor);
569            if let Some(mut h) = s.hijo.take() {
570                r = h.wait().ok().and_then(|e| e.code()).unwrap_or(0);
571            } else { r = 0; }
572        }
573        if let Some(mut e) = self.entradas.remove(nombre) {
574            drop(std::mem::replace(&mut e.lector, Box::new(std::io::empty())));
575            if let Some(mut h) = e.hijo.take() {
576                r = h.wait().ok().and_then(|x| x.code()).unwrap_or(0);
577            } else { r = 0; }
578        }
579        r
580    }
581
582    /// Lee un registro del lector dado, respetando RS. Devuelve `None` en fin de fichero.
583    fn leer_registro(lector: &mut dyn BufRead, rs: &str) -> Option<String> {
584        if rs == "\n" {
585            let mut buf = Vec::new();
586            match lector.read_until(b'\n', &mut buf) {
587                Ok(0) | Err(_) => return None,
588                Ok(_) => {}
589            }
590            if buf.last() == Some(&b'\n') { buf.pop(); }
591            if buf.last() == Some(&b'\r') { buf.pop(); }
592            return Some(String::from_utf8_lossy(&buf).into_owned());
593        }
594        if rs.is_empty() {
595            // RS="" es el MODO PÁRRAFO: los registros se separan por líneas en blanco, las de
596            // delante se descartan, y `\n` pasa a ser separador de campos además de FS.
597            let mut lineas: Vec<String> = Vec::new();
598            loop {
599                let mut buf = Vec::new();
600                match lector.read_until(b'\n', &mut buf) {
601                    Ok(0) | Err(_) => break,
602                    Ok(_) => {}
603                }
604                if buf.last() == Some(&b'\n') { buf.pop(); }
605                let l = String::from_utf8_lossy(&buf).into_owned();
606                if l.is_empty() {
607                    if lineas.is_empty() { continue; } // blancos de delante
608                    break;
609                }
610                lineas.push(l);
611            }
612            if lineas.is_empty() { return None; }
613            return Some(lineas.join("\n"));
614        }
615        // RS de un carácter.
616        let sep = rs.as_bytes()[0];
617        let mut buf = Vec::new();
618        match lector.read_until(sep, &mut buf) {
619            Ok(0) | Err(_) => return None,
620            Ok(_) => {}
621        }
622        if buf.last() == Some(&sep) { buf.pop(); }
623        Some(String::from_utf8_lossy(&buf).into_owned())
624    }
625
626    /// Siguiente registro de la entrada principal (los ficheros de ARGV, o la entrada estándar).
627    pub fn siguiente_registro(&mut self) -> Option<String> {
628        let rs = self.leer_var("RS").cadena("%.6g");
629        loop {
630            if self.lector_actual.is_none() && !self.abrir_siguiente_fuente() { return None; }
631            let lector = self.lector_actual.as_mut().unwrap();
632            match Self::leer_registro(lector.as_mut(), &rs) {
633                Some(r) => {
634                    let nr = self.leer_var("NR").numero() + 1.0;
635                    let fnr = self.leer_var("FNR").numero() + 1.0;
636                    self.poner_var("NR", Valor::Num(nr));
637                    self.poner_var("FNR", Valor::Num(fnr));
638                    return Some(r);
639                }
640                None => { self.lector_actual = None; }
641            }
642        }
643    }
644
645    pub fn cerrar_fuente_actual(&mut self) { self.lector_actual = None; }
646
647    /// Abre el siguiente operando de ARGV que no sea una asignación. Devuelve false si no queda.
648    fn abrir_siguiente_fuente(&mut self) -> bool {
649        let argc = self.leer_var("ARGC").numero() as usize;
650        while self.indice_argv < argc {
651            let i = self.indice_argv;
652            self.indice_argv += 1;
653            let arr = self.array("ARGV");
654            let v = arr.borrow().get(&i.to_string()).cloned().unwrap_or(Valor::Vacio);
655            let s = self.cadena_de(&v);
656            if s.is_empty() { continue; }
657            // `var=valor` entre ficheros: asignación diferida, no un fichero.
658            if let Some(p) = posicion_asignacion(&s) {
659                let (n, v) = (s[..p].to_string(), desescapar(&s[p + 1..]));
660                self.poner_var(&n, Valor::de_entrada(&v));
661                continue;
662            }
663            self.poner_var("FILENAME", Valor::Cad(s.clone()));
664            self.poner_var("FNR", Valor::Num(0.0));
665            if s == "-" || s == "/dev/stdin" {
666                self.lector_actual = Some(Box::new(BufReader::new(std::io::stdin())));
667                return true;
668            }
669            match std::fs::File::open(&s) {
670                Ok(f) => { self.lector_actual = Some(Box::new(BufReader::new(f))); return true; }
671                Err(e) => {
672                    eprintln!("qillqa: no puedo leer `{s}`: {e}");
673                    self.codigo_salida = 2;
674                    continue;
675                }
676            }
677        }
678        // Ningún operando de fichero: se lee la entrada estándar, una sola vez.
679        if !self.uso_stdin() {
680            self.marcar_stdin();
681            self.lector_actual = Some(Box::new(BufReader::new(std::io::stdin())));
682            return true;
683        }
684        false
685    }
686
687    fn uso_stdin(&self) -> bool { self.globales.contains_key("@stdin-usado") }
688    fn marcar_stdin(&mut self) { self.globales.insert("@stdin-usado".into(), Celda::Escalar(Valor::Num(1.0))); }
689
690    // ── Evaluación de expresiones ────────────────────────────────────────────────────────────────
691
692    pub fn eval(&mut self, e: &Expr) -> Result<Valor, String> {
693        Ok(match e {
694            Expr::Num(n) => Valor::Num(*n),
695            Expr::Cad(s) => Valor::Cad(s.clone()),
696            Expr::Grupo(inner) => self.eval(inner)?,
697            Expr::Ere(r) => {
698                // Una ERE suelta vale `$0 ~ /re/`.
699                let reg = self.leer_campo(0);
700                let s = self.cadena_de(&reg);
701                Valor::Num(if r.casa(&s) { 1.0 } else { 0.0 })
702            }
703            // ⚠ Leer NF obliga a PARTIR el registro: si no, `{print NF}` da 0 hasta que alguien
704            // toque un campo. El estado perezoso (`sucio_campos`) es lo que hace barato asignar a
705            // `$0`, y el precio es acordarse de forzarlo en cada lectura que dependa de los campos.
706            Expr::Var(n) if n == "NF" => { self.asegurar_campos(); self.leer_var(n) }
707            Expr::Var(n) => self.leer_var(n),
708            Expr::Campo(i) => {
709                let idx = self.eval(i)?.numero();
710                if idx < 0.0 { return Err(format!("intento de acceder al campo {idx}")); }
711                self.leer_campo(idx as usize)
712            }
713            Expr::Indice(n, idx) => {
714                let clave = self.clave(idx)?;
715                let a = self.array(n);
716                // Leer un elemento que no existe lo CREA vacío. Es observable con `in`, y es lo que
717                // manda POSIX: `if (a["x"] == "") ...` deja `"x"` dentro del array.
718                let mut m = a.borrow_mut();
719                m.entry(clave).or_insert(Valor::Vacio).clone()
720            }
721            Expr::Asigna(l, v) => { let val = self.eval(v)?; self.asignar(l, val.clone())?; val }
722            Expr::AsignaOp(l, op, v) => {
723                let actual = self.leer_lugar(l)?;
724                let d = self.eval(v)?;
725                let r = Valor::Num(aritmetica(*op, actual.numero(), d.numero()));
726                self.asignar(l, r.clone())?;
727                r
728            }
729            Expr::Bin(op, a, b) => {
730                let x = self.eval(a)?.numero();
731                let y = self.eval(b)?.numero();
732                Valor::Num(aritmetica(*op, x, y))
733            }
734            Expr::Cmp(op, a, b) => {
735                let x = self.eval(a)?;
736                let y = self.eval(b)?;
737                let o = x.comparar(&y, &self.convfmt());
738                let r = match op {
739                    CmpOp::Lt => o.is_lt(), CmpOp::Le => o.is_le(), CmpOp::Gt => o.is_gt(),
740                    CmpOp::Ge => o.is_ge(), CmpOp::Eq => o.is_eq(), CmpOp::Ne => o.is_ne(),
741                };
742                Valor::Num(if r { 1.0 } else { 0.0 })
743            }
744            Expr::Coincide(a, b, negado) => {
745                let v = self.eval(a)?;
746                let s = self.cadena_de(&v);
747                let re = self.ere_de(b)?;
748                let casa = re.casa(&s);
749                Valor::Num(if casa != *negado { 1.0 } else { 0.0 })
750            }
751            Expr::Y(a, b) => {
752                if !self.eval(a)?.verdad() { Valor::Num(0.0) }
753                else { Valor::Num(if self.eval(b)?.verdad() { 1.0 } else { 0.0 }) }
754            }
755            Expr::O(a, b) => {
756                if self.eval(a)?.verdad() { Valor::Num(1.0) }
757                else { Valor::Num(if self.eval(b)?.verdad() { 1.0 } else { 0.0 }) }
758            }
759            Expr::No(a) => Valor::Num(if self.eval(a)?.verdad() { 0.0 } else { 1.0 }),
760            Expr::Neg(a) => Valor::Num(-self.eval(a)?.numero()),
761            Expr::Pos(a) => Valor::Num(self.eval(a)?.numero()),
762            Expr::Ternario(c, a, b) => if self.eval(c)?.verdad() { self.eval(a)? } else { self.eval(b)? },
763            Expr::Concat(a, b) => {
764                let x = self.eval(a)?;
765                let y = self.eval(b)?;
766                Valor::Cad(format!("{}{}", self.cadena_de(&x), self.cadena_de(&y)))
767            }
768            Expr::Incr(l, previo, delta) => {
769                let antes = self.leer_lugar(l)?.numero();
770                let despues = antes + delta;
771                self.asignar(l, Valor::Num(despues))?;
772                Valor::Num(if *previo { despues } else { antes })
773            }
774            Expr::En(idx, arr) => {
775                let clave = self.clave(idx)?;
776                let a = self.array(arr);
777                let hay = a.borrow().contains_key(&clave);
778                Valor::Num(if hay { 1.0 } else { 0.0 })
779            }
780            Expr::Builtin(n, args) => self.builtin(n, args)?,
781            Expr::Llamada(n, args) => self.llamar(n, args)?,
782            Expr::Getline(g) => self.getline(g)?,
783        })
784    }
785
786    fn leer_lugar(&mut self, l: &Lugar) -> Result<Valor, String> {
787        Ok(match l {
788            Lugar::Var(n) => self.leer_var(n),
789            Lugar::Campo(i) => { let idx = self.eval(i)?.numero() as usize; self.leer_campo(idx) }
790            Lugar::Indice(n, idx) => {
791                let clave = self.clave(idx)?;
792                let a = self.array(n);
793                let v = a.borrow().get(&clave).cloned();
794                v.unwrap_or(Valor::Vacio)
795            }
796        })
797    }
798
799    fn asignar(&mut self, l: &Lugar, v: Valor) -> Result<(), String> {
800        match l {
801            Lugar::Var(n) => self.poner_var(n, v),
802            Lugar::Campo(i) => {
803                let idx = self.eval(i)?.numero() as usize;
804                let s = self.cadena_de(&v);
805                self.poner_campo(idx, s);
806            }
807            Lugar::Indice(n, idx) => {
808                let clave = self.clave(idx)?;
809                let a = self.array(n);
810                a.borrow_mut().insert(clave, v);
811            }
812        }
813        Ok(())
814    }
815
816    // ── Funciones del programa ───────────────────────────────────────────────────────────────────
817
818    fn llamar(&mut self, nombre: &str, args: &[Expr]) -> Result<Valor, String> {
819        let f = self.prog.funciones.iter().find(|f| f.nombre == nombre).cloned()
820            .ok_or_else(|| format!("función no definida: `{nombre}`"))?;
821        let mut marco: HashMap<String, Celda> = HashMap::new();
822        for (i, p) in f.params.iter().enumerate() {
823            match args.get(i) {
824                None => { marco.insert(p.clone(), Celda::Escalar(Valor::Vacio)); }
825                Some(a) => {
826                    // ⚠ Un array se pasa POR REFERENCIA y un escalar por valor. Cuál es cada uno se
827                    // decide mirando qué hay en la variable: si ya es un array, se comparte el Rc.
828                    if let Expr::Var(vn) = a {
829                        let es_array = matches!(self.celda(vn), Some(Celda::Array(_)));
830                        if es_array {
831                            let arr = self.array(vn);
832                            marco.insert(p.clone(), Celda::Array(arr));
833                            continue;
834                        }
835                        // Una variable nunca usada que se pasa a un parámetro que la función usa
836                        // como array tiene que compartir el array. Se resuelve creándolo ya.
837                        if self.celda(vn).is_none() && self.usa_como_array(&f, p) {
838                            let arr = self.array(vn);
839                            marco.insert(p.clone(), Celda::Array(arr));
840                            continue;
841                        }
842                    }
843                    let v = self.eval(a)?;
844                    marco.insert(p.clone(), Celda::Escalar(v));
845                }
846            }
847        }
848        self.marcos.push(marco);
849        let r = self.bloque(&f.cuerpo);
850        self.marcos.pop();
851        match r? {
852            Flujo::Retorna(v) => Ok(v),
853            Flujo::Sale(c) => Err(format!("@salir:{c}")),
854            _ => Ok(Valor::Vacio),
855        }
856    }
857
858    /// ¿La función usa este parámetro como array? Se mira el cuerpo: `p[...]`, `in p`, `delete p`.
859    fn usa_como_array(&self, f: &Funcion, p: &str) -> bool {
860        fn en_sent(s: &Sent, p: &str) -> bool {
861            match s {
862                Sent::Expr(e) => en_expr(e, p),
863                Sent::Print(a, _) | Sent::Printf(a, _) => a.iter().any(|e| en_expr(e, p)),
864                Sent::Bloque(b) => b.iter().any(|s| en_sent(s, p)),
865                Sent::Si(c, a, b) => en_expr(c, p) || en_sent(a, p) || b.as_ref().map_or(false, |b| en_sent(b, p)),
866                Sent::Mientras(c, b) => en_expr(c, p) || en_sent(b, p),
867                Sent::HazMientras(b, c) => en_expr(c, p) || en_sent(b, p),
868                Sent::Para(i, c, s2, b) => i.as_ref().map_or(false, |x| en_sent(x, p))
869                    || c.as_ref().map_or(false, |x| en_expr(x, p))
870                    || s2.as_ref().map_or(false, |x| en_sent(x, p)) || en_sent(b, p),
871                Sent::ParaEn(_, a, b) => a == p || en_sent(b, p),
872                Sent::Borra(n, _) | Sent::BorraTodo(n) => n == p,
873                Sent::Sale(e) | Sent::Retorna(e) => e.as_ref().map_or(false, |x| en_expr(x, p)),
874                _ => false,
875            }
876        }
877        fn en_lugar(l: &Lugar, p: &str) -> bool {
878            match l { Lugar::Indice(n, _) => n == p, Lugar::Campo(e) => en_expr(e, p), _ => false }
879        }
880        fn en_expr(e: &Expr, p: &str) -> bool {
881            match e {
882                Expr::Indice(n, _) => n == p,
883                Expr::En(_, n) => n == p,
884                Expr::Llamada(_, args) | Expr::Builtin(_, args) => args.iter().any(|a| en_expr(a, p))
885                    || args.iter().any(|a| matches!(a, Expr::Var(v) if v == p)),
886                Expr::Bin(_, a, b) | Expr::Cmp(_, a, b) | Expr::Y(a, b) | Expr::O(a, b)
887                    | Expr::Concat(a, b) => en_expr(a, p) || en_expr(b, p),
888                Expr::Coincide(a, b, _) => en_expr(a, p) || en_expr(b, p),
889                Expr::No(a) | Expr::Neg(a) | Expr::Pos(a) | Expr::Grupo(a) | Expr::Campo(a) => en_expr(a, p),
890                Expr::Ternario(a, b, c) => en_expr(a, p) || en_expr(b, p) || en_expr(c, p),
891                // ⚠ `a["k"] = 1` marca a `a` como array, y el nombre está en el LUGAR, no en el
892                // valor. Mirar sólo el valor dejaba `funcion-array-ref` sin compartir el array.
893                Expr::Asigna(l, v) => en_lugar(l, p) || en_expr(v, p),
894                Expr::AsignaOp(l, _, v) => en_lugar(l, p) || en_expr(v, p),
895                Expr::Incr(l, _, _) => en_lugar(l, p),
896                _ => false,
897            }
898        }
899        f.cuerpo.iter().any(|s| en_sent(s, p))
900    }
901
902    // ── getline ──────────────────────────────────────────────────────────────────────────────────
903
904    fn getline(&mut self, g: &Getline) -> Result<Valor, String> {
905        let rs = self.leer_var("RS").cadena("%.6g");
906        match g {
907            Getline::Simple(l) => {
908                match self.siguiente_registro() {
909                    None => Ok(Valor::Num(0.0)),
910                    Some(r) => {
911                        match l {
912                            None => { self.poner_registro(r); }
913                            Some(lug) => { let lug = (**lug).clone(); self.asignar(&lug, Valor::de_entrada(&r))?; }
914                        }
915                        Ok(Valor::Num(1.0))
916                    }
917                }
918            }
919            Getline::DeFichero(l, f) => {
920                let fv = self.eval(f)?;
921                let nombre = self.cadena_de(&fv);
922                if !self.entradas.contains_key(&nombre) {
923                    let lector: Box<dyn BufRead> = if nombre == "-" || nombre == "/dev/stdin" {
924                        Box::new(BufReader::new(std::io::stdin()))
925                    } else {
926                        match std::fs::File::open(&nombre) {
927                            Ok(f) => Box::new(BufReader::new(f)),
928                            // getline devuelve -1 cuando no puede abrir: es un valor, no un error.
929                            Err(_) => return Ok(Valor::Num(-1.0)),
930                        }
931                    };
932                    self.entradas.insert(nombre.clone(), Entrada { lector, hijo: None });
933                }
934                let ent = self.entradas.get_mut(&nombre).unwrap();
935                match Self::leer_registro(ent.lector.as_mut(), &rs) {
936                    None => Ok(Valor::Num(0.0)),
937                    Some(r) => {
938                        match l {
939                            // `getline < f` sin variable pone $0 y NF, pero NO toca NR.
940                            None => { self.poner_registro(r); }
941                            Some(lug) => { let lug = (**lug).clone(); self.asignar(&lug, Valor::de_entrada(&r))?; }
942                        }
943                        Ok(Valor::Num(1.0))
944                    }
945                }
946            }
947            Getline::DeTuberia(cmd, l) => {
948                let cv = self.eval(cmd)?;
949                let nombre = self.cadena_de(&cv);
950                if !self.entradas.contains_key(&nombre) {
951                    let _ = self.stdout.flush(); // que lo ya impreso salga antes que lo del hijo
952                    let mut hijo = std::process::Command::new("/bin/sh")
953                        .arg("-c").arg(&nombre)
954                        .stdout(std::process::Stdio::piped())
955                        .spawn().map_err(|e| format!("no pude lanzar `{nombre}`: {e}"))?;
956                    let salida = hijo.stdout.take().unwrap();
957                    self.entradas.insert(nombre.clone(), Entrada {
958                        lector: Box::new(BufReader::new(salida)), hijo: Some(hijo),
959                    });
960                }
961                let ent = self.entradas.get_mut(&nombre).unwrap();
962                match Self::leer_registro(ent.lector.as_mut(), &rs) {
963                    None => Ok(Valor::Num(0.0)),
964                    Some(r) => {
965                        // `"cmd" | getline` SÍ incrementa NR (POSIX lo distingue de `getline < f`).
966                        let nr = self.leer_var("NR").numero() + 1.0;
967                        self.poner_var("NR", Valor::Num(nr));
968                        match l {
969                            None => { self.poner_registro(r); }
970                            Some(lug) => { let lug = (**lug).clone(); self.asignar(&lug, Valor::de_entrada(&r))?; }
971                        }
972                        Ok(Valor::Num(1.0))
973                    }
974                }
975            }
976        }
977    }
978
979    // ── Funciones incorporadas ───────────────────────────────────────────────────────────────────
980
981    fn builtin(&mut self, n: &str, args: &[Expr]) -> Result<Valor, String> {
982        match n {
983            "length" => {
984                if args.is_empty() {
985                    let v = self.leer_campo(0);
986                    return Ok(Valor::Num(self.cadena_de(&v).chars().count() as f64));
987                }
988                // `length(a)` con un array da su número de elementos.
989                if let Expr::Var(vn) = &args[0] {
990                    if let Some(Celda::Array(a)) = self.celda(vn) {
991                        return Ok(Valor::Num(a.borrow().len() as f64));
992                    }
993                }
994                let v = self.eval(&args[0])?;
995                Ok(Valor::Num(self.cadena_de(&v).chars().count() as f64))
996            }
997            "substr" => {
998                let v = self.eval(&args[0])?;
999                let s: Vec<char> = self.cadena_de(&v).chars().collect();
1000                let m = self.eval(&args[1])?.numero();
1001                // ⚠ Los índices empiezan en 1, y los dos rincones de esta función se resolvieron
1002                // MIDIENDO los dos oráculos, no leyendo el estándar — que acá es ambiguo:
1003                //   · un inicio ≤ 0 se recorta a 1 y **el largo NO se ajusta**:
1004                //     `substr("abcdef", -1, 3)` es `abc` en busybox y en gawk --posix, no `a`.
1005                //     La lectura estricta de POSIX daría `a` (las posiciones -1,0,1); ningún awk
1006                //     real hace eso, y un guión escrito contra ellos es lo que hay que ejecutar.
1007                //   · los no enteros se TRUNCAN hacia cero: `substr("abcdef", 1.5, 2.5)` es `ab`.
1008                let inicio = m.trunc() as i64;
1009                let (desde, largo) = if args.len() > 2 {
1010                    let l = self.eval(&args[2])?.numero().trunc() as i64;
1011                    (inicio.max(1), l)
1012                } else {
1013                    let d = inicio.max(1);
1014                    (d, s.len() as i64 - d + 1)
1015                };
1016                if largo <= 0 || desde > s.len() as i64 { return Ok(Valor::Cad(String::new())); }
1017                let a = (desde - 1) as usize;
1018                let b = ((desde - 1 + largo) as usize).min(s.len());
1019                Ok(Valor::Cad(s[a..b].iter().collect()))
1020            }
1021            "index" => {
1022                let a = self.eval(&args[0])?; let sa = self.cadena_de(&a);
1023                let b = self.eval(&args[1])?; let sb = self.cadena_de(&b);
1024                let ha: Vec<char> = sa.chars().collect();
1025                let ag: Vec<char> = sb.chars().collect();
1026                if ag.is_empty() { return Ok(Valor::Num(if ha.is_empty() { 0.0 } else { 1.0 })); }
1027                for i in 0..ha.len().saturating_sub(ag.len() - 1) {
1028                    if ha[i..i + ag.len()] == ag[..] { return Ok(Valor::Num((i + 1) as f64)); }
1029                }
1030                Ok(Valor::Num(0.0))
1031            }
1032            "split" => {
1033                let v = self.eval(&args[0])?;
1034                let s = self.cadena_de(&v);
1035                let nombre = match &args[1] {
1036                    Expr::Var(n) => n.clone(),
1037                    Expr::Indice(n, _) => n.clone(),
1038                    _ => return Err("split necesita un array como segundo argumento".into()),
1039                };
1040                let fs = if args.len() > 2 {
1041                    match &args[2] {
1042                        Expr::Ere(r) => r.fuente().to_string(),
1043                        e => { let v = self.eval(e)?; self.cadena_de(&v) }
1044                    }
1045                } else { self.leer_var("FS").cadena("%.6g") };
1046                let trozos = if s.is_empty() { Vec::new() } else {
1047                    let mut cache: Vec<(String, Rc<Regex>)> = Vec::new();
1048                    let t = Self::partir_con(&s, &fs, |p| {
1049                        if let Some((_, r)) = cache.iter().find(|(k, _)| k == p) { return r.clone(); }
1050                        let r = Rc::new(Regex::nueva(p).unwrap_or_else(|e| {
1051                            eprintln!("qillqa: expresión regular inválida /{p}/: {e}");
1052                            std::process::exit(2);
1053                        }));
1054                        cache.push((p.to_string(), r.clone()));
1055                        r
1056                    });
1057                    t
1058                };
1059                let a = self.array(&nombre);
1060                a.borrow_mut().clear();
1061                for (i, t) in trozos.iter().enumerate() {
1062                    a.borrow_mut().insert((i + 1).to_string(), Valor::de_entrada(t));
1063                }
1064                Ok(Valor::Num(trozos.len() as f64))
1065            }
1066            "sub" | "gsub" => {
1067                let global = n == "gsub";
1068                let re = self.ere_de(&args[0])?;
1069                let rv = self.eval(&args[1])?;
1070                let reemplazo = self.cadena_de(&rv);
1071                let destino = if args.len() > 2 {
1072                    match &args[2] {
1073                        Expr::Var(v) => Lugar::Var(v.clone()),
1074                        Expr::Campo(i) => Lugar::Campo(i.clone()),
1075                        Expr::Indice(v, i) => Lugar::Indice(v.clone(), i.clone()),
1076                        Expr::Grupo(g) => match &**g {
1077                            Expr::Var(v) => Lugar::Var(v.clone()),
1078                            Expr::Campo(i) => Lugar::Campo(i.clone()),
1079                            Expr::Indice(v, i) => Lugar::Indice(v.clone(), i.clone()),
1080                            _ => return Err("sub/gsub necesita algo asignable".into()),
1081                        },
1082                        _ => return Err("sub/gsub necesita algo asignable".into()),
1083                    }
1084                } else { Lugar::Campo(Box::new(Expr::Num(0.0))) };
1085                let actual = self.leer_lugar(&destino)?;
1086                let texto = self.cadena_de(&actual);
1087                let (nuevo, cuantas) = sustituir(&re, &texto, &reemplazo, global);
1088                if cuantas > 0 { self.asignar(&destino, Valor::Cad(nuevo))?; }
1089                Ok(Valor::Num(cuantas as f64))
1090            }
1091            "match" => {
1092                let v = self.eval(&args[0])?;
1093                let s = self.cadena_de(&v);
1094                let re = self.ere_de(&args[1])?;
1095                let cs: Vec<char> = s.chars().collect();
1096                match re.buscar(&cs, 0) {
1097                    Some((a, b)) => {
1098                        self.poner_var("RSTART", Valor::Num((a + 1) as f64));
1099                        self.poner_var("RLENGTH", Valor::Num((b - a) as f64));
1100                        Ok(Valor::Num((a + 1) as f64))
1101                    }
1102                    None => {
1103                        self.poner_var("RSTART", Valor::Num(0.0));
1104                        self.poner_var("RLENGTH", Valor::Num(-1.0));
1105                        Ok(Valor::Num(0.0))
1106                    }
1107                }
1108            }
1109            "sprintf" => {
1110                if args.is_empty() { return Ok(Valor::Cad(String::new())); }
1111                let fv = self.eval(&args[0])?;
1112                let fmt = self.cadena_de(&fv);
1113                let mut vals = Vec::new();
1114                for a in &args[1..] {
1115                    let v = self.eval(a)?;
1116                    vals.push(match v {
1117                        Valor::Num(x) => Arg::Numero(x),
1118                        Valor::Cad(s) => Arg::Texto(s),
1119                        Valor::StrNum(s, x) => Arg::Entrada(s, x),
1120                        Valor::Vacio => Arg::Texto(String::new()),
1121                    });
1122                }
1123                Ok(Valor::Cad(formato::printf(&fmt, &vals, &self.convfmt())))
1124            }
1125            "sin" => Ok(Valor::Num(self.arg_num(args, 0)?.sin())),
1126            "cos" => Ok(Valor::Num(self.arg_num(args, 0)?.cos())),
1127            "atan2" => { let a = self.arg_num(args, 0)?; let b = self.arg_num(args, 1)?; Ok(Valor::Num(a.atan2(b))) }
1128            "exp" => Ok(Valor::Num(self.arg_num(args, 0)?.exp())),
1129            "log" => Ok(Valor::Num(self.arg_num(args, 0)?.ln())),
1130            "sqrt" => Ok(Valor::Num(self.arg_num(args, 0)?.sqrt())),
1131            "int" => Ok(Valor::Num(self.arg_num(args, 0)?.trunc())),
1132            "rand" => {
1133                // xorshift64*: determinista y suficiente. `rand` de awk NO promete calidad
1134                // criptográfica; lo que sí promete es repetirse con la misma semilla.
1135                self.semilla ^= self.semilla << 13;
1136                self.semilla ^= self.semilla >> 7;
1137                self.semilla ^= self.semilla << 17;
1138                let x = self.semilla.wrapping_mul(0x2545F4914F6CDD1D);
1139                Ok(Valor::Num((x >> 11) as f64 / (1u64 << 53) as f64))
1140            }
1141            "srand" => {
1142                let previa = self.semilla_previa;
1143                let nueva = if args.is_empty() { ahora_ms() as f64 } else { self.arg_num(args, 0)? };
1144                self.semilla_previa = nueva;
1145                self.semilla = (nueva as i64 as u64) | 1; // el 0 congelaría el xorshift
1146                Ok(Valor::Num(previa))
1147            }
1148            "tolower" | "toupper" => {
1149                let v = self.eval(&args[0])?;
1150                let s = self.cadena_de(&v);
1151                Ok(Valor::Cad(if n == "tolower" { s.to_lowercase() } else { s.to_uppercase() }))
1152            }
1153            "system" => {
1154                let v = self.eval(&args[0])?;
1155                let cmd = self.cadena_de(&v);
1156                let _ = self.stdout.flush(); // POSIX: se vacía la salida ANTES de lanzar
1157                let r = std::process::Command::new("/bin/sh").arg("-c").arg(&cmd).status();
1158                Ok(Valor::Num(r.ok().and_then(|s| s.code()).unwrap_or(-1) as f64))
1159            }
1160            "close" => {
1161                let v = self.eval(&args[0])?;
1162                let nombre = self.cadena_de(&v);
1163                Ok(Valor::Num(self.cerrar(&nombre) as f64))
1164            }
1165            "fflush" => {
1166                if args.is_empty() { let _ = self.stdout.flush(); return Ok(Valor::Num(0.0)); }
1167                let v = self.eval(&args[0])?;
1168                let nombre = self.cadena_de(&v);
1169                if let Some(s) = self.salidas.get_mut(&nombre) { let _ = s.escritor.flush(); }
1170                else { let _ = self.stdout.flush(); }
1171                Ok(Valor::Num(0.0))
1172            }
1173            otro => Err(format!("función incorporada desconocida: `{otro}`")),
1174        }
1175    }
1176
1177    fn arg_num(&mut self, args: &[Expr], i: usize) -> Result<f64, String> {
1178        match args.get(i) { Some(e) => Ok(self.eval(e)?.numero()), None => Ok(0.0) }
1179    }
1180}
1181
1182fn redondear(x: f64) -> i64 {
1183    // POSIX dice «truncado al entero más cercano», que es redondeo a la mitad hacia arriba.
1184    (x + 0.5).floor() as i64
1185}
1186
1187fn aritmetica(op: BinOp, a: f64, b: f64) -> f64 {
1188    match op {
1189        BinOp::Suma => a + b,
1190        BinOp::Resta => a - b,
1191        BinOp::Mult => a * b,
1192        BinOp::Div => a / b,
1193        // `%` de awk es `fmod`, no el resto entero: `5.5 % 2` es 1.5.
1194        BinOp::Modulo => a % b,
1195        BinOp::Potencia => a.powf(b),
1196    }
1197}
1198
1199/// `sub`/`gsub`. En el reemplazo, `&` es el texto que casó y `\&` es un `&` literal.
1200///
1201/// ⚠ La regla que decide el caso raro: **una coincidencia VACÍA pegada al final de la anterior no
1202/// sustituye**. Sin ella, `gsub(/a*/,"-")` sobre `"xax"` da `-x--x-` con 4 sustituciones —que es lo
1203/// que hace el awk de busybox— en vez de `-x-x-` con 3, que es lo que hacen gawk y el awk original
1204/// y lo que se deduce de POSIX. El banco diferencial lo destapó como una discrepancia ENTRE los
1205/// oráculos, que es justo para lo que sirve tener dos.
1206fn sustituir(re: &Regex, texto: &str, reemplazo: &str, global: bool) -> (String, usize) {
1207    let cs: Vec<char> = texto.chars().collect();
1208    let mut salida = String::new();
1209    let mut i = 0;
1210    let mut cuantas = 0;
1211    let mut ultimo_vacio_en: Option<usize> = None;
1212    while i <= cs.len() {
1213        match re.buscar(&cs, i) {
1214            None => break,
1215            Some((a, b)) => {
1216                // Una coincidencia VACÍA justo donde terminó la anterior no cuenta: si no,
1217                // `gsub(/x*/,"-")` sobre "xax" produciría guiones de más.
1218                if a == b && ultimo_vacio_en == Some(a) {
1219                    if a >= cs.len() { break; }
1220                    salida.push(cs[a]);
1221                    i = a + 1;
1222                    continue;
1223                }
1224                salida.extend(&cs[i..a]);
1225                let casado: String = cs[a..b].iter().collect();
1226                let mut rc = reemplazo.chars().peekable();
1227                while let Some(c) = rc.next() {
1228                    match c {
1229                        '&' => salida.push_str(&casado),
1230                        '\\' => match rc.peek() {
1231                            Some('&') => { salida.push('&'); rc.next(); }
1232                            Some('\\') => { salida.push('\\'); rc.next(); }
1233                            _ => salida.push('\\'),
1234                        },
1235                        otro => salida.push(otro),
1236                    }
1237                }
1238                cuantas += 1;
1239                if !global {
1240                    salida.extend(&cs[b..]);
1241                    return (salida, cuantas);
1242                }
1243                if a == b {
1244                    ultimo_vacio_en = Some(a);
1245                    if a >= cs.len() { i = a; break; }
1246                    salida.push(cs[a]);
1247                    i = a + 1;
1248                } else {
1249                    // Tras una coincidencia real, la posición donde termina queda VEDADA para una
1250                    // vacía: es la regla de arriba, y es la única diferencia con busybox.
1251                    ultimo_vacio_en = Some(b);
1252                    i = b;
1253                }
1254            }
1255        }
1256    }
1257    if i <= cs.len() { salida.extend(&cs[i.min(cs.len())..]); }
1258    (salida, cuantas)
1259}
1260
1261/// ¿`s` tiene forma `nombre=valor`? Devuelve la posición del `=`.
1262pub fn posicion_asignacion(s: &str) -> Option<usize> {
1263    let cs: Vec<char> = s.chars().collect();
1264    if cs.is_empty() { return None; }
1265    if !(cs[0].is_alphabetic() || cs[0] == '_') { return None; }
1266    let mut i = 1;
1267    while i < cs.len() && (cs[i].is_alphanumeric() || cs[i] == '_') { i += 1; }
1268    if i < cs.len() && cs[i] == '=' {
1269        return Some(s.char_indices().nth(i).map(|(p, _)| p).unwrap_or(i));
1270    }
1271    None
1272}
1273
1274/// Los escapes de una asignación de la línea de órdenes se procesan como en una cadena.
1275pub fn desescapar(s: &str) -> String {
1276    let mut out = String::new();
1277    let mut it = s.chars().peekable();
1278    while let Some(c) = it.next() {
1279        if c != '\\' { out.push(c); continue; }
1280        match it.next() {
1281            None => out.push('\\'),
1282            Some('n') => out.push('\n'), Some('t') => out.push('\t'), Some('r') => out.push('\r'),
1283            Some('\\') => out.push('\\'), Some('"') => out.push('"'), Some('/') => out.push('/'),
1284            Some('a') => out.push('\x07'), Some('b') => out.push('\x08'),
1285            Some('f') => out.push('\x0c'), Some('v') => out.push('\x0b'),
1286            Some(d @ '0'..='7') => {
1287                let mut v = d.to_digit(8).unwrap();
1288                let mut n = 1;
1289                while n < 3 {
1290                    match it.peek() {
1291                        Some(x) if x.is_digit(8) => { v = v * 8 + x.to_digit(8).unwrap(); it.next(); n += 1; }
1292                        _ => break,
1293                    }
1294                }
1295                out.push(char::from_u32(v).unwrap_or('\0'));
1296            }
1297            Some(o) => { out.push('\\'); out.push(o); }
1298        }
1299    }
1300    out
1301}
1302
1303impl Interprete {
1304    /// Acceso al array desde `main` (ENVIRON, ARGV), que es el único sitio de fuera que los toca.
1305    pub fn array_publico(&mut self, n: &str) -> Rc<RefCell<HashMap<String, Valor>>> { self.array(n) }
1306}