feat(charka): SET ... TO TRUE — escribir nombres de condición (88)
La cara de escritura de los nombres de condición de COBOL: si
IF ES-VALIDO los lee, SET ES-VALIDO TO TRUE los escribe.
- IR: Stmt::SetTrue { conditions }.
- Parser: SET cond-1 cond-2 ... TO TRUE. Otras formas de SET
(índices, TO FALSE) caen a Stmt::Unknown.
- Codegen y shadow: SET cond TO TRUE asigna a su dato padre el valor
del 88 (un MOVE del valor a la variable).
- Corpus: programa nuevo 16-bandera (cambia banderas de texto y de
número con SET). Verificado: el intérprete sombra y el crate
compilado por scaffold dan la misma salida.
Tests: charka-ir 29, charka-codegen 23, charka-shadow 21. fmt +
clippy limpios.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -86,8 +86,8 @@ Tercera etapa: `Program` → `Ir`. Aquí se parsea cada `Sentence` cruda
|
|||||||
- `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`, `Evaluate`, `StringConcat`, `Unstring`, `Inspect`,
|
`If`, `Evaluate`, `StringConcat`, `Unstring`, `Inspect`,
|
||||||
`Initialize`, `Perform`, `GoTo`, `StopRun`, `Goback`, `Exit`,
|
`Initialize`, `SetTrue`, `Perform`, `GoTo`, `StopRun`, `Goback`,
|
||||||
`Continue`.
|
`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
|
palabra) unidas por `AND`/`OR`/`NOT`, más nombres de condición
|
||||||
@@ -108,6 +108,8 @@ Tercera etapa: `Program` → `Ir`. Aquí se parsea cada `Sentence` cruda
|
|||||||
- `INITIALIZE` — resetea un dato (o todos los elementales de un grupo)
|
- `INITIALIZE` — resetea un dato (o todos los elementales de un grupo)
|
||||||
a su valor por defecto. El `model` registra los grupos y sus
|
a su valor por defecto. El `model` registra los grupos y sus
|
||||||
miembros (`DataModel::groups`).
|
miembros (`DataModel::groups`).
|
||||||
|
- `SET cond... TO TRUE` — la cara de escritura de los nombres de
|
||||||
|
condición (nivel 88): asigna a su dato padre el valor del 88.
|
||||||
- Fuera de alcance v1: E/S de ficheros, CICS, SQL embebido.
|
- Fuera de alcance v1: E/S de ficheros, CICS, SQL embebido.
|
||||||
|
|
||||||
## charka-runtime
|
## charka-runtime
|
||||||
@@ -179,8 +181,8 @@ que corre el `Ir` directamente sobre `charka-runtime`, sin compilar.
|
|||||||
|
|
||||||
## El corpus
|
## El corpus
|
||||||
|
|
||||||
`crates/modules/charka/corpus/` — 15 programas COBOL graduados
|
`crates/modules/charka/corpus/` — 16 programas COBOL graduados
|
||||||
(`01-hola` … `15-resetear`), cada uno con su `.expected`. Ejercita el
|
(`01-hola` … `16-bandera`), cada uno con su `.expected`. Ejercita el
|
||||||
pipeline completo de punta a punta. Ver su `README.md`.
|
pipeline completo de punta a punta. Ver su `README.md`.
|
||||||
|
|
||||||
## La CLI
|
## La CLI
|
||||||
|
|||||||
@@ -427,6 +427,18 @@ mod tests {
|
|||||||
assert!(out.contains("self.ws_b.fill(' ');"));
|
assert!(out.contains("self.ws_b.fill(' ');"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn set_to_true_moves_the_88_value() {
|
||||||
|
let out = gen("DATA DIVISION.\n\
|
||||||
|
WORKING-STORAGE SECTION.\n\
|
||||||
|
01 WS-F PIC X VALUE 'N'.\n\
|
||||||
|
88 LISTO VALUE 'S'.\n\
|
||||||
|
PROCEDURE DIVISION.\n\
|
||||||
|
MAIN.\n\
|
||||||
|
SET LISTO TO TRUE.\n");
|
||||||
|
assert!(out.contains("self.ws_f.store(\"S\");"));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn empty_program_still_compiles_shape() {
|
fn empty_program_still_compiles_shape() {
|
||||||
let out = gen("");
|
let out = gen("");
|
||||||
|
|||||||
@@ -87,6 +87,7 @@ pub(crate) fn emit_stmt(em: &mut Emitter, sym: &Symbols, stmt: &Stmt) {
|
|||||||
} => emit_unstring(em, sym, source, delimiter, into),
|
} => emit_unstring(em, sym, source, delimiter, into),
|
||||||
Stmt::Inspect { target, op } => emit_inspect(em, sym, target, op),
|
Stmt::Inspect { target, op } => emit_inspect(em, sym, target, op),
|
||||||
Stmt::Initialize { targets } => emit_initialize(em, sym, targets),
|
Stmt::Initialize { targets } => emit_initialize(em, sym, targets),
|
||||||
|
Stmt::SetTrue { conditions } => emit_set_true(em, sym, conditions),
|
||||||
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!(
|
||||||
@@ -492,6 +493,21 @@ fn emit_initialize(em: &mut Emitter, sym: &Symbols, targets: &[Operand]) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// `SET cond... TO TRUE` — asigna a cada dato padre el valor que hace
|
||||||
|
/// verdadero su nombre de condición (nivel 88).
|
||||||
|
fn emit_set_true(em: &mut Emitter, sym: &Symbols, conditions: &[String]) {
|
||||||
|
for name in conditions {
|
||||||
|
match sym.condition(name) {
|
||||||
|
Some(cn) => {
|
||||||
|
let target = Operand::Data(cn.parent.clone());
|
||||||
|
let value = cn.value.clone();
|
||||||
|
emit_move(em, sym, &value, std::slice::from_ref(&target));
|
||||||
|
}
|
||||||
|
None => em.line(&format!("// charka: condición 88 no resuelta — {name}")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Resetea un campo completo (escalar o tabla entera).
|
/// Resetea un campo completo (escalar o tabla entera).
|
||||||
fn emit_reset(em: &mut Emitter, sym: &Symbols, name: &str) {
|
fn emit_reset(em: &mut Emitter, sym: &Symbols, name: &str) {
|
||||||
let Some(f) = sym.lookup(name) else {
|
let Some(f) = sym.lookup(name) else {
|
||||||
|
|||||||
@@ -188,6 +188,9 @@ pub enum Stmt {
|
|||||||
/// `INITIALIZE targets...` — pone cada dato (o grupo) en su valor
|
/// `INITIALIZE targets...` — pone cada dato (o grupo) en su valor
|
||||||
/// por defecto: 0 los numéricos, espacios los alfanuméricos.
|
/// por defecto: 0 los numéricos, espacios los alfanuméricos.
|
||||||
Initialize { targets: Vec<Operand> },
|
Initialize { targets: Vec<Operand> },
|
||||||
|
/// `SET cond-name... TO TRUE` — hace verdaderos esos nombres de
|
||||||
|
/// condición (nivel 88): asigna a su dato padre el valor del 88.
|
||||||
|
SetTrue { conditions: Vec<String> },
|
||||||
/// `PERFORM ...` — ver [`Perform`].
|
/// `PERFORM ...` — ver [`Perform`].
|
||||||
Perform(Perform),
|
Perform(Perform),
|
||||||
/// `GO TO target`
|
/// `GO TO target`
|
||||||
|
|||||||
@@ -460,6 +460,17 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn set_to_true_parses() {
|
||||||
|
let b = body("SET ACTIVO LISTO TO TRUE.");
|
||||||
|
match &b[0] {
|
||||||
|
Stmt::SetTrue { conditions } => {
|
||||||
|
assert_eq!(conditions, &vec!["ACTIVO".to_string(), "LISTO".to_string()]);
|
||||||
|
}
|
||||||
|
other => panic!("se esperaba SET, 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.");
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ fn parse_one_stmt(c: &mut Cursor, stops: &[&str]) -> Stmt {
|
|||||||
"UNSTRING" => parse_unstring(c),
|
"UNSTRING" => parse_unstring(c),
|
||||||
"INSPECT" => parse_inspect(c),
|
"INSPECT" => parse_inspect(c),
|
||||||
"INITIALIZE" => parse_initialize(c),
|
"INITIALIZE" => parse_initialize(c),
|
||||||
|
"SET" => parse_set(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),
|
||||||
@@ -391,6 +392,24 @@ fn parse_unstring(c: &mut Cursor) -> Stmt {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_set(c: &mut Cursor) -> Stmt {
|
||||||
|
c.bump(); // SET
|
||||||
|
let mut conditions = Vec::new();
|
||||||
|
while let Some(name) = parse_one_name(c) {
|
||||||
|
conditions.push(name);
|
||||||
|
}
|
||||||
|
// La v1 sólo modela `SET ... TO TRUE`.
|
||||||
|
if c.eat_word("TO") && c.eat_word("TRUE") {
|
||||||
|
Stmt::SetTrue { conditions }
|
||||||
|
} else {
|
||||||
|
skip_to_stmt_boundary(c);
|
||||||
|
Stmt::Unknown {
|
||||||
|
verb: "SET".to_string(),
|
||||||
|
tokens: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn parse_initialize(c: &mut Cursor) -> Stmt {
|
fn parse_initialize(c: &mut Cursor) -> Stmt {
|
||||||
c.bump(); // INITIALIZE
|
c.bump(); // INITIALIZE
|
||||||
let mut rounded = false;
|
let mut rounded = false;
|
||||||
|
|||||||
@@ -312,6 +312,14 @@ impl<'a> Machine<'a> {
|
|||||||
}
|
}
|
||||||
Flow::Normal
|
Flow::Normal
|
||||||
}
|
}
|
||||||
|
Stmt::SetTrue { conditions } => {
|
||||||
|
for name in conditions {
|
||||||
|
if let Some(cn) = self.conditions.get(&name.to_uppercase()).cloned() {
|
||||||
|
self.do_move(&cn.value, &Operand::Data(cn.parent));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Flow::Normal
|
||||||
|
}
|
||||||
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.
|
||||||
|
|||||||
@@ -124,6 +124,7 @@ mod tests {
|
|||||||
corpus_test!(corpus_13_inspeccion, "13-inspeccion");
|
corpus_test!(corpus_13_inspeccion, "13-inspeccion");
|
||||||
corpus_test!(corpus_14_clasifica, "14-clasifica");
|
corpus_test!(corpus_14_clasifica, "14-clasifica");
|
||||||
corpus_test!(corpus_15_resetear, "15-resetear");
|
corpus_test!(corpus_15_resetear, "15-resetear");
|
||||||
|
corpus_test!(corpus_16_bandera, "16-bandera");
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn empty_source_runs_clean() {
|
fn empty_source_runs_clean() {
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
* corpus charka — nivel 5: SET de nombres de condición (nivel 88)
|
||||||
|
IDENTIFICATION DIVISION.
|
||||||
|
PROGRAM-ID. BANDERA.
|
||||||
|
DATA DIVISION.
|
||||||
|
WORKING-STORAGE SECTION.
|
||||||
|
01 WS-ESTADO PIC X VALUE 'N'.
|
||||||
|
88 ACTIVO VALUE 'S'.
|
||||||
|
88 INACTIVO VALUE 'N'.
|
||||||
|
01 WS-NIVEL PIC 9(1) VALUE 0.
|
||||||
|
88 NIVEL-MAX VALUE 9.
|
||||||
|
PROCEDURE DIVISION.
|
||||||
|
MAIN.
|
||||||
|
IF INACTIVO
|
||||||
|
DISPLAY 'EMPIEZA INACTIVO'
|
||||||
|
END-IF.
|
||||||
|
SET ACTIVO TO TRUE.
|
||||||
|
IF ACTIVO
|
||||||
|
DISPLAY 'AHORA ACTIVO'
|
||||||
|
END-IF.
|
||||||
|
SET NIVEL-MAX TO TRUE.
|
||||||
|
DISPLAY 'NIVEL = ' WS-NIVEL.
|
||||||
|
STOP RUN.
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
EMPIEZA INACTIVO
|
||||||
|
AHORA ACTIVO
|
||||||
|
NIVEL = 9
|
||||||
@@ -24,6 +24,7 @@ salida correcta, una línea por `DISPLAY`.
|
|||||||
| `13-inspeccion` | 6 | `INSPECT` — contar (`TALLYING`) y reemplazar |
|
| `13-inspeccion` | 6 | `INSPECT` — contar (`TALLYING`) y reemplazar |
|
||||||
| `14-clasifica` | 6 | `EVALUATE TRUE` y rangos `WHEN ... THRU` |
|
| `14-clasifica` | 6 | `EVALUATE TRUE` y rangos `WHEN ... THRU` |
|
||||||
| `15-resetear` | 6 | `INITIALIZE` — resetear datos y grupos |
|
| `15-resetear` | 6 | `INITIALIZE` — resetear datos y grupos |
|
||||||
|
| `16-bandera` | 5 | `SET` de nombres de condición (nivel 88) a `TRUE` |
|
||||||
|
|
||||||
## Formato
|
## Formato
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,19 @@
|
|||||||
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): SET ... TO TRUE — escribir nombres de condición (88)
|
||||||
|
|
||||||
|
La cara de escritura de los nombres de condición de COBOL: si `IF
|
||||||
|
ES-VALIDO` los lee, `SET ES-VALIDO TO TRUE` los escribe.
|
||||||
|
|
||||||
|
- IR: `Stmt::SetTrue { conditions }`.
|
||||||
|
- Parser: `SET cond-1 cond-2 ... TO TRUE`. Otras formas de `SET`
|
||||||
|
(índices, `TO FALSE`) caen a `Stmt::Unknown`.
|
||||||
|
- Codegen y shadow: `SET cond TO TRUE` asigna a su dato padre el valor
|
||||||
|
del 88 (un `MOVE` del valor a la variable).
|
||||||
|
- Corpus: programa nuevo `16-bandera` (cambia banderas de texto y de
|
||||||
|
número con `SET`). Verificado en ambas rutas.
|
||||||
|
|
||||||
### feat(charka): INITIALIZE — resetear datos y grupos
|
### feat(charka): INITIALIZE — resetear datos y grupos
|
||||||
|
|
||||||
El verbo de COBOL para volver un dato (o un registro entero) a su
|
El verbo de COBOL para volver un dato (o un registro entero) a su
|
||||||
|
|||||||
Reference in New Issue
Block a user