This page is a lookup for the RPGLE opcodes that decide which statements run and how often: the conditions, the select group, the loops, the loop exits and the monitor group. For each one it gives the free-form syntax, how it behaves, and a short example.
It is not a tutorial. Each section links to the lesson that teaches the topic, and the full list of opcodes by family is in the RPGLE Opcode Reference. File-reading loops use the operations in the RPGLE File I/O Opcodes Reference.
All examples are free-form RPGLE.
Control-flow opcodes at a glance
| Opcode | Free-form syntax | Use it to |
|---|---|---|
if | if condition; … endif; | Run statements when a condition is true |
elseif, else | elseif condition; and else; | Add alternative branches to an if |
select | select; or select expression; | Start a group of mutually exclusive choices |
when | when condition; | Run one branch of a select group |
when-is, when-in | when-is value;, when-in list; | Compare the select expression to a value or a list |
other | other; | Run when no when was true |
dow | dow condition; … enddo; | Repeat while a condition is true, testing first |
dou | dou condition; … enddo; | Repeat until a condition is true, testing last |
for | for index = start {by n} to or downto limit; | Repeat a counted number of times |
for-each | for-each item in array; … endfor; | Visit each item of an array, %list, %split, %subarr or enumeration |
leave | leave; | Exit the innermost loop |
iter | iter; | Skip to the next pass of the innermost loop |
return | return {expression}; | Return to the caller |
monitor | monitor; … on-error ...; … endmon; | Handle run-time errors in a block |
A condition is an expression that gives true or false, and it can use and, or and not. end can replace any specific end statement such as endif or enddo, but the specific form is easier to read.
IF, ELSEIF and ELSE
if runs its statements when the condition is true. elseif tests another condition only when the earlier ones were false, and else runs when none was true. Only one branch runs.
dcl-s qty int(10) inz(75);
dcl-s tier char(6);
if qty >= 100;
tier = 'LARGE';
elseif qty >= 50;
tier = 'MEDIUM';
else;
tier = 'SMALL';
endif;
Lesson: RPGLE IF, ELSE and ELSEIF Decisions.
SELECT, WHEN and OTHER
A select group runs the first when whose condition is true, then continues after endsl. If no when is true, the other block runs, if there is one. Without an other block, nothing runs.
dcl-s code char(1) inz('B');
dcl-s label char(10);
select;
when code = 'A';
label = 'ACTIVE';
when code = 'B' or code = 'C';
label = 'BLOCKED';
other;
label = 'UNKNOWN';
endsl;
Give select an expression to compare it with values. when-is tests for equality, and when-in tests membership in a list. The expression is evaluated once, when select starts.
dcl-s role char(10) inz('MANAGER');
dcl-s level int(10);
select role;
when-is 'OWNER';
level = 3;
when-is 'MANAGER';
level = 2;
when-in %list('REGULAR' : 'NEW');
level = 1;
other;
level = 0;
endsl;
Lesson: RPGLE SELECT and WHEN Logic.
DOW and DOU
dow tests its condition before each pass, so the body can run zero times. dou tests after each pass, so the body always runs at least once.
dcl-s n int(10) inz(0);
dow n < 3;
n += 1;
enddo;
dcl-s tries int(10) inz(0);
dou tries >= 3;
tries += 1;
enddo;
The condition in each case is for staying in the loop (dow) or for leaving it (dou). Whatever the loop tests must change inside the body, or the loop never ends. Lessons: RPGLE DOW Loops and RPGLE DOU Loops.
FOR
for counts. It sets the index to the start value, tests it against the limit, runs the body, then adds the increment, which is 1 unless you give by. to counts up and downto counts down. The by and to clauses can be written in either order.
dcl-s i int(10);
dcl-s total int(10) inz(0);
for i = 1 to 5;
total += i; // 15 at the end
endfor;
for i = 10 downto 0 by 5;
total += i; // 10, 5, then 0
endfor;
Declare the index separately, because for cannot declare it. It must be large enough to hold one more than the limit, since the loop ends when the index passes the limit. The limit is evaluated on every pass, so put an expensive expression in a variable first. Lesson: RPGLE DO Loops.
FOR-EACH
for-each visits every item of an array, a %list, a %split, a %subarr or an enumeration. Each item is assigned to the variable named first, and the group ends with endfor. The variable must have a type compatible with the items, and it cannot be an array.
dcl-s names char(8) dim(3);
dcl-s name char(8);
dcl-s seen int(10) inz(0);
names(1) = 'Ann';
names(2) = 'Bob';
names(3) = 'Cy';
for-each name in names;
seen += 1; // 3 passes
endfor;
Use for instead when you need the index.
LEAVE and ITER
Both act on the innermost loop only. leave jumps to the statement after the loop’s end. iter jumps to the end statement of the loop, so the next pass starts at once. In a for loop, iter still adds the increment, but leave does not.
dcl-s i int(10);
dcl-s sum int(10) inz(0);
for i = 1 to 10;
if i = 3;
iter; // skip 3
endif;
if i = 6;
leave; // stop before 6
endif;
sum += i; // 1 + 2 + 4 + 5 = 12
endfor;
leave and iter are not allowed outside a loop. Lesson: RPGLE LEAVE, ITER and Loop Control.
RETURN
return ends the current program or procedure and goes back to the caller. In a procedure that returns a value, return gives the value, and the procedure must reach a return before its end, or an exception is signalled to the caller.
dcl-proc doubled;
dcl-pi *n int(10);
value int(10) const;
end-pi;
return value * 2;
end-proc;
In a cycle-main program, return ends the program normally only when *inlr is on. Otherwise it returns to the caller and keeps its data for the next call.
MONITOR and ON-ERROR
monitor starts a group whose run-time errors are handled by on-error blocks that follow it. The first matching on-error runs, and the program continues after endmon. With no list, on-error handles all program and file errors. You can also name a status code, *program, *file or *all.
dcl-s text varchar(10) inz('Hi');
dcl-s part varchar(10);
monitor;
part = %subst(text : 1 : 5);
on-error;
part = '*ERR';
endmon;
Put specific status codes before *all, because only the first matching block is used. Errors inside an on-error block are not handled by that monitor. Lesson: Error Handling in RPG.
Which one to use
| Situation | Use |
|---|---|
| One test, one or two outcomes | if, with else |
| Several exclusive conditions | select with when |
| One value compared with many | select with when-is or when-in |
| Repeat while something is true, possibly never | dow |
| Repeat at least once, until something is true | dou |
| A known number of passes, or a counter | for |
| Every item of an array or list | for-each |
| Stop a loop early | leave |
| Skip the rest of one pass | iter |
| Recover from a run-time error | monitor and on-error |
Common mistakes
- Using
dowwhen the body must run once, ordouwhen it must be able to run zero times. - A loop whose condition never changes, so it never ends.
- Expecting
leaveto exit all nested loops. It exits one level. - A
forindex that is too small to hold the limit plus one. - Several
whenconditions that overlap, where only the first one runs. - Putting
*allbefore a specific status code in a monitor group.
FAQ
What is the difference between DOW and DOU?
dow checks the condition first and may not run at all. dou checks it at the end, so the body runs at least once.
Does SELECT run every true WHEN?
No. It runs the first true when and then continues after endsl.
Is there a FOR EACH loop in RPGLE?
Yes. for-each item in array; visits each item of an array, %list, %split, %subarr or enumeration, as shown above. Use for when you need the index.
Summary
- Use
ifandselectto choose,dow,dou,forandfor-eachto repeat,leaveanditerto change a loop,returnto go back to the caller, andmonitorto handle errors. dowmay run zero times anddoualways runs once.leaveanditeraffect only the innermost loop.- For the whole set of opcodes by family, see the RPGLE Opcode Reference.