Documentation menu
Package

serez-cobol

A COBOL → Serez (.sz) source-to-source translator, written entirely in pure Serez Code, with a reverse pass that takes a documented subset of Serez back to COBOL. It turns legacy COBOL into Serez scripts that run on sz — and turns Serez, whether the forward pass produced it or you wrote it yourself, back into COBOL.

New here? The step-by-step guide walks through both directions, the //@ annotations and the clean / sync options. This page is the reference.

Install

sz install serez-cobol

serez-cobol runs entirely in user-space as a command-line utility. It requires the File permission to read and write source files, and Env for system interactions.

Exact Fixed-Point Arithmetic

The core reason for serez-cobol's accuracy is Serez-Code's native dec type. COBOL arithmetic expects strict decimal fixed-point rounding (e.g., PIC 9V99 COMPUTE ... ROUNDED). Standard binary floats (f64) introduce rounding drift, which is unacceptable for financial calculations. Serez's exact decimal type mirrors this behavior faithfully:

For example, the COBOL statement:

COMPUTE WS-TAX = WS-SUBTOTAL * 0.21 ROUNDED.

Is translated directly into Serez Code as:

WS_TAX = ((WS_SUBTOTAL * 0.21m) + 0m).setScale(2, "half-up");

The arithmetic itself is exact: dec is base-10 with 28–29 digits, setScaleapplies COBOL's half-up rounding, and the language refuses to mix dec with binary floats, so drift cannot creep in silently.

Scope, honestly.The decimal arithmetic is exact and covered by the language's own test suite. What has not been done is a differential run against a production COBOL compiler (GnuCOBOL, Micro Focus, IBM Enterprise COBOL) over a corpus of real programs — so this is not a drop-in replacement for a migration toolchain, and it does not handle vendor dialects, COMP-3, EBCDIC or REDEFINES (see the unsupported list below).

Use it to read, understand and prototype legacy COBOL from a modern language. For migrating a core banking or government system, use a toolchain with decades of production history.

Usage

Once installed, the package exposes a single convert command (via sz run) that routes the direction automatically from the file extension — no sub-commands to remember:

sz run convert program.cob    // Translate COBOL (.cob/.cbl) → Serez (.sz)
sz run convert program.sz     // Reverse translate Serez (.sz) → COBOL (.cob)
sz run convert notes.txt      // Invalid extension raises an error

sz run convertforwards the file to the package's entry point (index.sz), which locates its engines next to itself. Running that entry directly also works — handy when developing inside the repo:

sz index.sz program.cob       // same routing, run from the repo

You can also invoke the individual translation engines directly:

// 1. Translate COBOL to Serez
sz cobol.sz examples/invoice.cob      // Emits examples/invoice.sz

// 2. Run the translated program
sz examples/invoice.sz

// 3. Translate Serez back to COBOL
sz serez.sz program.sz                // Emits program.cob

Options

Options are bare tokens with no leading dashes — the sz runtime rejects unknown --flags before the package runs:

OptionApplies toEffect
clean.cob inputOmit the //@ annotations from the generated .sz. The file still converts back, but through inference.
sync.sz inputWrite the reconciled annotation block back into the source .sz. Without it the source is never modified and drift is only reported.
sz run convert legacy.cob clean
sz run convert app.sz sync

Reverse pass: annotations and inference

Serez cannot express everything COBOL declares — level numbers, exact picture clauses, OCCURS, level-88 and FD/SELECT have no counterpart. The forward pass therefore embeds those declarations as //@ comment annotations: a lightweight source map, ignored when the program runs.

They are not required. The reverse engine takes one of two routes depending on the file it is given:

does the .sz have //@ annotations?
├── yes → reconciled against the code, then used
└── no  → the DATA DIVISION is inferred from the code alone

A file with some annotations gets both: what is annotated wins, what is missing is inferred. Because annotations are comments, they can drift out of step with the code, so every conversion cross-checks them against the actual let declarations — keeping what still matches, adding inferred entries for variables that were never annotated, dropping the orphans, and reporting all of it. Only sync writes the result back.

Round-trip preserves behavior, not text: ADD 1 TO X returns as COMPUTE X = X + 1, and EVALUATE as nested IF. What inference cannot recover: exact widths (a PIC X(20) VALUE "world" comes back as PIC X(5)), level hierarchies, OCCURS, level-88, edited masks and FD/SELECT.

Serez → COBOL: the supported subset

A hand-written .sz needs no annotations. Top-level statements become the main PROCEDURE DIVISION, and each fn name(params) becomes a COBOL subprogram reached with CALL … USING.

SerezCOBOL
let x = 5;01 X PIC S9(9) VALUE 5
x = a + b;COMPUTE X = A + B
x = y;MOVE Y TO X
x = "a" + y;STRING … DELIMITED BY SIZE INTO X
out expr;DISPLAY expr
if / else if / elseIF / ELSE / END-IF
while (cond)PERFORM UNTIL NOT (cond)
for (let i = 0; i < n; i = i + 1)PERFORM VARYING … FROM … BY … UNTIL
fn f(a, b) { return … }subprogram + CALL … USING
fn int f(int a, int b)typed: PIC S9(9) on the LINKAGE items
fn dec f(dec m)typed: PIC S9(9)V9… with the scale the arithmetic produces
fn string f(string s)typed: PIC X(n), sized to exactly what is returned
exit(0);STOP RUN
A computed value keeps its scale. let iva = precio * 0.21m; is declared PIC S9(9)V9999, not the PIC S9(9)a non-literal initializer would otherwise suggest — a product adds its operands' scales, a sum takes the widest. Anything narrower and COBOL would print 21 where Serez prints 21.0000.

What has no COBOL equivalent

COBOL has no dynamic arrays, dictionaries, objects, closures, exceptions, booleans or dynamic typing. Every construct below is refused with the line number and the reason, and no .cob is written at all — the translator never emits a half-program, and never emits COBOL that would not compile:

$ sz run convert app.sz
ERROR: cannot translate app.sz to COBOL — nothing was written.
  line 9: dictionaries have no COBOL equivalent

Only a subset of Serez maps onto COBOL; see the README for what translates.
SerezWhy COBOL cannot take it
[1, 2, 3] · a.push(x)A COBOL table is a fixed OCCURS, declared up front — there is no growing array.
({ k: v })No dictionary or map type exists in COBOL.
x => x * 2No closures and no first-class functions.
class · interface · enumNo user-defined types; a group item is data, not behavior.
try · throw · catchNo exceptions.
importNo module system. A CALL reaches a subprogram in the same file.
for (x in coll)Iteration over a collection is a counted PERFORM VARYING.
t[i] = xNeeds a table declared with OCCURS, which inference cannot invent.
obj.campo = xNo object to hold the field.
let ok = true; · let ok = a > b;No boolean data item — use 0/1, or a level-88 condition name on the COBOL side.
a % 3A remainder is a statement (DIVIDE … REMAINDER), not an operator.
a += 2No compound assignment — write a = a + 2;
break · continueNo mid-loop exit; the loop condition has to carry it.
do { … } while (c)PERFORM UNTIL tests before the body, never after.
match · switchEVALUATE is only produced going the other way; use if / else if.
s.toUpperCase() and any stdlib callOnly functions defined in the same file become a COBOL CALL.
out "n: " + F(x);A CALL is a statement, not an operand — assign the result first.
One statement per line. The reverse pass reads the .sz line by line, so a body squeezed onto its header line — if (c) { out 1; } — is refused with a block written on one line is not translated. The same if spread over three lines translates fine.

COBOL → Serez: supported features

CategoryDetails
Divisions & FormatSupports IDENTIFICATION, ENVIRONMENT, DATA, and PROCEDURE. Autodetects fixed-format (cols 1-72) and free-format layout. Resolves COPY copybooks.
Data TypesTranslates PIC 9(n) to int, PIC 9(n)V9(m) / S9 to dec, and PIC X(n) / A(n) to string. Supports VALUE clauses, level 77 variables, and level 88 condition names.
Group Items & TablesSupports group items (nested structural dicts) and OCCURS tables (arrays) accessed via subscripts like T(i). Supports PIC editing (Z, commas, decimals, signs, asterisks).
Control FlowTranslates IF/ELSE/END-IF, EVALUATE (including multi-subject ALSO tables), and PERFORM forms (UNTIL, TIMES, VARYING). Supports GO TO using a program-counter loop driving paragraph functions.
Arithmetic StmtsSupports COMPUTE [ROUNDED], ADD, SUBTRACT, MULTIPLY, DIVIDE (with TO, FROM, BY, INTO, GIVING, and REMAINDER) supporting multiple receivers.
SubprogramsCompiles nested units and END PROGRAM blocks. Translates CALL ... USING by reference, returning modified variables via tuples.
Strings & FilesReference modification X(p:len), STRING, UNSTRING, and INSPECT (TALLYING / REPLACING). File I/O (LINE SEQUENTIAL) with SELECT, FD, OPEN, READ, WRITE, and CLOSE.

Example Output

Here is a brief demonstration of how a simple paragraph loop translates.

Original COBOL source:

IDENTIFICATION DIVISION.
PROGRAM-ID. LOOPDEMO.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 I PIC 9(2) VALUE 0.
PROCEDURE DIVISION.
MAIN-PARA.
    PERFORM LOOP-PARA VARYING I FROM 1 BY 1 UNTIL I > 3.
    STOP RUN.
LOOP-PARA.
    DISPLAY "Iteration: " I.

Generated Serez Code:

// Generated by serez-cobol from COBOL source.
//@ IDENTIFICATION DIVISION.
//@ PROGRAM-ID. LOOPDEMO.
//@ DATA DIVISION.
//@ WORKING-STORAGE SECTION.
//@ 01 I PIC 9(2) VALUE 0.

let I = 0;

let __goto = "";

fn MAIN_PARA_() {
    I = 1;
    while (!(I > 3)) {
        LOOP_PARA_();
        I = (I + (1));
    }
    exit(0);
}

fn LOOP_PARA_() {
    out "Iteration: " + I;
}

// ── main ──
let __order = ["MAIN_PARA", "LOOP_PARA"];
let __fns = [MAIN_PARA_, LOOP_PARA_];
let __pc = 0;
while (__pc < __fns.length()) {
    __goto = "";
    __fns[__pc]();
    if (__goto == "") { __pc = __pc + 1; }
    else { /* GO TO: jump to the named paragraph */ }
}

Paragraphs become fn NAME_() and run under a program-counter driver — that is what lets GO TO, ALTER and fall-through behave exactly as they do in COBOL.

Not Yet Supported (Roadmap)

  • Inter-file calls (CALL to subprograms defined in separate source files).
  • REDEFINES clause byte-overlay logic.
  • COMP / COMP-3 packed binary numbers and EBCDIC character encodings.
  • SORT / MERGE verbs, Report Writer, and Screen Section layouts.