feat(charka): EVALUATE — el case de COBOL
EVALUATE atraviesa el pipeline entero — antes el parser lo guardaba
crudo como Stmt::Unknown.
- IR: Stmt::Evaluate { subject, whens, other } con
WhenBranch { values, body }. Varios WHEN apilados comparten cuerpo;
WHEN OTHER es el caso por defecto.
- Parser: EVALUATE subject WHEN v1 WHEN v2 ... [WHEN OTHER ...]
END-EVALUATE.
- Codegen: lo baja a una cadena if / else if / else — una rama se
elige si el sujeto es igual a alguno de sus valores, sin caída.
- Shadow: el intérprete evalúa el sujeto y ejecuta la primera rama
cuyos valores casen, o el WHEN OTHER.
- Corpus: programa nuevo 09-evaluar (EVALUATE por valor anidado en un
PERFORM VARYING, con WHEN apilados y WHEN OTHER). Verificado: el
intérprete sombra y el crate compilado por scaffold dan la misma
salida.
Alcance v1: EVALUATE por igualdad de valor; no la forma EVALUATE TRUE
con condiciones ni los rangos THRU.
Tests: charka-ir 19, charka-codegen 16, charka-shadow 14. fmt +
clippy limpios.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -233,6 +233,12 @@ fn collect_unknowns(stmts: &[Stmt], out: &mut Vec<String>) {
|
|||||||
collect_unknowns(then_branch, out);
|
collect_unknowns(then_branch, out);
|
||||||
collect_unknowns(else_branch, out);
|
collect_unknowns(else_branch, out);
|
||||||
}
|
}
|
||||||
|
Stmt::Evaluate { whens, other, .. } => {
|
||||||
|
for w in whens {
|
||||||
|
collect_unknowns(&w.body, out);
|
||||||
|
}
|
||||||
|
collect_unknowns(other, out);
|
||||||
|
}
|
||||||
Stmt::Perform(p) => {
|
Stmt::Perform(p) => {
|
||||||
if let PerformTarget::Inline(body) = &p.target {
|
if let PerformTarget::Inline(body) = &p.target {
|
||||||
collect_unknowns(body, out);
|
collect_unknowns(body, out);
|
||||||
|
|||||||
@@ -79,7 +79,8 @@ Tercera etapa: `Program` → `Ir`. Aquí se parsea cada `Sentence` cruda
|
|||||||
El modelo de datos pasa tal cual (sirve de tabla de símbolos).
|
El modelo de datos pasa tal cual (sirve de tabla de símbolos).
|
||||||
- `Procedure { name, body: Vec<Stmt> }`. `Stmt` cubre `Move`,
|
- `Procedure { name, body: Vec<Stmt> }`. `Stmt` cubre `Move`,
|
||||||
`Display`, `Accept`, `Compute`, `Add`/`Subtract`/`Multiply`/`Divide`,
|
`Display`, `Accept`, `Compute`, `Add`/`Subtract`/`Multiply`/`Divide`,
|
||||||
`If`, `Perform`, `GoTo`, `StopRun`, `Goback`, `Exit`, `Continue`.
|
`If`, `Evaluate`, `Perform`, `GoTo`, `StopRun`, `Goback`, `Exit`,
|
||||||
|
`Continue`.
|
||||||
- `Expr` — expresiones aritméticas con precedencia y paréntesis (Pratt:
|
- `Expr` — expresiones aritméticas con precedencia y paréntesis (Pratt:
|
||||||
`+ -` < `* /` < `**` der.). `Cond` — comparaciones (símbolo o forma
|
`+ -` < `* /` < `**` der.). `Cond` — comparaciones (símbolo o forma
|
||||||
palabra) unidas por `AND`/`OR`/`NOT`, más nombres de condición (88).
|
palabra) unidas por `AND`/`OR`/`NOT`, más nombres de condición (88).
|
||||||
@@ -91,8 +92,10 @@ Tercera etapa: `Program` → `Ir`. Aquí se parsea cada `Sentence` cruda
|
|||||||
para delimitar listas de operandos.
|
para delimitar listas de operandos.
|
||||||
- `PERFORM` cubre las cuatro formas: párrafo / en línea, `n TIMES`,
|
- `PERFORM` cubre las cuatro formas: párrafo / en línea, `n TIMES`,
|
||||||
`UNTIL cond` y `VARYING var FROM x BY y UNTIL cond`.
|
`UNTIL cond` y `VARYING var FROM x BY y UNTIL cond`.
|
||||||
- Fuera de alcance v1: `EVALUATE`, `STRING`/`UNSTRING`, E/S de
|
- `EVALUATE subject WHEN ... WHEN OTHER` — el `case` de COBOL, por
|
||||||
ficheros, CICS, SQL embebido.
|
igualdad de valor (no la forma `EVALUATE TRUE` con condiciones).
|
||||||
|
- Fuera de alcance v1: `STRING`/`UNSTRING`, E/S de ficheros, CICS,
|
||||||
|
SQL embebido.
|
||||||
|
|
||||||
## charka-runtime
|
## charka-runtime
|
||||||
|
|
||||||
|
|||||||
@@ -350,6 +350,21 @@ mod tests {
|
|||||||
assert!(out.contains("self.ws_i.store(self.ws_i.value().add(&(dec(\"1\"))));"));
|
assert!(out.contains("self.ws_i.store(self.ws_i.value().add(&(dec(\"1\"))));"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn evaluate_emits_an_if_else_chain() {
|
||||||
|
let out = gen("DATA DIVISION.\n\
|
||||||
|
WORKING-STORAGE SECTION.\n\
|
||||||
|
01 WS-X PIC 9(1).\n\
|
||||||
|
PROCEDURE DIVISION.\n\
|
||||||
|
MAIN.\n\
|
||||||
|
EVALUATE WS-X\n\
|
||||||
|
WHEN 1 DISPLAY 'UNO'\n\
|
||||||
|
WHEN OTHER DISPLAY 'OTRO'\n\
|
||||||
|
END-EVALUATE.\n");
|
||||||
|
assert!(out.contains("if ((self.ws_x.value()) == (dec(\"1\"))) {"));
|
||||||
|
assert!(out.contains("} else {"));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn empty_program_still_compiles_shape() {
|
fn empty_program_still_compiles_shape() {
|
||||||
let out = gen("");
|
let out = gen("");
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
//! Emisión de los statements del PROCEDURE: cada [`Stmt`] se traduce a
|
//! Emisión de los statements del PROCEDURE: cada [`Stmt`] se traduce a
|
||||||
//! una o varias líneas de código Rust sobre `charka-runtime`.
|
//! una o varias líneas de código Rust sobre `charka-runtime`.
|
||||||
|
|
||||||
use charka_ir::{Operand, Perform, PerformControl, PerformTarget, Stmt};
|
use charka_ir::{CmpOp, Cond, Operand, Perform, PerformControl, PerformTarget, Stmt, WhenBranch};
|
||||||
|
|
||||||
use crate::emit::Emitter;
|
use crate::emit::Emitter;
|
||||||
use crate::expr::{
|
use crate::expr::{
|
||||||
@@ -73,6 +73,11 @@ pub(crate) fn emit_stmt(em: &mut Emitter, sym: &Symbols, stmt: &Stmt) {
|
|||||||
em.line("}");
|
em.line("}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Stmt::Evaluate {
|
||||||
|
subject,
|
||||||
|
whens,
|
||||||
|
other,
|
||||||
|
} => emit_evaluate(em, sym, subject, whens, other),
|
||||||
Stmt::Perform(p) => emit_perform(em, sym, p),
|
Stmt::Perform(p) => emit_perform(em, sym, p),
|
||||||
Stmt::GoTo { target } => {
|
Stmt::GoTo { target } => {
|
||||||
em.line(&format!(
|
em.line(&format!(
|
||||||
@@ -321,6 +326,72 @@ fn count_expr(sym: &Symbols, op: &Operand) -> String {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Emite un `EVALUATE` como una cadena `if / else if / else`.
|
||||||
|
fn emit_evaluate(
|
||||||
|
em: &mut Emitter,
|
||||||
|
sym: &Symbols,
|
||||||
|
subject: &Operand,
|
||||||
|
whens: &[WhenBranch],
|
||||||
|
other: &[Stmt],
|
||||||
|
) {
|
||||||
|
if whens.is_empty() {
|
||||||
|
if !other.is_empty() {
|
||||||
|
em.line("{");
|
||||||
|
em.indent();
|
||||||
|
emit_block(em, sym, other);
|
||||||
|
em.dedent();
|
||||||
|
em.line("}");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (i, branch) in whens.iter().enumerate() {
|
||||||
|
let cond = branch_condition(sym, subject, branch);
|
||||||
|
if i == 0 {
|
||||||
|
em.line(&format!("if {cond} {{"));
|
||||||
|
} else {
|
||||||
|
em.line(&format!("}} else if {cond} {{"));
|
||||||
|
}
|
||||||
|
em.indent();
|
||||||
|
emit_block(em, sym, &branch.body);
|
||||||
|
em.dedent();
|
||||||
|
}
|
||||||
|
if other.is_empty() {
|
||||||
|
em.line("}");
|
||||||
|
} else {
|
||||||
|
em.line("} else {");
|
||||||
|
em.indent();
|
||||||
|
emit_block(em, sym, other);
|
||||||
|
em.dedent();
|
||||||
|
em.line("}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// La condición de una rama `WHEN`: el sujeto igual a cualquiera de
|
||||||
|
/// sus valores.
|
||||||
|
fn branch_condition(sym: &Symbols, subject: &Operand, branch: &WhenBranch) -> String {
|
||||||
|
if branch.values.is_empty() {
|
||||||
|
return "false".to_string();
|
||||||
|
}
|
||||||
|
branch
|
||||||
|
.values
|
||||||
|
.iter()
|
||||||
|
.map(|v| {
|
||||||
|
format!(
|
||||||
|
"({})",
|
||||||
|
emit_cond(
|
||||||
|
sym,
|
||||||
|
&Cond::Compare {
|
||||||
|
lhs: subject.clone(),
|
||||||
|
op: CmpOp::Eq,
|
||||||
|
rhs: v.clone(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(" || ")
|
||||||
|
}
|
||||||
|
|
||||||
fn emit_perform(em: &mut Emitter, sym: &Symbols, p: &Perform) {
|
fn emit_perform(em: &mut Emitter, sym: &Symbols, p: &Perform) {
|
||||||
// Emite el "cuerpo": la llamada al párrafo o el bloque en línea.
|
// Emite el "cuerpo": la llamada al párrafo o el bloque en línea.
|
||||||
let emit_body = |em: &mut Emitter, sym: &Symbols| match &p.target {
|
let emit_body = |em: &mut Emitter, sym: &Symbols| match &p.target {
|
||||||
|
|||||||
@@ -155,6 +155,15 @@ pub enum Stmt {
|
|||||||
then_branch: Vec<Stmt>,
|
then_branch: Vec<Stmt>,
|
||||||
else_branch: Vec<Stmt>,
|
else_branch: Vec<Stmt>,
|
||||||
},
|
},
|
||||||
|
/// `EVALUATE subject WHEN ... [WHEN OTHER ...] END-EVALUATE` — el
|
||||||
|
/// `case` de COBOL. Una rama se elige si `subject` es igual a
|
||||||
|
/// alguno de sus valores; sin caída entre ramas.
|
||||||
|
Evaluate {
|
||||||
|
subject: Operand,
|
||||||
|
whens: Vec<WhenBranch>,
|
||||||
|
/// El cuerpo de `WHEN OTHER` (vacío si no hay).
|
||||||
|
other: Vec<Stmt>,
|
||||||
|
},
|
||||||
/// `PERFORM ...` — ver [`Perform`].
|
/// `PERFORM ...` — ver [`Perform`].
|
||||||
Perform(Perform),
|
Perform(Perform),
|
||||||
/// `GO TO target`
|
/// `GO TO target`
|
||||||
@@ -172,6 +181,14 @@ pub enum Stmt {
|
|||||||
Unknown { verb: String, tokens: Vec<Token> },
|
Unknown { verb: String, tokens: Vec<Token> },
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Una rama `WHEN` de un `EVALUATE`: los valores que la disparan
|
||||||
|
/// (varios `WHEN` apilados comparten cuerpo) y el cuerpo a ejecutar.
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub struct WhenBranch {
|
||||||
|
pub values: Vec<Operand>,
|
||||||
|
pub body: Vec<Stmt>,
|
||||||
|
}
|
||||||
|
|
||||||
/// Un statement `PERFORM`: a quién ejecuta y cuántas veces.
|
/// Un statement `PERFORM`: a quién ejecuta y cuántas veces.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct Perform {
|
pub struct Perform {
|
||||||
|
|||||||
@@ -16,9 +16,9 @@
|
|||||||
//! Alcance v1 — los verbos parseados a fondo: `MOVE`, `DISPLAY`,
|
//! Alcance v1 — los verbos parseados a fondo: `MOVE`, `DISPLAY`,
|
||||||
//! `ACCEPT`, `COMPUTE` (con expresiones con precedencia), `ADD`,
|
//! `ACCEPT`, `COMPUTE` (con expresiones con precedencia), `ADD`,
|
||||||
//! `SUBTRACT`, `MULTIPLY`, `DIVIDE`, `IF`/`ELSE`/`END-IF` (con
|
//! `SUBTRACT`, `MULTIPLY`, `DIVIDE`, `IF`/`ELSE`/`END-IF` (con
|
||||||
//! condiciones `AND`/`OR`/`NOT`), `PERFORM` (fuera de línea, en línea,
|
//! condiciones `AND`/`OR`/`NOT`), `EVALUATE`/`WHEN`, `PERFORM` (fuera
|
||||||
//! `TIMES`, `UNTIL`, `VARYING`), `GO TO`, `STOP RUN`, `GOBACK`,
|
//! de línea, en línea, `TIMES`, `UNTIL`, `VARYING`), `GO TO`,
|
||||||
//! `EXIT`, `CONTINUE`. Fuera de alcance: `EVALUATE`,
|
//! `STOP RUN`, `GOBACK`, `EXIT`, `CONTINUE`. Fuera de alcance:
|
||||||
//! `STRING`/`UNSTRING`, E/S de ficheros, CICS y SQL embebido.
|
//! `STRING`/`UNSTRING`, E/S de ficheros, CICS y SQL embebido.
|
||||||
|
|
||||||
#![forbid(unsafe_code)]
|
#![forbid(unsafe_code)]
|
||||||
@@ -311,6 +311,34 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn evaluate_parses_whens_and_other() {
|
||||||
|
let b = body(
|
||||||
|
"EVALUATE WS-X \
|
||||||
|
WHEN 1 DISPLAY 'A' \
|
||||||
|
WHEN 2 WHEN 3 DISPLAY 'B' \
|
||||||
|
WHEN OTHER DISPLAY 'C' \
|
||||||
|
END-EVALUATE.",
|
||||||
|
);
|
||||||
|
match &b[0] {
|
||||||
|
Stmt::Evaluate {
|
||||||
|
subject,
|
||||||
|
whens,
|
||||||
|
other,
|
||||||
|
} => {
|
||||||
|
assert_eq!(subject, &Operand::Data("WS-X".into()));
|
||||||
|
assert_eq!(whens.len(), 2);
|
||||||
|
assert_eq!(whens[0].values, vec![Operand::Num("1".into())]);
|
||||||
|
assert_eq!(
|
||||||
|
whens[1].values,
|
||||||
|
vec![Operand::Num("2".into()), Operand::Num("3".into())]
|
||||||
|
);
|
||||||
|
assert_eq!(other.len(), 1);
|
||||||
|
}
|
||||||
|
other => panic!("se esperaba EVALUATE, vino {other:?}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn several_statements_in_one_sentence() {
|
fn several_statements_in_one_sentence() {
|
||||||
let b = body("MOVE 1 TO X DISPLAY X STOP RUN.");
|
let b = body("MOVE 1 TO X DISPLAY X STOP RUN.");
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
use charka_parser::TokenKind;
|
use charka_parser::TokenKind;
|
||||||
|
|
||||||
use crate::ast::{Operand, Perform, PerformControl, PerformTarget, Stmt};
|
use crate::ast::{Operand, Perform, PerformControl, PerformTarget, Stmt, WhenBranch};
|
||||||
use crate::cursor::{parse_operand, Cursor};
|
use crate::cursor::{parse_operand, Cursor};
|
||||||
use crate::expr::{parse_cond, parse_expr};
|
use crate::expr::{parse_cond, parse_expr};
|
||||||
use crate::kw::{is_boundary, is_terminator, is_verb};
|
use crate::kw::{is_boundary, is_terminator, is_verb};
|
||||||
@@ -38,6 +38,7 @@ fn parse_one_stmt(c: &mut Cursor, stops: &[&str]) -> Stmt {
|
|||||||
"MULTIPLY" => parse_multiply(c),
|
"MULTIPLY" => parse_multiply(c),
|
||||||
"DIVIDE" => parse_divide(c),
|
"DIVIDE" => parse_divide(c),
|
||||||
"IF" => parse_if(c),
|
"IF" => parse_if(c),
|
||||||
|
"EVALUATE" => parse_evaluate(c),
|
||||||
"PERFORM" => parse_perform(c),
|
"PERFORM" => parse_perform(c),
|
||||||
"GO" => parse_goto(c),
|
"GO" => parse_goto(c),
|
||||||
"STOP" => parse_stop(c),
|
"STOP" => parse_stop(c),
|
||||||
@@ -285,6 +286,40 @@ fn parse_if(c: &mut Cursor) -> Stmt {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_evaluate(c: &mut Cursor) -> Stmt {
|
||||||
|
c.bump(); // EVALUATE
|
||||||
|
let subject = parse_operand(c);
|
||||||
|
let mut whens = Vec::new();
|
||||||
|
let mut other = Vec::new();
|
||||||
|
while !c.done() && !c.at_word("END-EVALUATE") {
|
||||||
|
if !c.at_word("WHEN") {
|
||||||
|
break; // algo inesperado dentro del EVALUATE: se corta
|
||||||
|
}
|
||||||
|
// Varios `WHEN` apilados comparten el mismo cuerpo.
|
||||||
|
let mut values = Vec::new();
|
||||||
|
let mut is_other = false;
|
||||||
|
while c.eat_word("WHEN") {
|
||||||
|
if c.eat_word("OTHER") {
|
||||||
|
is_other = true;
|
||||||
|
} else {
|
||||||
|
values.push(parse_operand(c));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let body = parse_statements(c, &["WHEN", "END-EVALUATE"]);
|
||||||
|
if is_other {
|
||||||
|
other = body;
|
||||||
|
} else {
|
||||||
|
whens.push(WhenBranch { values, body });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.eat_word("END-EVALUATE");
|
||||||
|
Stmt::Evaluate {
|
||||||
|
subject,
|
||||||
|
whens,
|
||||||
|
other,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn parse_perform(c: &mut Cursor) -> Stmt {
|
fn parse_perform(c: &mut Cursor) -> Stmt {
|
||||||
c.bump(); // PERFORM
|
c.bump(); // PERFORM
|
||||||
|
|
||||||
|
|||||||
@@ -224,6 +224,22 @@ impl<'a> Machine<'a> {
|
|||||||
self.exec_block(else_branch)
|
self.exec_block(else_branch)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Stmt::Evaluate {
|
||||||
|
subject,
|
||||||
|
whens,
|
||||||
|
other,
|
||||||
|
} => {
|
||||||
|
for branch in whens {
|
||||||
|
if branch
|
||||||
|
.values
|
||||||
|
.iter()
|
||||||
|
.any(|v| self.operands_equal(subject, v))
|
||||||
|
{
|
||||||
|
return self.exec_block(&branch.body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.exec_block(other)
|
||||||
|
}
|
||||||
Stmt::Perform(p) => self.exec_perform(p),
|
Stmt::Perform(p) => self.exec_perform(p),
|
||||||
Stmt::GoTo { target } => {
|
Stmt::GoTo { target } => {
|
||||||
// Aproximación: ejecuta el destino y sale del párrafo.
|
// Aproximación: ejecuta el destino y sale del párrafo.
|
||||||
@@ -438,6 +454,15 @@ impl<'a> Machine<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// ¿Son iguales dos operandos? (Para las ramas `WHEN` del `EVALUATE`.)
|
||||||
|
fn operands_equal(&self, a: &Operand, b: &Operand) -> bool {
|
||||||
|
if self.is_text(a) || self.is_text(b) {
|
||||||
|
cobol_text_cmp(&self.eval_text(a), &self.eval_text(b)).is_eq()
|
||||||
|
} else {
|
||||||
|
self.eval_decimal(a) == self.eval_decimal(b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// La suma de una lista de operandos.
|
/// La suma de una lista de operandos.
|
||||||
fn fold_sum(&self, ops: &[Operand]) -> Decimal {
|
fn fold_sum(&self, ops: &[Operand]) -> Decimal {
|
||||||
let mut acc = Decimal::zero();
|
let mut acc = Decimal::zero();
|
||||||
|
|||||||
@@ -117,6 +117,7 @@ mod tests {
|
|||||||
corpus_test!(corpus_06_nomina, "06-nomina");
|
corpus_test!(corpus_06_nomina, "06-nomina");
|
||||||
corpus_test!(corpus_07_clasificar, "07-clasificar");
|
corpus_test!(corpus_07_clasificar, "07-clasificar");
|
||||||
corpus_test!(corpus_08_varying, "08-varying");
|
corpus_test!(corpus_08_varying, "08-varying");
|
||||||
|
corpus_test!(corpus_09_evaluar, "09-evaluar");
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn empty_source_runs_clean() {
|
fn empty_source_runs_clean() {
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
* corpus charka — nivel 5: EVALUATE, el case de COBOL
|
||||||
|
IDENTIFICATION DIVISION.
|
||||||
|
PROGRAM-ID. EVALUAR.
|
||||||
|
DATA DIVISION.
|
||||||
|
WORKING-STORAGE SECTION.
|
||||||
|
01 WS-DIA PIC 9(1) VALUE 0.
|
||||||
|
PROCEDURE DIVISION.
|
||||||
|
MAIN.
|
||||||
|
PERFORM VARYING WS-DIA FROM 1 BY 1 UNTIL WS-DIA > 5
|
||||||
|
EVALUATE WS-DIA
|
||||||
|
WHEN 1
|
||||||
|
DISPLAY 'LUNES'
|
||||||
|
WHEN 2
|
||||||
|
DISPLAY 'MARTES'
|
||||||
|
WHEN 3
|
||||||
|
DISPLAY 'MIERCOLES'
|
||||||
|
WHEN 6
|
||||||
|
WHEN 7
|
||||||
|
DISPLAY 'FIN DE SEMANA'
|
||||||
|
WHEN OTHER
|
||||||
|
DISPLAY 'OTRO DIA'
|
||||||
|
END-EVALUATE
|
||||||
|
END-PERFORM.
|
||||||
|
STOP RUN.
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
LUNES
|
||||||
|
MARTES
|
||||||
|
MIERCOLES
|
||||||
|
OTRO DIA
|
||||||
|
OTRO DIA
|
||||||
@@ -17,6 +17,7 @@ salida correcta, una línea por `DISPLAY`.
|
|||||||
| `06-nomina` | 5 | grupos, `COMPUTE` con paréntesis, `ROUNDED`, V99 |
|
| `06-nomina` | 5 | grupos, `COMPUTE` con paréntesis, `ROUNDED`, V99 |
|
||||||
| `07-clasificar` | 5 | `IF` anidado, condiciones con `AND` |
|
| `07-clasificar` | 5 | `IF` anidado, condiciones con `AND` |
|
||||||
| `08-varying` | 4 | `PERFORM VARYING` — el bucle con variable de control|
|
| `08-varying` | 4 | `PERFORM VARYING` — el bucle con variable de control|
|
||||||
|
| `09-evaluar` | 5 | `EVALUATE` — el `case` de COBOL, `WHEN` / `OTHER` |
|
||||||
|
|
||||||
## Formato
|
## Formato
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,26 @@
|
|||||||
Transpilador COBOL → Rust. El módulo más grande del ecosistema (Fase D
|
Transpilador COBOL → Rust. El módulo más grande del ecosistema (Fase D
|
||||||
del plan macro) — el parser COBOL completo es un esfuerzo multi-mes.
|
del plan macro) — el parser COBOL completo es un esfuerzo multi-mes.
|
||||||
|
|
||||||
|
### feat(charka): EVALUATE — el case de COBOL
|
||||||
|
|
||||||
|
`EVALUATE` atraviesa el pipeline entero — antes el parser lo guardaba
|
||||||
|
crudo como `Stmt::Unknown`.
|
||||||
|
|
||||||
|
- IR: `Stmt::Evaluate { subject, whens, other }` con
|
||||||
|
`WhenBranch { values, body }`. Varios `WHEN` apilados comparten
|
||||||
|
cuerpo; `WHEN OTHER` es el caso por defecto.
|
||||||
|
- Parser: `EVALUATE subject WHEN v1 WHEN v2 ... [WHEN OTHER ...]
|
||||||
|
END-EVALUATE`.
|
||||||
|
- Codegen: lo baja a una cadena `if / else if / else` — una rama se
|
||||||
|
elige si el sujeto es igual a alguno de sus valores; sin caída.
|
||||||
|
- Shadow: el intérprete evalúa el sujeto y ejecuta la primera rama
|
||||||
|
cuyos valores casen, o el `WHEN OTHER`.
|
||||||
|
- Corpus: programa nuevo `09-evaluar` (un `EVALUATE` por valor anidado
|
||||||
|
en un `PERFORM VARYING`, con `WHEN` apilados y `WHEN OTHER`).
|
||||||
|
Verificado: intérprete sombra y crate compilado dan la misma salida.
|
||||||
|
- Alcance v1: `EVALUATE` por igualdad de valor; no la forma
|
||||||
|
`EVALUATE TRUE` con condiciones ni los rangos `THRU`.
|
||||||
|
|
||||||
### feat(charka): PERFORM VARYING — el bucle con variable de control
|
### feat(charka): PERFORM VARYING — el bucle con variable de control
|
||||||
|
|
||||||
El bucle más usado de COBOL, que antes el parser degradaba a un
|
El bucle más usado de COBOL, que antes el parser degradaba a un
|
||||||
|
|||||||
Reference in New Issue
Block a user