gioser

tawasuyu/brahman

Rust 570TOML 215Markdown 68JSON 198261 Definiciones

⎇ 4d9ce11b1e444430e6e2bcc0a72baa1d40a4527c crates/modules/charka/charka-shadow/src/interp.rs Rust · 497 líneas · 16.5 KB Historia Quién tocó qué crudo

feat(charka): charka-shadow — validador en sombra + corpus COBOL 4d9ce11b1 · sergio ·

1//! El intérprete del IR: la ejecución «sombra» del programa COBOL.
2//!
3//! Ejecuta el [`Ir`] directamente sobre los tipos de `charka-runtime`,
4//! sin compilar nada. Es una segunda ruta de ejecución, independiente
5//! del código que emite `charka-codegen` — eso es lo que lo hace un
6//! validador: si el intérprete y el transpilado divergen, hay un bug.
7
8use std::collections::HashMap;
9
10use charka_ir::{
11    BinOp, CmpOp, Cond, Expr, Figurative, Ir, Operand, Perform, PerformControl, PerformTarget, Stmt,
12};
13use charka_runtime::{cobol_text_cmp, Decimal, Rounding};
14
15use crate::field::{build_fields, Cell};
16
17/// Tope de pasos: corta los bucles que no terminan (un `PERFORM UNTIL`
18/// con una condición que nunca se cumple) en vez de colgarse.
19const STEP_BUDGET: u64 = 5_000_000;
20
21/// Escala intermedia de la división dentro de una expresión.
22const DIV_SCALE: u8 = 9;
23
24/// El resultado de ejecutar un statement: cómo sigue el control.
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
26enum Flow {
27    /// Sigue con el statement siguiente.
28    Normal,
29    /// Sale del párrafo actual (`EXIT`).
30    Exit,
31    /// Termina el programa (`STOP RUN`, `GOBACK`, tope de pasos).
32    Stop,
33}
34
35/// La máquina sombra: el estado y el motor de ejecución.
36pub(crate) struct Machine<'a> {
37    ir: &'a Ir,
38    fields: HashMap<String, Cell>,
39    para_index: HashMap<String, usize>,
40    pub output: Vec<String>,
41    budget: u64,
42    pub step_limit_hit: bool,
43    pub stopped: bool,
44}
45
46impl<'a> Machine<'a> {
47    /// Prepara la máquina: aplana los datos e indexa los párrafos.
48    pub(crate) fn new(ir: &'a Ir) -> Self {
49        let mut para_index = HashMap::new();
50        for (i, proc) in ir.procedures.iter().enumerate() {
51            para_index.entry(proc.name.to_uppercase()).or_insert(i);
52        }
53        Self {
54            ir,
55            fields: build_fields(&ir.data),
56            para_index,
57            output: Vec::new(),
58            budget: STEP_BUDGET,
59            step_limit_hit: false,
60            stopped: false,
61        }
62    }
63
64    /// Corre el programa: encadena los párrafos en orden (el «caer» de
65    /// COBOL) hasta un `STOP RUN` o el final.
66    pub(crate) fn run(&mut self) {
67        let ir = self.ir;
68        for i in 0..ir.procedures.len() {
69            if let Flow::Stop = self.exec_block(&ir.procedures[i].body) {
70                self.stopped = true;
71                break;
72            }
73        }
74    }
75
76    // ── Ejecución ─────────────────────────────────────────────────
77
78    /// Consume un paso del presupuesto. `true` si se agotó.
79    fn tick(&mut self) -> bool {
80        if self.budget == 0 {
81            self.step_limit_hit = true;
82            return true;
83        }
84        self.budget -= 1;
85        false
86    }
87
88    fn exec_block(&mut self, stmts: &'a [Stmt]) -> Flow {
89        for s in stmts {
90            match self.exec_stmt(s) {
91                Flow::Normal => {}
92                other => return other,
93            }
94        }
95        Flow::Normal
96    }
97
98    fn exec_stmt(&mut self, stmt: &'a Stmt) -> Flow {
99        if self.tick() {
100            return Flow::Stop;
101        }
102        match stmt {
103            Stmt::Move { from, to } => {
104                for t in to {
105                    self.do_move(from, t);
106                }
107                Flow::Normal
108            }
109            Stmt::Display { items } => {
110                let line: String = items.iter().map(|o| self.eval_text(o)).collect();
111                self.output.push(line);
112                Flow::Normal
113            }
114            Stmt::Accept { .. } => Flow::Normal, // sin entrada: deja el campo igual
115            Stmt::Compute {
116                targets,
117                rounded,
118                expr,
119            } => {
120                let value = self.eval_expr(expr);
121                for t in targets {
122                    self.store(t, value, *rounded);
123                }
124                Flow::Normal
125            }
126            Stmt::Add {
127                addends,
128                to,
129                giving,
130                rounded,
131            } => {
132                let sum = self.fold_sum(addends);
133                if giving.is_empty() {
134                    for t in to {
135                        let cur = self.field_value(t);
136                        self.store(t, cur.add(&sum), *rounded);
137                    }
138                } else {
139                    let base = match to.first() {
140                        Some(first) => sum.add(&self.field_value(first)),
141                        None => sum,
142                    };
143                    for g in giving {
144                        self.store(g, base, *rounded);
145                    }
146                }
147                Flow::Normal
148            }
149            Stmt::Subtract {
150                amounts,
151                from,
152                giving,
153                rounded,
154            } => {
155                let sum = self.fold_sum(amounts);
156                if giving.is_empty() {
157                    for t in from {
158                        let cur = self.field_value(t);
159                        self.store(t, cur.sub(&sum), *rounded);
160                    }
161                } else {
162                    let minuend = from
163                        .first()
164                        .map(|f| self.field_value(f))
165                        .unwrap_or_else(Decimal::zero);
166                    let value = minuend.sub(&sum);
167                    for g in giving {
168                        self.store(g, value, *rounded);
169                    }
170                }
171                Flow::Normal
172            }
173            Stmt::Multiply {
174                left,
175                by,
176                giving,
177                rounded,
178            } => {
179                let value = self.eval_decimal(left).mul(&self.eval_decimal(by));
180                if giving.is_empty() {
181                    if let Operand::Data(name) = by {
182                        self.store(name, value, *rounded);
183                    }
184                } else {
185                    for g in giving {
186                        self.store(g, value, *rounded);
187                    }
188                }
189                Flow::Normal
190            }
191            Stmt::Divide {
192                left,
193                right,
194                by_form,
195                giving,
196                rounded,
197            } => {
198                let (num, den) = if *by_form {
199                    (self.eval_decimal(left), self.eval_decimal(right))
200                } else {
201                    (self.eval_decimal(right), self.eval_decimal(left))
202                };
203                if giving.is_empty() {
204                    if let Operand::Data(name) = right {
205                        let v = divide(num, den, self.target_scale(name));
206                        self.store(name, v, *rounded);
207                    }
208                } else {
209                    for g in giving {
210                        let v = divide(num, den, self.target_scale(g));
211                        self.store(g, v, *rounded);
212                    }
213                }
214                Flow::Normal
215            }
216            Stmt::If {
217                cond,
218                then_branch,
219                else_branch,
220            } => {
221                if self.eval_cond(cond) {
222                    self.exec_block(then_branch)
223                } else {
224                    self.exec_block(else_branch)
225                }
226            }
227            Stmt::Perform(p) => self.exec_perform(p),
228            Stmt::GoTo { target } => {
229                // Aproximación: ejecuta el destino y sale del párrafo.
230                match self.run_paragraph(target) {
231                    Flow::Stop => Flow::Stop,
232                    _ => Flow::Exit,
233                }
234            }
235            Stmt::StopRun | Stmt::Goback => Flow::Stop,
236            Stmt::Exit => Flow::Exit,
237            Stmt::Continue => Flow::Normal,
238            Stmt::Unknown { .. } => Flow::Normal, // verbo no soportado: se omite
239        }
240    }
241
242    fn exec_perform(&mut self, p: &'a Perform) -> Flow {
243        match &p.control {
244            PerformControl::Once => self.run_target(&p.target),
245            PerformControl::Times(n) => {
246                let count = self.count_of(n);
247                for _ in 0..count {
248                    if self.tick() {
249                        return Flow::Stop;
250                    }
251                    if let Flow::Stop = self.run_target(&p.target) {
252                        return Flow::Stop;
253                    }
254                }
255                Flow::Normal
256            }
257            PerformControl::Until(cond) => loop {
258                if self.tick() {
259                    return Flow::Stop;
260                }
261                if self.eval_cond(cond) {
262                    return Flow::Normal;
263                }
264                if let Flow::Stop = self.run_target(&p.target) {
265                    return Flow::Stop;
266                }
267            },
268        }
269    }
270
271    /// Ejecuta una vez el cuerpo de un `PERFORM`. Un `EXIT` dentro de
272    /// él termina esa pasada, no el programa.
273    fn run_target(&mut self, target: &'a PerformTarget) -> Flow {
274        let flow = match target {
275            PerformTarget::Paragraph { name, .. } => self.run_paragraph(name),
276            PerformTarget::Inline(body) => self.exec_block(body),
277        };
278        match flow {
279            Flow::Stop => Flow::Stop,
280            _ => Flow::Normal,
281        }
282    }
283
284    fn run_paragraph(&mut self, name: &str) -> Flow {
285        let Some(&idx) = self.para_index.get(&name.to_uppercase()) else {
286            return Flow::Normal;
287        };
288        let ir = self.ir;
289        match self.exec_block(&ir.procedures[idx].body) {
290            Flow::Stop => Flow::Stop,
291            _ => Flow::Normal,
292        }
293    }
294
295    /// `MOVE from` a un solo campo destino.
296    fn do_move(&mut self, from: &Operand, target: &str) {
297        let key = target.to_uppercase();
298        match self.fields.get(&key) {
299            Some(Cell::Num(_)) => {
300                let v = self.eval_decimal(from);
301                if let Some(Cell::Num(n)) = self.fields.get_mut(&key) {
302                    n.store(v);
303                }
304            }
305            Some(Cell::Text(_)) => {
306                if let Operand::Figurative(fig) = from {
307                    let ch = figurative_fill(*fig);
308                    if let Some(Cell::Text(t)) = self.fields.get_mut(&key) {
309                        t.fill(ch);
310                    }
311                } else {
312                    let s = self.eval_text(from);
313                    if let Some(Cell::Text(t)) = self.fields.get_mut(&key) {
314                        t.store(&s);
315                    }
316                }
317            }
318            None => {}
319        }
320    }
321
322    /// Almacena un valor en un campo, conformándolo a su tipo.
323    fn store(&mut self, name: &str, value: Decimal, rounded: bool) {
324        match self.fields.get_mut(&name.to_uppercase()) {
325            Some(Cell::Num(n)) => {
326                if rounded {
327                    n.store_rounded(value);
328                } else {
329                    n.store(value);
330                }
331            }
332            Some(Cell::Text(t)) => t.store(&value.to_string()),
333            None => {}
334        }
335    }
336
337    // ── Evaluación ────────────────────────────────────────────────
338
339    fn eval_decimal(&self, op: &Operand) -> Decimal {
340        match op {
341            Operand::Num(n) => Decimal::parse(n).unwrap_or_else(|_| Decimal::zero()),
342            Operand::Str(s) => Decimal::parse(s).unwrap_or_else(|_| Decimal::zero()),
343            Operand::Figurative(_) => Decimal::zero(),
344            Operand::Data(name) => match self.fields.get(&name.to_uppercase()) {
345                Some(Cell::Num(n)) => n.value(),
346                Some(Cell::Text(t)) => {
347                    Decimal::parse(t.as_str().trim()).unwrap_or_else(|_| Decimal::zero())
348                }
349                None => Decimal::zero(),
350            },
351        }
352    }
353
354    fn eval_text(&self, op: &Operand) -> String {
355        match op {
356            Operand::Str(s) => s.clone(),
357            Operand::Num(n) => n.clone(),
358            Operand::Figurative(f) => figurative_text(*f).to_string(),
359            Operand::Data(name) => match self.fields.get(&name.to_uppercase()) {
360                Some(Cell::Num(n)) => n.display(),
361                Some(Cell::Text(t)) => t.display(),
362                None => String::new(),
363            },
364        }
365    }
366
367    fn eval_expr(&self, e: &Expr) -> Decimal {
368        match e {
369            Expr::Operand(op) => self.eval_decimal(op),
370            Expr::Neg(inner) => Decimal::zero().sub(&self.eval_expr(inner)),
371            Expr::Binary { op, lhs, rhs } => {
372                let l = self.eval_expr(lhs);
373                let r = self.eval_expr(rhs);
374                match op {
375                    BinOp::Add => l.add(&r),
376                    BinOp::Sub => l.sub(&r),
377                    BinOp::Mul => l.mul(&r),
378                    BinOp::Div => divide(l, r, DIV_SCALE),
379                    BinOp::Pow => pow(&l, &r),
380                }
381            }
382        }
383    }
384
385    fn eval_cond(&self, c: &Cond) -> bool {
386        match c {
387            Cond::Compare { lhs, op, rhs } => {
388                let ord = if self.is_text(lhs) || self.is_text(rhs) {
389                    cobol_text_cmp(&self.eval_text(lhs), &self.eval_text(rhs))
390                } else {
391                    self.eval_decimal(lhs).cmp(&self.eval_decimal(rhs))
392                };
393                match op {
394                    CmpOp::Eq => ord.is_eq(),
395                    CmpOp::Ne => ord.is_ne(),
396                    CmpOp::Lt => ord.is_lt(),
397                    CmpOp::Gt => ord.is_gt(),
398                    CmpOp::Le => ord.is_le(),
399                    CmpOp::Ge => ord.is_ge(),
400                }
401            }
402            Cond::Named(_) => false, // nombres de condición (88): no soportado
403            Cond::Not(inner) => !self.eval_cond(inner),
404            Cond::And(a, b) => self.eval_cond(a) && self.eval_cond(b),
405            Cond::Or(a, b) => self.eval_cond(a) || self.eval_cond(b),
406        }
407    }
408
409    fn is_text(&self, op: &Operand) -> bool {
410        match op {
411            Operand::Str(_) => true,
412            Operand::Data(name) => {
413                matches!(self.fields.get(&name.to_uppercase()), Some(Cell::Text(_)))
414            }
415            _ => false,
416        }
417    }
418
419    /// La suma de una lista de operandos.
420    fn fold_sum(&self, ops: &[Operand]) -> Decimal {
421        let mut acc = Decimal::zero();
422        for o in ops {
423            acc = acc.add(&self.eval_decimal(o));
424        }
425        acc
426    }
427
428    /// El valor actual de un campo por nombre.
429    fn field_value(&self, name: &str) -> Decimal {
430        match self.fields.get(&name.to_uppercase()) {
431            Some(Cell::Num(n)) => n.value(),
432            Some(Cell::Text(t)) => {
433                Decimal::parse(t.as_str().trim()).unwrap_or_else(|_| Decimal::zero())
434            }
435            None => Decimal::zero(),
436        }
437    }
438
439    /// Los dígitos fraccionarios de un campo numérico destino.
440    fn target_scale(&self, name: &str) -> u8 {
441        match self.fields.get(&name.to_uppercase()) {
442            Some(Cell::Num(n)) => n.picture().fraction_digits,
443            _ => 4,
444        }
445    }
446
447    /// El número de repeticiones de un `PERFORM ... TIMES`.
448    fn count_of(&self, op: &Operand) -> usize {
449        let m = self
450            .eval_decimal(op)
451            .rescale(0, Rounding::Truncate)
452            .mantissa();
453        if m < 0 {
454            0
455        } else {
456            m as usize
457        }
458    }
459}
460
461/// División con escala fija; una división por cero da cero.
462fn divide(num: Decimal, den: Decimal, scale: u8) -> Decimal {
463    num.div(&den, scale, Rounding::Truncate)
464        .unwrap_or_else(|_| Decimal::zero())
465}
466
467/// Potencia con exponente entero no negativo; en otro caso da 1.
468fn pow(base: &Decimal, exp: &Decimal) -> Decimal {
469    let e = exp.rescale(0, Rounding::Truncate).mantissa();
470    if !(0..=256).contains(&e) {
471        return Decimal::from_integer(1);
472    }
473    let mut acc = Decimal::from_integer(1);
474    for _ in 0..e {
475        acc = acc.mul(base);
476    }
477    acc
478}
479
480/// El texto que representa una constante figurativa.
481fn figurative_text(f: Figurative) -> &'static str {
482    match f {
483        Figurative::Zero => "0",
484        Figurative::Space => " ",
485        Figurative::Quote => "\"",
486        Figurative::HighValue | Figurative::LowValue | Figurative::Null => "",
487    }
488}
489
490/// El carácter de relleno de una figurativa, para `Text::fill`.
491fn figurative_fill(f: Figurative) -> char {
492    match f {
493        Figurative::Zero => '0',
494        Figurative::Quote => '"',
495        _ => ' ',
496    }
497}