tawasuyu

tawasuyu/brahman

Rust 521TOML 202Markdown 57JSON 197505 Definiciones

feat(charka): INITIALIZE — resetear datos y grupos

El verbo de COBOL para volver un dato (o un registro entero) a su
valor por defecto.

- IR: Stmt::Initialize { targets }. El model de charka-ir registra
  ahora los grupos y sus datos elementales (DataModel::groups,
  GroupInfo { name, members }).
- Parser: INITIALIZE name-1 name-2 ...
- Codegen y shadow: cada destino, si es un grupo, se expande a sus
  miembros; cada dato elemental se pone a 0 (numérico) o a espacios
  (alfanumérico); una tabla OCCURS resetea todos sus elementos.
- Corpus: programa nuevo 15-resetear (resetea un grupo y un escalar).
  Verificado: el intérprete sombra y el crate compilado por scaffold
  dan la misma salida.

Tests: charka-ir 28, charka-codegen 22, charka-shadow 20. fmt +
clippy limpios.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

fa65f202065abca26e0fb28581427117b84af85c · por sergio · (hace 5 meses) · padre 7867d6830 · ver el árbol en este commit

Delta semántico · 22

crates/apps/charka/src/main.rs

crates/modules/charka/charka-codegen/src/lib.rs

crates/modules/charka/charka-codegen/src/stmt.rs

crates/modules/charka/charka-codegen/src/sym.rs

crates/modules/charka/charka-ir/src/ast.rs

  • cambió de firma enum Stmt pub

crates/modules/charka/charka-ir/src/lib.rs

crates/modules/charka/charka-ir/src/model.rs

crates/modules/charka/charka-ir/src/stmt.rs

crates/modules/charka/charka-shadow/src/interp.rs

15 archivos · +260 −17

diff --git a/crates/apps/charka/src/main.rs b/crates/apps/charka/src/main.rs
index 9d0d864..90ffe25 100644
--- a/crates/apps/charka/src/main.rs
+++ b/crates/apps/charka/src/main.rs
@@ -290,13 +290,13 @@ mod tests {
         let ir = ir_of(
             "PROCEDURE DIVISION.\n\
              MAIN.\n\
-                 INITIALIZE WS-X.\n",
+                 CALL 'SUBPROG'.\n",
         );
         let mut verbs = Vec::new();
         for proc in &ir.procedures {
             collect_unknowns(&proc.body, &mut verbs);
         }
-        assert_eq!(verbs, vec!["INITIALIZE".to_string()]);
+        assert_eq!(verbs, vec!["CALL".to_string()]);
     }
 
     #[test]
diff --git a/crates/modules/charka/SDD.md b/crates/modules/charka/SDD.md
index bd20fbc..611bf3d 100644
--- a/crates/modules/charka/SDD.md
+++ b/crates/modules/charka/SDD.md
@@ -85,8 +85,9 @@ Tercera etapa: `Program` → `Ir`. Aquí se parsea cada `Sentence` cruda
   reimplementar la clasificación.
 - `Procedure { name, body: Vec<Stmt> }`. `Stmt` cubre `Move`,
   `Display`, `Accept`, `Compute`, `Add`/`Subtract`/`Multiply`/`Divide`,
-  `If`, `Evaluate`, `StringConcat`, `Unstring`, `Inspect`, `Perform`,
-  `GoTo`, `StopRun`, `Goback`, `Exit`, `Continue`.
+  `If`, `Evaluate`, `StringConcat`, `Unstring`, `Inspect`,
+  `Initialize`, `Perform`, `GoTo`, `StopRun`, `Goback`, `Exit`,
+  `Continue`.
 - `Expr` — expresiones aritméticas con precedencia y paréntesis (Pratt:
   `+ -` < `* /` < `**` der.). `Cond` — comparaciones (símbolo o forma
   palabra) unidas por `AND`/`OR`/`NOT`, más nombres de condición
@@ -104,6 +105,9 @@ Tercera etapa: `Program` → `Ir`. Aquí se parsea cada `Sentence` cruda
 - `STRING` (concatenación) y `UNSTRING` (partición por delimitador) —
   el manejo de cadenas. `INSPECT` — contar (`TALLYING FOR ALL`) y
   reemplazar (`REPLACING ALL`).
+- `INITIALIZE` — resetea un dato (o todos los elementales de un grupo)
+  a su valor por defecto. El `model` registra los grupos y sus
+  miembros (`DataModel::groups`).
 - Fuera de alcance v1: E/S de ficheros, CICS, SQL embebido.
 
 ## charka-runtime
@@ -175,8 +179,8 @@ que corre el `Ir` directamente sobre `charka-runtime`, sin compilar.
 
 ## El corpus
 
-`crates/modules/charka/corpus/` — 14 programas COBOL graduados
-(`01-hola` … `14-clasifica`), cada uno con su `.expected`. Ejercita el
+`crates/modules/charka/corpus/` — 15 programas COBOL graduados
+(`01-hola` … `15-resetear`), cada uno con su `.expected`. Ejercita el
 pipeline completo de punta a punta. Ver su `README.md`.
 
 ## La CLI
diff --git a/crates/modules/charka/charka-codegen/src/lib.rs b/crates/modules/charka/charka-codegen/src/lib.rs
index df69ee9..92cffc0 100644
--- a/crates/modules/charka/charka-codegen/src/lib.rs
+++ b/crates/modules/charka/charka-codegen/src/lib.rs
@@ -280,8 +280,8 @@ mod tests {
     fn unknown_verb_becomes_a_comment() {
         let out = gen("PROCEDURE DIVISION.\n\
              MAIN.\n\
-                 INITIALIZE WS-X.\n");
-        assert!(out.contains("// charka: verbo no transpilado — INITIALIZE"));
+                 CALL 'SUBPROG'.\n");
+        assert!(out.contains("// charka: verbo no transpilado — CALL"));
     }
 
     #[test]
@@ -413,6 +413,20 @@ mod tests {
         assert!(out.contains("> (dec(\"5\"))"));
     }
 
+    #[test]
+    fn initialize_resets_group_members() {
+        let out = gen("DATA DIVISION.\n\
+             WORKING-STORAGE SECTION.\n\
+             01 WS-REC.\n\
+                05 WS-A PIC 9(3).\n\
+                05 WS-B PIC X(4).\n\
+             PROCEDURE DIVISION.\n\
+             MAIN.\n\
+                 INITIALIZE WS-REC.\n");
+        assert!(out.contains("self.ws_a.store(Decimal::zero());"));
+        assert!(out.contains("self.ws_b.fill(' ');"));
+    }
+
     #[test]
     fn empty_program_still_compiles_shape() {
         let out = gen("");
diff --git a/crates/modules/charka/charka-codegen/src/stmt.rs b/crates/modules/charka/charka-codegen/src/stmt.rs
index d0903e5..3542dba 100644
--- a/crates/modules/charka/charka-codegen/src/stmt.rs
+++ b/crates/modules/charka/charka-codegen/src/stmt.rs
@@ -86,6 +86,7 @@ pub(crate) fn emit_stmt(em: &mut Emitter, sym: &Symbols, stmt: &Stmt) {
             into,
         } => emit_unstring(em, sym, source, delimiter, into),
         Stmt::Inspect { target, op } => emit_inspect(em, sym, target, op),
+        Stmt::Initialize { targets } => emit_initialize(em, sym, targets),
         Stmt::Perform(p) => emit_perform(em, sym, p),
         Stmt::GoTo { target } => {
             em.line(&format!(
@@ -472,6 +473,56 @@ fn emit_inspect(em: &mut Emitter, sym: &Symbols, target: &Operand, op: &InspectO
     }
 }
 
+/// `INITIALIZE` — pone cada destino (o los miembros de un grupo) en su
+/// valor por defecto.
+fn emit_initialize(em: &mut Emitter, sym: &Symbols, targets: &[Operand]) {
+    for t in targets {
+        match t {
+            Operand::Data(name) => match sym.group(name) {
+                Some(members) => {
+                    for m in members {
+                        emit_reset(em, sym, m);
+                    }
+                }
+                None => emit_reset(em, sym, name),
+            },
+            Operand::Indexed { .. } => emit_reset_element(em, sym, t),
+            _ => {}
+        }
+    }
+}
+
+/// Resetea un campo completo (escalar o tabla entera).
+fn emit_reset(em: &mut Emitter, sym: &Symbols, name: &str) {
+    let Some(f) = sym.lookup(name) else {
+        em.line(&format!("// charka: INITIALIZE de {name} no resuelto"));
+        return;
+    };
+    let reset = match f.kind {
+        FieldKind::Num { .. } => "store(Decimal::zero())",
+        FieldKind::Text { .. } => "fill(' ')",
+    };
+    match f.occurs {
+        None => em.line(&format!("self.{}.{reset};", f.ident)),
+        Some(_) => {
+            em.line(&format!("for __e in self.{}.iter_mut() {{", f.ident));
+            em.indent();
+            em.line(&format!("__e.{reset};"));
+            em.dedent();
+            em.line("}");
+        }
+    }
+}
+
+/// Resetea un solo elemento de tabla (`INITIALIZE ELEM(I)`).
+fn emit_reset_element(em: &mut Emitter, sym: &Symbols, op: &Operand) {
+    match field_ref(sym, op) {
+        Some((lref, FieldKind::Num { .. })) => em.line(&format!("{lref}.store(Decimal::zero());")),
+        Some((lref, FieldKind::Text { .. })) => em.line(&format!("{lref}.fill(' ');")),
+        None => em.line("// charka: INITIALIZE no resuelto"),
+    }
+}
+
 fn emit_perform(em: &mut Emitter, sym: &Symbols, p: &Perform) {
     // 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 {
diff --git a/crates/modules/charka/charka-codegen/src/sym.rs b/crates/modules/charka/charka-codegen/src/sym.rs
index 1b319a5..95b6817 100644
--- a/crates/modules/charka/charka-codegen/src/sym.rs
+++ b/crates/modules/charka/charka-codegen/src/sym.rs
@@ -24,11 +24,12 @@ pub(crate) struct Field {
     pub occurs: Option<u32>,
 }
 
-/// Los campos del programa y sus nombres de condición, indexados.
+/// Los campos del programa, sus nombres de condición y sus grupos.
 pub(crate) struct Symbols {
     pub fields: Vec<Field>,
     by_name: HashMap<String, usize>,
     conditions: HashMap<String, ConditionName>,
+    groups: HashMap<String, Vec<String>>,
 }
 
 impl Symbols {
@@ -56,13 +57,24 @@ impl Symbols {
             .iter()
             .map(|c| (c.name.clone(), c.clone()))
             .collect();
+        let groups = model
+            .groups
+            .iter()
+            .map(|g| (g.name.clone(), g.members.clone()))
+            .collect();
         Self {
             fields,
             by_name,
             conditions,
+            groups,
         }
     }
 
+    /// Los miembros de un grupo, si `name` es un grupo.
+    pub(crate) fn group(&self, name: &str) -> Option<&[String]> {
+        self.groups.get(&name.to_uppercase()).map(|v| v.as_slice())
+    }
+
     /// Busca un campo por su nombre COBOL (sin distinguir mayúsculas).
     pub(crate) fn lookup(&self, cobol: &str) -> Option<&Field> {
         self.by_name
diff --git a/crates/modules/charka/charka-ir/src/ast.rs b/crates/modules/charka/charka-ir/src/ast.rs
index 9a0db93..77e6238 100644
--- a/crates/modules/charka/charka-ir/src/ast.rs
+++ b/crates/modules/charka/charka-ir/src/ast.rs
@@ -185,6 +185,9 @@ pub enum Stmt {
     },
     /// `INSPECT target ...` — cuenta o reemplaza caracteres.
     Inspect { target: Operand, op: InspectOp },
+    /// `INITIALIZE targets...` — pone cada dato (o grupo) en su valor
+    /// por defecto: 0 los numéricos, espacios los alfanuméricos.
+    Initialize { targets: Vec<Operand> },
     /// `PERFORM ...` — ver [`Perform`].
     Perform(Perform),
     /// `GO TO target`
diff --git a/crates/modules/charka/charka-ir/src/lib.rs b/crates/modules/charka/charka-ir/src/lib.rs
index 996b4a4..93a29c6 100644
--- a/crates/modules/charka/charka-ir/src/lib.rs
+++ b/crates/modules/charka/charka-ir/src/lib.rs
@@ -32,7 +32,7 @@ mod stmt;
 
 pub use ast::*;
 pub use charka_parser::Program;
-pub use model::{resolve_data, ConditionName, DataModel, Field, FieldKind};
+pub use model::{resolve_data, ConditionName, DataModel, Field, FieldKind, GroupInfo};
 
 use cursor::Cursor;
 
@@ -440,6 +440,26 @@ mod tests {
         }
     }
 
+    #[test]
+    fn initialize_parses_and_groups_are_modeled() {
+        let program = ir("DATA DIVISION.\n\
+             WORKING-STORAGE SECTION.\n\
+             01 WS-REC.\n\
+                05 WS-A PIC 9(3).\n\
+                05 WS-B PIC X(4).\n\
+             PROCEDURE DIVISION.\n\
+             MAIN.\n\
+                 INITIALIZE WS-REC.\n");
+        let g = program.model.group("WS-REC").expect("grupo WS-REC");
+        assert_eq!(g.members, vec!["WS-A".to_string(), "WS-B".to_string()]);
+        match &program.procedures[0].body[0] {
+            Stmt::Initialize { targets } => {
+                assert_eq!(targets, &vec![Operand::Data("WS-REC".into())]);
+            }
+            other => panic!("se esperaba INITIALIZE, vino {other:?}"),
+        }
+    }
+
     #[test]
     fn several_statements_in_one_sentence() {
         let b = body("MOVE 1 TO X DISPLAY X STOP RUN.");
@@ -451,10 +471,10 @@ mod tests {
 
     #[test]
     fn unrecognized_verb_becomes_unknown() {
-        let b = body("INITIALIZE WS-X WS-Y.");
+        let b = body("CALL 'SUBPROG' USING WS-X.");
         match &b[0] {
             Stmt::Unknown { verb, tokens } => {
-                assert_eq!(verb, "INITIALIZE");
+                assert_eq!(verb, "CALL");
                 assert!(!tokens.is_empty());
             }
             other => panic!("se esperaba Unknown, vino {other:?}"),
diff --git a/crates/modules/charka/charka-ir/src/model.rs b/crates/modules/charka/charka-ir/src/model.rs
index 84e61f8..44ec964 100644
--- a/crates/modules/charka/charka-ir/src/model.rs
+++ b/crates/modules/charka/charka-ir/src/model.rs
@@ -45,6 +45,16 @@ pub struct ConditionName {
     pub value: Operand,
 }
 
+/// Un grupo de datos: su nombre y los datos elementales que contiene
+/// (recursivamente).
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct GroupInfo {
+    /// Nombre COBOL del grupo, en mayúsculas.
+    pub name: String,
+    /// Nombres COBOL de los datos elementales que agrupa.
+    pub members: Vec<String>,
+}
+
 /// El modelo de datos resuelto de un programa.
 #[derive(Debug, Clone, PartialEq, Default)]
 pub struct DataModel {
@@ -52,6 +62,8 @@ pub struct DataModel {
     pub fields: Vec<Field>,
     /// Los nombres de condición (nivel 88).
     pub conditions: Vec<ConditionName>,
+    /// Los grupos y sus datos elementales.
+    pub groups: Vec<GroupInfo>,
 }
 
 impl DataModel {
@@ -66,6 +78,12 @@ impl DataModel {
         let up = name.to_uppercase();
         self.conditions.iter().find(|c| c.name == up)
     }
+
+    /// Busca un grupo por su nombre COBOL.
+    pub fn group(&self, name: &str) -> Option<&GroupInfo> {
+        let up = name.to_uppercase();
+        self.groups.iter().find(|g| g.name == up)
+    }
 }
 
 /// Aplana el árbol de datos en un [`DataModel`].
@@ -75,9 +93,12 @@ pub fn resolve_data(data: &[DataItem]) -> DataModel {
     model
 }
 
-/// Recorre el árbol: registra los 88 como condiciones sobre su dato
-/// padre, recurre en los grupos y emite los datos elementales.
-fn walk(items: &[DataItem], model: &mut DataModel) {
+/// Recorre el árbol: registra los 88 como condiciones, los grupos con
+/// sus miembros, y emite los datos elementales. Devuelve los nombres
+/// de los datos elementales producidos (para que el grupo que llama
+/// los reúna como sus miembros).
+fn walk(items: &[DataItem], model: &mut DataModel) -> Vec<String> {
+    let mut produced = Vec::new();
     for it in items {
         if it.level == 66 || it.level == 88 {
             // Los 88 los registra su dato padre; los 66 se omiten.
@@ -96,13 +117,21 @@ fn walk(items: &[DataItem], model: &mut DataModel) {
         // Un dato con hijos «reales» (no 88/66) es un grupo.
         let is_group = it.children.iter().any(|c| c.level != 88 && c.level != 66);
         if is_group {
-            walk(&it.children, model);
+            let members = walk(&it.children, model);
+            if it.name != "FILLER" {
+                model.groups.push(GroupInfo {
+                    name: it.name.to_uppercase(),
+                    members: members.clone(),
+                });
+            }
+            produced.extend(members);
         } else if it.name != "FILLER" {
             if let Some(kind) = classify(it.picture.as_deref()) {
                 let init = match kind {
                     FieldKind::Num { .. } => numeric_value(it.value.as_deref()),
                     FieldKind::Text { .. } => text_value(it.value.as_deref()),
                 };
+                produced.push(it.name.to_uppercase());
                 model.fields.push(Field {
                     name: it.name.to_uppercase(),
                     kind,
@@ -112,6 +141,7 @@ fn walk(items: &[DataItem], model: &mut DataModel) {
             }
         }
     }
+    produced
 }
 
 /// Clasifica una cláusula PICTURE: alfanumérica si tiene `X`/`A`,
diff --git a/crates/modules/charka/charka-ir/src/stmt.rs b/crates/modules/charka/charka-ir/src/stmt.rs
index 5c03a9b..ca570d6 100644
--- a/crates/modules/charka/charka-ir/src/stmt.rs
+++ b/crates/modules/charka/charka-ir/src/stmt.rs
@@ -44,6 +44,7 @@ fn parse_one_stmt(c: &mut Cursor, stops: &[&str]) -> Stmt {
         "STRING" => parse_string(c),
         "UNSTRING" => parse_unstring(c),
         "INSPECT" => parse_inspect(c),
+        "INITIALIZE" => parse_initialize(c),
         "PERFORM" => parse_perform(c),
         "GO" => parse_goto(c),
         "STOP" => parse_stop(c),
@@ -390,6 +391,14 @@ fn parse_unstring(c: &mut Cursor) -> Stmt {
     }
 }
 
+fn parse_initialize(c: &mut Cursor) -> Stmt {
+    c.bump(); // INITIALIZE
+    let mut rounded = false;
+    let targets = parse_targets(c, &mut rounded);
+    skip_to_stmt_boundary(c); // p. ej. la cláusula `REPLACING`
+    Stmt::Initialize { targets }
+}
+
 fn parse_inspect(c: &mut Cursor) -> Stmt {
     c.bump(); // INSPECT
     let target = parse_operand(c);
diff --git a/crates/modules/charka/charka-shadow/src/interp.rs b/crates/modules/charka/charka-shadow/src/interp.rs
index 0b6e2ba..7bf19f6 100644
--- a/crates/modules/charka/charka-shadow/src/interp.rs
+++ b/crates/modules/charka/charka-shadow/src/interp.rs
@@ -11,7 +11,7 @@ use charka_ir::{
     BinOp, CmpOp, Cond, ConditionName, Expr, Figurative, InspectOp, Ir, Operand, Perform,
     PerformControl, PerformTarget, Stmt, WhenTest,
 };
-use charka_runtime::{cobol_text_cmp, Decimal, Rounding};
+use charka_runtime::{cobol_text_cmp, Decimal, Num, Rounding, Text};
 
 use crate::field::{build_fields, Cell};
 
@@ -293,6 +293,25 @@ impl<'a> Machine<'a> {
                 }
                 Flow::Normal
             }
+            Stmt::Initialize { targets } => {
+                for t in targets {
+                    match t {
+                        Operand::Data(name) => {
+                            match self.ir.model.group(name).map(|g| g.members.clone()) {
+                                Some(members) => {
+                                    for m in &members {
+                                        self.reset_field(m);
+                                    }
+                                }
+                                None => self.reset_field(name),
+                            }
+                        }
+                        Operand::Indexed { .. } => self.reset_element(t),
+                        _ => {}
+                    }
+                }
+                Flow::Normal
+            }
             Stmt::Perform(p) => self.exec_perform(p),
             Stmt::GoTo { target } => {
                 // Aproximación: ejecuta el destino y sale del párrafo.
@@ -456,6 +475,44 @@ impl<'a> Machine<'a> {
         }
     }
 
+    /// Resetea un campo completo (escalar o tabla) a su valor por
+    /// defecto: 0 si es numérico, espacios si es alfanumérico.
+    fn reset_field(&mut self, name: &str) {
+        match self.fields.get_mut(&name.to_uppercase()) {
+            Some(Cell::Num(arr)) => {
+                for n in arr.iter_mut() {
+                    *n = Num::new(n.picture());
+                }
+            }
+            Some(Cell::Text(arr)) => {
+                for t in arr.iter_mut() {
+                    *t = Text::new(t.len());
+                }
+            }
+            None => {}
+        }
+    }
+
+    /// Resetea un solo elemento de tabla a su valor por defecto.
+    fn reset_element(&mut self, op: &Operand) {
+        let Some((key, idx)) = self.resolve(op) else {
+            return;
+        };
+        match self.fields.get_mut(&key) {
+            Some(Cell::Num(arr)) => {
+                if let Some(n) = arr.get_mut(idx) {
+                    *n = Num::new(n.picture());
+                }
+            }
+            Some(Cell::Text(arr)) => {
+                if let Some(t) = arr.get_mut(idx) {
+                    *t = Text::new(t.len());
+                }
+            }
+            None => {}
+        }
+    }
+
     /// Almacena un texto en un destino, conformándolo a su tipo.
     fn store_text(&mut self, target: &Operand, text: &str) {
         let Some((key, idx)) = self.resolve(target) else {
diff --git a/crates/modules/charka/charka-shadow/src/lib.rs b/crates/modules/charka/charka-shadow/src/lib.rs
index 259c867..55c84fb 100644
--- a/crates/modules/charka/charka-shadow/src/lib.rs
+++ b/crates/modules/charka/charka-shadow/src/lib.rs
@@ -123,6 +123,7 @@ mod tests {
     corpus_test!(corpus_12_cadenas, "12-cadenas");
     corpus_test!(corpus_13_inspeccion, "13-inspeccion");
     corpus_test!(corpus_14_clasifica, "14-clasifica");
+    corpus_test!(corpus_15_resetear, "15-resetear");
 
     #[test]
     fn empty_source_runs_clean() {
diff --git a/crates/modules/charka/corpus/15-resetear.cob b/crates/modules/charka/corpus/15-resetear.cob
new file mode 100644
index 0000000..c08d11e
--- /dev/null
+++ b/crates/modules/charka/corpus/15-resetear.cob
@@ -0,0 +1,20 @@
+* corpus charka — nivel 6: INITIALIZE (resetear datos y grupos)
+IDENTIFICATION DIVISION.
+PROGRAM-ID. RESETEAR.
+DATA DIVISION.
+WORKING-STORAGE SECTION.
+01 WS-REGISTRO.
+   05 WS-NOMBRE PIC X(6) VALUE 'PEDRO'.
+   05 WS-EDAD   PIC 9(3) VALUE 42.
+01 WS-CONTADOR PIC 9(3) VALUE 77.
+PROCEDURE DIVISION.
+MAIN.
+    DISPLAY 'ANTES NOMBRE=' WS-NOMBRE.
+    DISPLAY 'ANTES EDAD=' WS-EDAD.
+    DISPLAY 'ANTES CONT=' WS-CONTADOR.
+    INITIALIZE WS-REGISTRO.
+    INITIALIZE WS-CONTADOR.
+    DISPLAY 'DESP NOMBRE=' WS-NOMBRE.
+    DISPLAY 'DESP EDAD=' WS-EDAD.
+    DISPLAY 'DESP CONT=' WS-CONTADOR.
+    STOP RUN.
diff --git a/crates/modules/charka/corpus/15-resetear.expected b/crates/modules/charka/corpus/15-resetear.expected
new file mode 100644
index 0000000..bed8f2f
--- /dev/null
+++ b/crates/modules/charka/corpus/15-resetear.expected
@@ -0,0 +1,6 @@
+ANTES NOMBRE=PEDRO
+ANTES EDAD=042
+ANTES CONT=077
+DESP NOMBRE=
+DESP EDAD=000
+DESP CONT=000
diff --git a/crates/modules/charka/corpus/README.md b/crates/modules/charka/corpus/README.md
index 629c481..2c118d6 100644
--- a/crates/modules/charka/corpus/README.md
+++ b/crates/modules/charka/corpus/README.md
@@ -23,6 +23,7 @@ salida correcta, una línea por `DISPLAY`.
 | `12-cadenas`        | 6     | `STRING` (concatenar) y `UNSTRING` (partir)        |
 | `13-inspeccion`     | 6     | `INSPECT` — contar (`TALLYING`) y reemplazar       |
 | `14-clasifica`      | 6     | `EVALUATE TRUE` y rangos `WHEN ... THRU`           |
+| `15-resetear`       | 6     | `INITIALIZE` — resetear datos y grupos             |
 
 ## Formato
 
diff --git a/docs/changelog/charka.md b/docs/changelog/charka.md
index 5201887..c72a843 100644
--- a/docs/changelog/charka.md
+++ b/docs/changelog/charka.md
@@ -3,6 +3,21 @@
 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.
 
+### feat(charka): INITIALIZE — resetear datos y grupos
+
+El verbo de COBOL para volver un dato (o un registro entero) a su
+valor por defecto.
+
+- IR: `Stmt::Initialize { targets }`. El `model` de `charka-ir`
+  registra ahora los grupos y sus datos elementales
+  (`DataModel::groups`, `GroupInfo`).
+- Parser: `INITIALIZE name-1 name-2 ...`.
+- Codegen y shadow: cada destino, si es un grupo, se expande a sus
+  miembros; cada dato elemental se pone a 0 (numérico) o a espacios
+  (alfanumérico); una tabla `OCCURS` resetea todos sus elementos.
+- Corpus: programa nuevo `15-resetear` (resetea un grupo y un escalar).
+  Verificado en ambas rutas.
+
 ### feat(charka): EVALUATE TRUE y rangos WHEN ... THRU
 
 Completa el `EVALUATE` con sus dos formas que faltaban.