A JVM library and a command-line tool
Kotlin on the JVM, on Kolasu — the JVM implementation of Starlasu. Use it as a library from Java or Kotlin, or run the command-line tool over a directory and collect serialized trees.
The public conformance suite is the bar we publish, because you can download it and count it yourself. What earns the license is the machinery underneath: area-aware lexing, recursive copybook expansion with pseudo-text replacement, and a position on every node that still points at the original source line afterwards.
Most COBOL front ends parse the tutorial and fail on the estate. The difference is almost never the grammar. It is the seven columns before the code starts, and the copybook that arrives with a different prefix each time it is included.
Most parser pages ask you to believe a percentage. This one points at a public, vendor-neutral conformance suite that anybody can download, and tells you the shape of what is in it. The suite is part of our regression corpus.
The NIST COBOL-85 validation suite, as it sits in the repository
# the suite is distributed as one file
nist-testsuite/newcob.val
# uncompressed, it is:
459 .cob COBOL-85 programs
52 .coblib copy-library members
511 files total
# the CCVS85 families it covers
NC nucleus SQ sequential I/O
IC inter-program call RL relative I/O
IF conditionals IX indexed I/O
ST sort-merge RW report writer
SM source manipulation CM communication
DB debug SG segmentation
OB obsolete features EX copy librarynewcob.val from NIST, run the uncompressor, and count the .cob files. The suite is more than 345,000 lines of COBOL, and it is joined in the regression run by 12 open-source COBOL codebases and 681 automated tests. The honest caveat, stated here rather than in a footnote: CCVS85 tests the COBOL-85 standard. It does not test IBM Enterprise COBOL extensions, it does not test CICS, and it does not test Db2.A COBOL line is not a line of code. It is six columns of sequence number, one indicator column, an Area A, an Area B and eight columns the compiler ignores — and what a character means depends on which of those it lands in. A period in column 7 is not a period.
The lexer is area-aware in the strict sense: the rules for the period, the parentheses, the string literal and the end-of-line comment are guarded by predicates that ask which area the lexer is currently in. Line identifiers, the indicator column, whitespace and comments each go to their own token channel, so they are available to a tool that wants them and invisible to a grammar that does not.
Then there is the preprocessor, which is where COBOL front ends really die. COPY member REPLACING ==:PFX:== BY ==CUST== is pseudo-text substitution at inclusion time, including partial-word replacement — so the same copybook included twice with different prefixes produces two different record layouts. A copybook is not a compilation unit either: it can be a fragment of a data description, a fragment of the Procedure Division, or an unbalanced fragment of neither.
Copybooks are expanded at the token level through a resolver you supply, because only you know your SYSLIB concatenation or your library list. REPLACE, REPLACE LEADING, REPLACE TRAILING and REPLACE OFF are grammar rules. EXEC SQL INCLUDE is a second, independent include mechanism with its own resolver.
And underneath all of it, a mapping from every column of the joined, area-stripped “clean” text back to an original line and column. That is what lets a diagnostic point at the real source line after a literal has been continued across two records and a copybook has been expanded three levels deep. It is the single most useful thing this engine does and it is invisible until you need it.
Input — fixed-format COBOL, with the ruler
----+----1----+----2----+----3----+----4---
IDENTIFICATION DIVISION.
PROGRAM-ID. CUSTRPT.
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
SPECIAL-NAMES.
DECIMAL-POINT IS COMMA.
DATA DIVISION.
FILE SECTION.
FD CUST-FILE.
01 CUST-REC.
COPY CUSTREC REPLACING ==:PFX:== BY ==CUST==.
WORKING-STORAGE SECTION.
01 WS-STATUS PIC XX VALUE SPACES.
88 WS-EOF VALUE '10'.
01 WS-TOTAL PIC S9(9)V99 COMP-3 VALUE ZERO.
01 WS-EDITED PIC ---.---.--9,99.
PROCEDURE DIVISION.
MAIN-PARA.
OPEN INPUT CUST-FILE
PERFORM UNTIL WS-EOF
READ CUST-FILE
AT END SET WS-EOF TO TRUE
NOT AT END
ADD CUST-BALANCE TO WS-TOTAL
END-READ
END-PERFORM
STOP RUN.CUSTREC contains 05 :PFX:-ID PIC X(8). and two siblings. It is a fragment, not a program, and it only becomes a record layout after :PFX: becomes CUST. Note also that DECIMAL-POINT IS COMMA has already changed what , and . mean by the time the editing picture on the marked line is read.Output — parse tree, by grammar rule
compilationUnit
└── program CUSTRPT
├── ⋯ environment division
│ └── decimalPointIsComma = true
├── ⋯ data division
│ ├── dataDescriptionEntry 01 CUST-REC, line 11
│ │ ├── dataDescriptionEntry 05 CUST-ID, CUSTREC line 1
│ │ ├── dataDescriptionEntry 05 CUST-NAME, CUSTREC line 2
│ │ └── dataDescriptionEntry 05 CUST-BALANCE, CUSTREC line 3
│ └── dataDescriptionEntry 88 WS-EOF, line 15
└── ⋯ procedure divisionCUST-REC did not exist in the program file: they came from the copybook, arrived with CUST substituted for :PFX:, and still carry a position that points at the copybook line they came from. That provenance is what makes an impact analysis defensible.Not the hardest to parse — the hardest to model. Each of these is a place where treating COBOL as a normal programming language produces an answer that is quietly wrong.
REDEFINES gives the same bytes a second, incompatible interpretation. OCCURS DEPENDING ON makes the layout dynamic, so the offset of a field depends on a value at run time. Level 66 RENAMES regroups across boundaries. A data migration that treats an 01 as a struct of fields is already wrong on line one.
01 TXN-REC.
05 TXN-COUNT PIC S9(4) COMP.
05 TXN-LINE OCCURS 1 TO 99 TIMES
DEPENDING ON TXN-COUNT.
10 TXN-AMT PIC S9(7)V99 COMP-3.
05 TXN-RAW REDEFINES TXN-LINE PIC X(900).
An editing picture is an output format, a sign convention and a storage decision at once. COMP-3 is packed decimal; S9(7)V99 has an implied decimal point that exists nowhere in the bytes. Modeled as structure rather than stored as a string, because a migration has to reproduce the arithmetic, not the spelling.
01 WS-EDITED PIC ---.---.--9,99.
01 WS-PACKED PIC S9(7)V99 COMP-3.
01 WS-BLANK PIC 9(4) BLANK WHEN ZERO.
PERFORM A THRU Z executes every paragraph between two labels in source order, including the ones nobody meant to include. Paragraphs fall through. ALTER rewrites the target of a GO TO at run time. Building a correct control-flow graph from COBOL is materially harder than from a block-structured language, and it is the analysis that tells you which third of a 200,000-line program can be deleted.
PERFORM VALIDATE-PARA THRU VALIDATE-EXIT
ALTER SWITCH-PARA TO PROCEED TO NIGHT-PARA
GO TO SWITCH-PARA.
EXEC SQL and EXEC CICS are first-class statements in the grammar. The SQL is captured as a block with its position, in the Data Division as well as the Procedure Division. CICS commands are recognized with their options tokenized. Neither is parsed here — the SQL is a job for a SQL engine, and that is a feature rather than an omission. See the band below.
EXEC SQL
SELECT BALANCE INTO :CUST-BALANCE
FROM CUSTOMER WHERE ID = :CUST-ID
END-EXEC
EXEC CICS SEND MAP('CUSTM') ERASE END-EXEC
Both of those EXEC examples are IBM Enterprise COBOL on z/OS — the mainframe. IBM i is a midrange platform and has no CICS at all. We keep the two apart on purpose, because merging them is how a re-platforming estimate ends up wrong by a quarter.
| Construct | How it is handled | Status |
|---|---|---|
| COBOL-85 standard | the whole NIST CCVS85 suite in the regression run | Supported — the stated core |
| Fixed format, 80-column reference record | FIXED_FORMAT, area predicates, indicator column |
Supported |
| HP Tandem format | HP_TANDEM_FORMAT |
Supported |
COPY, and COPY … REPLACING with pseudo-text |
token-level expansion through a resolver you supply | Supported |
REPLACE, REPLACE LEADING, REPLACE TRAILING, REPLACE OFF |
replaceStatement |
Supported |
| Continuation lines, including inside a literal | joined, with a reverse map to the original line and column | Supported |
DECIMAL-POINT IS COMMA |
a parser setting, because it changes lexing | Supported |
| Nested programs and copybook fragments | compilationUnit, program, endProgram |
Supported — a unit may start at any division |
| ACU COBOL extensions | dialect work outside the core | Selected extensions supported |
| Micro Focus RM COBOL and Visual COBOL | dialect work outside the core | Partial — tell us your compiler and we will be specific |
EXEC SQL and EXEC CICS blocks |
execSqlStatement, execCicsStatement, execSqlInclude |
Recognized and positioned; contents preserved verbatim |
| Cross-file symbol resolution | — | Not part of this engine — and we will not pretend otherwise |
| Free-format COBOL | — | Not supported — the engine reads fixed and HP Tandem |
| COBOL 2002 / 2014 / 2023 features | object orientation, national data, >> directives |
Not claimed |
The default copybook resolver does nothing, on purpose. Only you know your SYSLIB concatenation, your library list, and which of the four copies of CUSTREC on your system is the one this program compiled against.
This is the part most COBOL parser pages do not mention, and it is the part that costs a week if you discover it late. There are two pluggable resolvers: one for COPY, one for EXEC SQL INCLUDE. You implement the lookup; the engine does the token splicing, applies the pseudo-text replacements, and keeps the position mapping intact through every level of expansion.
Naming it is more useful than a page that promises the integration will be effortless and leaves you to discover otherwise. It is a small, well-defined interface, it takes an afternoon, and it is the only place your environment has to meet the engine. Everything after it — the tree, the positions, the diagnostics, the serialization — is ours.
And in practice you will not have all the copybooks. Customers send forty thousand programs and thirty thousand of thirty-five thousand copybooks. Error tolerance is not a nicety here: malformed or unresolvable input returns a partial tree plus positioned issues, so a batch finishes and tells you exactly what it could not find.
The interface you implement
// your copybook search order
interface CopyResolver {
fun resolveCopyImport(
importName: String,
replacements: List<Replacement>
): List<Token>
}
// EXEC SQL INCLUDE is separate
interface ExecSqlResolver {
fun resolveSqlInclude(
importName: String
): List<Token>
}“Which paragraph writes to which table, through which host variable” is one question. Answering it with two unrelated parsers means writing the join yourself, twice, and then maintaining it.
This engine isolates and positions the EXEC SQL block. A Strumenta SQL engine parses what is inside it. Both produce nodes in the same model — so one traversal crosses the boundary, and a host variable in the SQL and its 01 declaration in Working-Storage are two nodes in one tree rather than two strings in two reports.
Every Strumenta engine is built on Starlasu, so a COBOL tree, a SQL tree and an RPG tree have the same shape, the same traversal model and the same API. That is what you cannot assemble by gluing unrelated open-source parsers together: separate models, separate idioms and separate sets of edge cases to reconcile before you have written a line of analysis.
Every engine also supports LionWeb, so the models interchange with LionWeb-compliant tooling instead of being locked inside one process — and every engine is available through bindings from Java, Kotlin, Python, TypeScript and C#. The host application picks the language; the parser does not.
Engines in the same estate
SQL engine →
What is inside the EXEC SQL block.
PL/SQL engine →
The packages the batch calls, if the database is Oracle.
RPG & DDS engine →
For estates that run z/OS and IBM i side by side.
The whole catalog →
15+ engines, one AST framework underneath.
A library, a command-line tool, the documents that explain the tree, and a person to write to. No mainframe or IBM i connection is required: you analyze exported sources offline.
Kotlin on the JVM, on Kolasu — the JVM implementation of Starlasu. Use it as a library from Java or Kotlin, or run the command-line tool over a directory and collect serialized trees.
Not a renamed parse tree: parse-tree-to-AST mapping is a separate layer, so the model is deliberate. Every node and every issue carries a position on the original source file, after continuation joining and copybook expansion. Serialize to JSON or XML; interchange through LionWeb.
You implement the copybook and EXEC SQL INCLUDE lookups for your environment; we do everything downstream. A printer that renders the AST back to COBOL text is also part of the product — useful for refactoring pipelines, and worth testing on your own sources before you build a plan around round-tripping.
Standard for use inside your own organization, Distribution if the engine ships inside a product you sell, Service if it runs behind a service you operate. A license file is registered once per process and refreshed from our license service, on the same terms as every other engine in the catalog. Support is part of the license, and we also do training, architectural design, implementation and coaching around it.
Week one is writing forty lines of copybook resolver and then running the engine over your own programs.
There is nothing to install on the mainframe and nothing to provision. Before any of that, paste a program into the Strumenta Playground — pick COBOL — and watch a real AST come back from the same engine. If the integration needs help, that is what the support in the license is for; if it needs more than help, we do that work too. Write to products@strumenta.com.
COBOL is the one language on this catalog where the open-source options are genuinely good, and we are not going to build a straw man. ProLeap, by Ulrich Wolffgang and contributors, is MIT-licensed, ANTLR4-based, has a real preprocessor that executes COPY and REPLACE, extracts EXEC SQL and EXEC CICS as text, and passes the NIST suite. The antlr/grammars-v4 COBOL-85 grammar is there too. If you are writing a one-off analysis over clean COBOL-85 and you enjoy this kind of work, start there and we will not be offended.
Here is where the calculation changes. Dialects are where COBOL projects die, and dialect work is not grammar work — it is a long tail of vendor extensions found one customer at a time. Our core is COBOL-85, with selected ACU COBOL extensions and partial Micro Focus RM COBOL and Visual COBOL support, and the offer we actually make is to tell you what we cover for your compiler and extend it where we do not. An open-source project cannot make that offer, because nobody is accountable for it.
Second, the estate is not only COBOL. The value of this engine is disproportionately that its tree is the same shape as the SQL tree, the PL/SQL tree and the RPG tree, so one tool answers questions that cross the boundary. Two unrelated open-source parsers give you two models and a join you write and maintain yourself.
Third, interchange. LionWeb export and bindings from Java, Kotlin, Python, TypeScript and C# mean the model leaves the JVM. If the tool consuming your COBOL model is written in Python or C#, that is not a serialization exercise you have to design.
Fourth, someone to call. That sounds like a brochure line until the week you have a program that will not parse, a deadline, and an issue tracker whose last reply was in 2021. Support is included in the license, and the people answering are the people who wrote the lexer predicates.
The method behind our estimates has a name: read the Chisel Method for how we make parser work estimable, or Migration Services if what you actually need is the migration rather than the engine.
Six things we would rather tell you now than have you find in week three.
COPY statements are recognized but not expanded, and your Data Division is a stub. It is a small interface, but it is an integration task and it belongs in your plan.COPY … REPLACING sites are there?EXEC CICS? EXEC SQL? EXEC DLI? Do you need those parsed, or isolated and inventoried?DECIMAL-POINT IS COMMA, a non-default CURRENCY SIGN, a non-standard ALPHABET?ALTER, PERFORM … THRU, or nested programs? These decide whether control-flow analysis is a week or a quarter.The NIST COBOL-85 validation suite, CCVS85, is distributed as a single file called newcob.val. Uncompressed it is 459 .cob programs and 52 .coblib copy-library members, covering the nucleus, sequential, relative and indexed I/O, inter-program communication, sort-merge, report writer, segmentation, debug and the obsolete-features families. The whole suite is in our regression corpus. It tests the COBOL-85 standard — not IBM extensions, not CICS, not Db2.
The engine does the expansion; you supply the lookup. Copybooks are spliced in at the token level, recursively, with COPY … REPLACING pseudo-text substitutions applied, including partial-word replacement — and every resulting node still carries a position that maps back to the original file and line. What the engine cannot know is your SYSLIB concatenation or your library list, so you implement a small resolver interface for that.
COBOL-85 is the core, and it is the standard the commercial dialects build on. Beyond it we support selected ACU COBOL extensions and partial Micro Focus RM COBOL and Visual COBOL. We do not claim universal dialect coverage, because nobody honestly can. Tell us your compiler and we will tell you what we cover for your code, and what it would take to cover the rest.
No. The supported source formats are the fixed 80-column ANSI reference record and HP Tandem format. Free format arrived with COBOL 2002 and we do not support it today. If your estate is free format, tell us before anything else, because it changes the answer to every other question.
Yes. The engine is built on Starlasu and is available through bindings from Java, Kotlin, Python, TypeScript and C#, and it supports LionWeb, so the model interchanges with LionWeb-compliant tooling. If you would rather not embed anything, the command-line tool serializes the tree to JSON or XML and any language can read that.
Enterprise COBOL on z/OS, ILE COBOL on IBM i, Micro Focus, ACU, RM, Fujitsu, Unisys, GnuCOBOL — with the version. Send that, with a handful of representative programs and the copybooks they use, and we will come back with what parses today, what would need dialect work, and how much.