Beginner

Control Flow in COBOL

Learn conditionals and loops in COBOL - IF/ELSE, 88-level condition names, EVALUATE, and the versatile PERFORM verb using Docker-ready GnuCOBOL examples

Control flow determines the order in which a program’s statements execute - which branches it takes and how many times it repeats work. As an imperative, procedural language, COBOL gives you a familiar set of tools for this: conditionals, multi-way branches, and loops. What makes COBOL distinctive is how it expresses them. True to its business-readable design, the language spells out control flow in English-like sentences, and it bundles almost all iteration into a single, remarkably flexible verb: PERFORM.

In this tutorial you’ll learn how COBOL handles decisions with IF/ELSE/END-IF, how the language’s signature 88-level condition names turn data values into self-documenting tests, how EVALUATE provides a powerful switch/case construct, and how the one PERFORM verb covers counted loops, conditional loops, and calling reusable paragraphs. COBOL has no for or while keyword - everything iterative flows through PERFORM.

All examples use modern free-format syntax and run with GnuCOBOL in Docker, so you can compile and execute each one without installing anything locally.

Conditionals with IF / ELSE / END-IF

The basic decision in COBOL reads almost like a sentence. The END-IF scope terminator marks where the conditional ends - a feature added in COBOL 85 that replaced error-prone period-based scoping.

Create a file named ifelse.cob:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
IDENTIFICATION DIVISION.
PROGRAM-ID. IF-ELSE.

DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-BALANCE   PIC 9(5) VALUE 1200.
01 WS-LIMIT     PIC 9(5) VALUE 1000.
01 WS-AGE       PIC 9(3) VALUE 25.

PROCEDURE DIVISION.
    IF WS-BALANCE > WS-LIMIT
        DISPLAY "Balance exceeds the credit limit"
    ELSE
        DISPLAY "Balance is within the credit limit"
    END-IF.

    *> Conditions combine with AND / OR
    IF WS-AGE >= 18 AND WS-AGE <= 65
        DISPLAY "Working-age adult"
    END-IF.

    *> COBOL also accepts spelled-out relational words
    IF WS-AGE IS GREATER THAN 17
        DISPLAY "Age is at least 18"
    END-IF.

    STOP RUN.

COBOL accepts both symbolic operators (>, <, =, >=, <=) and their English equivalents (GREATER THAN, LESS THAN, EQUAL TO). Combine conditions with AND, OR, and NOT. Always close an IF with END-IF in modern code - it makes nesting unambiguous.

Condition Names with 88-Levels

COBOL’s most idiomatic conditional feature is the 88-level condition name. Instead of comparing a variable to a literal value scattered throughout your code, you give meaningful names to the values a field can hold. This is one of the clearest examples of COBOL’s self-documenting philosophy.

Create a file named condname.cob:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
IDENTIFICATION DIVISION.
PROGRAM-ID. COND-NAMES.

DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-STATUS-CODE   PIC X VALUE "A".
   88 IS-ACTIVE     VALUE "A".
   88 IS-SUSPENDED  VALUE "S".
   88 IS-CLOSED     VALUE "C".

PROCEDURE DIVISION.
    IF IS-ACTIVE
        DISPLAY "Account is active"
    END-IF.

    *> SET ... TO TRUE assigns the 88-level's value to the parent field
    SET IS-SUSPENDED TO TRUE.

    IF IS-SUSPENDED
        DISPLAY "Account is now suspended"
    ELSE
        DISPLAY "Account is not suspended"
    END-IF.

    STOP RUN.

A VALUE on an 88-level lists the values that make the condition true. Testing IF IS-ACTIVE is equivalent to IF WS-STATUS-CODE = "A", but far more readable. The SET IS-SUSPENDED TO TRUE statement works in reverse - it stores "S" into the parent field, so condition names can both read and write meaning.

Multi-Way Branching with EVALUATE

For multiple branches, COBOL provides EVALUATE, the equivalent of a switch/case statement. Its most powerful form is EVALUATE TRUE, which lets each WHEN test an arbitrary condition - perfect for range checks like grading.

Create a file named evaluate.cob:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
IDENTIFICATION DIVISION.
PROGRAM-ID. EVAL-DEMO.

DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-SCORE  PIC 9(3) VALUE 85.
01 WS-GRADE  PIC X.

PROCEDURE DIVISION.
    *> EVALUATE TRUE - each WHEN is a full condition
    EVALUATE TRUE
        WHEN WS-SCORE >= 90
            MOVE "A" TO WS-GRADE
        WHEN WS-SCORE >= 80
            MOVE "B" TO WS-GRADE
        WHEN WS-SCORE >= 70
            MOVE "C" TO WS-GRADE
        WHEN OTHER
            MOVE "F" TO WS-GRADE
    END-EVALUATE.

    DISPLAY "Score " WS-SCORE " earns grade " WS-GRADE.

    *> EVALUATE on a value - matches WHEN against a single field
    EVALUATE WS-GRADE
        WHEN "A"
            DISPLAY "Performance: Excellent"
        WHEN "B"
            DISPLAY "Performance: Good"
        WHEN "C"
            DISPLAY "Performance: Satisfactory"
        WHEN OTHER
            DISPLAY "Performance: Needs improvement"
    END-EVALUATE.

    STOP RUN.

EVALUATE checks each WHEN in order and runs the first match - there is no fall-through, so no break is needed. WHEN OTHER acts as the default case. Notice the output shows 085 rather than 85: because WS-SCORE is declared PIC 9(3), it always displays three digits with leading zeros.

Loops with the PERFORM Verb

COBOL has no for or while keyword. Instead, the single PERFORM verb handles every kind of loop. The three inline forms below cover the most common needs.

Create a file named loops.cob:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
IDENTIFICATION DIVISION.
PROGRAM-ID. LOOP-DEMO.

DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-I       PIC 9(2) VALUE 0.
01 WS-SUM     PIC 9(4) VALUE 0.
01 WS-COUNT   PIC 9(2) VALUE 0.

PROCEDURE DIVISION.
    *> PERFORM n TIMES - a simple counted loop
    DISPLAY "PERFORM TIMES:".
    PERFORM 3 TIMES
        DISPLAY "  Hello from a loop"
    END-PERFORM.

    *> PERFORM VARYING - a counter loop (like a for loop)
    DISPLAY "PERFORM VARYING:".
    PERFORM VARYING WS-I FROM 1 BY 1 UNTIL WS-I > 5
        ADD WS-I TO WS-SUM
        DISPLAY "  i = " WS-I
    END-PERFORM.
    DISPLAY "Sum of 1 to 5 = " WS-SUM.

    *> PERFORM UNTIL - a condition-controlled loop (like a while loop)
    DISPLAY "PERFORM UNTIL:".
    PERFORM UNTIL WS-COUNT >= 3
        ADD 1 TO WS-COUNT
        DISPLAY "  count = " WS-COUNT
    END-PERFORM.

    STOP RUN.
  • PERFORM n TIMES repeats a fixed number of times.
  • PERFORM VARYING ... FROM ... BY ... UNTIL is COBOL’s counted loop, initializing and stepping a counter automatically.
  • PERFORM UNTIL loops while a condition stays false, like a while loop. By default the test happens before each iteration (WITH TEST BEFORE); add WITH TEST AFTER for do-while behavior.

Loop Control and Calling Paragraphs

Modern COBOL (2002+, supported by GnuCOBOL) adds loop control with EXIT PERFORM (break out of the loop) and EXIT PERFORM CYCLE (skip to the next iteration - like continue). PERFORM also has a second job: calling a named paragraph as a reusable block of code, which is how COBOL structures its procedural logic.

Create a file named loopctrl.cob:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
IDENTIFICATION DIVISION.
PROGRAM-ID. LOOP-CTRL.

DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-N    PIC 9(2) VALUE 0.

PROCEDURE DIVISION.
MAIN-PARA.
    PERFORM VARYING WS-N FROM 1 BY 1 UNTIL WS-N > 10
        IF WS-N = 6
            DISPLAY "Stopping at 6"
            EXIT PERFORM           *> break out of the loop
        END-IF
        IF FUNCTION MOD(WS-N, 2) = 0
            EXIT PERFORM CYCLE     *> skip even numbers, continue loop
        END-IF
        DISPLAY "Odd number: " WS-N
    END-PERFORM.

    *> PERFORM also calls a paragraph by name - reusable logic
    DISPLAY "Calling a paragraph:".
    PERFORM GREETING-PARA.
    PERFORM GREETING-PARA.

    STOP RUN.

GREETING-PARA.
    DISPLAY "  Greetings from a paragraph".

The loop prints odd numbers, uses EXIT PERFORM CYCLE to skip even ones via the intrinsic FUNCTION MOD, and EXIT PERFORM to break when it reaches 6. Then PERFORM GREETING-PARA runs the named paragraph twice - control returns automatically after the paragraph finishes. This out-of-line PERFORM is the foundation of COBOL’s procedural structure.

Running with Docker

Compile and run each example with GnuCOBOL. The -x flag produces an executable and -free enables free-format source. With -x, the executable takes its name from the source file (so ifelse.cob produces ./ifelse).

1
2
3
4
5
6
7
8
9
# Pull the official image
docker pull esolang/cobol:latest

# Run each control-flow example
docker run --rm -v $(pwd):/app -w /app esolang/cobol sh -c 'cobc -x -free ifelse.cob && ./ifelse'
docker run --rm -v $(pwd):/app -w /app esolang/cobol sh -c 'cobc -x -free condname.cob && ./condname'
docker run --rm -v $(pwd):/app -w /app esolang/cobol sh -c 'cobc -x -free evaluate.cob && ./evaluate'
docker run --rm -v $(pwd):/app -w /app esolang/cobol sh -c 'cobc -x -free loops.cob && ./loops'
docker run --rm -v $(pwd):/app -w /app esolang/cobol sh -c 'cobc -x -free loopctrl.cob && ./loopctrl'

Expected Output

ifelse.cob:

Balance exceeds the credit limit
Working-age adult
Age is at least 18

condname.cob:

Account is active
Account is now suspended

evaluate.cob:

Score 085 earns grade B
Performance: Good

loops.cob:

PERFORM TIMES:
  Hello from a loop
  Hello from a loop
  Hello from a loop
PERFORM VARYING:
  i = 01
  i = 02
  i = 03
  i = 04
  i = 05
Sum of 1 to 5 = 0015
PERFORM UNTIL:
  count = 01
  count = 02
  count = 03

loopctrl.cob:

Odd number: 01
Odd number: 03
Odd number: 05
Stopping at 6
Calling a paragraph:
  Greetings from a paragraph
  Greetings from a paragraph

Key Concepts

  • END-IF and scope terminators - Modern COBOL closes conditionals with explicit terminators (END-IF, END-EVALUATE, END-PERFORM), avoiding the period-scoping ambiguities of older code.
  • 88-level condition names are uniquely COBOL - they attach readable names to data values, so IF IS-ACTIVE replaces IF WS-STATUS-CODE = "A" and SET ... TO TRUE assigns those values back.
  • EVALUATE is COBOL’s switch - it has no fall-through, needs no break, and EVALUATE TRUE lets each WHEN test a full condition for range logic.
  • One verb, every loop - PERFORM handles counted (n TIMES), iterating (VARYING), and conditional (UNTIL) loops; there is no separate for or while.
  • TEST BEFORE vs TEST AFTER - PERFORM UNTIL tests before each iteration by default; WITH TEST AFTER gives do-while semantics that always run at least once.
  • Loop control is modern - EXIT PERFORM (break) and EXIT PERFORM CYCLE (continue) come from COBOL 2002 and work in GnuCOBOL.
  • PERFORM also calls paragraphs - the same verb invokes named paragraphs as reusable routines, the backbone of COBOL’s procedural organization.
  • Numeric display fields show leading zeros - a PIC 9(3) value of 85 displays as 085; the picture clause controls width, not just storage.

Running Today

All examples can be run using Docker:

docker pull esolang/cobol:latest
Last updated:

Comments

Loading comments...

Leave a Comment

2000 characters remaining