Free-Format Basics
Modern RPGLE looks almost nothing like the fixed-column RPG from old manuals. Since IBM i 7.1 TR7, the language has supported fully free-format source from column 1 — and that’s the only style you should write today.
This chapter covers the raw mechanics: how to declare variables, what types exist, and how control flow works.
The **free directive
Every modern RPG source file starts with this line:
**free
Two asterisks, the word free, nothing else on the line, no leading spaces. This tells the compiler: everything that follows is fully free-format. Leave it off and the compiler assumes fixed-format — a hard-to-diagnose mistake.
Variable declarations
Use dcl-s (“declare standalone”) for individual variables:
dcl-s orderNumber packed(7:0);
dcl-s customerName varchar(50);
dcl-s orderDate date;
dcl-s isActive ind;
dcl-s unitPrice zoned(9:2);
dcl-s lineCount int(10);
Each declaration ends with a semicolon. Names are case-insensitive to the compiler but conventionally use camelCase or snake_case — pick one and stay consistent.
Data types you’ll actually use
| Type | Example | Use |
|---|---|---|
char(n) |
char(10) |
Fixed-length character. Pads with spaces. |
varchar(n) |
varchar(50) |
Variable-length character, up to 16 MB. |
packed(n:d) |
packed(9:2) |
Packed decimal, n digits total, d after decimal. Most common numeric type. |
zoned(n:d) |
zoned(7:0) |
Zoned decimal. Legacy but still seen. |
int(10) |
int(10) |
Integer. Sizes: 3 (1 byte), 5 (2 bytes), 10 (4 bytes), 20 (8 bytes). |
date, time, timestamp |
date |
Temporal types — the compiler enforces type safety on date math (see Chapter 7). |
ind |
ind |
Boolean. Values are *on and *off. |
For most business code, packed(n:d) for numbers and varchar(n) for text are the defaults. Use char(n) when you’re matching a fixed-length field defined in the database.
Assignment and arithmetic
orderNumber = 12345;
customerName = 'Acme Distributors';
unitPrice = 29.99;
orderDate = %date();
orderNumber += 1; // same as orderNumber = orderNumber + 1
unitPrice *= 1.10; // 10% markup
All the operators you’d expect: +, -, *, /, ** for exponentiation, and the compound +=, -=, *=, /=.
Control flow
if / elseif / else
if qtyOnHand < reorderPoint;
placeOrder = *on;
elseif qtyOnHand < safetyStock;
alertBuyer = *on;
else;
// no action
endif;
Each clause ends with a semicolon. Close the whole construct with endif;.
for loop
dcl-s i int(10);
for i = 1 to 10;
totalValue += lineAmount(i);
endfor;
Also supports downto, by n for step increments, and to *blanks constructs.
dow — do while
dow not %eof(INVENTORY);
read INVENTORY;
if %eof(INVENTORY); leave; endif;
// process record
enddo;
leave exits the loop immediately. iter skips to the next iteration.
dou — do until
dou response = 'Y' or response = 'N';
dsply 'Continue (Y/N)?' '' response;
enddo;
Same as dow, but the condition is checked at the bottom of the loop instead of the top.
select / when
select;
when status = 'A';
processActive();
when status = 'P';
processPending();
when status = 'C' or status = 'X';
processClosed();
other;
processUnknown();
endsl;
Close with endsl; (not endselect). The other clause is the default case.
Comments
// Single-line comment
dcl-s x int(10); // Inline comment
/*
Block comment.
Multiple lines.
*/
Both styles are supported. Use them liberally — future you is also a reader.
Case convention
RPG is case-insensitive to the compiler, but editors and linters have opinions. A widespread convention:
- Lowercase for keywords (
dcl-s,if,endif,exec sql) - Lowercase for BIFs (
%trim,%date,%eof) - camelCase or snake_case for your own variables and procedures
- UPPERCASE for table and field names (DB2 returns them uppercase)
The RPGLE Linter can enforce whatever convention your team picks.
What you can do now
Declare variables of every common type, assign values, do arithmetic, and write programs with conditional logic and loops. That’s the foundation — the rest of Part 2 builds on it.
Next: Chapter 2: Data Structures.