BankLang

Getting started

Thirty minutes from clone to reading generated COBOL and understanding why it looks the way it does.

Requirements

Node.js 24 or later, and pnpm 11.7.0. GnuCOBOL is optional — everything except the compile and execute lanes works without it.

git clone <this repository>
cd banklang
pnpm install

Installing GnuCOBOL is worth the two minutes if you want the whole picture:

brew install gnu-cobol        # macOS, currently 3.2.0
apt-get install gnucobol      # Debian and Ubuntu, currently 3.1.2

Check what you got. cobc --version should say 3.2. Ubuntu's package is still 3.1.2, which is missing the JSON-STATUS special register that Enterprise COBOL has and this compiler emits, so six test files fail on COBOL that is correct for the target. CI builds 3.2 from source for exactly that reason; if your distribution ships 3.1, either build 3.2 or expect those lanes to fail.


Five minutes: see what it does

pnpm playground:dev

The entire compiler runs in your browser: there is no compile server and nothing you write is sent anywhere. Click any line of BankTS and the COBOL it produced lights up, and the other way round. That cross-link is read straight from the emitted source map, so traceability is something you click rather than something the documentation claims.


Ten minutes: compile something

pnpm bankc build examples/account-file-batch

That writes dist/:

dist/cobol/ACCOUNTF.cbl      the program
dist/copybooks/ACCOUNTR.cpy  a copybook per record
dist/jcl/ACCOUNTF.jcl        the job that builds and runs it
dist/maps/source-map.json    every module, record, field, function, transaction
dist/audit/                  diagnostics, decimal analysis, layout report

Read dist/cobol/ACCOUNTF.cbl from the top. The prologue tells you what the program is, how it is entered, which datasets it touches under which DD names, what modules it calls and what each return code means.

Then read dist/jcl/ACCOUNTF.jcl. It is meant to be submittable.

If you are a mainframe engineer, go to for-mainframe-engineers.md now. It reads that program with you, construct by construct, and every question you are about to have is answered there.


Fifteen minutes: break something on purpose

The compiler's whole claim is about what it refuses. Open examples/account-posting/src/main.bank.ts and try each of these:

Post a debit with no matching credit.

  debit(request.debitAccount, request.amount);

bankc check reports BANK-LED-001: the transaction does not balance.

Divide without saying how to round.

  let share: MoneyBDT = request.amount / 3.00;

BANK-DEC-003. The answer depends on the rounding mode, so somebody has to say which. divide(request.amount, 3.00, "HALF_EVEN") is accepted.

Add two different currencies.

type MoneyUSD = currency<"USD", 18, 2>;

BANK-DEC-005. There is no conversion operator, because a rate is a number somebody has to supply and a compiler that invented one would be inventing an exchange rate.

Write an audit event with no idempotency key.

BANK-TXN-001. An audit trail nobody can deduplicate is one nobody can reconcile.

pnpm bankc explain BANK-LED-001 prints the catalogue entry for any of them, and diagnostics.md is the whole list.


Thirty minutes: run the checks

pnpm typecheck          # TypeScript
pnpm test               # everything, including programs that are executed
pnpm test:gnucobol      # every example, compiled under an IBM-shaped dialect
pnpm lint:conformance   # every artifact, against the target's rules
pnpm lint:zos           # every artifact, against what z/OS will do with it

pnpm test:gnucobol is the one worth understanding. It compiles each example twice: once under tools/banklang-ibm.conf, which is shaped to Enterprise COBOL 6.4, and once under GnuCOBOL's default dialect, which is a superset of every COBOL it knows. A difference between the two is treated as a finding rather than as noise, because the default dialect accepting something the target rejects is exactly how a 31-character data name and a rounding phrase that does not exist both shipped.

pnpm lint:conformance reads every emitted artifact, every checked-in fixture and every evidence bundle as text and asserts the target's rules — 30-character words, column 72, ARITH(COMPAT)'s eighteen digits, dataset qualifiers at eight, and that every word in the program is one Enterprise COBOL has heard of.

pnpm lint:zos reads the same artifacts and asks the other question: not whether the toolchain will accept the program, but whether z/OS will do what it says. The 2026-08-07 audit found two shipped programs that compiled, bound and verified, and then aborted on their second MQCONN or returned the caller's own request unchanged — neither of which is a syntax error, a style violation or a wrong number. Both are in target-conformance.md.


Start a project

pnpm bankc init my-service
pnpm bankc check my-service

bankc init produces a project that compiles first try. banklang.json beside src/ holds the settings — see toolchain.md.


Bring your own records

If you have a copybook:

pnpm bankc copybook import path/to/ACCTMAST.cpy

It prints a BankTS record. Before printing anything it emits that record back to a copybook and compares the two field by field — same names, same order, same offsets, same lengths, same pictures. If they differ, nothing is written and the reason is named: a field read at the wrong length moves every field after it.

If you have a DCLGEN member:

pnpm bankc dclgen import path/to/ACCOUNT.cpy

Same idea, and it gets nullability from the catalogue — a column with no NOT NULL becomes nullable<T>, which makes the compiler require a presence check before the program reads it.


Where to go next

You are Read
A mainframe engineer for-mainframe-engineers.md
Reviewing the generated code generated-code-standards.md
Asking what it is checked against target-conformance.md, verification.md
Asking where the money could be wrong numeric-model.md
Asking what happens on a bad night error-handling.md
Asking about the job jcl-model.md
Asking about PII security-and-data.md
Asking why not something else comparison.md
Learning the language language-reference.md
Asking what it does not do divergences.md

Read this page as Markdown on GitHub →