0x- 7 cobol examples with explanations.

You may have heard COBOL before. If you search for it you will find images like this:

This is a picture of a COBOL program editor running in a mainframe. Below we will go over 7 examples to COBOL (COmmon Business Oriented Language). We’ll be running these programs on Linux. We are not going cover mainframe tutorials here; I’ve added some mainframe resources at the end.

The sail head dinosaur inside me thanks you for sharing.

Share Yvan

How to Install the GnuCobol compiler

This compiler transpiles COBOL to C bytecode that can run on your linux bash command line. Not all the features of COBOL are supported but most are.

Run (to install):

sudo apt-get install open-cobol

How to write a Program

We will write a simple program in cobol called ‘hello.cbl’. There are a lot of strange keywords in cobol. I will explain them after compilation.

*> setup the identification division
IDENTIFICATION DIVISION.
*> setup the program id
PROGRAM-ID. HELLO.
*> setup the procedure division (like 'main' function)
PROCEDURE DIVISION.
*> print a string
DISPLAY 'WILLKOMMEN'.
*> end our program
STOP RUN.

How to Compile and run

Create the runable bytecode file with the instructions below. This transpiles our COBOL program called ‘hello.cbl’ to C then it takes the C and produces an executable object/bytecode file called ‘hello’.

Compile and then run with:

cobc -x -o hello hello.cbl

./hello

Should output:

WILLKOMMEN

Understanding the Program Structure

First and foremost to write comments in cobol use the *> characters. In a cobol program there are several possible divisions. A division is just a way to break up the program into areas responsible for different things. So IDENTIFICATION DIVISION is responsible for identifying the program (docs). We are only going to use the PROGRAM-ID keyword, giving our program a name, to keep it simple. The DATA DIVISION is a place where we can declare variables we want to use in our program, we will use the WORKING-STORAGE keyword (docs). The PROCEDURE DIVISION is like the main function of our program. It actually runs our code and can access anything defined in the data division (docs). The STOP RUN sentence (sentence=one or more ‘statements’ that ends with a ‘.’) exits the program (like sys.exit() in python). You will also notice 6 spaces on the left of all my programs. This is not a mistake; the compiler expects this and on a mainframe these 6 spaces would be used for line numbers. Also: EVERYTHING IN COBOL IS CAPITALIZED SO ITS OFTEN EASIER TO TYPE WITH CAPSLOCK ON. You may notice the dot/period at the end of some lines. This is how you end a sentence (a series of one or more statements) in cobol. Below I may refer to anything that ends with a period as a statement. Ok so now that we have gotten through the basics of the program structure let’s write some programs.

Declaring Variables

I will write a script below that explains how to declare and print variables. We will declare several variables in the data division (FIRST-VAR, SECOND-VAR, etc) and then print them in the procedure division using DISPLAY.

IDENTIFICATION DIVISION.
PROGRAM-ID. VARS.
DATA DIVISION.
*> working storage defines variables
WORKING-STORAGE SECTION.
*> define a number with a sign, 3 numbers, a decimal, and then
*> two numbers aafter the decimal. by default it should be 0 filled
01 FIRST-VAR PIC S9(3)V9(2).
*> do the same thing as above but actually initialize
*> to a number -123.45
01 SECOND-VAR PIC S9(3)V9(2) VALUE -123.45.
*> defines an alphabetic string and initialize it to abcdef
01 THIRD-VAR PIC A(6) VALUE 'ABCDEF'.
*> define an alphanumeric string and initialize it to a121$
01 FOURTH-VAR PIC X(5) VALUE 'A121$'.
*> create a grouped variable
01 GROUP-VAR.
05 SUBVAR-1 PIC 9(3) VALUE 337.
*> create 3 alphanumerics, but use less than
*> the allocated space for each of them
05 SUBVAR-2 PIC X(15) VALUE 'LALALALA'.
05 SUBVAR-3 PIC X(15) VALUE 'LALALA'.
05 SUBVAR-4 PIC X(15) VALUE 'LALALA'.
*> print our variables
PROCEDURE DIVISION.
DISPLAY "1ST VAR :"FIRST-VAR.
DISPLAY "2ND VAR :"SECOND-VAR.
DISPLAY "3RD VAR :"THIRD-VAR.
DISPLAY "4TH VAR :"FOURTH-VAR.
DISPLAY "GROUP VAR :"GROUP-VAR.
STOP RUN.

PIC stands for picture (not sure why it is called this) and it is a keyword we use to define a variable. We use functions of the form type(elements). So 9(3) would correspond to laying aside enough room in memory for storing a number with 3 values. Above we define many variables and then print them out. You may also notice the 01 values before each declaration, and the 05 value before sub variables. These numbers are called level numbers and indicate to cobol what kind of variable we are declaring. 01 is for top level variables, 05 is group level variables under some other variable. Below are the functions and what each data type above corresponds to.

9 — numeric

A — alphabetic

X — alphanumeric

V — decimal

S — sign

A final example:

01 CAT-PEOPLE PIC X(15) VALUE '12@4A!D$'.

Would create a variable called CAT-PEOPLE with space for 15 alphanumeric elements that only actually fills out 8 of them. You may have noticed that I do this above in the subgroup. Here is another data declaration resource.

Common Verbs

In cobol a verb is a keyword that does something (docs). We will cover the compute, divide, multiply, subtract, add, move, and initialize verbs. These are verbs you will use often in cobol programming to calculate, say the result of a business transaction.

IDENTIFICATION DIVISION.
PROGRAM-ID. VERBS.
DATA DIVISION.
WORKING-STORAGE SECTION.
*> numbers we will perform operations on
*> with verbs
01 NUM1 PIC 9(9) VALUE 10.
01 NUM2 PIC 9(9) VALUE 10.
01 NUMA PIC 9(9) VALUE 100.
01 NUMB PIC 9(9) VALUE 15.
*> variables we will use to store results
*> of operations we do
01 NUMC PIC 9(9).
01 RES-DIV PIC 9(9).
01 RES-MULT PIC 9(9).
01 RES-SUB PIC 9(9).
01 RES-ADD PIC 9(9).
01 RES-MOV PIC X(9).
PROCEDURE DIVISION.
*> compute num1 times num2 and store result in numc
COMPUTE NUMC = (NUM1 * NUM2).
*> divide numa by numb and store result in res-div
DIVIDE NUMA BY NUMB GIVING RES-DIV.
*> multiply numa by numb storing result in res-mult
MULTIPLY NUMA BY NUMB GIVING RES-MULT.
*> subtract numa from numb store result in res-sub
SUBTRACT NUMA FROM NUMB GIVING RES-SUB.
*> add numa to numb and store result in res-add
ADD NUMA TO NUMB GIVING RES-ADD.
*> the pointer from numa to
MOVE NUMA TO RES-MOV.
*> reinitilize num1
INITIALIZE NUM1.
*> reinitilize num2 but replace numeric data with 12345
INITIALIZE NUM2 REPLACING NUMERIC DATA BY 12345.
DISPLAY "NUMC:"NUMC
DISPLAY "RES-DIV:"RES-DIV
DISPLAY "RES-MULT:"RES-MULT
DISPLAY "RES-SUB:"RES-SUB
DISPLAY "RES-ADD:"RES-ADD
DISPLAY "RES-MOV:"RES-MOV
DISPLAY "REINITIALIZED NUM1: "NUM1
DISPLAY "REINITIALIZED NUM2: "NUM2
STOP RUN.

compute — can be used to do arithmetic and store the result in a variable

divide — can be used to divide two numbers

multiply — can be used to you guessed it, multiply

add — adds two variables/numbers

move — moves a value or reference from a variable into another variable.

initialize — this is used above to reset a variable after its been set

Conditionals

In this section we will look at if/else statements and switch statements.

IDENTIFICATION DIVISION.
PROGRAM-ID. CONDITIONALS.
DATA DIVISION.
WORKING-STORAGE SECTION.
*> setting up places to store values
*> no values set yet
01 NUM1 PIC 9(9).
01 NUM2 PIC 9(9).
01 NUM3 PIC 9(5).
01 NUM4 PIC 9(6).
*> create a positive and a negative
*> number to check
01 NEG-NUM PIC S9(9) VALUE -1234.
*> create variables for testing classes
01 CLASS1 PIC X(9) VALUE 'ABCD '.
*> create statements that can be fed
*> into a cobol conditional
01 CHECK-VAL PIC 9(3).
88 PASS VALUES ARE 041 THRU 100.
88 FAIL VALUES ARE 000 THRU 40.
PROCEDURE DIVISION.
*> set 25 into num1 and num3
*> set 15 into num2 and num4
MOVE 25 TO NUM1 NUM3.
MOVE 15 TO NUM2 NUM4.
*> comparing two numbers and checking for equality
IF NUM1 > NUM2 THEN
DISPLAY 'IN LOOP 1 - IF BLOCK'
IF NUM3 = NUM4 THEN
DISPLAY 'IN LOOP 2 - IF BLOCK'
ELSE
DISPLAY 'IN LOOP 2 - ELSE BLOCK'
END-IF
ELSE
DISPLAY 'IN LOOP 1 -ELSE BLOCK'
END-IF
*> use a custom pre-defined condition
*> which checks CHECK-VAL
MOVE 65 TO CHECK-VAL.
IF PASS
DISPLAY 'PASSED WITH 'CHECK-VAL' MARKS.'.
IF FAIL
DISPLAY 'FAILED WITH 'CHECK-VAL' MARKS.'.
*> a switch statment
EVALUATE TRUE
WHEN NUM1 < 2
DISPLAY 'NUM1 LESS THAN 2'
WHEN NUM1 < 19
DISPLAY 'NUM1 LESS THAN 19'
WHEN NUM1 < 1000
DISPLAY 'NUM1 LESS THAN 1000'
END-EVALUATE.
STOP RUN.

All this should be familiar to you if you have done any programming. You have your standard if/else, not/and/or operators, type comparisons, and switch statements. The only thing that might be a little weird are the pre-defined statements. What has essentially happened here is that the variable CHECK-VAL has these two conditions that depend on it (hence why PASS/FAIL are indented underneath CHECK-VAL) 88 is a special level number (like 01) for indicating that a statement is a custom conditional that depends on the 01 variable above it. You will also notice our STOP RUN is not indented here. This exits the whole program which is why you can indent or un-indent it from the procedure division.

Here are some examples using NOT/AND/OR as well as some other extras (imagine putting these into the procedure division above):

*> NOT, negating a conditional
MOVE 50 TO NUM1.
MOVE 60 TO NUM2.
IF NOT NUM2 IS LESS THAN NUM1 THEN
DISPLAY NUM2' IS NOT LESS THAN 'NUM1
END-IF
*> AND, having multiple conditionals
IF NUM1 IS LESS THAN NUM2 AND NUM1 IS LESS THAN 100 THEN
DISPLAY 'COMBINED CONDITION'
ELSE
DISPLAY 'NAH'
END-IF
*> checking for negative or positive values
IF NEG-NUM IS POSITIVE OR NEG-NUM IS NEGATIVE THEN
DISPLAY 'A NUMBER IS POSITIVE'.
*> checking for negative or positive values
IF NEG-NUM IS NEGATIVE THEN
DISPLAY 'A NUMBER IS NEGATIVE'.
*> checking if a variable is a certain
*> data type
IF CLASS1 IS ALPHABETIC OR CLASS1 IS NUMERIC THEN
DISPLAY 'CLASS1 IS ALPHABETIC or numeric'.

One last thing to notice: IF statements that do not have END-IF need a period to end them inside their last ‘sub statement.’

String Handling

String handling in cobol is very verbose and requires a lot of typing. Let’s try it.

IDENTIFICATION DIVISION.
PROGRAM-ID. STRINGHANDLE.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-CNT1 PIC 9(2) VALUE 0.
01 WS-CNT2 PIC 9(2) VALUE 0.
01 WS-STRING PIC X(25) VALUE 'ABCDADADADABVDFDFFAF'.
01 WS-STRING-DEST PIC A(30).
01 WS-STR1 PIC A(15) VALUE 'TUTORIALSPOINT'.
01 WS-STR2 PIC A(7) VALUE 'WELCOME'.
01 WS-STR3 PIC A(7) VALUE 'TO AND'.
01 WS-COUNT PIC 99 VALUE 1.
01 WS-UNSTR PIC A(30) VALUE 'WELCOME TO TUTORIALSPOINT'.
PROCEDURE DIVISION.
*> count the number of chars in string, store in ws-cnt1
INSPECT WS-STRING TALLYING WS-CNT1 FOR ALL CHARACTERS.
DISPLAY "WS-CNT1 : "WS-CNT1.
*> count just the A characters
INSPECT WS-STRING TALLYING WS-CNT2 FOR ALL 'A'.
DISPLAY "WS-CNT2 : "WS-CNT2.
*> replace A chars with X in strings
DISPLAY "OLD STRING : "WS-STRING.
INSPECT WS-STRING REPLACING ALL 'A' BY 'X'.
DISPLAY "NEW STRING : "WS-STRING.
*> string concatenate
STRING WS-STR2 DELIMITED BY SIZE
WS-STR3 DELIMITED BY SPACE
WS-STR1 DELIMITED BY SIZE
INTO WS-STRING-DEST
WITH POINTER WS-COUNT
ON OVERFLOW DISPLAY 'OVERFLOW!'
END-STRING.
DISPLAY 'WS-STRING : 'WS-STRING-DEST.
DISPLAY 'WS-COUNT : 'WS-COUNT.
*> string split
UNSTRING WS-UNSTR DELIMITED BY SPACE
INTO WS-STR3, WS-STR2, WS-STR1
END-UNSTRING.
DISPLAY 'WS-STR1 : 'WS-STR1.
DISPLAY 'WS-STR2 : 'WS-STR2.
DISPLAY 'WS-STR3 : 'WS-STR3.
STOP RUN.
view raw first_cobol_str.cbl hosted with ❤ by GitHub

Tallying all or just specific characters is pretty clear. The replacing keyword is also pretty clear, it replaces specified data in the string with some other data. Whats really worth digging into here is the string concatenation and the splitting. In the STRING statement we pass in the original strings WS-STR2, WS-STR3, WS-STR1 and we use a DELIMITED BY to tell the string statement how to combine them. If we delimit by SIZE we are telling cobol to add the entire input string to the final string. If we delimit by SPACE we are saying to take the input string up to the first space and omit the rest. The INTO keyword tells us the variable (WS-STRING-DEST) where the resulting concatenated string will be stored. WITH POINTER here manages to count the things in the final string. So somehow putting a pointer to the string counts it as things are concatenated in. I think what happens is it sets a pointer to the beginning and as you push things into the final string it pushes the pointer down several locations which are then stored as a count. The ON OVERFLOW tells cobol what to do if the input strings are too large; here it prints/displays ‘OVERFLOW!’

The string docs on mainframetechhelp are very useful for understanding this section.

Looping

We will now cover some of the looping logic in cobol. One thing I’ll mention before we get into it: We can name parts of the procedure division; these named parts, called paragraphs, can be used kind of similarly to functions or named lambda functions in python. In cobol a paragraph can contain many sentences/statements.

IDENTIFICATION DIVISION.
PROGRAM-ID. LOOPS.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-CNT PIC 9(1) VALUE 0.
01 WS-A PIC 9 VALUE 0.
01 WS-Z PIC 9 VALUE 2.
PROCEDURE DIVISION.
*> run the b-para-times paragraph
*> 3 times
PERFORM B-PARA-TIMES 3 TIMES.
*> run b-para-until the count variable
*> ws-cnt incremented inside the paragraph is greater than 3
PERFORM B-PARA-UNTIL WITH TEST AFTER UNTIL WS-CNT>3.
*> you can actually run from one paragraph
*> to another ending paragraph
*> through all paragrpahs in between the two
PERFORM C-PARA-THRU THRU E-PARA-THRU.
*> perform a varying style loop which specified
*> a value to increment until it reaches some
*> final
PERFORM B-PARA-VARY VARYING WS-A FROM 1 BY 1 UNTIL WS-A=5.
DISPLAY 'WS-A AFTER VARYING 'WS-A.
STOP RUN.
*> define paragraphs/functions that will
*> be called in our loops above
B-PARA-TIMES.
DISPLAY 'IN B-PARA-TIMES'.
B-PARA-UNTIL.
DISPLAY 'WS-CNT : 'WS-CNT
ADD 1 TO WS-CNT.
C-PARA-THRU.
DISPLAY 'IN C-PARA-THRU'.
D-PARA-THRU.
DISPLAY 'IN D-PARA-THRU'.
E-PARA-THRU.
DISPLAY 'IN E-PARA-THRU'.
B-PARA-VARY.
DISPLAY 'IN B-PARA ' WS-A.

So I think the loops are pretty well explained above. You will notice here we set aside code to be called outside the procedure division. This is because we want to define these as things we can do but we don’t actually want to run them in the procedure division so we put them in paragraphs outside the procedure division. To do this each one needs to have a name that we can reference/use in the procedure division. So B-PARA-TIMES will only be run when its called in our loop on line 13.

Files

Files in cobol usually have a rigid structure like a table. This is because of what they were created for dealing with: well organized business data. There are a few kinds of files in cobol (docs, another example); we are going to deal with sequential files as they are the most basic. A sequential file consists of records (rows) and each record contains some number of fields (columns). Let’s get to the code.

IDENTIFICATION DIVISION.
PROGRAM-ID. FILES.
*> create an environment section
ENVIRONMENT DIVISION.
*> input output is where used files
*> will be declared
INPUT-OUTPUT SECTION.
FILE-CONTROL.
*> we will have one file called
*> transactions that is sequantially written
*> and accessed sequentially as well
SELECT TRANSACTIONS ASSIGN TO 'transactions.txt'
ORGANIZATION IS SEQUENTIAL.
DATA DIVISION.
FILE SECTION.
*> create a file specification
FD TRANSACTIONS.
01 TRANSACTION-STRUCT.
02 UID PIC 9(5).
02 DESC PIC X(25).
02 DETAILS.
03 AMOUNT PIC 9(6)V9(2).
03 START-BALANCE PIC 9(6)V9(2).
03 END-BALANCE PIC 9(6)V9(2).
02 ACCOUNT-ID PIC 9(7).
02 ACCOUNT-HOLDER PIC A(50).
*> create a single record for insertion
*> this has the same structure as the
*> record above but with actual values
WORKING-STORAGE SECTION.
01 TRANSACTION-RECORD.
02 UID PIC 9(5) VALUE 12345.
02 DESC PIC X(25) VALUE 'TEST TRANSACTION'.
02 DETAILS.
03 AMOUNT PIC 9(6)V9(2) VALUE 000124.34.
03 START-BALANCE PIC 9(6)V9(2) VALUE 000177.54.
03 END-BALANCE PIC 9(6)V9(2) VALUE 53.2.
02 ACCOUNT-ID PIC 9(7).
02 ACCOUNT-HOLDER PIC A(50).
PROCEDURE DIVISION.
*> print the record we are writing
DISPLAY 'WRITING RECORD: 'TRANSACTION-RECORD.
*> open the file in output mode
*> this will re-create the file
OPEN OUTPUT TRANSACTIONS
*> write a record of type transaction-struct
*> the actual record being transaction-record
WRITE TRANSACTION-STRUCT FROM TRANSACTION-RECORD
*> close the file
CLOSE TRANSACTIONS
STOP RUN.

So in cobol you need to specify the file and what kind of file it is in the INPUT-OUTPUT SECTION Then you need to specify what kind of records are in your file. Then you need to create such a record with the exact same structure. Then in the PROCEDURE DIVISION you open the file (see open modes for details). Then you when writing you specify what kind of record you are adding and the record itself. Here we have opened our file in OUTPUT mode which always re-creates a file when you open it even if it already exists. You can make writing a file take 100 lines of code in cobol so I tried to keep it as tight as I know how. This is a nice guide on sequential files in cobol. To understand the syntax of WRITE here are the ibm docs.

Resources for mainframe programming

If you are interested in legacy systems and cobol you will probably want to play around on a mainframe. They are hard to use and look something like this:

The below resources may be helpful:

https://medium.com/@bellmar/hello-world-on-z-os-a0ef31c1e87f (mainframe tutorial)

https://medium.com/@bellmar/mainframe-on-the-macbook-51bc1806d869

https://www.youtube.com/watch?v=Uv7ThVwb7m8 (programming mainframe cobol)

http://www.csis.ul.ie/cobol/examples/default.htm (more cobol examples)

http://www3.sympatico.ca/bredam/GoodBadUgly.html (overview of cobol quirks)

https://devops.com/the-beauty-of-the-cobol-programming-language-v2/ (another programming tutorial with cobol)

https://github.com/mickaelandrieu/awesome-cobol (cobol software)

Conclusion

Cobol is interesting. I think it’s really fascinating that a language like this has been around since the 1950s in some form and to be honest it will probably be around for the foreseeable future. It’s probably useful for some folks to have a grasp on the basics. It has some obvious issues though; it is extremely verbose and the documentation is a bit scattered.

If you liked this share it with a like minded friend.

Thank you for Sharing.

Share