DocumentationEndless PL/I for POSIX SystemsReference Manual  ·  ESS-PFURM-001

Chapter 1

Program Structure and Content

This chapter introduces the following elements of a PL/I program:

Future chapters discuss these topics in more detail.

1.1. Lexical Elements

This section describes the following topics:

  • PL/I keywords

  • Punctuation

  • Identifiers

  • Comments

1.1.1. Keywords

A keyword is a name that has a special meaning to PL/I when used in a specific context. In context, keywords identify statements, attributes, options, and other program elements. PL/I keywords are not reserved words, so it is possible to use them in a program in other than their keyword context.

PL/I has numerous keywords. Appendix A, Alphabetic Summary of Keywords describes the PL/I keywords, including brief identifications of their uses and valid abbreviations for the keywords that can be abbreviated.

1.1.2. Punctuation

PL/I recognizes punctuation marks in statements. The punctuation marks serve the following two functions:

  • They specify arithmetic or other operations to be performed on expressions.

  • They delimit and separate identifiers, keywords, constants, and statements.

For example:

A = B + C;

In this statement, the equal sign (=), the addition operator (+), and the semicolon (;) delimit the identifiers A, B, and C, as well as define the operation to be performed. (Chapter 6, Expressions and Data Type Conversions describes the effect of the various operators in expressions.)

Whenever you use a punctuation mark in a PL/I statement, you can precede or follow the character with any number of spaces (except in the case of an operator consisting of two characters, like >= or **, which you must enter without a space between the two characters). For example, the following two statements are equivalent:

DECLARE ( A, B ) FIXED DECIMAL ( 7, 0 ) ;
DECLARE(A,B)FIXED DECIMAL(7,0);

In the second statement, all nonessential spaces are omitted; the parentheses and commas are sufficient to distinguish elements in the statement. The only space required in this statement is the space that separates the two keywords FIXED and DECIMAL.

Table 1.1, “Punctuation Marks Recognized by PL/I” lists all the punctuation marks recognized by PL/I.

Table 1.1. Punctuation Marks Recognized by PL/I
CategorySymbolMeaning
Arithmetic operators+Addition or unary plus
-Subtraction or unary minus
/Division
*Multiplication
**Exponentiation
Relational (or comparison) operators>Greater than
<Less than
=Equal to
^>Not greater than
^<Not less than
^=Not equal to
>=Greater than or equal to
<=Less than or equal to
Logical operators^

Logical NOT (unary) and

EXCLUSIVE OR (binary)

&Logical AND
&:Logical AND THEN
| or !Logical OR
|: or !:Logical OR ELSE
Concatenation operator|| or !!String concatenation
Separators,Delimits elements in a list
;Terminates a PL/I statement
.Separates identifiers in a structure name; specifies a decimal point
:Terminates a procedure name or a statement label
()Encloses lists and extents; defines the order of evaluation of expressions; separates statement and option names from specific keywords; specifies a parameter list
'Delimits character strings and bit strings
Locator qualifier->Pointer resolution

The tilde (~) is equivalent to the circumflex (^), and the exclamation point (!) is equivalent to the vertical bar (|).

Spaces, Tabs, and Line-End Characters

In addition to punctuation marks, PL/I accepts spaces, tabs, and line-end characters between identifiers, constants, and keywords.

The rules for entering spaces are:

  • Between any identifiers, keywords, or constants

  • Preceding or following punctuation marks that normally serve as delimiters, for example, tabs or commas

The line-end character is a valid punctuation mark between items in a PL/I statement except when it is embedded in a string constant. In a string constant, the line-end character is ignored. For example:

A = 'THIS IS A VERY LONG STRING THAT MUST BE CONTI
NUED ON MORE THAN ONE LINE IN THE SOURCE FILE';

This assignment statement gives the variable A the value of the specified character-string constant. (The line-end character in the constant is ignored.) Note that any tabs or spaces preceding NUED in the previous example will be included in the string.

1.1.3. Identifiers

An identifier is a user-supplied name for a procedure, a statement label, or a variable that represents a data item. The rules for forming identifiers are:

  • An identifier can have from 1 to 31 characters.

  • An identifier can consist of any of the following characters:

    • The alphabetic letters A through Z and a through z. PL/I converts all lowercase letters to uppercase when it compiles a source program. The identifiers abc, ABC, Abc, and so on, all refer to the same object.

    • The numeric digits 0 through 9.

    • The underscore character (_).

    • The dollar sign character ($).

    • The pound sign character (#).

    • The at sign character (@).

  • An identifier cannot contain any blanks, spaces, or hyphens.

  • An identifier must begin with an alphabetic letter, a dollar sign ($), or an underscore (_). It cannot begin with a numeral.

Examples of valid identifiers are:

STATE
total
FICA_PAID_YEAR_TO_DATE
ROUND1
PAYMENTS_IN_$S

1.1.4. Comments

A comment is an informational tool for documenting a PL/I program. To insert a comment in a program, enclose it within the character pairs /* and */. For example:

/* This is a comment ... */

Wherever the starting characters (/*) appear in a program, the compiler ignores all text until it encounters the ending characters (*/). A comment can span several lines.

The rules for entering comments are:

  • A comment can appear anywhere that a space can appear.

  • A comment can contain any character except the pair */; comments cannot be nested.

The following are examples of comments:

A = B + C ; /* Add B and C */

/* ********* START OF SECOND PHASE ********* */

DECLARE/*COUNTER*/A FIXED BINARY (7);

/* This module performs the following steps:
       1. Initializes all arrays and data structures.
       2. Establishes default condition handlers.
*/

Although complete comments cannot be nested, you can comment out a statement such as the following:

DECLARE EOF BIT(1); /* end-of-file */

To do this, precede the DECLARE statement with another /* pair, as follows:

/* DECLARE EOF BIT(1); /* end-of-file */

The compiler will then ignore all text, including the DECLARE statement and the second /*, until it reaches the */.

1.2. Statements

A statement is the basic element of a PL/I procedure. Statements are used to do the following:

  • Define and identify the structure of the program and the data that it acts upon (possibly including text from other files; see Section 10.2.12, “%INCLUDE”.)

  • Request specific actions to be performed on data

  • Control the flow of execution in a program

Table 1.2, “Summary of PL/I Statements” and Table 10.1, “Summary of PL/I Preprocessor Statements” provide summaries of PL/I statements. Detailed descriptions of these statements appear throughout this manual.

1.2.1. Statement Formats

The general format of a PL/I statement consists of an optional condition prefix, an optional statement label, the body of the statement, and the required semicolon terminator. The format of a statement is:

[ ( condition-prefix [ ,condition-prefix ] ... ) : ] [ label: ] statement-body;

The body of the statement consists of user-specified identifiers, literal constants, or PL/I keywords. Each element must be properly separated, either by special characters that punctuate the statement or by spaces or comments.

1.2.2. Condition Prefixes (IBM Dialect Only)

The optional condition prefix specifies a PL/I condition to enable or disable for the statement. If you disable a condition, no program interrupt results if the condition occurs during execution of the statement.

The condition prefix precedes the statement label, if you specify a label, and the statement body. It consists of any valid condition or list of conditions, or the negated version of the condition or conditions, enclosed in parentheses, terminated by a colon. For example:

(ERROR,NOENDFILE): NEWREC: READ FILE (INFILE) INTO (INREC);

Any statement except precompiler statements (statements beginning with the % character), and the DECLARE, DEFAULT, or ENTRY statements can have condition prefixes.

If you use a condition prefix on a FORMAT statement, it must be the same prefix used on the referring GET or PUT statement.

1.2.3. Statement Labels

The optional statement label identifies a statement so that it can be referred to elsewhere in the program, for example, as the target of a GOTO statement. A label precedes a statement; it consists of any valid identifier terminated by a colon. For example:

TARGET: A = A + B;
READ_LOOP: READ FILE (TEXT) INTO (TEMP);

A statement cannot have more than one label. See Section 1.1.3, “Identifiers” for more information on identifier format.

1.2.4. Simple Statements

A simple statement contains only one action to be performed. There are three types of simple statements:

  • Keyword statements

  • Assignment statements

  • Null statements

Keyword Statements

Keyword statements are identified by the PL/I keyword that requests a specific action. Examples of keyword statements are:

READ FILE (A) INTO (B);
GOTO LOOP;
DECLARE COUNTER FIXED BINARY (7);

In these examples, READ, GOTO, and DECLARE are keywords that identify these statements to PL/I.

Assignment Statements

PL/I identifies an assignment statement by syntax: an assignment statement consists of an identifier followed by an equal sign (=) , followed by an identifier or expression. For example:

A = B;
TOTAL = TOTAL + PRICE;
COUNTER = 0;
Null Statements

A null statement consists of only a semicolon (;). It indicates that PL/I is to perform no operation. For example:

IF A < B THEN GOTO COMPUTE;
       ELSE;

This IF statement shows a common use of the null statement: as the target of an ELSE clause.

1.2.5. Compound Statements

A compound statement contains more than one PL/I statement within the statement body. It is terminated by the semicolon that terminates the final statement. The IF and ON statements are examples of compound statements.

1.2.6. Preprocessor Statements

Preprocessor statements start with a percent sign (%). They can be simple or compound, as described in the preceding sections. For a full description of preprocessor statements, see Chapter 10, Preprocessor .

1.2.7. Summary of Statements by Function

You can group PL/I statements by function into the following categories.

Data Definition and Assignment Statements

The DECLARE statement defines variable names:

DECLARE identifier [attribute ...];

The assignment statement gives a value to a variable:

reference = expression;
Input/Output Statements

These statements identify files and data formats and perform input and output operations:

CLOSEGETREAD
DELETEOPENREWRITE
FORMATPUTWRITE
Program Structure Statements

These statements define the organization of the program into procedures, blocks, and groups:

BEGINENDPROCEDURE
DOENTRYnull
Flow Control Statements

These statements change or interrupt the normal sequential flow of execution in a PL/I program:

CALLONSIGNAL
GOTORETURNSTOP
IFREVERT

LEAVESELECT

Storage Allocation Statements

These statements acquire and control the use of storage in a PL/I program:

ALLOCATE

FREE

Table 1.2, “Summary of PL/I Statements” gives a summary of the PL/I statements and their uses.

Table 1.2. Summary of PL/I Statements
StatementUse
AssignmentEvaluates an expression and gives its value to an identifier
NullSpecifies no operation
ALLOCATEAllocates storage for a based or controlled variable
BEGINDenotes the beginning of a block of statements to be executed as a unit
CALLTransfers control to a subroutine or external procedure
CLOSETerminates association of a file control block with an input or output file
DECLAREDefines the variable names and identifiers to be used in a PL/I program and specifies the data attributes associated with them
DELETERemoves an existing record from a file
DODenotes the beginning of a group of statements to be executed as a unit
ENDDenotes the end of a block or group of statements begun with a BEGIN, DO, or PROCEDURE statement
ENTRYSpecifies an alternative point at which a procedure can be invoked
FORMATSpecifies the format of data that is being read or written with GET EDIT and PUT EDIT statements and defines the conversion, if any, to be performed
FREEReleases storage of a based or controlled variable
GETObtains data from an external stream file or from a character-string expression
GOTOTransfers control to a labeled statement
IFTests an expression and establishes actions to be performed based on the result of the test
LEAVETransfers control out of a DO group
LOCATEAllocates storage in an output buffer. Used with the BUFFERED environment option. (ibm dialect only)
ONEstablishes the action to be performed when a specified condition is signaled
OPENEstablishes the association between a file control block and an external file
PROCEDURESpecifies the point of invocation for a program, subroutine, or user-defined function
PUTTransfers data to an external stream file or to a character-string variable
READObtains a record from a file
RETURNGives back control to the procedure from which the current procedure was invoked
REVERTCancels the effect of the most recently established ON unit
REWRITEReplaces a record in an existing file
SELECTTests a series of expressions and establishes the action to be performed based on the result of the test
SIGNALCauses a specific condition to be signaled
STOPHalts the execution of the current program
WRITECopies data from the program to an external record file

1.3. Program Format

A PL/I program consists of a series of statements, which perform the following tasks:

  • Define the data to be used for program input and output

  • Define the operations to be performed on the data during the execution of the program

  • Control the environment within which the program executes

  • Define the order of execution or control flow for a program

A statement comprises user-specified identifiers, constants, and PL/I keywords, separated by blanks, comments, and punctuation marks. You can organize statements into structural sequences of groups or blocks. Example 1.1, “Structure of a PL/I Program” shows the structure of a PL/I program.

Example 1.1. Structure of a PL/I Program
SAMPLE: PROCEDURE OPTIONS (MAIN); ❶

DECLARE (X,Y,Z) FIXED,  ❷
       MESSAGE CHARACTER(80) INITIAL ('HELLO'),
       CALC ENTRY (FLOAT) RETURNS (FLOAT),
       TOTAL FLOAT;

       X = 0;  ❸
       PUT SKIP LIST(MESSAGE);

FINISH: PROCEDURE;  ❹
DECLARE TEXT (5) CHARACTER (20);
END FINISH;

END SAMPLE; ❺

Key to Example 1.1, “Structure of a PL/I Program”:

❶

A PROCEDURE is the basic executable program unit.

❷

The declarations of variables in a procedure are usually, but not necessarily, placed at the beginning of the procedure.

❸

Executable statements are placed following variable declarations.

❹

Internal procedures may be placed anywhere.

❺

All procedures must terminate with END statements.

The source text of a PL/I program is freeform. As long as you terminate every statement with a semicolon (;), individual statements can begin in any column, be on additional lines, or be written with more than one statement to a line.

Individual keywords or identifiers of a statement, however, must be confined to one line. Only a character-string constant (which must be enclosed in apostrophes) can be on more than one line.

PL/I programs are easier to read and comprehend if you follow a standard pattern in formatting. For example:

  • Write source statements with no more than one statement per line.

  • Use indention to show the nesting level of blocks and DO-groups.

1.4. Blocks

PL/I is a block-structured language with each block composed of a sequence of PL/I statements. There are two types of blocks:

  • Procedure blocks. A procedure block begins with a PROCEDURE statement and terminates with an END statement. A procedure is the basic program unit of PL/I; it also defines the scope of names declared within it.

  • Begin blocks. A begin block begins with a BEGIN statement and terminates with an END statement. A begin block is always contained within a procedure. It delimits a portion of a program and defines the scope of names declared within it.

Scope of Names

The scope of a declaration of a name is that region of the program in which the name has meaning. A name has meaning in the following locations:

  • The block in which it is declared

  • Any blocks contained within the declaring block, as long as the name is not redeclared in the contained block

  • Any procedure contained in the program, if the name is declared outside a procedure

Two or more declarations of the same name are not allowed in a single block unless one or more of the declarations are of structure members.

Two declarations of the same name in different blocks denote distinct objects unless both specify the EXTERNAL attribute. All EXTERNAL declarations of a particular name denote the same variable or constant, and all must agree as to the properties of the variable or constant, otherwise unpredictable results will occur. Note that EXTERNAL is the default for declarations of ENTRY and FILE constants. It must be specified explicitly for variables.

The following example shows the scope of internal names:

NAMESCOPE
DECLARE Q STATIC FIXED;QMAINP, ALPHA, BETA, and CALC
MAINP: PROCEDURE OPTIONS (MAIN);MAINPMAINP, ALPHA, BETA, and CALC
DECLARE (X, Y, Z) FIXED;X, YMAINP, ALPHA, BETA, and CALC

Z in MAINPMAINP, ALPHA, and CALC

ALPHA: PROCEDURE;ALPHAMAINP, ALPHA, BETA, and CALC
BETA: BEGIN;BETAALPHA, BETA
DECLARE Z FLOAT;Z in BETABETA
GOTO ERROR;

END BETA;

ERROR:ERRORALPHA, BETA
END ALPHA;

CALC: PROCEDURE;CALCMAINP, ALPHA, and CALC
DECLARE (SUM, TOTAL) FLOAT;SUM, TOTALCALC
END CALC;

END MAINP;

Declarations can appear outside procedures and, if contained within the same block, have meaning throughout all procedures contained in the block. However, if there are multiple blocks, declarations outside procedures must have the EXTERNAL attribute if they are to be recognized by all blocks and procedures in the program. For example:

File A.PLI
       DECLARE X FIXED EXTERNAL STATIC;
A: PROCEDURE OPTIONS(MAIN);
       DECLARE B ENTRY;
       ⋮
       END A;
File B.PLI
B: PROCEDURE;
       ⋮
       END B;

In this example, the variable X has meaning in both procedures, which are part of the same program. Because the two procedures are in two different files, X must be declared with the EXTERNAL attribute. If X is declared with the INTERNAL attribute, X is recognized only in the first procedure.

1.4.1. Begin Blocks

A begin block is a sequence of statements headed by a BEGIN statement (see Section 8.2, “BEGIN Statement”) and terminated by an END statement (see Section 8.3, “END Statement”). In general, you can use a begin block wherever a single PL/I statement would be valid. In some contexts, such as an ON-unit, a begin block is the only way to perform several statements instead of one. A primary use of begin blocks is to localize variables. Because execution of a begin block causes a block activation, automatic variables declared within the begin block are local to it, and their storage disappears when the block completes execution.

Another way to allow your program to perform several statements in place of one is to use a DO group (see Section 8.1, “DO Groups and Statements”). You should choose it when possible because it does not incur the overhead associated with block activation. Use a begin block when there are declarations present or when you require multiple statements in an ON unit.

1.4.2. Procedure Blocks

A procedure is a sequence of statements (possibly including begin blocks and other procedures) headed by a PROCEDURE statement and terminated by an END statement. Unlike a begin block, which executes when control reaches it, a procedure executes only when it is specifically invoked. Invocation occurs in the following ways:

  • Enter the name of the program to invoke the main procedure of a PL/I program. This is the procedure that has OPTIONS(MAIN) on its PROCEDURE statement.

  • Statements within a procedure can invoke other procedures. The CALL statement invokes a procedure as a subroutine. A function reference invokes a function, which is a procedure that returns a value for use in the evaluation of an expression.

A PL/I program must have at least one procedure, the main procedure. Any procedure, including the main procedure, can contain others; these are called internal procedures. A procedure that is not contained within any other is called an external procedure. The main procedure is always an external procedure.

Except for the main procedure, no procedure executes unless it is invoked by a CALL statement or a function reference. Chapter 7, Procedures discusses procedures in more detail.

1.4.3. Containment

As an example, block B is said to be contained in another block A if all of B's source text, from label (if any) to END statement inclusive, is between A's BEGIN or PROCEDURE statement and A's END statement. If block B is not contained in any other block within block A, then B is said to be immediately contained in A. For example:

A: PROCEDURE OPTIONS(MAIN);
       B: PROCEDURE;
       END B;
       ⋮
       BEGIN;
               CALL B;
       END; /* of begin block */
END A;

The procedures B and the begin block all are immediately contained in A.

If block B is contained in block A, then B is said to be nested in A. The maximum nesting level is 64.

1.4.4. Block Activation

A block is activated when program execution flows into it. The following events happen when a block is activated:

  • All automatic variables declared in the block become active

  • All parameters passed to the block become active

When control leaves the block, the automatic variables become undefined and inaccessible.

You can only enter a procedure block with a CALL statement (see Section 7.4, “CALL Statement”) or a function reference. If an internal procedure is declared within a source program, control flows around the internal procedure during the normal sequence of execution.

A begin block is entered when it is encountered during the normal flow of execution or when a GOTO statement transfers control to a labelled BEGIN statement.

1.4.5. Relationship of Block Activations

During the execution of a program, many blocks can be simultaneously active. Two different relationships can be defined among block activations; they are the immediate dynamic descendance and the immediate parent activation.

For example:

B: PROCEDURE OPTIONS(MAIN);
       A: PROCEDURE;
               CALL Q;
       ⋮
       END A;
       Q: PROCEDURE;
       ⋮
       END Q;
       BEGIN;
               CALL A;
       END; /* of begin block */
END B;

Figure 1.1, “Relationship of Block Activations” shows these relationships.

Figure 1.1. Relationship of Block Activations
Relationship of Block Activations B Begin Block A Q Dynamic Descendent Chains Parent Chains

In the immediate dynamic descendance relationship, a block activation is the immediate dynamic descendant of the block that invoked it. At a given time, the chain of immediate dynamic descendants includes all existing block activations, starting with the activation of the main procedure and terminating in the current block activation. For example, in Figure 1.1, “Relationship of Block Activations”, the begin block is the immediate dynamic descendant of procedure B; the complete chain is B, begin block, A, Q. This chain is used for finding the applicable ON-unit when a condition is signaled.

The other relationship shown in Figure 1.1, “Relationship of Block Activations” applies to activations of nested blocks. An activation of a block X that is a begin block or internal procedure has an immediate parent activation, which is an activation of the block that immediately contains X. The chain of immediate parent activations extends back to an activation of the external procedure containing X. In Figure 1.1, “Relationship of Block Activations”, the parent chain for the begin block, procedure A, and procedure Q leads directly back to the activation of B, because each of these blocks is immediately contained in B. This chain is used in interpreting references.

When a block is activated, its immediate parent activation is determined as follows:

  • If the block is an external procedure, it has no parent activation.

  • If the block is a begin block, its immediate parent activation is the activation that invoked it. Therefore, the begin block is the immediate dynamic descendant of its immediate parent.

  • If the block is an internal procedure invoked in block activation B by a reference to an entry constant (such as A in Figure 1.1, “Relationship of Block Activations”) declared in block B, then the immediate parent of the new block activation of A is the activation of A in the parent chain starting at B.

  • If the block is an internal procedure invoked by an entry variable, the parent activation is taken from the entry value. It was originally set when the complete entry value was generated by the assignment of an entry constant to an entry variable (Section 3.8, “Entry Data” discusses entry data).

1.4.6. Block Termination

When a block terminates normally, that is, when an END statement or a RETURN statement is executed, the following events occur:

  • the current block is released and control goes to the preceding block activation.

  • If a nonlocal GOTO statement is executed that transfers control out of the current block, the current block and any blocks between it and the block containing the label that is the target of the GOTO statement are released.

  • The on-unit environment is reset to its former state.

  • Storage allocated for automatic variables local to the block is released.

  • Any tasks attached to the block terminate.

1.5. Data and Variables

The statements in a PL/I program process data, generally in the form of variables that take on different values as the result of program execution. In PL/I, you must declare variables in a DECLARE statement in the scope where you reference them. The compiler assigns the default attributes to any undeclared variables (see Section 3.1.2, “Default Attributes” for more information).

Declaring a variable, or using an undeclared variable, associates an identifier with a set of attributes and with a region of storage. Thus, when you declare a variable you must usually specify one or more data type attributes to be associated with it. (The concept of an attribute is more basic to PL/I than the concept of a data type.) Furthermore, you can specify how the variable is to be allocated by supplying a storage-class attribute in the declaration.

A few examples of PL/I attributes are BIT, CHARACTER, BINARY, DECIMAL, FILE, FLOAT, PRINT, UPDATE, and VALUE. For a complete alphabetic list of the PL/I attributes with their uses, see Section 2.2, “Attributes”.

An identifier can refer to a single variable (called a scalar variable) or to a collection of related variables. Such a collection is called an aggregate. There are two kinds of aggregates:

  • Arrays—all members have the same data type and are referenced by relative position

  • Structures—members can have different data types and are referenced in a hierarchical fashion

The following chapters provide information on these topics:

1.5.1. Preprocessor

PL/I supports an embedded lexical preprocessor, which recognizes a specific set of statements that are executed at compile time. These statements cause the PL/I compiler to include additional text in the source program or to change the values of constant identifiers at compile time.

Preprocessor statements are identified by a leading unquoted percent sign (%) and are terminated by an unquoted semicolon (;), except for %THEN and %IF statements. You can freely intermix preprocessor statements with the rest of the source program statements.

For additional information on the PL/I preprocessor and preprocessor statements, see Chapter 10, Preprocessor .