Appendix A: Open PL/I Macro Preprocessor

This appendix describes the Open PL/I Macro Preprocessor and defines the statements, procedures, and built-ins available for use with the preprocessor.

For information on invoking the Open PL/I Macro Preprocessor refer to the chapter Using Open PL/I in your Open PL/I User's Guide.

Overview

The Open PL/I Macro Preprocessor enables the PL/I programmer to have control of the source program during compile time. It is executed prior to the compilation, and its output is passed to the Open PL/I Compiler.

Preprocessor I/O

The preprocessor input is a file containing an Open PL/I source module, possibly including Open PL/I Macro Preprocessor statements. The preprocessor output is an Open PL/I source module containing no further preprocessor statements, but with the source code modified according to the semantics of the preprocessor statements that were encountered in the input file.

Preprocessor Statements

Preprocessor statements, with the exception of preprocessor procedures, are executed as they are encountered. Preprocessor procedures must be invoked in order to be executed. Preprocessor statements, except those in preprocessor procedures, begin with a percent symbol (%). Blanks, tabs, and returns may separate the percent symbol from the rest of the statement, except where indicated.

The preprocessor executes preprocessor statements and alters the input text accordingly. Preprocessor statements can cause parts of the input text to be altered in any of the following ways:

Listing Control Statements

Listing control statements are not copied to the preprocessor output. A warning message is given when these are found. These statements are:

Preprocessor Scan

The preprocessor starts its scan at the beginning of its input and proceeds sequentially. When preprocessor statements are found they are executed immediately (except for preprocessor procedures, which are executed only when referenced).

An identifier that matches the name of an active preprocessor variable is replaced in the preprocessor output by the value of the variable. When an identifier matches the name of an active preprocessor function (either programmer-written or built-in) the function is invoked and the invocation is replaced by the returned value. For a discussion about activation, replacement, and rescanning, see the %ACTIVATE statement.

Preprocessor Variables and Data Elements

Preprocessor local variables function like static storage, rather than automatic storage, so values are maintained across invocations of a preprocessor procedure.

Preprocessor names are formed according to the rules for other Open PL/I names.

A preprocessor variable is specified in a %DECLARE statement with the FIXED, CHARACTER, or BIT attribute. No other attributes can be declared for a preprocessor variable.

A preprocessor variable declared with the FIXED attribute is given the PL/I attribute DECIMAL and precision (5,0). Fixed decimal constants in preprocessor expressions must be integers.

A preprocessor variable declared with the CHARACTER attribute is given the PL/I VARYING attribute with certain restrictions on its length, as described elsewhere in this manual. String repetition factors are not allowed for character constants in preprocessor expressions.

There are no preprocessor bit variables. However, bit constants are allowed. They are converted to their corresponding decimal value and treated as fixed constants. Only the rightmost 17 digits are used. A conversion result greater than 99999 is given that value. String repetition factors are not allowed for bit constants in preprocessor expressions.

Preprocessor References and Expressions

Preprocessor references and expressions are written and evaluated the same way as PL/I references and expressions are, with the following restrictions:

Scope of Preprocessor Names

The scope of a preprocessor name is determined by where it is declared:

Preprocessor Statements

This section lists alphabetically, the preprocessor statements and discusses each. The characters /* introduce a comment, which terminates with the characters */. Comments do not nest and can appear within a preprocessor statement wherever blanks can appear. Comments within preprocessor statements are not inserted into the preprocessor output text.

%ACTIVATE

Purpose

Renders identifiers active and eligible for replacement.

Syntax
%[label:]...ACTIVATE ident[RESCAN|NORESCAN][,ident[RESCAN|NORESCAN]]...;

Abbreviation(s): %ACT for %ACTIVATE.

Parameters

Each identifier must be a preprocessor variable, a preprocessor procedure name, or a preprocessor built-in function name.

Description

When a preprocessor variable is declared, it is automatically activated. It can be deactivated by use of the %DEACTIVATE statement and reactivated by use of the %ACTIVATE statement.

If neither RESCAN nor NORESCAN is specified, RESCAN is assumed. RESCAN implies that, after a text replacement has been made, this text will be rescanned to see if any further substitutions apply. NORESCAN implies the opposite.

The appearance of an identifier in a %ACTIVATE statement makes it active and eligible for replacement; that is, any subsequent encounter of that identifier in a nonprocessor statement, while the identifier is active, will initiate replacement activity, except when the identifier appears within a comment or within single quotes.

Examples

Example A-1.

%DECLARE A FIXED, B CHARACTER;
%DEACTIVATE B;
%A = 24;
%B = 'VAR_NAME'; 
NUM = B + A; 
%ACTIVATE B; 
%DEACTIVATE A; 
NUM = B + A;

The text generated by this example would be as follows:

NUM = B + 24;
NUM = VAR_NAME + A;

Example A-2.

%DECLARE (C,D) CHARACTER;
%C = D**2;
%D = 'NUM';
NUM = C;
%ACTIVATE C NORESCAN;
NUM = C;

The text generated by this example would be as follows:

NUM = NUM**2; 
NUM = D**2;
Restrictions

A %ACTIVATE statement cannot appear within a preprocessor procedure. The replacement in a rescan operation is limited to 1023 characters.

References to preprocessor procedures will not be recognized when rescanning an identifier for replacement.

%ASSIGNMENT

Purpose

Assigns a value to a specified preprocessor variable.

Syntax
%[label:]... preprocessor-variable = preprocessor-expression;
Parameters

preprocessor-variable is a preprocessor variable reference, and preprocessor-expression is any valid expression.

Description

The %ASSIGNMENT statement is used to evaluate preprocessor expressions and to assign the result to a preprocessor variable.

Examples

Example A-1.

%DECLARE A FIXED (B,C) CHARACTER;
%A = 1024;
%B = 'BALANCE(CUST(I,J),DATE())';
%C = 'ALLOCATE STRUC SET(STRUC_PTR);'; 
C
STRUC.BAL(A) = B;

The text generated by this example would be as follows:

ALLOCATE STRUC SET(STRUC_PTR); 
STRUC.BAL(1024) = BALANCE(CUST(I,J),DATE());

Example A-2.

%DECLARE (D,E) CHAR;
%D = 'TEST_CASE';
%E = D || '_0';
F = E;

The text generated by this example would be as follows:

F = TEST_CASE_0;

%DEACTIVATE

Purpose

Renders identifiers inactive and ineligible for replacement.

Syntax
%[label:]... DEACTIVATE ident[,ident]...;

Abbreviation(s): %DEACT for %DEACTIVATE.

Parameters

Each identifier must be a preprocessor variable, a preprocessor procedure name, or a preprocessor built-in function name.

Description

The appearance of an identifier in a %DEACTIVATE statement makes it inactive and ineligible for replacement; that is, any subsequent encounter of that identifier in a nonpreprocessor statement will not initiate any replacement activity (unless, of course, the identifier is later reactivated by use of the %ACTIVATE statement).

%DECLARE

Purpose

Specifies that an identifier is a preprocessor variable or a preprocessor procedure name.

Syntax
%[label]... DECLARE(ident[,ident]...); 

Abbreviation(s): %DCL for %DECLARE.

Parameters

ident is a PL/I name.

Description

The %DECLARE statement establishes an identifier as a preprocessor variable or a preprocessor procedure name and also serves to activate that identifier with the RESCAN option.

When declaring parameters in a preprocessor procedure, they should all be declared together in the first statement.

CHARACTER (abbreviation, CHAR) specifies that the identifier represents a varying length character string of up to 4096 characters. FIXED specifies that the variable is also given the PL/I attributes DECIMAL (5,0). BIT specifies that the identifier represents a bit string of maximum length 16.

An ENTRY declaration can be specified for each preprocessor entry name in the source program. The declaration activates the entry name. The declaration of a preprocessor procedure entry name can be performed explicitly by its appearance as the label of a %PROCEDURE statement. This explicit declaration, however, does not activate the preprocessor procedure name. Preprocessor built-in function names must not be used to name preprocessor procedures.

BUILTIN specifies that the identifier is the preprocessor built-in function of the same name.

Factoring of attributes is allowed. Attributes common to several names can be factored to eliminate repeated specification of the same attribute. Factoring is achieved by enclosing names in parentheses, and following this by a set of attributes that apply to all of the names. Declarations within the parenthesized list are separated by commas.

Example
%DECLARE A FIXED, (B,C) CHARACTER; 
%DECLARE SUBSTR BUILTIN;

/* Note that preprocessor variables B and C are declared using factored declaration. */

%DO

Purpose

Begins a sequence of preprocessor statements to be executed in a group.

Syntax
%[labe1:]... DO[index = start[TO finish[BY increment]] | 
                                  [BY increment [TO finish]]];
Parameters

Where index is a preprocessor variable; start, finish, and increment are preprocessor expressions.

Description

The %DO statement and its corresponding %END statement delimit a preprocessor DO group and can also specify repetitive execution of the DO group.

Preprocessor DO groups can be nested. Both preprocessor statements and text other than preprocessor statements can appear within a preprocessor DO group. However, only the preprocessor statements are executed. Nonpreprocessor statements are scanned but only for possible replacement activity. Non-iterative preprocessor DO groups are useful as THEN or ELSE clauses of %IF statements.

If increment is ≤ 0 or start > finish, the body of the loop is not executed.

Example
%DECLARE IX FIXED; 
%DO IX = 1 TO 4; 
A(IX) = IX;
%END

The text generated by this example would be as follows:

A(1) = 1;
A(2) = 2;
A(3) = 3;
A(4) = 4;

%END

Purpose

Terminates a block or group headed by the most recent %DO or %PROCEDURE statement.

Syntax
%[label:]... END [label];
Description

The %END statement is used in conjunction with %DO or %PROCEDURE statements to delimit preprocessor DO groups or preprocessor procedures.

The label following the END can be a label of a %PROCEDURE or %DO statement. No checking is done.

Restrictions

Multiple closure, that is, using one %END to close multiple groups and/or blocks, is not permitted.

%GOTO

Purpose

Transfers control to a labeled preprocessor statement.

Syntax
%[label:]... GOTO statement-label; 

Alternate form: %GO TO for %GOTO.

Parameters

statement-label is a reference to a preprocessor statement.

Description

The %GOTO statement causes the preprocessor to continue its scan at the specified label.

The label following the GOTO specifies the point to which the scan will be transferred. It must be a label of a preprocessor statement.

A preprocessor GOTO statement appearing within a preprocessor procedure cannot transfer control to a point outside of that procedure.

A GOTO statement can transfer control to a label in another included file at an inner level of nesting.

Example
%DECLARE A CHARACTER;
%A = 'NAME';
%GOTO PREP_LABEL;
C = A;
%PREP_LABEL:;           /* Null statement */
D = A;

The text generated by this example would be as follows:

D = NAME;
Restrictions

The label following the GOTO cannot be the label of a preprocessor procedure.

%IF

Purpose

Conditionally executes code based on a test expression.

Syntax

%[label:]... IF expr %THEN clause-1 [%ELSE clause-2]

Parameters

expr is any expression that results in a scalar bit string value of length ≥ 1. The test-expression is considered true if any bit in the string is one; the expression is considered false only when all bits are zero. clause-n is any single preprocessor statement (other than %DECLARE, %PROCEDURE, %END or %DO), or a preprocessor DO group. Otherwise, the description is the same as that of Open PL/I IF statements.

Description

The %IF statement can control the flow of the scan according to the value of a preprocessor expression.

The preprocessor expression is evaluated. If the result of the evaluation is 1 or greater than 1, clause-1 is executed and clause-2 is ignored, if present. If the result of the evaluation is 0, clause-1 is ignored and clause-2, if present, is executed. In either case, the scan resumes immediately following the IF statement, unless, of course, a %GOTO in any of the clauses causes the scan to resume elsewhere. %IF statements can be nested according to the rules for nesting Open PL/I IF statements.

Example
%DECLARE (A,B) CHARACTER;
%A ='CONTROL_1';
%B = 'NAME';
%IF A = 'CONTROL_1' 
%THEN %DO;
   RES = B + F(X); 
%END;
%ELSE %DO;
   RES = B - F(X) + SQRT(X);
   LST = F(X) + 1; 
%END;

The text generated by this example would be as follows:

RES = NAME + F(X);

%INCLUDE

Purpose

Incorporates external text into the program being scanned.

Syntax

%[label:] . . . INCLUDE pathname [,pathname] . . . ;

Parameters

pathname can be any legal UNIX pathname.

Description

The %INCLUDE statement is used to include or incorporate strings of external text into the source program being scanned.

Included text can contain nonpreprocessor and/or preprocessor statements. The scan continues with the first character in the included text. The included text is scanned in the same manner as the source program.

%INCLUDE statements can be nested to a limit of eight levels.

%NOTE

Purpose

Generates a preprocessor diagnostic message.

Syntax

%[label:]... NOTE (message [, code]);

Parameters

message is a character string expression whose value is the required diagnostic message. code is a fixed expression whose value (in a range of 0 to 16) indicates the severity of the diagnostic. If code is omitted, 0 is assumed.

Description

The %NOTE statement enables the user to generate a preprocessor diagnostic message of specified text and severity.

A code of 16 (the maximum) causes immediate termination of the preprocessing.

%NULL

Purpose

Placeholder statement.

Syntax

%[label:]... ;

Description

The %NULL statement does nothing and does not modify sequential statement execution. It can be used to provide transfer targets for %GOTO statements. It is also useful for balancing ELSE clauses in nested %IF statements.

%REPLACE

Purpose

Specifies that an identifier is a constant of a given value.

Syntax

%REPLACE name BY constant;

Description

The %REPLACE statement specifies that an identifier is a constant of a given value.

Beginning at the point at which the %REPLACE statement is encountered, each occurrence of name that follows the %REPLACE statement, as interpreted by the macro preprocessor, is replaced by the constant until the end of compilation or until another %REPLACE statement that specifies the same name is encountered.

The %REPLACE statement is normally used to supply the sizes of tables or to give names to special constants whose meaning would not otherwise be obvious.

Examples
%REPLACE MSG BY 'login'; 
PUT SKIP LIST(MSG);
...
%REPLACE MSG BY 'logout'; 
PUT SKIP LIST(MSG);

Preprocessor Procedures

Preprocessor procedures are function procedures. A preprocessor procedure is delimited by %PROCEDURE and %END statements and contains at least one RETURN statement. Preprocessor statements in a preprocessor procedure do not begin with a percent symbol.

Invocation of Preprocessor Procedures

A preprocessor procedure is invoked by the appearance of its entry name, together with an optional list of arguments. The procedure must have been previously activated by use of the %ACTIVATE statement or by its inclusion in a %DECLARE statement.

The argument list must be a parenthesized list of positional arguments following the entry name. Trailing arguments can be omitted; their absence is detected by use of the PARMSET built-in.

%PROCEDURE

Purpose

Defines a procedure entry and specifies the parameters of the procedure, if any.

Syntax
%label:[label:]... PROCEDURE[(ident[,ident]...)] 
[STATEMENT]RETURNS(CHARACTER|FIXED|BIT);

Abbreviation(s): %PROC for %PROCEDURE.

Parameters

ident is a PL/I name.

Description

The %PROCEDURE statement is used in conjunction with a %END statement to delimit a preprocessor procedure. Such a preprocessor procedure is an internal function procedure that can be executed only by the preprocessor.

The STATEMENT option is ignored in this version of Open PL/I. One of the attributes, CHARACTER, FIXED, or BIT, must be specified in the RETURNS attribute list to indicate the type of value returned by the function procedure. There can be no default.

The preprocessor statements ASSIGNMENT, DECLARE, DO, GOTO, IF, NULL, RETURN, and NOTE can be used within a preprocessor procedure. Inside the preprocessor procedure the % symbols are omitted.

Example
%DECLARE (A,B,C) CHARACTER;
%ACTIVATE CAT;
%A = 'AAA';
%B = 'BBB';
%C = 'CCC';
RES_STRING_3 = CAT(A,B,C);
RES_STRING_2 = CAT (A,B);
RES_STRING_1 = CAT(A);
%CAT: PROCEDURE(X,Y,Z) RETURNS(CHAR);
DECLARE (X,Y,Z) CHAR;
DECLARE S CHAR;
IF PARMSET(Z) THEN S = ''''||X||Y||Z||'''';
ELSE IF PARMSET(Y) THEN S = ''''||X||Y||'''';
ELSE S = ''''||X||'''';
RETURN(S); 
%END CAT;

The text generated by this example would be as follows:

RES_STRING_3 = 'AAABBBCCC'; 
RES_STRING_2 = 'AAABBB'; 
RES_STRING_1 = 'AAA';

This example also shows how to generate a character string constant using the preprocessor. To do so, use four successive apostrophes, followed by the concatenation symbol, followed by the expression for the desired character string constant, followed by another concatenation symbol, followed by four more apostrophes.

RETURN

Purpose

Returns control and a value back to the point of invocation.

Syntax
[label:]...RETURN(expr);
Parameters

expr is any valid preprocessor expression.

Description

The preprocessor RETURN statement can be used only in a preprocessor procedure and can have no leading percent symbol. It returns a value as well as control back to the point from which the preprocessor procedure was invoked.

Preprocessor Built-In Functions

A reference to a preprocessor built-in function in input text is executed by the preprocessor only if the built-in function name is active. The functions are explained in the following table:

Function NameUse
COMPILETIMEReturns a character string containing the current date and time in the following format:

DD MMM YY HH.MM.SS

COUNTERReturns a character string containing a decimal number. The returned number is 00001 for the first invocation and is incremented by one on each successive invocation.
INDEX(x,y)Returns a FIXED value indicating the starting position within the character expression x of a substring identical to character expression y.
LENGTH(x)Returns a FIXED value specifying the current length of a given character expression x.
PARMSET(x)Used to determine whether a specified parameter has been set on the invocation of a procedure.
SUBSTR(x,y[,z])Returns a substring of the character expression x, starting at position y with length z.
TRANSLATE(s,t,x)Replaces occurrences in the character string s of a character in the specified string x with the corresponding translation character in translation string t and returns the resulting string.
VARIANTReturns a character string specified on the command line.
>Preprocessor Built-in Functions
Examples

Example A-1.

%ACTIVATE COMPILETIME;
%DECLARE TOC CHAR;
%TOC = ''''||COMPILETIME||'''';  
PUT LIST ('COMPILED AT' ||TOC); 
PUT SKIP;

The text generated by this example would be as follows:

PUT LIST('COMPILED AT '||' 14 NOV 93 15.53.12'); 
PUT SKIP;

Example A-2.

%DECLARE XXX CHAR, COUNTER BUILTIN, I FIXED;
%DO I = 1 TO 4;
%XXX = 'DECLARE NAME_'|| COUNTER || 'FIXED BIN(31);'; 
XXX
%END;

The text generated by this example would be as follows:

DECLARE NAME_00001 FIXED BIN(31); 
DECLARE NAME_00002 FIXED BIN(31) 
DECLARE NAME_00003 FIXED BIN(31) 
DECLARE NAME_00004 FIXED BIN(31)

Copyright © 2009 Micro Focus (IP) Ltd. All rights reserved.