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 1181Shell 82Rust 62Markdown 521515 Sutichaykuna

⎇ main crates/simi/src/arith.rs Rust · 389 siq'ikuna · 12.3 KB Ñawpa willay Pi imata llamkarqan ch'uya

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

1//! `$(( … ))`. Aritmética entera con signo (POSIX §2.6.4: la misma que el C de `long`), con
2//! precedencia completa, ternario y asignación. Propio: es un descenso recursivo de 150 líneas.
3//!
4//! La división por cero es un ERROR, no un 0 silencioso — un `$((x/0))` que devuelve 0 es
5//! exactamente la clase de fallo que llega hasta el final diciendo que todo fue bien.
6
7type Num = i64;
8
9pub struct Contexto<'a> {
10    /// Lee una variable (vacía si no existe), como manda POSIX para la aritmética.
11    pub leer: &'a dyn Fn(&str) -> String,
12    /// Asigna (para `x = …`, `x += …`).
13    pub asignar: &'a mut dyn FnMut(&str, &str),
14}
15
16pub fn evaluar(expr: &str, ctx: &mut Contexto) -> Result<Num, String> {
17    let mut p = P { cs: expr.chars().collect(), i: 0, ctx };
18    p.espacios();
19    let v = p.ternario()?;
20    p.espacios();
21    if p.i < p.cs.len() {
22        return Err(format!("aritmética: sobra `{}`", p.cs[p.i..].iter().collect::<String>()));
23    }
24    Ok(v)
25}
26
27struct P<'a, 'b> {
28    cs: Vec<char>,
29    i: usize,
30    ctx: &'a mut Contexto<'b>,
31}
32
33impl P<'_, '_> {
34    fn espacios(&mut self) {
35        while matches!(self.cs.get(self.i), Some(c) if c.is_whitespace()) {
36            self.i += 1;
37        }
38    }
39
40    fn come(&mut self, s: &str) -> bool {
41        let n = s.chars().count();
42        if self.cs[self.i..].starts_with(&s.chars().collect::<Vec<_>>()[..]) {
43            // no confundir `=` con `==`, ni `<` con `<<`
44            self.i += n;
45            self.espacios();
46            true
47        } else {
48            false
49        }
50    }
51
52    fn mira(&self, s: &str) -> bool {
53        self.cs[self.i..].starts_with(&s.chars().collect::<Vec<_>>()[..])
54    }
55
56    fn ternario(&mut self) -> Result<Num, String> {
57        let c = self.o_logico()?;
58        if self.come("?") {
59            let a = self.ternario()?;
60            if !self.come(":") {
61                return Err("aritmética: falta `:` del ternario".into());
62            }
63            let b = self.ternario()?;
64            return Ok(if c != 0 { a } else { b });
65        }
66        Ok(c)
67    }
68
69    fn o_logico(&mut self) -> Result<Num, String> {
70        let mut a = self.y_logico()?;
71        while self.mira("||") {
72            self.come("||");
73            let b = self.y_logico()?;
74            a = ((a != 0) || (b != 0)) as Num;
75        }
76        Ok(a)
77    }
78
79    fn y_logico(&mut self) -> Result<Num, String> {
80        let mut a = self.o_bits()?;
81        while self.mira("&&") {
82            self.come("&&");
83            let b = self.o_bits()?;
84            a = ((a != 0) && (b != 0)) as Num;
85        }
86        Ok(a)
87    }
88
89    fn o_bits(&mut self) -> Result<Num, String> {
90        let mut a = self.xor_bits()?;
91        while self.mira("|") && !self.mira("||") {
92            self.come("|");
93            a |= self.xor_bits()?;
94        }
95        Ok(a)
96    }
97
98    fn xor_bits(&mut self) -> Result<Num, String> {
99        let mut a = self.y_bits()?;
100        while self.mira("^") {
101            self.come("^");
102            a ^= self.y_bits()?;
103        }
104        Ok(a)
105    }
106
107    fn y_bits(&mut self) -> Result<Num, String> {
108        let mut a = self.igualdad()?;
109        while self.mira("&") && !self.mira("&&") {
110            self.come("&");
111            a &= self.igualdad()?;
112        }
113        Ok(a)
114    }
115
116    fn igualdad(&mut self) -> Result<Num, String> {
117        let mut a = self.relacional()?;
118        loop {
119            if self.mira("==") {
120                self.come("==");
121                a = (a == self.relacional()?) as Num;
122            } else if self.mira("!=") {
123                self.come("!=");
124                a = (a != self.relacional()?) as Num;
125            } else {
126                return Ok(a);
127            }
128        }
129    }
130
131    fn relacional(&mut self) -> Result<Num, String> {
132        let mut a = self.desplazamiento()?;
133        loop {
134            if self.mira("<=") {
135                self.come("<=");
136                a = (a <= self.desplazamiento()?) as Num;
137            } else if self.mira(">=") {
138                self.come(">=");
139                a = (a >= self.desplazamiento()?) as Num;
140            } else if self.mira("<") && !self.mira("<<") {
141                self.come("<");
142                a = (a < self.desplazamiento()?) as Num;
143            } else if self.mira(">") && !self.mira(">>") {
144                self.come(">");
145                a = (a > self.desplazamiento()?) as Num;
146            } else {
147                return Ok(a);
148            }
149        }
150    }
151
152    fn desplazamiento(&mut self) -> Result<Num, String> {
153        let mut a = self.aditivo()?;
154        loop {
155            if self.mira("<<") {
156                self.come("<<");
157                a = a.wrapping_shl(self.aditivo()? as u32);
158            } else if self.mira(">>") {
159                self.come(">>");
160                a = a.wrapping_shr(self.aditivo()? as u32);
161            } else {
162                return Ok(a);
163            }
164        }
165    }
166
167    fn aditivo(&mut self) -> Result<Num, String> {
168        let mut a = self.multiplicativo()?;
169        loop {
170            self.espacios();
171            if self.mira("+") && !self.mira("++") {
172                self.come("+");
173                a = a.wrapping_add(self.multiplicativo()?);
174            } else if self.mira("-") && !self.mira("--") {
175                self.come("-");
176                a = a.wrapping_sub(self.multiplicativo()?);
177            } else {
178                return Ok(a);
179            }
180        }
181    }
182
183    fn multiplicativo(&mut self) -> Result<Num, String> {
184        let mut a = self.unario()?;
185        loop {
186            self.espacios();
187            if self.mira("*") {
188                self.come("*");
189                a = a.wrapping_mul(self.unario()?);
190            } else if self.mira("/") {
191                self.come("/");
192                let b = self.unario()?;
193                if b == 0 {
194                    return Err("aritmética: división por cero".into());
195                }
196                a = a.wrapping_div(b);
197            } else if self.mira("%") {
198                self.come("%");
199                let b = self.unario()?;
200                if b == 0 {
201                    return Err("aritmética: módulo por cero".into());
202                }
203                a = a.wrapping_rem(b);
204            } else {
205                return Ok(a);
206            }
207        }
208    }
209
210    fn unario(&mut self) -> Result<Num, String> {
211        self.espacios();
212        if self.come("!") {
213            return Ok((self.unario()? == 0) as Num);
214        }
215        if self.come("~") {
216            return Ok(!self.unario()?);
217        }
218        if self.mira("-") && !self.mira("--") {
219            self.come("-");
220            return Ok(self.unario()?.wrapping_neg());
221        }
222        if self.mira("+") && !self.mira("++") {
223            self.come("+");
224            return self.unario();
225        }
226        self.primario()
227    }
228
229    fn primario(&mut self) -> Result<Num, String> {
230        self.espacios();
231        if self.come("(") {
232            let v = self.ternario()?;
233            if !self.come(")") {
234                return Err("aritmética: falta `)`".into());
235            }
236            return Ok(v);
237        }
238        match self.cs.get(self.i) {
239            Some(c) if c.is_ascii_digit() => self.numero(),
240            Some(c) if *c == '_' || c.is_ascii_alphabetic() => {
241                let mut n = String::new();
242                while let Some(d) = self.cs.get(self.i) {
243                    if *d == '_' || d.is_ascii_alphanumeric() {
244                        n.push(*d);
245                        self.i += 1;
246                    } else {
247                        break;
248                    }
249                }
250                self.espacios();
251                // asignaciones
252                for (op, f) in [
253                    ("+=", 1i32),
254                    ("-=", 2),
255                    ("*=", 3),
256                    ("/=", 4),
257                    ("%=", 5),
258                ] {
259                    if self.mira(op) {
260                        self.come(op);
261                        let b = self.ternario()?;
262                        let a = self.valor_de(&n)?;
263                        let v = match f {
264                            1 => a.wrapping_add(b),
265                            2 => a.wrapping_sub(b),
266                            3 => a.wrapping_mul(b),
267                            4 => {
268                                if b == 0 {
269                                    return Err("aritmética: división por cero".into());
270                                }
271                                a.wrapping_div(b)
272                            }
273                            _ => {
274                                if b == 0 {
275                                    return Err("aritmética: módulo por cero".into());
276                                }
277                                a.wrapping_rem(b)
278                            }
279                        };
280                        (self.ctx.asignar)(&n, &v.to_string());
281                        return Ok(v);
282                    }
283                }
284                if self.mira("=") && !self.mira("==") {
285                    self.come("=");
286                    let v = self.ternario()?;
287                    (self.ctx.asignar)(&n, &v.to_string());
288                    return Ok(v);
289                }
290                self.valor_de(&n)
291            }
292            Some(c) => Err(format!("aritmética: no esperaba `{c}`")),
293            None => Err("aritmética: expresión incompleta".into()),
294        }
295    }
296
297    fn valor_de(&self, n: &str) -> Result<Num, String> {
298        let s = (self.ctx.leer)(n);
299        let s = s.trim();
300        if s.is_empty() {
301            return Ok(0);
302        }
303        interpretar(s).ok_or_else(|| format!("aritmética: `{n}` no es un número (`{s}`)"))
304    }
305
306    fn numero(&mut self) -> Result<Num, String> {
307        let inicio = self.i;
308        while matches!(self.cs.get(self.i), Some(c) if c.is_ascii_alphanumeric()) {
309            self.i += 1;
310        }
311        let s: String = self.cs[inicio..self.i].iter().collect();
312        self.espacios();
313        interpretar(&s).ok_or_else(|| format!("aritmética: `{s}` no es un número"))
314    }
315}
316
317/// POSIX: decimal, octal con `0` delante, hexadecimal con `0x`.
318fn interpretar(s: &str) -> Option<Num> {
319    let (negativo, s) = match s.strip_prefix('-') {
320        Some(r) => (true, r),
321        None => (false, s),
322    };
323    let v = if let Some(h) = s.strip_prefix("0x").or_else(|| s.strip_prefix("0X")) {
324        Num::from_str_radix(h, 16).ok()?
325    } else if s.len() > 1 && s.starts_with('0') {
326        Num::from_str_radix(&s[1..], 8).ok()?
327    } else {
328        s.parse().ok()?
329    };
330    Some(if negativo { -v } else { v })
331}
332
333#[cfg(test)]
334mod pruebas {
335    use super::*;
336    use std::cell::RefCell;
337    use std::collections::HashMap;
338
339    fn ev(expr: &str, vars: &[(&str, &str)]) -> Result<Num, String> {
340        let mapa: RefCell<HashMap<String, String>> =
341            RefCell::new(vars.iter().map(|(k, v)| (k.to_string(), v.to_string())).collect());
342        let leer = |n: &str| mapa.borrow().get(n).cloned().unwrap_or_default();
343        let mut asignar = |n: &str, v: &str| {
344            mapa.borrow_mut().insert(n.to_string(), v.to_string());
345        };
346        let mut ctx = Contexto { leer: &leer, asignar: &mut asignar };
347        evaluar(expr, &mut ctx)
348    }
349
350    #[test]
351    fn lo_que_usan_las_cards() {
352        // `i=$((i+1))` es la única aritmética de las cards medidas
353        assert_eq!(ev("i+1", &[("i", "0")]).unwrap(), 1);
354        assert_eq!(ev("i+1", &[("i", "99")]).unwrap(), 100);
355        // una variable sin definir vale 0
356        assert_eq!(ev("n+1", &[]).unwrap(), 1);
357    }
358
359    #[test]
360    fn precedencia_y_parentesis() {
361        assert_eq!(ev("2+3*4", &[]).unwrap(), 14);
362        assert_eq!(ev("(2+3)*4", &[]).unwrap(), 20);
363        assert_eq!(ev("1 << 4", &[]).unwrap(), 16);
364        assert_eq!(ev("7 % 4", &[]).unwrap(), 3);
365        assert_eq!(ev("-3 + 1", &[]).unwrap(), -2);
366        assert_eq!(ev("!0", &[]).unwrap(), 1);
367        assert_eq!(ev("1 < 2 && 3 >= 3", &[]).unwrap(), 1);
368        assert_eq!(ev("0 ? 10 : 20", &[]).unwrap(), 20);
369    }
370
371    #[test]
372    fn bases() {
373        assert_eq!(ev("0x10", &[]).unwrap(), 16);
374        assert_eq!(ev("010", &[]).unwrap(), 8);
375        assert_eq!(ev("10", &[]).unwrap(), 10);
376    }
377
378    #[test]
379    fn division_por_cero_es_error_no_cero() {
380        assert!(ev("1/0", &[]).is_err());
381        assert!(ev("1%0", &[]).is_err());
382    }
383
384    #[test]
385    fn asigna() {
386        assert_eq!(ev("x = 3 + 4", &[]).unwrap(), 7);
387        assert_eq!(ev("x += 2", &[("x", "5")]).unwrap(), 7);
388    }
389}