Software Engineers Handbook/Language Dictionary/PLI
PL/I
editHere is the Wikipedia entry.
Type
editPL/I is a full procedural language.
Execution Entry Point
editThe program has its entry point specified as OPTIONS(MAIN)
, in a procedure statement, as in:
tree: procedure options (main);
Column specialties
editThere are no column specialities.
General Syntax
editAn 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 a
.
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 s
.
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.
Comments
editEvery comment commences with the pair /*
and terminates with the pair */
. Comments may span lines.
/*
* this is a long comment.
*/
Variable Declarations
editDeclarations 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.
To declare i
as an binary:
declare i fixed binary;
To declare 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 E
, write:
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');
describes variable 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);
String 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= 'abc';
V holds the three characters abc
.
V = 'friendly';
V holds the 8 characters friendly
.
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(1)
through 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]
The keywords DECLARE
and INITIAL
may abbreviated as DCL
and INIT
, many attributes may abbreviated, too.
Attribute | Abbreviation |
---|---|
BINARY
|
BIN
|
DECIMAL
|
DEC
|
CHARACTER
|
CHAR
|
VARYING
|
VAR
|
PICTURE
|
PIC
|
POINTER
|
PTR
|
Output Statements
editput 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.
Method Declaration/Implementation
editDescribe 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, b
, c
, and angle
. These parameters are defined in the next statement (DECLARE
).
The 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).
Scope
editThe 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
editConsider 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 a
> b
, and the second of which is executed if a
<= b
;
When multiple statements are to be executed conditionally, statement brackets do
and end
are used.
if a > b then
do; p = q; r = s; end;
Multi-branch Conditionals: SELECT
editThe 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;
var_or_expr
and val_I
( I
= 1..n ) may be variables or expressions.
unit_I
( 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.
The OTHERWISE
clause (which may be abbreviated OTHER
) is optional,
but if this clause is omitted and none of val_I
equals TEMP
a run time error will occur.
A 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;
expr_I
( I
= 1..n ) may be logical expressions or logical variables.
unit_I
( 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.
An example:
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;
Looping Statements
editThe 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 WHILE
construct:
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.
The REPEAT
form:
do next = head repeat next > link until (link = null);
may be used to traverse a linked list.
WHILE
and 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 WHILE
and UNTIL
in the statement doesn't matter )
DO I = ...
may by followed by WHILE
or 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 [7]
if W = false then go to [7]
... some_statements ...
if U = true then go to [7]
let I be I + 2 and go to [2]
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 */
Boolean Values
editIn PL/I bit (1)
strings represents single boolean values:
'1'B
is interpreted asTrue
'0'B
is interpreted asFalse
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 logicalNOT
. - The infix operator
&
is used as logicalAND
. - The infix operator
|
is used as logicalOR
.
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 NOT
, !
is used as OR
.
Error Handling/Recovery
editPL/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 ON
-unit for 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).
The keyword 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 x(11)
, the SUBSCRIPTRANGE
condition is raised, and causes the ON
unit 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 STOP
s. Some specific conditions are:
OVERFLOW | exponent overflow |
UNDERFLOW | exponent underflow |
FIXEDOVERFLOW | fixed-point overflow |
SIZE | fixed-point overflow |
ZERODIVIDE | division by zero |
SUBSCRIPTRANGE | subscript bound error |
STRINGRANGE | substring 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.
The 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.
Containers
edit<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.>
Algorithms
editGarbage collection
editAny garbage collection is automatic.
All variables with the automatic attribute or allocated with the automatic
(auto
) 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.
Physical Structure
editThe 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.
Tips
edit- 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 equivalent, 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 int
;
declare i fixed binary (31);
is equivalent to C's long int
.
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 ADD
, SUBTRACT
, MULTIPLY
, and 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
by B
, where A
has precision (10,5)
, and 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);
In the 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
SIZE
,STRINGRANGE
,STRINGSIZE
, andSUBSCRIPTRANGE
, 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,
TRUNC
orDIVIDE
may 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.
Web References
editBooks and Articles
edit- 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.