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//! `$(( … ))`. 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}