DocumentationEndless PL/I for OpenVMSReference Manual  ·  ESS-PFVRM-001

Chapter 10

Preprocessor

The PL/I preprocessor permits you to alter a source program at compile time. Preprocessor statements can be mixed with nonpreprocessor statements in the source program, but preprocessor statements are executed only at compile time. Any resulting source program changes are then used for further compilation.

The preprocessor is embedded in the compiler, and so is also called the embedded preprocessor.

During compilation, the preprocessor performs two types of preprocessing:

  • It interprets preprocessor statements, including preprocessor expression evaluation.

  • It replaces the value of preprocessor variables and procedures.

Preprocessor statements allow you to include text from alternative sources (INCLUDE libraries, directories, and the VAX Common Data Dictionary), control the course of compilation (%DO, %GOTO, %IF, and %PROCEDURE), issue user-generated diagnostic messages, and selectively control listings and formats. The preprocessor statements are summarized in Table 10.1, “Summary of PL/I Preprocessor Statements”.

This chapter describes the preprocessor statements and functions.

10.1. Preprocessor Compilation Control

At compile time, preprocessor variables, procedures, and variable expressions are evaluated in the order that they appear in the source text, and the new values are substituted in the source program in the same order. Thus, the course of compilation becomes conditional, and the resulting executable program can have a variety of features. Note that preprocessor variables must be declared and activated before replacement occurs.

For example:

EXAMPLE: PROCEDURE OPTIONS(MAIN);

%DECLARE HOUR FIXED;
%HOUR = SUBSTR(TIME(),1,2);

%IF HOUR > 7 & HOUR < 18
%THEN
%FATAL 'Please compile this outside of prime time';

%DECLARE T CHARACTER;
%ACTIVATE T NORESCAN;

%T='''Compiled on '||DATE()||'''';

DECLARE INIT_MESSAGE CHARACTER(60) VARYING INITIAL(T);

%IF VARIANT() = '' | VARIANT() = 'NORMAL'
%THEN
  %INFORM 'NORMAL';
%ELSE %DO;
  %IF VARIANT() = 'SPECIAL'
    %THEN
      %INFORM 'SPECIAL';
    %ELSE
      %IF VARIANT() = 'NONE'
      %THEN %;
      %ELSE
         %DO;
         %T='''unknown variant '||variant()||'''';
         %WARN t;
         INIT_MESSAGE=INIT_MESSAGE||' with '||T;
         %END;
%END;

PUT SKIP LIST (INIT_MESSAGE);

END EXAMPLE;

This example illustrates several aspects of the preprocessor. First, the programmer specified that this program must be compiled outside of prime time. Second, the VARIANT preprocessor built-in function is used to determine which variant was specified on the command line using the /VARIANT switch. Third, user-generated preprocessor messages remind the programmer which value was given to VARIANT.

Notice the number of apostrophes around the string constant assigned to T. Apostrophes are sufficient if the value of T is used only in a preprocessor user-generated diagnostic message; the value of T is concatenated with nonpreprocessor text and assigned to INIT_MESSAGE. During preprocessing, apostrophes are stripped off string constants. In order to ensure that the run-time program also has apostrophes around the string, additional apostrophes are needed.

10.2. Preprocessor Statements

All preprocessor statements are preceded by a percent sign (%) and terminated by a semicolon (;). All text that appears within these delimiters is considered part of the preprocessor statement and is executed at compile time. For example:

%DECLARE HOUR FIXED;   /* declaration of a preprocessor
                                          single variable */

%DECLARE (A,B) CHARACTER; /* a factored preprocessor
                                             declaration  */

%HOUR = SUBSTR(TIME(),1,2);  /* preprocessor assignment
                               statement using two built-in
                                               functions  */

%STATE: PROCEDURE (X) RETURNS (BIT); /* preprocessor
                                                procedure */

Notice that a percent sign is required only at the beginning of the statement. The percent sign alerts the compiler that until the line is terminated with a semicolon, all subsequent text is preprocessor text. Therefore, no other percent signs are required on the line. However, when you include Common Data Dictionary record definitions, you may need to include the usual PL/I punctuation.

Labels (preceded by a percent sign) are permitted on preprocessor statements and required on preprocessor procedures. As with other labels, preprocessor labels are used as the target of program control statements.

A preprocessor label must be an unsubscripted label constant. The format for a preprocessor label is:

%label: preprocessor-statement;

Table 10.1, “Summary of PL/I Preprocessor Statements” summarizes the preprocessor statements. Each statement is then described in detail in an individual subsection.

Table 10.1. Summary of PL/I Preprocessor Statements
StatementUse
%AssignmentEvaluates a preprocessor expression and gives its value to a preprocessor identifier
%;Null statement, specifies no preprocessor operation
%ACTIVATEMakes the value of declared preprocessor variables eligible for replacement
%DEACTIVATEMakes the value of declared preprocessor variables ineligible for replacement
%DECLAREDefines the preprocessor variable names and identifiers to be used in a PL/I program and specifies the data attributes associated with them
%DICTIONARYSpecifies data definitions to be included from the VAX Common Data Dictionary (CDD)
%DODenotes the beginning of a group of preprocessor statements to be executed as a unit
%ENDDenotes the end of a block or group of statements that started with a %PROCEDURE or a %DO statement
%ERRORGenerates a user-defined error diagnostic message
%FATALGenerates a user-defined fatal diagnostic message
%GOTOTransfers control to a labeled preprocessor statement
%IFTests a preprocessor expression and establishes action to be performed based on the results
%INCLUDECopies the text of an external file into the source file at compile time
%INFORMGenerates a user-defined informational diagnostic message
%[NO]LISTSame as %[NO]LIST_ALL
%[NO]LIST_ALLDoes or does not include CDD records, INCLUDE files, machine code, and source statements in the listing from that point on
%[NO]LIST_DICTIONARYDoes or does not include CDD records in the listing from that point on
%[NO]LIST_INCLUDEDoes or does not include INCLUDE files in the listing from that point on
%[NO]LIST_MACHINEDoes or does not include machine code in the listing from that point on
%[NO]LIST_SOURCEDoes or does not include source code in the listing from that point on
%PAGEProvides listing pagination without form feeds in the source text
%PROCEDUREBegins a preprocessor procedure
%REPLACEAssigns a constant value to an identifier at compile time
%RETURNReturns a value from execution of a preprocessor procedure to the point of invocation
%SBTTLAllows specification of a listing subtitle line
%TITLEAllows specification of a listing title line
%WARNGenerates a user-defined warning diagnostic message

10.2.1. %Assignment Statement

The preprocessor assignment statement gives a value to a specified preprocessor variable.

The format of the assignment statement is:

%target = expression;
target

The name of the preprocessor variable to be assigned a value. It must be an unsubscripted reference to a preprocessor variable.

expression

Any valid preprocessor expression.

For arithmetic operations, only decimal integer arithmetic of precision (10,0) is performed. Each operand and all results are converted, if necessary, to a fixed decimal value of precision (10,0). Fractional digits are truncated.

10.2.2. %Null

The %Null statement performs no action.

The format of the %Null statement is:

%;

The most common use for the %Null statement is as the target statement of a %THEN or %ELSE clause in an %IF statement. For example:

%IF
   ERROR() > 0;
%THEN
   %GOTO FIXIT;
%ELSE
   %;

10.2.3. %ACTIVATE

The %ACTIVATE statement makes preprocessor variable and procedure identifiers eligible for replacement. If the compiler encounters the named identifier after executing a %ACTIVATE statement, it initiates variable replacement.

The format of the %ACTIVATE statement is:

% { ACTIVATE }
  { ACT      }
element
[ RESCAN   ]
[ NORESCAN ]
,...;
element

The name of a previously declared preprocessor identifier and/or a list of identifiers, where the identifiers are separated by commas and the list is enclosed in parentheses.

RESCAN or NORESCAN

Specifies that the preprocessor is to continue or discontinue checking the text for secondary value replacement.

The RESCAN option specifies that preprocessor scanning continue until all possible identifier replacements are completed. RESCAN is the default option.

The NORESCAN option specifies that replacement be done only once; the resulting text is not rescanned for possible further replacement.

An identifier is activated by either a %ACTIVATE statement or a %DECLARE statement. When an activated identifier is encountered by the compiler, in unquoted nonpreprocessor statements, the variable name or procedure reference is replaced by its value. Replacement continues throughout the rest of the source program unless replacement is stopped with the %DEACTIVATE statement.

You can activate several variables with a single statement. For example:

%DECLARE (A,B,C) FIXED;
%ACTIVATE (A,B), C NORESCAN;

Because RESCAN is the default action, this statement activates A and B with the RESCAN option. C is activated, but is not rescanned.

If an identifier that is not a preprocessor variable or procedure is the target of a %ACTIVATE statement, a warning message is issued and the identifier is implicitly declared as a preprocessor variable with the FIXED attribute. Thereafter, the identifier variable is eligible for replacement when activated.

For example:

DECLARE (A,B,C) FIXED;
%DECLARE (A,B) FIXED;
%ACTIVATE (A,B);

%A = 1;
%B = (A + A);

C = A + B;
PUT SKIP LIST (C);    /* C = 3 */

In this example, the activated preprocessor variables A and B are assigned values by the preprocessor. Notice that variables A and B are also declared as nonpreprocessor variables; this establishes them as variables within the nonpreprocessor program.

In the following example, the variable B is deactivated by the %DEACTIVATE statement.

   ⋮
%DEACTIVATE B;
B = 900;
C = A + B;
PUT SKIP LIST (C);    /* C = 901 */
END;

Because the preprocessor variable B is deactivated, the preprocessor assignment statement %B = (A + A) is not in effect and the value of B is taken from the run-time assignment of B = 900. However, the value of A remains 1.

10.2.4. %DEACTIVATE

The %DEACTIVATE statement makes preprocessor variable and procedure identifiers ineligible for replacement. After a variable or procedure has been deactivated, it will not be replaced during preprocessing. Replacement of a deactivated variable or procedure occurs again only after it is reactivated with the %ACTIVATE statement.

The format of the %DEACTIVATE statement is:

% { DEACTIVATE }
  { DEACT      }
element,...;
element

The name of a preprocessor identifier, or a list of identifiers that is enclosed in parentheses. Deactivated elements must have been previously declared preprocessor variables.

For example:

TESTF:PROCEDURE OPTIONS (MAIN);
      DECLARE Y FIXED DECIMAL;
      Y = 10;                  /* initial value: Y = 10 */
      %DECLARE Y FIXED;
      %Y = 3;                  /* replacement value: Y = 3 */
      PUT SKIP LIST(Y);        /* output: Y = 3 */
      %DEACTIVATE Y;
      PUT SKIP LIST(Y);        /* output: Y = 10 */
      END;

In this example, %Y, when activated, replaces all the occurrences of the variable Y by the value assigned to %Y, until %Y is deactivated by the %DEACTIVATE statement. The identifier %Y is implicitly activated when it is declared as a preprocessor identifier.

It is possible to deactivate several variables with a single statement. For example:

%DEACTIVATE (A,B,C,D,E,F);

10.2.5. %DECLARE

The %DECLARE statement establishes an identifier as a preprocessor variable, specifies the data type of the variable, and activates the identifier for replacement. %DECLARE can occur anywhere in a PL/I source program.

The format of the %DECLARE statement is:

% { DECLARE } element
  { DCL     }
[ FIXED     ] ,...;
[ CHARACTER ]
[ BIT       ]
element

The name of a preprocessor identifier or a list of identifiers, which are separated by commas and enclosed in parentheses. You can give elements the attribute BIT, FIXED, or CHARACTER, but you cannot specify precision or length. The compiler supplies the variables with the following implied attributes:

Specified AttributeImplied Attributes
BIT(31) INITIAL ((31)'0'B)
FIXEDDECIMAL (10,0) INITIAL (0)
CHARACTERVARYING (32500) INITIAL (' ')

If no data type is specified, FIXED is assumed.

When a variable is declared in a preprocessor statement, it is activated for replacement and rescanning. The scope of a preprocessor variable is all of the text following the declaration of the variable, unless the variable is declared inside a preprocessor procedure. Using %DECLARE inside of a preprocessor procedure has the effect of declaring a local variable.

For example:

%DECLARE HOUR FIXED;

In this example, HOUR is declared as a preprocessor variable identifier with the FIXED attribute. The compiler supplies the default values that make this declaration the equivalent of the following:

DECLARE HOUR FIXED DECIMAL (10,0) INITIAL (0);

Note

In preprocessor declarations, the attribute FIXED implies FIXED DECIMAL. In nonpreprocessor declarations, FIXED implies FIXED BINARY.

Factored declarations are permitted and follow the same usage rules as nonpreprocessor declarations. For example:

%DECLARE (A,B) CHARACTER, C BIT;

Both A and B are declared with the CHARACTER attribute. The compiler supplies default values that make this declaration the equivalent of the following:

%DECLARE (A,B) CHARACTER VARYING(32500) INITIAL(''),
         C BIT(31)INITIAL((31)'0'B);

10.2.6. %DICTIONARY

The %DICTIONARY statement causes VAX Common Data Dictionary (CDD) data definitions to be incorporated into the current PL/I source file during compilation. The statement can occur anywhere in a PL/I source file. For information on using PL/I with CDD, see Appendix E in the Endless PL/I for OpenVMS User Manual.

The format of the %DICTIONARY statement is:

%DICTIONARY cdd-path;
cdd-path

Any preprocessor expression. It is evaluated and converted to a CHARACTER string if necessary. The resulting character string is interpreted as the full or relative path name of a CDD object. The resultant path name must conform to all rules for forming VAX CDD path names. See the Common Data Dictionary Utilities Manual for details.

For example, assume that you have a record with the following path name:

CDD$TOP.SALES.JONES.SALARY

You can then specify a relative path name as follows:

%DICTIONARY 'SALARY';

Or you can specify an absolute path name as follows:

%DICTIONARY '_CDD$TOP.SALES.JONES.SALARY';

The compiler extracts the record definition from the CDD and inserts the PL/I structure declaration corresponding to the record description in the PL/I program.

If the %DICTIONARY statement is not embedded in a PL/I language statement, then the resulting structure is declared with the logical level 1 and the BASED storage attribute is furnished. The logical member levels are incremented from 2. For example:

DECLARE PRICE FIXED BINARY(31);
%DICTIONARY 'ACCOUNTS';

This would result in a declaration of the following form:

DECLARE PRICE FIXED BINARY(31);
DECLARE 1 ACCOUNTS BASED,
          2 NUMBER,
            3 LEDGER CHARACTER(3),
            3 SUBACCOUNT CHARACTER(5),
          2 DATE CHARACTER(12),
             .
             .
             .

Notice that in the above example, ACCOUNTS is a relative dictionary path name.

If the %DICTIONARY statement is embedded in a PL/I language statement, as in a structure declaration, then the resulting structure is declared with no logical level and no storage attribute. Logical member numbers are supplied and incremented from 100. For example:

DECLARE 1 COMMON_INTERFACES STATIC EXTERNAL,
          %DICTIONARY 'ACCOUNTS';   ,
          %DICTIONARY 'ADDRESSES';  ;

Notice the syntax in the above %DICTIONARY example. The %DICTIONARY statement is terminated with the preprocessor terminator semicolon before the normal PL/I line punctuation. The normal PL/I punctuation must also be included so that the final structure declaration will contain proper PL/I punctuation. The previous declaration would result in a declaration of the following form:

DECLARE 1 COMMON_INTERFACES STATIC EXTERNAL,
          100 ACCOUNTS,
              101 NUMBER,
                  102 LEDGER CHARACTER(3),
                  102 SUBACCOUNT CHARACTER (5),
              101 DATE CHARACTER(12),
                 .
                 .
                 .
          100 ADDRESSES,
             .
             .
             .

The CDD supports data types that are not native to PL/I. If a data definition contains an unsupported data type, PL/I makes the unsupported data type accessible by declaring it as data type BIT_FIELD or data type BYTE_FIELD. PL/I does not attempt to approximate a data type that is not supported by PL/I. For example, an F_FLOATING_COMPLEX number is declared BYTE_FIELD(8), not (2)FLOAT(24).

Note, however, that use of these two data types is limited. Data declared with the BIT_FIELD or BYTE_FIELD data type can be manipulated only with the PL/I built-in functions ADDR, INT, POSINT, SIZE, and UNSPEC. A variable declared with either of these data types can be passed as a parameter provided the parameter is declared as ANY. Thus, references to data declared as BIT_FIELD or BYTE_FIELD are limited to contexts in which the interpretation of a data type is not applied to the reference.

PL/I ignores CDD features that are not supported by PL/I, but issues error messages when the features conflict with PL/I.

When you extract a record definition from the CDD, you can choose to include this translated record in the program listing by using the /LIST/SHOW=DICTIONARY qualifiers in the PLI command line. Even if you choose not to list the extracted record, the names, data types, and offsets of the CDD record definition are displayed in the program listing allocation map.

CDD data definitions can contain explanatory text in the CDDL DESCRIPTION IS clause. This text is included in the PL/I listing comments, if /LIST/SHOW=DICTIONARY is specified. For example, you could use CDDL comments to indicate the data type of each structure and member. The punctuation for CDDL comments is the same as for other PL/I programs: the slash-asterisk (/*) and the asterisk-slash (*/).

10.2.7. %DO

The %DO statement begins a preprocessor DO-group, a sequence of statements terminating with the %END statement. The preprocessor DO-group must be a simple DO-group and is noniterative, but it can be usefully combined with an %IF statement.

The format of the %DO statement is:

  • %DO;

  • ⋮

  • %END;

You can include both preprocessor and nonpreprocessor text in a preprocessor DO-group. For example:

%DECLARE T CHARACTER;          /* declare T */
%ACTIVATE T NORESCAN;          /* activate T for replacement */
   ⋮
%IF VARIANT() = 'NONE';
   %THEN
   %DO;
   %T = '''unknown variant'''; /* assign string to T */
   %WARN T;                    /* output unknown variant
                                  warning at compile time */
   INIT_MESSAGE = INIT_MESSAGE||' with '||T; /* assign
                               value of T to nonpreprocessor
                                    variable */
   %END;

This preprocessor DO-group performs several steps. First, a string constant is assigned to T. Then the value of T is used in a preprocessor user-generated diagnostic message. This message is issued at compile time to warn the programmer that the program is compiled with an unknown variant. Finally, the value of T is concatenated with a nonpreprocessor string constant. INIT_MESSAGE, including the value of T, is part of the run-time image.

10.2.8. %END

The %END statement terminates a preprocessor procedure or DO-group.

The format of the %END statement is:

%END;

Preprocessing then continues with the next executable preprocessor statement.

10.2.9. %ERROR

The %ERROR statement provides a diagnostic error message during program compilation.

The format of the %ERROR statement is:

%ERROR preprocessor-expression;
preprocessor-expression

A maximum of 64 characters giving the text of the error message to be displayed. Messages of more than 64 characters are truncated.

Returned Message

The message displayed by %ERROR is:

%PLIG-E-USERDIAG, preprocessor-expression

Compilation errors that result in the display of the %ERROR statement increment the informational diagnostic count displayed in the compilation summary, and inhibit production of an object file.

10.2.10. %FATAL

The %FATAL statement provides a diagnostic fatal message during program compilation.

The format of the %FATAL statement is:

%FATAL preprocessor-expression;
preprocessor-expression

The text of the fatal message you want displayed. The text is a character string with a maximum length of 64 characters. It is truncated if necessary.

Returned Message

The message displayed by %FATAL is:

%PLIG-F-USERDIAG, preprocessor-expression

Compilation errors that result in a fatal error terminate compilation after the message is displayed.

10.2.11. %GOTO

The %GOTO statement causes the preprocessor to interrupt its sequential processing of source text and continue processing at the point specified in the %GOTO statement. A %GOTO is useful for avoiding large segments of text in the source program.

The format of the %GOTO statement is:

%GOTO label-reference;
label-reference

A label of a preprocessor statement. The label reference determines the point to which the compiler processing will be transferred.

Nonlocal %GOTOs are not permitted. In other words, if a %GOTO is used within a preprocessor procedure, control must not be passed out of the containing procedure. Also, a %GOTO must not transfer control into a preprocessor procedure.

The following example illustrates transfers (forward and backward) and the use of %GOTO:

%TEXT:PROCEDURE RETURNS(CHARACTER);
         ⋮
%CHANG_TEXT: DO;
   ⋮
%IF WARN() = 5
    %THEN
         %GOTO CHANG_TEXT;

    %ELSE
         %GOTO INSERT_TEXT;
             ⋮


%INSERT_TEXT: DO;
   ⋮
%END;

Depending on the status of the %IF statement in this example, program compilation takes one of two courses. Control is transferred either forward to the statement labeled INSERT_TEXT or backward to the statement labeled CHANG_TEXT. The compiled program will then include one of the two blocks, but not both. Notice also in this example that the preprocessor built-in function WARN is used to determine preprocessor action, which makes the program self-diagnostic.

If program text is not compiled because the %GOTO statement transferred control over it, the compiler still checks the basic syntax of all statements. Therefore, comment delimiters and parentheses must balance, apostrophes must be paired correctly, and all statements must end with a semicolon.

10.2.12. %IF

The %IF statement controls the flow of program compilation according to the scalar bit value of a preprocessor expression. The %IF statement tests the preprocessor expression and performs the specified action if the result of the test is true.

The format of the %IF statement is:

%IF test-expression %THEN action [%ELSE action];
test-expression

Any valid preprocessor expression that yields a scalar bit value. If any bit of the value is 1, then the expression is true; otherwise, the expression is false.

action

A single, unlabeled preprocessor statement, %DO-group, %GOTO statement, or a preprocessor null statement. The specified action must not be an %END statement.

The %IF statement evaluates the preprocessor test expression. If the expression is true, the action specified following the keyword %THEN is compiled. Otherwise, the action, if any, following the %ELSE keyword is compiled. In either case, compilation resumes at the first executable statement following the termination of the %IF statement, unless a %GOTO in one of the action clauses causes compilation to resume elsewhere.

If an action is not compiled because the alternative action was compiled instead, the compiler still checks the basic syntax of all statements. Therefore, comment delimiters and parentheses must balance, apostrophes must be paired correctly, and all statements must end with a semicolon.

10.2.13. %INCLUDE

The %INCLUDE statement incorporates text from other files into the current source file during compilation. It can occur anywhere in a PL/I source file; it need not be part of a procedure.

The format of the %INCLUDE statement is:

%INCLUDE { 'file-spec'                 };
         { module-name                 }
         { 'library-name(module-name)' }
file-spec

A file specification enclosed in apostrophes interpreted as the complete file specification or path, including the device, directory, and file name. For the specification is subject to logical name translation and the application of default values by the OpenVMS operating system.

module-name

The 1- to 31-character name of a text module in a library of included files and/or other text modules.

library-name

The library containing the specified text module. Enclose the library and module in apostrophes. If you do not specify the library in the %INCLUDE statement, and if the text module is not in PLI$STARLET or in the text library pointed to by PLI$LIBRARY, you must specify the name of the library containing the module in the PLI compilation command.

Examples

%INCLUDE 'SUM.PLI';

This statement copies the contents of the file SUM.PLI into the current file during compilation.

%INCLUDE SYSTEM_PROCEDURES;

This statement includes a module from a text module library. The library containing the module SYSTEM_PROCEDURES must be present in the command that compiles this program, or the logical name PLI$LIBRARY can point to the library that contains it.

%INCLUDE 'PROJECT_LIBRARY(MY_MODULE)';

This statement includes module MY_MODULE from the text library PROJECT_LIBRARY.TLB in the default directory.

Restrictions

%INCLUDE statements can be nested up to a maximum of four levels.

10.2.14. %INFORM

The %INFORM statement specifies a user-written diagnostic informational message to be displayed during program compilation.

The format of the %INFORM statement is:

%INFORM preprocessor-expression;
preprocessor-expression

The text of the informational message displayed. The text is a character string of up to 64 characters. The string is truncated if necessary.

Returned Message

The format of the message to be displayed by %INFORM is:

%PLIG-I-USERDIAG, preprocessor-expression

The %INFORM statement increments the informational diagnostic count displayed in the compilation summary.

10.2.15. %LIST_xxx

The %LIST_xxx statement (where xxx is either ALL, DICTIONARY, INCLUDE, MACHINE, or SOURCE) enables the selective listing display of INCLUDE file contents, extracted Common Data Dictionary (CDD) record descriptions, machine code, and source code. The %LIST_xxx statement has a number of forms, each of which enables listing control for specific portions of the source text.

The format of the %LIST_xxx statements are:

{ %LIST_ALL;        }
{ %LIST_DICTIONARY; }
{ %LIST_INCLUDE;    }
{ %LIST_MACHINE;    }
{ %LIST_SOURCE;     }

You must compile the program with the appropriate value specified for the /SHOW qualifier before the above statements can be effective.

The %LIST_xxx form of each statement enables the appearance of the specified information starting with the listing line following the %LIST_xxx statement. If you previously specified %NOLIST_xxx, the %LIST_xxx statement has the effect of reenabling the display.

The text displayed with each form of the %LIST_xxx statement is summarized as follows:

  • %LIST_ALL displays all of the following information. You can shorten this statement to %LIST.

  • %LIST_DICTIONARY displays the PL/I translation of an included Common Data Dictionary record.

  • %LIST_INCLUDE displays the contents of INCLUDE files and modules in the program listing.

  • %LIST_MACHINE displays the machine code generated during compilation. For PL/I for RISC ULTRIX, see the -S option in the pli(1) manual page.

  • %LIST_SOURCE displays source program statements in the program listing.

To disable a %LIST_xxx statement, specify %NOLIST_xxx at the appropriate line in the source text.

%LIST_xxx statements cannot be nested.

10.2.16. %NOLIST_xxx

The %NOLIST_xxx statement (where xxx is either ALL, DICTIONARY, INCLUDE, MACHINE, or SOURCE) disables the selective listing display of INCLUDE file contents, extracted Common Data Dictionary (CDD) record descriptions, machine code, and source code. The %NOLIST_xxx statement has a number of forms; each disables listing control for specific portions of the source text.

The format of the %NOLIST_xxx statements are:

{ %NOLIST_ALL;        }
{ %NOLIST_DICTIONARY; }
{ %NOLIST_INCLUDE;    }
{ %NOLIST_MACHINE;    }
{ %NOLIST_SOURCE;     }

You must compile the program with the /SHOW qualifier before any of these statements can be effective.

The %NOLIST_xxx form of each statement disables the appearance of the specified information starting with the listing line following the %NOLIST_xxx statement. If you previously specified %LIST_xxx, the %NOLIST_xxx statement has the effect of disabling the display.

The following summarizes the text suppressed with each form of the %NOLIST_xxx statement:

  • %NOLIST_ALL does not display any of the following information. You can shorten this statement to %NOLIST.

  • %NOLIST_DICTIONARY does not display the PL/I translation of an included Common Data Dictionary record.

  • %NOLIST_INCLUDE does not display the contents of INCLUDE files and modules in the program listing.

  • %NOLIST_MACHINE does not display the machine code generated during compilation.

  • %NOLIST_SOURCE does not display source program statements in the program listing.

To cancel the effect of any of the %NOLIST_xxx statements, specify %LIST_xxx (see Section 10.2.15, “%LIST_xxx”) at the appropriate line in the source text.

10.2.17. %PAGE

The %PAGE statement provides listing pagination without inserting form-feed characters into the source text.

The format of the %PAGE statement is:

%PAGE;

The first source record following the record that contains the %PAGE statement is printed on the first line of the next page of the source listing.

10.2.18. %PROCEDURE

A preprocessor procedure is a sequence of preprocessor statements headed by a %PROCEDURE statement and terminated by a %END statement. A preprocessor procedure executes only at compile time. Invocation is similar to a function reference and occurs in two ways:

  • Preprocessor statements can invoke preprocessor procedures. In addition, preprocessor statements from within preprocessor procedures can invoke other preprocessor procedures.

  • Statements from the source program can invoke preprocessor procedures.

The format of the %PROCEDURE statement is:

%label:PROCEDURE [(parameter-identifier,...)]
[STATEMENT]
RETURNS ( { CHARACTER } );
          { FIXED     }
          { BIT       }
     ⋮
[%]RETURN (preprocessor-expression);
     ⋮
[%]END;
label

An unsubscripted label constant. A preprocessor procedure is invoked by the appearance of the label name on the %PROCEDURE statement and terminated by the corresponding %END statement. The label name must be active if invoked from a nonpreprocessor statement.

Preprocessor label names can be activated and deactivated, but cannot be specified in a %DECLARE statement.

parameter-identifier

The name of a preprocessor identifier. Each identifier is a parameter of the procedure.

STATEMENT

A preprocessor procedure option. The STATEMENT option permits the use of a keyword argument list followed by an optional positional argument list in the preprocessor procedure invocation. The STATEMENT option returns strings that can be used as PL/I statements at run time.

RETURNS

A preprocessor procedure attribute. The RETURNS attribute defines the data type to be returned to the point of invocation in the source code. If you specify a data type that is inconsistent with the returned value, a conversion error may result.

preprocessor-expression

Value to be returned to the invoking source code. The preprocessor expression must be specified. The preprocessor expression is converted to the data type specified in the RETURNS option and is returned to the point of invocation. Therefore, the expression must be capable of being converted to CHARACTER(32500), FIXED(10), or BIT(31).

The %PROCEDURE statement defines the beginning of a preprocessor procedure block and specifies the parameters, if any, of the procedure. Because the preprocessor procedure is always invoked as a function, the %PROCEDURE statement must also specify (via the RETURNS option) the data type attributes of the value that is returned to the point of invocation.

For example:

%A_VAR = A_PROC();

In this statement, the preprocessor procedure A_PROC is invoked and evaluated, and the result is returned and assigned to the preprocessor variable A_VAR.

As with other PL/I procedures, a parenthesized parameter list specifies the parameters that the preprocessor procedure expects when it is invoked. Each preprocessor parameter specifies the name of a variable declared in the preprocessor procedure. The preprocessor parameters must correspond one-to-one with arguments specified for the preprocessor procedure when it is invoked, except when the STATEMENT option is used.

The value to be returned to the invoking source code is converted to the data type specified in the RETURNS option. The return value replaces the preprocessor procedure reference in the invoking source code. Preprocessor procedures cannot return values through their parameter list. The return value must be capable of being converted to one of the data types CHARACTER, FIXED, or BIT. The maximum precision of the value returned by the %RETURNS statement is BIT(31), CHARACTER(32500), or FIXED(10).

Preprocessor procedures cannot be nested. The scope of a preprocessor procedure is the procedure itself; that is, variables, labels, and any %GOTO statements used inside of the procedure must be local.

A preprocessor procedure is invoked by the appearance of its entry-name and list of arguments. If the reference occurs in a nonpreprocessor statement, the entry name must be active before the preprocessor procedure is invoked. If the entry name is activated with the RESCAN option, the value of the preprocessor procedure is rescanned for further possible preprocessor variable replacement and procedure invocation. You can invoke preprocessor procedures recursively.

When a preprocessor procedure (with or without the STATEMENT option) is invoked from a preprocessor statement, each argument is treated as an expression and the result of executing the preprocessor procedure is returned to the statement containing the invocation.

When a preprocessor procedure is invoked from nonpreprocessor source text, the arguments are interpreted as character strings and are delimited by the appearance of a comma or a right parenthesis occuring outside of balanced parentheses. For example, the positional argument list (Q(E,D), XYZ) has two arguments; the strings 'Q(E,D)' and 'XYZ'.

Examples

%A1: PROCEDURE RETURNS(FIXED);
     DECLARE (A,B,C) FIXED;
        A = 2;
        B = 10;
        C = A + B;
        RETURN(C);
     END;

This example declares the preprocessor procedure A1 and specifies that the procedure return a fixed decimal result after the preprocessor statements within the procedure have been executed.

The procedure returns the value 12 to the point of invocation. Note that the leading percent signs, normally associated with preprocessor statements, are not required within a preprocessor procedure.

PPFIB: PROCEDURE OPTIONS (MAIN);
       DECLARE Y CHAR(14) INITIAL('Fibonacci Test'); ❶
       %DECLARE Y FIXED; ❷
       %F: PROCEDURE(X) RETURNS (FIXED); ❸
           DECLARE X FIXED;
           IF (X <= 1)
              THEN RETURN(1);
              ELSE RETURN(F(X-1)+F(X-2));
           END;           /* End preprocessor procedure */
       %Y = F(10); ❹
       PUT SKIP LIST(Y);
       %Y = F(11); ❺
       PUT SKIP LIST(Y);
       %Y = F(12); ❻
       PUT SKIP LIST(Y);
       %DEACTIVATE Y; ❼
       PUT SKIP LIST(Y); ❽
       END;               /* End run-time procedure */

This example uses a preprocessor procedure to return a Fibonacci number. The recursive preprocessor procedure labeled %F is invoked to return a single value, a Fibonacci number, to the point of invocation. The following notes correspond to the example:

❶

The run-time variable Y is declared with the CHARACTER attribute and initialized to Fibonacci Test.

❷

The preprocessor variable Y is declared with the FIXED attribute, which implies FIXED DECIMAL (10,0). This declaration automatically activates the preprocessor variable Y.

❸

The preprocessor procedure F is defined. The percent sign for the END statement is optional in a preprocessor procedure.

Note that this procedure is recursive.

❹

The preprocessor procedure is called, passed the value 10, and the 10th number in the Fibonacci series is calculated. The resulting value is assigned to the preprocessor variable Y.

Because the preprocessor variable Y is active by default, the compiler replaces the occurrence of Y in the PUT statement with the new preprocessor Y value.

❺

Step 4 is repeated for the value 11.

❻

Step 4 is repeated for the value 12.

❼

The preprocessor variable Y is deactivated. No more scanning or replacement occurs. The preprocessor variable Y retains its final replacement value, 233.

❽

The run-time value of Y (Fibonacci Test) is output.

The output from this program is:

89
144
233
Fibonacci Test

Using the STATEMENT Option

All preprocessor procedures (with or without the STATEMENT option) return a value to the invoking source code; that is, they are function procedures. Through the use of the STATEMENT option, the argument list to a preprocessor procedure can be a keyword argument list. Keyword argument lists are unique to preprocessor procedures and provide a powerful tool for manipulating PL/I.

A keyword argument list ends with a semicolon rather than the right parenthesis. In this way, the STATEMENT option permits you to use a preprocessor procedure as if it were a statement. Consequently, preprocessor procedures using the STATEMENT option permit you to extend the PL/I language by simulating features that may not otherwise be available.

Preprocessor procedures can have one of two distinctly different types of argument lists: positional or keyword. Positional argument lists (ending with a right parenthesis) use parameters sequentially, as in a parenthesized list. You can use positional argument lists in any preprocessor procedure. Keyword argument lists (ending with a semicolon) use parameters in any order, as long as each keyword matches the name of a parameter. This permits the option of specifying the order in which parameters are passed. You can use keyword argument lists only when the preprocessor procedure contains the STATEMENT option and is invoked from a nonpreprocessor statement.

When a preprocessor procedure is invoked from a nonpreprocessor statement, the STATEMENT option permits the use of a keyword argument list that follows the optional positional argument list in a preprocessor procedure invocation.

When you use keyword arguments in nonpreprocessor statements, the keywords can be used in any order. The following reference examples would produce a variety of results with positional arguments, because values would be used sequentially. Keyword arguments produce consistent results because keyword parameters are matched with keyword arguments.

%B: PROCEDURE (ALPHA, BETA, GAMMA) STATEMENT...;
    DECLARE (ALPHA, BETA, GAMMA) FIXED;
       ⋮
    END;

B(1,2,3);
B ALPHA(1) GAMMA(3) BETA(2);
B(1) GAMMA(3) BETA(2);
B (,2,3) ALPHA(1);

The next example shows a more common use of the STATEMENT option; to generate PL/I source statements that define a unique run time feature. The preprocessor procedure APPEND returns a string, which is incorporated into the source program at compile time. At run time, the returned string is used as a PL/I function.

This preprocessor procedure permits a varying string to accumulate text up to its maximum size without danger of undetected truncation. Normally, strings that exceed their maximum size are truncated. The text returned by the preprocessor procedure provides the run-time program with a way to handle truncation. If the string would be truncated, a message is printed and the FINISH condition is signaled.

%APPEND: PROCEDURE (string,to) STATEMENT RETURNS(CHARACTER); ❶

    %DECLARE (string,to) CHARACTER; ❷
    %RETURN (
        'DO;'|| ❸
        'IF LENGTH('||string||')+LENGTH('||to||') > SIZE('||to||')-2'||
        ' THEN DO;'||
           'PUT SKIP LIST (''Buffer overflowed appending to '||to||''');'||
           'SIGNAL FINISH;'|| ❹
           'END;'||
        'ELSE '||to||' = '||to||'||'||string||';'||
        'END;'
              );
     %END;

The following notes are keyed to this example:

❶

The preprocessor procedure APPEND is defined with the parameters 'string' and 'to' and the STATEMENT option.

❷

'String' and 'to' are declared as parameters within the preprocessor procedure.

❸

The %RETURN statement returns the value contained by the parentheses. This text then becomes part of the PL/I nonpreprocessor source program.

Notice the punctuation within the character string returned by %RETURN. At compile time, single quotes are stripped when the text is incorporated into the run-time PL/I program. In addition, the semicolon that delimits the invocation is not retained when the replacement takes place. All customary PL/I punctuation must be included in the character string.

❹

If the current varying string and the additional string together are greater than the maximum length of the varying string, an informational message is printed and the FINISH condition is signaled.

The following invocations of the preprocessor procedure APPEND are all equivalent:

APPEND STRING('New String') TO (My_string);
APPEND TO(My_string) STRING('New String');
APPEND('New String') TO(My_string);

Notice that if you have a preprocessor procedure (A) with a label that is the same as the name of a keyword argument in another preprocessor procedure (B) with the STATEMENT option, then when B is invoked the keyword argument is treated as a call to procedure A, and not as a keyword parameter in B.

10.2.19. %REPLACE Statement

The preprocessor %REPLACE statement specifies that an identifier is a constant of a given value. It can be used anywhere within a procedure or anywhere in a PL/I source file.

Beginning at the point at which a %REPLACE statement is encountered, PL/I replaces all occurrences of the specified identifier with the specified constant value, until the end of compilation.

The format of the %REPLACE statement is:

%REPLACE identifier BY constant-value;
identifier

Any valid PL/I identifier. PL/I keywords are not valid identifiers in a %REPLACE statement. The identifier must not be the name of a declared preprocessor or program variable. PL/I permits multiple %REPLACE statements and %REPLACE statements that redefine the %REPLACE identifier.

constant-value

Any valid character-string, bit-string, or arithmetic constant.

Integer constants that are given values by %REPLACE statements are valid in constant expressions. For example:

%REPLACE PREFIX BY 8;

DECLARE BUFFER CHARACTER( 80 + PREFIX);

When the program containing these lines is compiled, the variable BUFFER is declared with a length of 88 characters.

10.2.20. %RETURN Statement

The %RETURN statement terminates execution of the current preprocessor procedure.

The format of the %RETURN statement is:

[%]RETURN (preprocessor-expression);
preprocessor-expression

Value to be returned to the invoking procedure. The preprocessor expression must be specified. The preprocessor expression is converted to the data type specified in the RETURNS option, and the value of the expression is returned to the point of invocation. Therefore, the expression must be capable of being converted to CHARACTER (32500) VARYING, FIXED (10), or BIT (31).

The value returned by a preprocessor procedure cannot contain preprocessor statements.

When the value of the evaluated preprocessor expression is passed back to the point of invocation, control returns to the evaluation of the statement that contained the reference to the preprocessor procedure.

Within a preprocessor procedure, the leading percent sign (%) is optional.

Multiple %RETURN statements are permitted in preprocessor procedures. See Section 10.2.18, “%PROCEDURE” for examples of %RETURN.

10.2.21. %SBTTL

The %SBTTL statement allows specification of an arbitrary compile-time string for the listing subtitle line. PL/I uses the procedure IDENT, or 01 if no IDENT was specified. If %SBTTL is used, the specified subtitle appears to the right of IDENT or 01. Subtitles do not appear on the first page of the listing file.

The format of the %SBTTL statement is:

%SBTTL preprocessor-expression
preprocessor-expression

A character string with a maximum length of 30 characters. It is truncated if necessary.

10.2.22. %TITLE

The %TITLE statement allows specification of an arbitrary compile-time string for the listing title line. If %TITLE is used, the specified title appears to the right of the customary title. (If no TITLE option is specified, PL/I uses the name of the first level-1 procedure in the source program as the title.)

The format of the %TITLE statement is:

%TITLE preprocessor-expression
preprocessor-expression

A character string with a maximum length of 30 characters. It will be truncated if necessary.

10.2.23. %WARN

The %WARN statement provides a diagnostic warning message during program compilation.

The format of the %WARN statement is:

%WARN preprocessor-expression;
preprocessor-expression

The text of the warning message to be displayed. The text is a character string with a maximum length of 60 characters. It is truncated if necessary.

Returned Message

The message displayed by %WARN is:

%PLIG-W-USERDIAG, preprocessor-expression

The %WARN statement increments the warning diagnostic count displayed in the compilation summary.

10.3. User-Generated Diagnostic Messages

The PL/I embedded preprocessor provides four statements that permit user-generated diagnostic capability: %INFORM, %WARN, %ERROR, and %FATAL. Preprocessor diagnostic messages are compile-time messages, but you define the circumstances that invoke the message and the text displayed.

The action of each statement is to generate a diagnostic message of the appropriate severity level, with the preprocessor expression as the text of the message.

Examples

The first example shows how %INFORM can be used to return the value of VARIANT.

%IF VARIANT() = '' | VARIANT() = 'NORMAL'
%THEN
      %INFORM 'NORMAL';

In this example, the %INFORM diagnostic message is used to let the programmer know that compilation is continuing according to a normal plan.

If the value of VARIANT is not specified at compile time or if the value is 'NORMAL', then the following informational message is issued:

%PLIG-I-USERDIAG, NORMAL

In the following example, an unknown variant is included at compile time. The %WARN statement issues a compile-time warning diagnostic message and saves the message so that when the program is run, the appropriate text is output by the program.

DECLARE INIT_MESSAGE CHAR(40) VARYING INITIAL (T);
   ⋮
%IF VARIANT() = 'NONE';
%THEN %;
%ELSE
      %DO;
      %T = '''unknown variant''';
      %WARN T;
      INIT_MESSAGE = 'Compiled with '||T;
      %END;

PUT SKIP LIST (INIT_MESSAGE);

The preprocessor built-in functions INFORM, WARN, and ERROR return the number of user-generated diagnostics issued at any specified point during compilation. Therefore, you can use user-generated diagnostics to control the course of compilation. For example:

%IF WARN() > 5
%THEN
     %GOTO change_text;
%ELSE;

This example specifies that compilation take a different course if there are more than five warning messages at that point in program compilation. If there are fewer than five warnings, then compilation proceeds along the current path.

%IF ERROR() >= 1
%THEN
     %FATAL 'Ending Compilation';

This example stops compilation if there is an error that would inhibit the production of an object file.

User-generated diagnostic messages increment the count displayed in the diagnostic summary.

10.4. Preprocessor Built-In Functions

A number of PL/I built-in functions are available for use at compile time. Since preprocessor built-in functions work the same way as run-time PL/I built-in functions, refer to Chapter 11, Built-In Functions, Subroutines, and Pseudovariables for detailed descriptions of the functions.

The built-in functions are summarized in Table 10.2, “Summary of PL/I Preprocessor Built-In Functions” according to the following functional categories:

  • Arithmetic built-in functions provide information about the properties of arithmetic values, or perform common arithmetic calculations.

  • String-handling built-in functions process character-string and bit-string values.

  • Conversion built-in functions convert data from one data type to another.

  • Timekeeping built-in functions return the system date and time of day.

  • Miscellaneous built-in functions are specifically preprocessor built-in functions.

Table 10.2. Summary of PL/I Preprocessor Built-In Functions
CategoryFunction ReferenceValue Returned
ArithmeticABS(x)Absolute value of x
 MAX(x1,x2)Larger of the values x1 and x2
 MIN(x1,x2)Smaller of the values x1 and x2
 MOD(x,y)Value of x modulo y
 SIGN(x)-1, 0, or 1 to indicate the sign of x
String-HandlingCOPY(s,c)c copies of specified string s
 INDEX(s,c[,p])Position of the character string c within the string s, starting at position p
 LENGTH(s)Number of characters or bits in the string s
 LTRIM(s,[b])Removes blanks from left of string s; or if b is supplied, removes string b from left of string s
 REVERSE(s)Reverse of the source character string or bit string
 RTRIM(s,[e])Removes blanks from right of string s; or if e is supplied, removes string e from right of string s
 SEARCH(s,c[,p])Position of the first character in s, starting at position p, that is found in c
 SUBSTR(s,i[,j])Part of string s beginning at i for j characters
 TRANSLATE(s,c[,d])String s with substitutions defined in c and d
 TRIM(s[,e,f])String s with all characters in e removed from the left and all characters in f removed from the right
 VERIFY(s,c[,p])Position of the first character in s, starting at position p, which is not found in c
ConversionBYTE(x)ASCII character represented by the integer x
 DECODE(c,r)Fixed binary value of the character string c converted to a base r number
 ENCODE(i,r)Character string representing the base r number that is equivalent to the fixed binary expression i
 RANK(c)Integer representation of the ASCII character c
TimekeepingDATE()System date of compilation in the form YYMMDD
 DATETIME()System date and time of compilation in the form CCYYMMDDHHMMSSXX
 TIME()System time of day of compilation in the form HHMMSSXX
MiscellaneousERROR()Count of user-generated diagnostic error message
 INFORM()Count of user-generated diagnostic informational message
 LINE()Line number in source program that contains the end of a specified preprocessor statement
 VARIANT()String result representing the value of the /VARIANT command qualifier
 WARN()Count of user-generated diagnostic warning message