Skip to content

Repository files navigation

OpenRewrite Logo

rewrite-mainframe

ci Maven Central

What is this?

This project implements a Rewrite module that provides parsers, visitors, and recipes for COBOL and related mainframe technologies. It supports parsing and transforming COBOL source code, JCL (Job Control Language), BMS map sets, DB2 DDL, DB2 bind cards, DB2 utility control cards, link-edit decks, DFSORT and IDCAMS control cards, Control-M job definitions, IMS gen source and HLASM assembler, and it reads the SAS, load module listings, CLISTs, REXX execs, run book members, C and PL/I an estate keeps beside them out of the plain text a build keeps them as.

Three words recur in the documentation and are not interchangeable. The shop is the organization: its conventions, its libraries, what its people wrote down. The estate is the mainframe world the shop runs — jobs, load modules, databases, schedules — whether or not every piece of it was checked in. The portfolio is the set of sources an analysis was actually given, which is why a member the estate has and the portfolio does not is a finding rather than a surprise.

Language Support

  • COBOL — Full parsing of COBOL-85 (IBM ANSI 85 and HP Tandem dialects), including preprocessor directives (COPY, REPLACE) and copybook resolution; programs by .cbl, .cob and .cobol, copybooks by .cpy, .copy and .dcl
  • JCL — Job Control Language parsing (.jcl, .prc and .proc files, a .txt or extensionless PDS member whose first card is JCL, and the .j2 Jinja templates an installation ships its jobs as), and the resolution a job's cards leave to other members: the procedures and INCLUDE members a step runs through, the DD overrides merged in, and the symbols a data set name is written with, which is what a step's real program and real data sets are read from. Whether all of that resolved is an answer of its own: a step says whether every name it is written with was followed, and lists the ones that were not, since a job whose procedure was never supplied is one no transformation may touch
  • BMS — CICS Basic Mapping Support map sets (.bms): the DFHMSD/DFHMDI/DFHMDF macros, and the symbolic map names they generate, which is what joins a screen field to the COBOL data item a program reads it from
  • DB2 DDL — DB2 for z/OS DDL (.ddl, .sql, and the SYSIN streams of the jobs that create a schema): every statement the SQL reference documents is modelled, so a statement that cannot be read is a syntax error rather than a node that says nothing
  • Bind cards — DSN command decks (.bnd, and an extensionless CARDLIB member whose first subcommand binds): BIND PLAN, BIND PACKAGE and REBIND with their keyword operands, read from a member of their own or from the in-stream SYSTSIN of the job that runs them
  • Link-edit decks — Binder control statements (.lnk, .lked, and an extensionless LINKLIB member whose first statement links): INCLUDE ddname(member), ENTRY, ALIAS, NAME xxx(R), ORDER, MODE, SETCODE and SETOPT, with column 72 saying which card continues which. A deck is the only place a load module's composition is written down — a step names the module, and the module names its programs here
  • Load module listings — What AMBLIST and the binder printed about a load library (.amblist, .binder, .listload, and a control card member that asks for a report), kept as plain text and read for what it names: the module and its entry point, aliases and size, the control sections it holds with their offsets and lengths, the alternate entry points within them, and the compiler each section came from. A deck says what a module was meant to hold; a listing says what it holds, including the language interface and the runtime nobody wrote a card for
  • Sort cards — DFSORT and ICETOOL control statements: SORT/MERGE FIELDS, INCLUDE/OMIT COND, INREC/OUTREC/OUTFIL, SUM, OPTION, with the control fields read as byte positions into the record, which is what joins a sort card to the copybook that describes it
  • IDCAMS cards — Access method services commands: DEFINE CLUSTER/AIX/PATH/GDG with their parameter groups, REPRO, DELETE, LISTCAT, PRINT and ALTER, which is where a VSAM file's key, record size and components are written down
  • Db2 utility cards — The control statements a SYSIN hands a Db2 utility, in both dialects: High Performance Unload's own GLOBAL, UNLOAD with a SELECT and an OUTDDN per output file, FORMAT, OPTIONS, TEMPLATE and LISTDEF, and the base utility's UNLOAD ... FROM TABLE with SHRLEVEL, beside LOAD, COPY, REORG and RUNSTATS. Which dialect a deck is in is read off the deck and never off the program name, since a shop may alias DSNUTILB to the unload product's compatibility entry point. What the deck leaves out matters as much as what it says: an omitted FORMAT, DB2, LOCK or QUIESCE is resolved from the site parmlib rather than from a default, so a trait says which keywords were coded and which the site answers
  • Control-M — Job scheduling definitions, both dialects a shop has: the z/OS panel (.ctms) and the XML an export writes (.controlm). The JCL member each job runs, the IN and OUT conditions that order them, the SMART table they sit in and the calendars they run on, which is what turns a library of jobs into the order they actually run in
  • IMS gen source — The DBDs, PSBs, MFS format sets and stage 1 decks a shop gens its IMS system from (.dbd, .psb, .mfs, .gen, and an .asm whose first macro is one that gens, since a gen library is assembler source and is often kept as such): the databases with their segments, keys and logical relationships, the PCBs a program is handed and the segments each is sensitive to, the screens a message is laid out on, and the transactions that schedule a PSB. A DL/I call names a PCB by position and a segment by name, and the gen source is the only place either resolves to anything
  • Assembler — HLASM statement source (.asm, .mac): control sections and the DSECTs that are the assembler's copybooks, laid out constant by constant so a layout can be compared with the COBOL one; COPY members, macro prototypes and invocations, CALL and V-type constants, DCBs, entry points and external names. There is no grammar because the columns are the syntax — a card continues the one above it because of a character in column 72, and ICTL moves the columns at run time
  • SAS — SAS programs (.sas, and the SYSIN a job carries in-stream), kept as plain text and read for %INCLUDE, LIBNAME, INFILE/FILE/FILENAME, macro definitions and invocations, INPUT column layouts and the tables a PROC SQL reads, which is where a batch extract is read again by the group that asks what the numbers mean. A statement runs to the first semicolon outside a quoted string or a comment and SAS has no reserved words, so what there is to read is a text search and a name reference rather than a grammar
  • CLISTs, REXX execs and run book members — Kept as plain text and read for what they name, since a member no grammar claims still says who runs what. A CLIST (.clist, .clst) and an exec (.rexx, .rex, .rx, and an extensionless member whose first line is the /* REXX */ comment TSO reads) are read for the jobs they submit, the programs they run and the scripts they call, which is the only place in an estate that says how a job is started by hand; a run book member (.docjob, .docpgm, .docfich, .docappl, .docoper) for the component it documents and the components it names. C (.c, .h) and PL/I (.pli, .pl1) are kept so that they are searchable as what they are, and nothing more is read from them

Recipes

  • Search — Find copybooks, words, indicators, references, and relationships across COBOL programs
  • Cleanup — Remove debugging mode from SOURCE-COMPUTER paragraphs
  • Format — Remove words and shift sequence areas

Tested against

Unit tests cover the constructs one at a time, and the NIST COBOL-85 conformance suite in src/test/resources/gov/nist covers the standard. Neither contains CICS, IMS, DB2 or JCL, so the parser is also measured against real mainframe code — these public repositories, each measured on its own:

Application Repository Programs Jobs CICS Embedded SQL BMS maps DL/I
CardDemo aws-samples/aws-mainframe-modernization-carddemo 42 of 44 61 of 61 25 4 7 7
GenApp cicsdev/cics-genapp 31 of 31 29 of 29 31 8 5
Bank of Z IBM/Bank-of-Z 43 of 43 1 of 1 31 15 9 11
CBSA cicsdev/cics-banking-sample-application-cbsa 31 of 31 109 of 110 30 12 9
COBOL Programming Course openmainframeproject/cobol-programming-course 26 of 30 43 of 43 3
Cash Account IBMStockTrader/cash-account-cobol 1 of 1 3 of 3 1 1
zAppBuild IBM/dbb-zappbuild 6 of 7 3 1 2
DBB Samples ibmdbbdev/Samples 16 of 17 3 1 2
Z Open Editor sample IBM/zopeneditor-sample 0 of 5 8 of 10
base64 cicsdev/base64 1 of 1 1 of 1
GenevaERS Workbench genevaers/Workbench 20 of 20
GenevaERS Performance Engine genevaers/Performance-Engine 1 of 1
ADCD setup davidegirardi/adcdsetup 11 of 11
Zowe install packaging zowe/zowe-install-packaging 50 of 53
MainframeJCL billrain/MainframeJCL 83 of 108
zorow openmainframeproject/zorow 50 of 56
CLAIMS fixture moderneinc/mainframe-fixtures 19 of 19 48 of 48 5 4 4 6

Programs and Jobs say how many files of each kind the parser read, and out of how many: 216 of 229 COBOL programs and 518 of 555 JCL members, plus all 50 BMS map sets in the seven applications that have them. The technology columns say how many programs use each. A program counts as read when it parsed and printed back byte for byte; a job when it also had exactly the EXEC cards the traits found and nothing in it was left unplaced. The gaps are the measurement: CorpusCoverageTest groups the COBOL failures by cause and JclCorpusTest names each member it could not read and why.

The other member kinds are not spread the way COBOL and JCL are — most applications hold none of them at all — so they are counted in a table of their own, read of found. CBSA, the COBOL Programming Course, Cash Account, GenevaERS Workbench and MainframeJCL have none of the five and are left out of it:

Application IMS gen Assembler SAS Scripts and run books C and PL/I
CardDemo 8 of 8 4 of 4
GenApp 2 of 2
Bank of Z 17 of 17 2 of 2
zAppBuild 1 of 1
DBB Samples 1 of 1 1 of 1
Z Open Editor sample 3 of 3 4 of 4 4 of 4
base64 2 of 2 2 of 2
GenevaERS Performance Engine 116 of 116
ADCD setup 2 of 2
Zowe install packaging 22 of 22
zorow 45 of 45
CLAIMS fixture 19 of 19 8 of 8 4 of 4 42 of 42

That is 45 IMS gen members, 134 assembler members, 4 SAS members, 117 scripts and run book members and 9 C and PL/I members, every one of them read and printed back byte for byte — which for the last column is the whole of what is read, since there is no grammar for either language and a member of one is held as the lines it was written as. Two of the five are thin outside the fixture: no public application here has any SAS at all, and 25 of the 26 public IMS gen members belong to the two applications that run IMS. The assembler goes the other way — 116 of the 134 are one build engine's, and the C and PL/I are the one column the fixture has none of, since the claims application is a COBOL shop. Bank of Z's seventeen count in the IMS column and not the assembler one because that is what they are: src/base/ims/DBD/*.asm and src/base/ims/PSB/*.asm are a gen library kept as assembler, which is the ordinary way to keep one.

Bank of Z descends from CBSA, so the two are not independent measurements. 29 of CBSA's 31 program names, 36 of its 37 copybooks and all 10 of its map sets are in Bank of Z too; of the shared programs two are byte-identical, 19 differ by a few lines, and eight (BANKDATA, BNK1DCS, CREACC, CRECUST, DELCUS, INQCUST, UPDCUST, XFRFUN) were rewritten. Bank of Z adds the 14 IMS programs; CBSA adds the 110 jobs, among them DB2 DDL and BIND in JCL, where Bank of Z has one.

The last row is not a public application but a fixture: one fictional insurance claims application, CLAIMS, written so that every COPY, CALL, EXEC PROC, SEND MAP and SYSIN member in it resolves to a member of the same repository, and every member of it a reader here takes reads. The readers take 271 of its members between them and the only member kind left is the plain control card — the eight IEBGENER, DSN and parm cards of claims/ctlcard — which the walks skip rather than count against a parser. Nothing else in the repository is a member of a library at all: its licence and its two markdown documents are the whole of the rest. The public applications are measured; the fixture is required. The tests know it by its directory name, mainframe-fixtures, and fail when a program, copybook, job, procedure, map set, bind deck, link-edit deck, module listing, DDL member, Db2 utility deck, IMS gen member, assembler member, SAS member, CLIST, REXX exec, run book member, control card or schedule of it does not parse, read or print back — or when the corpus root does not contain it, since a fixture the walk cannot see, a symbolic link say, would otherwise pass as an empty application. FixtureCoverageTest puts every one of its members past every reader at once, so that a member is claimed by one reader and no more and the kinds nothing reads are claimed by none: a reader that quietly takes a parm card reports something plausible about a file it cannot read.

Clone them side by side into one directory and point the tests at it. The tests find files the way a build does, whatever the case of the extension: programs by .cbl, .cob and .cobol, copybooks by .cpy, .copy and .dcl, map sets by .bms, bind decks by .bnd, link-edit decks by .lnk and .lked, module listings by .amblist, .binder and .listload, schedules by .ctms and .controlm, IMS gen members by .dbd, .psb, .gen and .mfs, assembler programs and macro library members by .asm and .mac, SAS members by .sas, CLISTs by .clist and .clst, REXX execs by .rexx, .rex and .rx, run book members by .docjob, .docpgm, .docfich, .docappl and .docoper, C by .c and .h, PL/I by .pli and .pl1, and jobs by .jcl, .prc and .proc. Module listings, SAS members, scripts, run book members, C and PL/I have no grammar here: a build keeps them as plain text — the CLI reads them with Members.parser(), as the tests here do — and which technology a member is comes from its path, which is what the traits reading it are matched by. Since MainframeJCL, ADCD setup and Zowe's SZWESAMP keep their members as they came off the PDS, a .txt or extensionless file whose first card is JCL is a job, an extensionless file whose first subcommand binds is a bind deck, and an extensionless file whose first line is the /* REXX */ comment TSO itself reads is an exec. Control card members are typed by what they say rather than by what they are called: a .ctl, .prm or extensionless member is a sort deck, an IDCAMS deck, a Db2 utility deck, a link-edit deck or an AMBLIST request deck if its first statement is one, and anything else stays plain. An IMS gen library is often kept as .asm — Bank of Z writes its DBDs as src/base/ims/DBD/*.asm and its PSBs as src/base/ims/PSB/*.asm — so an .asm is a gen member when the first macro it invokes is one that gens, and the HLASM reader takes the rest. A member whose name promises a language its content is not — CBSA's DFH$SIP1.jcl is a CICS parameter member — is reported as such, under WrongLanguageException, rather than as a grammar failure. The corpus tests skip themselves when the variables are unset, so a normal ./gradlew test does not need them:

COBOL_CORPUS=/path/to/corpus JCL_CORPUS=/path/to/corpus BMS_CORPUS=/path/to/corpus \
  CONTROLM_CORPUS=/path/to/corpus DB2_CORPUS=/path/to/corpus IMS_CORPUS=/path/to/corpus \
  ASM_CORPUS=/path/to/corpus SAS_CORPUS=/path/to/corpus ./gradlew test

IMS_CORPUS reads 45 gen members — Bank of Z's 17, CardDemo's 8, DBB Samples' 1 and the fixture's 19 — for 19 databases of 20 segments and 72 fields with 9 names belonging to another database, 18 PSBs of 43 PCBs and 49 sensitive segments, and 7 format sets of 8 device pages and 199 device fields carrying 13 messages of 143 fields. The fixture's are the measurement and the rest are report-only: ImsCorpusTest holds it to INTERLINKS.md sections 19.1 to 19.5 — six DBDs of eight segments and thirty fields with seven references over five of them, six PSBs of ten PCBs, fourteen sensitive segments and the seven SENFLDs that build a fifty byte I/O area out of a sixty five byte segment, a stage 1 deck of six APPLCTN, two TRANSACT and five DATABASE, and six format sets of seven device pages carrying four MIDs and seven MODs — and counts every SEGM, FIELD, LCHILD, PCB, SENSEG, SENFLD, DFLD and MFLD of every member against an independent count of the source, since a misgrouped continuation prints back byte for byte and says something else.

Which PCB a program's nth mask is depends on what runs under the PSB, and section 6.1's six cases are tests of their own: a message driven program and a BMP are handed an I/O PCB no PCB statement codes, so every database after it is one mask along, while a DL/I batch program under a PSB without CMPAT=YES gets the database PCBs alone. Psb.getPcbAtMask is that rule, Psb.getPcb is the PCB an AIB call names by PCBNAME=, and Psb.getDatabasePcb is the one an EXEC DLI PCB(n) numbers.

A format set is where MFS and COBOL meet. An MFLD names a DFLD, so a field of the message has a place on the screen and a length, and the message is the layout of the area the program declares it with — matched by order and length and never by name, after the four byte LL/ZZ prefix IMS supplies and no MFLD describes. Message.getLength is what a copybook can be checked against and MessageField.getOffset says where each field lands: section 19.5's totals of 31, 154, 312, 288, 392, 344 and 281 bytes are tests. The other direction is one line of COBOL — a program names a MOD by passing it as the fourth argument of an ISRT against a message PCB, which DliCall.getMod resolves from working storage. Nothing names a MID: the NXT= of the MOD says which format the reply arrives on, and IMS applies it.

ASM_CORPUS reads 134 assembler members — GenevaERS Performance Engine's 116, CardDemo's 4, Z Open Editor sample's 3, base64's 2, zAppBuild's 1 and the fixture's 8 — for 301 control sections of which 233 are dummy, 13,180 constants laid out in them, 221 COPY statements, 47 macro definitions and 6,744 macro invocations, 31 calls of which 2 are DL/I, 37 DCBs and 123 entry points. There is no grammar: the columns are the syntax, so a hand-written statement reader over the lines does the whole of it, and the 1.2 MB GVBMR95.asm reads in about 25 ms where an ANTLR lexer alone takes three times that over the same text. The fixture's members are the measurement and the rest are report-only: AssemblerCorpusTest holds it to INTERLINKS.md sections 20.2 to 20.6 — five COPY statements over three copy members, fourteen invocations of the two macros the shop wrote (CLMSAVE three, CLMRTRN eleven, which is also the count of each program's exits), the three calls written in assembler of which two are ASMTDLI, the nineteen rows and three hundred bytes of CLMRECD at their offsets against cpy/CLMREC, the fifty of CLMPCBD and the sixty five of CLMROOTD, the two DL/I calls and the two DD names CLMA010 opens — and counts every CSECT, DSECT, COPY, MACRO, CALL, DCB, ENTRY and EXTRN of every member against an independent count of the source, since a misgrouped continuation prints back byte for byte and says something else. It also requires that no white space of a member holds anything but white space, which is the other way to print back perfectly and say nothing: text nobody took into a node of its own comes back out of the space in front of the next one. Both checks found a real defect on the corpus that the round trip did not.

A DSECT is the assembler's copybook and the only thing that can be checked against the other is the layout, so ControlSection.getFields works the location counter forward through the constants — duplication factor, type, length modifier, nominal value, and the boundary a type is aligned on — and gives each one where it starts and how long it is. The section runs to the next one, to the END, or to the EQU *-name that measures it, which is where a shop writes the length down. An operand this does not read, a length given as an expression say, leaves the offsets after it null rather than guessed at.

What tells a shop macro from IBM's is the library and not the program: the statement is the same either way, so CALL, DCB, OPEN, GET, PUT, CLOSE and WTO are known to come out of SYS1.MACLIB only because no member of the estate writes a prototype for them. The prototype is also what tells a macro library member from a copy member — most of a MACLIB is DSECTs read by COPY — and FindRelationships scans every assembler member before it writes an edge for the same kind of reason: a COBOL CALL 'CLMU030' reaches an assembler subroutine, and nothing in the COBOL says so. An operation that is neither an assembler directive nor a machine instruction is an invocation, so a mnemonic missing from the table reports one invocation too many; reading the corpus rows rather than the totals is what found the ones that were.

SAS_CORPUS reads 4 SAS members, all of them the fixture's: no public application in the corpus has any SAS at all. There is no grammar because there is almost no syntax to have one for — a statement runs to the first semicolon outside a quoted string or a comment, SAS has no reserved words, and everything that is not a boundary stays a word, which is the depth a text search and a name reference need. What does move a boundary is read: a /* */ comment anywhere a blank may go, a statement beginning * that runs to its own semicolon, and a quoted string, where a semicolon means nothing. SasCorpusTest holds it to INTERLINKS.md sections 21.1 to 21.5 — four %INCLUDE statements over one member, four LIBNAMEs over the one libref CLMSAS, the libref LIBRARY that PROC FORMAT writes its formats to and no statement of the member declares, two INFILEs whose DD names are the ones the COBOL programs SELECT for the same two data sets, five invocations of the one macro the shop wrote, the eleven and seven columns of the two INPUT layouts against the copybooks they were written from, and the one DB2 name the subsystem reads — and counts the statements of every member against the semicolons its source writes, since a boundary read in the wrong place prints back byte for byte and says something else.

A SAS program has no name of its own the way a COBOL program has a PROGRAM-ID: the member name is the only name it has. jcl/CLMSTAT runs three members of the SAS library and carries a fourth program in-stream, and that fourth has no name at all, so InstreamSas reads it out of the job's SYSIN and hangs its edges on the job — the same shape as the DDL most of an estate's tables are actually created by. What the SAS reaches by DD name is left on the traits rather than resolved: SASSRC is not a path, CLMSAS with no data set after it is whatever the step allocated, and a DD name closes only against the job that ran the program. An INPUT layout is the other join, and it is by position and never by name — a SAS variable holds eight characters and AUD-OLD-RESERVE is fifteen — so InputLayout.Field gives the column each variable starts in and how many bytes its informat reads.

JCL_CORPUS also reads 117 scripts and run book members — zorow's 45, Zowe install packaging's 22, Z Open Editor sample's 4, GenApp's 2, ADCD setup's 2 and the fixture's 42 — every line of them kept as it was written, since there is no grammar for any of the five kinds and what is to be had from them is what they name. A verb is what makes a reference. Every script writes SUBMIT &JOB (Y/N)? in a message somewhere, so a name in prose reaches nothing and only SUBMIT, CALL, RUN PROGRAM, EXEC, ALLOC, EDIT and SELECT are followed. A REXX command is built out of quoted strings and variables written side by side, so the quotes come off and what was between them joins up — "SUBMIT '"HLQ".JCL("JOB")'" is the one command SUBMIT 'HLQ.JCL(JOB)' — while what was written outside them is what the exec computed. Nothing resolves a variable, and the fixture is why that is the honest answer rather than a gap: not one of its four submits names a job, because the job a script really submits is a fact about two members and a parameter.

PlainTextCorpusTest holds the fixture to INTERLINKS.md sections 17.1 to 17.3 and 18.1 to 18.2 row by row: forty three statements over eleven scripts, of which ten call another script, one CALLs a load module and one RUNs a program under a plan; eleven allocations, four of them carrying the DD names CLMB010 and CLMD020 assign; and all thirty one run book subjects — ten jobs, fourteen programs and seven data sets. What a CLIST writes down beside its submits is held to 17.1 the same way: the PROC each of the eight declares, the ten calls they make of one another, and the eleven job names a SET chooses. A run book is held to the labelled fields it writes — the fifty one labels of the shop's own vocabulary — since a field is where the member says a name is a component and a sentence is not. The names a member merely mentions are reported and not resolved — 7,005 of them over the 117 members — and two of the fixture's matches are wrong on purpose: an English word of eight letters or fewer is spelled exactly like a member name, so which of them is a component of the estate is a join against the members a repository holds rather than something one member says.

Bind decks, link-edit decks, module listings and control cards ride on JCL_CORPUS too, since a deck is reached through the step that runs it and a listing is what that step printed. Each of the eight variables and the files under it are declared as inputs of the test task, so a corpus that grew, or a variable that was not set on the last run, invalidates the task rather than replaying its old counts from the cache.

The numbers above were taken at these commits. CBSA's main is a README; the application is on the July2024Refresh branch. DBB Samples declares an EBCDIC working-tree encoding in .gitattributes, so check it out with * -working-tree-encoding in .git/info/attributes, or the files on disk are not text the parser can read.

Application Checked out License
CardDemo main 59cc6c2 Apache-2.0
GenApp main f6f3f4b EPL-2.0
Bank of Z main 17a50f3 Apache-2.0
CBSA July2024Refresh 4173345 EPL-2.0
COBOL Programming Course master 61c573d CC-BY-4.0, by the Open Mainframe Project's contributors
Cash Account main c35db0d Apache-2.0
zAppBuild main ddc51c7 Apache-2.0
DBB Samples master 61a1a20 Apache-2.0
Z Open Editor sample main c57a788 Apache-2.0
base64 main 3960650 EPL-2.0
GenevaERS Workbench main 907e21f Apache-2.0
GenevaERS Performance Engine main 705a969 Apache-2.0
ADCD setup master a931dba MIT
Zowe install packaging v3.x/staging 20977c3 EPL-2.0
MainframeJCL main 598744a MIT
zorow master 9f1fdf0 Apache-2.0
CLAIMS fixture main b2952a3 Apache-2.0

CorpusCoverageTest reports how much of each application parses rather than requiring all of it to: what the parser cannot yet read is the point of the measurement. It asserts that every program it did parse prints back byte-identical to the input, which is the property recipes depend on, and JclCorpusTest asserts the same of every job.

BmsCorpusTest asserts the same over the 50 map sets, and counts the macros it read against an independent count of the source. A map set that groups its continuation lines wrongly still prints back perfectly — it just says something else — so printing alone would not catch it. 47 of the corpus's fields are initialised with a literal written over more than one card, and none of them comes back with a quote nothing closes, which is what a field read only as far as its first blank looks like.

Db2CorpusTest does the same for the schema, over both places DDL is written: 49 files of DDL and the 23 SYSIN streams the corpus's jobs submit. It reads 22 tables, 192 columns, 31 indexes and 9 foreign keys, and counts each CREATE TABLE, CREATE INDEX and PRIMARY KEY it read against an independent count of the source. The fixture is required to read where the rest is reported: its ddl/ members, its four catalog query decks and the index CLMJ004 creates in-stream are checked object by object against INTERLINKS sections 15 and 16, which is where the 2 tables, 18 columns, 2 primary keys, 1 foreign key and 5 indexes are written down. It also names the members it holds out — a Postgres port of one schema, a file that sets its own statement terminator, and members whose banner comment is damaged — and asserts how many there are, so that set cannot grow without someone saying why. BindCorpusTest does the same for bind cards, over the fixture's ten CARDLIB decks and the eleven decks the corpus writes in-stream, counting every BIND and REBIND it read against a line scan of the source. It runs under JCL_CORPUS: a bind deck is reached through the jobs that run it.

LinkEditCorpusTest does the same for link-edit decks, over 74 members and the ten decks the corpus writes in-stream, counting every control statement it read against a card scan of the source. It also checks the fixture's linklib deck by deck against INTERLINKS section 12, which is where the 19 modules, the five that enter at a DLITCBL label, the two ALIAS cards and every statically included object are written down. GenevaERS keeps its decks as build templates, so the 14 with no template directive in them are read as the decks they are and the one with a directive is left alone.

ListLoadCorpusTest does the same for load module listings, over the fixture's two AMBLIST reports, thirteen binder listings and two request decks, counting every module and every control section it read against a column scan of the source. It checks them against INTERLINKS section 14, which is where the 16 modules, the 16 program CSECTs, the three map sets, the one module entered at a DLITCBL label and the one alias are written down, and it requires each module's reported size to be what its sections add up to and what the binder printed for a module to agree with what AMBLIST found in the library afterwards.

ControlCardCorpusTest does the same for sort and IDCAMS cards, over 16 members and the 153 decks the corpus writes in-stream, and requires that no member is claimed by both parsers. It also checks the fixture's ctlcard library member by member against INTERLINKS sections 8.3 and 22, which is where the six IDCAMS members, the two sort members, the five Db2 utility decks and the ten that are none of those are written down.

UnloadCorpusTest does the same for Db2 utility cards, over six members and the twelve decks the corpus writes in-stream, counting every control statement it read against a card scan of the source. It checks the fixture's unload set against INTERLINKS section 22, which is where the six decks, the one written in the base utility's dialect, the seven SELECTs and their seven OUTDDNs, the thirteen DD names the decks reach and the four keywords ctlcard/UNLCLM04 leaves to the site parmlib are written down. It runs under JCL_CORPUS: a utility deck is reached through the step that runs it.

SourcePositionsCorpusTest reads every JCL position back out of the source, since a position landing anywhere but on the word it names is worse than no position at all. Over the 553 members it reads 202,383 words back and compares each to the text at the offset reported for it, and requires every one of the 102,005 statements to be placed: 79,648 in the member they were written in, and the 22,357 a procedure or INCLUDE member wrote against the EXEC or INCLUDE card that brought them in.

Contributing

We appreciate all types of contributions. See the contributing guide for detailed instructions on how to get started.

Licensing

This is a Moderne proprietary module available only for use by Moderne customers under the terms of a commercial contract.

For more information about licensing, please contact Moderne.

About

COBOL abstract syntax tree

Resources

Stars

0 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages