Jump to content

PL/I preprocessor

From Alfawiki
Adapted from the English Wikipedia article PL/I preprocessor, available under CC BY-SA 4.0Updated 9 August 2025
PL/I preprocessor
Company typeSoftware component
IndustryProgramming languages
ProductsPL/I preprocessor functions

The PL/I preprocessor functions as the dedicated preprocessor for the PL/I programming language, interpreting a subset of PL/I syntax to facilitate source file inclusion, conditional compilation, and macro expansion capabilities.

Its syntax closely resembles PL/I, with preprocessor directives and procedures identified by a leading %. Statements controlling listing output, which format compiler listings, also begin with %. Although primarily designed to process PL/I source code, the preprocessor can independently handle other text files because it does not depend on the complete PL/I grammar.

While not standardized within the official PL/I specification, the majority of implementations incorporate support for IBM’s preprocessor language.

Including files

The %INCLUDE directive inserts content from an external file, which may itself contain additional preprocessor commands. Recent IBM compilers introduce %XINCLUDE, a variant that includes a file only if it has not been previously included. Similarly, %INSCAN and %XINSCAN directives include files determined by preprocessor expressions.

Listing control

Statements managing listing output affect both preprocessor and compiler listings:

  • %PRINT; initiates or resumes output to the listing.
  • %NOPRINT; suppresses any listing output.
  • %PAGE; starts a new page within listings.
  • %SKIP [(n)]; advances the listing by n lines, defaulting to one if unspecified.
  • %PUSH and %POP save and restore the current print or suppress state using a stack mechanism.

Preprocessor operation

The preprocessor examines input text for declared preprocessor names (identifiers) and substitutes them with their assigned values or macro expansions. The substituted text may be scanned again to allow further replacements.

Preprocessor data types

Data can be declared as CHARACTER (strings of unlimited length) or FIXED (integers with up to five digits). A set of predefined preprocessor builtins operate on these data types. Preprocessor expressions combine identifiers, procedure invocations, builtins, and constants; although no BIT variables exist, comparisons produce BIT-type results. Expressions support all PL/I operators except exponentiation.

Preprocessor statements

  • defines an identifier as a preprocessor variable ( or ).
  • assigns a value to a preprocessor identifier.
  • activates a preprocessor identifier for substitution during input processing.
  • deactivates a preprocessor identifier from substitution.
  • initiates a preprocessor -group, which groups statements and supports iteration. It may contain any mix of preprocessor statements and input text.
  • starts a preprocessor procedure—a macro-like collection of statements that returns a value when invoked.
  • begins a preprocessor <syntaxhighlight lang="text" class="" style="" inline="1">SELECT</syntaxhighlight>-group.
  • closes a -group, -group, or procedure.
  • (or ) instructs the preprocessor to resume scanning at a specified label or location in the input stream.
  • directs flow control based on evaluation of a preprocessor expression:
 %IF preprocessor-expression
 %THEN preprocessor unit1
 %ELSE preprocessor-unit2

Preprocessor units may consist of single statements or entire -groups.

  • ends the current iteration of a -group and proceeds to the next iteration if applicable.
  • terminates all remaining iterations of the current -group and transfers control to .
  • issues user-defined diagnostic messages.
  • serves as a no-operation statement used as a placeholder where necessary.
  • performs immediate substitution of a name with a character or fixed expression without requiring prior declaration.

Preprocessor Procedures

A preprocessor procedure is a specialized subroutine executed during preprocessing, delimited by %PROCEDURE and %END. It exclusively contains preprocessor commands (without a leading %) and yields a value of type CHARACTER or FIXED. These procedures are called similarly to functions, either from open code or other procedures, with arguments passed by name as character strings that retain all original whitespace.

Preprocessor Built-in Functions

The following built-in functions pertain to IBM's PL/I for MVS and VM compiler. It is important to note that built-in functions differ across PL/I compiler preprocessors:

  • — provides the current compilation date and time as a character string, for example, "15 SEP 12 15:30:00".
  • — generates a sequential character string number beginning at "00001".
  • — equivalent to the PL/I built-in .
  • — equivalent to the PL/I built-in .
  • — returns if the argument has been set during the present preprocessor invocation; otherwise, returns .
  • — equivalent to the PL/I built-in .

Example

Within IBM PL/I for OS/2, a preprocessor procedure simulates a C-style write statement for PL/I. The statement triggers this procedure, where the optional parameter defaults to the length of . <syntaxhighlight lang="rexx" line highlight="1,25,26">

%uwrite:
procedure keys (File, From, Count);
dcl (File, From, Count, Number, Size) char;
if parmset(File) & parmset(From) then; else do;
   note ('FILE and FROM must be specified!', 12);
   return;
   end;
if parmset(Count)
   then Size = 'min(length(' || From || '), ' || Count || ')';
   else Size = 'length(' || From || ')';
Number = Counter();
ans ('do;');
ans ('dcl Count' || Number || ' fixed bin (15);' ) skip;
ans ('Count' || Number || ' = filewrite('
                       || File
                       || ', ptradd(addr(' || From || '), 2)'
                       || ', ' || Size
                       || ');') skip;
ans ('end;') skip;
%end;
%act uwrite;

</syntaxhighlight>

Executing the statement produces: <syntaxhighlight lang="rexx">

do;
dcl Count00001 fixed bin (15);
Count00001 = filewrite(file_name, ptradd(addr(var_str), 2), min(length(var_str), 64));
end;

</syntaxhighlight>

Historical Development

A 1964 document on "NPL"—an early version of PL/I—proposed macro procedures marked by MACRO, utilizing full language capabilities. Compile-time statements available included:

  • %DECLARE for both fixed and varying-length character strings.
  • %assignment
  • %null statement
  • %IF ... THEN ... [ELSE ...] for conditional inclusion of source units.
  • %GOTO

This design was never brought into implementation.

In 1965, IBM's PL/I specification introduced a more limited preprocessor language without macro procedures. The permitted compile-time statements were:

  • %DECLARE
  • %assignment
  • %null statement
  • %IF ... THEN GOTO ... (note: no ELSE clause)
  • %GOTO

This iteration also remained unimplemented. By 1966, revisions reinstated preprocessor procedures using the current %PROCEDURE ... %END syntax consistent with PL/I(F). Fixed-length character variables were eliminated. Additional compile-time statements introduced include:

  • %ACTIVATE
  • %DEACTIVATE
  • %DO [var = expr TO expr [BY expr]]
  • RETURN within compile-time procedures
  • %INCLUDE
  • %IF ... %THEN ... [%ELSE ...]

Moreover, a compile-time built-in function named SUBSTR was added.

In the same year, Robert Rosin authored articles outlining preprocessor evolution based on a SHARE XXVI memorandum and Mark Elson’s research. Rosin acknowledged MAD as the sole preceding macro processor implemented in a high-level programming language.

See also

External links

  • Enterprise PL/I Language Reference (SC27-1460-09): Chapter 21. Preprocessor Facilities – IBM Corporation, October 2009
  • Micro Focus Documentation: Open PL/I Macro Preprocessor – Micro Focus International plc, 2011
  • Kednos PL/I for OpenVMS Systems Reference Manual: Chapter 10 Preprocessor – Kednos Enterprises, 2007
  • PL/I Preprocessor Wiki – Peter Flass, 2010; Comparison of preprocessor features