Software Engineers Handbook/Language Dictionary/PLI
- 1 PL/I
- 1.1 Type
- 1.2 Execution Entry Point
- 1.3 Column specialties
- 1.4 General Syntax
- 1.5 Comments
- 1.6 Variable Declarations
- 1.7 Output Statements
- 1.8 Method Declaration/Implementation
- 1.9 Scope
- 1.10 Conditional Statements: IF..THEN..ELSE
- 1.11 Multi-branch Conditionals: SELECT
- 1.12 Looping Statements
- 1.13 Boolean Values
- 1.14 Error Handling/Recovery
- 1.15 Containers
- 1.16 Algorithms
- 1.17 Garbage collection
- 1.18 Physical Structure
- 1.19 Tips
- 1.20 Web References
- 1.21 Books and Articles
- 1.22 See also
Here is the Wikipedia entry.
PL/I is a full procedural language.
Execution Entry Point
The program has its entry point specified as
OPTIONS(MAIN), in a procedure statement, as in:
tree: procedure options (main);
There are no column specialities.
An assignment example:
a = b*(c-d);
In this numeric assignment statement,
d is subtracted from
c, and the result multiplied by the value of
b, before storing in variable
s = t || u;
In this example string assignment, string
u is joined to the end of string
t, and the composite string is stored in string variable
Note: Using a compile-time option the concatenation operator
|| may be replaced by another pair of symbols, for being compatible with existing PL/I programs often
!! is used.
Every comment commences with the pair
/* and terminates with the pair
*/. Comments may span lines.
/* * this is a long comment. */
Declarations are not compulsory, though most PL/I compilers provide a compiler option to force declarations, while some of the subset compilers insist that every variable be declared.
i as an binary:
declare i fixed binary;
i as an binary and give it an initial value of 0
declare i fixed binary initial (0);
To declare a variable as decimal,
declare D fixed decimal; declare E fixed decimal (10,4);
The first declaration is of an integer
D stored in decimal format, while the second is of a variable E capable of storing ten digits having 4 digits after the decimal point. To initialize
declare E fixed decimal (10,4) initial (123456.7891);
Note that values such as 0.1 are stored exactly.
To define a floating-point variable:
declare f float;
that uses single precision hardware; and double precision:
declare float decimal (15);
and extended precision:
declare float decimal (18);
(typical declarations for the PC). The numbers 15 and 18 indicate the number of decimal digits that will be stored. (Although the values are actually held in binary, the decimal precisions are more convenient to recall than specifying the precisions in binary.) To specify a complex variable, simply include the word
COMPLEX in the declaration, thus:
DECLARE C FLOAT DECIMAL (15) COMPLEX; C = 5.2+8i;
The assignment includes the complex constant 5.2+8i where 5.2 is the real part and 8 is the imaginary part.
The arithmetic data attributes for base (binary or decimal), scale (fixed or float) and mode (real or complex) may appear in any order, the attribute for precision (e.g.
(15)) must not be the first one.
The following definitions are equivalent, except for the last one.
declare number fixed binary (15) real; declare number binary fixed (15) real; declare number binary fixed real (15); declare number binary fixed (15); /* because real is the default value of mode */ declare number (15) binary fixed real; /* defines 15 numbers with default precision */
To declare a variable having the picture attribute:
declare p picture ('9999v.999');
P as having as total of 7 decimal digits, with three digits after the decimal point. The decimal point is held in storage. The letter
V indicates the position of the actual decimal point. For Europe, the specification may include a comma:
declare p picture ('9999v,999');
To declare variable
S as a character string:
declare S character(10);
S can store exactly 10 characters.
To declare variable
V as a character string capable of storing up to 100 characters:
declare V character (100) varying;
A simple assignment:
V holds the three characters
V = 'friendly';
V holds the 8 characters
PL/I also permits bit strings:
declare b bit(10);
permits bit strings to be stored, of length ten. The assignment statement:
b = '1011110010'b;
stores the bits in the given order. Such bit strings can be searched, etc., just like character strings.
Data structures, or aggregates, may be declared:
declare 1 name, 2 first_name character (10), 2 surname character (15);
Arrays are declared by one of the above means, along with dimension information:
declare x(10) float;
specifies an array of ten elements,
x(10). The default lower bound is unity. To specify a different lower bound:
declare y(0:15) float;
declares a 16-element array
Y(0) through Y(15) of floating-point elements.
Multi-dimension array are declared by separating the dimensions with commas
declare M ( 2 , 4:5 ) fixed binary (15)
defines a 2-dimensional array of 4 elements:
M (1, 4),
M (1, 5),
M (2, 4),
M (2, 5)
To handle dates given as 2-digit years and as 4-digit years, possibly intermixed in the same program, the
DATE attribute may be used.
declare d date ('YY'); [more to come]
INITIAL may abbreviated as
INIT, many attributes may abbreviated, too.
put list ('Hello world!'); put skip list ('Hello world!');
The first example writes
Hello world! on the standard output (a screen or printer or file). The words are printed after any output already on that line. The second example starts a new line (the word
SKIP) before printing.
Describe how methods/functions/procedures are declared and implemented. The following example is of a complete PL/I program containing a function.
/* A program to calculate the area of a triangle, */ /* given two sides and an included angle. */ d: procedure options (main); declare (b, c, angle) float; put list ('Today is ', date()); put skip list ('Please type the lengths of two sides, and an angle'); get list (b, c, angle); put skip list ('The two sides are', b, c, 'and the angle is', angle, 'degrees'); put list ('Area of triangle=', area(b, c, angle)); area: procedure (b, c, angle) returns (float); declare (b, c, angle) float; return (b*c*sin(angle)/2); end area; end d;
The above program contains some typical input and output statements of the list-directed form. The first
PUT statement prints the date. The second requests the reader to type three values representing the parameters of some triangle. The third echoes the inputs as confirmation. The last
PUT statement contains a function reference,
area(b, c, angle), which invokes the function. When it has been computed, the current
PUT statement prints that area. The definition of the function
AREA includes a
PROCEDURE statement that lists the parameters required from the calling program, namely,
angle. These parameters are defined in the next statement (
PROCEDURE statement also states the type of the result to be returned to the calling program, namely, a floating-point value. This is indicated by
RETURNS (FLOAT). The
RETURN statement contains the formula to be used in calculating the area of the triangle. It then returns control to the calling program. A
RETURN statement typically includes the name of a variable, but may include an expression (as is the case here).
The scope of PL/I variables is confined to procedures in which they are declared. The scope extends into any procedures contained within that procedure (except of course any procedures in which a new declaration of the same name occurs).
In the event that a variable is not declared, then the scope of that variable is the whole program. Again, if a declaration (for a variable of the same name) appears in a block that is subordinate to the one(s) where the variable is (are) used, then the scope is excluded from the block containing the declaration.
PL/I has two kinds of blocks. One is the
PROCEDURE block that is used for subroutines and functions. The other is the
BEGIN block (
BEGIN; ... END;). The
BEGIN block behaves as a
PROCEDURE block, except that a
BEGIN block has no parameters nor does it have to have a label. It is entered in normal sequential execution, whereas a
PROCEDURE block can be executed only by executing a
CALL statement or function reference.
Conditional Statements: IF..THEN..ELSE
Consider the two examples of conditional statements:
if a = b then c = d; if a > b then c = d; else c = e;
The first example illustrates one optionally-executed statement
c = d;
The second example shows two optionally-executed statements, the first of which is executed when
b, and the second of which is executed if
When multiple statements are to be executed conditionally, statement brackets
end are used.
if a > b then do; p = q; r = s; end;
Multi-branch Conditionals: SELECT
The select-group offers a multi-branch evaluation, it may be written in one of the 2 following syntax schemes:
SELECT ( var_or_expr ); WHEN ( val_1 ) unit_1; .... OTHERWISE unit_other; END;
SELECT ; WHEN ( expr_1 ) unit_1; .... OTHERWISE unit_other; END;
A select-group of scheme type 1 has the form
SELECT ( var_or_expr ); WHEN ( val_1 ) unit_1; .... WHEN ( val_n ) unit_n; OTHERWISE unit_other; END;
I = 1..n ) may be variables or expressions.
I = 1..n ) and
unit_other may be single statements or do-groups.
The select-group is evaluated as (pseudo code):
Evaluate var_or_expr and store it to the variable TEMP. For I = 1 to n: Evaluate val_I, if the result equals TEMP: Execute unit_I and leave the select-group. Execute unit_other.
OTHERWISE clause (which may be abbreviated
OTHER) is optional, but if this clause is omitted and none of
TEMP a run time error will occur.
WHEN clause may contain more than 1 value, e.g.
SELECT ( my_char ); WHEN ( '0' , '2' , '4' , '6' , '8' ) put skip list ( 'character is an even digit' ); WHEN ( '1' , '3' , '5' , '7' , '9' ) put skip list ( 'character is an odd digit' ); OTHERWISE put skip list ( 'character is no decimal digit' ); END;
A select-group of scheme type 2 has the form
SELECT; WHEN ( expr_1 ) unit_1; .... WHEN ( expr_n ) unit_n; OTHERWISE unit_other; END;
I = 1..n ) may be logical expressions or logical variables.
I = 1..n ) and unit_other may be single statements or do-groups.
The select-group is evaluated as (pseudo code):
For I = 1 to n: if expr_I is true: Execute unit_I and leave the select-group. Execute unit_other.
SELECT; WHEN ( a > b ) DO; call use_as_max ( a ); put skip list ( 'max = a' ); END; WHEN ( b > a ) DO; call use_as_max ( b ); put skip list ( 'max = b' ); END; OTHERWISE ; /* clause with empty statement to avoid run time error if a = b */ END;
The basic form of looping statement is:
do k = 1 to 10; >>body<< end;
The body of the loop is executed ten times, with control variable
k taking the values from 1 through 10. On loop exit, the value of
k is one increment more, namely 11. The loop extends to the next
END statement. Variations of looping statements are:
do k = 1 to n by 2; do k = 20 to 1 by -1;
Multiple iterative specifications may be used:
do k = 1 to 10, 20 to 100 by 5, 120 to 200 by 20;
in which the loop will be executed with control variable
k taking the values 1 through 10, then 20, 25, 30, 35, ... 100, 120, 140, 160, 180, and 200.
A loop that is conditionally terminated uses the
do while (a > b);
Such a loop is executed zero or more times, the test being performed at the beginning of the loop.
do until (a > b);
The loop specified by the above statement is executed at least once, the test being performed at the end of the loop.
In the following, a
REPEAT is used to specify an unusual change in the value of the control variable.
do k = 1 repeat k*i until (k > 1000);
In this case, the control variable
k takes on the values 1, 2, 4, 8, 16, and so on, until
k becomes greater than 1000.
do next = head repeat next > link until (link = null);
may be used to traverse a linked list.
UNTIL may be combined:
do while ( W ) until ( U ); ... end; do until ( U ) while ( W ); ... end;
(both examples are equivalent, i.e. the order of
UNTIL in the statement doesn't matter )
DO I = ... may by followed by
REPEAT or both of them:
do I = 1 to 5 while ( W ); ... end; do I = 1 to 5 until ( U ); ... end; do I = 1 to 5 while ( W ) until ( U ); ... end; do I = 1 to 5 until ( U ) while ( W ); ... end;
(the last two examples are equivalent)
Order of evaluation:
do I = 3 to 9 by 2 while ( W ) until ( U ); ... some_statements ... end; put skip list ( 'I am ready' );
is evaluated as:
let I be 3
if I > 9 then go to 
if W = false then go to 
... some_statements ...
if U = true then go to 
let I be I + 2 and go to 
put skip list ( 'I am ready' );
One more example: The following statement specifies that the do-group has to be executed eight times, with the value of I equal to 1, 3, 5, 9, 18, 36, 9, 18.
do I = 1 to 5 by 2, /* 1 3 5 */ 9 repeat 2 * I until ( I > 20 ), /* 9 18 36 */ 9 repeat 2 * I while ( I < 21 ); /* 9 18 */
bit (1) strings represents single boolean values:
'1'Bis interpreted as
'0'Bis interpreted as
Bit (1) strings may be used as boolean expressions in conditional and loop statements.
declare my_bit bit (1); my_bit = ( 1 < 2 ); /* now my_bit has the value '1'B */ my_bit = ( 2 < 1 ); /* now my_bit has the value '0'B */ ..... if my_bit then put skip list ( 'value of my_bit is true' ); else put skip list ( 'value of my_bit is false' ); do while ( my_bit ); ..... end;
Boolean operators may be used for calculating new values:
- The prefix operator
¬is used as logical
- The infix operator
&is used as logical
- The infix operator
|is used as logical
declare bit_a bit (1); declare bit_b bit (1); declare result bit (1); result = ¬ bit_a; /* result = '1'B if and only if bit_a is '0'B */ result = bit_a & bit_b; /* result = '1'B if and only if both bit_a and bit_b are '1'B */ result = bit_a | bit_b; /* result = '0'B if and only if both bit_a and bit_b are '0'B */
Note: Using compile-time options
NOT operator and
OR operator may be replaced by other symbols, for being compatible with existing PL/I programs often
^ is used as
! is used as
PL/I provides error detection and error handling. Errors that are detected include
- floating-point overflow and underflow;
- fixed-point overflow;
- division by zero;
- subscript bound error;
- substring violation;
- string truncation.
- general errors.
The user can permit the system to report the error and perform a default action, or the user can specify a computational action to be executed when a given error is detected.
Events (including errors) are termed conditions.
For a condition to be detected, it must first be enabled. If the condition is enabled, and the corresponding condition occurs, the corresponding
ON-unit (if supplied by the user) is executed.
Consider the following code:
on overflow snap go to restart; a = b * c; ... restart:
In the event that the product
b*c exceeds the maximum value permitted, the
OVERFLOW condition is raised, and the
OVERFLOW is executed. The
ON-unit here is one of the simplest (
go to), and merely transfers control to the statement labelled
RESTART (where, perhaps, the program deals with the next set of values).
SNAP causes the running program to print an error message, along with the statement number where the error occurred.
A more general example follows:
(SUBSCRIPTRANGE): a: procedure; declare x(10) float; declare (i, k) fixed binary; ON SUBSCRIPTRANGE SNAP BEGIN; PUT DATA (K); STOP; END; get list (k); do i = 1 to k; x(i) = 0; end; end a;
In this example, the error detection and handling parts are shown in upper case. Subscript checking is enabled by the condition prefix
SUBSCRIPTRANGE (parenthesised before the
PROCEDURE statement). Before any statements are executed, the
ON statement must be executed, which establishes the action to be carried out in the event of a subscript error. The next statement executed is the input statement
GET, which reads in a value, say 12. The loop is entered, and initializes ten values of x. On attempting to initialize
SUBSCRIPTRANGE condition is raised, and causes the
BEGIN; PUT DATA (K); STOP; END; to be executed.
SNAP causes an error message to be printed along with the statement number, and then statements within the
BEGIN block are executed, namely the
PUT DATA statement, which prints
K = 11. The program then
STOPs. Some specific conditions are:
|ZERODIVIDE||division by zero|
|SUBSCRIPTRANGE||subscript bound error|
|STRINGSIZE||truncation of a string|
|ENDFILE||end of file|
|ENDPAGE||end of page|
|ATTENTION||request from keyboard|
Some conditions are provided to handle everyday events, for example, end of file and end of page. For example, to print a page number on the top of each page:
ON ENDPAGE PUT PAGE LIST('Page', Page_no);
To detect the end of the input file
IN, and to transfer to a named statement:
ON ENDFILE(IN) go to next;
The programmer can invent his own conditions, and can signal them.
SIGNAL statement is provided to test a given
ON-unit (error-handler) during program testing. For example,
SIGNAL ZERODIVIDE; To raise a user-defined condition called
RANGE, the statement
SIGNAL CONDITION(RANGE); would be used.
<List containers or references to lists of containers available natively for this language. List ways to incorporate containers if they are not native to the language.>
Any garbage collection is automatic. All variables with the automatic attribute or allocated with the
builtin are removed from storage at the end of the block where they are declared. All allocated storage (
ALLOC statement or
alloc builtin) is removed from storage at the end of the program.
The libraries of the standard functions are all automatically provided; it is unnecessary to do anything to obtain access to them, such as to specify directories or to use
INCLUDE statements or the like.
- Source programs may be written in upper or lower case. There is no difference between the cases, except within strings.
- Varying strings (declared with the attribute
VARYING) are similar to C's char.
For an exact equivalant, in Enterprise PL/I for z/OS you may use the attribute
VARYINGZ, in which strings are zero-terminated.
Without the attribute
VARYING, strings are of fixed length. Such strings always store the specified number of characters. Thus,
DECLARE S CHARACTER(8); S = 'abc';
stores eight characters, namely,
abc followed by five blanks, whereas
declare V character(8) varying; V = 'abc';
requires a storage of 10 bytes: 2 bytes for the actual length of V and 8 bytes for the characters. This latter feature makes possible the assignment
V = V || 'dog';
which appends the word dog to what is already stored in V (up to a maximum of 8 characters in this example, of course).
Content of V after V = 'abc';: +---+---+---+---+---+---+---+---+---+---+ | 3 | a | b | c | ? | ? | ? | ? | ? | /* "?" means: value is undefined */ +---+---+---+---+---+---+---+---+---+---+ 1 2 3 4 5 6 7 8 /* index of character */
Content of V after V = V || 'dog';: +---+---+---+---+---+---+---+---+---+---+ | 6 | a | b | c | d | o | g | ? | ? | /* "?" means: value is undefined */ +---+---+---+---+---+---+---+---+---+---+ 1 2 3 4 5 6 7 8 /* index of character */
declare i fixed binary;
is equivalent to C's
declare i fixed binary (31);
is equivalent to C's
declare i fixed binary (63);
is equivalent to C's
long long int.
- Decimal arithmetic, because it is fixed-point, needs some care.
The built-in functions
DIVIDE are provided to enable the programmer to specify the precision of each result. Thus they help to avoid overflow. For decimal fixed-point division, it is strongly recommended that the
DIVIDE function be used. Thus, to divide
A has precision
B has precision
(6), the recommended form is:
declare A fixed decimal (10,5), B fixed decimal(6), C fixed decimal (10,5); A = 12345.67891; b = 98765; C = divide(A, B, 10, 5);
DIVIDE reference, the arguments 10 and 5 direct that the result A/B has a total of ten digits, with 5 digits after the decimal point, respectively. This value is then stored in C. IBM PL/I permits up to 31 decimal digits for fixed-point working.
- When running PL/I programs, use the condition prefixes
SUBSCRIPTRANGE, on the initial procedure statement thus:
(SIZE, STRINGRANGE, STRINGSIZE, SUBSCRIPTRANGE): trial: procedure options (main);
This enables checking for fixed-point overflow, and range-checking and truncation for strings and for subscripts.
- The lower bound of arrays is unity by default, not zero.
- Division of integers (whether decimal or binary or mixed) may produce a fixed-point result having a scale factor. Thus, 9/2 produces 4.5000000, unlike Fortran which produces the integer 4. Should an integer result be required,
DIVIDEmay be used, thus:
TRUNC(9/2)or - more generally -
DIVIDE(J, K, 31), the latter giving 31 bits of precision for a binary integer result. Alternatively, declaring binary integer variables to have maximum precision (typically 31 bits) will ensure that the result of dividing two such integer variables will give an integer result.
-  (beginner)
-  (intermediate)
-  (beginner)
-  IBM manuals: PL/I for MVS & VM
-  IBM manuals: Enterprise PL/I for z/OS
Books and Articles
- J. K. Hughes, PL/I Structured Programming, 3rd Ed., Wiley, 1986. (beginner to professional; commercial applications)
- R. Reddy & C. Ziegler, PL/I: Structured Programming and Problem Solving, West, 1986, ISBN 0-314-93915-6. (beginner to advanced)
- R. A. Barnes, PL/I for Programmers, North-Holland, 1979. (professional)
- G. F. Groner, PL/I Programming in Technological Applications, Books on Demand, Ann Arbor, MI, 1971. (professional)
- M. E. Anderson, PL/I for Programmers, Prentice-Hall, 1973. (professional)
- D. R. Stoutemyer, PL/I Programming for Engineering & Science, Prentice-Hall, 1971. (professional)
- E. Sturm, Das neue PL/I (fur PC, Workstations and Mainframe), 5th Ed., Vieweg-Verlag, 2002.