COBOL Backend Specification
1. Target
Primary target:
ibm-enterprise-cobol-zos
Secondary local target:
gnucobol-local
The IBM target is the source of truth for generated enterprise COBOL style. The GnuCOBOL target exists for local testing and CI.
2. Reference format
Generated COBOL is written in fixed reference format, which is the only one
Enterprise COBOL on z/OS reads. A source line is 72 characters: columns 1-6 are
the sequence number area, column 7 is the indicator area, columns 8-11 are Area
A and columns 12-72 are Area B. Columns 73-80 are the identification area and
are not part of the program. SOURCEFORMAT(EXTEND) is an AIX option; there is
nothing on z/OS that widens the line.
Nothing warns about crossing the margin. The compiler does not see the text past
column 72, so a name is silently shortened and the compile fails somewhere else
on a name the source appears to define. Every generated line therefore goes
through packages/cobol-backend/src/reference-format.ts on its way out:
- a statement too wide for the line is broken at a space and continued in Area B;
- an alphanumeric literal too wide for the line is filled to column 72 exactly and reopened on a continuation line carrying a hyphen in column 7, because every column of a continued line through 72 is part of the literal;
- a comment continues as a comment;
- a copybook is source too, so it is written in the same format — the 01 sits in Area A like any other level indicator.
JCL follows its own version of the same rule: fields end at column 71, and a
parameter field continues by breaking after a complete parameter including its
comma, then resuming between columns 4 and 16 of a card beginning // and a
blank.
Every cobc invocation in this repository passes -fixed. GnuCOBOL guesses the
format from the first line and will read a whole program as free format, where
none of these rules exist — which is a validation that proves nothing about the
target. tests/reference-format.test.ts checks the margins and the areas over
every generated artifact.
3. Generated COBOL style
The house style is a contract with a check against every rule, and it lives in
generated-code-standards.md. The rules the target
itself imposes, each with the manual it comes from, are in
target-conformance.md. Neither is repeated here,
because a second copy is a copy that goes stale — this section used to hold one,
and it was still describing a PROGRAM-ID with a hyphen in it long after the
compiler stopped emitting one.
4. Program structure
A generated program opens with the compiler options its behaviour depends on, then a prologue derived from the program itself:
CBL ARITH(COMPAT),TRUNC(STD),NUMPROC(NOPFD),NOSSRANGE
CBL RENT,NODYNAM,QUOTE,PGMNAME(COMPAT)
*> ---------------------------------------------------------------
*> ACCOUNTT — AccountTransfer
*>
*> Generated by bankc from main.bank.ts.
*> ...
*> ---------------------------------------------------------------
IDENTIFICATION DIVISION.
PROGRAM-ID. ACCOUNTT.
DATA DIVISION.
WORKING-STORAGE SECTION.
PROCEDURE DIVISION.
BANK-MAIN.
The PROGRAM-ID is at most eight characters and carries no hyphen, because
under the default PGMNAME(COMPAT) an external program-name is folded to
uppercase, truncated to eight and has its hyphens translated to zero — so
PROGRAM-ID. ACCOUNT-TRANSFER. would define the entry point ACCOUNT0 while
the job's EXEC PGM= said something else. The program-name, the load module
member, the artifact file name and the EXEC PGM= are one string from one rule.
BANK-MAIN is the only paragraph that ends the program. Every routine has an
exit paragraph and every caller performs it THRU that paragraph, so a failure
leaves through one path rather than ending where it was found.
Which sections appear depends on what the program uses: an ENVIRONMENT DIVISION and FILE SECTION only where there are files, a LINKAGE SECTION
only where something is passed in, a REPORT SECTION only where a report is
declared.
5. Data mapping
Decimal
decimal<18, 2>;
Default IBM COBOL mapping:
PIC S9(16)V99 COMP-3
The exact mapping must be controlled by backend profile and documented.
String
string<16>;
COBOL:
PIC X(16)
Bool
PIC X(1) VALUE "N"
One character, "Y" or "N", initialised false. No condition names are
generated for it: a bool is tested by comparing the field, and an 88 would be a
second spelling of the same two values. Enums are the opposite case — they carry
one 88 per member, because there the member name is what the procedure division
should be naming.
Date
PIC 9(8)
Format:
YYYYMMDD
6. Copybook generation
Generated copybooks must:
- preserve field order
- include stable comments where configured
- use deterministic names
- support group records
- support packed decimal fields
- support bounded arrays
- support condition names where needed
7. Paragraph generation
Function:
function validateAmount(amount: decimal<18, 2>): bool;
Generated paragraph name:
VALIDATE-AMOUNT.
Rules:
- paragraph names are globally unique per program
- source map links paragraph to source span
- no generated paragraph can be unreachable without a warning
- error paragraphs use stable names
8. Decimal operations
Decimal operations must preserve:
- precision
- scale
- rounding
- overflow checks
- signedness
Generated COBOL must not silently truncate precision.
If a target COBOL operation may truncate, the compiler must emit either safe generated code or a compile-time diagnostic.
9. Db2 generation
Generated Db2 code must use:
EXEC SQL
...
END-EXEC.
Requirements:
- SQLCA declaration
- host variable declaration
- SQLCODE checks
- generated error paragraph
- audit report listing SQL statements
- build report listing precompile/bind needs
10. CICS generation
Generated CICS code must use:
EXEC CICS
...
END-EXEC.
Requirements:
- response-code handling
- syncpoint/rollback mapping
- transaction boundary in source map
- generated error paragraphs
- COMMAREA/channel/container strategy documented per profile
11. VSAM/file generation
Generated file programs must contain:
ENVIRONMENT DIVISIONINPUT-OUTPUT SECTIONFILE-CONTROLDATA DIVISIONFILE SECTION- FD declarations
- file status variables
- open/read/write/close paragraphs
- file status checks
12. Source maps
Every emitted artifact must be traceable.
Source map fields:
{
"sourceFile": "examples/account-transfer/src/main.bank.ts",
"sourceStart": { "line": 10, "column": 1 },
"sourceEnd": { "line": 18, "column": 2 },
"artifact": "dist/cobol/ACCOUNTT.cbl",
"targetStartLine": 120,
"targetEndLine": 163,
"category": "function",
"symbol": "validateAmount"
}
13. Generated-code banner
Every generated source file should contain:
Generated by bankc.
Do not edit this file directly.
Source maps are available in dist/maps.
No timestamp in banner by default because output must be deterministic.
14. Unsupported target features
What the backend cannot emit fails with a diagnostic rather than with partial output. A program that compiles to something incomplete is worse than one that does not compile, because only the first reaches a reader who believes it.
- Dynamic SQL, refused by
BANK-SQL-002and refused on purpose: a statement built at run time cannot be checked at compile time, which is the whole basis of the SQL rules. - A rowset fetch on a scrollable cursor, which is
FETCH ROWSET STARTING AT ABSOLUTE nand a different statement from either of the two this backend emits.BANK-SQL-011refuses the combination rather than emitting the single-row form against aWITH ROWSET POSITIONINGcursor, which is-249. (Scrollable cursors themselves were listed here as absent until 2026-08-07; they arecursor ... scrollwithfor each ... from n backward. So wasGET DIAGNOSTICS, which never was — the SQL text is passed through, so it has always been writable.) - A
REDEFINESpattern the copybook importer cannot lay out, which is refused at import rather than imported at the wrong offsets.
Recursion is not on this list. It is emitted as a contained RECURSIVE
program with LOCAL-STORAGE, and examples/amortisation-schedule runs it.
status-and-limits.md is the current list of what is
missing; this section is only about how a refusal behaves.