Beginner

Control Flow in PL/I

Learn conditionals, SELECT groups, and the many forms of the DO loop in PL/I with runnable, Docker-ready examples using the Iron Spring compiler

Control flow is how a program decides what to do and how many times to do it. PL/I, designed in 1964 as IBM’s “one language to rule them all,” was one of the earliest mainstream languages to embrace structured programming — block-structured conditionals and loops with explicit END terminators, rather than the tangle of GOTO statements common in its FORTRAN and assembly contemporaries.

As an imperative, procedural, and structured language, PL/I gives you a rich set of control constructs: the familiar IF/THEN/ELSE, the multi-way SELECT group (a switch/case ancestor), and a remarkably versatile DO statement that handles counted loops, conditional loops, and post-tested loops all with one keyword. PL/I’s strong, static typing means the compiler checks your comparisons and loop bounds at compile time.

In this tutorial you’ll learn how to branch with IF and SELECT, iterate with every flavor of DO, and control loops early with LEAVE. Every example is a complete, runnable program you can compile with the Iron Spring PL/I compiler inside Docker.

Conditionals: IF / THEN / ELSE

The IF statement evaluates a condition and runs the statement after THEN when it’s true. Add ELSE for the false branch, and chain ELSE IF for multi-way decisions. To run more than one statement in a branch, wrap the statements in a DO; ... END; group.

PL/I uses & for logical AND, | for logical OR, and ^= for “not equal” (the ^ is PL/I’s NOT operator).

Create a file named conditionals.pli:

CONDITIONALS: PROCEDURE OPTIONS(MAIN);
   DECLARE SCORE FIXED BINARY(15);

   SCORE = 85;

   /* Multi-way branch with chained ELSE IF */
   IF SCORE >= 90 THEN
      PUT LIST('Grade: A');
   ELSE IF SCORE >= 80 THEN
      PUT LIST('Grade: B');
   ELSE IF SCORE >= 70 THEN
      PUT LIST('Grade: C');
   ELSE
      PUT LIST('Grade: F');

   /* Compound condition runs a DO group of several statements */
   IF SCORE >= 80 & SCORE < 90 THEN
      DO;
         PUT SKIP LIST('You passed with a solid grade.');
         PUT SKIP LIST('Keep up the good work!');
      END;

END CONDITIONALS;

Note that a bare IF ... THEN statement; runs only the single statement that follows THEN. The DO; ... END; group is how PL/I lets a branch control a block of statements — the same idea as { } braces in C, which PL/I predates by nearly a decade.

Multi-Way Branching: SELECT

When you need to choose among many discrete cases, the SELECT group is cleaner than a long IF/ELSE IF chain. PL/I offers two forms:

  • SELECT (expression); — compares the control expression against each WHEN value.
  • SELECT; — evaluates each WHEN as an independent boolean condition (handy for ranges).

OTHERWISE catches anything not matched, like default in a C switch. Unlike C, PL/I’s SELECT does not fall through — exactly one WHEN (or OTHERWISE) runs.

Create a file named select.pli:

SELECTDEMO: PROCEDURE OPTIONS(MAIN);
   DECLARE DAY FIXED BINARY(15);

   /* Form 1: match the control expression against WHEN values */
   DAY = 3;
   SELECT (DAY);
      WHEN (1) PUT LIST('Monday');
      WHEN (2) PUT LIST('Tuesday');
      WHEN (3) PUT LIST('Wednesday');
      WHEN (4) PUT LIST('Thursday');
      WHEN (5) PUT LIST('Friday');
      OTHERWISE PUT LIST('Weekend');
   END;

   /* Form 2: each WHEN is a full boolean condition */
   DAY = 7;
   SELECT;
      WHEN (DAY >= 1 & DAY <= 5) PUT SKIP LIST('It is a weekday.');
      WHEN (DAY = 6 | DAY = 7)   PUT SKIP LIST('It is the weekend.');
      OTHERWISE PUT SKIP LIST('Invalid day.');
   END;

END SELECTDEMO;

Counted Loops: The DO Statement

The DO statement is PL/I’s workhorse iterator. The counted form DO var = start TO end; runs the loop body once for each value, automatically incrementing the control variable. Add BY step to change the increment (including negative steps to count down).

Create a file named loops.pli:

LOOPS: PROCEDURE OPTIONS(MAIN);
   DECLARE I     FIXED BINARY(15);
   DECLARE TOTAL FIXED BINARY(31);

   /* Simple counted loop, 1 through 5 */
   PUT LIST('Counting 1 to 5:');
   DO I = 1 TO 5;
      PUT SKIP EDIT('  Count = ', I) (A, F(1));
   END;

   /* Counted loop with a step using BY */
   PUT SKIP LIST('Even numbers 2 to 10:');
   DO I = 2 TO 10 BY 2;
      PUT SKIP EDIT('  Even = ', I) (A, F(2));
   END;

   /* Accumulate a running total across iterations */
   TOTAL = 0;
   DO I = 1 TO 100;
      TOTAL = TOTAL + I;
   END;
   PUT SKIP EDIT('Sum of 1 to 100 = ', TOTAL) (A, F(4));

END LOOPS;

Here we use PUT EDIT instead of PUT LIST for the numbers. The format item F(w) writes an integer right-justified in a field of width w, giving precise, predictable column alignment — F(2) prints 10 as 10 and 2 as 2.

Conditional Loops: DO WHILE and DO UNTIL

Not every loop has a known count. PL/I provides two condition-controlled forms:

  • DO WHILE (cond); — tests the condition before each iteration. The body may run zero times.
  • DO UNTIL (cond); — tests the condition after each iteration. The body always runs at least once.

Create a file named whileloops.pli:

WHILELOOPS: PROCEDURE OPTIONS(MAIN);
   DECLARE N FIXED BINARY(15);

   /* DO WHILE tests the condition BEFORE each pass */
   PUT LIST('Countdown with DO WHILE:');
   N = 5;
   DO WHILE (N > 0);
      PUT SKIP EDIT('  N = ', N) (A, F(1));
      N = N - 1;
   END;

   /* DO UNTIL tests the condition AFTER each pass */
   PUT SKIP LIST('Doubling with DO UNTIL:');
   N = 1;
   DO UNTIL (N > 50);
      PUT SKIP EDIT('  N = ', N) (A, F(2));
      N = N * 2;
   END;

END WHILELOOPS;

Early Loop Exit: LEAVE

PL/I’s LEAVE statement immediately exits the enclosing DO loop — the equivalent of break in C-family languages. To conditionally skip work within an iteration, the structured approach is to guard the work with an IF test (PL/I’s built-in MOD function returns the remainder of a division).

Create a file named loopctl.pli:

LOOPCTL: PROCEDURE OPTIONS(MAIN);
   DECLARE I FIXED BINARY(15);

   /* Guard with IF to act only on odd numbers */
   PUT LIST('Odd numbers from 1 to 10:');
   DO I = 1 TO 10;
      IF MOD(I, 2) ^= 0 THEN
         PUT SKIP EDIT('  Odd = ', I) (A, F(2));
   END;

   /* LEAVE exits the loop the moment a match is found */
   PUT SKIP LIST('First multiple of 7:');
   DO I = 1 TO 100;
      IF MOD(I, 7) = 0 THEN
         DO;
            PUT SKIP EDIT('  Found ', I) (A, F(2));
            LEAVE;
         END;
   END;

END LOOPCTL;

Running with Docker

These examples use the custom codearchaeology/pli:latest image with the Iron Spring PL/I compiler. The plicc wrapper compiles and links in one step, producing an executable named after the source file (loops.pli./loops). The --security-opt seccomp=unconfined flag is required for the 32-bit compiler on Docker Desktop.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# Pull the PL/I compiler image
docker pull codearchaeology/pli:latest

# Compile and run each example
docker run --rm --security-opt seccomp=unconfined -v $(pwd):/app -w /app codearchaeology/pli:latest sh -c 'plicc conditionals.pli && ./conditionals'

docker run --rm --security-opt seccomp=unconfined -v $(pwd):/app -w /app codearchaeology/pli:latest sh -c 'plicc select.pli && ./select'

docker run --rm --security-opt seccomp=unconfined -v $(pwd):/app -w /app codearchaeology/pli:latest sh -c 'plicc loops.pli && ./loops'

docker run --rm --security-opt seccomp=unconfined -v $(pwd):/app -w /app codearchaeology/pli:latest sh -c 'plicc whileloops.pli && ./whileloops'

docker run --rm --security-opt seccomp=unconfined -v $(pwd):/app -w /app codearchaeology/pli:latest sh -c 'plicc loopctl.pli && ./loopctl'

Expected Output

conditionals.pli (SCORE is 85, so the B branch and the compound block both run):

Grade: B
You passed with a solid grade.
Keep up the good work!

select.pli (DAY is 3, then 7):

Wednesday
It is the weekend.

loops.pli:

Counting 1 to 5:
  Count = 1
  Count = 2
  Count = 3
  Count = 4
  Count = 5
Even numbers 2 to 10:
  Even =  2
  Even =  4
  Even =  6
  Even =  8
  Even = 10
Sum of 1 to 100 = 5050

whileloops.pli:

Countdown with DO WHILE:
  N = 5
  N = 4
  N = 3
  N = 2
  N = 1
Doubling with DO UNTIL:
  N =  1
  N =  2
  N =  4
  N =  8
  N = 16
  N = 32

loopctl.pli:

Odd numbers from 1 to 10:
  Odd =  1
  Odd =  3
  Odd =  5
  Odd =  7
  Odd =  9
First multiple of 7:
  Found  7

Key Concepts

  • IF/THEN/ELSE controls one statement by default — wrap multiple statements in a DO; ... END; group to make a branch govern a block.
  • Logical operators are & (AND), | (OR), and ^ (NOT) — so “not equal” is written ^=. These work in IF and WHEN conditions alike.
  • SELECT is PL/I’s switch/case ancestor and comes in two forms: SELECT (expr) matches values, while bare SELECT evaluates each WHEN as a boolean condition. There is no fall-through — exactly one branch runs.
  • One DO keyword, many loop shapes: counted (DO I = 1 TO 10 BY 2), pre-tested (DO WHILE), and post-tested (DO UNTIL). WHILE may run zero times; UNTIL always runs at least once.
  • LEAVE exits a loop early, like break — and the structured way to skip an iteration is a guarding IF rather than a jump.
  • Every block construct ends with an explicit END, a hallmark of PL/I’s early commitment to structured programming over GOTO.
  • PUT EDIT with an F(w) format gives precise, column-aligned numeric output, while PUT SKIP starts a fresh line before writing.

Running Today

All examples can be run using Docker:

docker pull codearchaeology/pli:latest
Last updated:

Comments

Loading comments...

Leave a Comment

2000 characters remaining