BankLang

Toolchain

Everything around the compiler: the CLI, the formatter, project configuration, CI integration, and editor support.

Commands

Every command bankc --help lists, and nothing else. tests/cli-toolchain.test.ts holds this table to the CLI's own inventory, so a command added without a row here fails the build.

Command Purpose
bankc init <dir> Scaffold a project that compiles on first run
bankc check <project> Diagnostics only, no artifacts
bankc build <project> COBOL, copybooks, JCL, source map, audit bundle
bankc job <directory> Several programs and their sorts, as one JCL stream
bankc emit <what> <project> One artifact kind: cobol, copybooks or jcl
bankc audit-report <project> The audit bundle on its own
bankc verify <project> Determinism and source-map coverage
bankc test <project> check, build, verify, plus local cobc validation
bankc zunit <project> zUnit test case: configuration, driver, and job
bankc layout <project> Copybook byte layout report
bankc config <project> Show the resolved configuration
bankc fmt <project> Format source; --check to verify only
bankc analyse <path>... What COBOL you already have contains, as an inventory
bankc copybook <sub> <file> Import, inspect, summarise, or diff a copybook
bankc dclgen import <file> A Db2 DCLGEN member, as BankTS
bankc explain [id] Explain a diagnostic, or list the catalogue
bankc doctor Environment report
bankc version The compiler version, and nothing else

Global options

Option Applies to Meaning
--format text|json|sarif check Diagnostic output format
--output <file> check Write the machine-readable report to a file
--out <dir> build-like Output root for generated artifacts
--watch project Rerun when a .bank.ts file changes
--debug any Print the stack when the compiler itself fails

Positional arguments may appear before or after flags.

Everything the compiler means to report — a diagnostic, a missing project, an unreadable copybook — comes back as a message and an exit code. A failure it did not anticipate is still a thrown error, and bankc prints its message on one line prefixed with bankc: and exits 1. --debug adds the stack, which is the right output for a bug in the compiler and the wrong output for a mistake in a program.

Under --watch the same failure is printed and the session continues. A watch exists to shorten the loop on exactly the errors that end a build, so ending the watch on one is the wrong response: the exit code follows the last build, and saving a fix clears it. bankc job <directory> --watch watches the directory itself, since a job's sources are one level down in each step's project.

--watch applies to the commands that read a project — check, build, job, emit, audit-report, verify, test, zunit, layout, config — and is refused with exit code 2 on the rest, which name the commands that take it. It used to be accepted everywhere, and "everywhere" included commands with no project to find: bankc explain BANK-LED-001 --watch read the diagnostic identifier as a project path and died on ENOENT … watch '/…/BANK-LED-001', and bankc doctor --watch opened a recursive watch over the working directory to rerun a command no .bank.ts can change.

bankc doctor

The first command worth running on a machine that has just cloned this, and the one to paste into a bug report. It reports the compiler's own version, the working directory and whether it holds a project, Node and the platform, the backend target, and whether GnuCOBOL is installed — by running cobc --version through the same resolution pnpm test:gnucobol uses, including GNUCOBOL_COBC_PATH, so it names the compiler that lane would actually run rather than whatever is first on the path.

BankLang doctor
bankc: 0.10.0
cwd: /home/somebody/banklang
project: src/main.bank.ts
node: v24.18.0
platform: linux
arch: x64
compiler target: ibm-enterprise-cobol-zos
local validation target: gnucobol-local
gnucobol: cobc (GnuCOBOL) 3.2.0
ibm enterprise cobol: not detected — no native IBM validation is claimed

An absent cobc is a normal state, not a failure: the line says so and says what it costs, which is that pnpm test:gnucobol and the cobc tests skip.

The last line is a constant. Nothing on a workstation can detect IBM Enterprise COBOL, and a line that is printed only when something is missing reads as an unasked question rather than an answered one — so it is always there, and it never becomes conditional on anything a machine could accidentally satisfy.

Formatting

bankc fmt prints from the AST, so the output shape is decided by one code path rather than by regex rewriting.

Two properties are tested:

The formatter refuses to rewrite a file with syntax errors, because a formatter that reshapes source it could not fully parse can destroy work.

Author blank lines inside a body are preserved (normalised to one), since grouping is meaningful. Blank lines between top-level declarations are normalised to exactly one.

pnpm fmt          # format every example
pnpm fmt:check    # verify, used by CI

Project configuration

banklang.json sits beside src/:

{
  "$schema": "https://banklang.mwhassan.com/schema/banklang.json",
  "entry": "src/main.bank.ts",
  "outDir": "dist",
  "backendProfile": "ibm-enterprise-cobol-zos",
  "formatCheck": false
}
Option Default Meaning
entry src/main.bank.ts Entry source file
outDir dist Output root
backendProfile ibm-enterprise-cobol-zos Target profile
formatCheck false Treat formatting drift as a failure
copybookMode inline Whether records are written in or COPYd
decimalPoint point DECIMAL-POINT IS COMMA when comma
currencySign $ CURRENCY SIGN IS, for an edited picture
runtimeOptions TERMTHDACT(UADUMP), TRAP(ON) Cards written to the job's CEEOPTS DD

Language Environment run-time options

A step that states none runs on whatever the installation's defaults are, which is not something a job's behaviour should depend on silently. The two defaults are about whether a bad night can be diagnosed at all: TERMTHDACT(UADUMP) asks for a readable dump when a program abends, and TRAP(ON) is what puts LE in the path to produce one.

Everything else is a site's. A long-running batch wants HEAP and STACK sized for the region and the data, and those are numbers this compiler cannot see and does not invent:

{
  "runtimeOptions": [
    "TERMTHDACT(UADUMP)",
    "TRAP(ON)",
    "HEAP(4M,1M,ANYWHERE,KEEP)",
    "STACK(1M,1M,ANYWHERE)"
  ]
}

Each entry is written on its own card, exactly as given. Nothing validates the option names: the set belongs to Language Environment and grows with it, and a compiler that refused an option it had not heard of would be one nobody could use a new release of.

Unknown keys and wrong types are reported as warnings and fall back to defaults, rather than throwing. A typo in a config file should produce a clear message, not a stack trace. Inspect the resolved values with bankc config.

CI integration

Diagnostics as pull request annotations

bankc check --format sarif emits SARIF 2.1.0, which GitHub code scanning ingests. Banking safety diagnostics then appear inline on the pull request instead of buried in a log.

- name: Produce SARIF report
  run: pnpm bankc check examples/account-posting --format sarif --output bankc.sarif

- name: Upload SARIF
  uses: github/codeql-action/upload-sarif@v3
  with:
    sarif_file: bankc.sarif

Uploading requires a public repository or GitHub Advanced Security.

Each SARIF rule carries the catalogue's explanation and remediation, so a reviewer seeing BANK-LED-001 for the first time gets the reasoning inline.

JSON

--format json is the simpler machine-readable option, enriched with the catalogue title and explanation for each diagnostic.

Both formats emit a valid report even when there are no diagnostics, so a CI step can upload unconditionally. Exit status is 1 when any diagnostic was reported and 0 otherwise, regardless of format.

Editor support

Language server

packages/language-server implements LSP over stdio with no dependencies:

Capability Behaviour
publishDiagnostics On open and change, from the real compiler
hover Diagnostic explanation, or the COBOL a line produces
documentFormatting Runs the formatter
documentSymbol Outline, with record fields nested
pnpm lsp   # start the server on stdio

Transport is separate from request handling, so the whole protocol surface is unit-tested without spawning a process.

Hover is worth calling out: on a clean line it reports which COBOL lines that source produced, reading the same source map the playground uses.

VS Code extension

packages/vscode-extension provides the language client, a TextMate grammar, and editor configuration.

pnpm --filter banklang-vscode build

The extension is not published to the marketplace. Load it with Run Extension from VS Code, or point banklang.server.path at the server entry point.

Programmatic use

import { compile } from "@banklang/compiler";
import { formatBankTs } from "@banklang/formatter";
import { explainDiagnostic } from "@banklang/diagnostics";

const result = compile(source);
if (!result.ok) {
  for (const diagnostic of result.diagnostics) {
    const doc = explainDiagnostic(diagnostic.id);
    console.error(`${diagnostic.id}: ${doc?.remediation}`);
  }
}

compile and formatBankTs perform no file system or network access, so they run in Node and in a browser. That is what lets the playground run the real compiler client-side, and a test fails if any compiler package imports a Node built-in.


Read this page as Markdown on GitHub →