Files
brahman/docs/changelog/charka.md
T
sergio 4df7478b71 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>
2026-05-21 21:37:28 +00:00

224 lines
11 KiB
Markdown

# 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): 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 <in.cob> [-o out.rs]` — emite el código Rust (a un archivo
o a la salida estándar).
- `scaffold <in.cob> -o <dir>` — genera un crate Rust completo
(`Cargo.toml` + `src/main.rs`) que depende de `charka-runtime` y
compila tal cual.
- `run <in.cob>` — ejecuta el programa con el intérprete sombra y
muestra su salida, sin compilar nada.
- `check <in.cob> -e <esperado>` — 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_<párrafo>(&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<Token>` → `Program` (AST). Alcance v1: el esqueleto del
programa.
- `parse(&[Token]) -> Result<Program, ParseError>`. 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<Paragraph>`, 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.