13 Commits

Author SHA1 Message Date
sergio 634a43006a feat(charka): PICTURE de edición — Z, coma de millares y punto decimal
El formateo de informes de COBOL: supresión de ceros a la izquierda,
coma de millares e inserción del punto decimal. Rebanada vertical.

- charka-lexer: el punto separador exige un espacio detrás; un punto
  pegado a un carácter (ZZ9.99) ya no es terminador, sino símbolo —
  el parser lo reensambla dentro de la cláusula PICTURE.
- charka-runtime: format_edited(valor, pic) — 9, Z, coma, punto, B.
- charka-ir: Field::edit guarda la PICTURE; el campo es texto.
- charka-codegen / charka-shadow: MOVE a un campo de edición pasa por
  format_edited antes de almacenar.
- Corpus: 19-reporte. Sombra y crate compilado dan la misma salida.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 23:00:15 +00:00
sergio b3278bdb0c feat(charka): E/S de ficheros — SELECT/FD/OPEN/READ/WRITE/CLOSE
El gran hueco que faltaba para el COBOL real: el procesamiento de
ficheros secuenciales. Una rebanada vertical por los seis crates.

- charka-parser: la ENVIRONMENT division ya no se ignora — se parsea
  FILE-CONTROL (SELECT name ASSIGN TO "ruta"); del FILE SECTION se
  asocia cada FD con su registro 01. Program::files.
- charka-runtime: tipo CobFile — un fichero «line sequential» (cada
  registro una línea). Lectura: carga a memoria. Escritura: acumula y
  vuelca al cerrar.
- charka-ir: Ir::files y los statements Open/Close/Read/Write. READ
  lleva sus bloques AT END / NOT AT END.
- charka-codegen: un campo CobFile por fichero en el struct Program;
  los verbos emiten llamadas al runtime.
- charka-shadow: el intérprete hace E/S de ficheros real.
- Corpus: programa nuevo 18-fichero — escribe tres líneas, las relee
  con READ ... AT END y las muestra. Verificado: el intérprete sombra
  y el crate compilado por scaffold dan la misma salida.

Alcance v1: organización line sequential; sin ficheros indexados ni
relativos, sin FILE STATUS.

Tests: charka-parser 17, charka-runtime 19, charka-ir 30,
charka-codegen 25, charka-shadow 23. fmt + clippy limpios.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 22:47:26 +00:00
sergio f250fd0765 feat(charka): PERFORM ... THRU como rango real de párrafos
PERFORM A THRU C ejecuta A, B y C; antes el transpilador sólo
ejecutaba A (lo marcaba como aproximado).

- charka-codegen: Symbols registra ahora los párrafos en orden con su
  nombre de método; Symbols::build toma el Ir completo.
  paragraph_range(name, thru) da los métodos del rango; emit_perform
  emite la llamada a cada uno.
- charka-shadow: run_paragraph_range ejecuta los párrafos de name a
  thru inclusive.
- Corpus: programa nuevo 17-rangopar (PERFORM PASO-A THRU PASO-C sobre
  tres párrafos). Verificado: el intérprete sombra y el crate
  compilado por scaffold dan la misma salida.

Tests: charka-codegen 24, charka-shadow 22. fmt + clippy limpios.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 22:36:53 +00:00
sergio 82ba0b7a1a 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>
2026-05-21 22:32:08 +00:00
sergio fa65f20206 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>
2026-05-21 22:28:47 +00:00
sergio 7867d6830e feat(charka): EVALUATE TRUE y rangos WHEN ... THRU
Completa el EVALUATE con sus dos formas que faltaban.

- IR: la rama WhenBranch pasa de values: Vec<Operand> a
  tests: Vec<WhenTest>, donde WhenTest es Value (igualdad), Range
  (WHEN lo THRU hi) o Cond (EVALUATE TRUE WHEN cond).
- Parser: detecta EVALUATE TRUE y entonces cada WHEN parsea una
  condición; en modo valor reconoce WHEN lo THRU hi.
- Codegen y shadow: una prueba Range se traduce a lo <= s <= hi; una
  Cond, a la condición directa.
- Corpus: programa nuevo 14-clasifica (clasifica notas con rangos THRU
  y un EVALUATE TRUE). Verificado: intérprete sombra y crate compilado
  dan la misma salida.

Tests: charka-ir 27, charka-codegen 21, charka-shadow 19. fmt +
clippy limpios.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 22:22:43 +00:00
sergio 2728698f5e 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 por scaffold dan la misma salida.

Alcance v1: TALLYING FOR ALL y REPLACING ALL; sin LEADING, FIRST,
CHARACTERS, BEFORE/AFTER.

Tests: charka-ir 26, charka-codegen 20, charka-shadow 18. fmt +
clippy limpios.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 22:17:47 +00:00
sergio 47c49acd47 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 a los destinos.
- 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 por scaffold dan la misma salida.

Alcance v1: STRING con DELIMITED BY SIZE (otros delimitadores se
ignoran); sin WITH POINTER ni ON OVERFLOW.

Tests: charka-ir 25, charka-codegen 19, charka-shadow 17. fmt +
clippy limpios.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 22:09:10 +00:00
sergio 3902763daa feat(charka): OCCURS — tablas y referencias con subíndice
Los arrays de COBOL, que antes el transpilador descartaba en silencio.
Una rebanada vertical amplia que atraviesa el pipeline entero.

- Parser: la cláusula OCCURS n [TIMES] se captura en DataItem.
- IR: Operand::Indexed { name, index } — una referencia ELEM(I), con
  subíndice 1-based. Los destinos de los statements pasan de
  Vec<String> a Vec<Operand>, así que se puede escribir a un elemento
  de tabla (MOVE x TO ELEM(I), COMPUTE ELEM(I) = ...). model::Field
  gana occurs: Option<u32>.
- Codegen: un campo OCCURS se emite como Vec<Num>/Vec<Text>,
  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 elemental, una dimensión, subíndice de un operando.
Fuera: OCCURS de grupo, multidimensional, DEPENDING ON.

Tests: charka-parser 16, charka-ir 24, charka-codegen 18,
charka-shadow 16. fmt + clippy limpios.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 22:03:48 +00:00
sergio 28ee1ae260 feat(charka): nivel 88 + modelo de datos compartido en charka-ir
Los nombres de condición de COBOL (IF ES-VALIDO), que antes el
transpilador evaluaba siempre como false. Y, de paso, se elimina la
duplicación de 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 lleva
  ahora un campo `model` — 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, 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: un dato con hijos de nivel 88 antes se perdía como si
  fuera un grupo; ahora se reconoce como campo elemental.
- Corpus: programa nuevo 10-condicion (semáforo con 88 de texto y de
  número). Verificado: intérprete y crate compilado dan igual salida.

Tests: charka-ir 23, charka-codegen 17, charka-shadow 15. fmt +
clippy limpios.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 21:50:06 +00:00
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
sergio 321e6f8e27 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 (un hueco de corrección real). Ahora atraviesa el
pipeline entero como una rebanada vertical.

- 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). Verificado: el
  intérprete sombra y el crate compilado por scaffold dan ambos
  SUMA 1 A 10 = 00055 — las dos rutas concuerdan.

Tests: charka-ir 18, charka-codegen 15, charka-shadow 13. fmt +
clippy limpios.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 21:33:14 +00:00
sergio 4d9ce11b1e feat(charka): charka-shadow — validador en sombra + corpus COBOL
El pipeline COBOL->Rust queda completo (7 crates) 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
— si la sombra y el transpilado divergieran, sería un bug.

- interpret(&Ir) -> Outcome ejecuta el IR y captura las líneas de
  DISPLAY; run_source(&str) corre el pipeline completo.
- Tope de pasos (Halt::StepLimit): un bucle que no termina se corta
  en vez de colgarse.
- Módulos: field (datos -> campos vivos) / interp (el motor).

Corpus nuevo crates/modules/charka/corpus/ — 7 programas COBOL de
complejidad graduada (01-hola .. 07-clasificar) con sus salidas
esperadas verificadas a mano: DISPLAY, aritmética con GIVING,
IF/ELSE, PERFORM TIMES/UNTIL, grupos, COMPUTE con paréntesis,
ROUNDED, IF anidado con AND. Material de prueba del pipeline entero.

11 tests (los 7 del corpus + fuente vacío, STOP RUN, tope de pasos,
error de léxico); fmt + clippy limpios.

No hay GnuCOBOL en la máquina: la referencia v1 es el corpus; un modo
futuro diferenciará contra el compilador real.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 21:23:07 +00:00