An RPG source from thirty years ago 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.
It was not an oddity: it was how you wrote code when the input unit was an eighty-column punched card, and every wasted character was space taken from data.
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
fully free-form arrived with release 7.1 through a technology refresh, and is
complete from 7.2 onwards. On a system stuck at an earlier release, or updated but
without the PTFs of that period, **FREE does not compile. It is the first thing to
check before proposing a change of style to a department.
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 program with the cycle active, the *INLR indicator is not a detail.
On, it closes the files and cleans up at the end; off, the program "returns" leaving
everything open and variables holding whatever they held — and the next call picks up
from there. Half the inexplicable behaviour in RPG is explained by *INLR not being set
and a shared activation group.
Comments
No comments yet. Be the first to comment!
You need an account to comment. Log in · Sign up