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 699Rust 56Shell 36Markdown 281227 Definitions

⎇ 6809ef4be28f606aae856890a74667187ff75b05 crates/simi/src/shell.rs Rust · 168 lines · 5.6 KB History Who touched what raw

simi: el /bin/sh propio del producto — 4448 líneas, sólo libc, y CERO divergencias contra el ash 6809ef4be · Sergio ·

1//! El estado del shell y el flujo de control. Nada de sintaxis acá.
2
3use std::collections::HashMap;
4use std::sync::atomic::AtomicBool;
5
6use crate::ast::Orden;
7
8/// Señales atrapadas que llegaron y todavía no se ejecutaron. Un handler de señal sólo puede tocar
9/// cosas async-signal-safe, así que **lo único que hace es prender una bandera**; la acción del
10/// `trap` corre entre órdenes, que es donde POSIX dice que corre.
11pub static LLEGO_SENAL: [AtomicBool; 32] = [const { AtomicBool::new(false) }; 32];
12
13#[derive(Debug, Clone, PartialEq)]
14pub enum Flujo {
15    Normal,
16    Break(u32),
17    Continue(u32),
18    /// `return` dentro de una función o de un `.`
19    Return,
20    /// `exit` — se propaga hasta arriba
21    Exit(i32),
22}
23
24#[derive(Default, Clone)]
25pub struct Opciones {
26    /// `set -e`
27    pub errexit: bool,
28    /// `set -u`
29    pub nounset: bool,
30    /// `set -x`
31    pub xtrace: bool,
32    /// `set -n`: parsear y no ejecutar
33    pub noexec: bool,
34    /// `set -f`: sin expansión de rutas
35    pub noglob: bool,
36}
37
38pub struct Shell {
39    /// Variables del shell. `exportada` decide si viaja en el `environ` de los hijos.
40    pub vars: HashMap<String, (String, bool)>,
41    pub funcs: HashMap<String, Orden>,
42    /// `$1`, `$2`…
43    pub params: Vec<String>,
44    /// `$0`
45    pub nombre: String,
46    pub estado: i32,
47    pub opts: Opciones,
48    /// `trap` por señal: `"EXIT"` o el nombre/número de la señal.
49    pub traps: HashMap<String, String>,
50    /// PID del último `&`
51    pub ultimo_fondo: Option<i32>,
52    /// profundidad de bucles, para `break`/`continue`
53    pub bucles: u32,
54    /// profundidad de funciones, para `return` y `local`
55    pub funcion_honda: u32,
56    /// pila de variables locales a restaurar al salir de cada función
57    pub locales: Vec<Vec<(String, Option<(String, bool)>)>>,
58    /// Profundidad de contextos donde un estado ≠ 0 NO dispara `set -e`: condiciones de `if` y
59    /// `while`, operandos no finales de `&&`/`||`, y `!`. Es un contador DINÁMICO a propósito: así
60    /// la suspensión entra sola en el cuerpo de una función llamada desde una condición, que es lo
61    /// que POSIX manda y lo que el banco diferencial midió roto en la primera versión
62    /// (`f() { false; echo sigo; }; f || true` con `set -e`: el ash sigue, simi salía).
63    pub errexit_suspendido: u32,
64    /// ¿este proceso es un subshell? Decide si el `trap EXIT` ya corrió.
65    pub es_subshell: bool,
66    pub trap_exit_corrido: bool,
67}
68
69impl Shell {
70    pub fn nuevo() -> Self {
71        let mut vars = HashMap::new();
72        for (k, v) in std::env::vars() {
73            vars.insert(k, (v, true));
74        }
75        vars.entry("IFS".into()).or_insert((" \t\n".into(), false));
76        if let Ok(d) = std::env::current_dir() {
77            vars.insert("PWD".into(), (d.to_string_lossy().into_owned(), true));
78        }
79        vars.insert("PPID".into(), (unsafe { libc::getppid() }.to_string(), false));
80        Shell {
81            vars,
82            funcs: HashMap::new(),
83            params: Vec::new(),
84            nombre: "simi".into(),
85            estado: 0,
86            opts: Opciones::default(),
87            traps: HashMap::new(),
88            ultimo_fondo: None,
89            bucles: 0,
90            funcion_honda: 0,
91            locales: Vec::new(),
92            errexit_suspendido: 0,
93            es_subshell: false,
94            trap_exit_corrido: false,
95        }
96    }
97
98    pub fn leer(&self, nombre: &str) -> Option<String> {
99        match nombre {
100            "?" => return Some(self.estado.to_string()),
101            "#" => return Some(self.params.len().to_string()),
102            "$" => return Some(unsafe { libc::getpid() }.to_string()),
103            "!" => return Some(self.ultimo_fondo.unwrap_or(0).to_string()),
104            "0" => return Some(self.nombre.clone()),
105            "-" => {
106                let mut s = String::new();
107                if self.opts.errexit {
108                    s.push('e');
109                }
110                if self.opts.nounset {
111                    s.push('u');
112                }
113                if self.opts.xtrace {
114                    s.push('x');
115                }
116                return Some(s);
117            }
118            _ => {}
119        }
120        if let Ok(n) = nombre.parse::<usize>() {
121            if n >= 1 {
122                return self.params.get(n - 1).cloned();
123            }
124        }
125        self.vars.get(nombre).map(|(v, _)| v.clone())
126    }
127
128    pub fn ifs(&self) -> String {
129        self.leer("IFS").unwrap_or_else(|| " \t\n".to_string())
130    }
131
132    pub fn asignar(&mut self, nombre: &str, valor: &str) {
133        let exportada = self.vars.get(nombre).map(|(_, e)| *e).unwrap_or(false);
134        self.vars.insert(nombre.to_string(), (valor.to_string(), exportada));
135    }
136
137    pub fn exportar(&mut self, nombre: &str) {
138        if let Some(e) = self.vars.get_mut(nombre) {
139            e.1 = true;
140        } else {
141            self.vars.insert(nombre.to_string(), (String::new(), true));
142        }
143    }
144
145    /// Marca una variable como local a la función en curso, guardando su valor previo.
146    pub fn declarar_local(&mut self, nombre: &str) {
147        if let Some(marco) = self.locales.last_mut() {
148            let previo = self.vars.get(nombre).cloned();
149            marco.push((nombre.to_string(), previo));
150        }
151    }
152
153    /// El `environ` para un hijo: sólo las exportadas.
154    pub fn entorno(&self) -> Vec<String> {
155        let mut v: Vec<String> = self
156            .vars
157            .iter()
158            .filter(|(_, (_, e))| *e)
159            .map(|(k, (val, _))| format!("{k}={val}"))
160            .collect();
161        v.sort();
162        v
163    }
164
165    pub fn error(&self, msg: &str) {
166        eprintln!("{}: {}", self.nombre, msg);
167    }
168}