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…
qillqa: el awk propio — 3531 líneas, sólo libc, y CERO divergencias contra dos oráculos
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(®);
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 ®las {
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(®);
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}