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 1182Shell 87Rust 63Markdown 541592 Definitions

⎇ main crates/simi/src/ast.rs Rust · 150 lines · 5.4 KB History Who touched what raw

simi: las cuatro expansiones de bash que no rompen POSIX 5ab371db3 · Sergio ·

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/// ⚠ **Sin `Copy` desde 2026-10-22**: las tres extensiones de abajo llevan datos (una palabra de
21/// reemplazo, dos expresiones aritméticas), y `Vec`/`String` no son `Copy`. El coste fue cambiar
22/// dos sitios en `expand.rs` a pasar `&ParamOp`. También se cae `Eq`, porque `Seg` sólo tiene
23/// `PartialEq`.
24#[derive(Debug, Clone, PartialEq)]
25pub enum ParamOp {
26    /// `$x` / `${x}`
27    Valor,
28    /// `${#x}`
29    Largo,
30    /// `${x:-p}` · `${x-p}` (sin dos puntos: sólo si NO está definida)
31    Defecto { dos_puntos: bool },
32    /// `${x:=p}` · `${x=p}`
33    Asigna { dos_puntos: bool },
34    /// `${x:?p}` · `${x?p}`
35    Error { dos_puntos: bool },
36    /// `${x:+p}` · `${x+p}`
37    Alterno { dos_puntos: bool },
38    /// `${x#p}` · `${x##p}`
39    QuitaPrefijo { largo: bool },
40    /// `${x%p}` · `${x%%p}`
41    QuitaSufijo { largo: bool },
42
43    // ══ EXTENSIONES QUE NO ROMPEN POSIX (2026-10-22) ══════════════════════════════════════════
44    // POSIX deja esta sintaxis **sin definir**: un script conforme nunca la escribe, así que
45    // soportarla no puede cambiarle el comportamiento a nadie. Se añaden porque el corpus las usa
46    // —`${t/unknown-/}` en `wasi-libcxx` era la ÚNICA divergencia de simi en 1339 casos— y porque
47    // rechazarlas obligaba a reescribir recetas ajenas al shell. No se añade `${!x}`: choca con el
48    // `$!` que el lexer ya trata como nombre especial, y es la menos usada de las cuatro.
49    /// `${x/pat/rep}` · `${x//pat/rep}` — sustituir. `pat` va en `arg`, el reemplazo acá.
50    /// `ancla` guarda el `#`/`%` de `${x/#pat/rep}`: tiene que separarse en el LEXER, porque un
51    /// `#` al empezar una palabra abre un COMENTARIO y el patrón llegaría vacío a la expansión
52    /// (medido: `${v/#a/X}` devolvía la cadena intacta, un no-op silencioso).
53    Sustituye { todas: bool, ancla: Option<char>, rep: Palabra },
54    /// `${x:desde}` · `${x:desde:largo}` — subcadena. Las dos son expresiones ARITMÉTICAS (se
55    /// evalúan con `arith`), y un `desde` negativo cuenta desde el final, como en bash.
56    Subcadena { desde: String, largo: Option<String> },
57    /// `${x^}` `${x^^}` `${x,}` `${x,,}` — cambiar caja (primera letra o todas).
58    Caso { mayus: bool, todas: bool },
59}
60
61pub type Palabra = Vec<Seg>;
62
63#[derive(Debug, Clone, PartialEq)]
64pub enum Redir {
65    /// `n> pal` (`trunca` falso ⇒ `>>`)
66    Salida { fd: i32, destino: Palabra, append: bool },
67    /// `n< pal`
68    Entrada { fd: i32, origen: Palabra },
69    /// `n<> pal`
70    LecturaEscritura { fd: i32, ruta: Palabra },
71    /// `n>&m`, `n<&m`, y los cierres `n>&-` / `n<&-`
72    Duplica { fd: i32, otro: DupDestino, escritura: bool },
73    /// `n<< DELIM` ya resuelto: el cuerpo viene del lexer. `expande` = el delimitador NO estaba
74    /// entrecomillado.
75    AquiDoc { fd: i32, cuerpo: Palabra, expande: bool },
76}
77
78#[derive(Debug, Clone, PartialEq)]
79pub enum DupDestino {
80    Fd(Palabra),
81    Cerrar,
82}
83
84#[derive(Debug, Clone, PartialEq)]
85pub struct Asignacion {
86    pub nombre: String,
87    pub valor: Palabra,
88}
89
90#[derive(Debug, Clone, PartialEq)]
91pub enum Orden {
92    Simple {
93        asignaciones: Vec<Asignacion>,
94        palabras: Vec<Palabra>,
95        redirs: Vec<Redir>,
96    },
97    /// `{ …; }`
98    Grupo { cuerpo: Lista, redirs: Vec<Redir> },
99    /// `( … )`
100    Subshell { cuerpo: Lista, redirs: Vec<Redir> },
101    Si {
102        ramas: Vec<(Lista, Lista)>, // (condición, cuerpo)
103        sino: Option<Lista>,
104        redirs: Vec<Redir>,
105    },
106    /// `while`/`until`
107    Bucle { hasta: bool, cond: Lista, cuerpo: Lista, redirs: Vec<Redir> },
108    Para {
109        var: String,
110        /// `None` = `for x; do` ⇒ itera sobre `"$@"`
111        palabras: Option<Vec<Palabra>>,
112        cuerpo: Lista,
113        redirs: Vec<Redir>,
114    },
115    Caso {
116        sujeto: Palabra,
117        ramas: Vec<(Vec<Palabra>, Lista)>,
118        redirs: Vec<Redir>,
119    },
120    Funcion { nombre: String, cuerpo: Box<Orden> },
121}
122
123#[derive(Debug, Clone, PartialEq)]
124pub struct Tuberia {
125    /// `! cmd | cmd`
126    pub negada: bool,
127    pub ordenes: Vec<Orden>,
128}
129
130#[derive(Debug, Clone, Copy, PartialEq, Eq)]
131pub enum Enlace {
132    Y,  // &&
133    O,  // ||
134}
135
136#[derive(Debug, Clone, PartialEq)]
137pub struct YO {
138    pub primera: Tuberia,
139    pub resto: Vec<(Enlace, Tuberia)>,
140}
141
142#[derive(Debug, Clone, Copy, PartialEq, Eq)]
143pub enum Fin {
144    /// `;` o fin de línea
145    Secuencial,
146    /// `&`
147    SegundoPlano,
148}
149
150pub type Lista = Vec<(YO, Fin)>;