A fixed-format RPG source looks unreadable because the position of a character matters. Every line starts with a letter declaring its type, and every piece of information has its column: what a line does is read from where things sit, not from how they are written.
The specifications
The line type is declared in column 6, and the letters still turn up today when reading historic code:
| Letter | What it declares |
|---|---|
| H | program compile options |
| F | the files used |
| D | variables, structures, constants, prototypes |
| I | how incoming records are laid out |
| C | the statements |
| O | how outgoing records are laid out |
| P | the start and end of a procedure |
I and O specifications are rare today: they described record layouts by hand, something the compiler works out on its own from the file's external description.
What changed
Free-form did not arrive all at once, and that explains why you find three different forms of the same language out there.
First the statements became free, with /FREE and /END-FREE delimiting blocks:
calculations were written freely, while files and variables stayed in columns. It is the
transitional form, and on many systems it is the prevailing one.
Then everything became free. With **FREE on the first line, the source has no columns
to respect: ctl-opt instead of the H specification, dcl-f for files, dcl-ds and
dcl-s for data, dcl-proc for procedures.
**FREE
ctl-opt dftactgrp(*no) actgrp('INVOICES') option(*srcstmt: *nodebugio);
dcl-f orders disk keyed usage(*input);
dcl-s total packed(11:2);
dcl-s rate packed(5:2) inz(22);
dcl-ds customer qualified;
code char(10);
name char(50);
balance packed(11:2);
end-ds;
dcl-proc calcNetAmount;
dcl-pi *n packed(11:2);
amount packed(11:2) const;
pct packed(5:2) const;
end-pi;
return amount - (amount * pct / 100);
end-proc;
Note
in IBM's ILE RPG manual the free-form CTL-OPT, DCL-F, DCL-S, DCL-DS,
DCL-PR, DCL-PI and DCL-PROC statements are among the 7.2 enhancements, and
**FREE on the first line, with no column limits at all, among the 7.3 ones. Before
proposing a change of style to a department, check that your system's compiler accepts
them: a test source with **FREE that compiles settles it.
What did not change
Free-form changed how you write, not what the language does. The RPG cycle, indicators, level breaks, record-at-a-time reading: they are all still there, and a program written in free-form can use them exactly as before.
This matters because it disposes of a common misunderstanding in both directions. Converting a program to free-form does not modernise it: it makes it readable. And an old but well-written program does not become better for being rewritten in the new syntax: it becomes a program to test all over again, with the same behaviour as before and one extra risk.
Tip
conversion makes sense when you are going into a program anyway, not as a project in its own right. A program nobody has touched in ten years and which works is the last candidate: the benefit is nil while nobody reads it, and the risk is real.
There is a tool on the site for automatic conversion, useful for seeing how a source would come out before deciding:
What is written differently today
Three habits separate a recently written program from one of twenty years ago, and all three can be adopted gradually.
Variables are declared where they are used. In the old model every variable sat together at the top. In free-form a declaration can live inside a procedure, which means a variable has a scope, and two procedures cannot tread on each other by accident.
Indicators are used less. *IN71 set somewhere and read three hundred lines later is
why an RPG program becomes incomprehensible. Conditions today are written with named
variables, and indicators stay where the language requires them: display files, for
instance.
Built-in functions replaced operations. %scan, %subst, %trim, %date, %diff,
%editc do in one line what used to need dedicated operations and working variables.
The RPG cycle
It deserves a paragraph because it is what surprises people from other languages most.
Classic RPG has no main you read top to bottom: it has an implicit cycle, which reads
a record from the primary file, runs the calculations, writes the output, and starts again
until the file ends. The program describes what to do with each record; the language is
what calls it.
On historic code this has to be understood in order to read it. On new code it is no longer
used: you declare dcl-f with no primary file and write an explicit loop anyone can
follow.
Warning
in a cycle-main procedure, the *INLR indicator is not a detail. IBM's
manual describes RETURN like this: with LR on the program ends normally; with LR off it
returns to the caller, data is preserved for the next call, and files and data areas
are not written out. On the next call, variables still hold their previous values.
Comments
No comments yet. Be the first to comment!
You need an account to comment. Log in · Sign up