Tres correcciones, ninguna dentro del propio motor. Primera: RELATIVE data-name nombra el número de registro, con o sin la palabra KEY. La cláusula se escribe RELATIVE KEY IS RK, RELATIVE KEY RK y RELATIVE RK a secas, y solo las dos primeras se entendían. La tercera se leía como una cláusula de organización a secas, así que la clave se consumía y se descartaba en silencio — el fichero quedaba entonces sin número de registro alguno y cada WRITE aleatorio devolvía 24, una violación de límites sobre la ranura cero. Lo que distingue ambos casos es simplemente lo que sigue: una cláusula de organización está completa por sí sola, de modo que un data-name tras RELATIVE solo puede ser la clave. Esto cerró ocho programas de una vez.
Segunda: estado de fichero 14 — un número de registro relativo demasiado grande para su elemento de clave. La anchura de la PICTURE del elemento RELATIVE KEY forma parte del comportamiento del fichero, no solo de su almacenamiento: con una clave CUST-SLOT PIC 99, leer ese fichero secuencialmente funciona hasta el registro 99 y a partir de ahí no puede informar de dónde está. COBOL-85 tiene un estado exactamente para eso, y es una condición de clase at-end como 10, así que la frase AT END es la que lo maneja.
Tercera: una frase condicional sin terminar se detiene ahora en el ELSE que la engloba. Una frase escrita sin su terminador de ámbito —
Código:
IF WS-N < 201
WRITE REC-A
INVALID KEY GO TO BAD
ELSE
WRITE REC-B
INVALID KEY GO TO BAD.
— leía de largo a través del ELSE, y el programa no llegaba a analizarse. El conjunto de parada propio de una frase no puede saber dentro de qué está anidada, así que la regla se aplica ahora una vez por cada cuerpo con ámbito: ELSE, END-IF, WHEN, END-EVALUATE, END-PERFORM y END-SEARCH cierran una construcción dentro de la cual el cuerpo puede estar anidado, y ninguna lista imperativa puede leer más allá de uno de ellos. Conformidad de compilación de la suite completa: de 422 a 423 de 434. Además, RL301M queda excluido de la puntuación de ejecución, la misma decisión que ya lleva IX301M: pide que una implementación de subconjunto alto señale ORGANIZATION IS RELATIVE, ACCESS MODE IS RANDOM, RELATIVE KEY IS y las frases NOT INVALID KEY como no conformes, y todas son características que PowerRustCOBOL soporta. Sigue contando en el censo de compilación, donde pasa.
=== PowerRustCOBOL 1.62.78 — 2026-08-29 ===
• Una coma separadora delante de un signo ya no se descarta, y dos programas que hacían caer el intérprete ahora se ejecutan.
IF (Conditional) pasa de 24 a 32 de 45 ejecutando limpio, de 702 a 776 aserciones pasando (87.4 % -> 91.9 %), sin caídas restantes en el módulo. Una coma seguida de espacio es un separador de COBOL-85: puede aparecer donde puede aparecer un espacio y significa exactamente lo que significa un espacio, así que el lexer la descartaba. Hay un lugar donde eso no es cierto. El estándar distingue un signo de un operador binario por el espacio que le sigue — A -3 son dos operandos, A - 3 es una resta —, de modo que descartar la coma de COMPUTE WS-NUM = FUNCTION MOD(A, -3). deja A -3, que se leía como una única resta. MOD se invocaba entonces con un solo argumento y el intérprete entraba en pánico al leer el segundo. La coma se conserva ahora cuando el token siguiente es un + o un -, lo que basta para que el parser distinga ambos casos sin enseñarle la regla de espaciado.
El punto y coma no conserva tal excepción. Nunca es un separador de listas, solo decoración, y ningún parser se come uno — MOVE ELEM3( +3; +5, +10) TO TEMP es una lista de subíndices que la suite escribe precisamente para demostrarlo. Aplicar la excepción a ambos signos de puntuación costó un programa que venía pasando, que es como se descubrió la distinción.
Una intrínseca invocada con menos argumentos de los debidos ahora se reporta en lugar de ser fatal. FUNCTION MOD needs 2 arguments, 1 given es algo sobre lo que un desarrollador puede actuar; un pánico por índice fuera de rango, no. Un programa COBOL nunca debe poder hacer caer el intérprete, diga lo que diga.
=== PowerRustCOBOL 1.62.79 — 2026-08-29 ===
• NUMVAL y NUMVAL-C leen las formas que COBOL-85 realmente permite, y las funciones intrínsecas se señalan como por encima del subconjunto alto.
IF (Conditional) pasa de 32 a 37 de 45 ejecutando limpio, de 776 a 824 aserciones pasando (91.9 % -> 97.7 %). NUMVAL: el argumento no es un literal float de Rust, y leerlo como tal devolvía cero para la mayor parte de lo que el estándar permite. El signo puede estar en cualquiera de los dos extremos y no tiene que tocar los dígitos; CR y DB son la grafía crédito-débito de un menos final; NUMVAL-C admite además una cadena de moneda y separadores de grupos de dígitos, y su segundo argumento opcional — la cadena de moneda a ignorar — no se leía en absoluto.
Código:
COMPUTE WS-NUM = FUNCTION NUMVAL (" - 4929.0323").
COMPUTE WS-NUM = FUNCTION NUMVAL (" 200.0002 - ").
COMPUTE WS-NUM = FUNCTION NUMVAL-C (" $ 90.54 - ", "$").
Los tres devolvían cero antes. Sin segundo argumento, NUMVAL-C usa ahora el CURRENCY SIGN de SPECIAL-NAMES, y DECIMAL-POINT IS COMMA intercambia los papeles de . y , como lo hace en todas partes.
Las funciones intrínsecas están por encima del subconjunto alto de COBOL-85: llegaron con la adenda de 1989, así que un compilador que valida en el subconjunto alto reporta cada uso como no conforme — implemente o no la función. flag_high_subset no las cubría, y eso es todo lo que piden IF401M, IF402M e IF403M: 44 expectativas entre los tres, ahora todas coincidentes.
Con ello cayeron dos falsos positivos. La lista de argumentos de una función no es una lista de subíndices ni una fuente de operadores para una relación que la engloba, de modo que IF FUNCTION ORD-MAX (5, 3, 2, 8, 3, 1) = ... no son 6 subíndices e IF FUNCTION MEAN (5, -2, -14, 0) = ... no contiene aritmética — cada una atraía una segunda señalización errónea. Siete sentencias de IF402M estaban afectadas y el emparejamiento voraz del puntuador lo ocultaba: las señalizaciones espurias suplantaban a las de función intrínseca, así que los totales parecían correctos. Ambos detectores siguen disparándose sobre las construcciones genuinas, lo que ahora está cubierto por tests unitarios en ambas direcciones. Una intrínseca invocada con menos argumentos de los debidos se reporta en lugar de ser fatal — la guarda añadida en 1.62.78 cubre ahora también ANNUITY.
=== PowerRustCOBOL 1.62.80 — 2026-08-29 ===
Un cociente ya no pierde sus decimales frente a un divisor largo, y las funciones intrínsecas de lista de argumentos comparan como compara COBOL. IF (Conditional) pasa de 37 a 44 de 45 programas ejecutando limpio, 824 -> 841 aserciones superadas (97.7 % -> 99.9 %).
• Una división ya no pierde los decimales del cociente ante un divisor largo
Dar al cociente sus dígitos de guarda implica desplazar el dividendo, y ese desplazamiento tiene que caber en el entero de trabajo. Un divisor con muchos decimales lo agranda por sí solo — 1 / SQRT3, donde SQRT3 PIC S9V9(17), pide 43 dígitos — y la ruta de desbordamiento caía entonces a coma flotante a la escala del propio dividendo. El dividendo es el entero 1, así que 0.577 se redondeaba a un valor sin ninguna posición decimal y volvía como 1:
Código:
01 SQRT3 PIC S9V9(17) VALUE 1.732050808.
...
COMPUTE WS-NUM = FUNCTION ATAN(1 / SQRT3).
respondía atan(1): un cuarto de vuelta en lugar de un sexto. El cociente ahora descarta dígitos de guarda hasta que el desplazamiento cabe, lo que conserva la ruta entera exacta; solo una magnitud realmente enorme llega a coma flotante, y lo hace a la precisión de trabajo. Nada que antes cabía cambia: la búsqueda empieza en la precisión que la división siempre usó.
• MAX, MIN, ORD-MAX y ORD-MIN comparan como compara COBOL
Las cuatro leían todos sus argumentos como números en coma flotante, lo cual es erróneo por partida doble. Primero, el resultado de MAX/MIN es el propio argumento: cuando los argumentos son alfanuméricos la comparación se hace por la secuencia de intercalación y la respuesta es una cadena de caracteres — FUNCTION MAX("R", I, "I", "a") es "a" —, y leídos como flotantes los cuatro argumentos valían cero. Segundo, gana el primero de varios argumentos iguales: ORD-MAX y ORD-MIN devuelven una posición, así que los empates se ven — FUNCTION ORD-MAX(A, 5, 5, A) es 1 cuando A es el mayor, donde el código anterior respondía 4. La comparación ahora es cob_ordering, la misma ordenación que usan SORT y todas las relaciones.
• Se conserva una coma separadora delante de (
Por la misma razón que la que precede a un signo (1.62.78): quitarla de FUNCTION MAX(A * B, (C + 1) / 2, 3 + 4) deja B (C + 1), que es la sintaxis de una referencia con subíndice. Los dos primeros argumentos se fusionaban en uno y MAX devolvía 17.5 donde la respuesta es 35.
=== PowerRustCOBOL 1.62.81 — 2026-08-29 ===
• Un signo pegado a un literal es un signo, no un operador
El módulo Conditional queda terminado: 45 de 45 en ambos ejes, 841 aserciones, 0 fallos.
COBOL-85 distingue los dos casos por el espacio que sigue: un operador binario debe llevar espacio a ambos lados, de modo que 10.2 -0.2 son dos operandos y 10.2 - 0.2 es una resta. La suite escribe COMPUTE WS-NUM = FUNCTION RANGE(10.2 -0.2, 5.6, -15.6) sin ninguna coma entre los dos primeros argumentos, y espera cuatro: 25.8 es 10.2 menos -15.6. Leído como resta eran tres argumentos y 25.6.
La salvaguarda es deliberadamente más estrecha que la regla: solo se activa cuando el token anterior al signo es un literal numérico o un paréntesis de cierre. Un identificador delante no puede distinguirse de una palabra clave a nivel léxico, y PICTURE -9(9).9(9) y VARYING ... BY -1 son ambos "operando, hueco, signo pegado, dígitos" — ninguno es un lugar donde cambiar nada. Ampliarla es una decisión para la puerta de la suite completa, no una conjetura.
=== PowerRustCOBOL 1.62.82 — 2026-08-29 ===
• Un cambio del arnés NIST, sin ningún cambio del compilador ni del runtime detrás
El módulo Inter-program Communication trata de CALL, y sus programas llamados son miembros separados de la distribución: IC101A llama a IC102A, IC108A llama a IC109A, IC110A e IC111A. El arnés ejecutaba un fichero fuente cada vez, así que el llamante no tenía a quién llamar. La propia suite lo dice en sus tarjetas de control, con una palabra que el divisor no conocía: *HEADER,COBOL,IC101A,SUBRTN,IC102A.
SUBPRG y SUBRTN no son sinónimos, y la diferencia decide si un miembro es o no un test. SUBPRG nombra el siguiente programa del mismo grupo — cada uno es un test completo con su propio informe, e IX101A,SUBPRG,IX102A es exactamente la cadena que la tabla de productores ya describe. SUBRTN nombra un programa al que el test llama; los 24 están en IC y OBIC, y cada uno responde a un CALL literal. Confundir ambos le costó a SQ203A su puesto y llevó SQ de 85 de 85 a 84 de 84, que es como se descubrió la distinción.
Un llamante se ejecuta ahora con sus llamados concatenados en una sola unidad de ejecución, y un llamado ya no se puntúa como un test que no produjo informe. IC pasa de 6 a 10 de sus 25 tests ejecutando limpio, 134 -> 187 aserciones superadas (51.0 % -> 72.5 %). El censo de compilación no cambia: 423 de 434, porque un llamado es COBOL ordinario y ahí sigue contando. Nota: el denominador de ejecución de IC pasa de 47 a 25 con esta versión; los 47 contaban los 22 llamados como tests, que no tienen informe CCVS y, ejecutados en solitario, solo podían fallar. Las cifras comparables son 6 -> 10 de 25.
=== PowerRustCOBOL 1.62.83 — 2026-08-29 ===
• Segmentation sale del alcance NIST (decisión del operador)
SG se une a CM, RW, los tests de elementos obsoletos OB* y EXEC85 como módulo que RustCOBOL no mide.
La segmentación existe para encajar un programa en una máquina demasiado pequeña para contenerlo: las cabeceras SECTION llevan un número de segmento y el runtime superpone los segmentos independientes unos sobre otros. RustCOBOL es una solución de 64 bits sobre sistemas de 64 bits, con más espacio de direcciones del que ningún programa COBOL puede agotar — así que un número de segmento compila y no tiene ningún efecto. No hay comportamiento que el módulo pueda ejercitar, y puntuarlo sería puntuar un mecanismo que nunca existirá.
Sus 13 programas siguen compilando y se informan como N-A en lugar de eliminarse, de modo que la exclusión queda visible en el censo. La suite dentro de alcance es de 421 programas, no 434, y la conformidad de compilación pasa a leerse 410 de 421 (97.4 %) donde antes leía 423 de 434 (97.5 %).
=== PowerRustCOBOL 1.62.84 — 2026-08-29 ===
• CALL ... USING enlaza el almacenamiento del llamante, no una copia de él
Un parámetro BY REFERENCE es ahora un alias sobre su argumento, de modo que qué almacenamiento nombra un parámetro lo decide el argumento, no el nombre que el programa llamado le dé. Enlazar por nombre significaba que el parámetro del llamado era lo que el llamante tuviera bajo ese nombre: en CALL "IC202A" USING DN1, DN2, DN1, DN4 el tercer argumento es DN1; IC202A llama DN3 a su tercer parámetro y escribe a través de él — y el DN3 propio y sin relación del llamante quedaba sobrescrito, porque los dos eran la misma ranura. El informe CCVS85 lo dice con todas las letras: "DN3 VALUE CHANGED BY CALL".
Solo el parámetro se convierte en alias, deliberadamente. Los items subordinados de un parámetro de grupo siguen resolviéndose por nombre a los del llamante, que es como un programa llamado lee los campos del registro que le entregaron; darles ranuras propias recortó IC de 10 programas limpios a 5. BY CONTENT no cambia — una copia tomada a la entrada, sin escritura de vuelta — y el alias se libera cuando la llamada retorna, de modo que un programa llamado desde dentro de otro que puso alias al mismo nombre recupera su propio enlace. IC (Inter-program Communication) pasa de 187 a 194 aserciones superadas (72.5 % -> 75.8 %), 71 -> 62 fallos.
• La puerta de regresión NIST pasa a una vez por módulo (decisión del operador)
Ejecutar todos los módulos terminados tras cada cambio detectó cuatro problemas en todo el proceso, lo que no compensa volver a medir más de 7000 aserciones cada vez. La puerta completa se ejecuta ahora cuando se resuelve la última aserción fallida de un módulo, antes de declararlo terminado; por cada cambio, solo el módulo en curso y el censo de compilación. Un cambio de lexer o de parser sigue pasando la puerta de inmediato: un cambio del front-end no tiene frontera de módulo, y dos de las cuatro detecciones fueron exactamente eso.
=== PowerRustCOBOL 1.62.85 — 2026-08-29 ===
• Los campos de un parámetro de grupo se emparejan con los del argumento por posición
Los dos programas no tienen por qué usar los mismos nombres para ellos, y hasta ahora solo se enlazaba el grupo en sí — así que el programa llamado escribía en sus propias ranuras de LINKAGE y el llamante no veía cambiar nada.
Código:
* the caller
01 TABLE-1.
02 DN2 PICTURE XXX.
02 DN3 PICTURE 99.
02 DN4 PICTURE X(5).
...
CALL "IC204A" USING TABLE-1, DN1.
* the called program
01 SUB-TABLE-1.
02 SUB-DN2 PIC XXX.
02 SUB-DN3 PIC 99.
02 SUB-DN4 PIC X(5).
PROCEDURE DIVISION USING SUB-TABLE-1, SUB-DN1.
Los mismos bytes bajo nombres distintos. Emparejar por nombre no alcanza nada, así que los items subordinados se emparejan en orden de declaración — no hay otra cosa por la que emparejarlos — y cada uno se pone como alias del correspondiente del llamante, junto con el grupo. IC (Inter-program Communication) pasa de 194 a 206 aserciones superadas (75.8 % -> 80.5 %), 62 -> 50 fallos. IC203A por sí solo pasó de 13 fallos a 1.
=== PowerRustCOBOL 1.62.86 — 2026-08-29 ===
• BY CONTENT entrega el valor, no el almacenamiento, y la frase BY gobierna todos los operandos que la siguen
Dos defectos, un solo síntoma visible. BY CONTENT copiaba el argumento en el parámetro por nombre, así que cuando el nombre del parámetro era uno que el llamante también usaba, la copia aterrizaba en el almacenamiento del propio llamante y cada escritura pasaba directa. Y la frase se leía por operando: CALL "IC225A-1" USING BY REFERENCE DN1, DN2, CONTENT DN3, DN4 END-CALL pasa dos operandos por cada vía; leída por operando, solo DN3 era BY CONTENT y DN4 recaía en BY REFERENCE. La suite comprueba exactamente eso: "VALUE OF DN4 HAS BEEN CHANGED".
BY CONTENT enlaza ahora como BY REFERENCE y el almacenamiento del llamante — el argumento y cada uno de sus campos — se restaura cuando la llamada retorna. Dentro de una sola unidad de ejecución eso es indistinguible de una copia privada, y hereda el mecanismo de alias que ya hace alcanzables los campos de un parámetro de grupo. IC (Inter-program Communication) pasa de 206 a 212 aserciones superadas (80.5 % -> 82.8 %), 50 -> 44 fallos.
=== PowerRustCOBOL 1.62.87 — 2026-08-29 ===
• Corregido — un programa de consola ya no construye una pila TLS que nunca usa
El compañero de 1.62.9, una plataforma más abajo. Aquel cambio evitó que todas las aplicaciones compilaran SQLite; este evita que un programa de consola enlace la red. ureq y native-tls eran dependencias incondicionales de cobolt-runtime, y native-tls en Linux es OpenSSL — así que compilar allí cualquier aplicación exigía libssl-dev y un compilador de C para openssl-sys, hubiera o no oído hablar el programa de HTTP. google_maps también era incondicional: reqwest más tokio, la mayor dependencia individual que arrastra el runtime, compilada dentro de programas sin ningún control de Maps. Ambos son ahora features, activas por defecto, y el manifiesto generado las apaga para un programa que demostrablemente no alcanza ninguna de las dos. openssl-sys y tokio salen del grafo de dependencias por completo — verificado con cargo tree --target x86_64-unknown-linux-gnu, porque en macOS native-tls usa Security.framework y el problema es invisible.
Maps lo decide el formulario, no el COBOL: MAPS-1::Geocode es una llamada a método sobre un id de control, y nada en el AST la distingue de cualquier otra llamada a método — GET sobre un RestClient se lee exactamente igual que GET sobre cualquier otra cosa. Así que lo que se lee es el .cfrm: un proyecto con un control Maps o WebSearch enlaza el cliente, y uno sin él no. Ese es el cambio que más ahorra, porque una aplicación de formularios corriente no tiene ningún control de Maps. HTTP se enlaza a propósito en toda aplicación de formularios, sin inspeccionar nada: la feature render de cobolt-forms descarga las teselas del mapa base de OSM con su propio ureq, así que una aplicación de formularios enlaza la pila TLS de la plataforma decida lo que decida esto — no hay nada que ganar con ser listos y sí un programa que funciona que perder. Los programas de consola son los que de verdad pueden desprenderse de OpenSSL: no poseen controles y su HTTP llega como un CALL que la build puede leer. Un CALL cuyo destino es un item de datos en lugar de un literal enlaza ahora los tres puentes, no solo SQL: el nombre solo se conoce en tiempo de ejecución, así que podría ser cualquier verbo; la versión anterior lo concedía para SQL y lo asumía en silencio para los demás.
Las builds con un puente apagado informan por el canal de fallo que ese puente ya tenía — HTTP mediante (body, status 0), exactamente donde cae una conexión rechazada; Maps mediante el Err que usan sus propios fallos de API — y cada mensaje nombra la decisión de build y cómo revertirla, en lugar de leerse como una avería de red corriente. Aviso: todos los puentes opcionales son ahora separables, y ninguno cambia lo que enlaza una build por defecto de este workspace: rcrun, el IDE y todos los tests conservan la superficie completa.
=== PowerRustCOBOL 1.62.88 — 2026-08-29 ===
NIST CCVS85 Source Text Manipulation (SM) queda con línea base: 0 -> 4 de 17, con las aserciones pasando de 8 PASS / 15 FAIL a 25 PASS / 9 FAIL (34.8 % -> 73.5 %). Nada cambió en el compilador ni en el runtime — ambas correcciones están en el arnés de conformidad, que medía el módulo injustamente. Todos los módulos terminados quedan intactos: NC 95/95 con 4 614 aserciones, SQ 85/85 con 624, IX 41/41 con 574, IF 45/45 con 841, RL 34/34 con 354, todos con cero fallos, y el censo de compilación de la suite completa sin cambios en 410 de 421.
• Corregido — la pasada de ejecución nunca suministró los copybooks de la suite
La pasada de compilación expande COPY contra la biblioteca de copybooks de CCVS85 desde que el arnés la incorporó. La pasada de ejecución nunca lo hizo. rcrun resuelve un copybook contra el propio directorio del fichero fuente, así que la biblioteca se replica ahora allí antes de ejecutar cada programa.
El módulo Source Text Manipulation trata de COPY, y demuestra un copybook usándolo: SM101A construye un fichero de datos a partir de declaraciones de registro suministradas por COPY, y SM102A vuelve a leer esos registros. Sin la biblioteca, SM101A se expandía a nada, escribía un fichero de cero bytes y SM102A informaba EOF PREMATURELY FOUND — una entrada ausente que se lee exactamente como un defecto del runtime.
• Corregido — las parejas constructor/comprobador de SM declaran sus productores
Tres miembros de SM leen un fichero que un miembro anterior escribió y lo dicen en su propia cabecera, así que entran en la tabla de miembros encadenados: SM102A <- SM101A, SM104A <- SM103A, SM202A <- SM201A. Cada entrada está citada de la cabecera del consumidor ("PROGRAM SM102A TESTS THE OUTPUT FILE PRODUCED BY SM101A"), nunca inferida de una tarjeta X que dos programas casualmente compartan.
ST102A y ST120A leen la misma tarjeta que escribe ST101A y deliberadamente NO se encadenan: ningún miembro de ST declara un productor, y ST102A no tiene PRINT-FILE alguno, de modo que el informe de un productor quedaría atrás y se puntuaría como propio de ST102A. Eso queda fijado como test negativo, con su razón.
=== PowerRustCOBOL 1.62.89 — 2026-08-30 ===
• Corregido — el FILE STATUS de un subprograma se informaba en el almacenamiento del llamante
Un fichero puede compartirse entre programas — eso es lo que significa IS EXTERNAL en una FD, y su estado de apertura y su posición sí son una sola cosa en toda la unidad de ejecución. Su item de FILE STATUS no lo es: lo nombra el SELECT propio de cada programa, sale del almacenamiento propio de ese programa, y una operación informa en el item que pertenece al programa que la ejecutó. Dos defectos superpuestos hacían que CCVS85 IC227A fallara diez aserciones en cinco sentencias — WRITE, CLOSE, OPEN, READ y EOF — con el mismo par intercambiado cada vez.