Software Engineers Handbook/Language Dictionary/PLI

From Wikibooks, open books for an open world
Jump to navigation Jump to search

PL/I[edit | edit source]

Here is the Wikipedia entry.

Type[edit | edit source]

PL/I is a full procedural language.

Execution Entry Point[edit | edit source]

The program has its entry point specified as OPTIONS(MAIN), in a procedure statement, as in:

 tree: procedure options (main);

Column specialties[edit | edit source]

There are no column specialities.

General Syntax[edit | edit source]

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 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[edit | edit source]

Every comment commences with the pair /* and terminates with the pair */. Comments may span lines.

/*
 * this is a long comment.
 */

Variable Declarations[edit | edit source]

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.

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[edit | edit source]

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.

Method Declaration/Implementation[edit | edit source]

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, 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[edit | edit source]

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[edit | edit source]

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 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[edit | edit source]

The select-group offers a multi-branch evaluation, it may be written in one of the 2 following syntax schemes:

  1. SELECT ( var_or_expr ); WHEN ( val_1 ) unit_1; .... OTHERWISE unit_other; END;
  2. 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[edit | edit source]

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 WHILE construct:

do while (a &gt; b);

Such a loop is executed zero or more times, the test being performed at the beginning of the loop.

do until (a &gt; 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:

  1. let I be 3
  2. if I > 9 then go to [7]
  3. if W = false then go to [7]
  4. ... some_statements ...
  5. if U = true then go to [7]
  6. let I be I + 2 and go to [2]
  7. 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[edit | edit source]

In PL/I bit (1) strings represents single boolean values:

  • '1'B is interpreted as True
  • '0'B is interpreted as False

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 NOT.
  • The infix operator & is used as logical AND.
  • The infix operator | is used as logical OR.
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[edit | edit source]

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 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 STOPs. 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 | edit source]

<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[edit | edit source]

Garbage collection[edit | edit source]

Any 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[edit | edit source]

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.

Tips[edit | edit source]

  • 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, and 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, TRUNC or DIVIDE 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[edit | edit source]

  • [1] IBM manuals: PL/I for MVS & VM
  • [2] IBM manuals: Enterprise PL/I for z/OS

Books and Articles[edit | edit source]

  • 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.

See also[edit | edit source]