COBOL Textbook

PERFORM VARYING, PERFORM UNTIL, and inline PERFORM in COBOL

Every common COBOL loop in one GnuCOBOL program: PERFORM TIMES, UNTIL with TEST BEFORE and TEST AFTER, inline VARYING over a table, nested AFTER, and EXIT PERFORM.

Published
Updated
Dialect
GnuCOBOL 3.x, free format
Status
Compiled and run with GnuCOBOL 3.2
Length
1,256 words, 6 min

COBOL has one looping statement, PERFORM, and it wears six different shapes. This lesson puts all six in one program: perform a paragraph a fixed number of times, loop until a condition with the test before or after the body, count with VARYING over a table, nest two counters with AFTER, and leave a loop early with EXIT PERFORM. You get a loop table that shows exactly when the condition is tested, which is the part people get wrong.

The program is desk-checked against the GnuCOBOL 3.2 Programmer's Guide (sections 7.8.29.1 to 7.8.29.3 and the EXIT statement in 7.8.16) and IBM's description of the VARYING phrase. The output was first derived by hand, and the derivation is in the loop table; on 29 October 2026 we compiled and ran the program with GnuCOBOL 3.2, and it printed exactly that output. The table it fills uses OCCURS, which the tables lesson covers properly; here it is only a place to put five numbers.

The program

       >>SOURCE FORMAT IS FREE
*> cobol-perform-varying.cob - every common loop shape:
*>   PERFORM n TIMES, UNTIL with TEST BEFORE and TEST AFTER, inline
*>   VARYING over a table, nested VARYING ... AFTER, and EXIT PERFORM
*> Dialect: GnuCOBOL 3.x, free format
*> Status : desk-checked against the GnuCOBOL 3.2 Programmer's Guide;
*>          compiled and run with GnuCOBOL 3.2 on 2026-10-29
*> Inputs : none
*> Outputs: nine lines on standard output
*> Build  : cobc -x cobol-perform-varying.cob
*> Run    : ./cobol-perform-varying
*>          (Windows: cobol-perform-varying.exe)

IDENTIFICATION DIVISION.
PROGRAM-ID. PERFORM-DEMO.

ENVIRONMENT DIVISION.

DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-Counter        PIC 9(2) VALUE 0.
01 WS-Idx            PIC 9(2).
01 WS-Row            PIC 9.
01 WS-Col            PIC 9.
01 WS-Total          PIC 9(4) VALUE 0.
01 WS-Squares.
   05 WS-Square      PIC 9(3) OCCURS 5 TIMES.
01 WS-Grid-Data      PIC X(12) VALUE "ABCDEFGHIJKL".
01 WS-Grid REDEFINES WS-Grid-Data.
   05 WS-Grid-Row OCCURS 3 TIMES.
      10 WS-Cell     PIC X OCCURS 4 TIMES.

PROCEDURE DIVISION.
MAIN-PARAGRAPH.
*>  1. Out-of-line PERFORM, a fixed number of times.
    PERFORM SHOW-TICK 3 TIMES

*>  2. UNTIL with the default TEST BEFORE: condition already true,
*>     so the body never runs.
    MOVE 10 TO WS-Counter
    PERFORM WITH TEST BEFORE UNTIL WS-Counter >= 10
        DISPLAY "test-before body ran"
    END-PERFORM

*>  3. UNTIL with TEST AFTER: the body always runs at least once.
    PERFORM WITH TEST AFTER UNTIL WS-Counter >= 10
        DISPLAY "test-after body ran once"
    END-PERFORM

*>  4. Inline VARYING over a five-element table.
    PERFORM VARYING WS-Idx FROM 1 BY 1 UNTIL WS-Idx > 5
        COMPUTE WS-Square(WS-Idx) = WS-Idx * WS-Idx
        ADD WS-Square(WS-Idx) TO WS-Total
    END-PERFORM
    DISPLAY "Sum of squares 1..5 : " WS-Total
    DISPLAY "Index after the loop: " WS-Idx

*>  5. Nested VARYING ... AFTER over a 3-row by 4-column grid.
    PERFORM VARYING WS-Row FROM 1 BY 1 UNTIL WS-Row > 3
            AFTER   WS-Col FROM 1 BY 1 UNTIL WS-Col > 4
        DISPLAY WS-Cell(WS-Row, WS-Col) WITH NO ADVANCING
    END-PERFORM
    DISPLAY SPACE

*>  6. EXIT PERFORM leaves an inline loop early.
    PERFORM VARYING WS-Idx FROM 1 BY 1 UNTIL WS-Idx > 5
        IF WS-Square(WS-Idx) > 10
            DISPLAY "First square over 10: " WS-Square(WS-Idx)
            EXIT PERFORM
        END-IF
    END-PERFORM
    STOP RUN.

SHOW-TICK.
    ADD 1 TO WS-Counter
    DISPLAY "tick " WS-Counter.
Listing 1.5-1. Lines 1 to 76 of cobol-perform-varying.cob.

Two kinds of PERFORM

The Programmer's Guide separates the procedural (out-of-line) PERFORM, which names a paragraph or section to execute, from the inline PERFORM, whose body sits between PERFORM and END-PERFORM. Both accept the same repetition clauses: TIMES, UNTIL, VARYING, and FOREVER. The one restriction IBM's page adds is that the two forms cannot be mixed in one statement; you either name a procedure or you supply an inline body.

Loop 1 is the procedural form. PERFORM SHOW-TICK 3 TIMES runs the paragraph SHOW-TICK, which sits after STOP RUN at the bottom of the program, three times and then continues with the next statement. A paragraph is any labelled block of sentences, and control returns to the statement after the PERFORM when the paragraph's last statement is done. The guide notes that changing the TIMES operand inside the loop has no effect, because the count is copied into an internal counter when the loop starts.

When the condition is tested

Loops 2 and 3 exist to settle the question that causes off-by-one bugs. PERFORM UNTIL condition tests the condition before each pass by default; the guide calls this WITH TEST BEFORE and says that it is assumed when the clause is absent. IBM's VARYING page says the same for the VARYING form: "If neither the TEST BEFORE nor the TEST AFTER phrase is specified, the TEST BEFORE phrase is assumed." With WITH TEST AFTER, the body runs first and the condition is tested afterwards, so the body runs at least once.

The program sets WS-Counter to 10 and then runs both loops with the condition WS-Counter >= 10, which is already true. Loop 2 tests first, finds the condition true, and never runs its body; nothing is displayed. Loop 3 runs its body once, displays its line, then tests and stops. The choice matters most for a read loop: a file-processing loop that reads before testing for end of file needs TEST BEFORE semantics with the first read done ahead of the loop, or TEST AFTER with a read inside; the sequential file lesson shows the standard shape.

VARYING, step by step

Loop 4 is the workhorse. PERFORM VARYING WS-Idx FROM 1 BY 1 UNTIL WS-Idx > 5 initialises WS-Idx to 1, then repeats: test the condition; if false, run the body; add the BY value; test again. The guide lists this sequence explicitly, and the loop table below follows it for every pass. Note the > in the condition rather than =. The guide remarks that most programmers use > with TEST BEFORE out of habit, and the habit has a reason: UNTIL WS-Idx = 5 would stop before processing element 5, because the test happens before the body.

Table 1.5-1
Pass WS-Idx at the test WS-Idx > 5 Body runs Square stored Running total
1 1 false yes 1 1
2 2 false yes 4 5
3 3 false yes 9 14
4 4 false yes 16 30
5 5 false yes 25 55
6 6 true no 55

After the loop, WS-Idx is 6, not 5: the counter is incremented once more before the test that ends the loop. That is why the program displays the index afterwards. Code that reuses the counter after a loop as if it still pointed at the last element is a classic bug, on any compiler.

Loop 5 adds an AFTER clause, which nests a second counter inside the first. For each value of WS-Row, WS-Col runs through its whole range, and only then does WS-Row advance. The guide's own example uses a 3-row by 4-column grid of letters, and this program borrows it: with TEST BEFORE and > in both conditions all twelve cells are visited in row order, A through L. The guide also shows what happens if you write = 3 and = 4 with TEST BEFORE: the last row and last column are skipped and only ABCEFG appears. IBM's reference notes that the AFTER form can be nested to walk a seven-dimensional table; two dimensions is as far as this site goes.

Loop 6 shows EXIT PERFORM, which the guide documents under the EXIT statement. Inside an inline PERFORM it ends the loop immediately, and control continues after END-PERFORM. Its sibling EXIT PERFORM CYCLE ends only the current pass and lets the VARYING clause carry on. Both belong to the inline form; in a procedural PERFORM the equivalent is EXIT PARAGRAPH. The loop here finds the first square greater than 10, which is 16 at index 4, and leaves before looking at index 5.

Compile and run

cobc -x cobol-perform-varying.cob
./cobol-perform-varying

The first program lesson explains the >>SOURCE FORMAT IS FREE line and the -x option.

Worked example

The output, derived by hand from the loop table and the guide's grid example, and identical to what the compiled program printed:

tick 01
tick 02
tick 03
test-after body ran once
Sum of squares 1..5 : 0055
Index after the loop: 06
ABCDEFGHIJKL
First square over 10: 016

Nine lines were promised in the header and eight are visible; the ninth is the DISPLAY SPACE that ends the grid line, which prints a single space and a line ending. The leading zeros follow from the pictures: WS-Counter is PIC 9(2), WS-Total is PIC 9(4), and WS-Square is PIC 9(3).

Limitations

The program was run on GnuCOBOL 3.2 only. The grid output relies on the guide's documented ordering for VARYING ... AFTER, which we have reproduced with the > form of the conditions precisely because the guide shows that form giving all twelve cells. Two shapes are left out on purpose. PERFORM FOREVER (or UNTIL EXIT) with an EXIT PERFORM inside is common in read loops but adds nothing to the table above. And PERFORM paragraph-a THRU paragraph-b, which runs a range of paragraphs, is everywhere in older code and is worth knowing, but it depends on paragraph order in the source and is easy to break during maintenance, so this site's listings use single-paragraph performs and inline loops.

Correction, 29 October 2026: the build command shown before named the source cobol-perform-varying-until-inline.cob, and GnuCOBOL 3.2's cobc refuses a source file whose base name is longer than 31 characters ("invalid file base name ... length exceeds maximum"), even with -o, so it failed as printed. The listing is now shipped as cobol-perform-varying.cob, and the old download address redirects there. The same day we compiled and ran the program with GnuCOBOL 3.2, and its output matched the output shown here line for line, so the page now says that instead of calling the listing desk-checked only.

Index terms cobol, gnucobol, perform varying, perform until, loops