Al generar un formulario, el Caption, el Text, la URL base y la cadena de conexión del desarrollador se escribían en crudo entre dos apóstrofos. Dos valores corrientes rompían el programa generado: un apóstrofo — Don't Save — cerraba el literal antes de tiempo y el resto del caption se convertía en palabras COBOL sueltas; y un salto de línea en un caption emitía un literal partido en dos líneas de fuente sin continuación, lo que no es COBOL válido. El síntoma reportado era una compilación bloqueada con expected PROCEDURE DIVISION (line 367) — un número de línea sin relación alguna con el caption que lo causaba.
Ahora los delimitadores se duplican (el escape del estándar, que el léxico decodifica de vuelta) y los caracteres de control se pliegan a un espacio. La PICTURE, además, se dimensiona a partir del valor en lugar de un PIC X(256) fijo, así que un caption de más de 256 caracteres ya no se trunca en silencio. Un PROGRAM-ID es una palabra COBOL, no un literal, de modo que un nombre de formulario que no lo sea se sanea ahí en lugar de emitirse tal cual.
• Conformidad NIST COBOL-85: 242 -> 292 de 434 programas dentro de alcance
Progresión de PASS dentro de alcance por cambio: 1.62.12 (anterior), 242; delimitador duplicado dentro de un literal, 244; coma / punto y coma separadores, 285; subíndice tras nombre calificado, 292. Tres grupos de diagnóstico completos quedan ahora vacíos: unexpected token in statement: Comma (eran 50 programas), expected RParen (23) y unexpected token in statement: Semicolon (14). Especificación: specs/nist/NIST-spec-separators.md, con el plan y el registro de tareas junto a ella.
=== PowerRustCOBOL 1.62.14 — 2026-08-26 ===
• Corregido — una tabla entera puede pasarse a una intrínseca: FUNCTION MAX(IND(ALL))
COBOL-85 pasa una tabla completa a una función estadística subindexándola con la palabra reservada ALL: un argumento escrito se convierte en un argumento por cada ocurrencia, como en COMPUTE WS-NUM = FUNCTION MAX(IND(ALL)). o COMPUTE WS-NUM = FUNCTION SUM(TBL(ALL, 2)). ALL se leía como el prefijo de constante figurativa (ALL "X"), así que el compilador exigía un literal a continuación. Quedan cubiertas las once intrínsecas de longitud variable — MAX, MIN, SUM, MEAN, MEDIAN, MIDRANGE, RANGE, VARIANCE, STANDARD-DEVIATION, ORD-MAX, ORD-MIN.
ALL puede ocupar una dimensión de una tabla multidimensional con subíndices ordinarios en las demás, y se expande en orden por filas (row-major). Una tabla OCCURS ... DEPENDING ON se expande contra su contador en el momento en que se llama a la función.
• Corregido — MOVE ALL "X" rellenaba un carácter, no el campo
Con 01 WS-T PIC X(5)., la sentencia MOVE ALL "X" TO WS-T. dejaba "X " y ahora deja "XXXXX". ALL es la única constante figurativa cuyo carácter de relleno tiene más de un byte, y por eso SPACES y ZEROS nunca mostraron el problema: el literal aterrizaba una sola vez y el resto del campo quedaba en espacios.
• Corregido — MOVE ALL ZEROS se rechazaba
COBOL-85 permite ALL delante de otra constante figurativa, donde es simplemente redundante: ALL ZEROS es ZEROS. Solo se aceptaba un literal real, así que esa escritura fallaba con "expected literal after ALL".
• Corregido — CLOSE ... WITH LOCK y las frases de carrete/unidad
Ninguna de estas escrituras parseaba: CLOSE IX-FD2 WITH LOCK., CLOSE SQ-FS1 WITH NO REWIND., CLOSE TAPE-FILE REEL FOR REMOVAL. WITH se reportaba como token inesperado y LOCK se leía como el nombre de otro archivo que cerrar. WITH LOCK se aplica de verdad en lugar de aceptarse e ignorarse: reabrir ese archivo en la misma unidad de ejecución reporta ahora file status 38, que es el código del estándar para exactamente eso. REEL / UNIT posicionan una cinta multivolumen y se aceptan como no-ops en disco.
• Corregido — un literal con signo como objeto de WHEN
En WHEN -0.000020 THRU 0.000020 el - inicial hacía que el parser leyera el objeto como el comienzo de una condición, con lo que reportaba expected comparison operator in condition y luego se atragantaba con el THRU.
• Corregido — PERFORM ... TIMES con un item de datos como contador
COBOL-85 admite un identificador como contador de repetición, no solo un literal: con 77 THREE PIC 9 VALUE 3., la sentencia PERFORM PFM-C THREE TIMES. ahora es válida.
• Corregido — una cláusula cuyo contador salta a la línea siguiente
Un número que abre una línea se toma por un número de nivel — y normalmente lo es —, así que en 10 STUFF-1 OCCURS seguido de 31 TIMES. en la línea siguiente, el 31 llegaba como número de nivel y OCCURS reportaba "expected integer after OCCURS". Donde la gramática exige un entero y un número de nivel jamás podría aparecer, ambas escrituras se leen ahora como el mismo número. Lo mismo ocurría con un contador de PERFORM ... TIMES escrito en su propia línea.
• Conformidad NIST COBOL-85: 303 -> 317 de 434 programas dentro de alcance
El módulo de Intrinsic Functions está ahora completo: 45 / 45. Progresión de PASS dentro de alcance: 1.62.13, 303; frases de CLOSE, WHEN con signo y PERFORM TIMES con identificador, 314; contadores enteros en línea de continuación, 317. Movimiento por módulos: IF 40 -> 45, IX 38 -> 40, ST 30 -> 32, SQ 50 -> 52, NC 56 -> 58, RL 31 -> 32.
=== PowerRustCOBOL 1.62.15 — 2026-08-26 ===
• Añadido — literales de bloque en formato libre (```)
Una extensión del lenguaje, no COBOL-85: el estándar no tiene literal multilínea alguno — la continuación es un mecanismo de columnas del formato fijo —, así que el fuente en formato libre no podía escribir uno, ni escribir un literal lleno de comillas sin duplicar cada una de ellas. Un literal de bloque se delimita como un bloque de código de Markdown; el texto son las líneas entre las cercas, tomadas tal cual:
Código:
MOVE
```
Hello, World!
```
TO WS-GREETING.
WS-GREETING recibe Hello, World!. El texto empieza en la línea siguiente a la cerca de apertura (lo que siga a ``` en esa línea es una etiqueta, como en Markdown), la línea de la cerca de cierre no es texto, y tampoco lo es el salto de línea anterior a ella — así que un bloque de una sola línea se comporta exactamente igual que un literal entre comillas. Los saltos de línea interiores se conservan y nada necesita escaparse, que es lo que hace legibles el JSON, el SQL y el HTML embebidos (por ejemplo {"name": "O'Brien", "tags": ["a", "b"], "ok": true} bajo una etiqueta json). Solo en formato libre: el formato fijo tiene columna indicadora y área de secuencia, así que una línea de acentos graves significa allí otra cosa y se rechaza con un diagnóstico que lo dice.
• Corregido — un nombre de FUNCTION desconocido es ahora un error de compilación
Una intrínseca que RustCOBOL no implementa antes registraba un aviso y devolvía 0, de modo que una errata producía una respuesta erróneamente segura que nada reportaba. COMPUTE WS-X = FUNCTION SQRTT(4). ahora no compila, con el mensaje 'SQRTT' is not an intrinsic function RustCOBOL implements — did you mean FUNCTION SQRT?
El conjunto implementado (53 funciones) se lista una sola vez, en cobolt-ast, y una prueba afirma en ambas direcciones que coincide con lo que implementa el intérprete — un nombre listado pero no implementado compilaría y luego fallaría en ejecución, y un nombre implementado pero no listado se rechazaría aunque funciona. Esa segunda dirección no es hipotética: VARIANCE faltaba en el primer borrador de la lista y dos programas NIST que funcionaban dejaron de compilar hasta que se escribió la prueba.
• Corregido — una palabra definida por el usuario puede empezar por un dígito
COBOL-85 forma una palabra con A-Z, 0-9 y el guion; solo un data-name debe contener al menos una letra, y un nombre de párrafo o de sección no la necesita. No parseaba ninguno de estos casos: 01 25COUNT PICTURE 99., 01 3-DEM-TBL REDEFINES 3-DIMENSION-TBL. ni 0 SECTION. 25COUNT se descomponía en el entero 25 y la palabra reservada COUNT, y por eso el diagnóstico nombraba una palabra clave que no estaba en ninguna parte del fuente. 3-DEM-TBL es el caso que merece mención: 3- podría leerse como "tres menos...", y el estándar lo resuelve a favor de la palabra porque un operador aritmético debe llevar espacios alrededor — la misma regla que ya hacía de B-C una sola palabra y de B - C una resta.
• Corregido — una línea de depuración es un comentario salvo que el programa lo pida
Una D en la columna 7 marca una línea de depuración. El valor por defecto de COBOL-85 es que sea un comentario; solo se compila bajo SOURCE-COMPUTER. XYZ WITH DEBUGGING MODE. Los dos preprocesadores de formato fijo discrepaban al respecto — la vía estricta trataba la D como comentario, la relajada la compilaba incondicionalmente —, así que el mismo texto significaba cosas distintas según cuál lo leyera. WITH DEBUGGING MODE no estaba implementado en absoluto. Solo formato fijo: el formato libre no tiene área indicadora, así que una D allí es una palabra COBOL corriente.
• Corregido — una aplicación compilada no podía leer su propio programa embebido
El fallo decía: deserialize embedded AST: Custom("invalid value: integer `10`, expected variant index 0 <= i < 10"). Una aplicación compilada lleva su programa como un AST serializado con bincode, y bincode identifica una variante de enum por su ordinal. Añadir una variante en medio de Expr renumeraba todas las variantes posteriores, de modo que un programa escrito por una build no podía ser leído por otra — y el fallo afloraba al arrancar la aplicación, mucho después de la build que lo causó.
La variante se movió al final del enum, donde toma un ordinal nuevo y deja intactos todos los existentes, y el enum lleva ahora un comentario que explica por qué eso importa. Un desajuste que aún puede darse (añadir un campo también cambia el layout) reporta ahora la causa y el remedio — rebuild the application with the current toolchain — en lugar del mensaje propio de bincode.
• Corregido — un nombre de formulario que no es una palabra COBOL
derive_paragraph_name se llama con el nombre del formulario para los eventos a nivel de formulario, y un nombre de formulario no se valida en ninguna parte. Un formulario llamado MAIN'FORM producía el manejador MAIN'FORM--ONLOAD, emitido a la vez como PROGRAM-ID anidado y como el CALL que lo alcanza — y ninguno de los dos puede contener un apóstrofo. Un nombre que ya es una palabra COBOL se devuelve sin cambios, así que cada .cfrm en disco conserva el nombre de párrafo que tiene almacenado.
• Conformidad NIST COBOL-85: 317 -> 332 de 434 programas dentro de alcance
Progresión de PASS dentro de alcance: 1.62.14, 317; FUNCTION desconocida convertida en error de compilación y palabras que empiezan por dígito, 331; VARIANCE restaurada a la lista de implementadas, 332. Segmentation 0 -> 10, Nucleus 58 -> 61, Relative 32 -> 34.
• Añadido — puntuación de ejecución, y lo que encontró
Todas las cifras de conformidad anteriores miden compilación: si el front end acepta el programa. nist_conformance run ahora ejecuta cada programa que compila y lee el informe PASS/FAIL del propio programa, que es lo que la suite CCVS85 existe para producir.
De los 434 programas dentro de alcance, 0 llegan al final de su ejecución. 101 no compilan; de los 332 que sí compilan, 170 entran en bucle hasta que se les mata por exceso de salida, 76 agotan el tiempo, 66 no imprimen informe alguno y 21 son rechazados por el runtime. Esa brecha — 332 compilan, 0 funcionan — es el estado honesto del runtime frente a la suite, y ahora está medida en lugar de sospechada.
=== PowerRustCOBOL 1.62.16 — 2026-08-27 ===
• Corregido — el AT de AT END es opcional
COBOL-85 escribe la frase como [AT] END, así que RETURN SORTFILE RECORD AT END GO TO EOF-PARA. y RETURN SORTFILE END GO TO EOF-PARA. son la misma sentencia. Solo se aceptaba la primera. La segunda dejaba la frase sin consumir, y la sentencia entonces seguía de largo y se tragaba el encabezado del párrafo siguiente — con lo que cada GO TO que apuntara a ese párrafo lo reportaba como no declarado, en una línea lejana a la causa. CCVS85 escribe ambas ortografías a propósito, una tras otra, etiquetadas "WITH ALL OPTIONAL WORDS" y "WITHOUT OPTIONAL WORDS".
NOT AT END ya aceptaba la forma sin AT; las dos mitades de la misma regla discrepaban. Este era el mayor defecto individual que quedaba en la suite: 33 programas.
• Corregido — la palabra COPY dentro de un literal no es una directiva
El preprocesador de COPY/REPLACE recorría un literal de cadena hasta la siguiente comilla en cualquier punto del archivo, con lo que una sola comilla sin pareja cambiaba la paridad de todos los literales posteriores y dejaba prosa corriente expuesta como texto de fuente. En CCVS85 eso hacía que el rótulo de copyright pareciera una directiva COPY y corrompía cuatro programas por lo demás limpios:
Código:
02 FILLER PIC X(40) VALUE
"CCVS74 NCC COPY, NOT FOR DISTRIBUTION.".
Un literal termina ahora en su línea y un delimitador duplicado es contenido — exactamente lo que el léxico hace desde 1.62.12. El preprocesador nunca tuvo ninguna de las dos reglas.
• Corregido — un literal numérico puede abrir una lista de operandos con su punto decimal
ADD y SUBTRACT delimitaban su lista de operandos emisores con Token::Period, así que en SUBTRACT SUBTR-4 SUBTR-5 .499 FROM SUBTR-2 GIVING SUBTR-11. o en ADD .3 TO PERFORM4. la sentencia terminaba en el . de .499 y 499 FROM ... se leía como una sentencia nueva — reportado como unexpected token: IntegerLiteral(499), señalando a los dígitos en lugar de al punto. La adyacencia es la regla, como en el resto del compilador: COBOL-85 exige un espacio tras el punto de fin de sentencia, así que un punto pegado a dígitos solo puede ser un punto decimal. Solo cambió el lado emisor — MOVE X TO Y.5 sigue siendo un error de compilación, según la resolución vigente, porque un receptor no puede empezar con punto decimal.
• Conformidad NIST COBOL-85: 332 -> 376 de 434 programas dentro de alcance
Progresión de PASS dentro de alcance: 1.62.15, 332; [AT] END con el AT opcional, 365; biblioteca COPY conectada al arnés más la corrección de literales anterior, 372; punto decimal inicial en lista de operandos, 376. Indexed I/O está ahora completo: 42 / 42 — el segundo módulo terminado, tras Intrinsic Functions. Sequential I/O 52 -> 77, Sort/Merge 32 -> 36, Source Text Manipulation 4 -> 13, Nucleus 57 -> 64.
• El arnés ahora expande COPY, como rcrun siempre ha hecho
CCVS85 incluye sus 51 copybooks dentro de la misma distribución, como miembros CLBRY. El arnés de conformidad tokenizaba sin ejecutar nunca el preprocesador, así que el módulo Source Text Manipulation — cuyo tema completo es COPY y REPLACE — no podía medirse en absoluto. Ahora escribe la biblioteca a disco y expande a través de cobolt_lexer::expand_copybooks, la misma vía que usa rcrun.
=== PowerRustCOBOL 1.62.17 — 2026-08-27 ===
• Añadido — LINAGE: la página impresa, y AT END-OF-PAGE
Un archivo de informe puede ahora declarar la disposición de su página, y un WRITE puede preguntar si ha alcanzado el pie de esa página:
Código:
FD PRINT-FILE
LINAGE IS 50 LINES
WITH FOOTING AT 45
LINES AT TOP 10
LINES AT BOTTOM 6.
...
WRITE PRINT-REC BEFORE ADVANCING 1 LINE
AT END-OF-PAGE PERFORM PAGE-TRAILER
NOT AT END-OF-PAGE CONTINUE
END-WRITE.
COBOL-85 divide la página en un margen superior, un cuerpo de n líneas y un margen inferior. LINAGE-COUNTER cuenta desde 1 las líneas escritas en el cuerpo; alcanzar el FOOTING levanta la condición de fin de página, que es como un informe sabe que debe imprimir su pie. ADVANCING PAGE inicia una página nueva y reinicia el contador. Omitir FOOTING lo deja por defecto en el tamaño del cuerpo, de modo que la condición espera a la página completa. AT EOP se acepta como la ortografía corta, y el AT es opcional en ambas. El parser de FD antes se saltaba la cláusula LINAGE completa y AT END-OF-PAGE era un error de parseo, así que un programa de informes no podía compilar.
Parsearlo sin más habría sido peor que dejarlo roto: la frase habría compilado y no se habría disparado nunca — una respuesta errónea en silencio en lugar de un fallo honesto —, así que el contador está implementado, no simulado. Un archivo sin cláusula LINAGE no tiene página, y su AT END-OF-PAGE, correctamente, no se levanta jamás.
• Conformidad NIST COBOL-85: 376 -> 380 de 434 programas dentro de alcance
Sequential I/O 77 -> 81. Los cuatro programas de informe de SQ que quedaban (SQ201M, SQ208M, SQ209M, SQ401M) esperaban todos esto.
=== PowerRustCOBOL 1.62.18 — 2026-08-27 ===
• Corregido — un número que abre una línea de continuación es un operando
Un número al comienzo de una línea se clasifica como número de nivel, y normalmente lo es. Pero una sentencia cuya lista de operandos salta a la línea siguiente pone un entero corriente exactamente en esa posición:
Código:
SUBTRACT DNAME-1
1 FROM ERROR-COUNTER.
WRITE PRINT-REC FROM OVERPRINTED-LINE AFTER
000000000000000001 LINE.
DISPLAY "NUMERIC LITERALS "
21 SPACE 35 I-DATA
El léxico no puede distinguir los dos casos — un número de nivel solo se reconoce por el contexto —, así que el parser acepta ahora esa escritura allí donde se espera una expresión. Eso es exacto y no laxo: la DATA DIVISION casa sus entradas antes de que se parsee expresión alguna, de modo que un número de nivel real nunca llega al parser de literales. Vale 8 programas.
• Corregido — el IS es opcional en una condición de clase o de signo
COBOL-85 escribe por igual IF X IS NUMERIC e IF X NUMERIC. Solo se aceptaba la ortografía con IS, así que la condición terminaba en el nombre del dato; casos como IF IF-D8 POSITIVE PERFORM PASS ELSE PERFORM FAIL., IF WS-FIELD NUMERIC ... o IF WS-FIELD NOT NUMERIC ... parsean ahora. Un NOT inicial se consume solo cuando de verdad le sigue una palabra de clase o de signo, así que a NOT = b sigue siendo una comparación negada.
• Corregido — una condición puede ser sujeto de un EVALUATE
EVALUATE parseaba su sujeto como una expresión simple, así que en EVALUATE WRK-XN-00001-1 NUMERIC con sus ramas WHEN TRUE y WHEN FALSE y su END-EVALUATE, se detenía en el nombre del dato y NUMERIC se leía como una sentencia — que entonces se tragaba las ramas WHEN y el END-EVALUATE.
• Corregido — un nombre de procedimiento puede escribirse solo con dígitos
COBOL-85 exige un carácter alfabético en un data-name, pero no en un nombre de párrafo o de sección: PERFORM 00., GO TO 50., el párrafo 00. y el encabezado 50 SECTION. no parseaban ni como referencias ni como encabezados. El caso del encabezado necesitaba además que el escáner de listas de sentencias reconociera un nombre numérico como frontera — sin eso, las sentencias del párrafo anterior se tragaban el encabezado.
• Conformidad NIST COBOL-85: 380 -> 391 de 434 programas dentro de alcance
Más del 90 % de la suite dentro de alcance. Nucleus 64 -> 69, Segmentation 10 -> 12, Sort/Merge 36 -> 38.
=== PowerRustCOBOL 1.62.19 — 2026-08-27 ===
Conformidad NIST COBOL-85: 391 -> 396 de 434 programas dentro de alcance (91.2 %). Nucleus 69 -> 74; ningún módulo retrocedió.
• Corregido — un item numérico editado es un item numérico
Un item numérico editado es un receptor legal para COMPUTE, ADD, SUBTRACT, MULTIPLY y DIVIDE ... GIVING — editar el resultado es la razón de declararlo. Dos defectos independientes hacían que un receptor correcto pareciera no numérico, y el analizador rechazaba código válido con "'DIV9' is not numeric; DIVIDE GIVING requires a numeric receiver":
Código:
01 DIV9 PICTURE IS ZZ,ZZZ.9.
01 WRK-NE-6 PIC $**.**CR.
DIVIDE GROSS BY 12 GIVING DIV9.
SUBTRACT TAX FROM GROSS GIVING WRK-NE-6.
Primero, el punto decimal de edición se tragaba su dígito. Es la misma ambigüedad que 1.62.18 encontró en una línea de continuación, en un lugar nuevo: un número que sigue a un punto se clasifica como número de nivel, porque ahí es donde normalmente empieza el siguiente item de datos. Dentro de una PICTURE no lo es, así que la plantilla se truncaba en el punto — ZZ,ZZZ.9 quedaba en ZZ,ZZZ —, perdiendo el dígito y, con él, la categoría numérica del item. Ahora deciden los spans, exactamente como ya lo hacen para B-C frente a B - C: solo un dígito pegado al punto forma parte de la picture, de modo que el siguiente item de datos no se traga jamás. ZZ,ZZZ.99 nunca tuvo el problema, porque 99 no es un número de nivel.
Segundo, una picture no necesita llevar ningún 9: Z, * y un $, + o - flotantes son posiciones de dígito por derecho propio, así que ZZZZ, $.** y $**.**CR son numéricos editados; antes caían en el fallback alfanumérico. El recuento de dígitos sigue ahora la misma regla con la que el runtime da formato, de modo que la clasificación y la salida editada no pueden discrepar.
=== PowerRustCOBOL 1.62.20 — 2026-08-27 ===
• Corregido: PERFORM a THRU b es un rango de párrafos, no un bloque de sentencias
PERFORM BAIL-OUT THRU BAIL-OUT-EX aplanaba los párrafos del rango en una sola lista de sentencias y ejecutaba esa lista. El aplanado pierde la identidad de los párrafos, de modo que un GO TO que nombraba un párrafo dentro del rango no podía aterrizar allí: escapaba al controlador de nivel superior, que reanudaba la ejecución secuencial más allá del rango y nunca regresaba al llamador.
Código:
PRINT-DETAIL.
...
ELSE PERFORM BAIL-OUT THRU BAIL-OUT-EX.
BAIL-OUT.
IF CORRECT-A EQUAL TO SPACE GO TO BAIL-OUT-EX.
BAIL-OUT-WRITE.
...
BAIL-OUT-EX. EXIT.
El GO TO BAIL-OUT-EX de arriba es la ruta ordinaria, no un caso límite. El control caía a través de BAIL-OUT-EX, atravesaba los párrafos que hay detrás y entraba en la propia sección de pruebas del programa — que volvía a ejecutar las pruebas, las imprimía de nuevo y no se detenía jamás. Un rango ahora se ejecuta párrafo a párrafo: un GO TO dentro del rango transfiere el control dentro de él, un GO TO que sale del rango sigue propagándose como exige COBOL, y el PERFORM retorna cuando termina el último párrafo.
Ésta era la razón sistémica por la que los programas NIST CCVS85 no se ejecutaban. La construcción está en el código común que comparten todos los programas CCVS, en la ruta que recorre cada prueba que pasa, así que cualquier programa con una prueba superada quedaba en bucle infinito. NC104A pasó de producir salida sin límite a terminar e informar 199 PASS / 30 FAIL.
=== PowerRustCOBOL 1.62.21 — 2026-08-27 ===
NIST CCVS85 Nucleus (NC): compilación 76 -> 92 de 95, ejecución 16 -> 28 de 95. Dos números separados, siempre. La compilación cuenta los programas que el front end acepta; la ejecución cuenta los programas que corren hasta el final e informan cero fallos en su propio informe CCVS — la afirmación estrictamente más fuerte, y la que significa que el programa funciona. Ningún otro módulo fue tocado (GOLDEN RULE #9).