Skip to main content

COBOL parser for Python, porting the proleap-cobol-parser preprocessor + Cobol.g4 pipeline to a startRule AST

Project description

cobol-py

PyPI Python Versions License: MIT CI

A COBOL parser for Python that ports the parsing pipeline of proleap-cobol-parser (Java) to Python, built on ANTLR4.

cobol-py reproduces proleap's two-stage design:

  1. Preprocessor (CobolPreprocessor.g4) — a fixed-format line model plus a document walker that expands COPY copybooks, applies REPLACE pseudo-text, and extracts EXEC CICS / EXEC SQL / EXEC SQLIMS blocks into tagged lines.
  2. Main grammar (Cobol.g4) — parses the preprocessed text into a startRule AST.

The deliverable is the full pipeline: raw source → preprocessed text → AST → ASG (Abstract Semantic Graph). The ASG is a typed, registry-backed object model of the program — divisions, sections, data descriptions, paragraphs, statements, and resolved calls — built on top of the startRule AST.

Install

pip install cobol-py             # from PyPI

Or with uv for development:

uv sync                          # create .venv and install antlr4-python3-runtime + pytest
uv pip install -e .              # or, install the package into another project

Runtime dependency: antlr4-python3-runtime 4.13.2.

Quick start

from cobol_py import CobolParserRunner, CobolParserParams, CobolSourceFormatEnum

src = open("examples/example.cbl").read()

# A source format is required — it drives the fixed-format line model.
params = CobolParserParams(format=CobolSourceFormatEnum.FIXED)

ast = CobolParserRunner().parse(src, params)
print(ast.toStringTree(recog=ast.parser))   # noqa: the generated StartRuleContext

Or parse straight from a file (the file's directory becomes the default copybook search path):

from cobol_py import CobolParserRunner, CobolSourceFormatEnum

ast = CobolParserRunner().parse_file(
    "examples/example.cbl", CobolSourceFormatEnum.FIXED
)

Source formats

COBOL source is column-oriented. CobolSourceFormatEnum selects the layout the line reader expects:

Format Sequence (cols) Indicator Area A Area B
FIXED 1–6 7 8–12 13–72
VARIABLE 1–6 7 8–12 8–end
TANDEM 1 2–5 2–end

FIXED is the standard ANSI / IBM reference. Unlike FIXED, VARIABLE does not drop a comment area past column 72, so long AREA B content survives intact.

Preprocessor constructs

CobolPreprocessorImpl performs the textual transforms that a pure grammar cannot:

  • COPY — inlines a copybook resolved against params.copy_book_directories / copy_book_files (by cobol-word, literal, or filename).
  • REPLACE ==a== BY ==b== — applies pseudo-text replacement to the surrounding source.
  • EXEC SQL|CICS|SQLIMS … END-EXEC — tags each line (*>EXECSQL, …) and emits a } terminator so the main grammar's lexer can recognise the block.
from cobol_py import CobolPreprocessorImpl, CobolParserParams, CobolSourceFormatEnum

params = CobolParserParams(format=CobolSourceFormatEnum.FIXED)
preprocessed = CobolPreprocessorImpl().process(src, params)

Walk the AST

The generated listener / visitor let you traverse the startRule tree:

from cobol_py import CobolListener

class MyListener(CobolListener):
    def enterMoveStatement(self, ctx):        # noqa: N802
        print("found MOVE:", ctx.getText())

ASG (semantic layer)

CobolParserRunner().analyze(...) runs the preprocessor + AST parse, then the ASG visitor passes, and returns a typed Program model — the same shape as proleap's ASG, ported to idiomatic Python (snake_case attrs, one class per Java interface+Impl pair, direct attributes instead of getters).

from cobol_py import CobolParserRunner, CobolParserParams, CobolSourceFormatEnum

params = CobolParserParams(format=CobolSourceFormatEnum.FIXED)
program = CobolParserRunner().analyze(src, params)

# Procedure-division statements, fully typed.
proc = program.compilation_unit.program_unit.procedure_division
for stmt in proc.root_paragraphs[0].statements:
    print(type(stmt).__name__, stmt.statement_type)

Coverage (mirrors proleap's metamodel):

  • StructureCompilationUnit / ProgramUnit / four divisions; identification, environment (FILE-CONTROL SELECTs), data (working-storage / linkage / local-storage / file-section FDs with the 01-level hierarchy), procedure (sections / paragraphs / root-paragraphs).
  • Statements — all 50 verbs produce a typed node. File I/O (OPEN/CLOSE/ READ/WRITE/REWRITE/DELETE/START), arithmetic (ADD/SUBTRACT/MULTIPLY/DIVIDE/ COMPUTE), control (IF/PERFORM/EVALUATE/GO TO/SEARCH), MOVE/CALL/SET/STRING/ UNSTRING/INSPECT/INITIALIZE/DISPLAY/ACCEPT/STOP, and the C2 tail (SORT/MERGE/ ALTER/CANCEL/RETURN/RELEASE + EXEC CICS/SQL/SQLIMS and the rarer verbs).
  • Phrase scopes — every statement-owning phrase (AT END, INVALID KEY, ON SIZE ERROR, ON EXCEPTION, ON OVERFLOW, AT END-OF-PAGE, IF THEN/ELSE, PERFORM inline body, SEARCH/EVALUATE WHEN) owns its nested statements, so they nest under their parent instead of leaking to the paragraph — matching proleap, where these phrases implement Scope.
  • Calls — references resolve to ProcedureCall / SectionCall / DataDescriptionEntryCall / FileControlEntryCall (with back-links on the declarations); unresolved names fall back to UndefinedCall.

Clause detail still deferred on a few verbs (full arithmetic/condition operand decomposition, OCCURS/REDEFINES/VALUE/USAGE data clauses, SEARCH/EVALUATE WHEN condition decomposition); every verb already produces its typed node.

Testing

uv run pytest                      # full suite (~197 tests)
COBOL_PY_NIST_FULL=1 uv run pytest tests/test_nist.py   # full NIST suite (slow)
Module What it checks
tests/test_phase1_leaf.py leaf types, params, format enum
tests/test_phase2_line.py line sub-pipeline (reader / indicator / writer)
tests/test_parse.py full pipeline on small FIXED/TANDEM/VARIABLE fixtures
tests/test_ast_tree.py parse-tree golden comparison vs proleap's .cbl.tree files
tests/test_nist.py NIST COBOL-85 conformance (a stride by default; full is opt-in)
tests/test_asg/*.py ASG layer: foundation, data/procedure structure, statements, file verbs, call resolution, phrase-scope nesting

tests/test_ast_tree.py is the strongest fidelity check: it parses every program under testdata/io/proleap/cobol/ast and asserts the cleaned toStringTree output matches proleap's committed .cbl.tree golden byte-for-byte.

tests/test_nist.py mirrors proleap's gov/nist/*Test.java — it runs each NIST program through parse_file(file, FIXED) and asserts a clean parse. The Python ANTLR runtime simulates the ATN in pure Python, so prediction on this grammar is ~2-4 s per medium NIST file (vs. milliseconds on the JVM); the full 459-file suite is therefore opt-in via COBOL_PY_NIST_FULL=1, while a representative stride runs in the default suite.

Regenerate the parser

The generated modules under src/cobol_py/ are committed, but you can regenerate them from Cobol.g4 / CobolPreprocessor.g4 after editing a grammar:

uv run python scripts/generate_parser.py
uv run pytest                 # verify

The generator drives the official ANTLR4 jar through java. The jar version is derived from the installed antlr4-python3-runtime (so the emitted ATN and the runtime always agree); the jar is cached under .tools/ and downloaded automatically if missing. A JDK must be on PATH.

Project layout

Cobol.g4                       # main COBOL grammar (source of truth)
CobolPreprocessor.g4           # preprocessor grammar (COPY/REPLACE/EXEC/opts)
pyproject.toml                 # uv / hatchling package definition
scripts/generate_parser.py     # regenerate src/cobol_py/*.py from the grammars
src/cobol_py/                  # importable package
  preprocessor/                # line reader/writer, indicator processor,
                               # comment marker, document parser, copybook finders
  asg/                         # Abstract Semantic Graph: typed program model
                               # (divisions, statements, calls) over the AST
  runner.py                    # CobolParserRunner — the public entry point
  Cobol{Lexer,Parser,Listener,Visitor}.py
  CobolPreprocessor{Lexer,Parser,Listener,Visitor}.py
tests/
  fixtures/                    # .cbl/.CPY programs: FIXED/TANDEM/VARIABLE +
                               # COPY + REPLACE + EXEC SQL/CICS
  test_phase1_leaf.py          # leaf types & utils
  test_phase2_line.py          # line sub-pipeline
  test_parse.py                # full-pipeline integration tests
examples/example.cbl           # self-contained FIXED example

Design notes & limitations

  • The proleap whitespace model is preserved: NEWLINE and whitespace live on the HIDDEN channel and statements terminate on DOT_FS.
  • EXEC CICS/SQL/SQLIMS bodies are parsed inline as a free-form token run up to END-EXEC, gated by the preprocessor-inserted *>EXEC* tags and the } terminator.
  • compilerOptions is recognised tolerantly (the option stream is captured up to IDENTIFICATION).
  • Parse speed: the runner uses ANTLR's SLL→LL two-stage strategy. SLL is exact and fast for most input; when it bails (ambiguity or a real syntax error), the runner falls back to full LL with the error listeners re-armed, so the parse tree and error behaviour are identical to a plain LL parse. This is a Python-specific optimisation — proleap on the JVM does not need it.
  • The ASG is a port of proleap's semantic layer (idiomatic Python: one class per Java interface+Impl pair, snake_case, direct attrs). A handful of clause details are still deferred (see the ASG section above); the parsing pipeline is complete and every verb already produces its typed ASG node.

License

MIT.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

cobol_py-0.1.0.tar.gz (3.8 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

cobol_py-0.1.0-py3-none-any.whl (431.7 kB view details)

Uploaded Python 3

File details

Details for the file cobol_py-0.1.0.tar.gz.

File metadata

  • Download URL: cobol_py-0.1.0.tar.gz
  • Upload date:
  • Size: 3.8 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for cobol_py-0.1.0.tar.gz
Algorithm Hash digest
SHA256 139b56f2f0f2d59e47a3618829642b29135bf2058175b47239bd524af7aebe50
MD5 3e3a02a07fa1f948b73e807c5f5e5ce4
BLAKE2b-256 c9846f1bd7bd632c1f5f97cb39235322409d4a87382240cba75ccc0e7f612928

See more details on using hashes here.

Provenance

The following attestation bundles were made for cobol_py-0.1.0.tar.gz:

Publisher: release.yml on aixfoundry/cobol-py

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file cobol_py-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: cobol_py-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 431.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for cobol_py-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 1b61d3a4890149b43fff8230f2b47ad3918972444e0ce153f519aec087d26333
MD5 8a6a865fd20f33ef48b9b5e8b2eac3f2
BLAKE2b-256 f8822dd57018a426670d9da2f0dd57be7baa8ef35dc72b537968f2946c15149b

See more details on using hashes here.

Provenance

The following attestation bundles were made for cobol_py-0.1.0-py3-none-any.whl:

Publisher: release.yml on aixfoundry/cobol-py

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page