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 815Rust 56Shell 37Markdown 291229 Sutichaykuna

⎇ 83720b303431ffec520a4171b21b2da651a80d31 crates/akllay/src/parser.rs Rust · 589 siq'ikuna · 24.4 KB Ñawpa willay Pi imata llamkarqan ch'uya

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

1//! Analizador sintáctico de awk: de tokens a `Programa`.
2//!
3//! La gramática de awk tiene tres rincones que obligan a escribir el parser de cierta forma, y los
4//! tres están marcados abajo donde aparecen:
5//!
6//! 1. **La concatenación no tiene operador.** `a b` es concatenar. Eso choca con el unario `-`:
7//!    en `a - b` hay una resta, no la concatenación de `a` con `-b`. Se resuelve dando a la
8//!    concatenación una precedencia propia, entre la comparación y la suma, y no dejándola empezar
9//!    por un token que pueda continuar la expresión de la izquierda.
10//! 2. **`>` es ambiguo dentro de `print`.** `print a > "f"` REDIRIGE; no compara. Por eso el parser
11//!    lleva una bandera `sin_mayor` mientras lee la lista de un `print`.
12//! 3. **`in` y `|` cambian de significado según el contexto**: `(i,j) in a`, `"cmd" | getline`.
13
14use crate::ast::*;
15use crate::lexer::{Lexer, Tok};
16use crate::regex::Regex;
17use std::rc::Rc;
18
19const BUILTINS: &[&str] = &[
20    "length", "substr", "index", "split", "sub", "gsub", "match", "sprintf", "sin", "cos", "atan2",
21    "exp", "log", "sqrt", "int", "rand", "srand", "tolower", "toupper", "system", "close", "fflush",
22];
23
24pub struct Parser {
25    toks: Vec<Tok>,
26    i: usize,
27    sin_mayor: bool,
28    rangos: usize,
29    funciones_vistas: Vec<String>,
30}
31
32impl Parser {
33    pub fn nuevo(src: &str) -> Result<Parser, String> {
34        let mut lx = Lexer::nuevo(src);
35        let mut toks = Vec::new();
36        loop {
37            let t = lx.siguiente()?;
38            let fin = t == Tok::Fin;
39            toks.push(t);
40            if fin { break; }
41        }
42        Ok(Parser { toks, i: 0, sin_mayor: false, rangos: 0, funciones_vistas: Vec::new() })
43    }
44
45    fn ver(&self) -> &Tok { &self.toks[self.i.min(self.toks.len() - 1)] }
46    fn avanza(&mut self) -> Tok { let t = self.ver().clone(); self.i += 1; t }
47    fn es_op(&self, o: &str) -> bool { matches!(self.ver(), Tok::Op(x) if x == o) }
48    fn es_clave(&self, k: &str) -> bool { matches!(self.ver(), Tok::PalabraClave(x) if x == k) }
49
50    fn come_op(&mut self, o: &str) -> bool { if self.es_op(o) { self.i += 1; true } else { false } }
51    fn come_clave(&mut self, k: &str) -> bool { if self.es_clave(k) { self.i += 1; true } else { false } }
52
53    fn exige_op(&mut self, o: &str) -> Result<(), String> {
54        if self.come_op(o) { Ok(()) } else { Err(format!("esperaba `{o}` y encontré {:?}", self.ver())) }
55    }
56
57    /// Se come los saltos de línea y los `;` que separan sentencias.
58    fn saltos(&mut self) {
59        while matches!(self.ver(), Tok::Nueva) || self.es_op(";") { self.i += 1; }
60    }
61    fn solo_saltos(&mut self) {
62        while matches!(self.ver(), Tok::Nueva) { self.i += 1; }
63    }
64
65    pub fn programa(&mut self) -> Result<Programa, String> {
66        let mut p = Programa::default();
67        self.saltos();
68        while !matches!(self.ver(), Tok::Fin) {
69            if self.es_clave("function") {
70                self.i += 1;
71                let nombre = match self.avanza() {
72                    Tok::Ident(n) | Tok::Funcion(n) => n,
73                    t => return Err(format!("nombre de función esperado, encontré {t:?}")),
74                };
75                self.exige_op("(")?;
76                let mut params = Vec::new();
77                if !self.es_op(")") {
78                    loop {
79                        match self.avanza() {
80                            Tok::Ident(n) => params.push(n),
81                            t => return Err(format!("parámetro esperado, encontré {t:?}")),
82                        }
83                        self.solo_saltos();
84                        if !self.come_op(",") { break; }
85                        self.solo_saltos();
86                    }
87                }
88                self.exige_op(")")?;
89                self.solo_saltos();
90                self.funciones_vistas.push(nombre.clone());
91                let cuerpo = self.bloque()?;
92                p.funciones.push(Funcion { nombre, params, cuerpo });
93            } else {
94                let regla = self.regla()?;
95                p.reglas.push(regla);
96            }
97            self.saltos();
98        }
99        p.rangos = self.rangos;
100        Ok(p)
101    }
102
103    fn regla(&mut self) -> Result<Regla, String> {
104        let mut rango_id = usize::MAX;
105        let patron = if self.come_clave("BEGIN") { Patron::Begin }
106            else if self.come_clave("END") { Patron::End }
107            else if self.es_op("{") { Patron::Siempre }
108            else {
109                let e1 = self.expr()?;
110                if self.come_op(",") {
111                    self.solo_saltos();
112                    let e2 = self.expr()?;
113                    rango_id = self.rangos;
114                    self.rangos += 1;
115                    Patron::Rango(e1, e2)
116                } else { Patron::Expr(e1) }
117            };
118        let accion = if self.es_op("{") { Some(self.bloque()?) } else { None };
119        Ok(Regla { patron, accion, rango_id })
120    }
121
122    fn bloque(&mut self) -> Result<Vec<Sent>, String> {
123        self.exige_op("{")?;
124        let mut v = Vec::new();
125        self.saltos();
126        while !self.es_op("}") {
127            if matches!(self.ver(), Tok::Fin) { return Err("falta `}`".into()); }
128            v.push(self.sentencia()?);
129            self.saltos();
130        }
131        self.exige_op("}")?;
132        Ok(v)
133    }
134
135    /// Una sentencia, que puede ser un bloque o una simple.
136    fn sentencia(&mut self) -> Result<Sent, String> {
137        if self.es_op("{") { return Ok(Sent::Bloque(self.bloque()?)); }
138        if self.come_op(";") { return Ok(Sent::Nada); }
139
140        if self.come_clave("if") {
141            self.exige_op("(")?;
142            let cond = self.expr()?;
143            self.exige_op(")")?;
144            self.solo_saltos();
145            let entonces = Box::new(self.sentencia()?);
146            // El `else` puede venir tras saltos de línea y tras `;`.
147            let guardado = self.i;
148            self.saltos();
149            let si_no = if self.come_clave("else") {
150                self.solo_saltos();
151                Some(Box::new(self.sentencia()?))
152            } else { self.i = guardado; None };
153            return Ok(Sent::Si(cond, entonces, si_no));
154        }
155        if self.come_clave("while") {
156            self.exige_op("(")?;
157            let cond = self.expr()?;
158            self.exige_op(")")?;
159            self.solo_saltos();
160            // `while (c) ;` es un bucle con cuerpo vacío.
161            if self.come_op(";") { return Ok(Sent::Mientras(cond, Box::new(Sent::Nada))); }
162            return Ok(Sent::Mientras(cond, Box::new(self.sentencia()?)));
163        }
164        if self.come_clave("do") {
165            self.solo_saltos();
166            let cuerpo = Box::new(self.sentencia()?);
167            self.saltos();
168            if !self.come_clave("while") { return Err("`do` sin `while`".into()); }
169            self.exige_op("(")?;
170            let cond = self.expr()?;
171            self.exige_op(")")?;
172            return Ok(Sent::HazMientras(cuerpo, cond));
173        }
174        if self.come_clave("for") { return self.para(); }
175        if self.come_clave("break") { return Ok(Sent::Corta); }
176        if self.come_clave("continue") { return Ok(Sent::Sigue); }
177        if self.come_clave("next") { return Ok(Sent::Siguiente); }
178        if self.come_clave("nextfile") { return Ok(Sent::SiguienteFichero); }
179        if self.come_clave("exit") {
180            let e = if self.fin_de_sentencia() { None } else { Some(self.expr()?) };
181            return Ok(Sent::Sale(e));
182        }
183        if self.come_clave("return") {
184            let e = if self.fin_de_sentencia() { None } else { Some(self.expr()?) };
185            return Ok(Sent::Retorna(e));
186        }
187        if self.come_clave("delete") {
188            let nombre = match self.avanza() {
189                Tok::Ident(n) | Tok::Funcion(n) => n,
190                t => return Err(format!("`delete` necesita un array, encontré {t:?}")),
191            };
192            if self.come_op("[") {
193                let mut idx = vec![self.expr()?];
194                while self.come_op(",") { idx.push(self.expr()?); }
195                self.exige_op("]")?;
196                return Ok(Sent::Borra(nombre, idx));
197            }
198            // `delete a` y `delete a()` (la forma con paréntesis que acepta gawk)
199            if self.come_op("(") { self.exige_op(")")?; }
200            return Ok(Sent::BorraTodo(nombre));
201        }
202        if self.es_clave("print") || self.es_clave("printf") {
203            let es_printf = self.es_clave("printf");
204            self.i += 1;
205            return self.print(es_printf);
206        }
207        Ok(Sent::Expr(self.expr()?))
208    }
209
210    fn fin_de_sentencia(&self) -> bool {
211        matches!(self.ver(), Tok::Nueva | Tok::Fin) || self.es_op(";") || self.es_op("}")
212    }
213
214    fn para(&mut self) -> Result<Sent, String> {
215        self.exige_op("(")?;
216        // `for (k in a)` — se distingue de `for (expr; …)` mirando dos tokens adelante.
217        if let Tok::Ident(k) = self.ver().clone() {
218            if matches!(&self.toks[self.i + 1], Tok::PalabraClave(p) if p == "in") {
219                self.i += 2;
220                let arr = match self.avanza() {
221                    Tok::Ident(a) | Tok::Funcion(a) => a,
222                    t => return Err(format!("`for (k in a)` necesita un array, encontré {t:?}")),
223                };
224                self.exige_op(")")?;
225                self.solo_saltos();
226                return Ok(Sent::ParaEn(k, arr, Box::new(self.sentencia()?)));
227            }
228        }
229        let init = if self.es_op(";") { None } else { Some(Box::new(self.sentencia()?)) };
230        self.exige_op(";")?;
231        self.solo_saltos();
232        let cond = if self.es_op(";") { None } else { Some(self.expr()?) };
233        self.exige_op(";")?;
234        self.solo_saltos();
235        let paso = if self.es_op(")") { None } else { Some(Box::new(self.sentencia()?)) };
236        self.exige_op(")")?;
237        self.solo_saltos();
238        if self.come_op(";") { return Ok(Sent::Para(init, cond, paso, Box::new(Sent::Nada))); }
239        Ok(Sent::Para(init, cond, paso, Box::new(self.sentencia()?)))
240    }
241
242    /// ⚠ RINCÓN 2: dentro de la lista de un `print`, un `>` REDIRIGE, no compara. `sin_mayor` lo
243    /// dice, y se apaga en cuanto entramos en un paréntesis —`print (a > b)` sí compara.
244    fn print(&mut self, es_printf: bool) -> Result<Sent, String> {
245        let mut args = Vec::new();
246        if !self.fin_de_sentencia() && !self.es_op(">") && !self.es_op(">>") && !self.es_op("|") {
247            let previo = self.sin_mayor;
248            self.sin_mayor = true;
249            args.push(self.expr()?);
250            while self.come_op(",") {
251                self.solo_saltos();
252                args.push(self.expr()?);
253            }
254            self.sin_mayor = previo;
255        }
256        // `print (a, b) > "f"`: una lista entre paréntesis es la lista de argumentos, no una
257        // expresión agrupada. Se detecta cuando el único argumento es un grupo con comas dentro —
258        // eso lo resuelve `expr_lista_agrupada` al construirlo.
259        let redir = if self.come_op(">") { Some(Redir::Fichero(Box::new(self.expr()?))) }
260            else if self.come_op(">>") { Some(Redir::Anexa(Box::new(self.expr()?))) }
261            else if self.come_op("|") { Some(Redir::Tuberia(Box::new(self.expr()?))) }
262            else { None };
263        Ok(if es_printf { Sent::Printf(args, redir) } else { Sent::Print(args, redir) })
264    }
265
266    // ── Expresiones, por precedencia de menor a mayor ────────────────────────────────────────────
267
268    pub fn expr(&mut self) -> Result<Expr, String> { self.ternario() }
269
270    fn ternario(&mut self) -> Result<Expr, String> {
271        let cond = self.o_logico()?;
272        if self.come_op("?") {
273            self.solo_saltos();
274            let a = self.ternario()?;
275            self.solo_saltos();
276            self.exige_op(":")?;
277            self.solo_saltos();
278            let b = self.ternario()?;
279            return Ok(Expr::Ternario(Box::new(cond), Box::new(a), Box::new(b)));
280        }
281        // La asignación se reconoce DESPUÉS: en awk `a = b ? c : d` asigna el ternario entero, y
282        // el lado izquierdo de una asignación siempre es un lugar simple.
283        if let Some(op) = self.op_asignacion() {
284            if let Some(lugar) = self.a_lugar(&cond) {
285                self.i += 1;
286                self.solo_saltos();
287                let val = self.ternario()?;
288                return Ok(match op.as_str() {
289                    "=" => Expr::Asigna(Box::new(lugar), Box::new(val)),
290                    "+=" => Expr::AsignaOp(Box::new(lugar), BinOp::Suma, Box::new(val)),
291                    "-=" => Expr::AsignaOp(Box::new(lugar), BinOp::Resta, Box::new(val)),
292                    "*=" => Expr::AsignaOp(Box::new(lugar), BinOp::Mult, Box::new(val)),
293                    "/=" => Expr::AsignaOp(Box::new(lugar), BinOp::Div, Box::new(val)),
294                    "%=" => Expr::AsignaOp(Box::new(lugar), BinOp::Modulo, Box::new(val)),
295                    "^=" => Expr::AsignaOp(Box::new(lugar), BinOp::Potencia, Box::new(val)),
296                    _ => unreachable!(),
297                });
298            }
299        }
300        Ok(cond)
301    }
302
303    fn op_asignacion(&self) -> Option<String> {
304        match self.ver() {
305            Tok::Op(o) if matches!(o.as_str(), "=" | "+=" | "-=" | "*=" | "/=" | "%=" | "^=") => Some(o.clone()),
306            _ => None,
307        }
308    }
309
310    fn a_lugar(&self, e: &Expr) -> Option<Lugar> {
311        match e {
312            Expr::Var(n) => Some(Lugar::Var(n.clone())),
313            Expr::Campo(i) => Some(Lugar::Campo(i.clone())),
314            Expr::Indice(n, idx) => Some(Lugar::Indice(n.clone(), idx.clone())),
315            _ => None,
316        }
317    }
318
319    fn o_logico(&mut self) -> Result<Expr, String> {
320        let mut izq = self.y_logico()?;
321        while self.come_op("||") {
322            self.solo_saltos();
323            let der = self.y_logico()?;
324            izq = Expr::O(Box::new(izq), Box::new(der));
325        }
326        Ok(izq)
327    }
328
329    fn y_logico(&mut self) -> Result<Expr, String> {
330        let mut izq = self.en_array()?;
331        while self.come_op("&&") {
332            self.solo_saltos();
333            let der = self.en_array()?;
334            izq = Expr::Y(Box::new(izq), Box::new(der));
335        }
336        Ok(izq)
337    }
338
339    fn en_array(&mut self) -> Result<Expr, String> {
340        let mut izq = self.coincidencia()?;
341        while self.es_clave("in") {
342            self.i += 1;
343            let arr = match self.avanza() {
344                Tok::Ident(a) | Tok::Funcion(a) => a,
345                t => return Err(format!("`in` necesita un array, encontré {t:?}")),
346            };
347            // `(i,j) in a` llega como un grupo con lista; `i in a`, como una expresión suelta.
348            izq = match izq {
349                Expr::Grupo(inner) => Expr::En(vec![*inner], arr),
350                otro => Expr::En(vec![otro], arr),
351            };
352        }
353        Ok(izq)
354    }
355
356    fn coincidencia(&mut self) -> Result<Expr, String> {
357        let mut izq = self.comparacion()?;
358        loop {
359            if self.come_op("~") {
360                let der = self.comparacion()?;
361                izq = Expr::Coincide(Box::new(izq), Box::new(der), false);
362            } else if self.come_op("!~") {
363                let der = self.comparacion()?;
364                izq = Expr::Coincide(Box::new(izq), Box::new(der), true);
365            } else { break; }
366        }
367        Ok(izq)
368    }
369
370    /// POSIX: los relacionales NO son asociativos (`a < b < c` es un error en la gramática), pero
371    /// todos los awk reales lo aceptan como `(a<b)<c`. Se acepta igual, que romper eso sólo rompe
372    /// guiones que ya funcionaban en otro lado.
373    fn comparacion(&mut self) -> Result<Expr, String> {
374        let izq = self.concatenacion()?;
375        let op = match self.ver() {
376            Tok::Op(o) => match o.as_str() {
377                "<" => CmpOp::Lt, "<=" => CmpOp::Le, "==" => CmpOp::Eq, "!=" => CmpOp::Ne,
378                ">=" if !self.sin_mayor => CmpOp::Ge,
379                ">" if !self.sin_mayor => CmpOp::Gt,
380                _ => return Ok(izq),
381            },
382            _ => return Ok(izq),
383        };
384        self.i += 1;
385        let der = self.concatenacion()?;
386        Ok(Expr::Cmp(op, Box::new(izq), Box::new(der)))
387    }
388
389    /// ⚠ RINCÓN 1: la concatenación no tiene operador. Continúa mientras lo que viene PUEDE empezar
390    /// una expresión unaria… salvo `+` y `-`, que se leen como binarios (`a - b` es una resta).
391    fn concatenacion(&mut self) -> Result<Expr, String> {
392        let mut izq = self.aditiva()?;
393        while self.empieza_valor() {
394            let der = self.aditiva()?;
395            izq = Expr::Concat(Box::new(izq), Box::new(der));
396        }
397        Ok(izq)
398    }
399
400    fn empieza_valor(&self) -> bool {
401        match self.ver() {
402            Tok::Num(_) | Tok::Cad(_) | Tok::Ere(_) | Tok::Ident(_) | Tok::Funcion(_) => true,
403            Tok::Op(o) => matches!(o.as_str(), "$" | "(" | "!" | "++" | "--"),
404            Tok::PalabraClave(k) => k == "getline",
405            _ => false,
406        }
407    }
408
409    fn aditiva(&mut self) -> Result<Expr, String> {
410        let mut izq = self.multiplicativa()?;
411        loop {
412            if self.come_op("+") { let d = self.multiplicativa()?; izq = Expr::Bin(BinOp::Suma, Box::new(izq), Box::new(d)); }
413            else if self.come_op("-") { let d = self.multiplicativa()?; izq = Expr::Bin(BinOp::Resta, Box::new(izq), Box::new(d)); }
414            else { break; }
415        }
416        Ok(izq)
417    }
418
419    fn multiplicativa(&mut self) -> Result<Expr, String> {
420        let mut izq = self.unaria()?;
421        loop {
422            if self.come_op("*") { let d = self.unaria()?; izq = Expr::Bin(BinOp::Mult, Box::new(izq), Box::new(d)); }
423            else if self.come_op("/") { let d = self.unaria()?; izq = Expr::Bin(BinOp::Div, Box::new(izq), Box::new(d)); }
424            else if self.come_op("%") { let d = self.unaria()?; izq = Expr::Bin(BinOp::Modulo, Box::new(izq), Box::new(d)); }
425            else { break; }
426        }
427        Ok(izq)
428    }
429
430    fn unaria(&mut self) -> Result<Expr, String> {
431        if self.come_op("!") { return Ok(Expr::No(Box::new(self.unaria()?))); }
432        if self.come_op("-") { return Ok(Expr::Neg(Box::new(self.unaria()?))); }
433        if self.come_op("+") { return Ok(Expr::Pos(Box::new(self.unaria()?))); }
434        self.potencia()
435    }
436
437    /// `^` asocia a la DERECHA (`2^3^2` es 512), y su operando derecho admite unario:
438    /// `2^-1` es válido.
439    fn potencia(&mut self) -> Result<Expr, String> {
440        let base = self.postfija()?;
441        if self.come_op("^") {
442            let exp = self.unaria()?;
443            return Ok(Expr::Bin(BinOp::Potencia, Box::new(base), Box::new(exp)));
444        }
445        Ok(base)
446    }
447
448    fn postfija(&mut self) -> Result<Expr, String> {
449        let e = self.primaria()?;
450        if self.es_op("++") || self.es_op("--") {
451            if let Some(l) = self.a_lugar(&e) {
452                let delta = if self.es_op("++") { 1.0 } else { -1.0 };
453                self.i += 1;
454                return Ok(Expr::Incr(Box::new(l), false, delta));
455            }
456        }
457        Ok(e)
458    }
459
460    fn primaria(&mut self) -> Result<Expr, String> {
461        // Pre-incremento.
462        if self.es_op("++") || self.es_op("--") {
463            let delta = if self.es_op("++") { 1.0 } else { -1.0 };
464            self.i += 1;
465            let obj = self.primaria()?;
466            let l = self.a_lugar(&obj).ok_or("`++` necesita una variable, un campo o un elemento")?;
467            return Ok(Expr::Incr(Box::new(l), true, delta));
468        }
469        if self.come_op("$") {
470            let idx = self.primaria()?;
471            let campo = Expr::Campo(Box::new(idx));
472            // `$1++` incrementa el campo.
473            if self.es_op("++") || self.es_op("--") {
474                let delta = if self.es_op("++") { 1.0 } else { -1.0 };
475                self.i += 1;
476                if let Some(l) = self.a_lugar(&campo) {
477                    return Ok(Expr::Incr(Box::new(l), false, delta));
478                }
479            }
480            return Ok(campo);
481        }
482        if self.come_op("(") {
483            let previo = self.sin_mayor;
484            self.sin_mayor = false; // dentro de paréntesis, `>` vuelve a comparar
485            self.solo_saltos();
486            let e = self.expr()?;
487            // `(a, b) in arr` y `print (a, b)`: una lista.
488            if self.es_op(",") {
489                let mut lista = vec![e];
490                while self.come_op(",") { self.solo_saltos(); lista.push(self.expr()?); }
491                self.exige_op(")")?;
492                self.sin_mayor = previo;
493                if self.es_clave("in") {
494                    self.i += 1;
495                    let arr = match self.avanza() {
496                        Tok::Ident(a) | Tok::Funcion(a) => a,
497                        t => return Err(format!("`in` necesita un array, encontré {t:?}")),
498                    };
499                    return Ok(Expr::En(lista, arr));
500                }
501                // Lista suelta: sólo tiene sentido como argumentos de print, y allí se desarma.
502                return Ok(Expr::Llamada("@lista".into(), lista));
503            }
504            self.exige_op(")")?;
505            self.sin_mayor = previo;
506            let g = Expr::Grupo(Box::new(e));
507            // `("cmd" | getline x)`
508            return self.quizas_tuberia(g);
509        }
510        match self.avanza() {
511            Tok::Num(n) => Ok(Expr::Num(n)),
512            Tok::Cad(s) => self.quizas_tuberia(Expr::Cad(s)),
513            Tok::Ere(r) => Ok(Expr::Ere(Rc::new(Regex::nueva(&r)?))),
514            // ⚠ `length` sin paréntesis es el ÚNICO builtin al que POSIX se lo permite, y tiene
515            // que ir ANTES del brazo general de identificador — si no, se lee como una variable
516            // llamada `length`, que siempre vale vacío. El banco lo cazó con `{print length}`.
517            Tok::Ident(n) if n == "length" && !self.es_op("[") => Ok(Expr::Builtin("length".into(), vec![])),
518            Tok::Ident(n) => {
519                if self.come_op("[") {
520                    let mut idx = vec![self.expr()?];
521                    while self.come_op(",") { idx.push(self.expr()?); }
522                    self.exige_op("]")?;
523                    return Ok(Expr::Indice(n, idx));
524                }
525                Ok(Expr::Var(n))
526            }
527            Tok::Funcion(n) => {
528                self.exige_op("(")?;
529                let previo = self.sin_mayor;
530                self.sin_mayor = false;
531                let mut args = Vec::new();
532                self.solo_saltos();
533                if !self.es_op(")") {
534                    loop {
535                        args.push(self.expr()?);
536                        self.solo_saltos();
537                        if !self.come_op(",") { break; }
538                        self.solo_saltos();
539                    }
540                }
541                self.exige_op(")")?;
542                self.sin_mayor = previo;
543                if BUILTINS.contains(&n.as_str()) { Ok(Expr::Builtin(n, args)) }
544                else { Ok(Expr::Llamada(n, args)) }
545            }
546            Tok::PalabraClave(k) if k == "getline" => {
547                // `getline`, `getline var`, `getline < f`, `getline var < f`
548                let lugar = self.lugar_opcional()?;
549                if self.come_op("<") {
550                    let f = self.concatenacion()?;
551                    return Ok(Expr::Getline(Getline::DeFichero(lugar, Box::new(f))));
552                }
553                Ok(Expr::Getline(Getline::Simple(lugar)))
554            }
555            t => Err(format!("expresión esperada, encontré {t:?}")),
556        }
557    }
558
559    /// `expr | getline [lugar]`
560    fn quizas_tuberia(&mut self, izq: Expr) -> Result<Expr, String> {
561        if self.es_op("|") && matches!(&self.toks[self.i + 1], Tok::PalabraClave(k) if k == "getline") {
562            self.i += 2;
563            let lugar = self.lugar_opcional()?;
564            return Ok(Expr::Getline(Getline::DeTuberia(Box::new(izq), lugar)));
565        }
566        Ok(izq)
567    }
568
569    fn lugar_opcional(&mut self) -> Result<Option<Box<Lugar>>, String> {
570        match self.ver().clone() {
571            Tok::Ident(n) => {
572                self.i += 1;
573                if self.come_op("[") {
574                    let mut idx = vec![self.expr()?];
575                    while self.come_op(",") { idx.push(self.expr()?); }
576                    self.exige_op("]")?;
577                    return Ok(Some(Box::new(Lugar::Indice(n, idx))));
578                }
579                Ok(Some(Box::new(Lugar::Var(n))))
580            }
581            Tok::Op(o) if o == "$" => {
582                self.i += 1;
583                let idx = self.primaria()?;
584                Ok(Some(Box::new(Lugar::Campo(Box::new(idx)))))
585            }
586            _ => Ok(None),
587        }
588    }
589}