Write banking logic in BankTS. Review the COBOL it generates.
BankTS is a small, statically typed language designed for banking workloads, and BankLang is its compiler. With consistent compiler settings and versions, a module generates IBM Enterprise COBOL, record copybooks, JCL, a source map, and an audit report. The compiler does not call an AI model during a build or at runtime.
BankTS makes selected rules explicit: decimal precision and scale, rounding mode, idempotency keys, audit events, file status, and selected SQL and CICS outcomes. BankLang rejects scenarios it can prove unsafe. It is a narrow compiler, not a replacement for COBOL or for a bank's core systems.
Current validation uses GnuCOBOL, not IBM's compiler. The examples are compiled and, where supported, executed locally with GnuCOBOL 3.2.0 under an IBM-shaped profile. No IBM Enterprise COBOL validation has been performed, and no generated output has run on z/OS. What that leaves open →
Compile-time checks for selected banking risks
This transfer contains three independent issues. BankLang reports them before it emits COBOL.
transaction postTransfer(request: TransferRequest) {
debit(request.debitAccount, request.amount);
credit(request.creditAccount, request.fee);
}
BANK-TXN-001 Transaction postTransfer has no idempotency key.
BANK-AUD-001 Transaction postTransfer does not emit an audit event.
BANK-LED-001 Transaction postTransfer does not balance: debited request.amount against credited request.fee.
Open this program in the playground →
In plain terms, a retry can apply the transfer twice, no audit event records the movement, and the amount debited does not match the amount credited. These are the kinds of omissions that otherwise depend on a review or a test case noticing them.
The compiler currently implements 16 rules in this category, alongside 114 implemented diagnostics overall. The catalogue explains each rule, and the test suite exercises the rules. Mutation results are tracked separately in the verification documentation.
Why keep COBOL as the output
Existing systems often hold the rules for balances, interest, and settlement in long-lived COBOL programs. Replacing them means rediscovering those rules and proving that the new system behaves the same way. That makes a full rewrite a large operational and verification project.
BankLang takes a narrower approach for new programs. Developers write BankTS, while the reviewed and deployed artifact remains COBOL. The source language adds types and compile-time checks for a selected banking surface; it does not migrate an existing estate automatically.
Generated arithmetic is explicit
Money fields use decimal types with declared precision and scale. When a result needs rounding, BankTS requires a named mode and the emitter writes the corresponding arithmetic into COBOL.
// Interest is balance * rate, rounded to the currency scale with banker's
// rounding. The rounding mode is required by the compiler, not optional.
function accrue(balance: MoneyBDT, rate: Rate): MoneyBDT {
return round(balance * rate, "HALF_EVEN");
}
ACCRUE.
*> HALF_EVEN is generated. COBOL has
*> only ROUNDED, which is HALF_UP.
COMPUTE BANK-RND-1-VALUE = (ACCRUE-P1 * ACCRUE-P2)
ON SIZE ERROR
DISPLAY "ARITHMETIC OVERFLOW ACCRUE-RESULT" UPON
SYSOUT
MOVE 12 TO BANK-RETURN-CODE
MOVE "ARITHMETIC-OVERFLOW" TO BANK-FAILURE-CODE
GO TO ACCRUE-EXIT
END-COMPUTE
COMPUTE BANK-RND-1-EXCESS =
(ACCRUE-P1 * ACCRUE-P2) - BANK-RND-1-VALUE
COMPUTE BANK-RND-1-STEP = 0.01
IF BANK-RND-1-EXCESS < 0
COMPUTE BANK-RND-1-STEP = -0.01
END-IF
COMPUTE BANK-RND-1-UNITS = BANK-RND-1-VALUE * 100
EVALUATE TRUE
WHEN FUNCTION ABS (BANK-RND-1-EXCESS) > 0.005
ADD BANK-RND-1-STEP TO BANK-RND-1-VALUE
WHEN FUNCTION ABS (BANK-RND-1-EXCESS) = 0.005
IF FUNCTION MOD (BANK-RND-1-UNITS, 2) = 1
ADD BANK-RND-1-STEP TO BANK-RND-1-VALUE
END-IF
END-EVALUATE
MOVE BANK-RND-1-VALUE TO ACCRUE-RESULT
CONTINUE.
Enterprise COBOL's ROUNDED phrase uses half-up rounding.
Banker's rounding, where a tie goes to the even digit, needs explicit
arithmetic in the generated program. The test suite compares the
generated result with exact rational arithmetic across boundary cases,
products, quotients, and all seven supported modes.
Scale is part of the type. A balance times a rate can have more
fractional places than a money field, so round makes the
conversion explicit instead of discarding digits silently.
The generated ON SIZE ERROR branch handles a result that
does not fit in its receiving field. It reports the overflow and
leaves the step with a failure code instead of allowing a truncated
value to continue.
Work with existing layouts
BankLang is not a COBOL-to-BankTS converter. It can import selected copybook and DCLGEN layouts, and the repository includes worked conversions to show the boundary. This example is a sequential master update that reads transactions, applies them, and writes balances and rejects.
FILE-CONTROL.
SELECT TRANS-FILE ASSIGN TO TRANSIN.
SELECT MASTER-IN ASSIGN TO MASTIN.
SELECT MASTER-OUT ASSIGN TO MASTOUT.
SELECT REJECT-FILE ASSIGN TO REJECTS.
* 41 lines: the FDs and WORKING-STORAGE
0000-MAIN.
OPEN INPUT TRANS-FILE
MASTER-IN
OUTPUT MASTER-OUT
REJECT-FILE.
file transFile sequential input record TransRecord status transStatus;
file masterIn sequential input record MasterRecord status masterInStatus;
file masterOut sequential output record MasterRecord status masterOutStatus;
file rejectFile sequential output record RejectRecord status rejectStatus;
on error transFile {
log "TRANSIN FAILED, STATUS ", transStatus;
returnCode = 12;
}
The original opens four files but declares no FILE STATUS
fields. A missing input can therefore look like a clean run after the
first read reaches AT END.
The BankTS version declares the same layouts and gives each file a status field with an error handler. In this example, the generated step distinguishes a file failure from a run that had no records to apply, so a following JCL step can make a different decision.
Scope and limits
- IBM validation has not happened. Local examples use GnuCOBOL under an IBM-shaped profile and GnuCOBOL's default dialect. The known and suspected differences are documented in the divergence list.
- It has no production integration. The repository contains no live ledger, bank deployment, or pilot.
- The language covers a narrow subset. It generates selected batch and online programs using QSAM, VSAM, embedded SQL, CICS, IMS, MQ, and Report Writer constructs. The ledger, audit, and subsystem interfaces used in local tests are repository-defined interfaces, not IBM or bank services.
- 23 of 25 examples are executed, not just compiled against the repository's reference runtime. 3 of those have hand-written expected balances; the remainder are run by GnuCOBOL and an independent interpreter and compared. Two examples are compile-only because their generated constructs have no local execution path.
Choose a starting point
- Getting started walks from cloning the repository to compiling a sample and reading its generated COBOL.
- For mainframe engineers explains the generated program, its data layout, control flow, file handling, and JCL.
- For evaluators explains the evidence, the limits, and a practical way to test the tool with your own copybooks and compiler.