PL/I - Language features 2
Let’s continue our look at the PL/I language.
Storage classes
There are four storage classes in PL/I. AUTOMATIC
(the default) and STATIC
are much like in C:
CONTROLLED
variables are assigned space dynamically with ALLOCATE
and released with FREE
. Unlike C malloc
/free
, allocations are stacked so you can reallocate space and the program will remember the last allocation after it is freed. For example, the below will print 20 then 10.
TEST: PROCEDURE OPTIONS(MAIN);
DECLARE A FIXED BINARY(16) CONTROLLED;
ALLOCATE A;
A = 10;
ALLOCATE A;
A = 20;
PUT FILE (SCARDS) LIST(A);
FREE A;
PUT FILE (SCARDS) LIST(A);
FREE A;
END TEST;
Use of controlled variables before they are allocated or after they are freed will lead to a runtime error.
BASED
variables are similar to CONTROLLED
variables but also allow pointer style access.
Variables (and procedures) can also be declared EXTERNAL
if they are defined elsewhere in the program.
Procedures and functions
Procedures can be declared at file level, within other procedures, or even within DO blocks. They are introduced with PROCEDURE
and executed using CALL
. Parameters are passed by name so the procedure can alter them; you can also pass an expression and the compiler will introduce a temporary variable. The below will print 41.
TEST: PROCEDURE OPTIONS(MAIN);
DECLARE A FIXED BINARY(16);
A = 41;
CALL INCR(A);
CALL INCR(A+1);
PUT FILE (SCARDS) LIST(A);
INCR: PROCEDURE(X);
DECLARE X FIXED BINARY(16);
X = X + 1;
END INCR;
END TEST;
A function is a procedure that returns a value, so the above could also be written as:
TEST: PROCEDURE OPTIONS(MAIN);
DECLARE A FIXED BINARY(16);
A = 41;
A = INCR(A);
PUT FILE (SCARDS) LIST(A);
INCR: PROCEDURE(X) RETURNS(FIXED BINARY(16));
DECLARE X FIXED BINARY(16);
RETURN(X + 1);
END INCR;
END TEST;
Note that brackets are needed around the expression being returned.
Preprocessor
The source file can be augmented at compile time by using preprocessor directives, which start with %
. On MTS, to use this facility you must set the MACRO
parameter in the compiler command (eg $run *pl1f scards=in.pl1 spunch=-load par=macro
).
%INCLUDE otherfile
copies the contents of otherfile
into the file currently being compiled.
%DECLARE
can be used for macro replacement. %DEACTIVATE
will undefine a macro variable allowing it to be used by the program again. The following will print 42.
%DECLARE A CHARACTER, B FIXED;
%A = 'B + 2';
%B = 40;
TEST: PROCEDURE OPTIONS(MAIN);
DECLARE X FIXED BINARY(16);
X = A;
PUT FILE (SCARDS) LIST(A);
END TEST;
So far so much like the C preprocessor. But you can also do compile time programming using labels, %IF
and %GOTO
. The below will substitute the middle section with X = X + 1;
, X = X + 2;
etc and prints 10.
TEST: PROCEDURE OPTIONS(MAIN);
DECLARE X FIXED BINARY(16);
X = 0;
%DECLARE N FIXED;
%N = 1;
%LABEL1:;
X = X + N;
%N = N + 1;
%IF N < 5 %THEN %GO TO LABEL1;
PUT FILE (SCARDS) LIST(X);
END TEST;
There is also a %DO
loop and the ability to create compile time %PROCEDURES
.
Further information
There’s a lot more we haven’t covered. PL/I has extensive I/O operations in both stream and record formats. There is support for multi-tasking, although much of this is not available on MTS.
There is surprisingly little on the internet about PL/I, outside of IBM. There’s an (opinionated) comparison between C and PL/I. The experience Multics had using PL/I is also interesting.