# Changelog — charka 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): INSPECT — contar y reemplazar caracteres El verbo de COBOL para analizar y limpiar campos de texto. - IR: `Stmt::Inspect { target, op }` con `InspectOp::TallyingForAll` (cuenta apariciones y las suma a un contador) y `InspectOp::ReplacingAll` (reemplaza apariciones). - Parser: `INSPECT t TALLYING n FOR ALL lit` y `INSPECT t REPLACING ALL a BY b`. Una forma no soportada cae a `Stmt::Unknown`. - Codegen: `TALLYING` → `str::matches(..).count()`; `REPLACING` → `str::replace`. - Shadow: el intérprete cuenta / reemplaza el texto. - Corpus: programa nuevo `13-inspeccion`. Verificado: el intérprete sombra y el crate compilado dan la misma salida. - Alcance v1: `TALLYING FOR ALL` y `REPLACING ALL`; sin `LEADING`, `FIRST`, `CHARACTERS`, `BEFORE`/`AFTER`. ### feat(charka): STRING y UNSTRING — manejo de cadenas Dos verbos comunes de COBOL para construir y partir cadenas. - IR: `Stmt::StringConcat { sources, into }` y `Stmt::Unstring { source, delimiter, into }`. - Parser: `STRING a b DELIMITED BY SIZE INTO t END-STRING` y `UNSTRING s DELIMITED BY d INTO a b c END-UNSTRING`. - Codegen: `STRING` → `format!` concatenado; `UNSTRING` → un bloque que parte con `str::split` y reparte los trozos. - Shadow: el intérprete concatena / parte el texto y lo reparte. - Corpus: programa nuevo `12-cadenas`. Verificado: el intérprete sombra y el crate compilado dan la misma salida. - Alcance v1: `STRING` con `DELIMITED BY SIZE` (los demás delimitadores se ignoran); sin `WITH POINTER` ni `ON OVERFLOW`. ### feat(charka): OCCURS — tablas y referencias con subíndice Los arrays de COBOL, que antes el transpilador descartaba en silencio. Atraviesa el pipeline entero. - Parser: la cláusula `OCCURS n [TIMES]` se captura en `DataItem`. - IR: `Operand::Indexed { name, index }` — una referencia `ELEM(I)`. El subíndice es 1-based, como COBOL. Los destinos de los statements pasan de `Vec` a `Vec`, así que se puede escribir a un elemento de tabla (`MOVE x TO ELEM(I)`, `COMPUTE ELEM(I) = ...`). `Field` del modelo gana `occurs: Option`. - Codegen: un campo `OCCURS` se emite como `Vec`/`Vec`, inicializado con `vec![..; n]`; una referencia con subíndice indexa el vector (1-based → 0-based). - Shadow: en el intérprete todo campo es un vector — un escalar es de longitud 1, una tabla de `n`; las referencias se resuelven a `(nombre, índice)`. - Corpus: programa nuevo `11-tabla` (llena una tabla con cuadrados y los suma). Verificado: el intérprete sombra y el crate compilado por `scaffold` dan ambos `SUMA DE CUADRADOS = 000055`. - Alcance v1: `OCCURS` en datos elementales, una dimensión, subíndice de un solo operando. Fuera: `OCCURS` de grupo, multidimensional, `OCCURS DEPENDING ON`. ### feat(charka): nombres de condición (nivel 88) + modelo de datos compartido `IF ES-VALIDO` — los nombres de condición de COBOL, que antes el transpilador evaluaba siempre como `false`. Y, de paso, se elimina una duplicación: la resolución del modelo de datos. - `charka-ir` gana un módulo `model`: `resolve_data(&[DataItem]) -> DataModel` aplana el árbol de datos a campos elementales (`Field` con `FieldKind`) y a nombres de condición (`ConditionName`). El `Ir` ahora lleva un campo `model`. Es la fuente única de verdad sobre la clasificación de PICTURE. - `charka-codegen` y `charka-shadow` consumen `ir.model` en vez de reimplementar cada uno la clasificación numérico/alfanumérico, el ancho de PICTURE y la normalización de `VALUE`. `charka-codegen` ya no depende de `charka-bcd`. - `Cond::Named` (un nivel 88) se resuelve a `padre = valor`: el codegen emite la comparación, el intérprete sombra la evalúa. - Corregido de paso: un dato con hijos de nivel 88 (p. ej. `01 WS-FLAG PIC X. 88 ES-SI VALUE 'Y'.`) antes se perdía como si fuera un grupo; ahora se reconoce como campo elemental. - Corpus: programa nuevo `10-condicion` (un semáforo con 88 de texto y de número, en `IF` e `IF/ELSE`). Verificado: intérprete y crate compilado dan la misma salida. ### 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 El bucle más usado de COBOL, que antes el parser degradaba a un `PERFORM` vacío. Ahora atraviesa el pipeline entero. - IR: `PerformControl::Varying { var, from, by, until }`. - Parser: reconoce `PERFORM VARYING var FROM x BY y UNTIL cond` en línea (`END-PERFORM`) y fuera de línea (`PERFORM párrafo VARYING …`). - Codegen: emite `var = from; while !(until) { cuerpo; var += by; }`. - Shadow: el intérprete inicializa la variable, evalúa la condición antes de cada vuelta e incrementa al final. - Corpus: programa nuevo `08-varying` (suma 1..10 con `PERFORM VARYING`). Verificado: el intérprete sombra y el crate compilado por `scaffold` dan ambos `SUMA 1 A 10 = 00055`. ### feat(charka): CLI del transpilador — transpile / scaffold / run / check App nueva `crates/apps/charka` — el binario `charka`, que vuelve usable el pipeline desde la terminal. Cuatro comandos: - `transpile [-o out.rs]` — emite el código Rust (a un archivo o a la salida estándar). - `scaffold -o ` — genera un crate Rust completo (`Cargo.toml` + `src/main.rs`) que depende de `charka-runtime` y compila tal cual. - `run ` — ejecuta el programa con el intérprete sombra y muestra su salida, sin compilar nada. - `check -e ` — ejecuta y diferencia la salida contra un archivo esperado; reporta las líneas que difieren. Avisa de los verbos COBOL que aún no se transpilan. Verificado de punta a punta contra el corpus: `scaffold` de `06-nomina` genera un crate que compila y produce la misma salida que el intérprete sombra — las dos rutas de ejecución concuerdan. 4 tests; fmt + clippy limpios. ### feat(charka-shadow): validador en sombra + corpus COBOL Crate nuevo `crates/modules/charka/charka-shadow` y un corpus de prueba `crates/modules/charka/corpus/` — el pipeline COBOL→Rust queda completo y validado de punta a punta. - `charka-shadow` certifica que el transpilador preserva la semántica del COBOL original con una **ejecución sombra**: un intérprete que corre el `Ir` directamente sobre `charka-runtime`, sin compilar nada. Es una segunda ruta de ejecución, independiente del código que emite `charka-codegen`. - `interpret(&Ir) -> Outcome` ejecuta el IR y captura las líneas de `DISPLAY`; `run_source(&str)` corre el pipeline completo (lexer → parser → IR → intérprete). - Tope de pasos (`Halt::StepLimit`): un bucle que no termina se corta en vez de colgar la ejecución. - **Corpus**: 7 programas COBOL de complejidad graduada — `01-hola` (un `DISPLAY`), `02-aritmetica` (`ADD`/`SUBTRACT`/`MULTIPLY`), `03-condicional` (`IF`/`ELSE`), `04-bucle` (`PERFORM TIMES`), `05-factorial` (`PERFORM UNTIL`), `06-nomina` (grupos, `COMPUTE` con paréntesis, `ROUNDED`, `V99`), `07-clasificar` (`IF` anidado, `AND`). Cada uno con su `.expected` verificada a mano. - 11 tests: los 7 programas del corpus + fuente vacío, `STOP RUN`, corte por tope de pasos y propagación de error de léxico. ### feat(charka-codegen): emisión de Rust desde el IR Crate nuevo `crates/modules/charka/charka-codegen` — la etapa final del pipeline. `generate(&Ir) -> String` produce un fuente Rust (un `main.rs`) que, compilado contra `charka-runtime`, ejecuta la lógica del programa COBOL. - Un `struct Program` con un campo por cada dato elemental — `Num` para los numéricos, `Text` para los alfanuméricos; `Program::new()` los inicializa desde sus cláusulas `VALUE`. - Un método `p_(&mut self)` por cada párrafo del PROCEDURE; `run()` los encadena en orden (el «caer» de COBOL); `main()` construye el `Program` y lo corre. - Cada `Stmt` → código Rust: `MOVE`→`.store`/`.fill`, `DISPLAY`→`println!`, `COMPUTE` y la aritmética → expresiones `Decimal`, `IF`→`if`/`else`, `PERFORM`→ llamada de método / `for` / `while`, `GO TO`→ llamada + `return`, `STOP RUN`→`process::exit`. - Tolerante: lo no transpilable (`Stmt::Unknown`, un dato sin resolver, el operador `**`) se emite como comentario `// charka:`; el código generado siempre compila. - Saneado de identificadores COBOL→Rust (incl. choques con keywords). - Verificado de punta a punta: un programa COBOL de demostración transpila a Rust que compila contra `charka-runtime` y produce la salida esperada (`COMPUTE`, `IF`, `PERFORM`, `DISPLAY`). - 14 tests del fuente emitido; fuera de alcance v1: grupos como campo, `REDEFINES`, `OCCURS`, `PERFORM ... THRU` como rango, E/S. ### feat(charka-runtime): soporte de ejecución — campos Num y Text Crate nuevo `crates/modules/charka/charka-runtime` — el soporte que los programas COBOL transpilados enlazan. `charka-codegen` no emitirá Rust autónomo: emitirá Rust que llama a esta biblioteca. - `Num` — campo numérico (`PIC 9(5)V99`): un `Decimal` conformado a su `Picture`. `store` trunca a la escala declarada; `store_rounded` redondea; al desbordar la parte entera conserva los dígitos de bajo orden (el `ON SIZE ERROR` de COBOL sin cláusula). `display` da los dígitos con relleno de ceros y signo si corresponde. - `Text` — campo alfanumérico (`PIC X(n)`) de longitud fija: `store` justifica a la izquierda y rellena con espacios o trunca; `fill` mueve las constantes figurativas (`SPACES`, `ZEROS`). - `cobol_text_cmp` — comparación alfanumérica que rellena el más corto con espacios. - Reexporta `Decimal`/`Picture`/`Rounding` de `charka-bcd`. - Construido antes que `charka-codegen` (la nota de orden del plan los listaba al revés): el codegen emite contra esta API, así que el runtime debe existir primero — y se verifica solo, sin el codegen. - 17 tests: campo en cero, `VALUE` inicial, truncado y redondeo, desbordamiento que conserva bajo orden, magnitud sin signo y signo con signo, justificación y relleno de texto, `fill`, comparación. ### feat(charka-ir): representación intermedia — statements tipados Crate nuevo `crates/modules/charka/charka-ir` — la tercera etapa del pipeline: `Program` → `Ir`. El PROCEDURE division pasa de sentencias con tokens crudos a un árbol de instrucciones tipadas. - `lower(&Program) -> Ir` — total y tolerante: nunca falla. - `Ir { program_id, data, procedures }`. El modelo de datos (la DATA division) pasa tal cual — sirve de tabla de símbolos. - `Stmt` cubre `MOVE`, `DISPLAY`, `ACCEPT`, `COMPUTE`, `ADD`, `SUBTRACT`, `MULTIPLY`, `DIVIDE`, `IF`/`ELSE`/`END-IF`, `PERFORM` (fuera de línea, en línea, `TIMES`, `UNTIL`), `GO TO`, `STOP RUN`, `GOBACK`, `EXIT`, `CONTINUE`. - Expresiones de `COMPUTE` con precedencia y paréntesis (`+ -` < `* /` < `**` asociativo a derecha). Condiciones de `IF`/`UNTIL` con comparadores en forma símbolo (`= < > <= >= <>`) o palabra (`EQUAL TO`, `GREATER THAN`...), `AND`/`OR`/`NOT` y nombres de condición (datos de nivel 88). - COBOL no termina los statements con un símbolo: el parser delimita las listas de operandos con palabras "frontera" (verbos, terminadores `END-*`/`ELSE`, conectores `TO`/`GIVING`/`BY`...). - Un verbo no soportado se conserva como `Stmt::Unknown { verb, tokens }` — el lowering nunca aborta. - Fuera de alcance v1: `EVALUATE`, `STRING`/`UNSTRING`, E/S de ficheros, `PERFORM VARYING`, CICS, SQL embebido. - 17 tests: MOVE simple y multi-destino, DISPLAY con figurativas, precedencia de COMPUTE, flag ROUNDED, ADD in-place vs GIVING, SUBTRACT, DIVIDE BY/INTO, IF/ELSE, condiciones con AND, nombre de condición, PERFORM párrafo/TIMES/UNTIL en línea, varios statements en una sentencia, verbo desconocido, programa completo. ### feat(charka-parser): parser COBOL'85 → AST Crate nuevo `crates/modules/charka/charka-parser` — la segunda etapa del pipeline: `Vec` → `Program` (AST). Alcance v1: el esqueleto del programa. - `parse(&[Token]) -> Result`. El AST: `Program` (`program_id`, `data`, `paragraphs`), `DataItem`, `Paragraph`, `Sentence`. - Particiona el flujo de tokens en las cuatro divisiones por sus encabezados (`X DIVISION`); de la IDENTIFICATION extrae el `PROGRAM-ID`. - **DATA division** → árbol de `DataItem`: número de nivel, nombre, cláusula `PICTURE` reensamblada (`S9` `(` `5` `)` `V99` → `S9(5)V99`) y `VALUE`. Anida por número de nivel — 01 y 77 son raíces, 88 cuelga del ítem precedente. - **PROCEDURE division** → `Vec`, cada párrafo con sus `Sentence` (tokens crudos; sin parseo a nivel de statement, eso es `charka-ir`). Las sentencias previas al primer encabezado van a un párrafo implícito de nombre "". - Parser tolerante: salta encabezados de `SECTION`, entradas `FD`/`SD` y cláusulas de datos que no sean `PICTURE`/`VALUE`. `ParseError` sólo ante un número de nivel inválido o un dato sin nombre. - 15 tests: PROGRAM-ID (forma larga y `ID` corta), ítems planos y anidados, reensamblado de PICTURE, variantes de VALUE, niveles 88, `FILLER`, párrafos y párrafo implícito, encabezado de sección, programa completo de punta a punta, nivel inválido. ### feat(charka-lexer): tokenizador de COBOL Crate nuevo `crates/modules/charka/charka-lexer` — la primera etapa del pipeline del transpilador: texto COBOL → secuencia de `Token`. - **Lexer deliberadamente tonto**: no conoce keywords ni la cláusula `PICTURE`; emite `Word` para todo identificador y deja la clasificación al parser. - Tokens: `Word` (palabras COBOL con guiones internos), `Number` (literal sin signo), `String` (comillas dobladas colapsadas), `Period` (el `.` terminador), `Symbol` (paréntesis, separadores y operadores `+ - * / ** = < > <= >= <>`). - Dos formatos de fuente: **fijo** (la tarjeta de 80 columnas — cols 1-6 secuencia, 7 indicadora, 8-72 código, 73-80 identificación) y **libre**. Comentarios por la columna indicadora (`*`/`/`) o por `*` inicial en formato libre. - Cada `Token` lleva línea y columna 1-based; `LexError` tipado (literal sin cerrar, carácter inesperado). - Limitación v1 documentada: no soporta continuación de literales entre líneas (indicador `-`). Subconjunto COBOL'85, el hito intermedio. - 17 tests: sentencias, palabras con guiones, literales y comillas dobladas, números vs terminador, operadores, ambos formatos, tracking de posición, errores. ### feat(charka-bcd): aritmética decimal con semántica COBOL (Pre-existente.) `Picture` + `Decimal` de punto fijo exacto — ver el SDD del módulo. 22 tests.