//! El árbol. Nada de estado ni de ejecución acá: sólo la forma del lenguaje. /// Un trozo de palabra. La palabra se arma en el lexer y se expande en `expand`, en ese orden: /// tilde → parámetro/orden/aritmética → división en campos → rutas → quita de comillas. #[derive(Debug, Clone, PartialEq)] pub enum Seg { /// Texto literal. `comillado` = vino de comillas ⇒ **no** se divide en campos ni se globea. Lit { texto: String, comillado: bool }, /// `~` al principio de una palabra (o de un campo tras `:` en una asignación). Tilde, /// `$x`, `${x}`, `${x:-def}`, `${#x}`, `${x%%pat}`… `comillado` va aparte porque /// `"$x"` no se divide y `$x` sí. Param { nombre: String, op: ParamOp, arg: Vec, comillado: bool }, /// `$( … )` y `` ` … ` ``. El cuerpo es shell otra vez y se parsea al expandir. CmdSub { cuerpo: String, comillado: bool }, /// `$(( … ))`. Arith { expr: String, comillado: bool }, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ParamOp { /// `$x` / `${x}` Valor, /// `${#x}` Largo, /// `${x:-p}` · `${x-p}` (sin dos puntos: sólo si NO está definida) Defecto { dos_puntos: bool }, /// `${x:=p}` · `${x=p}` Asigna { dos_puntos: bool }, /// `${x:?p}` · `${x?p}` Error { dos_puntos: bool }, /// `${x:+p}` · `${x+p}` Alterno { dos_puntos: bool }, /// `${x#p}` · `${x##p}` QuitaPrefijo { largo: bool }, /// `${x%p}` · `${x%%p}` QuitaSufijo { largo: bool }, } pub type Palabra = Vec; #[derive(Debug, Clone, PartialEq)] pub enum Redir { /// `n> pal` (`trunca` falso ⇒ `>>`) Salida { fd: i32, destino: Palabra, append: bool }, /// `n< pal` Entrada { fd: i32, origen: Palabra }, /// `n<> pal` LecturaEscritura { fd: i32, ruta: Palabra }, /// `n>&m`, `n<&m`, y los cierres `n>&-` / `n<&-` Duplica { fd: i32, otro: DupDestino, escritura: bool }, /// `n<< DELIM` ya resuelto: el cuerpo viene del lexer. `expande` = el delimitador NO estaba /// entrecomillado. AquiDoc { fd: i32, cuerpo: Palabra, expande: bool }, } #[derive(Debug, Clone, PartialEq)] pub enum DupDestino { Fd(Palabra), Cerrar, } #[derive(Debug, Clone, PartialEq)] pub struct Asignacion { pub nombre: String, pub valor: Palabra, } #[derive(Debug, Clone, PartialEq)] pub enum Orden { Simple { asignaciones: Vec, palabras: Vec, redirs: Vec, }, /// `{ …; }` Grupo { cuerpo: Lista, redirs: Vec }, /// `( … )` Subshell { cuerpo: Lista, redirs: Vec }, Si { ramas: Vec<(Lista, Lista)>, // (condición, cuerpo) sino: Option, redirs: Vec, }, /// `while`/`until` Bucle { hasta: bool, cond: Lista, cuerpo: Lista, redirs: Vec }, Para { var: String, /// `None` = `for x; do` ⇒ itera sobre `"$@"` palabras: Option>, cuerpo: Lista, redirs: Vec, }, Caso { sujeto: Palabra, ramas: Vec<(Vec, Lista)>, redirs: Vec, }, Funcion { nombre: String, cuerpo: Box }, } #[derive(Debug, Clone, PartialEq)] pub struct Tuberia { /// `! cmd | cmd` pub negada: bool, pub ordenes: Vec, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Enlace { Y, // && O, // || } #[derive(Debug, Clone, PartialEq)] pub struct YO { pub primera: Tuberia, pub resto: Vec<(Enlace, Tuberia)>, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Fin { /// `;` o fin de línea Secuencial, /// `&` SegundoPlano, } pub type Lista = Vec<(YO, Fin)>;