gioser

tawasuyu/brahman

Rust 212TOML 64JSON 14HTML 12703 Definitions

⎇ 28ee1ae2602662b216549ba4db0d34a9b55b8deb crates/modules/charka/charka-shadow/src/interp.rs Rust · 558 lines · 18.6 KB History Who touched what raw

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