b3278bdb0c
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>
385 lines
19 KiB
Markdown
385 lines
19 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): 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 a un
|
|
fichero, lo cierra, lo relee con `READ ... AT END` y las muestra.
|
|
Verificado: el intérprete sombra y el crate compilado dan la misma
|
|
salida.
|
|
- Alcance v1: organización «line sequential»; sin ficheros indexados
|
|
ni relativos, sin `FILE STATUS`.
|
|
|
|
### 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 en ambas rutas.
|
|
|
|
### 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 en ambas rutas.
|
|
|
|
### 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.
|
|
|
|
- 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 en ambas rutas.
|
|
|
|
### 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<String>` a `Vec<Operand>`, así que se puede escribir a
|
|
un elemento de tabla (`MOVE x TO ELEM(I)`, `COMPUTE ELEM(I) = ...`).
|
|
`Field` del modelo 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` 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 <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.
|