Skip to content

Latest commit

 

History

History
501 lines (458 loc) · 47.9 KB

File metadata and controls

501 lines (458 loc) · 47.9 KB

COBOL 1:1 parity tracker -- everything GnuCOBOL does, and our native-Rust status

The goal: the Rust port natively does everything GnuCOBOL does. This tracks every aspect of the language + runtime against two axes -- Runtime (is the libcob primitive ported 1:1?) and Front-end (can the native interpreter cobrun actually run it?). The authoritative surface is derived from the admitted GnuCOBOL 3.2 source (cobc/parser.y statements, libcob/intrinsic.c functions, cobc/reserved.c clauses). The gap between the two axes -- minus the rows marked BOUNDARY, which the admitted GnuCOBOL 3.2 oracle itself cannot run -- is what is left to build.

Summary

surface total runtime ported (1:1) front-end runs it
libcob runtime files 13 13 (100%) n/a
statements (verbs) 66 51 (77%) 58 (88%)
intrinsic functions 110 110 (100%) 94 (85%)
data-description clauses 18 (runtime via move/layout) see table
USAGE forms 10 (runtime ported) see table

Reading this: the runtime engine is ~100% -- every admitted libcob file and every intrinsic is ported 1:1 and oracle-sealed. The front-end (the native interpreter that turns source into runtime calls) runs the statements marked DONE, each proven byte-identical to the admitted cobc. The rows marked BOUNDARY are NOT a TODO: the admitted GnuCOBOL 3.2 oracle itself cannot compile/run them (it does not implement the COMMUNICATION SECTION, the ACUCOBOL screen/GUI verbs are not in its grammar, and ENTRY is invalid in a nested program), so there is no oracle output to be byte-identical to. Anything still unmarked is the genuine remaining front-end work.

Statements (the verb surface, from cobc/parser.y)

statement category runtime (libcob) front-end (cobrun) status
JSON GENERATE XML/JSON yes (mlio.c) yes DONE -- parses + runs
JSON PARSE XML/JSON yes (mlio.c) yes DONE -- parses + runs
XML GENERATE XML/JSON yes (mlio.c) yes DONE -- parses + runs
XML PARSE XML/JSON yes (mlio.c) yes DONE -- parses + runs
ADD arithmetic yes (numeric.c) yes DONE -- parses + runs
COMPUTE arithmetic yes (numeric.c) yes DONE -- parses + runs
DIVIDE arithmetic yes (numeric.c) yes DONE -- parses + runs
MULTIPLY arithmetic yes (numeric.c) yes DONE -- parses + runs
SUBTRACT arithmetic yes (numeric.c) yes DONE -- parses + runs
DISABLE communication n/a no BOUNDARY -- GnuCOBOL 3.2 does not implement the COMMUNICATION SECTION (the oracle itself cannot run it)
ENABLE communication n/a no BOUNDARY -- GnuCOBOL 3.2 does not implement the COMMUNICATION SECTION (the oracle itself cannot run it)
PURGE communication n/a no BOUNDARY -- GnuCOBOL 3.2 does not implement the COMMUNICATION SECTION (the oracle itself cannot run it)
RECEIVE communication n/a no BOUNDARY -- GnuCOBOL 3.2 does not implement the COMMUNICATION SECTION (the oracle itself cannot run it)
SEND communication n/a no BOUNDARY -- GnuCOBOL 3.2 does not implement the COMMUNICATION SECTION (the oracle itself cannot run it)
ALTER control flow n/a yes DONE -- parses + runs
CONTINUE control flow n/a yes DONE -- parses + runs
EVALUATE control flow n/a yes DONE -- parses + runs
EXIT control flow n/a yes DONE -- parses + runs
GOBACK control flow yes (call.c) yes DONE -- parses + runs
GOTO control flow n/a yes DONE -- parses + runs
IF control flow n/a yes DONE -- parses + runs
PERFORM control flow n/a yes DONE -- parses + runs
STOP control flow yes (common.c) yes DONE -- parses + runs
INITIALIZE data movement yes (move.c) yes DONE -- parses + runs
MOVE data movement yes (move.c) yes DONE -- parses + runs
SET data movement yes (common.c) yes DONE -- parses + runs
EXHIBIT debug (dialect) n/a yes DONE -- parses + runs
READY debug (dialect) n/a yes DONE -- parses + runs
USE declaratives yes (fileio.c) yes DONE -- parses + runs
RAISE exception yes (common.c) yes DONE -- parses + runs
RESET exception yes (common.c) yes DONE -- parses + runs
CLOSE file I/O yes (fileio.c) yes DONE -- parses + runs
COMMIT file I/O yes (fileio.c) yes DONE -- parses + runs
DELETE file I/O yes (fileio.c) yes DONE -- parses + runs
OPEN file I/O yes (fileio.c) yes DONE -- parses + runs
READ file I/O yes (fileio.c) yes DONE -- parses + runs
REWRITE file I/O yes (fileio.c) yes DONE -- parses + runs
ROLLBACK file I/O yes (fileio.c) yes DONE -- parses + runs
START file I/O yes (fileio.c) yes DONE -- parses + runs
UNLOCK file I/O yes (fileio.c) yes DONE -- parses + runs
WRITE file I/O yes (fileio.c) yes DONE -- parses + runs
CALL inter-program yes (call.c) yes DONE -- parses + runs
CANCEL inter-program yes (call.c) yes DONE -- parses + runs
ENTRY inter-program yes (call.c) no BOUNDARY -- invalid in a nested program; requires separately-compiled units
GENERATE report writer yes (reportio.c) yes DONE -- parses + runs
INITIATE report writer yes (reportio.c) yes DONE -- parses + runs
SUPPRESS report writer yes (reportio.c) yes DONE -- parses + runs
TERMINATE report writer yes (reportio.c) yes DONE -- parses + runs
DESTROY screen yes (screenio.c) yes DONE -- parses + runs
INQUIRE screen yes (screenio.c) no BOUNDARY -- an ACUCOBOL GUI verb absent from the GnuCOBOL 3.2 grammar (the oracle rejects it)
MODIFY screen yes (screenio.c) no BOUNDARY -- an ACUCOBOL GUI verb absent from the GnuCOBOL 3.2 grammar (the oracle rejects it)
MERGE sort/merge yes (fileio.c) yes DONE -- parses + runs
RELEASE sort/merge yes (fileio.c) yes DONE -- parses + runs
RETURN sort/merge yes (fileio.c) yes DONE -- parses + runs
SORT sort/merge yes (fileio.c) yes DONE -- parses + runs
ALLOCATE storage yes (common.c) yes DONE -- parses + runs
FREE storage yes (common.c) yes DONE -- parses + runs
EXAMINE string handling yes (strings.c) yes DONE -- parses + runs
INSPECT string handling yes (strings.c) yes DONE -- parses + runs
STRING string handling yes (strings.c) yes DONE -- parses + runs
TRANSFORM string handling yes (strings.c) yes DONE -- parses + runs
UNSTRING string handling yes (strings.c) yes DONE -- parses + runs
SEARCH table handling n/a yes DONE -- parses + runs
ACCEPT terminal I/O yes (termio.c / screenio.c) yes DONE -- parses + runs
DISPLAY terminal I/O yes (termio.c / screenio.c) yes DONE -- parses + runs
VALIDATE validation yes (common.c) yes DONE -- parses + runs

Intrinsic functions (FUNCTION ..., from libcob/intrinsic.c)

All 110 intrinsic functions are ported 1:1 in the runtime (110/110 confirmed present as cob_intr_* in the port; intrinsic.c is 100% in the doxygen parity). The front-end now evaluates 94 (85%) of them in FUNCTION ... references (DISPLAY / COMPUTE / MOVE / conditions), each proven byte-identical to cobc -- including cobc's compile-time constant fold for LENGTH/BYTE-LENGTH, the libcob-faithful display of binary, scaled and signed results, the full-precision 2048-bit transcendentals, the date functions and CURRENT-DATE under a pinned COB_CURRENT_DATE, MODULE-ID/MODULE-CALLER-ID from the interpreter's program stack, the LOCALE-* conversions under the pinned locale, and the compile stamp (WHEN-COMPILED/MODULE-DATE/MODULE-TIME) via the interpreter's compile step under a pinned SOURCE_DATE_EPOCH. Wired functions are bold. The unbold remainder is not an easy TODO; each is classified with a specific reason in the boundary tables below -- split into those absent/inactive in GnuCOBOL 3.2 itself (no oracle behaviour) and those present-but-not-yet-reproduced (a concrete future target each).

ABS, ACOS, ANNUITY, ASIN, ATAN, BINOP, BIT-OF, BIT-TO-CHAR, BOOLEAN-OF-INTEGER, BYTE-LENGTH, CHAR, CHAR-NATIONAL, COMBINED-DATETIME, CONCATENATE, CONTENT-LENGTH, CONTENT-OF, COS, CURRENCY-SYMBOL, CURRENT-DATE, DATE-OF-INTEGER, DATE-TO-YYYYMMDD, DAY-OF-INTEGER, DAY-TO-YYYYDDD, DISPLAY-OF, E, EXCEPTION-FILE, EXCEPTION-FILE-N, EXCEPTION-LOCATION, EXCEPTION-LOCATION-N, EXCEPTION-STATEMENT, EXCEPTION-STATUS, EXP, EXP10, FACTORIAL, FORMATTED-CURRENT-DATE, FORMATTED-DATE, FORMATTED-DATETIME, FORMATTED-TIME, FRACTION-PART, HEX-OF, HEX-TO-CHAR, HIGHEST-ALGEBRAIC, INTEGER, INTEGER-OF-BOOLEAN, INTEGER-OF-DATE, INTEGER-OF-DAY, INTEGER-OF-FORMATTED-DATE, INTEGER-PART, LCL-TIME-FROM-SECS, LENGTH, LOCALE-COMPARE, LOCALE-DATE, LOCALE-TIME, LOG, LOG10, LOWER-CASE, LOWEST-ALGEBRAIC, MAX, MEAN, MEDIAN, MIDRANGE, MIN, MOD, MODULE-CALLER-ID, MODULE-DATE, MODULE-FORMATTED-DATE, MODULE-ID, MODULE-PATH, MODULE-SOURCE, MODULE-TIME, MON-DECIMAL-POINT, MON-THOUSANDS-SEP, NATIONAL-OF, NUM-DECIMAL-POINT, NUM-THOUSANDS-SEP, NUMVAL, NUMVAL-C, NUMVAL-F, ORD, ORD-MAX, ORD-MIN, PI, PRESENT-VALUE, RANDOM, RANGE, REM, REVERSE, SECONDS-FROM-FORMATTED-TIME, SECONDS-PAST-MIDNIGHT, SIGN, SIN, SQRT, STANDARD-COMPARE, STANDARD-DEVIATION, STORED-CHAR-LENGTH, SUBSTITUTE, SUBSTITUTE-CASE, SUM, TAN, TEST-DATE-YYYYMMDD, TEST-DAY-YYYYDDD, TEST-FORMATTED-DATETIME, TEST-NUMVAL, TEST-NUMVAL-C, TEST-NUMVAL-F, TRIM, UPPER-CASE, VARIANCE, WHEN-COMPILED, YEAR-TO-YYYY

Boundary intrinsics (16 not wired). Two kinds, distinguished so the gap is not read as latent work:

Deliberately bounded -- absent/inactive in GnuCOBOL 3.2, or a non-libcob substrate (16). These cannot be byte-identical to anything the port reproduces: the oracle has no fixed behaviour for them -- libcob leaves them unimplemented, cobc rejects them as unknown user functions, the value is a compiler artifact with no interpreter analog (the compiled-binary path), or it is GMP's internal RNG substrate (the port reproduces libcob's algorithms, not GMP's internals, and does not link libgmp).

intrinsic why it is a boundary
BINOP not a user FUNCTION in GnuCOBOL 3.2: cobc rejects it as unknown (libcob-internal helper)
BOOLEAN-OF-INTEGER cobc rejects it at compile: "FUNCTION is not implemented" (no oracle output exists)
CHAR-NATIONAL cobc rejects it at compile: "FUNCTION is not implemented" (no oracle output exists)
DISPLAY-OF cobc rejects it at compile: "FUNCTION is not implemented" (no oracle output exists)
EXCEPTION-FILE-N cobc rejects it at compile: "FUNCTION is not implemented" (no oracle output exists)
EXCEPTION-LOCATION-N cobc rejects it at compile: "FUNCTION is not implemented" (no oracle output exists)
INTEGER-OF-BOOLEAN cobc rejects it at compile: "FUNCTION is not implemented" (no oracle output exists)
LCL-TIME-FROM-SECS not a user FUNCTION in GnuCOBOL 3.2: cobc rejects it as unknown (libcob-internal helper)
MODULE-PATH compiler artifact: cobc returns the compiled binary path; an interpreter produces no binary
MON-DECIMAL-POINT not a user FUNCTION in GnuCOBOL 3.2: cobc rejects it as unknown (libcob-internal helper)
MON-THOUSANDS-SEP not a user FUNCTION in GnuCOBOL 3.2: cobc rejects it as unknown (libcob-internal helper)
NATIONAL-OF cobc rejects it at compile: "FUNCTION is not implemented" (no oracle output exists)
NUM-DECIMAL-POINT not a user FUNCTION in GnuCOBOL 3.2: cobc rejects it as unknown (libcob-internal helper)
NUM-THOUSANDS-SEP not a user FUNCTION in GnuCOBOL 3.2: cobc rejects it as unknown (libcob-internal helper)
RANDOM GMP-RNG substrate boundary: the value is GMP's internal Mersenne-Twister stream (gmp_randseed_ui), not a libcob algorithm; the port does not reproduce GMP internals / link libgmp
STANDARD-COMPARE cobc rejects it at compile: "FUNCTION is not implemented" (no oracle output exists)

Present in GnuCOBOL 3.2, but no fixed oracle value (0). Runs under the oracle, but the result is not deterministic, so there is no single byte string to match (it is not a dead end -- it would be admissible under an explicit pinned profile the live source does not honour).

intrinsic why there is no fixed value

Data-description clauses

clause front-end (cobrun)
PICTURE yes
USAGE yes
VALUE yes
OCCURS yes
REDEFINES yes
RENAMES yes
SIGN yes
SYNCHRONIZED yes
JUSTIFIED yes
BLANK WHEN ZERO yes
GLOBAL yes
EXTERNAL yes
LEVEL 88 (condition-name) yes
LEVEL 66 (RENAMES) yes
LEVEL 77 yes
FILLER yes
OCCURS DEPENDING ON yes
INDEXED BY yes

USAGE forms

usage front-end (cobrun)
DISPLAY yes
COMP/BINARY yes
COMP-3/PACKED-DECIMAL yes
COMP-1 (float) yes
COMP-2 (double) yes
COMP-5 yes
COMP-6 yes
POINTER yes
INDEX yes
NATIONAL boundary

NATIONAL (UTF-16) is a boundary, not a TODO: GnuCOBOL 3.2 declares it unfinished -- cobc emits warning: handling of USAGE NATIONAL is unfinished; implementation is likely to be changed [-Wunfinished], and the explicit USAGE NATIONAL form does not compile. Pinning to an admittedly-unstable implementation is not a 1:1 target.

Front-end coverage -- what runs, and the COMPLETE fail-closed map

The 26 PARTIAL files above (the cobc parser / scanner / typeck / field / preprocessor) ARE this one clean-room interpreter (src/frontend.rs + examples/cobrun.rs). Verb-level status hides the forms WITHIN a wired verb, so here is the exhaustive picture, derived live from source (not curated): 69 sealed sub-form(s) proven byte-identical to cobc, and every distinct fail-closed form -- 206 of them, de-duplicated from the 228 RunError::Unsupported guards in src/frontend.rs (placeholder variants such as USAGE <x> collapse; the catch-all re-wrap is dropped). The doctrine is fail-closed: each is an explicit error + exit 2, never a silent wrong answer. Of the 206: 0 feature gaps (the genuine remaining work), 14 boundary non-claims (the oracle itself cannot run them, or they need a pinned env -- not TODOs), and 192 input-validation guards (malformed input cobc also rejects -- not feature gaps, listed for completeness).

Completion scorecard

Two axes. Breadth -- can the front-end run the construct at all (the bounded surface from cobc/parser.y / libcob/intrinsic.c / cobc/reserved.c). Depth -- which sub-forms within a wired verb run (sections A/B). All figures are computed live from the wired-marker scan, not asserted.

surface axis (breadth) total front-end runs % complete left (and why)
statements (verbs) 66 58 88% 8 -- all COMMUNICATION / ACUCOBOL GUI / ENTRY boundaries
intrinsic functions 110 94 85% 16 -- all non-deterministic / oracle-rejected boundaries
data-description clauses 18 18 100%
USAGE forms 10 9 90% 1 -- USAGE NATIONAL (unfinished in cobc -- boundary)
TOTAL (language breadth) 204 179 88% 25 left

Runtime engine: 100% (13/13 libcob files + 110/110 intrinsics ported 1:1, oracle-sealed) -- the breadth figure above is the FRONT-END (interpreter) axis only. Excluding the boundary non-claims the oracle itself cannot run, the front-end runs ~100% of the runnable language surface; the 204-item denominator above keeps those boundaries IN, so the honest front-end-of-everything figure is 88%.

Depth -- sub-forms within wired verbs

Breadth (above) asks can the verb run at all; depth asks which sub-forms of a wired verb run. 69 front-end sub-forms have been identified -- 69 sealed (byte-identical to cobc, section A) and 0 still open (fail-closed guards, section B). Depth completion = 69/69 = 100.0%, climbing to 100% as the open gaps seal. The denominator is the IDENTIFIED forms (sealed + open), computed live from source -- not "% of all COBOL" and not a grammar-alternative count (the gap rows are edge-cases that do not line up 1:1 with parser.y alternatives). A new fail-closed guard raises the denominator, so this can never read 100% while any guard remains. Below: the open gaps per verb, biggest movers first.

verb / clause open gaps (what's left) share of the remaining work
TOTAL 0 open (+ 69 sealed = 69 identified) 100.0% complete

A. Sealed sub-forms (69) -- proven byte-identical to cobc

Reality-checked against FRONTEND_SUBFORMS: the doc gate fails if a corpus anchor vanishes.

verb sub-form corpus proof
IF sign condition x IS [NOT] {POSITIVE|NEGATIVE|ZERO} (incl. COMP-3) lab/corpus/frontend/p104_sign_cond.cob
ADD/SUBTRACT/MULTIPLY/DIVIDE multiple receivers (ADD 1 TO Y Z, ... GIVING C D, in-place per-receiver) lab/corpus/frontend/p105_multi_receiver.cob
DIVIDE GIVING q REMAINDER r (incl. ROUNDED quotient, signed/scaled, via the sealed GNURUST.REMAINDER.1 primitive) lab/corpus/frontend/p106_divide_remainder.cob
INITIALIZE REPLACING cat BY val (NUMERIC/ALPHANUMERIC/ALPHABETIC/NUMERIC-EDITED, multi-category, PIC A vs X) lab/corpus/frontend/p107_initialize_replacing.cob
INSPECT REPLACING CHARACTERS BY y (incl. BEFORE/AFTER region) lab/corpus/frontend/p108_inspect_chars.cob
INSPECT multi-clause TALLYING ... REPLACING ... in one statement (ISO two-pass) lab/corpus/frontend/p112_inspect_multi.cob
UNSTRING DELIMITER IN / COUNT IN per receiver + TALLYING IN (added) lab/corpus/frontend/p109_unstring_delim.cob
EXAMINE UNTIL FIRST (REPLACING + TALLYING...REPLACING), via the inspect CHARACTERS/BEFORE helper lab/corpus/frontend/p110_examine_until.cob
COMPUTE ** fractional / identifier exponent (e.g. 9 ** 0.5), via the sealed cob_decimal_pow lab/corpus/frontend/p111_exponent.cob
SET SET cond TO FALSE (the 88 WHEN SET TO FALSE value) lab/corpus/frontend/p113_set_false.cob
UNSTRING INTO DISPLAY-numeric receivers (alphanumeric->numeric per field) lab/corpus/frontend/p114_unstring_num.cob
INITIALIZE over an OCCURS table (plain + REPLACING; expands to subscripted element leaves) lab/corpus/frontend/p115_init_occurs.cob
SORT/MERGE multiple KEYs with mixed ASCENDING/DESCENDING direction (major-to-minor) lab/corpus/frontend/p116_sort_multikey.cob
file I/O INDEXED files: READ NEXT in RECORD KEY order, random READ by key, START KEY >=, DELETE by key lab/corpus/frontend/p117_indexed.cob
file I/O READ ... NOT AT END / NOT INVALID KEY handler (success branch in one statement) lab/corpus/frontend/p118_read_handlers.cob
ACCEPT FROM ENVIRONMENT "name" (read a pinned environment variable) lab/corpus/frontend/p119_accept_env.cob
file I/O START ... INVALID KEY / NOT INVALID KEY handler clauses lab/corpus/frontend/p120_start_invalid.cob
UNSTRING WITH POINTER p scan cursor (read in, advanced past delimiters, written back), coexisting with TALLYING IN lab/corpus/frontend/p121_unstring_pointer.cob
DIVIDE ... GIVING q REMAINDER r with ON SIZE ERROR / NOT ON SIZE ERROR (zero divisor + per-receiver overflow; receiver left unchanged with a handler) lab/corpus/frontend/p122_divide_remainder_size.cob
INSPECT figurative-constant operands (HIGH-VALUE / LOW-VALUE / QUOTE as a 1-byte TALLYING/REPLACING comparand or replacement) lab/corpus/frontend/p123_inspect_figurative.cob
INITIALIZE [WITH FILLER] ALL TO VALUE -- restore each leaf to its VALUE clause; a no-VALUE leaf is left unchanged lab/corpus/frontend/p124_initialize_to_value.cob
UNSTRING ON OVERFLOW / NOT ON OVERFLOW handler (overflow = source characters remain after every receiver is filled) lab/corpus/frontend/p125_unstring_overflow.cob
PERFORM bare inline PERFORM <stmts> END-PERFORM (no TIMES/UNTIL/VARYING) runs the body exactly once lab/corpus/frontend/p126_perform_inline_once.cob
EXHIBIT CHANGED keyword -- cobc 3.2 leaves the suppression unimplemented (runs as plain EXHIBIT); CHANGED-without-NAMED is value-only lab/corpus/frontend/p127_exhibit_changed.cob
MOVE alphanumeric literal/source into a COMP/COMP-3/COMP-5 receiver (move.c indirect display path; previously yielded 0) lab/corpus/frontend/p128_move_alnum_to_binary.cob
UNSTRING into numeric-edited / scaled-DISPLAY receivers (the delimited substring is MOVEd; binary/packed still fail closed) lab/corpus/frontend/p129_unstring_edited.cob
MOVE/ADD/SUBTRACT CORRESPONDING -- like-named elementary leaves matched between two groups (MOVE all; ADD/SUBTRACT numeric pairs) lab/corpus/frontend/p130_corresponding.cob
qualified names name OF group [OF group...] / IN resolution + duplicate child-name disambiguation across record layouts lab/corpus/frontend/p131_qualified_names.cob
OCCURS / tables two-dimensional tables: outer group-OCCURS + inner elementary-OCCURS child, addressed C(i,j) row-major (read/write/COMPUTE-receiver) lab/corpus/frontend/p132_table_2d.cob
OCCURS / tables group-of-group tables (outer group-OCCURS over a sub-group; deep leaves A(i)) and N-dimensional C(i,j,k), via the recursive nested layout lab/corpus/frontend/p135_table_3d.cob
INITIALIZE over a nested / multi-dimension OCCURS table (every cell expanded across its dims) lab/corpus/frontend/p136_initialize_2d.cob
OCCURS / tables OCCURS min TO max DEPENDING ON on a GROUP (variable-length group table; live image + LENGTH = counter*elem, built at MAX) lab/corpus/frontend/p137_odo_group.cob
OCCURS / tables REDEFINES over a group-OCCURS interleaved buffer -- read AND write-through (the alias is a flat view of the buffer) lab/corpus/frontend/p138_redefines_group_occurs.cob
JSON/XML GENERATE NAME data-name IS "key" key-rename (incl. the outer object) + SUPPRESS data-name... field omission lab/corpus/frontend/p139_json_name_suppress.cob
INITIALIZE {category} TO VALUE (cobc ignores the category -> all valued leaves) and ALL TO VALUE over an OCCURS table (each element to its VALUE) lab/corpus/frontend/p141_initialize_to_value_forms.cob
REPORT WRITER Layer 1: RD PAGE LIMIT/HEADING/FIRST DETAIL + TYPE REPORT HEADING / DETAIL (LINE abs/PLUS) / CONTROL FOOTING FINAL with SUM; INITIATE/GENERATE/TERMINATE page-buffer flush lab/corpus/frontend/p142_report_writer.cob
REPORT WRITER Layer 2: PAGE LIMIT/FOOTING overflow -> flush page at high-water, new page, re-emit PAGE HEADING (final page padded) lab/corpus/frontend/p143_report_page_break.cob
REPORT WRITER Layer 3: data CONTROL breaks -> CONTROL FOOTING (per-control SUM subtotal, minor->major, reset on break) + CONTROL HEADING (major->minor); FINAL at TERMINATE lab/corpus/frontend/p144_report_control_break.cob
UNSTRING DELIMITED BY [ALL] d1 [OR [ALL] d2]... multi-delimiter (earliest match splits, DELIMITER IN captures it, ALL collapses repeats) lab/corpus/frontend/p145_unstring_multi_delim.cob
MOVE conversion completeness -- binary/packed -> alphanumeric (magnitude digits via DISPLAY), packed -> packed, binary/packed -> numeric-edited (found by the MOVE cross-product differential) lab/corpus/frontend/p146_move_conversions.cob
DIVIDE binary/packed operands + uninitialized binary/packed zero -- DIVIDE with a COMP/COMP-3 operand normalizes to DISPLAY (was InvalidAttr); a no-VALUE COMP/COMP-3 reads as 0, not '0'-byte garbage (found by the arithmetic cross-product differential) lab/corpus/frontend/p147_arith_binary.cob
ADD ADD a b [c...] GIVING r multi-operand fold computes the sum at full width before the store (was folded into the first operand's narrow width -- 60+60 -> "20" -- and the ON SIZE ERROR was judged on the truncated value); found by the SIZE-ERROR/ROUNDED differential battery lab/corpus/frontend/p148_add_giving_fold.cob
(refmod) reference modification base(start:len) / base(start:) -- alphanumeric substring, 1-based start, as a VALUE (DISPLAY/IF/MOVE-source/arith) and as a RECEIVER (MOVE/INSPECT/STRING target), with literal / data-name / subscripted bounds and a subscripted base T(i)(s:l). Boundary: expression-valued bounds and refmod inside a FUNCTION argument fail closed lab/corpus/frontend/p149_reference_modification.cob
IF class conditions IS [NOT] {NUMERIC | ALPHABETIC | ALPHABETIC-UPPER | ALPHABETIC-LOWER} wired to the sealed class byte predicates; the NUMERIC variant chosen by usage (binary always numeric, packed nibble-validated, signed DISPLAY by sign convention incl. SIGN LEADING/TRAILING SEPARATE); found by the class-condition cross-product battery lab/corpus/frontend/p150_class_conditions.cob
IF abbreviated combined conditions -- a term after AND/OR may omit the subject (A > B AND < C) and the operator (A = 1 OR 2), reusing the last-stated subject/operator/negation; a leading NOT negates a term (NOT A = 5). Found by the abbreviated-condition battery lab/corpus/frontend/p152_abbreviated_conditions.cob
(intrinsic) FUNCTION TRIM(x [LEADING | TRAILING]) -- the optional direction keyword is parsed as a modifier (was evaluated as a second argument and failed); wired to the runtime cob_intr_trim direction lab/corpus/frontend/p153_trim_directions.cob
COMPUTE unary sign glued to an operand or paren (-A, -(A-B), nested -(-(-3))) and RIGHT-associative exponentiation (2 ** 3 ** 2 = 512, non-negative integer exponent = exact repeated multiply, else the sealed decimal pow); found by the COMPUTE-expression battery lab/corpus/frontend/p154_compute_unary_pow.cob
ROUNDED ROUNDED MODE [IS] <mode> on COMPUTE/ADD/SUBTRACT/MULTIPLY/DIVIDE -- all eight modes (TRUNCATION, NEAREST-AWAY-FROM-ZERO, AWAY-FROM-ZERO, NEAREST-TOWARD-ZERO, NEAREST-EVEN, TOWARD-GREATER, TOWARD-LESSER, PROHIBITED=size-error-on-drop) via the mode-aware round_decimal_mode; found by the ROUNDED-MODE differential battery lab/corpus/frontend/p155_rounded_modes.cob
EXIT EXIT PERFORM (break the nearest inline PERFORM), EXIT PERFORM CYCLE (skip to its next iteration -- innermost loop of a VARYING/AFTER), and NEXT SENTENCE (transfer to the statement after the next period, distinct from CONTINUE); found by the control-flow battery lab/corpus/frontend/p156_exit_perform_next_sentence.cob
DISPLAY DISPLAY ... UPON ENVIRONMENT-NAME / UPON ENVIRONMENT-VALUE set the runtime environment (a per-run override) with NO stdout; ACCEPT ... FROM ENVIRONMENT-VALUE / FROM ENVIRONMENT "name" read it back (override before the real process env). Found by the SORT/ACCEPT battery lab/corpus/frontend/p157_display_accept_environment.cob
(intrinsic) FUNCTION arguments that are subscripted f(A(i)) or reference-modified f(S(s:l)), and mixed literal+identifier lists -- the arg parser splits at top-level separators only, keeping each argument's own parens; FUNCTION LENGTH of a refmod is the BASE item length (cobc ignores the refmod). Boundary: a nested FUNCTION as an argument fails closed. Found by the FUNCTION-complex-arg battery lab/corpus/frontend/p158_function_complex_args.cob
UNSTRING into binary (COMP/COMP-5) and packed (COMP-3) receivers -- the delimited segment is sized by the receiver's DIGIT width (not its physical byte length) and stored via the sealed alnum->binary/packed MOVE; COUNT/DELIMITER and mixed receiver lists work. Found by the FILE-PARITY section-B gap sweep lab/corpus/frontend/p159_unstring_binary_recv.cob
(JSON/XML) GENERATE [ON EXCEPTION imp] [NOT ON EXCEPTION imp] -- the handler blocks are parsed + dispatched; on success cobc runs NOT ON EXCEPTION ONLY when it is the sole handler (with both present it runs neither -- a 3.2 quirk reproduced). Found by the FILE-PARITY section-B gap sweep lab/corpus/frontend/p160_ml_generate_exception.cob
SET SET <switch-mnemonic> [...] TO ON|OFF -- toggle a SPECIAL-NAMES SWITCH-n IS <mnemonic> UPSI switch at runtime (multi-target); the ON/OFF STATUS condition-names read the live (RefCell) state. Found by the FILE-PARITY section-B gap sweep lab/corpus/frontend/p161_set_switch.cob
INITIALIZE ... [WITH FILLER] {ALL|cat} TO VALUE [THEN] REPLACING cat BY val ... -- TO VALUE restores each leaf that HAS a VALUE; the trailing REPLACING then sets each leaf WITHOUT a VALUE whose category is named (neither -> unchanged). Found by the FILE-PARITY section-B gap sweep lab/corpus/frontend/p162_init_value_then_repl.cob
(tables) SPACE-separated multi-dimension subscripts -- CELL(1 1), CELL (3 2) (space before paren), 3-D C3(2 1 2) -- cobc accepts space OR comma separators; the subscript gluer keeps them distinct (was concatenating C(2+3) into one 23 subscript). Relative subscripting C(I + 1) stays one subscript lab/corpus/frontend/p163_space_subscripts.cob
OCCURS / tables OCCURS DEPENDING ON the OUTER dimension of a multi-dimension group (05 ROW OCCURS 1 TO 3 DEPENDING ON N. 10 CELL ... OCCURS 2.): the interleaved buffer is built at MAX, element addressing uses the fixed MAX strides, and the LIVE image / FUNCTION LENGTH is counter*stride. Found by the FILE-PARITY section-B gap sweep lab/corpus/frontend/p164_odo_multidim.cob
OCCURS / tables REDEFINES descendant inside a table element -- the redefining item overlays its target at the same element offset across all occurrences (flat group-OCCURS and multi-dimension); the layout places it at the target's offset and does not advance the element size. Found by the FILE-PARITY section-B gap sweep lab/corpus/frontend/p165_redefines_in_table.cob
OCCURS / tables SYNCHRONIZED descendant of a table element -- slack before each SYNC field aligns it to its boundary, and the element is padded up to the largest SYNC alignment so every occurrence stays aligned (e.g. X + S9(9) COMP SYNC + X -> 12-byte element). Found by the FILE-PARITY section-B gap sweep lab/corpus/frontend/p166_sync_in_table.cob
(JSON/XML) XML GENERATE SUPPRESS id WHEN {ZERO|SPACE|LOW-VALUE|HIGH-VALUE} -- the element is omitted only when its value matches the figurative (per-element conditional suppression). JSON GENERATE SUPPRESS WHEN is a cobc compile error, so it fails closed as validation. Found by the FILE-PARITY section-B gap sweep lab/corpus/frontend/p167_xml_suppress_when.cob
(JSON/XML) GENERATE content rendering -- an all-spaces alphanumeric keeps ONE space ({"A":" "} / <A> </A>, never empty), and XML element content escapes the double-quote (&quot;) as well as & < >. Found by the JSON/XML rendering battery lab/corpus/frontend/p168_ml_content_render.cob
(JSON/XML) GENERATE over an elementary OCCURS table emits only the FIRST occurrence (cobc 3.2 behaviour -- T OCCURS 3 -> {"T":<T(1)>}). A group-OCCURS source fails closed (the strided element-1 children are not addressable by the renderer). Found by the JSON/XML table battery lab/corpus/frontend/p169_ml_occurs.cob
(tables) a SPACE before a balanced subscript -- E (I), C (2) -- is glued to the name (the gluer previously joined only lexer-SPLIT subscripts like (1 1), not a balanced (I) after a space). Found running the real-world opencbs corpus (SEARCH / EVALUATE over name (sub)) lab/corpus/frontend/p170_space_before_subscript.cob
OCCURS / tables a group-OCCURS table that REDEFINES a VALUE-bearing group (the classic "table initialised via a redefinition" idiom): the OCCURS descendants ALIAS the redefined target's live image at their offset -- reads see the literal entries, SEARCH matches them, and a write through the redefining table lands in the shared storage. Found running the real-world opencbs corpus (DF08/DF23/DF26 SEARCH a redefined table) lab/corpus/frontend/p171_redefines_group_occurs.cob
(groups) MOVE CORRESPONDING moves only like-NAMED elementary leaves -- FILLER never corresponds, so a separator FILLER in the target keeps its own value (the - in a yyyy-mm-dd trailer date survives, instead of being overwritten by the source's blank FILLER). Found running the real-world opencbs corpus (DF29 MOVE CORR a date group) lab/corpus/frontend/p172_move_corr_skips_filler.cob
(arithmetic) a multi-line arithmetic expression whose continuation line BEGINS with the division operator / (indented past the indicator area) keeps the divide -- cobc treats a deeply-indented line-leading / as DIVISION, only a column-7 / is a fixed-format page-eject comment. The comment-indicator detection is bounded to column <= 7. Found running the real-world opencbs corpus (DF36 split COMPUTE) lab/corpus/frontend/p173_compute_slash_continuation.cob
(arithmetic) a numeric literal written with no leading zero (.08 = 0.08, .5 = 0.5) -- cobc accepts it; the lexer now keeps a . that is directly followed by a digit as the start of the literal instead of emitting it as a sentence terminator. Found running the real-world opencbs corpus (DF06 COMPUTE ... * .08) lab/corpus/frontend/p174_leadingdot_decimal.cob
(groups) a qualified name X OF Y as a COMPUTE target and as a PARENTHESISED operand (X OF Z * .08): the qualified-name collapser strips the leading ( the lexer glued onto the operand so the inner name still resolves to the right record. Found running the real-world opencbs corpus (DF06 qualified COMPUTE) lab/corpus/frontend/p175_qualified_compute_operand.cob
(groups) several FILLERs of DIFFERENT sizes in one group each occupy their own slot -- they get unique field keys, so a group MOVE distributes bytes to the named children at the correct offsets (two same-parent FILLERs previously collided to one field, and the wrong length shifted every later child). Found running the real-world opencbs corpus (DF19 splits a 2021 09 15 date) lab/corpus/frontend/p176_multi_filler_group_move.cob

B. Feature gaps -- the genuine remaining work (0)

Deliberate limits of an otherwise-wired verb. These are the real "what's missing" list; sealing one removes its row on the next regenerate (the gate enforces it).

verb / clause fail-closed form (<x> = a runtime value)

C. Boundary non-claims -- NOT TODOs (14)

The admitted GnuCOBOL 3.2 oracle itself cannot run these (COMMUNICATION SECTION, ACUCOBOL GUI verbs, ENTRY in a nested program), or they depend on a non-pinned environment (the live clock / compile stamp) -- so there is no byte-truth to match. Documented, not latent work.

verb / clause fail-closed form (<x> = a runtime value)
ACCEPT ACCEPT FROM : a runtime non-claim (terminal/command-line input has no deterministic oracle); wired sources are DATE/DAY/TIME/DAY-OF-WEEK/ENVIRONMENT
ACCEPT FROM terminal/console: interactive input is a runtime non-claim (no deterministic oracle); the wired sources are DATE/DAY/TIME/DAY-OF-WEEK/ENVIRONMENT
ACUCOBOL GUI (oracle boundary) : an ACUCOBOL screen/GUI verb absent from the GnuCOBOL 3.2 grammar -- the oracle itself rejects it (boundary non-claim)
COMMUNICATION (oracle boundary) : GnuCOBOL 3.2 does not implement the COMMUNICATION SECTION -- the oracle itself cannot run it (boundary non-claim)
ENTRY (oracle boundary) ENTRY: an alternate entry point that is invalid in a nested program -- it requires separately-compiled units, while the front-end runs one source with contained programs
FUNCTION FUNCTION : cobc 3.2 does not implement it (a compile-reject) or it is a live-clock/locale/GMP-PRNG non-claim
USAGE USAGE : cobc 3.2 leaves NATIONAL unfinished (-Wunfinished) -- a non-claim, not a buildable front-end gap
compile stamp (pinned-env) FUNCTION WHEN-COMPILED / MODULE-DATE / MODULE-TIME / MODULE-FORMATTED-DATE requires a pinned SOURCE_DATE_EPOCH (the live compile clock is a non-claim)
SOURCE_DATE_EPOCH exceeds the year-9999 ceiling
SOURCE_DATE_EPOCH is not a number
date / clock (pinned-env) ACCEPT FROM DATE/TIME requires a pinned COB_CURRENT_DATE (the live clock is a non-claim)
FUNCTION requires a pinned COB_CURRENT_DATE
FUNCTION : COB_CURRENT_DATE has no year
FUNCTION CURRENT-DATE requires a pinned COB_CURRENT_DATE (the live clock is a non-claim)

D. Input-validation guards -- malformed input rejected (192)

Not feature gaps: these reject malformed / incomplete source (a missing operand, an undeclared file, a non-integer subscript) that cobc also rejects. Listed so the inventory is provably COMPLETE: B + C + D together account for every distinct fail-closed form in the source (nothing cherry-picked).

verb / clause fail-closed form (<x> = a runtime value)
88 condition-name 88 has no VALUE to SET
88 condition-name is not a value operand
88 condition-name with no parent item
SET TO : an 88 condition-name is only SET ... TO TRUE
SET TO FALSE: not an 88 condition-name
SET TO FALSE: the 88 has no WHEN SET TO FALSE value
SET TO TRUE: not an 88 condition-name
cannot MOVE into an 88 condition-name
expected condition-name after 88
ACCEPT ACCEPT FROM : receiver must be a -digit numeric/alphanumeric item
ACCEPT FROM ENVIRONMENT: missing variable name
ACCEPT FROM: missing source
ACCEPT: missing receiver
CALL CALL \
CALL without a program name
COMPUTE COMPUTE with no receiver
COMPUTE without '='
missing ')' in COMPUTE
string in COMPUTE
trailing tokens in COMPUTE expr at
unexpected end of COMPUTE expr
DIVIDE / arithmetic DIVIDE ... REMAINDER requires GIVING
DIVIDE ... REMAINDER: GIVING needs exactly one quotient + one remainder receiver
DIVIDE ... REMAINDER: missing INTO/BY
DIVIDE ... REMAINDER: missing dividend/divisor operand
DIVIDE ... REMAINDER: receiver <x> must be a numeric (non-edited) item
EVALUATE EVALUATE without a subject
EXAMINE EXAMINE REPLACING mode
EXAMINE REPLACING: unrecognized mode <x> (expected ALL/LEADING/FIRST/UNTIL FIRST)
EXAMINE TALLYING REPLACING: unrecognized mode
EXAMINE TALLYING mode
EXAMINE TALLYING: unrecognized mode <x> (expected ALL/LEADING/UNTIL FIRST)
EXAMINE: expected TALLYING or REPLACING
EXAMINE: missing field
FUNCTION FUNCTION : missing argument
FUNCTION : needs a subject and from/to pairs
FUNCTION : needs at least one argument
FUNCTION : needs two arguments
FUNCTION : the pointer has no SET ... TO ADDRESS OF target
FUNCTION CONCATENATE: needs at least one argument
FUNCTION PRESENT-VALUE: needs a rate and at least one flow
FUNCTION TRIM: missing argument
GO TO GO TO unknown paragraph <x>
GO TO without a target paragraph
INITIALIZE INITIALIZE ... : cobc rejects this clause placement
INITIALIZE ... TO VALUE requires ALL or a category before VALUE (cobc rejects a bare TO VALUE)
INITIALIZE ... TO VALUE: cobc rejects this trailing clause (only [THEN] REPLACING ... or [THEN] TO DEFAULT)
INITIALIZE ... TO VALUE: no item named
INITIALIZE REPLACING: expected BY
INITIALIZE REPLACING: expected a category
INITIALIZE REPLACING: missing replacement value
INITIALIZE REPLACING: no category given
INITIALIZE REPLACING: unrecognized category <x> (expected NUMERIC/ALPHANUMERIC/ALPHABETIC/NUMERIC-EDITED/ALPHANUMERIC-EDITED)
INITIALIZE: no item named
INSPECT INSPECT CONVERTING: expected TO
INSPECT REPLACING CHARACTERS: expected BY
INSPECT REPLACING: expected BY
INSPECT REPLACING: missing mode
INSPECT REPLACING: unrecognized mode <x> (expected CHARACTERS/ALL/LEADING/FIRST)
INSPECT TALLYING FOR: unrecognized mode <x> (expected ALL/LEADING/CHARACTERS)
INSPECT TALLYING: expected FOR
INSPECT TALLYING: missing FOR mode
INSPECT TALLYING: missing counter
INSPECT region clause near
INSPECT: missing operand
INSPECT: missing target
INSPECT: unrecognized clause (expected TALLYING/REPLACING/CONVERTING)
MOVE MOVE ALL: expected a non-empty literal or figurative
MOVE without TO
MOVE without source
a group MOVE is distributed across its leaves by write_field
MOVE / ADD / SUBTRACT CORR CORRESPONDING is not a valid form
CORRESPONDING without
CORRESPONDING: missing source group
CORRESPONDING: missing target group
CORRESPONDING operand <x> is not a group item
MOVE CORRESPONDING without TO
MOVE CORRESPONDING: missing source group
MOVE CORRESPONDING: missing target group
OCCURS / tables INITIALIZE over OCCURS child <x>: a table element requires a subscript (cobc rejects the bare name)
JSON/XML GENERATE: source <x> has a nested or multi-dimension group-OCCURS (cobc handles only a flat group-OCCURS here; deeper nesting is -Wpending)
OCCURS count is not an integer
OCCURS max is not an integer
SEARCH <x> is not an OCCURS table
group-OCCURS <x> cannot contain an OCCURS DEPENDING ON item -- cobc rejects a table of variable-length items
group-OCCURS child <x> must be subscripted
PERFORM PERFORM TIMES count not an integer
PERFORM VARYING: expected
PERFORM VARYING: missing BY value
PERFORM VARYING: missing FROM value
PERFORM VARYING: missing loop variable
PERFORM: unknown paragraph <x>/<x>
PICTURE PIC :
RENAMES (66) 66 level without RENAMES
RENAMES THRU without an end data-name
RENAMES without a start data-name
REPORT / ML GENERATE GENERATE: <x> is not a report group -- cobc rejects GENERATE of a non-report item (\
GENERATE: missing report group
JSON GENERATE SUPPRESS WHEN: cobc rejects WHEN on a JSON SUPPRESS (a compile error)
JSON/XML GENERATE NAME: expected `data-name IS \
JSON/XML GENERATE without FROM
JSON/XML GENERATE: missing destination
JSON/XML GENERATE: missing source
JSON: expected GENERATE/PARSE
XML GENERATE SUPPRESS ... WHEN: expected a figurative (ZERO/SPACE/LOW-VALUE/HIGH-VALUE)
XML: expected GENERATE
SEARCH SEARCH ALL <x>: no ASCENDING/DESCENDING KEY
SEARCH ALL: WHEN must be a key equality (key = value)
SEARCH ALL: missing WHEN
SEARCH <x>: no INDEXED BY or VARYING index
SEARCH: missing table name
SET SET ... BY : not an integer
SET ... BY: missing amount
SET ... TO: missing value
SET ... UP/DOWN must be followed by BY
SET TO : target is not a numeric/index item
SET UP/DOWN BY: not a numeric index
SET: expected TO or UP|DOWN BY (cobc rejects a SET with neither)
SET: no target before TO
SORT / MERGE RELEASE <x>: not an SD/FD record
RELEASE: missing record
RETURN: <x> is not a declared file
RETURN: missing sort file
SORT INPUT PROCEDURE: unknown paragraph <x>
SORT OUTPUT PROCEDURE: unknown paragraph <x>
SORT/MERGE KEY <x> is not a field of the sort record
SORT/MERGE requires GIVING or OUTPUT PROCEDURE
SORT/MERGE requires USING or INPUT PROCEDURE
SORT/MERGE: no KEY given
SORT: <x> is not a declared file
SORT: missing sort file
STRING STRING without INTO
STRING: missing target
TRANSFORM TRANSFORM without FROM
TRANSFORM without TO
TRANSFORM: missing target
UNSTRING UNSTRING COUNT IN: missing field
UNSTRING DELIMITER IN: missing field
UNSTRING TALLYING IN: missing field
UNSTRING into <x>: target must be an elementary or group data item (cobc rejects other targets)
UNSTRING without INTO
UNSTRING: no receiving field
USAGE USAGE with no form
unrecognized USAGE
VALUE empty VALUE
file I/O CLOSE: <x> is not a declared file
DELETE requires a RELATIVE or INDEXED file (invalid on SEQUENTIAL)
DELETE: <x> is not a declared file
DELETE: missing file
OPEN: <x> is not a declared file
OPEN: missing mode
OPEN: unrecognized mode <x> (expected INPUT/OUTPUT/EXTEND/I-O)
READ: <x> is not a declared file
READ: missing file
RELATIVE KEY <x> is not an integer
RELATIVE file <x> has no RELATIVE KEY
REWRITE <x>: not an FD record
REWRITE: missing record
START KEY: NOT EQUAL relation is not allowed on START (only NOT < / NOT >)
START KEY: unrecognized relation (expected = > < >= <= NOT< NOT>)
START requires a RELATIVE or INDEXED file (invalid on SEQUENTIAL)
START: <x> is not a declared file
START: missing file
UNLOCK: <x> is not a declared file
WRITE <x>: not an FD record
WRITE: missing record
level numbers expected data name after a level number
unsupported level number
other form (target/giving)
: no receiver
: no source operands
INDEXED RECORD KEY <x> is not a field of the record
INDEXED file <x> has no RECORD KEY
THRU without lower bound
THRU without upper bound
WHEN without a value
condition: missing left operand
condition: missing right operand
condition: unrecognized relational operator (expected = > < >= <= <> GREATER LESS EQUAL)
expected data-name after 66
multi-dimension leaf needs subscript(s), got
not a numeric literal:
reference-modification length '' is not an integer
reference-modification start '' is not an integer
subscript '' is not an integer
trailing tokens in condition at
unexpected '.'
verb
program structure : no PROCEDURE DIVISION
expected program name after PROGRAM-ID
no PROCEDURE DIVISION
no PROGRAM-ID

Provenance + method

Derived from the admitted GnuCOBOL 3.2 source: statements from cobc/parser.y (statement: grammar rules), intrinsic functions from libcob/intrinsic.c (cob_intr), data clauses/usages from cobc/reserved.c. The libcob runtime files are the 13 admitted compilation units.

Regenerate with cargo run -p xtask -- cobol-parity generate. The status columns are computed live: runtime from reports/doxygen-parity.json, front-end from the WIRED_STATEMENTS and WIRED_FUNCTIONS markers in src/frontend.rs. As statements and intrinsics are wired into the front-end, regenerating updates this doc; the doc-refresh gate (lab/check-docs.sh) fails if it drifts.