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…
el awk se llama akllay: `qillqa` estaba TOMADO en tawasuyu, y no lo comprobé
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}