Preprocessor
This chapter describes the basic concept of a preprocessor and how to use it.
1. Overview
A preprocessor receives a source program then changes and displays a part of the source program according to a preprocessor statement.
The output of the preprocessor is used as an input to the complier.
The source program input to a preprocessor consists of three elements.
-
Preprocessor Statement
A preprocessor statement must begin with a percent sign (%). However, the statements described in a preprocessor procedure do not use a percent sign. Preprocessor statements are executed in the sequence they appear in the source program.
Preprocessor statements can have the following effect on input texts:
-
Preprocessors execute preprocessor statements in order, and then changes input texts based on the execution.
-
Preprocessor statements can determine whether to change an input text.
-
A string of characters included in another file or library can be included in the preprocessor input.
The following is the description of tasks that can be done through preprocessor statements.
Statement Description Variable name and attribute to be used in a preprocessor.
Declares that the %ACTIVATE statement was executed so the variable is activated. If a variable is not declared explicitly, it will be declared as a CHARACTER attribute and treated as inactivated.
%ACTIVATE (abbreviation: %ACT), %DEACTIVATE (abbreviation: %DEACT)
Option to convert the string of an input text to the value of a preprocessor variable.
-
%ACTIVATE: activates
-
%DEACTIVATE: deactivates
Strings that are saved in another file can be included in the input to a preprocessor.
Alters the execution order of a preprocessor.
Alters the value of a preprocessor variable.
%PROCEDURE
Defines and uses a preprocessor procedure.
-
-
Listing Control Statement
A listing control statement specifies the layout of a printed program listing. In OpenFrame PL/I, this only affects preprocessor input and not output.
-
Input Text
An input text represents any preprocessor input other than the aforementioned preprocessor statement or input text. An input text can be either a PL/I source program or any other text. A PL/I constant is displayed as-is without any change. A string that matches the name of an activated preprocessor variable is converted to the value of the variable.
The RESCAN or NORESCAN option can be set on a preprocessor variable. If the NORESCAN option is set, the value is converted to the processor output only once. If the RESCAN option is set, a rescan is performed until all possible replacements of the value have been made.
Variables and Data
The variables of a preprocessor can be declared using the %DECLARE statement. Variables can be set with the FIXED and CHARACTER attributes. All variables have a static storage class.
The data types of preprocessors are as follows:
Type | Description |
---|---|
FIXED |
Corresponds to the integer type of Linux C++. |
CHARACTER |
Corresponds to the std::string type of Linux C++. |
A numeric constant only allows an unscaled integer. It does not allow string repetition factors. |
References and Expressions
A reference and expression of a preprocessor are evaluated in the same way as explained in Expressions.
Note the following exceptions:
-
The operands of an expression that include preprocessor variables, procedure references, fixed decimal constants, bit constants, character constants, and references to preprocessor built-in functions.
-
Arrays that are declared outside a preprocessor procedure that can be referenced by multiple procedures. However, these arrays cannot be referenced outside a procedure.
-
The exponentiation symbol (**) cannot be used.
-
A decimal fixed-point result that is converted to a precision. For example, 3/5 that is saved as 0, rather than 0.6.
-
A string that can be used in arithmetic operations. However, it must be an integer.
-
A null string that is converted to 0.
-
A fixed-point value that is always converted to the string of 8 characters in length.
Name Scope
The scope of a name used in a preprocessor is determined based on where it is declared.
The scope of a name declared within a procedure is that procedure. The name declared in an included string is referenced by that string and all input text scanned after the string is included.
All other names correspond to an entire preprocessor input. However, a preprocessor procedure where a name is declared is excluded.
2. Procedure
This section describes the basic concepts and rules of a preprocessor procedure.
The basic syntax of a preprocessor procedure is as follows:
The understanding of how to send parameters and how to process return values is required for using a preprocessor procedure.
Parameters can be sent as positional and keyword parameters. Keyword parameters are used to send an argument that corresponds to the name of a parameter.
In a positional parameter, an argument is matched with a parameter according to the sequence of the argument. The number of arguments and parameters may not be equal. If the number of arguments is greater than that of parameters, the remainder is ignored. If the number of parameters is greater than that of arguments, all parameter values are initialized. For fixed parameters, the value of zero is given and for CHARACTER parameters, the null string is given.
If a function reference is referenced by an input text, the arguments are delimited by a comma or right parenthesis.
For example, (A(B,C),D) consists of two arguments. Replacement activity is performed before the arguments "A(B,C)" and "D" are passed to the parameter. However, this replacement does not have an effect on the number of arguments. If the arguments are replaced by E,F and become E, F, D due to A(B,C), there are still 2 arguments, "E, F" and "D".
If a reference appears in an input text and the STATEMENT option exists, arguments can be a positional argument or keyword reference. The end of a reference is indicated by a semicolon(;), but the semicolon is not displayed.
The following is an example of a preprocessor procedure:
%FIND:PROC(A,B,C) STATEMENT;
If a preprocessor procedure is declared as shown above, it can be referenced as follows. The following examples have the same meaning:
FIND(X,Y,Z);
FIND B(Y) C(Z) A(X);
FIND(X) C(Z) B(Y);
FIND(,Y,Z) A(X);
An attribute of a value returned by a function procedure is set to CHARACTER or FIXED.
A preprocessor procedure that has the RETURNS attribute must include one or more RETURN statements. The RETURN statement returns the control to the point of invocation. The resulting argument is converted to the value specified in the RETURN statement of the function reference.
The followings are the considerations for using a preprocessor procedure:
-
A preprocessor procedure is delimited by %PROCEDURE and %END.
-
If a procedure does not include the RETURNS attribute, it must not also include the RETURN statement. If a procedure is a function, it must include at least one statement.
-
A preprocessor statement within a preprocessor procedure does not begin with a percent sign (%).
-
A preprocessor procedure cannot be nested.
-
A preprocessor ENTRY cannot be declared within a preprocessor procedure.
-
A preprocessor procedure entry name set with an argument is called a function reference. A function reference can be invoked within a preprocessor expression.
-
A name of a preprocessor procedure does not need to be declared in the %DECLARE statement.
-
When an entry name is activated, the preprocessor procedure does not need to be processed before it is invoked. However, the procedure must be included in the preprocessor input or must be already included before it is invoked.
-
A value that is returned by a preprocessor function replaces the function reference and its argument list.
3. Statements
This section describes how to use preprocessor statements. All statements can be labeled.
3.1. %ACTIVATE Statement (Abbreviation: %ACT)
The %ACTIVATE statement makes an identifier active to convert input text.
All variables of the input texts encountered while an identifier is active are converted to the value of that variable. The replacement activity does not have any effect on the variables that are already active.
Component | Description |
---|---|
identifier |
Name of a preprocessor identifier, procedure, or built-in function. An array variable cannot be specified. |
RESCAN |
Specifies that a converted identifier is replaced again if it is the same as the preprocessor after conversion. This activity repeats until there are no more names to be mapped to. |
SCAN |
Specifies that an identifier is replaced only once. |
NORESCAN |
Same as SCAN. |
3.2. %assignment Statement
The %assignment statement calculates a preprocessor expression and assigns its results to a preprocessor variable.
Compound and multiple arguments ( +=, -= , or a = b = c ) are not allowed. The target of an argument cannot be an array.
3.3. %DEACTIVATE Statement (Abbreviation: %DEACT)
The %DEACTIVATE statement makes an identifier active.
Component | Description |
---|---|
identifier |
Name of a preprocessor identifier, procedure, or built-in function. |
If an identifier becomes inactive, the identifier will no longer be converted. However the value of the preprocessor identifier will not be lost, so a value does not need to be reassigned when the identifier is reactivated. Reactivation of an inactive identifier does not have any effect.
3.4. %DECLARE Statement
The %DECLARE statement defines an identifier for a preprocessor variable, procedure, or built-in function. In addition, a scanning status can be specified for a variable. (Abbreviation: %DCL for DECLARE, CHAR for CHARACTER, INT for INTERNAL, EXT for EXTERNAL)
or
Component | Description |
---|---|
identifier_description |
Name and attribute of a preprocessor identifier. |
BUILTIN |
Specifies that an identifier is a preprocessor built-in function with the same name. |
ENTRY |
Specifies that an identifier is a preprocessor procedure. This declaration activates an entry name. An entry name can be explicitly declared using the name labeled in the %PROCEDURE statement. However, a name declared in this way is initialized with an inactive status. |
identifier_description:
dimension:
attributes:
Component | Description |
---|---|
CHARACTER |
Mapped to the std::string type of Linux C++. |
FIXED |
Mapped to the integer type of Linux C++. |
SCAN |
Specifies that an identifier is replaced only once. |
RESCAN |
Specifies that an identifier is replaced again if a converted value has the name of the preprocessor. |
NOSCAN |
Makes an identifier inactive. |
3.5. %DO Statement
The %DO statement with the matching %END statement defines a DO-group.
DO type 1:
DO type 2:
specification1:
DO type 3:
specification2:
DO type 4:
The description of the %DO statement is provided in DO Statement.
A preprocessor %DO statement differs from a compiler DO statement in the following ways:
-
UPTHRU and DOWNTHRU do not exist in a %DO statement.
-
Specification 1 and 2 of a DO type 3 are not iterated in a %DO statement.
-
The %DO SKIP statement, which does not exist in a DO statement, is supported in a %DO statement. The %DO SKIP statement also comments out its matching %END statement.
A preprocessor DO-group can be nested. All preprocessor statements, Input texts, and listing control statements can exist in a DO-group. |
3.6. %END Statement
The %END statement is mapped to a %DO, %SELECT, or %PROCEDURE statement to define a group or procedure. The labels that follow an END statement must be a label of a %PROCEDURE, %DO, or %SELECT statement.
3.7. %GO TO Statement
The %GO TO statement specifies a label of a statement that comes after it.
Component | Description |
---|---|
label |
Can only use a statement within a preprocessor procedure in which a %GOTO statement is defined. A label of a %GOTO statement in an included file can only be set to a statement within the file. |
3.8. %IF Statement
The %IF statement control the flow of statement by evaluating a preprocessor expression value. The %IF statement can be nested.
Component | Description |
---|---|
preprocessor_expression |
Value of a preprocessor-expression is evaluated and set to true or false temporarily. If the preprocessor expression is not a conditional expression and its value is 0, this is evaluated to false and true otherwise. If true, unit1 is executed. If false, unit2 is executed. |
preprocessor_unit |
Any preprocessor statement including DO-group and SELECT-group (other than %DECLARE, %PROCEDURE, %END, and %DO). |
3.9. %INCLUDE Statement
The %INCLUDE statement includes the specified external text where the statement is specified in the preprocessor input. The included external text is referred to as an included text.
An %INCLUDE statement cannot be nested. Another %INCLUDE statement can exist within an included text.
Preprocessor, Do-groups, SELECT-groups, and other procedures must be defined within a single text. This means that a corresponding %END statement must also exist within the text. The name of an included file is limited to 8 characters in length.
3.10. %INSCAN Statement
Similar to the %INCLUDE statement, the %INSCAN statement includes external text where the statement is specified in the preprocessing input. However, unlike the %INCLUDE statement, the name of an included file can be saved in a preprocessor variable.
3.11. %ITERATE Statement
The %ITERATE statement jumps to the %END position of a DO-group that includes the %ITERATE statement. After the iteration of the current %DO statement ends, the next iteration continues if necessary.
Component | Description |
---|---|
label_constant |
Must be a label of a DO-group. If omitted, the control jumps to the nearest %END. |
3.12. %LEAVE Statement
The %LEAVE statement terminates the iteration of a DO-group that has a %LEAVE statement. It then continues the tasks after the %END statement as if the current DO-group was terminated.
Component | Description |
---|---|
label_constant |
Must be a label of a DO-group. Terminates all iterations of the DO-group of the label. If omitted, the nearest DO-group is terminated. |
3.14. %REPLACE Statement
The %REPLACE statement replaces a name with a specified string or number. The corresponding name does not need to be declared as a preprocessing variable.
Component | Description |
---|---|
identifier |
Name to be replaced. |
string_constant |
String constant as a replacement. |
arithmetic_constant |
Arithmetic constant as a replacement. |
4. Preprocessor Built-in Functions
A preprocessor built-in function can be invoked through a function reference. It can be invoked in the same way that a user-defined function is invoked. However, the number of arguments must match exactly.
When an input text calls a built-in function, the name of the function must be active. The name of the function can be activated through the %DECLARE and %ACTIVATE statements. The name of a built-in function is always active in a preprocessor statement. If it is a user-defined procedure name, the procedure is referenced.
The following describes preprocessor built-in functions. For detailed description, refer to the relevant subsection.
Function | Description |
---|---|
Returns a character string of length 18 that contains the date and time of compilation. |
|
Returns a character string of length 5 that contains a decimal number. |
|
Returns the starting position of the first occurrence of y in x as a FIXED type. |
|
Returns the length of the string x as a FIXED type. |
|
Returns whether or not the specified parameter was set when the procedure was invoked. |
|
Returns a substring specified by y and z in x. |
4.1. COMPILETIME
COMPILETIME returns an 18 character string that includes date and time.
-
Return Value
A return value is returned in the following format:
DD.MMM.YYbHH.MM.SS
The following is an example of a return value:
01.JAN.14 12.01.59
4.2. COUNTER
COUNTER returns a 5 digit character string that includes a decimal number.
It COUNTER is invoked for the first time, the value is 00001 and it is incremented by 1 the next time COUNTER is invoked. After COUNTER is invoked 99999 times, it is reset to 00000 at the next invocation.
4.3. INDEX
INDEX returns a FIXED value indicating the first position within x of y. The location where processing begins can be also specified.
-
Argument
Component Description x
String to be searched.
If it is not a CHARACTER type, it is converted to a CHARACTER type.
y
String used for searching.
If it is not a CHARACTER type, it is converted to CHARACTER type.
n
Position within x to start the search.
If it is not a FIXED type, it is converted to a FIXED type.
-
Return Value
-
0 is returned when x does not exist in y or the length of y is 0.
-
n must be greater than 0 or less than the length of x +1. If n is the same as the length of x +1, 0 is returned.
-
4.4. LENGTH
LENGTH returns a fixed value specifying the length of a specified character string x.
-
Argument
Component Description x
If x is not a CHARACTER type, it is converted to a CHARACTER type.
4.5. PARMSET
PARMSET returns a 0 or 1 that indicates whether a parameter was set when a procedure was invoked.
-
Argument
Component Description x
Must be a parameter of a preprocessor procedure.
-
Return Value
-
If x is set, 1 or 0 is returned.
-
4.6. SUBSTR
SUBSTR returns a substring specified by y and z of x.
-
Argument
Component Description x
Expression that specifies a string from which a substring is extracted.
If it is not a CHARACTER type, it is converted to a CHARACTER type.
y
Expression that specifies a starting position to extract a substring of x.
If it is not a CHARACTER type, it is converted to a FIXED type.
z
Expression that specifies the length of a substring of x.
If it is not a CHARACTER type, it is converted to a FIXED type. If z is 0, the string null is returned. If z is omitted, it will return the position y of x to the end of x.
The value of z cannot be a negative value. The values of y and z must be within a range where a substring of x can be extracted.
-
Return Value
-
If y = LENGTH(x) + 1 and z = 0, the null string is returned.
-