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…
simi: el /bin/sh propio del producto — 4448 líneas, sólo libc, y CERO divergencias contra el ash
1//! El árbol. Nada de estado ni de ejecución acá: sólo la forma del lenguaje.
2
3/// Un trozo de palabra. La palabra se arma en el lexer y se expande en `expand`, en ese orden:
4/// tilde → parámetro/orden/aritmética → división en campos → rutas → quita de comillas.
5#[derive(Debug, Clone, PartialEq)]
6pub enum Seg {
7 /// Texto literal. `comillado` = vino de comillas ⇒ **no** se divide en campos ni se globea.
8 Lit { texto: String, comillado: bool },
9 /// `~` al principio de una palabra (o de un campo tras `:` en una asignación).
10 Tilde,
11 /// `$x`, `${x}`, `${x:-def}`, `${#x}`, `${x%%pat}`… `comillado` va aparte porque
12 /// `"$x"` no se divide y `$x` sí.
13 Param { nombre: String, op: ParamOp, arg: Vec<Seg>, comillado: bool },
14 /// `$( … )` y `` ` … ` ``. El cuerpo es shell otra vez y se parsea al expandir.
15 CmdSub { cuerpo: String, comillado: bool },
16 /// `$(( … ))`.
17 Arith { expr: String, comillado: bool },
18}
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21pub enum ParamOp {
22 /// `$x` / `${x}`
23 Valor,
24 /// `${#x}`
25 Largo,
26 /// `${x:-p}` · `${x-p}` (sin dos puntos: sólo si NO está definida)
27 Defecto { dos_puntos: bool },
28 /// `${x:=p}` · `${x=p}`
29 Asigna { dos_puntos: bool },
30 /// `${x:?p}` · `${x?p}`
31 Error { dos_puntos: bool },
32 /// `${x:+p}` · `${x+p}`
33 Alterno { dos_puntos: bool },
34 /// `${x#p}` · `${x##p}`
35 QuitaPrefijo { largo: bool },
36 /// `${x%p}` · `${x%%p}`
37 QuitaSufijo { largo: bool },
38}
39
40pub type Palabra = Vec<Seg>;
41
42#[derive(Debug, Clone, PartialEq)]
43pub enum Redir {
44 /// `n> pal` (`trunca` falso ⇒ `>>`)
45 Salida { fd: i32, destino: Palabra, append: bool },
46 /// `n< pal`
47 Entrada { fd: i32, origen: Palabra },
48 /// `n<> pal`
49 LecturaEscritura { fd: i32, ruta: Palabra },
50 /// `n>&m`, `n<&m`, y los cierres `n>&-` / `n<&-`
51 Duplica { fd: i32, otro: DupDestino, escritura: bool },
52 /// `n<< DELIM` ya resuelto: el cuerpo viene del lexer. `expande` = el delimitador NO estaba
53 /// entrecomillado.
54 AquiDoc { fd: i32, cuerpo: Palabra, expande: bool },
55}
56
57#[derive(Debug, Clone, PartialEq)]
58pub enum DupDestino {
59 Fd(Palabra),
60 Cerrar,
61}
62
63#[derive(Debug, Clone, PartialEq)]
64pub struct Asignacion {
65 pub nombre: String,
66 pub valor: Palabra,
67}
68
69#[derive(Debug, Clone, PartialEq)]
70pub enum Orden {
71 Simple {
72 asignaciones: Vec<Asignacion>,
73 palabras: Vec<Palabra>,
74 redirs: Vec<Redir>,
75 },
76 /// `{ …; }`
77 Grupo { cuerpo: Lista, redirs: Vec<Redir> },
78 /// `( … )`
79 Subshell { cuerpo: Lista, redirs: Vec<Redir> },
80 Si {
81 ramas: Vec<(Lista, Lista)>, // (condición, cuerpo)
82 sino: Option<Lista>,
83 redirs: Vec<Redir>,
84 },
85 /// `while`/`until`
86 Bucle { hasta: bool, cond: Lista, cuerpo: Lista, redirs: Vec<Redir> },
87 Para {
88 var: String,
89 /// `None` = `for x; do` ⇒ itera sobre `"$@"`
90 palabras: Option<Vec<Palabra>>,
91 cuerpo: Lista,
92 redirs: Vec<Redir>,
93 },
94 Caso {
95 sujeto: Palabra,
96 ramas: Vec<(Vec<Palabra>, Lista)>,
97 redirs: Vec<Redir>,
98 },
99 Funcion { nombre: String, cuerpo: Box<Orden> },
100}
101
102#[derive(Debug, Clone, PartialEq)]
103pub struct Tuberia {
104 /// `! cmd | cmd`
105 pub negada: bool,
106 pub ordenes: Vec<Orden>,
107}
108
109#[derive(Debug, Clone, Copy, PartialEq, Eq)]
110pub enum Enlace {
111 Y, // &&
112 O, // ||
113}
114
115#[derive(Debug, Clone, PartialEq)]
116pub struct YO {
117 pub primera: Tuberia,
118 pub resto: Vec<(Enlace, Tuberia)>,
119}
120
121#[derive(Debug, Clone, Copy, PartialEq, Eq)]
122pub enum Fin {
123 /// `;` o fin de línea
124 Secuencial,
125 /// `&`
126 SegundoPlano,
127}
128
129pub type Lista = Vec<(YO, Fin)>;