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>
This commit is contained in:
sergio
2026-05-21 22:28:47 +00:00
parent 7867d6830e
commit fa65f20206
15 changed files with 260 additions and 17 deletions
+34 -4
View File
@@ -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`,