This is a reference manual for PL/I Subset G (ANSI X3.74-1987). It is not derived from that standard, but from the Kednos PL/I manual published in 2007, and is designed to be more readable than the standard. It includes the keywords and the semantic and syntax rules of PL/I programming language statements, attributes, built-in functions, and other language elements.
This is a draft document. It may contain errors such as documenting features that are not in Subset G, failing to document features that are, and incorrectly documenting features. I have made an effort to remove all such errors, but I am not a PL/I expert, and any that remain are solely my responsibility.
This manual is intended for programmers using PL/I to design or implement applicationsIn PL/I Subset G. A prerequisite for attaining optimal benefit from the manual is that its users understand the concepts of programming and are familiar with the keywords and topics that will be searched for information. This manual is not suitable for use as a tutorial document.
Table 1 lists the conventions used in this manual.
Conventions | Meaning |
---|---|
[Return] | In examples, the symbol [Return] represents a single stroke of the key on the terminal. |
[Ctrl/] X | In examples, [Ctrl/] X indicates that you hold down the Ctrl key while you press another key (represented here by X). |
monospace | This bold monospace typeface is used in interactive examples to indicate input entered by the user. |
italic | This italic typeface is used to identify variable names. |
.
. . |
Vertical ellipses indicate that not all of the text of a program or program output is illustrated. Only relevant material is shown in the example. |
... | Horizontal ellipses indicate that additional parameters, options, or values can optionally be entered. When a comma precedes an ellipsis, it indicates that successive items must be separated by commas. |
quotation mark
apostrophe |
The term quotation mark is used only to refer to the double quotation mark character ("). The term apostrophe is used to refer to the single quotation mark character ('). |
[OPTIONS (option,...)] | Brackets indicate that a syntactic element is optional and you need not specify it. |
[] | Brackets surrounding two or more stacked items indicate conflicting options, one of which can optionally be chosen. |
{} | Braces surrounding two or more stacked items indicate conflicting options, one of which must be chosen. |
FILE (file-reference) | An uppercase word or phrase indicates a keyword that must be entered as shown; a lowercase word or phrase indicates an item for which a variable value must be supplied. This convention applies to format (syntax) lines, not to code examples. |
# | A # symbol is used in some contexts to indicate a single space character. |
All descriptions of the effects of executing statements and evaluating expressions assume that the initial procedure activation of the program is through an entry point with OPTIONS(MAIN), which is not a standard part of PL/I Subset G, but is universally implemented.
The terms "full PL/I" and "standard PL/I" refer to the ANSI standard PL/I, X3.53-1976.
This chapter introduces the following elements of a PL/I program:
Future chapters discuss these topics in more detail.
1.1 Lexical Elements
This section describes the following topics:
A keyword is a name that has a special meaning to PL/I when used in a specific context. In context, keywords identify statements, attributes, options, and other program elements. PL/I keywords are not reserved words, so it is possible to use them in a program in other than their keyword context.
PL/I has numerous keywords. Table A-1 describes the PL/I keywords,
including brief identifications of their uses and valid abbreviations
for the keywords that can be abbreviated.
1.1.2 Punctuation
PL/I recognizes punctuation marks in statements. The punctuation marks serve the following two functions:
For example:
A = B + C; |
Whenever you use a punctuation mark in a PL/I statement, you can precede or follow the character with any number of spaces (except in the case of an operator consisting of two characters, like >= or **, which must be entered without a space between the two characters). For example, the following two statements are equivalent:
DECLARE ( A, B ) FIXED DECIMAL ( 7, 0 ) ; DECLARE(A,B)FIXED DECIMAL(7,0); |
In the second statement, all nonessential spaces are omitted; the parentheses and commas are sufficient to distinguish elements in the statement. The only space required in this statement is the space that separates the two keywords FIXED and DECIMAL.
Table 1-1 lists all the punctuation marks recognized by PL/I.
Category | Symbol | Meaning |
---|---|---|
Arithmetic operators | + | Addition or unary plus |
- | Subtraction or unary minus | |
/ | Division | |
* | Multiplication | |
** | Exponentiation | |
Relational (or comparison) operators | > | Greater than |
< | Less than | |
= | Equal to | |
^> | Not greater than | |
^< | Not less than | |
^= | Not equal to | |
>= | Greater than or equal to | |
<= | Less than or equal to | |
Logical operators | ^ | Logical NOT (unary) and |
EXCLUSIVE OR (binary) | ||
& | Logical AND | |
&: | Logical AND THEN | |
| | Logical OR | |
|: | Logical OR ELSE | |
Concatenation operator | || | String concatenation |
Separators | , | Delimits elements in a list |
; | Terminates a PL/I statement | |
. | Separates identifiers in a structure name; specifies a decimal point | |
: | Terminates a procedure name or a statement label | |
() | Encloses lists and extents; defines the order of evaluation of expressions; separates statement and option names from specific keywords; specifies a parameter list | |
' | Delimits character strings and bit strings | |
Locator qualifier | -> | Pointer resolution |
The tilde (~) is equivalent to the circumflex (^), and the exclamation point (!) is equivalent to the vertical bar (|).
Spaces, Tabs, and Line-End Characters
In addition to punctuation marks, PL/I accepts spaces, tabs, and line-end characters between identifiers, constants, and keywords.
The rules for entering spaces are:
The line-end character is a valid punctuation mark between items in a PL/I statement except when it is embedded in a string constant. In a string constant, the line-end character is ignored. For example:
A = 'THIS IS A VERY LONG STRING THAT MUST BE CONTI NUED ON MORE THAN ONE LINE IN THE SOURCE FILE'; |
This assignment statement gives the variable A the value of the
specified character-string constant. (The line-end character in the
constant is ignored.) Note that any tabs or spaces preceding NUED in
the previous example will be included in the string.
1.1.3 Identifiers
An identifier is a user-supplied name for a procedure, a statement label, or a variable that represents a data item. The rules for forming identifiers are:
Examples of valid identifiers are:
STATE total FICA_PAID_YEAR_TO_DATE ROUND1 RESULTS_OF_PREVIOUS_YEAR_TO_DATE_CALCULATED_AVERAGES |
A comment is an informational tool for documenting a PL/I program. To insert a comment in a program, enclose it within the character pairs /* and */. For example:
/* This is a comment...*/ |
Except inside a string constant, wherever the starting characters (/*) appear in a program, the compiler ignores all text until it encounters the ending characters (*/). A comment can span several lines.
The rules for entering comments are:
The following are examples of comments:
A = B + C ; /* Add B and C */ /* ********* START OF SECOND PHASE ********* */ DECLARE/*COUNTER*/A FIXED BINARY (7); /* This module performs the following steps: 1. Initializes all arrays and data structures. 2. Establishes default condition handlers. */ |
Although complete comments cannot be nested, you can comment out a statement such as the following:
DECLARE EOF BIT(1); /* end-of-file */ |
To do this, precede the DECLARE statement with another /* pair, as follows:
/* DECLARE EOF BIT(1); /* end-of-file */ |
The compiler will then ignore all text, including the DECLARE statement
and the second /*, until it reaches the */.
1.2 Statements
A statement is the basic element of a PL/I procedure. Statements are used to do the following:
Table 1-2 and Table 10-1 provide summaries of PL/I statements.
Detailed descriptions of these statements appear throughout this manual.
1.2.1 Statement Formats
The general format of a PL/I statement consists of an optional statement label, the body of the statement, and the required semicolon terminator.
The body of the statement consists of user-specified identifiers,
literal constants, or PL/I keywords. Each element must be properly
separated, either by special characters that punctuate the statement or
by spaces or comments.
1.2.2 Statement Labels
The optional statement label identifies a statement so that it can be referred to elsewhere in the program, for example, as the target of a GOTO statement. A label precedes a statement; it consists of any valid identifier (see Section 1.1.3) terminated by a colon. For example:
TARGET: A = A + B; READ_LOOP: READ FILE (TEXT) INTO (TEMP); |
A statement cannot have more than one label.
1.2.3 Simple Statements
A simple statement contains only one action to be performed. There are three types of simple statements:
Keyword Statements
Keyword statements are identified by the PL/I keyword that requests a specific action. Examples of keyword statements are:
READ FILE (A) INTO (B); GOTO LOOP; DECLARE PRICE PICTURE '$$$99V.99';In these examples, READ, GOTO, and DECLARE are keywords that identify these statements to PL/I.
Assignment Statements
PL/I identifies an assignment statement by syntax: an assignment statement consists of an identifer and an expression separated by an equal sign (=). For example:
TOTAL = TOTAL + PRICE; COUNTER = 0;Null Statements
A null statement consists of only a semicolon (;); it indicates that PL/I is to perform no operation. For example:
IF A < B THEN GOTO COMPUTE; ELSE;This IF statement shows a common use of the null statement: as the target of an ELSE clause.
A compound statement contains more than one PL/I statement within the
statement body; it is terminated by the semicolon that terminates the
final statement.
1.2.5 Summary of Statements by Function
You can group PL/I statements by function into the following categories.
Data Definition and Assignment Statements
The DECLARE statement defines variable names:
DECLARE identifier [attribute ...];The DEFINE CONSTANT statement defines variable names:
DEFINE CONSTANT identifier [attribute ...];
The assignment statement gives a value to a variable:
reference = expression;
Input/Output Statements
These statements identify files and data formats and perform input and output operations:
CLOSE GET READ DELETE OPEN REWRITE FORMAT PUT WRITE Program Structure Statements
These statements define the organization of the program into procedures, blocks, and groups:
BEGIN PROCEDURE DO null END Flow Control Statements
These statements change or interrupt the normal sequential flow of execution in a PL/I program:
CALL ON SIGNAL GOTO RETURN STOP IF REVERT LEAVE SELECT Storage Allocation Statements
These statements acquire and control the use of storage in a PL/I program:ALLOCATE
FREE
Table 1-2 gives a summary of the PL/I statements and their uses.
Statement | Use |
---|---|
Assignment | Evaluates an expression and gives its value to an identifier |
Null | Specifies no operation |
ALLOCATE | Allocates storage for a based variable |
BEGIN | Denotes the beginning of a block of statements to be executed as a unit |
CALL | Transfers control to a subroutine or external procedure |
CLOSE | Terminates association of a file control block with an input or output file |
DECLARE | Defines the variable names and identifiers to be used in a PL/I program and specifies the data attributes associated with them |
DEFINE CONSTANT | Defines the constant names and identifiers to be used in a PL/I program and specifies the data attributes associated with them |
DELETE | Removes an existing record from a file |
DO | Denotes the beginning of a group of statements to be executed as a unit |
END | Denotes the end of a block or group of statements begun with a BEGIN, DO, or PROCEDURE statement |
FORMAT | Specifies the format of data that is being read or written with GET EDIT and PUT EDIT statements and defines the conversion, if any, to be performed |
FREE | Releases storage of a based variable |
GET | Obtains data from an external stream file or from a character-string expression |
GOTO | Transfers control to a labeled statement |
IF | Tests an expression and establishes actions to be performed based on the result of the test |
LEAVE | Transfers control out of a DO group |
ON | Establishes the action to be performed when a specified condition is signaled |
OPEN | Establishes the association between a file control block and an external file |
PROCEDURE | Specifies the point of invocation for a program, subroutine, or user-defined function |
PUT | Transfers data to an external stream file or to a character-string variable |
READ | Obtains a record from a file |
RETURN | Gives back control to the procedure from which the current procedure was invoked |
REVERT | Cancels the effect of the most recently established ON unit |
REWRITE | Replaces a record in an existing file |
SELECT | Tests a series of expressions and establishes the action to be performed based on the result of the test |
SIGNAL | Causes a specific condition to be signaled |
STOP | Halts the execution of the current program |
WRITE | Copies data from the program to an external record file |
A PL/I program consists of either a single procedure or a package, which is a collection of procedures. A procedure consists of a series of statements, which perform the following tasks:
A statement comprises user-specified identifiers, constants, and PL/I keywords, separated by blanks, comments, and punctuation marks. You can organize statements into structural sequences of groups or blocks. Example 1-1 shows the structure of a PL/I program.
Example 1-1 Structure of a PL/I Program |
---|
SAMPLE: PROCEDURE OPTIONS (MAIN); (1) DECLARE (X,Y,Z) FIXED, (2) MESSAGE CHARACTER(80), CALC ENTRY (FLOAT) RETURNS (FLOAT), TOTAL FLOAT; X = 0; (3) PUT SKIP LIST(MESSAGE); FINISH: PROCEDURE; (4) DECLARE TEXT (5) CHARACTER (20); END FINISH: (5) END SAMPLE; |
Key to Example 1-1
The source text of a PL/I program is freeform. As long as you terminate every statement with a semicolon (;), individual statements can begin in any column, be on additional lines, or be written with more than one statement to a line.
Individual keywords or identifiers of a statement, however, must be confined to one line. Only a character-string constant (which must be enclosed in apostrophes) can be on more than one line.
PL/I programs are easier to read and comprehend if you follow a standard pattern in formatting. For example:
A package is a sequence of DECLARE and PROCEDURE statements headed by a PACKAGE statement and terminated by an END statement (see Section 8.3). PACKAGE statements must not appear anywhere else in a program.
The PACKAGE statement has an EXPORTS option which names the identifiers visible outside the package. All other identifiers are internal to the package; within a package, EXTERNAL means "external to the procedure but internal to the package".
A package must define at least one procedure. DECLARE statements at the top level of a package must be BASED or STATIC.
Example:
PACKAGE_DEMO: PACKAGE EXPORTS (FACTORIAL); DECLARE N FIXED BINARY(15), MESSAGE CHARACTER(*) VALUE('THE FACTORIAL OF '); FACTORIAL: PROCEDURE OPTIONS (MAIN); DECLARE RESULT FIXED BIN(31); PUT SKIP LIST('PLEASE ENTER A NUMBER WHOSE FACTORIAL ' || 'MUST BE COMPUTED ’); GET LIST(N); RESULT = COMPUTE_FACTORIAL(N); PUT LIST(MESSAGE || TRIM(N) || ' IS ' || TRIM(RESULT)); END FACTORIAL; COMPUTE_FACTORIAL: PROCEDURE (INPUT) RECURSIVE RETURNS (FIXED BINARY(31)); DECLARE INPUT FIXED BIN(15); IF INPUT <= 1 THEN RETURN(1); ELSE RETURN(INPUT*COMPUTE_FACTORIAL(INPUT-1)); END COMPUTE_FACTORIAL; END PACKAGE_DEMO; |
PL/I is a block-structured language with each block composed of a sequence of PL/I statements. There are two types of blocks:
The scope of a declaration of a name is that region of the program in which the name has meaning. A name has meaning in the following locations:
Two or more declarations of the same name are not allowed in a single block unless one or more of the declarations are of structure members.
Two declarations of the same name in different blocks denote distinct objects unless both specify the EXTERNAL attribute. All EXTERNAL declarations of a particular name denote the same variable or constant, and all must agree as to the properties of the variable or constant, otherwise unpredictable results will occur. Note that EXTERNAL is the default for declarations of FILE constants. It must be specified explicitly for variables.
The following example shows the scope of internal names:
NAME | SCOPE | |
DECLARE Q STATIC FIXED; | Q | MAINP, ALPHA, BETA, and CALC |
MAINP: PROCEDURE OPTIONS (MAIN); | MAINP | MAINP, ALPHA, BETA, and CALC |
DECLARE (X, Y, Z) FIXED; |
X, Y
Z in MAINP |
MAINP, ALPHA, BETA, and CALC
MAINP, ALPHA, and CALC |
ALPHA: PROCEDURE; | ALPHA | MAINP, ALPHA, BETA, and CALC |
BETA: BEGIN; | BETA | ALPHA, BETA |
DECLARE Z FLOAT; | Z in BETA | BETA |
GOTO ERROR; | ||
END BETA; | ||
ERROR: | ERROR | ALPHA, BETA |
END ALPHA; | ||
CALC: PROCEDURE; | CALC | MAINP, ALPHA, and CALC |
DECLARE (SUM, TOTAL) FLOAT; | SUM, TOTAL | CALC |
END CALC; | ||
END MAINP; |
Declarations can appear outside procedures and, if contained within the same block, have meaning throughout all procedures contained in the block. However, if there are multiple blocks, declarations outside procedures must have the EXTERNAL attribute if they are to be recognized by all blocks and procedures in the program. For example:
DECLARE X FIXED EXTERNAL STATIC; A: PROCEDURE OPTIONS(MAIN); DECLARE B ENTRY; . . . END A; |
B: PROCEDURE; . . . END B; |
In this example, the variable X has meaning in both procedures. Because
the two procedures are in two different files, X must be declared with
the EXTERNAL attribute. If X is declared with the INTERNAL attribute, X
is recognized only in the first procedure.
1.4.1 Begin Blocks
A begin block is a sequence of statements headed by a BEGIN statement (see Section 8.2) and terminated by an END statement (see Section 8.3). In general, you can use a begin block wherever a single PL/I statement would be valid. In some contexts, such as an ON-unit, a begin block is the only way to perform several statements instead of one. A primary use of begin blocks is to localize variables. Because execution of a begin block causes a block activation, automatic variables declared within the begin block are local to it, and their storage disappears when the block completes execution.
Another way to allow your program to perform several statements in
place of one is to use a DO group (see Section 8.1). You should choose
it when possible because it does not incur the overhead associated with
block activation. Use a begin block when there are declarations present
or when you require multiple statements in an ON unit.
1.4.2 Procedure Blocks
A procedure is a sequence of statements (possibly including begin blocks and other procedures) headed by a PROCEDURE statement and terminated by an END statement. Unlike a begin block, which executes when control reaches it, a procedure executes only when it is specifically invoked. Invocation occurs in the following ways:
A PL/I program must have at least one procedure, the main procedure. Any procedure, including the main procedure, can contain others; these are called internal procedures. A procedure that is not contained within any other is called an external procedure. The main procedure is always an external procedure.
Except for the main procedure, no procedure executes unless it is
invoked by a CALL statement or a function reference. Chapter 7
discusses procedures in more detail.
1.4.3 Containment
As an example, block B is said to be contained in another block A if all of B's source text, from label (if any) to END statement inclusive, is between A's BEGIN or PROCEDURE statement and A's END statement. If block B is not contained in any other block within block A, then B is said to be immediately contained in A. For example:
A: PROCEDURE OPTIONS(MAIN); B: PROCEDURE; END B; . . . BEGIN; CALL B; END; /* of begin block */ END A; |
The procedures B and the begin block all are immediately contained in A.
If block B is contained in block A, then B is said to be nested in A.
1.4.4 Block Activation
A block is activated when program execution flows into it. Then, all automatic variables declared in the block become active. When control leaves the block, the variables become undefined and inaccessible.
You can only enter a procedure block with a CALL statement (see Section 7.4) or a function reference. If an internal procedure is declared within a source program, control flows around the internal procedure during the normal sequence of execution.
A begin block is entered when it is encountered during the normal flow
of execution.
1.4.5 Relationship of Block Activations
During the execution of a program, many blocks can be simultaneously active. Two different relationships can be defined among block activations; they are the immediate dynamic descendance and the immediate parent activation. For example:
B: PROCEDURE OPTIONS(MAIN); A: PROCEDURE; CALL Q; . . . END A; Q: PROCEDURE; . . . END Q; BEGIN; CALL A; END; /* of begin block */ END B; |
Figure 1-1 shows these relationships.
Figure 1-1 Relationship of Block Activations
In the immediate dynamic descendance relationship, a block activation is the immediate dynamic descendant of the block that invoked it. At a given time, the chain of immediate dynamic descendants includes all existing block activations, starting with the activation of the main procedure and terminating in the current block activation. For example, in Figure 1-1, the begin block is the immediate dynamic descendant of procedure B; the complete chain is B, begin block, A, Q. This chain is used for finding the applicable ON-unit when a condition is signaled.
The other relationship shown in Figure 1-1 applies to activations of nested blocks. An activation of a block X that is a begin block or internal procedure has an immediate parent activation, which is an activation of the block that immediately contains X. The chain of immediate parent activations extends back to an activation of the external procedure containing X. In Figure 1-1, the parent chain for the begin block, procedure A, and procedure Q leads directly back to the activation of B, because each of these blocks is immediately contained in B. This chain is used in interpreting references.
When a block is activated, its immediate parent activation is determined as follows:
When a block terminates normally, that is, when an END statement or a
RETURN statement is executed, the current block is released and control
goes to the preceding block activation. If a nonlocal GOTO statement is
executed that transfers control out of the current block, the current
block and any blocks between it and the block containing the label that
is the target of the GOTO statement are released.
1.5 Data and Variables
The statements in a PL/I program process data, generally in the form of variables that take on different values as the result of program execution. In PL/I, you must declare variables in a DECLARE statement before you can use them in other statements. Declaring a variable associates an identifier with a set of attributes and with a region of storage. Thus, when you declare a variable you must usually specify one or more data type attributes to be associated with it. (The concept of an attribute is more basic to PL/I than the concept of a data type.) Furthermore, you can specify how the variable is to be allocated by supplying a storage-class attribute in the declaration.
A few examples of PL/I attributes are BIT, CHARACTER, BINARY, DECIMAL, FILE, FLOAT, PRINT, and UPDATE. For a complete alphabetic list of the PL/I attributes with their uses, see Section 2.2.
An identifier can refer to a single variable (called a scalar variable) or to a collection of related variables. Such a collection is called an aggregate. There are two kinds of aggregates:
The following chapters provide information on these topics:
PL/I supports an embedded lexical preprocessor, which recognizes a specific set of statements that are executed at compile time. These statements cause the PL/I compiler to include additional text in the source program or to change the values of constant identifiers at compile time.
Preprocessor statements are identified by a leading unquoted percent sign (%) and are terminated by an unquoted semicolon (;). You can freely intermix preprocessor statements with the rest of the source program statements.
The only preprocessor statement in Subset G is %INCLUDE, which specifies a file name to be textually included in place of itself.
The declaration of a name in a PL/I program consists of a user-specified identifier and the attributes of the name. The attributes describe the following:
A name is declared either explicitly in a DECLARE or DEFINE CONSTANT statement or implicitly by its appearance in a particular context. For example:
CALC: PROCEDURE; |
This statement is an implicit declaration of the name CALC as an entry constant.
This chapter describes the DECLARE statement and data attributes.
2.1 DECLARE Statement
The DECLARE statement specifies the attributes associated with names.
The format of the DECLARE statement is:
⎧DECLARE⎫ ⎨ ⎬ declaration [,declaration,...]; ⎩DCL ⎭
declaration
One or more declarations consisting of identifiers and attributes. A declaration has the following format:
[level] declaration-item
level
Levels are used to specify the relationship of members of structures; if a level is present in the declaration, it must be written first.declaration-item
A declaration-item has the following format:
⎧identifier [(bound-pair,...]) ⎫ ⎨(declaration-item,...) ⎬ [attribute ...] ⎩(identifier,...) [(bound-pair,...)]⎭
The format of the DECLARE statement varies according to the number and nature of the items being declared. The DECLARE statement can list a single identifier, optionally specifying a level, bound-pair list, and other attributes for that identifier. Alternatively, the statement can include, in parentheses, a list of declarations to which the level and all subsequent attributes apply. The declarations in the second case can be simple identifiers or can include attributes that are specific to individual identifiers.
Bound pairs are used to specify the dimensions of arrays. If bound pairs are present, they must be in parentheses and must immediately follow the identifier or the list of declarations. For example, (2:10), lower bound is 2 and upper bound is 10. Bounds can be any valid PL/I expression.
The various formats are described individually in the following
sections.
2.1.1 Simple Declarations
A simple declaration defines a single name and describes its attributes. The format of a simple declaration is:
DECLARE identifier [attribute ...] ; |
identifier
A user-supplied name, containing a minimum of 1 character. The name must be unique within the current block.An identifier can consist of any of the alphanumeric characters A through Z, a through z, 0 through 9, dollar signs ($), and underscores (_), but must begin with an alphabetic letter, dollar sign, or underscore. PL/I converts all lowercase letters to uppercase when it compiles a source program. The identifiers abc, ABC, Abc, and so on all refer to the same object.
attribute
One or more attributes of the name. Attribute keywords must be separated by spaces. They can appear in any order.See Section 2.2 for a list of the valid attribute keywords and their meanings.
The following are examples of simple declarations:
DECLARE COUNTER FIXED BINARY (7); DECLARE TEXT_STRING CHARACTER (80) VARYING; DECLARE INFILE FILE; |
2.1.2 Declarations Outside Procedures
You can declare a variable outside any procedure. Any variable so declared will be visible within all procedures contained by the module. The format for declarations outside procedures is the same as for other declarations, except that the storage-class attribute cannot be AUTOMATIC. The following example shows the use of this type of declaration:
DECLARE A STATIC FIXED BINARY(31); . . . FIRST: PROCEDURE; DECLARE B FIXED BINARY(31); . . . END FIRST; SECOND: PROCEDURE; DECLARE C FIXED BINARY(31); . . . END SECOND; |
In this example, variable A is visible in both the FIRST and SECOND
procedures, but variables B and C are visible only in their containing
procedures.
2.1.3 Multiple Simple Declarations
Multiple simple declarations define two or more names and their individual attributes. This format of the DECLARE statement is:
DECLARE identifier [attribute ...] [,identifier [attribute ...]] ...; |
When you specify more than one set of names and their attributes, separate each name and attribute set from the preceding set with a comma. A semicolon must follow the last name.
The following is an example of multiple declarations:
DECLARE COUNTER FIXED BINARY (7), TEXT_STRING CHARACTER (80) VARYING, Y FILE; |
This DECLARE statement defines the variables COUNTER, TEXT_STRING, and
Y. The attributes for each variable follow the name of the variable.
2.1.4 Factored Simple Declarations
When two or more names have common attributes, you can combine the declarations into a single, factored declaration. This format of the DECLARE statement is:
DECLARE (identifier[,identifier ...]) [attribute ...]; |
When you use this format, you must place names that share common attributes within parentheses and separate them with commas. The attributes that follow the parenthetical list of names are applied to all the named identifiers.
The following are examples of factored declarations:
DECLARE (COUNTER, RATE, INDEX) FIXED BINARY (7) INITIAL (0); DECLARE (INPUT_MESSAGE, OUTPUT_MESSAGE, PROMPT) CHARACTER (80) VARYING; |
In these declarations, the variables COUNTER, RATE, and INDEX share the attributes FIXED BINARY (7) and are given the initial value of zero. The variables INPUT_MESSAGE, OUTPUT_MESSAGE, and PROMPT share the attributes CHARACTER (80) VARYING.
You can also specify, within the parentheses, attributes that are unique to specific variable names, using the following format:
DECLARE (declaration-item, declaration-item [,declaration-item]) attribute ... |
For example:
DECLARE (INFILE INPUT RECORD, OUTFILE OUTPUT STREAM) FILE; |
The DECLARE statement declares INFILE as a RECORD INPUT file and OUTFILE as an STREAM OUTPUT file.
The parentheses can be nested. For example:
DECLARE ( (INFILE INPUT, OUTFILE OUTPUT) RECORD, SYSFILE STREAM ) FILE; |
The DECLARE statement declares INFILE as a RECORD INPUT file, OUTFILE
as a RECORD OUTPUT file, and SYSFILE as a STREAM file.
2.1.5 Array Declarations
The declaration of an array specifies the dimensions of the array and the bounds of each dimension. This format of a DECLARE statement is:
DECLARE declaration (bound-pair,...) [attribute ...]; where each bound pair has the following format: ⎰[lower-bound:]upper-bound⎱ ⎱* ⎰
One bound pair is specified for each dimension of the array. The number of elements per dimension is defined by the bound pair. The extent of an array is the product of the numbers of elements in its dimensions. If the lower bound is omitted, the lower bound for that dimension is 1 by default.
You can use the asterisk (*) as the bound pair when arrays are declared as parameters of a procedure. The asterisk indicates that the parameter can accept array arguments with any number of elements. (If one dimension is specified with an asterisk, all must be specified with asterisks.)
For example:
DECLARE SALARIES(100) FIXED DECIMAL(7,2); |
DECLARE (SALARIES,PAYMENTS) (100) FIXED DECIMAL(7,2); |
For further details on how to specify the bounds of an array, and for
examples of array declarations, see Section 4.1.1.
2.1.6 Structure Declarations
The declaration of a structure defines the organization of the structure and the names of members at each level in the structure. This format of a DECLARE statement is:
⎧DECLARE⎫ ⎨ ⎬ level declaration-item [,level declaration-item...]; ⎩DCL ⎭
Each declaration specifies a member of the structure and must be preceded by a level number. As shown in the following example, a single variable can be declared at a particular level; or the level can contain one or more complete declarations, including declarations of arrays or other structures. The major structure name is declared as structure level 1; minor members must be declared with level numbers greater than 1.
DECLARE 1 PAYROLL, 2 NAME, 3 LAST CHARACTER(80) VARYING, 3 FIRST CHARACTER(80) VARYING, 2 SALARY FIXED DECIMAL(7,2); |
This statement declares a structure named PAYROLL.
Alternatively, because the last and first names have the same attributes, the same structure can be declared as follows:
DECLARE 1 PAYROLL, 2 NAME, 3 (LAST,FIRST) CHARACTER(80) VARYING, 2 SALARY FIXED DECIMAL(7,2); |
For details and examples of structure declarations, see Section 4.2.1.
2.2 Attributes
Attributes define and describe the characteristics of names used in a PL/I program. Each name in a PL/I program has a set of attributes associated with it. You can specify attributes in any of the following contexts:
DECLARE SIGNAL CHARACTER (20); |
Attributes can also be implied by the presence of other attributes. For example, if the RETURNS attribute is specified for an identifier, the compiler supplies the ENTRY attribute by default.
The entry for each attribute in this chapter gives its syntax and abbreviation (if any) and describes related and conflicting attributes. See Table 2-1 for a concise alphabetic summary of PL/I attributes.
Computational Data Type Attributes
The attributes that define arithmetic and string data are:
⎡VARYING ⎤ CHARACTER [(length)] ⎢ ⎥ ⎣NONVARYING⎦ ⎡ALIGNED ⎤ BIT [(length)] ⎢ ⎥ ⎣UNALIGNED ⎦ ⎰FLOAT⎱ ⎰BINARY ⎱ ⎱FIXED⎰ ⎱DECIMAL⎰ [(precision[,scale-factor])] https://paste.debian.net/1255194[PICTURE 'picture']
You can specify these attributes for all elements of an array and for individual members of a structure.
Noncomputational Data Type Attributes
The following attributes apply to program data that is not used for computation:
Storage-Class and Scope Attributes
The following attributes control the allocation and use of storage for a computational variable and define the scope of the variable:
You can apply the following attributes to the major or minor members of a structure:
You can apply the following attributes to file constants and used in OPEN statements:
ENVIRONMENT(option,...) ⎧RECORD [KEYED]⎫ ⎧INPUT ⎫ ⎨ ⎬ ⎨OUTPUT [PRINT}⎬ ⎩STREAM ⎭ ⎩UPDATE ⎭ ⎰SEQUENTIAL⎱ ⎱DIRECT ⎰
You can apply the following attributes to identifiers of entry points:
You can apply the following attributes to data declarations:
Table 2-1 lists the PL/I attributes. The sections following this table describe each attribute in detail.
Attribute | Use |
---|---|
ALIGNED | Requests alignment of bit-string variables in storage |
AREA [(extent)] | Defines an area of storage for the allocation of based variables |
AUTOMATIC
AUTO+ |
Requests dynamic allocation of storage for a variable |
BASED [(pointer-reference)] | Indicates that a variable's storage is located by a pointer |
BINARY [(precision[,scale-factor])]
BIN+ |
Defines a binary base for arithmetic data |
BIT [(length)] | Defines bit-string data |
BUILTIN | Defines a built-in function name |
CHARACTER [(length)]
CHAR+ |
Defines character-string data |
CONDITION (condition-name)
COND+ |
Defines an identifier as a condition name |
DECIMAL [(precision[,scale-factor])]
DEC+ |
Defines a decimal base for arithmetic data |
DEFINED (variable-reference)
DEF+ |
Indicates that a variable will share the storage allocated for another variable |
DIMENSION (bound-pair,...)
DIM+ |
Indicates that a variable is an array, and defines the number and extent of its dimensions |
DIRECT | Specifies that a file will be only accessed randomly |
ENTRY (descriptor,...) | Describes an external procedure and its parameters |
ENVIRONMENT (option,...)
ENV+ |
Specifies system-dependent information about a file |
EXTERNAL [(external-name)]
EXT+ |
Identifies the name of a variable whose storage is referenced or defined in other procedures |
FILE | Identifies a PL/I file constant or file variable |
FIXED [(precision[,scale-factor])] | Defines a fixed-point arithmetic variable |
FLOAT [(precision)] | Defines a floating-point arithmetic variable |
INITIAL (value,...)
INIT+ |
Provides initial values for variables |
INPUT | Specifies that a file will be used for input |
INTERNAL
INT+ |
Limits the scope of a variable to the block in which it is defined |
KEYED | Specifies that a file can be accessed randomly by key |
LABEL | Defines a label variable |
NONPRINT | Specifies that a file is not to be formatted for printing |
NONVARYING
NONVAR+ |
Specifies that the length of a string is nonvarying |
OFFSET [(area-reference)] | Defines an offset variable |
OUTPUT | Specifies that a file will be used for output |
PARAMETER
PARM+ |
Indicates that a variable will be assigned a value when it is used as an argument to a procedure |
PICTURE 'picture'
PIC+ |
Specifies the format of numeric data stored in character form |
POINTER
PTR+ |
Defines a pointer variable |
Specifies that a file is to be formatted for printing | |
RECORD | Specifies that a file will be accessed by record I/O statements |
REFER | Defines dynamically self-referring structures |
RETURNS (returns-descriptor) | Specifies that an external entry is a function and describes the value returned by it |
SEQUENTIAL
SEQL+ |
Specifies that a file can be accessed sequentially |
STATIC | Requests static allocation of storage |
STREAM | Specifies that a file will be accessed by stream I/O statements |
UNALIGNED
UNAL+ |
Specifies nonalignment for bit-string variables in storage |
UNION | Indicates that a variable will share the storage allocated for another variable |
UPDATE | Specifies that records in a file can be rewritten or deleted |
VARIABLE | Defines variable entry and file data |
VARYING
VAR+ |
Defines a varying-length character string |
ALIGNED |
You can specify the ALIGNED attribute in conjunction with the BIT or CHARACTER
attribute in a DECLARE statement to request alignment of a string
variable. If you specify ALIGNED for an array of
string variables, each element of the array is aligned.
Restriction
The ALIGNED attribute conflicts with the VARYING attribute and is
invalid with all data-type attributes other than BIT and CHARACTER. You
must specify either BIT or CHARACTER with the ALIGNED attribute.
2.2.2 [omitted]
2.2.3 AREA Attribute
The AREA attribute defines an area variable. The format of the AREA attribute is:
AREA [(extent)] |
extent
The size of the area in bytes. The extent must be a nonnegative integer. The rules for specifying the extent are:
- If AREA is specified for a static variable declaration, extent must be a restricted integer expression. A restricted integer expression is one that yields only integral results and has only integral operands. Such an expression can use only the addition (+), subtraction ( - ), and multiplication (*) operators.
- If AREA is specified in the declaration of a parameter or in a parameter descriptor, you can specify extent as an integer constant or as an asterisk (*).
- If AREA is specified for an automatic or based variable, you can specify extent as an integer constant or as an expression. For automatic variables, the extent expression must not contain any variables or functions declared in the same block, except for parameters.
The AREA attribute is not allowed in a returns descriptor. The AREA
attribute conflicts with all other data-type attributes.
2.2.4 AUTOMATIC Attribute
The AUTOMATIC attribute specifies, for one or more variables, that PL/I is to allocate storage only for the duration of a block. An automatic variable is not allocated storage until the block that declares it is activated. The storage is released when the block is deactivated. The format of the AUTOMATIC attribute is:
⎰AUTOMATIC⎱ ⎱AUTO ⎰
AUTOMATIC explicitly defines the storage-class of a variable, array, or major structure in a DECLARE statement. Because AUTOMATIC is the default for internal variables, you need not specify it.
The AUTOMATIC attribute conflicts with the following attributes (the specification of which implies that storage allocation is not to be automatic):
BASED | |
PARAMETER | |
DEFINED | |
EXTERNAL | STATIC |
BASED [ (reference) ] |
reference
A reference to a pointer or offset variable or pointer-valued function. If the reference is to an offset variable, that variable must be declared with a base area. Each time a reference is made to a based variable without an explicit pointer or offset qualifier, the reference is evaluated to obtain the pointer or offset value.
The following attributes conflict with the BASED attribute:
AUTOMATIC |
DEFINED |
EXTERNAL |
PARAMETER |
STATIC |
The BASED attribute cannot be applied to minor structures, members of structures, parameters, or descriptions in an ENTRY or RETURNS attribute.
See Section 5.5 for more information on the BASED variable.
2.2.6 BINARY Attribute
The BINARY attribute specifies that an arithmetic variable has a binary base. The format of the BINARY attribute is:
⎰BINARY⎱ ⎱BIN ⎰
When you specify the BINARY attribute for an identifer, you can also specify one of the following attributes to define the scale and precision of the data:
FIXED [(precision[,scale])] FLOAT [(precision)] |
FIXED indicates a fixed-point binary value and FLOAT indicates a floating-point binary value.
For a fixed-point binary value, the precision specifies the number of bits representing an integer and must be positive. For a fixed-point binary value, the scale factor represents the number of bits to the right of the binary point and must be 0.
For a floating-point value, the precision specifies the number of bits representing the mantissa of a floating-point number.
The BINARY attribute directly conflicts with any other data-type
attribute.
2.2.7 BIT Attribute
The BIT attribute identifies a variable as a bit-string variable. The format of the BIT attribute is:
BIT[(length)] |
length
The number of bits in the variable. If you do not specify a length, the default length is 1 bit. The length must be in the range 0 through 32,767.The rules for specifying the length are:
- If the attribute is specified for a static variable declaration or in a returns descriptor, length must be a restricted integer expression. A restricted integer expression is one that yields only integral results and has only integral operands. Such an expression can use only the addition (+), subtraction ( - ), and multiplication (*) operators.
- If the attribute is specified in the declaration of a parameter or in a parameter descriptor, you can specify length as a restricted integer expression or as an asterisk (*).
- If the attribute is specified for an automatic, based, or defined variable, you can specify length as an expression. In the case of automatic or defined variables, the expression must not contain any variables or functions that are declared in the same block except for parameters.
If specified, the length in parentheses must follow the keyword BIT.
If you give a variable the BIT attribute, you can also specify the ALIGNED attribute to request alignment of the variable on a byte boundary in storage.
The BIT attribute directly conflicts with any other data-type attribute.
2.2.8 BUILTIN Attribute
The BUILTIN attribute indicates that the name declared is the name of a PL/I built-in function. Within the block in which the name is declared, all references to the name will be interpreted as references to the built-in function or pseudo-variable of that name. The format of the BUILTIN attribute is:
BUILTIN |
Use the BUILTIN attribute when you want to refer to a built-in function within a block in which the function's name has been used to declare a variable.
You also use the BUILTIN attribute when you want to invoke a built-in function that takes no arguments (such as the DATE function) and you do not want to include a null argument list.
When you specify the BUILTIN attribute, you cannot specify any other attributes.
OUTER: PROCEDURE; DECLARE MAX FIXED BINARY STATIC INITIAL (10); . . . INNER: PROCEDURE; DECLARE MAX BUILTIN; TEST = MAX(A,B); . . . END INNER; END OUTER; |
The keyword MAX is used here as a variable name. In the internal procedure INNER, the MAX built-in function is invoked. Because the scope of the name MAX includes the internal procedure, the function must be redeclared with BUILTIN.
You can also use the BUILTIN attribute to declare PL/I built-in functions that have no arguments, if you want to invoke them without the empty argument list. For example:
DECLARE DATE BUILTIN; PUT LIST(DATE); |
Without the declaration, the PUT LIST statement would have to include an empty argument list for DATE:
PUT LIST(DATE()); |
The CHARACTER attribute identifies a variable as a character-string variable. The format of the CHARACTER attribute is:
⎧CHARACTER⎫ ⎨ ⎬ [(length)] ⎩CHAR ⎭
length
The number of characters in a fixed-length string or the maximum length of a varying-length string. If not specified, a length of 1 is assumed. The length must be in the range 0 through 32,767 characters.The rules for specifying the length are:
- If the attribute is specified for a static variable declaration or in a returns descriptor, length must be a restricted integer expression.
- If the attribute is specified in the declaration of a parameter or in a parameter descriptor, you can specify length as a restricted integer expression or as an asterisk (*).
- If the attribute is specified for an automatic, based, or defined variable, you can specify length as an expression. In the case of automatic or defined variables, the expression must not contain any variables or functions that are declared in the same block except for parameters.
If specified, the length must immediately follow the keyword CHARACTER, and it must be enclosed in parentheses.
If you give a variable the CHARACTER attribute, you can also specify the attribute VARYING, NONVARYING, ALIGNED, or UNALIGNED.
The CHARACTER attribute directly conflicts with any other data-type
attribute.
2.2.10 CONDITION Attribute
You can optionally use the CONDITION attribute in a declaration to specify that the variable name is a condition name. You can specify INTERNAL or EXTERNAL scope attributes with the CONDITION attribute. The default scope is external. The format of the CONDITION attribute is:
⎧CONDITION⎫ ⎨ ⎬ (condition-name) ⎩COND ⎭
condition-name
Name used for ON units to handle programmer-defined conditions.
The DECIMAL attribute specifies that an arithmetic variable has a decimal base. The format of the DECIMAL attribute is:
⎰DECIMAL⎱ ⎱DEC ⎰
When you specify the DECIMAL attribute for a variable, you can also specify the following attributes to define the scale factor and precision of the data:
FIXED (precision[,scale-factor]) FLOAT (precision) |
FIXED indicates a fixed-point value, and FLOAT indicates a floating-point decimal value.
(precision[,scale-factor])
The precision of a fixed-point decimal value is the total number of integral and fractional digits. The precision of a floating-point decimal value is the total number of digits in the mantissa.
The default values applied to the DECIMAL attribute are:
Attributes Specified | Defaults Supplied |
---|---|
DECIMAL | FIXED (10,0) |
DECIMAL FIXED | (10,0) |
DECIMAL FIXED (n) | (n,0) |
DECIMAL FLOAT | (7) |
The DECIMAL attribute conflicts with any other data-type attribute.
2.2.13 DEFINED Attribute
The DEFINED attribute indicates that PL/I is not to allocate storage for the variable, but is to map the description of the variable onto the storage of another base variable. The DEFINED attribute provides a way to access the same data using different names. The format of the DEFINED attribute is:
⎧DEFINED⎫ ⎨ ⎬ (variable-reference) ⎩DEF ⎭
variable-reference
A reference to a variable that has storage associated with it. The variable must not have the BASED or DEFINED attribute. The variable and the declared variable must satisfy the rules given for defined variables in Section 5.8.
The following attributes conflict with the DEFINED attribute:
AUTOMATIC | BASED |
EXTERNAL | INITIAL |
PARAMETER | STATIC |
UNION |
The DEFINED attribute cannot be applied to minor structures, members of structures, parameters, or descriptions in an ENTRY or RETURNS attribute.
See Section 5.8 for more information on defined variables.
2.2.14 [omitted]
2.2.15 DIMENSION Attribute
The DIMENSION attribute defines a variable as an array. It specifies the number of dimensions of the array and the bounds of each dimension. The format of the DIMENSION attribute is:
bound-pair
One or two expressions that indicate the number of elements in a single dimension of the array. You must specify the list of bound pairs immediately following the name of the identifier in the array declaration if the optional keyword DIMENSION or DIM is omitted; otherwise, you must specify the list of bound pairs immediately following the keyword DIMENSION or DIM. See the following examples.A bound pair can be specified:
- [lowerbound:]upperbound
This format of a bound pair specifies the minimum and maximum subscripts that can be used for the dimension. The number of elements is:
(upperbound - lowerbound) + 1
If the lower bound is omitted, it is assumed to be 1.- *
This format of a bound pair, when used to define a parameter for a procedure or function, indicates that the bounds are to be determined from the associated argument. If one bound pair is specified as an asterisk, all bound pairs must be specified as asterisks.
The following two declarations are exactly equivalent:
DECLARE A(10) FIXED BIN; DECLARE A FIXED BIN DIMENSION(10); |
The following two declarations are also equivalent:
DECLARE B(1:5,1:5) FLOAT DEC; DECLARE B DIM(1:5,1:5) FLOAT DEC; |
The DIRECT file description attribute indicates that a file will be accessed only in a nonsequential manner, that is, by key or by relative record number.
The format of the DIRECT attribute is:
DIRECT |
The DIRECT attribute implies the RECORD and KEYED attributes.
Specify the DIRECT attribute on a DECLARE statement for a file constant or on an OPEN statement to access the file. A file declared with the DIRECT attribute must be one of the following:
To to access a file both randomly and sequentially, use the SEQUENTIAL attribute instead of DIRECT.
The DIRECT attribute conflicts with the SEQUENTIAL, STREAM, and PRINT
attributes.
2.2.17 ENTRY Attribute
The ENTRY attribute declares a constant or variable whose value is an entry point and describes the attributes of the parameters (if any) that are declared for the entry point. The format of the ENTRY attribute is:
parameter-descriptor
A set of attributes describing a parameter of the entry. Attributes describing a single parameter must be separated by spaces; sets of attributes (each set describing a different parameter) must be separated by commas. Parameter descriptors are not allowed if the ENTRY attribute is within a RETURNS descriptor.The following rules apply to the specification of a parameter descriptor for an array or structure:
- If the parameter is a structure, the level number must precede the attributes for each member.
- You must specify extents for a parameter using only integer constants, restricted integer expressions, or asterisks (*) .
- You cannot specify storage-class attributes.
RETURNS (returns-descriptor)
For an entry that is invoked as a function reference, an option giving the data type attributes of the function value returned. For entry points that are invoked by function references, the RETURNS attribute is required; for procedures that are invoked by CALL statements, the RETURNS attribute is invalid.
The ENTRY attribute without the VARIABLE attribute implies the EXTERNAL attribute (and implies that the declared item is a constant), unless the ENTRY attribute is used to declare a parameter.
You must declare all external entry constants with the ENTRY attribute.
You cannot declare internal entry constants with the ENTRY attribute in the procedure to which they are internal. Internal entry constants are declared implicitly by the labels on the PROCEDURE statements of an internal procedure.
The ENTRY attribute conflicts with all other data-type attributes.
DECLARE COPYSTRING ENTRY (CHARACTER (40) VARYING, FIXED BINARY(7)) RETURNS (CHARACTER(*)); |
This declaration describes the external entry COPYSTRING. This entry has two parameters: a varying-length character string with a maximum length of 40 and a fixed-point binary value. The RETURNS attribute indicates that COPYSTRING is invoked as a function and that it returns a character string of any length.
FILE |
The FILE attribute is implied by any of the following file description attributes:
DIRECT | OUTPUT | SEQUENTIAL |
ENVIRONMENT | STREAM | |
INPUT | RECORD | UPDATE |
KEYED |
See Table 9-2 for definitions of these file description attributes.
If the VARIABLE attribute is not specified, the FILE attribute declares a file constant. If the INTERNAL attribute is not specified, the file has the EXTERNAL attribute by default. All external declarations of a file constant are associated with the same file.
The FILE attribute conflicts with all other data-type attributes. If
the FILE attribute is used to declare a variable or parameter, no file
description attributes may be specified. If the VARIABLE attribute is
not specified, no storage-class attributes are allowed.
2.2.21 FIXED Attribute
The FIXED attribute indicates that the variable so declared is arithmetic with a fixed number of fractional digits. Such variables are called fixed-point (as opposed to floating-point) variables because the decimal point and binary point are fixed relative to the representation of the value. The format of the FIXED attribute is:
FIXED [(precision[,scale-factor])] |
precision
The precision is the number of decimal or binary digits used to represent values of the variable.scale-factor
Scale factor indicates how much of the precision is to be used for fractional digits.
When you specify the FIXED attribute in a DECLARE statement, you can specify either the BINARY or the DECIMAL attribute to indicate a binary or decimal fixed-point variable. For example, the attributes FIXED DECIMAL(31,5) define a variable that takes fixed-point decimal values of up to a maximum of 31 bits, 5 of which are fractional. The attributes FIXED BINARY(10,0) define a variable that takes fixed-point binary values. PL/I supplies default attributes for attributes that you do not specify (as shown in the following table).
You normally use fixed-point binary data to represent integers.
You can also use fixed-point decimal data, which can represent larger absolute values. You use fixed-point data whenever arithmetic values must be precise to a specified number of fractional digits. For a fixed-point decimal value, the precision must be in the range 1 through 31 (decimal digits). The scale factor, if specified, must be greater than or equal to zero and less than or equal to the specified precision.
If the scale factor is omitted, zero is used (that is, an integer variable is declared).
The default values given for unspecified related attributes are:
Attributes Specified | Defaults Supplied |
---|---|
FIXED | BINARY |
The FIXED attribute directly conflicts with all data-type attributes
except BINARY and DECIMAL.
2.2.22 FLOAT Attribute
The FLOAT attribute indicates that a variable is a floating-point arithmetic item. The format of the FLOAT attribute is:
FLOAT [(precision)] |
When you specify the FLOAT attribute in a DECLARE statement, you can specify either the BINARY or the DECIMAL attribute. The default values given for unspecified related attributes are:
Attributes Specified | Defaults Supplied |
---|---|
FLOAT | BINARY |
The FLOAT attribute directly conflicts with all data-type attributes
except BINARY and DECIMAL.
2.2.23 [omitted]
2.2.24 [omitted]
2.2.25 INITIAL Attribute
The INITIAL attribute provides an initial value for a declared variable. The format of the INITIAL attribute is:
⎰INITIAL⎱ ⎰ (initial-element[,initial-element...])⎱ ⎱INIT ⎰ ⎱((*) valid-expression) ⎰
initial-element
A construct that supplies a value for the initialized variable. The value must be valid for assignment to the initialized variable. If the initialized variable is an array, a list of initial elements separated by commas is used to initialize individual elements. The number of initial elements must be 1 for a scalar variable and must not exceed the number of elements of an array variable. Each initial element must have one of the following forms:
- string-constant
- (replication-factor) string-constant
- (iteration-factor) (string-constant)
- (iteration-factor) ((replication-factor) string-constant)
- [(iteration-factor)] arithmetic-constant
- [(iteration-factor)] scalar-reference
- [(iteration-factor)] (scalar-expression)
- [(iteration-factor)] *
The iteration factors are nonnegative integer-valued expressions that specify the number of successive array elements to be initialized with the following value.
An asterisk ( * ) following the iteration factor specifies that the corresponding array elements are to be skipped during the initialization.
You can use a replication factor in combination with an iteration factor in initializing a string constant. For example, the following two statements are equivalent:
INITIAL ((10)('ABCABC')) INITIAL ((10)((2)'ABC'))The first statement uses an iteration factor exclusively; the second statement combines an iteration factor of 10 with a replication factor of 2.
A string constant must be parenthesized if it is used with an iteration factor, because this set of parentheses prevents the iteration factor from being interpreted as a string replication factor.
INITIAL ((10)'ABC')For example, the initial value is interpreted as a string replication factor, not an iteration factor, and cannot be used to initialize a whole array.
(*) valid-expression
A construct that initializes all elements of an array to the same value by means of the asterisk iteration factor. The expression must evaluate to a value that is valid for assignment to the initialized array. If the expression is a string constant, it must be parenthesized so that the asterisk iteration factor is not interpreted as a string replication factor. The possible expressions are:
- (string-constant)
- ((replication-factor) string-constant)
- arithmetic-constant
- scalar-reference
- (scalar-expression)
- *
An asterisk following the asterisk iteration factor results in no initializations being performed.
The following are examples of declarations that include the INITIAL attribute:
DECLARE RATE FIXED DECIMAL (2,2) STATIC INITIAL (.04); DECLARE SWITCH BINARY STATIC INITIAL ('1'B); DECLARE BELL_CHAR BINARY STATIC INITIAL ('07'B4); DECLARE OUTPUT_MESSAGE CHARACTER(20) STATIC INITIAL ('GOOD MORNING'); DECLARE (A INITIAL ('A'), B INITIAL ('B'), C INITIAL ('C')) STATIC CHARACTER; DECLARE QUEUE_END POINTER STATIC INITIAL(NULL(:POINTER:)); DECLARE X(10,5) FIXED BIN(31) INITIAL ((*) -2); /* Initializes all 50 elements to -2 */ DECLARE 1 A(10), 2 B(10), 3 C(10) FIXED BIN(31) INITIAL ((*) 4); /* Initializes all 1000 elements to 4 */ DECLARE A(10) FIXED INIT ((5) 1,(5) 2); /* Initializes the first 5 elements to 1 and the second 5 elements to 2 */ |
The following declaration is not valid, because the asterisk iteration factor cannot be used to initialize part of an array; it can only be used to initialize all elements of the array to the same value:
DECLARE A(10) FIXED INIT ((5) 1,(*)2); /* Invalid use of asterisk iteration factor */ |
You cannot specify the INITIAL attribute for a structure variable. You must individually initialize the members of the structure.
You cannot specify the INITIAL attribute for a variable or member of a structure that has any of the following attributes:
If the initialized variable is STATIC, only constants, restricted expressions, and references to the EMPTY built-in functions are allowed. You can use these initial values with a constant iteration factor.
Variables and functions (except for parameters) occurring in an initial
element (for automatic variables) must not be declared in the same
block as the variable being initialized.
2.2.26 INPUT Attribute
The INPUT file description attribute indicates that the associated file is to be an input file. The format of the INPUT attribute is:
INPUT |
Specify the INPUT attribute on a DECLARE statement for a file constant or on an OPEN statement to access the file for reading.
You can specify the INPUT attribute with either the STREAM or the RECORD attribute. For a stream file, INPUT indicates that the file will be accessed with GET statements. For a record file, INPUT indicates that the file will be accessed only with READ statements.
For example:
DECLARE INFILE RECORD INPUT; OPEN FILE(INFILE); READ FILE(INFILE) INTO(RECORD_BUFFER); |
These statements declare, open, and access the first record in the input file INFILE.
For a description of the attributes that can be applied to files, see Table 9-2.
The INPUT attribute can be supplied by default for a file, depending on the context of its opening. See Section 9.1.3.3 for more information.
The INPUT attribute conflicts with the OUTPUT, UPDATE, and PRINT
attributes and with any data-type attribute other than FILE.
2.2.27 INTERNAL Attribute
The INTERNAL attribute limits the scope of an identifier to the block in which the identifier is declared and its dynamic descendents.
The format of the INTERNAL attribute is:
⎰INTERNAL⎱ ⎱INT ⎰
You only need to use the INTERNAL attribute to explicitly declare the scope of a file constant as internal. File constants, by default, have the EXTERNAL attribute.
The INTERNAL attribute directly conflicts with the EXTERNAL, GLOBALDEF,
and GLOBALREF attributes.
2.2.28 KEYED Attribute
The KEYED file description attribute indicates that you can randomly access records in the specified file. The KEYED attribute implies the RECORD attribute.
Specify KEYED in a DECLARE statement to identify a file or in an OPEN statement to open the file. For a description of the attributes that can be applied to files, see Table 9-2.
The KEYED attribute conflicts with the STREAM attribute and with any
data-type attributes other than FILE.
2.2.29 LABEL Attribute
The LABEL attribute declares a label variable; it indicates that values given to the variable will be statement labels. The format of the LABEL attribute is:
LABEL |
You cannot specify the LABEL attribute with any other data-type
attribute, the INITIAL attribute, or any file description attributes.
2.2.30 [omitted]
2.2.31 [omitted]
2.2.32 [omitted]
2.2.33 NONVARYING Attribute
The NONVARYING attribute keyword explicitly states that a bit-string or character-string variable has a fixed length, not a varying length. Because NONVARYING is the default for bit and character strings, it need not be specified. The format of the NONVARYING attribute is:
⎰NONVARYING⎱ ⎱NONVAR ⎰
2.2.34 OFFSET Attribute
The OFFSET attribute declares a variable that will be used to reference
a based variable within an area. The format of the OFFSET attribute is:
OFFSET [(area-reference)] |
area-reference
The name of a variable with the AREA attribute. The value of the offset variable will be interpreted as an offset within the specified area.
DECLARE MAP_SPACE AREA (40960), MAP_START OFFSET (MAP_SPACE), MAP_LIST(100) CHARACTER(80) BASED (MAP_START); |
These declarations define an area named MAP_SPACE, an offset variable that will contain offset values within that area, and a based variable whose storage is located by the value of the offset variable MAP_START.
The area reference must be omitted if the OFFSET attribute is specified
within a returns descriptor, parameter declaration, or a parameter
descriptor. The OFFSET attribute conflicts with all other data-type
attributes.
2.2.35 [omitted]
2.2.36 OUTPUT Attribute
The OUTPUT file description attribute indicates that data is to be written to, and not read from, the associated external device or file. The format of the OUTPUT attribute is:
OUTPUT |
Specify the OUTPUT attribute on a DECLARE statement for a file constant or on an OPEN statement to access the file for writing. You can specify the OUTPUT attribute with either the STREAM or the RECORD attribute. For a stream file, OUTPUT indicates that the file will be accessed with PUT statements. For a record file, OUTPUT indicates that the file will be accessed with only WRITE statements.
DECLARE OUTFILE RECORD OUTPUT; OPEN FILE(OUTFILE); WRITE FILE(OUTFILE) FROM(RECORD_BUFFER); |
These statements declare, open, and write a record to the output file OUTFILE.
For a description of the attributes that you can apply to files and the effects of combinations of these attributes, see Chapter 9.
The OUTPUT attribute conflicts with the INPUT and UPDATE attributes and
with any data-type attributes other than FILE.
2.2.37 PARAMETER Attribute
A variable occurring in the parameter list of a PROCEDURE or ENTRY statement has the PARAMETER attribute implicitly. You can optionally use the PARAMETER keyword in the declaration of a variable name to state explicitly that it is a parameter. The format of the PARAMETER attribute is:
⎰PARAMETER⎱ ⎱PARM ⎰
The following example uses the PARAMETER keyword:
TEST: PROC( A, B ); DCL A CHAR(*) PARAMETER; DCL B FIXED BIN PARM; . . . |
Refer to Section 7.5 for a discussion on parameters.
2.2.38 PICTURE Attribute
The PICTURE attribute is used to declare a pictured variable. Pictured variables have fixed-point decimal attributes, but values of the variable are stored internally as character strings. The character string contains decimal digits representing the numeric value of the variable, plus special editing symbols described in the picture. The format of the PICTURE attribute is:
⎧PICTURE⎫ ⎨ ⎬ P'picture' ⎩PIC ⎭
Whitespace may or may not be present between the P and the quoted string.
picture
A string of picture characters that define the representation of the variable.
See Section 3.2.5.1 for detailed information about picture characters, syntax, and examples.
The PICTURE attribute conflicts with all other data-type attributes.
2.2.39 POINTER Attribute
The POINTER attribute indicates that the associated variable will be used to identify locations of data. The format of the POINTER attribute is:
⎰POINTER⎱ ⎱PTR ⎰
The POINTER attribute conflicts with all other data-type attributes.
2.2.40 [omitted]
2.2.41 [omitted]
2.2.42 PRINT Attribute
The PRINT attribute is used to declare a print file. The file SYSPRINT, used as the default output by PUT statements, is a print file. The format of the PRINT attribute is:
|
Print files are stream output files with special formatting characteristics. The PRINT attribute implies the OUTPUT and STREAM attributes.
The PRINT attribute conflicts with the INPUT, RECORD, UPDATE, KEYED,
SEQUENTIAL, and DIRECT attributes.
2.2.43 [omitted]
2.2.44 RECORD Attribute
The RECORD file description attribute indicates that data in an input or output file consists of separate records and that the file will be processed by record I/O statements. The format of the RECORD attribute is:
RECORD |
The RECORD attribute is implied by the DIRECT, SEQUENTIAL, KEYED, and UPDATE attributes.
You can specify this attribute in a DECLARE statement for a file constant or in the OPEN statement that accesses the file.
The RECORD attribute conflicts with the STREAM and PRINT attributes.
2.2.45 REFER Attribute
The REFER attribute defines dynamically self-defining structures. The format of the REFER attribute is:
REFER |
See Section 4.2.6.3 for more information on the REFER option.
2.2.46 [omitted]
2.2.47 RESERVED Attribute
The RESERVED option must be specified on a DECLARE statement. It is similar to STATIC, except that the variable is not allocated. A STATIC definition of the variable must be present elsewhere.
If both RESERVED and STATIC are present, RESERVED takes precedence.
The RETURNS option must be specified on the PROCEDURE or ENTRY statement if the corresponding entry point is invoked as a function. The RETURNS attribute is specified with the ENTRY attribute to give the data-type of a value returned by an external function. The format of the RETURNS option and attribute is:
RETURNS (returns-descriptor...) |
returns-descriptor
One or more attributes that describe the value returned by the function to its point of invocation. The returned value becomes the value of the function reference in the invoking procedure. The attributes must be separated by spaces, except for attributes (the precision, for example) that are enclosed in parentheses.
The data types you can specify for a returns descriptor are restricted to scalar elements of either computational or noncomputational types. Areas are not allowed.
You can specify the extent of a character-string or bit-string value as an asterisk (*) to indicate that the string can have any length. Otherwise, extents must be specified with restricted expressions.
You cannot use the RETURNS option or the RETURNS attribute for procedures that are invoked by the CALL statement.
The attributes specified in a returns descriptor in a RETURNS attribute must correspond to those specified in the RETURNS option of the PROCEDURE statement or ENTRY statements in the corresponding procedure. For example:
CALLER: PROCEDURE OPTIONS (MAIN); DECLARE COMPUTER ENTRY (FIXED BINARY) RETURNS (FIXED BINARY); /* RETURNS attribute */ DECLARE TOTAL FIXED BINARY; . . . TOTAL = COMPUTER (A+B); |
The first DECLARE statement declares an entry constant named COMPUTER. COMPUTER will be used in a function reference to invoke an external procedure, and the function reference must supply a fixed-point binary argument. The invoked function returns a fixed-point binary value, which then becomes the value of the function reference.
The function COMPUTER contains the following:
COMPUTER: PROCEDURE (X) RETURNS (FIXED BINARY); /* RETURNS option */ DECLARE (X, VALUE) FIXED BINARY; . . . RETURN (VALUE); /* RETURN statement */ |
In the PROCEDURE statement, COMPUTER is declared as an external entry
constant, and the RETURNS option specifies that the procedure return a
fixed-point binary value to the point of invocation. The RETURN
statement specifies that the value of the variable VALUE be returned by
COMPUTER. If the data-type of the returned value does not match the
data-type specified in the RETURNS option, PL/I converts the value to
the correct data-type according to the rules given under Section 6.4.
2.2.49 SEQUENTIAL Attribute
The SEQUENTIAL file description attribute indicates that records in the file will be accessed in a sequential manner. The format of the SEQUENTIAL attribute is:
If you specify SEQUENTIAL, the RECORD attribute is implied.
Specify the SEQUENTIAL attribute in a DECLARE statement for a file constant or in the OPEN statement that accesses the file.
You can apply the SEQUENTIAL attribute to files with sequential,
The SEQUENTIAL attribute conflicts with the DIRECT, STREAM, and PRINT
attributes.
2.2.50 STATIC Attribute
The STATIC attribute specifies the way that PL/I is to allocate storage for a variable. The format of the STATIC attribute is:
STATIC |
The STATIC attribute is implied by the EXTERNAL attribute. For more information on STATIC and on other storage-class attributes, see Chapter 5.
The STATIC attribute directly conflicts with the BASED,
DEFINED, and parameter attributes. The STATIC attribute cannot be
applied to members of structures, parameters, or descriptions in an
ENTRY or RETURNS attribute.
2.2.51 STREAM Attribute
The STREAM file description attribute indicates that the file consists of characters and that it will be processed using GET and PUT statements. The format of the STREAM attribute is:
STREAM |
The STREAM attribute is implied by the PRINT attribute. It is also supplied by default for a file that is implicitly opened with a GET or PUT statement.
Specify the STREAM attribute in a DECLARE statement for a file identifier or in the OPEN statement that opens the file.
The STREAM attribute directly conflicts with the RECORD, KEYED, DIRECT,
SEQUENTIAL, and UPDATE attributes.
2.2.52 [omitted]
2.2.53 [omitted]
2.2.54 UNALIGNED Attribute
The UNALIGNED attribute is used in conjunction with the BIT attribute to specify that a bit-string variable should not be aligned on a byte boundary. Because UNALIGNED is the default for bit strings, it need not be specified. The format of the UNALIGNED attribute is:
You can use the UNALIGNED attribute in the declaration of character strings. All character strings are aligned on byte boundaries; therefore, the UNALIGNED attribute has no effect on the actual storage of a character string.
The UNALIGNED attribute conflicts with all data-type attributes other
than BIT and CHARACTER.
2.2.55 UNION Attribute
The UNION attribute, which can be used only in conjunction with a level number in a structure declaration, signifies that all immediate members of the major or minor structure so designated occupy the same storage. Immediate members are those members having a level number 1 higher than the major or minor structure with the UNION attribute. For example, if the UNION attribute were associated with level n, then all members or minor structures at level n+1 up to the next member at level n would be immediate members and would occupy the same storage. The format for the UNION attribute is:
level-number identifier [storage-class] UNION |
level-number
The level number of the variable with which the declarations in the reference share storage.identifier
Names the variable with which the declarations in the reference share storage. A variable declared with the UNION attribute must be a major or minor structure.storage-class
The storage class specified for the structure. You can specify the storage class only on level 1.
The UPDATE attribute is a file description attribute indicating that the associated file is to be used for both input and output. You can sequential disk files with fixed-length records. The format of the UPDATE attribute is:
UPDATE |
Specify the UPDATE attribute on a DECLARE statement for a file constant or on an OPEN statement to access the file for update. The UPDATE attribute implies the RECORD attribute.
For a description of the attributes that are applied to files, see Section 9.1.3.3.
The UPDATE attribute directly conflicts with the INPUT, OUTPUT, STREAM,
and PRINT attributes and with any data-type attribute other than FILE.
2.2.57 [omitted]
2.2.58 VARIABLE Attribute
The VARIABLE attribute indicates that the associated identifier is a variable. VARIABLE is implied by all computational data-type attributes and by all noncomputational attributes except FILE and ENTRY. The format of the VARIABLE attribute is:
VARIABLE |
If you specify the FILE or ENTRY attribute in a DECLARE statement without the VARIABLE attribute, the defined object is assumed to be a file or entry constant.
The VARIABLE attribute is implied by the LABEL attribute. You can declare label constants only by using the label identifier in the program; you cannot define a label constant in a DECLARE statement.
The VARIABLE attribute is not valid in a returns descriptor or in a
parameter descriptor.
2.2.59 VARYING Attribute
The VARYING attribute indicates that a character-string variable does not have a fixed length, but that its length changes according to its current value. The format of the VARYING attribute is:
You must specify a length attribute in conjunction with VARYING by giving the maximum length allowed for the variable. The current length is stored with the value and can be determined at any time with the LENGTH built-in function. If you need to determine the maximum declared length of a varying- length character string, use the MAXLENGTH built-in function.
The value of an uninitialized CHARACTER VARYING variable is undefined.
The VARYING attribute directly conflicts with any data-type attribute other than CHARACTER.
DECLARE STRING CHARACTER(80) VARYING; |
A variable named STRING is declared as a varying-length character string with a maximum length of 80 characters.
S: PROCEDURE OPTIONS(MAIN); DECLARE STRING CHARACTER(80) VARYING; STRING = 'PIE'; PUT LIST (LENGTH(STRING)); PUT LIST (MAXLENGTH(STRING)); PUT LIST (SIZE(STRING)); END; |
The value returned by the built-in function LENGTH is 3, the length of
the current value of the string. The value returned by the built-in
function MAXLENGTH is 80, the maximum declared length.
2.3 DEFINE CONSTANT statement
The DEFINE CONSTANT statement is equivalent to the DECLARE statement with the following restrictions:
The VALUE attribute specifies the value of a defined constant. It is required in a DEFINE CONSTANT statement.
Restricted expressions can be evaluated at compile time, and are used not only in VALUE attributes, but also in extents for STATIC variables, parameters, return descriptors, and INITIAL attributes of STATIC variables.
All programs process data items. Data items can be constants or variables. A constant data item has a value that does not change during program execution; a variable data item can represent different values.
A data item has an associated type that you can specify as an attribute or collection of attributes in a declaration. Unlike other languages that often have a distinction between data types and data attributes, a PL/I data type is entirely defined by the data attributes given to a data item identifier. The data type that you select determines the operations that you can perform on data items and how they are stored.
The rest of this chapter describes data types in more detail.
3.1 Summary of Data Types
PL/I supports the following computational data types:
PL/I also supports the following noncomputational data types and attributes:
You can place each of these data types in aggregate structures or
arrays to form new data types. See Chapter 4 for more information.
3.1.1 Declarations
All names referenced in a PL/I program must be declared explicitly, with the exception of entry-point names, statement labels, built-in functions, and the default file constants SYSIN and SYSPRINT. You declare a name and its data type attributes in a DECLARE statement. For example:
DECLARE AVERAGE FIXED DECIMAL; DECLARE NAME CHARACTER (20); |
The keywords DECIMAL, FIXED, and CHARACTER describe characteristics, or
attributes, of the variables AVERAGE and NAME.
3.1.2 Default Attributes
It is not always necessary to define all the characteristics, or attributes, of a variable; the PL/I compiler makes assumptions about attributes that are not explicitly defined. For example:
DECLARE NUMBER FIXED; |
The default FIXED attribute implies the attribute BINARY and a precision of 0.,
Table 3-1 shows the default attributes implied by each computational data attribute.
Specified | Implied |
---|---|
FIXED | BINARY |
BINARY | FIXED |
FLOAT | BINARY |
FIXED DECIMAL(p) | (p,0) |
BIT [ALIGNED] | (1) |
CHARACTER [VARYING] | (1) |
PICTURE 'picture' |
Constants have attributes implied by the characters used to specify them. The following list describes the expression of constants and their attributes:
⎡ + ⎤ ⎢ ⎥ digit... ⎣ - ⎦
Note that PL/I has no constants with the attributes FIXED BINARY, FLOAT BINARY, or PICTURE. However, this presents no problems in programming because you can assign constants of any computational type to variables of any computational type and they are converted automatically to the target type.
You usually give values to binary variables by assigning decimal constants to them. For example:
I = 1; |
F = 1.333E-12; |
Picture variables are usually given values by assigning fixed-point decimal constants. For example:
PAY_PIC = 123.44; |
The implied data types of constants are important primarily because of PL/I's rules for converting operands in an arithmetic operation. (Bit-string and character-string operations must have bit- and character-string operands, respectively.) All operations, including arithmetic operations, must be performed in a single data type, and automatic conversions are performed on arithmetic operands to make this possible. For example:
DECLARE X FLOAT DECIMAL (49); X = X + 1.3; |
In this example, the fixed-point decimal constant 1.3 is converted to floating-point decimal before the addition is performed. The rules for operand conversion are discussed in detail in Section 6.4.2.
For information about arithmetic operators, operands, and data
conversions, see Chapter 6.
3.1.3 Compatible Data Types
In PL/I, the notion of compatible data types is used in the rules for passing arguments by reference and for based, defined, or external variables. For two nonstructure variables to have compatible data types, the following attributes must agree. That is, if one variable has the attribute, the other variable must also have it after the application of default rules.
ALIGNED | DIMENSION | OFFSET |
AREA | ENTRY | picture |
array bounds | FILE | PICTURE |
BINARY | FIXED | POINTER |
BIT | FLOAT | precision |
CHARACTER | LABEL | VARYING |
DECIMAL | length |
Two pictured variables must have identical pictures after the expansion of iteration factors.
In addition, the following values must be equal:
Two structure variables have compatible data types if they have the same number of immediate members, and if corresponding members have compatible data types.
In general, you can specify string lengths, area sizes, and array bounds with expressions or with asterisks. The values used to determine whether two variables have compatible data types are obtained as follows:
Consider the following example:
/* Example of extent determination */ DATAT: PROCEDURE (PTR1); DECLARE N FIXED, S CHARACTER(N) BASED(PTR1); DECLARE PTR1 POINTER; N = 10; CALL P(S); P: PROCEDURE(A); DECLARE A CHARACTER(*), B CHARACTER(N); N = 20; PUT LIST(LENGTH(A),LENGTH(B),LENGTH(S)); END P; END DATAT; |
The PUT statement writes out:
10 10 20 |
The assignment to N inside the procedure P affects the extent of S, but not the extents of A or B, which were frozen when P was invoked.
DECLARE x FIXED DECIMAL(10,3) INITIAL(1.234); |
This example indicates that the value of x has 10 decimal digits and that 3 of those are fractional. When a value is assigned to the variable, its internal representation does not include the decimal point; the previous value for x is stored as 1234, and the decimal point is inserted when the number is output. The scale factor has the effect of multiplying the internal representation of the decimal number by a factor of 10-q (where q is the absolute value of the specified scale).
If no scale factor is specified for fixed-point data, the default is 0.
Internally, binary numbers undergo an implicit conversion and are represented as powers of 2. For instance, in the previous example variable A is first divided by 23 because it is declared with a scale factor of -3. The stored number is 16. On output, the number 16 is multiplied by 23 and the number is again 128. However, when variable B is first divided by 23, the result is 0, which is the value of the stored number. Therefore, on output, 0 is multiplied by 23 and the output is 0.
Integer variables declared in the previous example with a positive scale factor are output as they were input, but they are followed on the right with a decimal point and a 0.
Even though arithmetic operands can be of different arithmetic types, all operations must be performed on objects of the same type. Consequently, the compiler can convert operands to a derived type, as previously shown. Therefore, when you declare a fixed binary number with a scale factor and assign it a decimal value, the results may not be as you expect because the binary scale factor left-shifts the specified number of bits to the right of the decimal point. During conversion to a decimal representation, the difference between the resulting binary number and its decimal representation is not the equivalent of dividing or multiplying the decimal number by 10. Instead, the binary number is first converted to its internal representation and then this representation is converted to its decimal representation.
When excess fractional digits are truncated, no condition is signaled. If there is any resulting loss of precision, it may be difficult to detect because truncated fractional digits do not signal a condition.
For example:
A: PROCEDURE OPTIONS (MAIN); DECLARE A FIXED DEC (31,3), B DECIMAL (10,5), C DECIMAL (10,5); A = .3; B = 34.8; C = MULTIPLY(A,B,10,5); PUT SKIP LIST (A,B,C); END; |
Before the multiplication is performed, the variables are converted to fixed binary so that the operands share a common data type. However, after conversion, variable A is output as 0.2 rather than 0.3. The output from the previous example is:
0.2 34.80000 8.6875 |
If variable A was declared with the attributes FIXED DECIMAL (10,5), the output will be:
0.30000 34.80000 10.44000 |
The attributes FIXED and BINARY are used to declare integer variables and fractional variables in which the number of fractional digits is fixed (that is, nonfloating-point numbers). The BINARY attribute is implied by FIXED.
For example, a fixed-point binary variable can be declared as:
DECLARE X FIXED BINARY; |
There is no representation in PL/I for a fixed-point binary constant. Instead, integer constants are represented as fixed decimal. However, fixed decimal integer constants (and variables) are converted to fixed binary when combined with fixed binary variables in expressions. For example:
I = I+3; |
In this example, if I is a fixed binary variable, the integer 3 is represented as fixed decimal; however, PL/I converts it to fixed binary when evaluating the expression.
The attributes FIXED BINARY are used to declare binary data in PL/I. The BINARY attribute is implied by FIXED. The format of a declaration of a single, fixed-point, binary variable is:
DECLARE identifier FIXED [BINARY] [(precision[,scale-factor])]; |
There is no form for a fixed-point binary constant, although constants
of other computational types are convertible to fixed-point binary. A
fixed-point binary variable usually receives given values by being
assigned to an expression of another computational type or another
fixed-point binary variable.
3.2.3 Fixed-Point Decimal Data
Fixed-point decimal data is used in calculations where exact decimal values must be maintained, for example, in financial applications. You can also use fixed-point decimal data with a scale factor of 0 whenever integer data is required.
The following sections describe fixed-point constants and variables and their use in expressions.
This discussion is divided into the following parts:
A fixed-point decimal constant can have a positive number of the decimal digits 0 through 9 with an optional decimal point or sign, or both. If there is no decimal point, PL/I assumes it to be immediately to the right of the rightmost digit. Some examples of fixed-point decimal constants are:
12 4.56 12345.54 -2 01. |
The precision of a fixed-point decimal value is the total number of
digits in the value. The scale factor is the number of digits to the
right of the decimal point, if any. The scale factor cannot be greater
than the precision.
3.2.3.2 Fixed-Point Decimal Variables
The attributes FIXED and DECIMAL are used to declare fixed-point decimal variables. The FIXED attribute is implied by DECIMAL.
If you do not specify the precision and the scale factor, the default values are 10 and 0, respectively.
The format of a declaration of a single fixed-point decimal variable is:
DECLARE identifier [FIXED] DECIMAL [(p[,q])]; |
Some examples of fixed-point decimal declarations are:
DECLARE PERCENTAGE FIXED DECIMAL (5,2); DECLARE TONNAGE FIXED DECIMAL (9); |
You cannot use fixed-point decimal data with a nonzero scale factor in calculations with binary integer variables. If you must use the two types of data together, use the DECIMAL built-in function to convert the binary value to a scaled decimal value before attempting an arithmetic operation. For example:
DECLARE I FIXED BINARY, SUM FIXED DECIMAL (10,2); SUM = SUM + DECIMAL (I); |
The floating-point data types provide a way to express very large and very small numbers such as in scientific calculations. All floating-point calculations are performed on values in one of the binary floating-point formats. In general, the precision of the result is determined by the maximum precision of any operands in the operation. The numerical result of an operation is rounded to the result precision.
The following sections describe floating-point constants and variables and their use in expressions.
This discussion of floating-point data is divided into the following parts:
A floating-point constant can have one or more of the decimal digits 0 through 9 (with an optional decimal point), followed by the letter E and from one to five decimal digits representing a power of 10. The floating-point value and the integer exponent can both be signed. The first portion of the value, to the left of the letter E, is called the mantissa. The value to the right of the letter E is called the exponent.
Some examples of floating-point constants are:
2E10 -3E8 32E-8 .45632E16 |
The decimal precision of each of these values is the number of digits in the mantissa.
If you write a constant without the E and the exponent, it is considered to be fixed-point decimal. However, you can use such constants anywhere in expressions involving floating-point data.
All floating-point constants are decimal.
If a floating-point constant contains more decimal digits
than the implementation can support, the excess digits are ignored.
3.2.4.2 Floating-Point Variables
The keyword FLOAT identifies a floating-point variable in a declaration.
A floating-point value can be either binary or decimal. Because the internal representation of floating-point variables is binary, it is recommended that you use FLOAT BINARY (which is the default) to declare variables, unless you need the properties of FLOAT DECIMAL. (The difference between FLOAT BINARY and FLOAT DECIMAL appears only when a conversion to another type, such as character, for doing I/O is necessary.) You should declare all floating-point variables using the same base.
To declare a single floating-point binary variable, specify a DECLARE statement as follows:
DECLARE identifier FLOAT [BINARY] [(p)]; |
You can optionally specify the precision for a floating-point variable in the declaration. For example:
DECLARE X FLOAT BINARY(53); |
The keyword FLOAT identifies a floating-point variable.
To declare a decimal floating-point variable, use the following format:
DECLARE identifier FLOAT DECIMAL [(p)]; |
For example:
DECLARE X FLOAT DECIMAL (30); |
You can use both integer and scaled decimal constants in floating-point expressions. An arithmetic constant is always converted to the appropriate internal representation for use in a floating-point operation. The target type for the conversion depends on the context. For example:
DECLARE X FLOAT BINARY (53); X = X + 1.3; |
Such a conversion is normally done during compilation, but in some
cases the constant is maintained in decimal until run time.
3.2.5 Pictured Data
Use pictured data when you want to manipulate a quantity arithmetically and accept or display its value using a special format.
A picture specification (or picture) describes both the numeric attributes of a pictured variable and its input/output (I/O) format. A simple picture might look like this in a DECLARE statement:
DECLARE CREDIT PICTURE '$99999V.99DB'; |
This statement declares the variable CREDIT as a pictured variable. The characters within the apostrophes describe its format.
The formatting possible with pictured data is useful in many applications, but pictured data is much less efficient than fixed-point decimal data for strictly computational use.
This section discusses the following topics:
The formatting possible with pictured data is useful in many
applications, but pictured data is less efficient than fixed-point
decimal data in computations. Therefore, do not use pictured data
unless you need the formatting.
3.2.5.1 Picture Characters
Table 3-6 summarizes the PL/I picture characters, their meaning, and whether they effect numeric interpretation and internal representation. The paragraphs following the table describe the picture characters and syntax. All picture characters are shown here in uppercase, but their lowercase equivalents can be used.
Character | Meaning | Numeric Interpretation |
---|---|---|
V | Position of assumed decimal point | yes |
9 | Decimal digit, including leading zeros | yes |
Z | Decimal digit with leading-zero suppression | yes |
* | Decimal digit with asterisk for leading zero | yes |
(n) | Iteration factor for subsequent character | yes |
$ | Position(s) of (drifting) dollar sign | yes |
+ | Position(s) of (drifting) plus sign if number >= 0 | yes |
- | Position(s) of (drifting) minus sign if number < 0 | yes |
S | Position(s) of (drifting) plus sign or minus sign | yes |
, | Position at which comma is inserted | no |
. | Position at which decimal point is inserted | no |
/ | Position at which slash is inserted | no |
B | Position at which space is inserted | no |
CR | Positions at which <BIT_STRING>(CR) is inserted if number < 0 | no |
DB | Positions at which <BIT_STRING>(DB) is inserted if number < 0 | no |
<...> | Positions at which characters between < and > are inserted | no |
<...>> | Positions at which (drifting) characters between < and >> are inserted | no |
The V character shows the position of the assumed decimal point, or the scale factor for the fixed-point decimal value. The V character has no effect on the internal representation of the pictured value and does not cause a decimal point to appear in the internal representation or in the output (use the period insertion character for this purpose). The following rules apply to the V character:
The following example shows the effect of the V character:
DECLARE PRICE PICTURE '$$9V.99', BAD_PRICE PICTURE '$$9.99'; PRICE = .98; /* Output as $0.98 */ BAD_PRICE = .98; /* Output as $0.00 */ PRICE = 98; /* Output as $98.00 */ BAD_PRICE = 98; /* Output as $0.98 */ |
In this example, note that the variable PRICE, which contains the V character, represents the value properly. The variable BAD_PRICE, which contains only the period insertion character, has an assumed V character at the end of the picture, which causes the variable to misrepresent the value.
The characters 9, Z, and Y, and the asterisk character (*) mark the positions occupied by decimal digits. The number of these characters present in a picture specifies the number of digits, or precision, of the fixed-point decimal value of the pictured variable. The following rules apply to these characters:
You can precede any picture character that can appear more than once in a picture by an iteration factor, which must be a positive integer constant enclosed in parentheses. For example:
'(4)9' |
'9999' |
Drifting Characters ($, +, -, S)
The dollar sign ($), plus sign (+), minus sign (-), and S character are drifting characters. You can use the drifting characters to indicate digits, and they also indicate a symbol to be inserted when, for example, a pictured value is written out by PUT LIST. The meanings of the characters are:
If one of these characters is used alone in the picture, it marks the position at which a special symbol or space is always inserted, and it has no effect on the value's numeric interpretation. In this case, the character must appear either before or after all characters that specify digit positions.
However, if a series of n of these characters appears, then the rightmost n-1 of the characters in the series also specify digit positions. If the digit is a leading 0, the leading 0 is suppressed, and the leftmost character drifts to the right; the character appears either in the position of the last drifting character in the series or immediately to the left of the first significant digit, whichever comes first.
Used this way, the n-1 drifting characters also define part of the numeric precision of the pictured variable, because they describe at least some of the positions occupied by decimal digits. For an example of this behavior by a drifting character (the dollar sign), refer to the V decimal place character description.
The following additional rules apply to drifting characters:
Insertion Characters (, . / B)
The insertion characters indicate that characters are inserted between digits in the pictured value. The insertion characters are the comma (,), period (.), slash (/), and the space (B). The B character indicates that a space is always inserted at the indicated position.
The drifting characters ($, +, -, S) also function as insertion characters when used singly (that is, not as part of a drifting string).
The period (.) does not imply a decimal place character V (see the example in the description of the V character, described earlier).
The following rules describe insertion by the comma, period, and slash insertion characters.
DECLARE NUM PICTURE 'ZZZV.ZZ', BAD_NUM PICTURE 'ZZZ.VZZ'; NUM=0.02; /* Output as .02 */ BAD_NUM=0.02; /* Output as 02 */ |
Credit (CR) and Debit (DB) Characters
These picture characters are always specified as the character pairs CR and DB. If either pair is included, it appears if the numeric value is less than zero. In each case, the associated positions contain two spaces if the numeric value is greater than or equal to 0.
The characters are inserted with the same case as used in the picture. If the lowercase form cr is used in the picture, lowercase letters are inserted in the pictured value; if the combination Cr is used, then Cr is inserted.
The credit and debit characters cannot be combined in one picture, nor can they be used in the same picture as any other character that specifies the sign of the value (S, plus sign (+), and minus sign (-) characters). In addition, they must appear to the right of all picture characters specifying digits.
After all its iterations are expanded and all its insertion characters are removed, a picture must satisfy the following syntax rules (the notation character, or ellipsis (...), indicates a series of the same character, with no embedded characters).
Picture:
'S99V.99'
The picture specifies a signed fixed-point number with a precision of 4 (p=4) and a scale factor of 2 (q=2). The sign of the number is always included in its representation, in the first position. A period is inserted at the position of the assumed decimal point.
'****99' |
The picture specifies a 6-digit integer, with the first four leading zeros replaced by asterisks.
'****V.**' |
The picture specifies a fixed-point number with p=6, q=2. The first four leading zeros are replaced by asterisks in the integral portion. Both fractional digits always appear unless all six digits are 0. A period is inserted at the position of the assumed decimal point.
'ZZ99V.99' |
The picture specifies a fixed-point number with p=6, q=2. The first two digits in the integral portion are replaced with spaces if they are zeros. Two digits always appear on either side of the decimal point.
'(4)SV.99' |
The picture specifies a fixed-point number with p=5, q=2. (The iteration factor 4 specifies a string of four S characters, one of which specifies a sign and three of which specify digits.) A plus (+) or minus (-) symbol is inserted to the immediate left of the first significant integral digit, or to the left of the decimal point if no integral digit is significant. Any insignificant integral digits are replaced with spaces or with the sign symbol.
'ZZZ,ZZZV.99' |
The picture specifies a fixed-point number with p=8, q=2. If the integral portion has four or more significant digits, a comma is inserted between the third and fourth digit; otherwise, both the leading zeros and the comma are suppressed. The decimal point always appears followed by two fractional digits.
'ZZZ.ZZZV,99' |
The picture specifies a fixed-point number with p=8, q=2. If the integral portion has four or more significant digits, a period is inserted between the third and fourth; otherwise, both the leading zeros and the period are suppressed. The decimal point (indicated by a comma) always appears followed by two fractional digits.
'ZZZ/ZZZ/ZZZ' |
The picture specifies a fixed-point number with p=9, q=0. A slash is inserted between the 3-digit groups unless the digit preceding the slash is a suppressed 0.
'999ZZZZV.99'
The picture is invalid because a 9 occurs to the left of Z.
'$$$-99v.99' |
The picture is invalid because it contains two drifting strings (<BIT_STRING>($$$) and <BIT_STRING>(- -)).
'(4)-V.ZZZ' |
The picture is invalid because fractional digits in this case must be pictured either with a drifting minus sign or with 9s.
CREDIT = '$12443.00'; |
This example signals the CONVERSION condition, because the character string contains a dollar sign and cannot be converted to fixed-point decimal. The value assigned to CREDIT should be either '12443.00' or 12443.00, both of which result in the same value assigned to CREDIT.
If a negative value is assigned to a pictured variable, the picture must include one of the sign picture characters (such as DB). For example:
CREDIT = -12443.00; |
When you use a pictured value in an arithmetic context (such as an assignment to an arithmetic variable), the picture is used to extract the fixed-point decimal number from the character string that internally represents the pictured value. Extraction also occurs when you input a pictured value with the GET EDIT statement and the P format item. If the contents of the pictured variable or input item do not conform to the picture, an error occurs.
For example:
DECLARE CREDIT PICTURE '$99999V.99DB'; |
The period and dollar sign are always inserted in the internal representation and the output value regardless of CREDIT's numeric value.
The picture character DB appears only when the value of CREDIT is less
than 0; otherwise, two spaces appear in the indicated positions. The DB
character also indicates that CREDIT's value is numerically negative,
so that if CREDIT is later assigned to an arithmetic variable the
variable will be given a negative value.
3.2.5.4 Editing by Picture
Any computational value or expression can be assigned to a pictured variable, as long as it meets these two qualifications:
When a value is assigned to a pictured variable, the value is edited to construct a character string that meets the picture specification. Editing also occurs when a value is output with the PUT EDIT statement and the P format item. Editing was performed in the previous examples in which fixed-point decimal values were assigned to the pictured variable CREDIT.
Because a picture specifies a fixed-point decimal value, the
FIXEDOVERFLOW condition is signaled in the same circumstances as for
assignment of an expression to a FIXED DECIMAL variable.
3.2.5.5 [omitted]
3.3 Character-String Data
A character string is a sequence of zero or more characters.
Every character-string variable has a length attribute that specifies either the length of all values of the variable (fixed-length strings) or the maximum length of a value of the variable (varying-length strings).
This discussion of character-string data is divided into the following parts:
A character-string constant can consist of any characters supported by the implementation. When you use character-string constants in a program, you must enclose the strings in apostrophes, as shown in the following examples:
'Total is:' 'Enter your name and age' 'Error - value is out of range' |
To specify a string containing a literal apostrophe, use two apostrophes within the string. For example:
'Life isn''t fair' |
When a character string that has embedded apostrophes is specified as previously shown, the final result contains only a single apostrophe.
Note that the quotation mark (") is not a legal delimiter for PL/I
character constants.
3.3.2 Character-String Variables
The CHARACTER keyword identifies a variable as a character-string variable in a declaration. The format for specifying a character-string variable is:
⎡CHARACTER [(n)] ⎤ DECLARE variable-name ⎢VARYING CHARACTER [(n)]⎥ ⎣CHARACTER [(n)] VARYING⎦
The CHARACTER keyword identifies a character-string variable in a declaration.
The addition of the VARYING attribute indicates a varying-length character-string variable.
An optional number in parentheses specifies the length of the variable, that is, the number of bytes needed to contain its value. This length attribute specifies either the length of all values of the variable (fixed-length strings) or the maximum length of a value of the variable (varying-length strings). If the length is not specified, PL/I uses the default length of one character, or byte. The rules for specifying the length are:
If specified, n must immediately follow the keyword CHARACTER and must
be enclosed in parentheses.
3.3.2.1 Fixed-Length Character-String Variables
A fixed-length character string is one that does not have the VARYING attribute. For a particular allocation of a fixed-length character-string variable, all its values have the same length. When a program assigns a value to a fixed-length character-string variable, however, the value does not need to have the same length defined for the variable. Depending on the size of the value, PL/I adjusts the assignment length according to the following rules:
DECLARE STRING CHARACTER (10); STRING = 'ABCDEF'; |
DECLARE STRING CHARACTER (4); STRING = 'ABCDEF'; |
When you define a character-string variable, you can also specify the VARYING attribute. In a varying character-string variable, the length is not fixed. The length specified in the declaration of the variable defines the maximum length of any value that can be assigned to the variable. Each time a value is assigned, the current length changes. For example:
DECLARE NAME CHARACTER (20) VARYING; NAME = 'COOPER'; NAME = 'RANDOM FACTOR'; |
The declaration of the variable NAME indicates that the maximum length of any character-string value it can have is 20. The current length becomes 6 when NAME is assigned the value 'COOPER'; the length becomes 13 when NAME is assigned the value 'RANDOM FACTOR'; and so on.
When a varying character string is assigned a value with a length greater than the maximum defined, the value is truncated on the right.
The initial length of an automatic varying-length character-string variable is undefined unless the variable is initialized.
You can use the LENGTH built-in function to determine the current
length of any string, and the MAXLENGTH built-in function to determine
the maximum length.
3.3.3 Alignment of Character Strings
The PL/I language makes a distinction between aligned and unaligned (fixed-length) character-string variables. (No such distinction is made for varying character strings or for character-string constants.) A character-string variable is aligned if it is declared with the ALIGNED attribute.
This distinction affects only argument passing. If a procedure declares a parameter as ALIGNED CHARACTER, and if the corresponding argument is an unaligned character-string variable or vice versa, the actual argument will be a dummy variable. For example:
DECLARE GETSTRING ENTRY (CHARACTER (*) ALIGNED); DECLARE STRING CHARACTER (8); CALL GETSTRING (STRING); |
PL/I constructs a dummy variable here to pass the unaligned string
variable STRING to the called procedure GETSTRING, rather than passing
the actual argument by reference.
3.4 Bit-String Data
A bit string consists of a sequence of binary digits, or bits. It can be used as a Boolean value, which has one of two states: true (if any bit is non-zero) or false (if all bits are 0).
Like a fixed-length character string, a bit string has a fixed length defined in the declaration or specified by the number of bits in a bit-string constant. However, bit-string variables cannot be declared with the VARYING attribute.
The rest of this section discusses bit-string constants and variables, alignment of bit-string data, and the use of bit strings to represent integers.
This discussion of bit-string data is divided into the following parts:
To specify a bit-string constant, enclose the string in apostrophes and follow the closing apostrophe with the letter B. For example:
'0101'B '10101010'B '1'B |
The length of a bit-string constant is always the number of binary digits specified; the B does not count in the length of the string. You can specify a bit-string constant with a maximum of 1000 characters between the apostrophes.
You can also specify a bit-string constant using the following syntax:
'character-string'Bn |
n
Is the number of bits to be represented by each digit in the string. n can have the value 1 through 4, and if not specified defaults to 1.
This format allows you to specify bit-string constants with bases other than 2. You can use base 4, 8, and 16, where n equals 2, 3, and 4 respectively. For example:
'EF8'B4 '117'B3 '223'B2 |
These constants specify the hexadecimal value EF8, the octal value 117, and the base 4 value 223. All such constants are stored internally as bit strings, not as integer representations of the value.
The valid characters for each type of bit-string constant are as follows:
Using the B format items, you can also acquire or output (with the GET
EDIT and PUT EDIT statements) bit-string data in binary, base 4, octal,
or hexadecimal format. See Section 9.2.4.2 for more information on the B
format item.
3.4.2 Bit-String Variables
Use the BIT attribute to declare a bit-string variable. The format is:
DECLARE variable-name BIT [(length)]; |
You can optionally specify the length of the variable in parentheses. The length can be from 0 to 32,767; the default length is one bit. The rules for specifying the length are as follows:
A program can assign to a bit-string variable a value larger or smaller than the variable's defined length. In such cases, PL/I does the following:
You can convert bit-string variables to other data types; however,
there are some precautions you must observe if you do so. Section 6.4
describes how to convert bit-string variables.
3.4.3 Alignment of Bit-String Data
PL/I distinguishes between aligned and unaligned bit-string variables. (Bit-string constants are always unaligned.) A bit-string variable is aligned only if it is declared with the ALIGNED attribute, as shown in the following example:
DECLARE FLAGS BIT (8) ALIGNED; |
Unaligned bit-string variables always occupy only as many bits as are needed to contain them. They need not be on byte boundaries. You can optionally specify the UNALIGNED attribute in a declaration; UNALIGNED is the default for bit strings.
In general, operations involving unaligned bit-string variables are less efficient than those involving aligned bit-string variables. Unaligned bit-string variables are also invalid as the targets of the FROM and INTO options of record I/O statements, and as the argument of the ADDR built-in function. Moreover, most non-PL/I programs that accept bit-string arguments require the strings to be aligned.
In most cases, you should declare bit-string variables with the ALIGNED attribute. Use unaligned bit-string variables when bit strings must be packed as tightly as possible, for example, in arrays and in structures. See Section 2.2.1 for a description of the ALIGNED attribute.
DECLARE BITSTR BIT (10); BITSTR = 1; PUT LIST (BITSTR); |
The output is:
'0001000000'B |
The result may seem incorrect, but it conforms to PL/I's rules for conversion to bit strings. In this case, the fixed-decimal constant 1 is converted to a FIXED BINARY(4) value, which is in turn converted to an intermediate bit string of length 4:
'0001'B |
Next, this intermediate bit string is assigned to the variable BITSTR. Because BITSTR is of length 10, the intermediate bit string is padded on the right with zeros, producing the result as output by PUT LIST. If you now attempt to interpret the value of BITSTR as an integer (for example, by using BITSTR as the argument of the BINARY built-in function), the result would be 64, not 1.
Extra execution time is required to reverse the order of bits when the integer's value is computed. Using arithmetic variables to represent integers is more efficient.
Because of the unexpected results and longer execution time, avoid
using bit strings to represent integers or other data types.
3.5 Pointer Data
A pointer is a variable whose value represents the address in memory of another variable or data item.
Pointers are used to qualify references to based variables, that is, variables for which storage is explicitly allocated at run time by the ALLOCATE statement. For example:
DECLARE LIST_POINTER POINTER; DECLARE 1 LIST_STRUCTURE BASED, 2 FORWARD_PTR POINTER, 2 MEMBER_NAME CHAR(20) VAR; ALLOCATE LIST_STRUCTURE SET (LIST_POINTER); LIST_POINTER -> LIST_STRUCTURE.MEMBER_NAME = 'newname'; |
When these statements are executed, the ALLOCATE statement allocates storage for a variable LIST_STRUCTURE and sets the pointer LIST_POINTER to the address in memory of the allocated storage. This dynamically created variable is called an allocation of the variable LIST_STRUCTURE.
In the assignment statement, the locator qualifier (->) and the identifier LIST_POINTER distinguish this allocation of LIST_STRUCTURE from allocations created by other ALLOCATE statements, if any. Pointers may also be used directly in declarations of based variables. For example:
DECLARE X POINTER, BUFFER CHARACTER(80) BASED (X); |
The variable X is given the POINTER attribute. Then it is used as the target pointer in another declaration, which defines a buffer to be based on X.
This section discusses the following:
Expressions containing pointer variables are restricted to the relational operators equal (=) and not equal (^=).
For example, to test whether a pointer is currently pointing to valid storage, you can write the following statement:
IF LIST_POINTER ^= NULL(:POINTER:) THEN DO; |
The NULL(:POINTER:) enquiry function always returns a null pointer value.
You can use pointer variables in simple assignment statements that assign a pointer value to a pointer variable. For example:
LIST_POINTER_1 = LIST_POINTER_2; LIST_END = NULL(:POINTER:); |
You can also use a pointer variable as the source or target in an
assignment statement involving an offset variable or offset value.
3.5.2 [omitted]
3.6 Offset Data
You declare an offset variable with the OFFSET attribute, optionally followed by an area variable reference. The value of the offset variable will be interpreted as an offset within the specified area, unless the POINTER function is used to explicitly specify another area. You must omit the area reference if the OFFSET attribute is specified within a returns descriptor, parameter declaration, or parameter descriptor. For example:
DECLARE MAP_SPACE AREA (40960), MAP_START OFFSET (MAP_SPACE), MAP_LIST(100) CHARACTER(80) BASED (MAP_START); |
These declarations define an area named MAP_SPACE; an offset variable, MAP_START, that will contain offset values within that area; and a based variable whose storage is located by the value of MAP_START.
Offset variables are given values by assignment from existing offset values or from conversion of pointer values. The OFFSET built-in function (described in Section 11.4.59) converts a pointer value to an offset value.
In assignment 2, any area references are ignored in the assignment; therefore, the offset value and variable can refer to different areas. In assignments 3 and 4, the offset variable must have been declared with an area reference.
Expressions containing offset variables are restricted to the
relational operators = and ^=, for testing the equality or inequality
of two values.
3.7 Label Data
A label identifies a statement so that it can be referred to elsewhere in the program, for example, as the target of a GOTO statement. A label precedes a statement and consists of any valid identifier terminated by a colon. Some examples are:
TARGET: A = A + B; READ_LOOP: READ FILE (TEXT) INTO (TEMP); |
These statements contain the implicit declarations of the names TARGET and READ_LOOP as label constants.
No statement can have more than one label. A statement can, however, be preceded by any number of labeled null statements. For example:
A: ; B: DO I = 1 TO 5; |
Other statements in the program can refer to the DO statement in this example by specifying either label A or label B.
A name occurring as a statement label is implicitly declared as a label constant. It has the attributes LABEL and constant. You cannot explicitly declare label constants.
This section discusses the following:
Any label constant except the label of a PROCEDURE or FORMAT statement can have a single subscript. Subscripts must be specified with integer constants; a subscript must appear in parentheses following the label name. An example is:
PART(1): . . . PART(2): . . . PART(*): |
When labels are written this way, the unscripted label name represents the implicit declaration of a label array constant. In this example, the array is named PART and is treated as if it were declared within the block containing the subscripted labels.
Elements of the array can be referenced in GOTO statements that specify a subscript. For example:
GOTO PART(I); |
Within a single block, you cannot use the same subscript value in two different subscripted references with the same name. For example:
PART(1): |
If a name is used as a label array constant in two or more different blocks, each declaration of the name is treated as an internal declaration. For example:
LIST(2): RETURN; BEGIN; GOTO LIST (ELEMENT); LIST(1):; LIST(3): END; |
In this example, the value of ELEMENT cannot cause control to pass to
the RETURN statement labeled LIST(2) in the containing block. The
subscripted LIST labels in the begin block restrict the scope of the
name to that block.
3.7.2 Label Values
Whenever a reference to a label constant is interpreted, the result is a label value. A label value has two components:
The GOTO statement with a label reference transfers control to the designated statement in the designated block activation. If the target block activation is different from the block activation in which the GOTO statement is executed, then the GOTO is nonlocal. For example:
DECLARE LV LABEL; /* LABEL variable */ . . . LV = L; /* assigns a bound label value to LV */ BEGIN; . . . GOTO LV; /* nonlocal GOTO */ END; L: RETURN; |
Operations on label values are restricted to the operators = and ^= for testing the equality or inequality of two values. Two values are equal if they refer to the same statement in the same block activation.
Any reference to a label value after its block activation ceases to
exist is an error with unpredictable results.
3.7.3 Label Variables
When an identifier is explicitly declared with the LABEL attribute, it acquires the VARIABLE attribute by default. You can use such a variable to denote different label values during the execution of the program. For example:
DECLARE PROCESS LABEL; . . . IF CODE THEN PROCESS = BILLING; ELSE PROCESS = CHARGE; . . . GOTO PROCESS; |
When the GOTO statement evaluates the reference to the label PROCESS, the result is the current value of the variable. The GOTO statement transfers control to either of the labels BILLING or CHARGE, depending on the current value of the Boolean variable CODE.
You can also give values to label variables by passing label values as arguments or by returning a label value as the value of a function (although the latter method can lead to programming errors that are difficult to diagnose). For example:
CALL COMPUTER(ERROR_EXIT, YVAL, XVAL); . . . ERROR_EXIT: |
In this example, the actual argument that is passed for ERROR_EXIT is a dummy argument whose value consists of the following:
Any statement in a PL/I program can be labeled except the following:
Labels on PROCEDURE and FORMAT statements are not considered statement labels and cannot be used as the targets of GOTO statements.
An identifier occurring as a label in a block cannot be declared in
that block (except as a structure member), and cannot occur in a
parameter list of that block.
3.8 Entry Data
Entry constants and variables are used to invoke procedures through specified entry points. An entry value specifies an entry point and a block activation of a procedure.
This section discusses the following:
You declare entry constants by using labels on PROCEDURE statements.
You declare internal entry constants by using labels on PROCEDURE statements whose procedure blocks are nested in another block. You can use an internal entry constant anywhere within its scope to invoke its procedure block.
You declare external entry constants either by using labels on PROCEDURE statements that belong to external procedures, or by explicitly declaring the constant names with the ENTRY attribute. You can use an external entry constant to invoke its procedure block from any program location that is within its scope. Its scope is either the scope of its declaration (as a label) or the scope of a DECLARE statement for the constant.
In DECLARE statements, you declare external entry constants with the
ENTRY attribute. The declaration must agree with the actual entry
point. That is, the declaration of the external entry constant must
contain parameter descriptors for any parameters specified at the entry
point, and, if the entry constant is to be used in a function
reference, the declaration must have a returns descriptor describing
the returned value.
3.8.2 Entry Values
Whenever a reference to an entry constant is interpreted, the result is an entry value. An entry value is the entry point of a procedure, and it serves to activate the block in which the entry point is declared (that is, the block in which the entry point's name appears as the label of a PROCEDURE statement). This block activation is the current block activation if the entry point belongs to the current block. If the entry point belongs to a containing block, the activation is on the chain of parent activations that ends at the current block activation.
No conversions are defined between entry data and other data types. You can assign an entry variable only the value of an entry constant or the value of another entry variable. The only operations that are valid for entry data are comparisons for equality (=) and inequality (^=). Two entry values are equal if they refer to the same entry point in the same block activation.
PL/I supports the passing of external procedures, but not
internal procedures, as entry value parameters. To pass an internal
procedure, use an entry parameter.
3.8.3 Entry Variables
Entry variables are variables (including parameters) that take entry values. If the VARIABLE attribute is specified with the ENTRY attribute in a DECLARE statement, the declared identifier is an entry variable. You can assign to an entry variable either another entry variable or an entry constant.
When an entry variable is used to invoke a procedure, its declaration must agree with the definition of the entry point. If the value you assign to an entry variable specifies an entry point with parameters, the parameters must be described with parameter descriptors in the declaration of the variable. If the assigned value specifies an entry point that is invoked as a function, then the declaration of the entry variable must have a RETURNS attribute that describes the data type of the returned value.
The scope of an entry variable name can be either internal or external. If neither the EXTERNAL nor the INTERNAL attribute is specified with the entry variable, the default is INTERNAL.
You can use the entry variable to represent different entry points during the execution of the PL/I program. For example:
DECLARE E ENTRY VARIABLE, (A,B) ENTRY; E = A; CALL E; |
The entry constant A is assigned to the entry variable E. The CALL statement results in the invocation of the external entry point A.
You can also declare arrays of entry variables. The following example shows an array of external functions:
DECLARE EXTRACT(10) ENTRY (FIXED,FIXED) VARIABLE RETURNS (FLOAT), GETVAL FLOAT; GETVAL = EXTRACT(3)(1,3); |
This assignment statement references the third element of the array EXTRACT. When the statement is executed, this array element must contain a valid entry value.
Exercise caution using static entry variables. The value of a static entry variable is valid only as long as the block in which that value was declared is active. |
DECLARE INFILE FILE; |
This declaration declares a file constant named INFILE whose attributes include EXTERNAL by default.
DECLARE INFILE FILE INTERNAL; |
This declaration specifies that the file constant named INFILE is internal to the block in which it is declared.
If you declare a file constant as EXTERNAL, you must use identical
attributes, including ENVIRONMENT attributes, in all blocks that
declare the constant. Otherwise, PL/I uses the last set of attributes
encountered during compilation and ignores the others.
3.9.2 File Values
Whenever a reference to a file constant is interpreted, the result is a file value. A file value is a pointer to the file control block for the file with which the constant is associated.
PL/I supports the passing of external files, but not internal
files, as file value parameters. To pass an internal file, use a file
parameter.
3.9.3 File Variables
File variables are variables (including parameters) that take file values. If the VARIABLE attribute is specified with the FILE attribute in a DECLARE statement, the declared identifier is a file variable. You can assign to a file variable either another file variable or a file constant.
A file variable is represented internally as a longword that contains a pointer to a file control block. The value of the file variable, when evaluated, is the address of the file control block for the file with which the variable is currently associated.
The scope of a file variable name can be either internal or external. If neither the EXTERNAL nor the INTERNAL attribute is specified with the file variable, the default is external.
If you declare a file variable implicitly or explicitly as EXTERNAL, you must use identical attributes, including ENVIRONMENT attributes, in all blocks that declare the variable. Otherwise, PL/I uses the last set of attributes encountered during compilation and ignores the others.
You can use the file variable to represent different files during the execution of the PL/I program. For example:
DECLARE F FILE VARIABLE, (A,B) FILE; E = A; CALL READFILE(E); |
The file constant A is assigned to the file variable E. The CALL statement results in the invocation of the entry point READFILE with file A as its parameter.
You can also declare arrays of file variables. The following example shows an array of external file variables:
DECLARE FILELIST(10) FILE VARIABLE, MYFILE FILE VARIABLE; MYFILE = FILELIST(3); |
This assignment statement references the third element of the array
FILELIST. When the statement is executed, this array element must
contain a valid file value.
3.10 Area Data
An area is a region of storage in which based variables can be allocated and freed. You define an area by declaring a variable with the AREA attribute. An area variable can belong to any storage class. Areas provide the following programming capabilities:
All areas must be declared with the AREA attribute before they can be referenced in a BASED attribute or an ALLOCATE statement with the IN option. For example:
DECLARE MYAREA AREA; DECLARE PTR OFFSET(MYAREA); DECLARE MYDATA FIXED BIN(31) BASED(PTR); |
The variable MYAREA is given the AREA attribute. Then it is used as the target in another declaration, which defines a pointer offset based on MYAREA. To allocate storage for MYDATA in area MYAREA, use the IN option of the ALLOCATE statement as follows:
ALLOCATE MYDATA IN(MYAREA) SET (PTR); |
When these statements are executed, the ALLOCATE statement allocates storage for a variable MYDATA in the area MYAREA and sets PTR to the offset in the area of the allocated storage.
This section discusses the following:
Expressions containing area variables are restricted to the relational operators equal (=) and not equal (^=) and to comparison to the empty () built-in function (BIF).
For example, to test whether an area is empty, that is, to determine whether it currently has storage allocated in it, you can write the following statement:
IF MYAREA = EMPTY() THEN DO; |
The EMPTY() built-in function always returns an empty area value.
You can use area variables in simple assignment statements that assign one area variable to another. For example:
AREA_1 = AREA_2; AREA_2 = EMPTY(); |
An area can be the source or target of data transmission in READ and
WRITE record I/O statements.
3.11 Condition Data
PL/I provides a CONDITION attribute for declaring programmer-defined conditions. These conditions may only be signaled by the SIGNAL statement.
Condition data occupies a longword (32 bits) of storage.
No conversions are defined between condition data and other data types. The only operations that are valid for condition data are comparisons for equality (=) and inequality (^=).
Unlike some other noncomputational data type (such as FILE), the CONDITION data type may only be used as a constant. You cannot declare condition variables. For example, the following results in a compile-time error:
DECLARE (C1, C2, C3) CONDITION; DECLARE C CONDITION VARIABLE; |
The compiler will reject the declaration of C in the previous example.
An aggregate is a data structure, either an array or structure composed of items as follows:
Arrays provide an orderly way to manipulate related variables of the
same data type. An array variable is defined in terms of the number of
elements that the array contains and the organization of those
elements. These attributes of an array are called its dimensions.
4.1.1 Array Declarations
To declare an array, specify its dimensions in a DECLARE statement using one of the following syntaxes:
DECLARE identifier [DIMENSION] (bound-pair,...) [attribute ...]; |
DECLARE (identifier [DIMENSION] (bound-pair,...)) [attribute ...]; |
To declare two or more array variables that have the same dimensions, bounds, and attributes, use the following syntax:
DECLARE (identifier, ...) [DIMENSION] (bound-pair,...) [attribute ...]; |
identifier
A valid PL/I identifier to be used as the name of the array.bound-pair
A specification of the number of elements in each dimension of the array. A bound pair can consist of one of the following:
- Two expressions separated by a colon giving the lower and upper bounds for that dimension
- A single expression giving the upper bound only (the lower bound is then 1 by default)
- An asterisk (*), used in the declaration of array parameters, indicating that the parameter can be matched to array arguments with varying numbers of elements in that dimension
Bound pairs in series must be separated by commas, and the list of bound pairs must be enclosed in parentheses. The list of bound pairs must immediately follow the identifier or the optional keyword DIMENSION or the list of declarations. The following rules apply to specifying the dimensions of an array and the bounds of a dimension:
- An array can have up to eight dimensions.
- The values you can specify for bounds are restricted as follows:
- If the array has the STATIC attribute, you must specify all bounds as restricted integer expressions. A restricted integer expression is one that yields only integral results and has only integral operands, which can be evaluated at translation time. Such an expression can use only the addition (+), subtraction (-), and multiplication (*) operators.
- If the array has the AUTOMATIC, BASED, or DEFINED attribute, you can specify the bounds as optionally signed integer constants or as expressions that yield integer values at run time. If the array has AUTOMATIC or DEFINED, the expressions must not contain any variables or functions that are declared in the same block, except for parameters.
- The value of the lower bound you specify must be less than the value of the upper bound.
Table 4-1 shows several forms of bound pairs as used in declarations. Note that all the examples in Table 4-1 would be identical in effect if the optional keyword DIMENSION were added.
attribute
One or more data type attributes of the elements of the array. All attributes you specify apply to each of the elements in the array.Elements of an array can have any data type. If the array has the FILE or ENTRY attribute, it must also have the VARIABLE attribute.
Bound Pairs | Examples |
---|---|
ARRAY_NAME (bound)
A single value specifies:
|
DECLARE VERBS (6) CHARACTER (12) ; |
ARRAY_NAME (low-bound:high-bound) | |
A single range of values specifies:
|
DECLARE TEMPERATURES (-60:120) ; |
ARRAY_NAME (bound1,bound2,...)
A list of values specifies:
|
DECLARE TABLE (10,10) FIXED BINARY ; |
ARRAY_NAME (low-bound1:high-bound1,low-bound2,high-bound2,...) | |
A list of ranges specifies:
|
DECLARE WINDOWS (1:10,-2:32) FIXED; |
ARRAY_NAME (*,...)
Asterisk extents specify:
|
ADDIT: PROCEDURE (ARR) ; |
The declaration of an array specifies its dimensions, the bounds of each dimension, and the attributes of the elements.
One bound pair is specified for each dimension of the array to define the number of elements in that dimension. The total number of elements in the array, called its extent, is the product of the number of elements in all the dimensions of the array. If omitted, the lower bound is 1 by default.
You can use an asterisk (*) as the bound pair when you declare arrays as parameters of a procedure; the asterisk indicates that the parameter can accept array arguments with any number of elements. (If one dimension is specified with an asterisk, all must be specified with asterisks.) For example:
DECLARE SALARIES (100) FIXED DECIMAL (7,2); |
The following statement declares a two-dimensional array of 64 integers:
DECLARE GAME_BOARD (8,8) FIXED BINARY (7); |
The following statement declares a one-dimensional array of 12 character strings, each having a length of 2:
DECLARE PM_HOURS(13:24) CHARACTER(2); |
The elements of the previous array is numbered 13 through 24 instead of 1 through 12.
You can replace the identifier in a statement with a list of declarations, which declares several arrays with the same attributes. For example:
DECLARE (SALARIES,PAYMENTS)(100) FIXED DECIMAL(7,2); |
This statement declares SALARIES and another array, PAYMENTS, with the
same dimensions and other attributes.
4.1.2 References to Individual Elements
You refer to an individual element in the array with subscripts. Because an array's attributes are common to all of its elements, a subscripted reference has the same properties as a reference to a scalar variable with those attributes.
You must enclose subscripts in parentheses in a reference to an array element. For example, in a one-dimensional array named ARRAY declared with the bounds (1:10), the elements are numbered 1 through 10 and are referred to as ARRAY(1), ARRAY(2), ARRAY(3), and so on. The lower and upper bounds that you declare for a dimension determine the range of subscripts you can specify for that dimension.
The lower and upper bounds that you declare for a dimension determine the range of subscripts that you can specify for that dimension. The number of elements in any dimension of any array is:
(upper bound) - (lower bound) + 1
For multidimensional arrays, the subscript values represent an element's position with respect to each dimension in the array. Figure 4-1 shows subscripts for elements of one-, two-, and three-dimensional arrays. In subscripted references for multidimensional arrays, the number of subscripts must match the number of dimensions of the array and must be separated by commas.
Figure 4-1 Specifying Elements of an Array
You can specify the subscript of an array element using any variables or expressions having integer values, that is, values that can be expressed as fixed binary or fixed decimal with a zero scale factor. For example:
DECLARE DAYS_IN_MONTH(12) FIXED BINARY; DECLARE (COUNT, TOTAL) FIXED BINARY; TOTAL = 0; DO COUNT = 1 TO 12; TOTAL = TOTAL + DAYS_IN_MONTH(COUNT); END; |
Here, the variable COUNT is used as a control variable in a DO loop. As
the value of COUNT is incremented from 1 to 12, the value of the
corresponding element of the array DAYS_IN_MONTH is added to the value
of the variable TOTAL.
4.1.3 Initializing Arrays
Specify the INITIAL attribute for an array to initialize its values in the declaration. For example:
DECLARE MONTHS (12) CHARACTER (9) VARYING INITIAL ('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'); |
Each element of the array MONTHS is assigned a value according to the order of the character-string constants in the initial list: MONTH(1) is assigned the value 'January'; MONTH(2) is assigned the value 'February'; and so on.
If the array being initialized is multidimensional, the initial values are assigned in row-major order.
To assign identical initial values to some or all elements of an array, you can use an iteration factor with the INITIAL attribute. For example:
DECLARE TEST_AVGS (30,4) FIXED DECIMAL (5,2) STATIC INITIAL ((120) 50); |
This statement declares the array TEST_AVGS with 120 elements, each of which is given an initial value of 50.
You can also use the asterisk (*) iteration factor to initialize all the elements of an array to the same value. For example:
DECLARE TEST_AVGS (30,4) FIXED DECIMAL (5,2) STATIC INITIAL ((*) 50); |
This statement also declares the array TEST_AVGS with 120 elements, each of which is given an initial value of 50.
When more than one successive element of an array is to be assigned the same value with the INITIAL attribute, you can specify an iteration factor. An iteration factor indicates the number of times that a specified value is to be used in the assignment of values to elements of an array. You can specify an iteration factor in one of the following formats:
iteration-factor
An unsigned decimal constant indicating the number of times the specified value is to be used in the assignment of an array element. The iteration factor can be zero.arithmetic-constant
Any arithmetic constant whose data type is valid for conversion to the data type of the array.scalar-reference
A reference to any scalar variable or to the NULL enquiry function.scalar-expression
Any arithmetic or string expression or string constant. The expression or constant must be enclosed in parentheses.*
Symbol used to indicate that the corresponding array element is not to be assigned an initial value.
You can use any of these forms for arrays that have the AUTOMATIC attribute. For arrays with the STATIC attribute, you can use only constants and the NULL enquiry function.
For example, the following declaration of the array SCORES initializes all elements of the array to 1:
DECLARE SCORES (100) FIXED STATIC INITIAL ((100)1); |
The next declaration initializes the first 50 elements to 1 and the last 50 elements to -1:
DECLARE SCORES (100) FIXED STATIC INITIAL ((50)1,(50)-1); |
The following declaration initializes the first 49 elements to 1; the next 2 elements are not initialized; and the next 49 elements are initialized to -1:
DECLARE SCORES (100) FIXED STATIC INITIAL ((49)1,(2)*,(49)-1); |
The declaration in the next example initializes all 10 elements of an array of character strings to the 26-character value in apostrophes. The string constant is enclosed in parentheses; this is required to differentiate between iteration factors and replication factors.
DECLARE ALPHABETS (10) CHARACTER(26) STATIC INITIAL((10)('ABCDEFGHIJKLMNOPQRSTUVWXYZ')); |
DECLARE TESTS (2,2,3); |
If TESTS is specified in a GET statement or in a declaration with the INITIAL attribute, values are assigned to the elements in the following order:
When an array is output with a PUT statement, PL/I uses the same order to output the array elements. For example:
PUT LIST (TESTS); |
This PUT statement outputs the contents of TESTS in the order
previously shown.
4.1.5.1 Using GET and PUT Statements with Array Variables
When you specify an array variable name in the input-target list of a GET LIST or GET EDIT statement, elements of the array are assigned values from the data items in the input stream. For example:
DECLARE VERBS (6) CHARACTER (15) VARYING; GET LIST (VERBS); |
When this GET LIST statement executes, it accepts data from the default input stream. Each input field delimited by blanks, tabs, or commas is considered a separate string. The values of these strings are assigned to elements of the array VERBS in the order VERBS(1), VERBS(2),...VERBS(6). If a multidimensional array appears in an input-target list, input data items are assigned to the array elements in row-major order.
An array can also appear, with similar effects, in the output-source
list of a PUT statement.
4.1.6 Passing Arrays as Arguments
You can pass an array variable as an argument to another procedure. Within the invoked procedure, the corresponding parameter must be declared with the same number of dimensions. The rules for specifying the bounds in a parameter descriptor for an array parameter are as follows:
For example:
DECLARE SCAN ENTRY ((5,5,5) FIXED,(*) FIXED), MATRIX (5,5,5) FIXED, OUTPUT (20) FIXED; CALL SCAN (MATRIX,OUTPUT); |
The procedure SCAN receives two arrays as arguments. The first is a three-dimensional array whose bounds are known. The second is a one-dimensional array whose bounds are not known. The procedure SCAN can declare these parameters as follows:
SCAN: PROCEDURE (IN,OUT); DECLARE IN (*,*,*) FIXED, OUT (*) FIXED; |
An array whose storage is unconnected cannot be passed as an argument.
Arrays are always passed by reference.
4.1.7 Built-In Functions Providing Array Dimension Information
PL/I provides the following built-in functions that return information about the dimensions of an array, defaulting to the first:
For the first dimension of an array X, the relationship of these functions can be expressed as follows:
DIMENSION (X,1) = HBOUND (X,1) - LBOUND (X,1) + 1
The procedure that follows uses the HBOUND and LBOUND built-in functions:
ADDIT: PROCEDURE (X); DECLARE X (*) FIXED BINARY, (COUNT,I) FIXED BINARY; COUNT = 0; DO I = LBOUND (X,1) TO HBOUND(X,1); COUNT = COUNT + 1; X(I) = COUNT; END; RETURN; END; |
This procedure receives a one-dimensional array as a parameter and
initializes the elements of the array with integral values beginning
with 1.
4.2 Structures
A structure is a data aggregate consisting of one or more members. The
members can be scalar data items or aggregrates. Different members can
have different data types. Structures are useful when you want to group
related data items having different data types.
4.2.1 Structure Declarations and Attributes
The declaration of a structure defines its organization and the names of members at each level in the structure. The major structure name is declared as structure level 1; minor members must be declared with level numbers greater than 1. For example:
DECLARE 1 PAYROLL, 2 NAME, 3 LAST CHARACTER(80) VARYING, 3 FIRST CHARACTER(80) VARYING, 2 SALARY FIXED DECIMAL(7,2); |
PAYROLL.NAME.LAST = 'ROOSEVELT'; |
Alternatively, because the last and first names have the same attributes, you can declare the same structure as follows:
DECLARE 1 PAYROLL, 2 NAME, 3 (LAST,FIRST) CHARACTER(80) VARYING, 2 SALARY FIXED DECIMAL(7,2); |
The following additional rules apply to the specification of level numbers:
Attributes for Structure Variables
Within a structure, you can only declare members at the lowest level of each substructure with data-type attributes. Additional rules for specifying attributes for the various components of a structure are as follows:
AUTOMATIC | BASED | INTERNAL |
DEFINED | STATIC | EXTERNAL |
UNION | TYPE |
A union is a variation of a structure in which all immediate members occupy the same storage. The UNION attribute (which must be associated with a level number in a structure declaration) declares a union. All immediate members of the union-that is, all members having a logical level number one higher-occupy the same storage. A reference to one member of a union refers to storage occupied by all members of the union. Therefore, a union provides a convenient way to look at a large entity (such as a character string or a bit mask) as a series of smaller entities (such as component character strings or individual flag bits).
A variable declared with the UNION attribute must be a major or minor structure. All members of a union must have a constant size (see Chapter 2 for format and details).
The following example shows unions:
DECLARE 1 CUSTOMER_INFO, . . . 2 PHONE_DATA UNION, 3 PHONE_NUMBER CHARACTER (13), 3 COMPONENTS, 4 LEFT_PAREN CHARACTER (1), 4 AREA_CODE CHARACTER (3), 4 RIGHT_PAREN CHARACTER (1), 4 EXCHANGE CHARACTER (3), 4 HYPHEN CHARACTER (1), 4 SPECIFIC_NUMBER CHARACTER (4), 2 ADDRESS_DATA, . . . |
The UNION attribute associated with the declaration of PHONE_DATA signifies that PHONE_DATA's immediate members (PHONE_NUMBER and COMPONENTS) occupy the same storage. Any modification of PHONE_NUMBER also modifies one or more members of COMPONENTS; conversely, modification of a member of COMPONENTS also modifies PHONE_NUMBER. Note, however, that the UNION attribute does not apply to the members of COMPONENTS because they are not immediate members of PHONE_DATA. The members of COMPONENTS occupy separate storage in the normal fashion for structure members.
Unions provide capabilities similar to those provided by defined variables. However, the rules governing defined variables are more restrictive than those governing unions. The following example demonstrates a use of a union that would not be possible with a defined variable:
DECLARE 1 X UNION, 2 FLOAT_NUM FLOAT BINARY (24), 2 BREAKDOWN, 3 FRAC_1 BIT (7), 3 EXPONENT BIT (8), 3 SIGN BIT (1), 3 FRAC_2 BIT (16); |
You can initialize a structure by giving the INITIAL attribute to its members. Not all members need be initialized. For example:
DECLARE 1 COUNTS, 2 FIRST FIXED BIN(15) INITIAL(0), 2 SECOND FIXED BIN(15), 2 THIRD (5) FIXED BIN(15) INITIAL (5(1)); |
The first and third members of the structure COUNTS are initialized.
The INITIAL attribute cannot be applied, however, to a major or a minor structure name.
In a union, the same data can only be initialized once.
4.2.4 Using Structure Variables in Expressions
You can specify the name of a major or minor structure in an assignment
statement only if the source expression and the target variable are
identical in size and structure, and all corresponding members have the
same data types.
4.2.5 Passing Structure Variables as Arguments
You can pass a structure variable as an argument to another procedure. The relative structuring of the structure variable specified as the argument and the corresponding parameter must be the same. The level numbers do not have to be identical. The following example shows the parameter descriptor for a structure variable:
DECLARE SEND_REC ENTRY (1, 2 FIXED BINARY(31), 2 CHARACTER(40), 2 PICTURE '999V99'); |
The written argument in the invocation of the external procedure SEND_REC must have the same structure, and its corresponding members must have the same data types.
When structures are passed as arguments, they must match the
corresponding parameters. They cannot be passed by dummy argument.
4.2.6 Using the REFER Option
Use the REFER option to create self-defining based structures. In a based structure, the value of the last member is used to determine the size of the storage space allocated for another member of the same structure. You can use the REFER option in a DECLARE statement to specify array bounds, the length of a string, or the size of an area. The format of the REFER option is as follows:
refer-element REFER (refer-object-reference) |
refer-element
An expression that represents the value assigned to the refer object when the structure is allocated. The refer element must satisfy the following conditions:
- It must be an expression that produces a FIXED BINARY value or a value that can be converted to FIXED BINARY.
- It cannot reference storage in the structure containing the refer element.
refer-object-reference
A reference to a scalar variable. The refer object reference must satisfy the following conditions:
- It cannot be a subscripted variable reference.
- It cannot be locator qualified.
- It must reference a refer object that is a previous member of the structure containing the REFER option.
The refer object is a scalar variable contained by the structure. The refer object must satisfy the following conditions:
An example of a structure declaration containing the REFER option is as follows:
DECLARE 1 STRUCTURE S BASED(P), 2 I FIXED BINARY(31), 2 A CHARACTER(20 REFER(I)); |
For the compiler to allocate storage for a based structure, the structure must have a known size. In the example, the initial length for A is taken from the refer element, 20. However, the REFER option permits the size of the structure to change at run time as the value of the refer object (I) changes. After allocation, the length of A is determined by I.
You can have only one REFER option within a structure, and it must appear at the end of the structure.
The following example and figures show storage mapping with the REFER option.
DECLARE 1 S BASED (POINTER), 2 I FIXED BINARY(15), 2 J FIXED BINARY(15), 2 A CHARACTER ((X*2+2) REFER(I)), 2 B(2) CHARACTER (Y REFER(J)); X = 5; Y = 10; ALLOCATE S; S.A = 'ABCDEFGHIJKL'; S.B(1) = '0123456789'; S.B(2) = 'NOW IS THE'; . . . END; |
When this structure is allocated, the refer elements (X*2+2) and Y are evaluated and used to determine the length of the associated string. The evaluated refer element value (X*2+2) is assigned to the refer object I and Y is assigned to J. Thereafter, the sizes of strings A and B are determined by the value of the refer objects I and J.
Storage for the previous structure is shown in Figure 4-2.
Figure 4-2 Storage of Structure with REFER Option
If the refer object I is assigned the value 6 and the refer object J is assigned the value 4, the resulting storage is remapped as shown in Figure 4-3.
Figure 4-3 Remapped Storage of Structure with REFER Option
PL/I does not restrict the use of the REFER option within structure declarations: therefore, exercise caution in its use. |
If you change a value that causes the size of one or more structure members to decrease, then some storage at the end of the allocated storage will become inaccessible for future reference.
If the scalar variable (the refer object) does not satisfy the following criteria, the results are undefined:
To refer to a structure in a program, you use the major structure name, minor structure names, and individual member names. Member names need not be unique even within the same structure. To refer to the name of a member or minor structure, you must ensure only that the reference uniquely identifies it. You can qualify the variable name by preceding it with the name or names of higher-level (lower-numbered) variables in the structure; names in this format, called a qualified reference, must be separated by periods (.).
The following sample structure definition shows the rules for identifying names of variables within structures:
DECLARE 1 STATE, 2 NAME CHARACTER (20), 2 POPULATION FIXED (10), 2 CAPITAL, 3 NAME CHARACTER (30), 3 POPULATION FIXED (10,0), 2 SYMBOLS, 3 FLOWER CHARACTER (20), 3 BIRD CHARACTER (20); |
The rules for selecting and specifying variable names for structures are as follows:
If a name is ambiguous, the compiler cannot resolve the reference and issues a message. In the example, the names POPULATION and NAME are ambiguous.
You can specify the name of a major or minor structure in an assignment
statement only if the source expression and the target variable are
identical in size and structure, and all corresponding members have the
same data types.
4.3 Arrays of Structures
An array of structures is an array whose elements are structures. Each structure has identical logical levels, minor structure names, and member names and attributes. For example, a structure STATE can be declared an array:
DECLARE 1 STATE (50), 2 NAME CHARACTER (20) VARYING, 2 POPULATION FIXED (31), 2 CAPITAL, 3 NAME CHARACTER (30) VARYING, 3 POPULATION FIXED (31), 2 SYMBOLS, 3 FLOWER CHARACTER (20), 3 BIRD CHARACTER (20); |
A member of a structure that is an array inherits the dimensions of the structure. For example, the member CAPITAL.NAME of the structure STATE inherits the dimension 50. You must use a subscript whenever you refer to the variable CAPITAL.NAME, as in the following example:
PUT LIST (CAPITAL.NAME(I)) ; |
A subscript for a member of a structure that is an array element can appear following any name within a qualified reference. For example, all of these references are equivalent:
STATE(10).CAPITAL.NAME STATE.CAPITAL(10).NAME STATE.CAPITAL.NAME(10) |
A structure that is defined with a dimension can have members that are arrays. For example:
DECLARE 1 STATE (50), 2 AVERAGE_TEMPS(12) FIXED DECIMAL (5,2), . . . |
In this example, the elements of the array STATE are structures. At the second level of the hierarchy of each structure, AVERAGE_TEMPS is an array of 12 elements. Because AVERAGE_TEMPS inherits the dimension of STATE, any of AVERAGE_TEMPS's elements must be referred to by two subscripts:
These subscripts can appear following any name in the qualified reference. For example:
STATE(3).AVERAGE_TEMPS(4) STATE.AVERAGE_TEMPS(3,4) |
These references are equivalent.
Note the following rules for specifying subscripts for members of structures containing arrays:
A connected array is one whose elements occupy consecutive locations in storage. For example:
DECLARE NEWSPAPERS (10) CHARACTER (30); |
In storage, the 10 elements of the array NEWSPAPERS occupy 10 consecutive 30-byte units. Thus, NEWSPAPERS is a connected array.
A connected array is valid as the target of an assignment statement, as long as the source expression is a similarly dimensioned array or a single scalar value. The top diagram in Figure 4-4 shows the storage of a connected array.
In an unconnected array, the elements do not occupy consecutive storage locations. The bottom diagram in Figure 4-4 shows the storage of an unconnected array. An unconnected array is not valid in an assignment statement or as the source or target of a record I/O statement. A structure with the dimension attribute always results in unconnected arrays. When a structure is dimensioned, each member of the structure inherits the dimensions of the structure and becomes, in effect, an array. For example:
DECLARE 1 STATE (50), 2 NAME CHARACTER (20) VARYING, 2 POPULATION FIXED (31); |
In this example, the members NAME and POPULATION of the major structure STATE inherit the dimension 50 from the major structure. When PL/I allocates storage for a structure or a dimensioned structure, each member is allocated consecutive storage locations; thus, the elements of the arrays NAME and POPULATION are not connected.
Figure 4-4 Connected and Unconnected Arrays
The storage class to which a variable belongs determines whether PL/I allocates its storage at compile time or dynamically at run time. This chapter describes the following classes of variables:
Section 5.7 describes the mechanisms for dynamically allocating storage. Section 5.9 describes how variables can share physical storage locations.
The default storage-class attribute for PL/I variables is AUTOMATIC. PL/I does not allocate storage for an automatic variable until the block that declares it is activated. When the block is deactivated the storage is released. For example:
CALC: BEGIN; DECLARE TEMP FIXED BINARY (31); . . . END; |
Each time the block labeled CALC is activated, storage is allocated for the variable TEMP. When the END statement is executed, the block is deactivated, and all storage for TEMP and all other automatic variables is released. The value of TEMP becomes undefined.
The storage requirements of an automatic variable are evaluated each time the block is activated. Thus, you can specify the length of an automatic character-string variable as follows:
DECLARE STRING_LENGTH FIXED; . . . COPY: BEGIN; DECLARE TEXT CHARACTER(STRING_LENGTH); |
When this begin block is activated, the length of TEXT is evaluated.
The variable is allocated storage depending on the value of
STRING_LENGTH, which must have a valid value.
5.2 Static Variables
A static variable is allocated storage when the program is activated, and it exists for the duration of the program. A variable has the static attribute if you declare it with either of the attributes STATIC or EXTERNAL. In declaring static arrays and strings, you must use restricted expressions. (Note that the EXTERNAL scope attribute implies static storage for variables.)
If a block that declares a static variable is entered more than once during the execution of the program, the value of the static variable remains valid. For example:
UNIQUE_ID: PROCEDURE RETURNS (FIXED BINARY(31)); DECLARE ID STATIC INTERNAL FIXED INITIAL (0); ID = ID + 1; /* Increment ID */ RETURN (ID); END; |
The function UNIQUE_ID declares the variable ID with the STATIC attribute and specifies an initial value of 0 for it. The variable is initialized to this value when the program is activated. The storage for the variable is preserved, and the function returns a different integer value each time it is referenced.
A variable with the STATIC attribute can also have external scope; that
is, its definition and value can be accessed by
any other procedure that declares it with the STATIC and EXTERNAL
attributes.
5.3 Internal Variables
An internal variable is known only within the block in which it is
defined and within all contained blocks. By default, PL/I gives all
variables the INTERNAL attribute with the exception of data with the
FILE and CONDITION attributes.
5.4 External Variables
An external variable provides a way for external procedures to share common data. All declarations that refer to an external variable must also declare it with the EXTERNAL attribute (or with an attribute that implies EXTERNAL) and with identical data type attributes. You can abbreviate the EXTERNAL keyword to EXT. The following example and Figure 5-1 shows how procedures can use external variables:
APPLIC: PROCEDURE OPTIONS (MAIN); DECLARE FLAGS BIT (64) ALIGNED EXTERNAL; . . . CALL READY; READY: PROCEDURE; DECLARE FLAGS BIT (64) ALIGNED EXTERNAL; |
Figure 5-1 External Variables
The EXTERNAL attribute is implied by the FILE and CONDITION attributes, and also by declarations of entry constants (that is, declarations that contain the ENTRY attribute but not the VARIABLE attribute). For variables, the EXTERNAL attribute implies the STATIC attribute.
The following rules apply to the use of external names:
DECLARE BUFFER CHARACTER(80) BASED (BUF_PTR), LINE CHARACTER(80), BUF_PTR POINTER; BUF_PTR = ADDR(LINE); |
The declaration of the variable BUFFER does not allocate any storage for it. Rather, PL/I associates the declaration of the variable with the pointer variable BUF_PTR. During the execution of the program, the value of the pointer variable is set to the location (address) in storage of the variable LINE by means of the ADDR built-in function. This effectively associates the description of BUFFER with the actual data value of LINE.
You can associate a based variable with a storage location by using the ADDR built-in function, as in the preceding example; by using the ALLOCATE statement; by using a locator-qualified reference to the based variable; by using the SET option of the READ statement; or by explicit allocation within an area.
The following sections cover these topics:
The data types most commonly associated with based variables are pointers, areas, and offsets.
A pointer is a variable whose value represents the location in memory of another variable or data item. Pointers are used to access based variables and buffers allocated by the system as a result of the SET option of the READ and ALLOCATE statements.
Areas are regions of storage in which based variables can be allocated and freed. The use of areas can simplify and speed operations involving large or numerous based variables.
An offset is a value indicating the location of a based variable
relative to the beginning of an area.
5.5.2 Allocation in Areas
PL/I supports storage management in areas (see Section 3.10 for a description of area data). If you use the ALLOCATE statement with an area (either implied or explicitly specified), you can cause the allocation of storage to be performed in that area, instead of in the general memory pool for based storage.
Storage management in areas has a number of uses, including the following:
You allocate storage in areas with the IN and SET options on the ALLOCATE statement, the AREA and OFFSET data attributes, and the EMPTY built-in function.
The IN option on the ALLOCATE statement takes a reference to an area. If the IN option is not specified, the area is implied from the SET option if the SET option specifies an offset variable with a base area. The SET option itself can be implied from the base variable. Whether an area is specified explicitly or implied, the allocation is performed in that area instead of in the available memory pool for based storage. If an error is detected in the process, the AREA condition is raised.
PL/I provides full support for allocation in areas as specified in the ANSI full PL/I language standard. In addition, it provides extensions to full PL/I, which enhance the usefulness of areas or provide for improved compatibility with other implementations of PL/I. These extensions are as follows:
A = EMPTY(); |
DECLARE A AREA(100) STATIC INITIAL(EMPTY()); |
Note that the last three items in this list are features that differ in some other implementations of PL/I.
For examples showing allocation in areas, see Section 5.5.7.
5.5.3 Referring to Based Variables
A reference to a based variable (except in an ALLOCATE statement) must specify a pointer or offset reference designating the storage to be accessed. This qualifying pointer or offset reference can be implicit, if it is specified with the BASED attribute, or explicit, if the based variable reference is prefixed with a locator qualifier. A complete based variable reference (with the locator qualifier) has the following form:
qualifying-reference -> based-reference |
Whether explicit or implicit, the qualifying reference must be to a pointer variable, a pointer-valued function, or an offset variable declared with a base area. The qualifying reference is evaluated each time the complete reference is evaluated and must yield a valid pointer value. If the qualifying reference is to an offset variable, the offset value is converted to a pointer using the base area specified in the offset variable's declaration.
You can use both implicit and explicit qualifications with the same based variable; the explicit qualifier overrides the implicit one. For example:
DECLARE X FIXED BIN BASED(P), P POINTER, (A,B) FIXED BIN; P = ADDR(A); X = ADDR(B)->X; |
In the second assignment statement, the reference to X on the left-hand side of the assignment has the implicit qualifier P, which is the address of the variable A. The reference to X on the right-hand side is explicitly qualified with the address of another variable, B. This assigns the value of B to the variable A.
In PL/I, you can obtain a valid pointer value in any of the following ways:
A pointer value is valid only as long as the storage to which it applies remains allocated. Moreover, a pointer obtained by the application of ADDR to a parameter or an automatic variable is valid only as long as the parameter's procedure invocation exists, even though the storage pointed to may exist longer.
The NULL enquiry function returns a null pointer value that can be
assigned
to pointer and offset variables, but that is not valid for qualifying a
based variable reference.
5.5.4 Based Variables and Dynamic Storage Allocation
These subsections discuss the dynamic allocation of storage by the ALLOCATE statement and the READ SET statement.
Each time it is executed, the ALLOCATE statement allocates storage for a based variable and, optionally, sets a pointer or offset variable to the location of the storage in memory. The storage allocated can also be assigned values if the variable is declared with the INITIAL attribute. For example:
DECLARE LIST (10) FIXED BINARY BASED, (1) (LIST_PTR_A, LIST_PTR_B) POINTER; ALLOCATE LIST SET (LIST_PTR_A); (2) ALLOCATE LIST SET (LIST_PTR_B); (3) LIST_PTR_A -> LIST(1) = 10; (4) LIST_PTR_B -> LIST(1) = 15; |
The numbered items in this example are shown in Figure 5-2.
As you can see in this example, the array LIST is declared with the BASED attribute; however, the declaration does not reserve storage for this variable. Instead, the ALLOCATE statements allocate storage for the variable and set the pointers LIST_PTR_A and LIST_PTR_B to the storage locations. LIST_PTR_A and LIST_PTR_B must both be declared with the POINTER attribute.
In references, the different allocations of LIST can then be distinguished (unless the pointers are assigned new values) by locator qualifiers that identify the specific allocation of LIST.
The phrase LIST_PTR_A-> is a locator qualifier; it specifies the pointer that locates an allocation of storage for the variable. In this example, the first element of the storage pointed to by LIST_PTR_A is assigned the value 10. The first element of the storage pointed to by LIST_PTR_B is assigned the value 15.
Figure 5-2 Using the ALLOCATE Statement
Any extent expressions in the based variable declaration are evaluated each time the variable is allocated or referenced. Therefore, based variables can be used for data aggregates whose size depends on input data. Here is an example of dynamically allocating a matrix that will be accessed by several external procedures:
DECLARE 1 MATRIX_CONTROL_BLOCK STATIC EXTERNAL, 2 MATRIX_POINTER POINTER, 2 (ROW_SIZE,COL_SIZE) FIXED BINARY; DECLARE 1 MATRIX(ROW_SIZE,COL_SIZE) BASED(MATRIX_POINTER); GET LIST(ROW_SIZE,COL_SIZE); ALLOCATE MATRIX; |
The SET and SIZETO Options of the READ Statement
When you use the READ statement with a based variable, you do not have to define storage areas within your program to buffer records for I/O operations. If you specify the SET option on the READ statement, the READ statement places an input record in a system buffer and sets a pointer variable to the location of this buffer. Furthermore, if you specify the SIZETO option, the READ statement places the length of the input record in an arithmetic variable. (The two options may be used independently of each other.) For example:
DECLARE REC_PTR POINTER, REC_SIZE FIXED BINARY, NEW_BALANCE FIXED DECIMAL (6,2), INFILE FILE RECORD INPUT SEQUENTIAL; DECLARE 1 RECORD_LAYOUT BASED (REC_PTR), 2 NAME CHARACTER (15), 2 AMOUNT PICTURE '999V99', 2 BALANCE FIXED DECIMAL (6,2); . . . READ FILE (INFILE) SET (REC_PTR) SIZETO(REC_SIZE); . . . REC_PTR->BALANCE = NEW_BALANCE; REWRITE FILE (INFILE); |
In this example, the structure defined to describe the records in a file is declared with the BASED attribute; the declaration does not reserve storage for this structure. When the READ statement is executed, the record is read into a system buffer and the pointer REC_PTR is set to its location and the variable REC_SIZE to its length.
When you use the SET option with the READ statement, a subsequent REWRITE statement need not specify the record to be rewritten. PL/I rewrites the record indicated by the pointer variable specified in the READ statement.
Figure 5-3 shows this example.
Figure 5-3 Using the READ Statement with a Based Variable
The ADDR built-in function returns the storage location of a variable. You can use it to associate the storage occupied by a variable with the description of a based variable. For example:
DECLARE A FIXED BINARY BASED (X), B FIXED BINARY, X POINTER; X = ADDR (B); A = 15; |
In this example, the variable A is declared as a based variable, with X designated as its pointer. The variable B is an automatic variable; PL/I allocates storage for B when the block is activated. When the ADDR built-in function is referenced, it returns the location in storage of the variable B, and the assignment statement gives this value to the pointer X. This assignment associates the variable A with the storage occupied by B. Because A is based on X and X points to B, an assignment statement that gives a value to A actually modifies the storage occupied by the variable B. Figure 5-4 shows this example.
Figure 5-4 Using the ADDR Built-In Function
In most applications, the data type of a based variable reference is
identical to the data type under which the accessed storage is
allocated. However, it is not required that the data types be
identical. The following sections discuss type-matching criteria in
more detail.
5.5.6.1 Matching by Overlay Defining
Matching by overlay defining is in effect if the based variable reference and the variable for which the storage was originally allocated are both suitable for character-string or bit-string overlay defining. The only further restriction is that the size n (in characters or bits) of the based variable reference must be less than or equal to the size in characters or bits of the original variable. The based variable reference accesses the first n characters or bits of the storage.
The first program in Section 5.5.7 contains an example of this type of
matching. The structure members PAY_RECORD.GROSS_PAY (a character
string) and HEALTH_RECORD.EXAM DATE (a picture) are not identical data
types. However, both are stored as a character string of length 9;
therefore, they meet the criteria for string overlay defining and for
data-type matching.
5.5.6.2 Matching by Left-to-Right Equivalence
Matching by left-to-right equivalence applies to structured variables that are identical up to a certain point. To see if this applies, examine the declaration of the based variable, and consider only the portion on the left that includes the referenced member and all of the level-2 substructure containing the referenced member (if the member is not itself at level 2). If the original variable's declaration has a similar left part with identical data type, then the based variable reference and the original reference match. For example:
DECLARE 1 S1 BASED (P), 2 X, 3 (A,B) FIXED BIN, 2 Y, 3 C CHAR(10), 3 D(5) FLOAT; DECLARE 1 S2 BASED(P), 2 X, 3 (A,B) FIXED BIN, 2 Y, 3 C CHAR(10), 3 E BIT(32); ALLOCATE S1; S2.A = 3; /* valid l-to-r match */ S2.C = 'X'; /* INVALID */ |
In the first assignment, S2.A is a valid reference because S1 and S2 match through the level-2 structure X. In the second assignment, S2.C is invalid in standard PL/I because the level-2 structures S2.Y and S1.Y do not match. However, the reference to S2.C does work.)
This sort of matching is useful in connection with data structures and files, where the first part of a record contains a value indicating the precise structure of the remainder of the record.
Note that the UNION attribute allows this type of declaration to be
written more easily.
5.5.6.3 Nonmatching Based Variable References
In PL/I, a based variable reference need not match the variable for
which the storage was originally allocated. The only requirement is
that the size of the based variable in bits be less than or equal to
the size of the original variable in bits. However, use of such
nonmatching references requires knowledge of the internal
representation of data. You should not expect the resulting code to be
transportable between implementations.
5.5.7 Examples of Based Variables
The program DEFINED uses based variables and the READ SET statement to process a file of personnel data (PERSONNEL.DAT). The file has two types of valid records, a pay record and a health record, which are identified by a 1-character code in the first position. The two record types are declared as based structures (PAY_RECORD and HEALTH_RECORD), one of which is selected based on the record type character ('P' for pay, 'E' for health). Any record that does not begin with one of these characters is invalid and is written out as a reference to the based character variable INVALID_RECORD.
DEFINED: PROCEDURE OPTIONS(MAIN); DECLARE P POINTER; /* pointer to structures */ DECLARE 1 PAY_RECORD BASED(P), 2 RECORD_TYPE CHARACTER(1), 2 NAME CHARACTER(20), /* the two structures differ in this member: */ 2 GROSS_PAY PICTURE '999999V.99'; DECLARE 1 HEALTH_RECORD BASED(P), 2 RECORD_TYPE CHARACTER(1), 2 NAME CHARACTER(20), 2 EXAM_DATE CHARACTER(9); DECLARE INVALID_RECORD CHARACTER(30) BASED(P); DECLARE PERSONNEL RECORD FILE; DECLARE PERSOUT STREAM OUTPUT PRINT FILE; /* used to control DO group: */ ON ENDFILE(PERSONNEL) BEGIN; PUT FILE(PERSOUT) SKIP LIST ('All processing complete.'); STOP; /* program stops here */ END; OPEN FILE(PERSONNEL) INPUT TITLE('PERSONNEL.DAT'); DO WHILE(1'B'); /* terminated by ENDFILE ON-unit */ READ FILE(PERSONNEL) SET(P); /* P is the location of the record acquired by the READ statement */ IF P->PAY_RECORD.RECORD_TYPE = 'P' THEN PUT FILE(PERSOUT) SKIP LIST ('Name=',P->PAY_RECORD.NAME, 'Gross pay=',P->GROSS_PAY); ELSE /* either a health record or an invalid record */ DO; IF P->HEALTH_RECORD.RECORD_TYPE = 'E' THEN PUT FILE(PERSOUT) SKIP LIST ('Name=',P->HEALTH_RECORD.NAME, 'Exam date:',P->EXAM_DATE); ELSE /* invalid record type */ PUT FILE(PERSOUT) SKIP LIST ('Invalid record:',P->INVALID_RECORD); END; END; /* repeat DO group until ENDFILE is signaled */ END DEFINED; |
For example, assume that the file PERSONNEL.DAT contains these records:
PMary A. Ford 125000.55 EMary A. Ford 22July 80 t12345678901234567890pppppp.pp |
Name= Mary A. Ford Gross pay= 125000.55 Name= Mary A. Ford Exam date: 22July 80 Invalid record: t12345678901234567890pppppp.pp All processing complete. |
Notice these other features of the program:
The UNION attribute can be used to declare a single record with a variant portion in place of PAY_RECORD and HEALTH_RECORD. For example:
1 RECORD BASED(P), 2 RECORD_TYPE CHARACTER(1), 2 NAME CHARACTER(20), 2 VARIANS UNION, 3 GROSS_PAY PICTURE '999999V.99', 3 EXAM_DATE CHARACTER(9) |
Note that the UNION attribute is not available in many other PL/I implemenations.
variable-reference [SET(locator-reference)] [IN(area-reference)] |
DECLARE STATE CHARACTER(100) BASED (STATE_POINTER), STATE_POINTER POINTER; ALLOCATE STATE; |
This ALLOCATE statement allocates storage for the variable STATE and sets the pointer STATE_POINTER to the location of the allocated storage.
The ALLOCATE statement obtains the amount of storage needed to accommodate the current extent of the specified variable. If, for example, a character-string variable is declared with an expression for its length, the ALLOCATE statement evaluates the current value of the expression to determine the amount of storage to be allocated. For example:
DECLARE BUFFER CHARACTER (BUFLEN) BASED, BUF_PTR POINTER; . . . BUFLEN = 80; ALLOCATE BUFFER SET (BUF_PTR); |
Here, the value of BUFLEN is evaluated when the ALLOCATE statement is executed. The ALLOCATE statement allocates 80 bytes of storage for the variable BUFFER and sets the pointer variable BUF_PTR to its location.
If the variable being allocated has been declared with initial values,
these values are assigned to the variable after allocation.
5.7.2 FREE Statement
The FREE statement releases the storage that was allocated for a based variable. The format of the FREE statement is as follows:
FREE free-item[,free-item ...]; |
free-item
The syntax of the free-item is:
variable-reference [IN(area-reference)]
variable-reference
A reference to the based variable whose storage is to be released.If you do not explicitly free the storage acquired by the variable, the storage is not freed until the program terminates.
If you free a variable that is explicitly associated with a pointer, the pointer variable becomes invalid and must not be used to reference storage. You can only free a variable once for each allocation.
IN(area-reference)
The specification of an area reference (for based variables) in which the storage is to be freed. If the IN option is omitted, the variable reference must be either implicitly or explicitly based on an offset variable with a base area.
FREE LIST; FREE P->INREC; |
These statements release the storage acquired for the based variable LIST and for the allocation of INREC pointed to by the pointer P.
ALLOCATE STATE SET (STATE_PTR); . . . FREE STATE; |
This FREE statement releases the storage for the based variable STATE
and makes the value of STATE_PTR undefined.
5.7.3 Other Mechanisms for Dynamic Storage Allocation
PL/I has a variety of dynamic storage management mechanisms available besides those for based variables. You can also use explicitly specified calls to system memory management.
These storage control mechanisms are generally similar in the amount of overhead that they require both in execution time and in storage space, although certain mechanisms have characteristics that make them useful in specific circumstances.
In general, the standard PL/I language manipulation of dynamic memory
provides reasonable performance with some built-in checking.
5.8 Defined Variables
The DEFINED attribute indicates that PL/I is not to allocate storage for the variable, but is to map the description of the variable onto the storage of another variable called the base variable. The DEFINED attribute provides a way to access the same data using different names (see Section 2.2.13 for a description of the DEFINED attribute).
In a declaration of a defined variable, the DEFINED keyword, which you can abbreviate to DEF, is followed by a variable reference (which must not have the BASED or DEFINED attribute).
When you use the DEFINED attribute in the declaration of a variable, PL/I associates the description of the variable in the declaration with the storage allocated for the variable on which the declaration is defined. For example:
DECLARE NAMES(10) CHARACTER(5) DEFINED (LIST), LIST(10) CHARACTER(5); |
In this example, the variable NAMES is a defined variable; its data description is mapped to the storage occupied by the variable LIST. Any reference to NAMES or to LIST is resolved to the same location in memory.
The DO group assigns I to A(I) for I = 1,2,...10.
The base reference of a defined variable cannot be a reference to a based variable or to another defined variable. A defined variable and its base reference must satisfy one of the following criteria:
In brief, a variable is suitable for overlay defining if it consists entirely of characters or bits, and those characters or bits are packed into adjacent storage without gaps. Such a variable can be treated as a string or interpreted as different types of aggregates. For example:
DECLARE A (10) CHARACTER (5); DECLARE B (5) CHARACTER (10) DEFINED (A); A (1) = 'AAAAA'; A (2) = 'BBBBB'; PUT LIST (B(1)); |
Figure 5-5 shows a 50-byte region of storage treated either as a 10-element array (A) of 5-character strings or as a 5-element array (B) of 10-character strings.
Figure 5-5 An Overlay Defined Variable
If the defined variable and its base reference have identical data types, a reference to the defined variable is equivalent to the base reference. In the case of overlay defining, the defined variable maps onto part of the base reference's storage as follows:
A variable V is suitable for character-string overlay defining if V is not an unconnected array and if one of the following criteria is satisfied:
A variable V is suitable for bit-string overlay defining if V is not an unconnected array and if one of the following criteria is satisfied:
Variables that have any of the attributes BASED, DEFINED, UNION, or PARAMETER can share physical storage locations with one or more other variables.
A based variable is not allocated any storage when it is declared. Instead, storage is either located by a locator-qualified reference to the variable or allocated by the ALLOCATE statement. The BASED attribute then allows you to describe the characteristics of a variable, which can then be located by a reference that qualifies the variable's name with any valid pointer value. Based variables are useful when the program must control the allocation of storage for several variables with identical attributes. The creation and processing of a queued or linked list is a common case. For full details on based variables and valid pointer values, see Section 5.5.
A defined variable uses the storage of a previously declared variable, which is referenced in the DEFINED attribute. The referenced variable is known as the base of the defined variable. The base can be a character- or bit-string variable, suitable for a technique called string overlay defining. Defined variables are useful when the program must refer to the same storage by different names. For full details, see Section 5.8
Unions provide capabilities similar to those of defined variables, but the rules governing unions are less restrictive. A union is a variation of a structure in which all immediate members occupy the same storage.
The UNION attribute, which is used only in conjunction with a level number in a structure declaration, signifies that all immediate members of the major or minor designated structure occupy the same storage. Immediate members are those members having a level number one higher than the major or minor structure with the union attribute. For more details, see Section 4.2.2
Parameters of a procedure share storage with their associated arguments. The associated argument is either a variable written in the argument list or a dummy variable allocated by the compiler. When the written argument is a variable, the sharing of storage by the parameter and argument allows a procedure to return values to the invoking procedure by changing the value of the parameter. For instance, a function can return values in this manner in addition to returning the value specified in its RETURN statement. For more information, see Section 7.5.
target,...= expression; |
target
A reference to a variable to be assigned the expression's value. If there are two or more targets, they are separated by commas. A target can be:
- A reference to a scalar variable or scalar array element
- A reference to a pseudo-variable (for example, SUBSTR)
- A reference to a major or minor structure name or any member of a structure
- A reference to an array variable
expression
Any valid expression.
PL/I evaluates the targets and the expression in any order. Thus, a program should not depend on the evaluation of the targets before the expression.
PL/I performs the following steps for assignment. Note that the only certain things about the order of steps performed are that step 1 precedes step 3 and that step 4 is performed last.
Some general rules regarding the types of data you can specify in assignment statements are listed in Table 6-1. For the complete rules for data conversion in assignments, see Section 6.4.
Data Type | Rules |
---|---|
Area | Only the current extent of an area is moved from the source area to a target. If the target area is not large enough to hold the extent, the AREA condition is raised. Note that the assignment is performed in such a way that all offsets in the source area are valid in the target area after the assignment. Areas cannot be assigned as members of structures. |
Arithmetic |
PL/I converts an arithmetic expression to the type of its target if
their types are different. If the target is a character- or bit-string
variable, PL/I converts the arithmetic expression to its character- or
bit-string equivalent.
A character-string expression can be converted to the data type of an arithmetic target only if the string consists solely of characters that have numeric equivalents. |
Arrays |
You can specify an array variable as the target of an assignment
statement in only the following ways:
Any array variable specified in an assignment statement must occupy connected storage. All other specifications of an array variable as a target of an assignment statement are invalid. |
Bit | When a target of an assignment is a bit-string variable, the resulting expression is truncated or padded with trailing zeros to match the length of the target. |
Character |
When a target of an assignment is a fixed-length character string, the
resulting expression is truncated on the right or padded with trailing
spaces to match the length of the target. If a target is a
varying-length character string, the resulting expression is truncated
on the right if it exceeds the maximum length of the target.
When one character-string variable is assigned to another, the storage occupied by the two variables cannot overlap. |
Entry | If the specified expression is an entry constant, an entry variable, or a function reference that returns an entry value, the target variable must be an entry variable. |
Label | If the specified expression is a label constant, a label variable, or a function reference that returns a label value, the target variable must be a label variable. |
Pointer and Offset | If the specified expression is a pointer or offset, or a function reference that returns a pointer or offset, the target variable must be a pointer or offset variable. |
Structures |
You can specify the name of a major or minor structure as a target of
an assignment statement only if the source expression is an identical
structure with members in the same hierarchy and with identical sizes
and data type attributes. The storage occupied by the two structures
must not overlap.
Any structure variable specified in an assignment statement must occupy connected storage. |
The following are examples of assignment statements:
A = 1; A = B + A; SUM = A + 3; STRING = 'word'; |
An operator is a symbol that requests a unique operation. Operands are the expressions on which operations are performed. Built-in functions can also be considered operators, as well as their arguments considered operands.
A prefix operator precedes a single operand. The prefix operators are the unary plus (+), the unary minus (-), and the logical NOT (^).
The following are examples of expressions containing prefix operators:
A = +55; B = -88; BITC = ^BITB; |
An infix operator appears between two operands, and indicates the operation to be performed on them. PL/I has infix operators for arithmetic, logical, and relational (comparison) operations, and for string concatenations. Following are some examples of expressions containing infix operators:
RESULT = A / B; IF NAME = FIRST_NAME || LAST_NAME THEN GOTO NAMEOK; |
An expression can contain both prefix and infix operators. For example:
A = -55 * +88; |
You can apply prefix and infix operators to expressions by using parentheses for grouping.
For a table giving the categories of operators and the operator symbols, see Chapter 1.
Because all operators must yield scalar values, operands cannot be arrays or structures. The data type that you can use for an operand in a specific operation depends on the operator:
The arithmetic operators perform calculations. Programs that accept numeric input and produce numeric output use arithmetic operators to construct expressions that perform the required calculations. The infix arithmetic operators are:
Operator | Operation |
---|---|
+ | Addition |
- | Subtraction |
* | Multiplication |
/ | Division |
** | Exponentiation |
In addition, there are two prefix operators: unary plus (+) and unary minus (-). The unary plus is valid on any arithmetic operand, but it performs no actual operation. The unary minus reverses the sign of any arithmetic operand.
For any arithmetic operator, operands must be arithmetic; that is, they must be constants, variables, or other expressions with the data type attribute BINARY, DECIMAL, or PICTURE. Operands of different arithmetic types are converted to a common type before the operation is performed.
Arithmetic operators have a predefined precedence that governs the
order in which operations are performed. All expressions can be
enclosed in parentheses to override the rules of precedence.
Table 6-2 lists the precedence of operators.
6.2.2 Logical Operators
The logical operators perform logical operations on one or two operands. The operands of the logical operators must be bit-string expressions, except that the operand of the NOT operator can be a bit-string expression or a single relational operator. All relational expressions result in bit-string values of length 1, and they can therefore be used as operands in logical operations.
Except when the NOT operator is used as the prefix of a relational operator, the result of a logical operation is always a bit string.
Except for AND THEN and OR ELSE, logical operations are performed on their operands bit by bit. If bit-string operands are not the same length, PL/I extends the smaller of the operands on the right (that is, in the direction of the least significance) with zeros to match the length of the larger operand. This length is always the length of the result.
There are five infix operators and one prefix operator:
Prefix Operator | Operation |
---|---|
^ (circumflex) | Logical NOT. In a logical NOT operation, the value of the operand is complemented; that is, a 1 bit becomes a 0 and a 0 bit becomes a 1. The value of a relational expression is also complemented; that is ^(A < B) is equivalent to (A >= B). |
Infix Operator | Operation |
---|---|
& (ampersand) | Logical AND. In a logical AND operation, two operands are compared. If corresponding bits are 1, the result is 1; otherwise, the result is 0. |
| (vertical bar) or ! (exclamation point) | Logical OR. In a logical OR operation, two operands are compared. If either or both of two corresponding bits are 1, the result is 1; otherwise the result is 0. (The | and the ! characters can be used interchangeably.) |
&: (ampersand and colon) | Logical AND THEN. The operation is like AND except that the second operand is evaluated only if the first operand is true, and except that AND THEN does not do bit-by-bit operations on bit-string operands. |
^ (circumflex) | Logical EXCLUSIVE OR. Two operands are compared, and the result is 1 if one of the corresponding bits is 1 and the other is 0. |
|: (vertical bar and colon) | Logical OR ELSE. The operation is like OR except that the second operand is evaluated only if the first operand is false, and except that OR ELSE does not do a bit-by-bit operation on bit-string operands. |
You can define additional operations on bit strings with the BOOL built-in function.
Logical expressions will not be completely evaluated in some cases. If the result of the total expression can be determined from the value of one or more individual operands, the evaluation can be terminated. For example:
A & B & C & D & E |
In this expression, evaluation will stop when any operand or the result of any operation is a bit string containing all zeros.
DECLARE (BITA,BITB,BITC) BIT(4); BITA = '0001'B; BITB = '1001'B; BITC = ^BITA; /* BITC equals '1110'B */ BITC = BITA | BITB; /* BITC equals '1001'B */ BITC = BITA & BITB; /* BITC equals '0001'B */ BITC = ^(BITA & BITB); /* BITC equals '1110'B */ BITC = ^(BITA > BITB); /* BITC equals '1000'B (true) */ |
In the last assignment statement, the logical NOT expression yields
<BIT_STRING>(1)B; when this value is assigned to BITC, a BIT(4)
variable, the value is padded with zeros and becomes
<BIT_STRING>(1000)B.
6.2.2.1 NOT
The logical NOT operator in PL/I is the circumflex character (^), used as a prefix operator. In a logical NOT operation, the value of a bit is reversed. If a bit is 1, the result is 0; if a bit is 0, the result is 1.
The NOT operator can be used on expressions that yield bit-string values (bit-string, relational, and logical expressions). It can also be used to negate the meanings of the relational operators (<,>, =). For example:
IF A ^> B THEN ... /* equivalent to IF A <= B THEN ...*/ |
The result of a logical NOT operation on a bit-string expression is a bit-string value. For example:
DECLARE (BITA, BITB) BIT (4); BITA = '0011'B; BITB = ^BITA; |
The resulting value of BITB is 1100.
The NOT operator can test the falsity of an expression in an IF statement. For example:
IF ^(MORE_DATA) THEN ... |
The ampersand (&) character is the logical AND operator in PL/I. In a logical AND operation, two bit-string operands are compared bit by bit. If two corresponding bits are 1, the corresponding bit in the result is 1; otherwise, the resulting bit is 0.
The result of a logical AND operation is a bit-string value. All relational expressions result in bit strings of length 1; they can therefore be used as operands in an AND operation. If the two operands have different lengths, the shorter operand is converted to the length of the longer operand, and the greater length is the length of the result.
DECLARE (BITA, BITB, BITC) BIT (4); BITA = '0011'B; BITB = '1111'B; BITC = BITA & BITB; |
The resulting value of BITC is <BIT_STRING>(0011)B.
The AND operator can test whether two or more expressions are both true in an IF statement. For example:
IF (LINENO(PRINT_FILE) < 60) & (MORE_DATA = YES) THEN ... |
The vertical bar character (|) represents the logical OR operation in PL/I. In a logical OR operation, two bit-string operands are compared bit by bit. If the two operands are of different lengths, the shorter operand is converted to the length of the longer operand, and this is the length of the result. If either of two corresponding bits is 1, the resulting bit is 1; otherwise, the resulting bit is 0.
All relational expressions result in bit strings of length 1, and they can therefore be used as operands in an OR operation.
The result of the OR operation is a bit-string value. For example:
DECLARE (BITA, BITB, BITC) BIT (4); BITA = '0011'B; BITB = '1111'B; BITC = BITA | BITB ; |
The resulting value of BITC is <BIT_STRING>(1111)B.
The OR operator can test whether one of the expressions in an IF statement is true. For example:
IF (LINENO(PRINT_FILE) < 60) | (MORE_DATA = YES) THEN ... |
You can use the exclamation point (!) in place of the vertical bar, for
compatibility with other PL/I implementations.
6.2.2.4 EXCLUSIVE OR
The EXCLUSIVE OR operator (infix or dyadic ^ ) causes a bit-by-bit comparison of two bit-string operands. If the two operands are not of equal length, the shorter is padded with 0s until it is the same length as the other, and this length is also the length of the result. If either of two corresponding bits is 1 and the other is 0, the result is 1. If both are 1, or if both are 0, the result is 0.
All relational expressions result in bit strings of length 1, and they can therefore be used as operands in an EXCLUSIVE OR operation.
The result of the EXCLUSIVE OR operation is a bit-string value. For example:
DECLARE (BITA, BITB, BITC) BIT (4); BITA = '0011'B; BITB = '1011'B; BITC = BITA ^ BITB; |
The EXCLUSIVE OR operator can be used to test whether one and only one of the expressions in an IF statement is true. For example:
IF (A > 0) ^ (B > 0) THEN ... |
The ampersand-colon token (&:) is the AND THEN operator in PL/I. The AND THEN operator causes the first operand to be evaluated; if it is false, the result returned is '0'B. The second operand will never be evaluated if the first operand is false. If and only if the first operand is true, the second operand is evaluated. If both are true, the result returned is true ('1'B); otherwise, the result is false ('0'B).
The AND THEN operator performs a Boolean truth evaluation, not a bit-by-bit operation, even when the two operands are bit strings. For example, '00001'B &: '10000'B yields '1'B (not '00000'B, which would be the result of an AND operation on these two bit strings). The reason is that each operand is a non-zero bit value, and therefore each evalutes to '1'B.
The AND THEN operator yields the same result as the AND operator (&) when expressions are tested in an IF statement (as in the last example in the "AND Operator" entry). The difference is that the AND operator can have its operands evaluated in either order.
The AND THEN operator is useful in compound test expressions in which the second test should occur only if the first test was successful. For example:
IF (P ^= NULL(:POINTER:)) &: (P->X ^= 4) THEN ... |
This statement causes P->X to be evaluated only if P is not a null
pointer. If the AND operator were used instead of AND THEN, this
expression could cause an access violation (invalid pointer reference).
6.2.2.6 OR ELSE
The vertical bar and colon characters (|:) together are the OR ELSE operator in PL/I. The OR ELSE operator causes the first operand to be evaluated. If it is true, the result returned is '1'B. If and only if the first operand is false, the second operand is evaluated. If either or both operands are true, the result returned is '1'B; otherwise, the result is '0'B.
The OR ELSE operator performs a Boolean truth evaluation, not a bit-by-bit operation, even when the two operands are bit strings. For example:
'00001'B |: '10000'B |
This yields:
'1'B |
It does not yield '10001'B, which would be the result of an OR operation on these two bit strings. The reason is that each operand is a nonzero bit value, and therefore each evalutes to '1'B.
The OR ELSE operator yields the same result as the OR operator (|) when expressions are tested in an IF statement (as in the last example in the "OR Operator" entry). The difference is that the OR operator can have its operands evaluated in any order.
The OR ELSE operator is useful in compound test expressions in which the second test should occur only if the first test failed. For example:
IF (A=0) |: (B/A > 1) THEN ... |
This results in the second expression (B/A > 1) being evaluated only if the first expression is false. Thus, the OR ELSE operator prevents an attempt to divide by zero.
Operator | Operation |
---|---|
< | Less than |
^< | Not less than |
<= | Less than or equal to |
= | Equal to |
^= | Not equal to |
>= | Greater than or equal to |
> | Greater than |
^> | Not greater than |
Note that PL/I recognizes the tilde symbol (~) as synonymous with the circumflex (^).
Relational operators compare any of the following data types: arithmetic (decimal or binary); bit-string; character-string; and entry, pointer, label, or file data. Specific results of operations on each type of data are elaborated below. The following general rules apply:
Arithmetic and picture operands are compared algebraically. If the
operands have a different base, scale, or precision, PL/I converts them
according to the rules for arithmetic operand conversion.
6.2.3.2 Bit-String Comparisons
When two bit strings are compared, they are compared bit by bit from
the most significant bit to the least significant bit (as represented
by PUT LIST). If the operands have different lengths, PL/I extends the
smaller operand with zeros in the direction of the least significance.
Null bit strings are equal.
6.2.3.3 Character-String Comparisons
When two character strings are compared, they are compared character by character in a left-to-right order. The comparison is based on the native collating sequence.
Note the following characteristics of the collating sequence:
If the operands do not have the same length, PL/I extends the smaller
operand on the right with blanks for the comparison. Either or both of
the strings can have the attribute VARYING; PL/I uses the current
length of a varying character string when it makes the comparison. Null
character strings are equal.
6.2.3.4 Comparing Noncomputational Data
Only the following operators are valid, or meaningful, for comparisons of any of the noncomputational data types except areas (condition, entry, file, label, offset, and pointer):
Operator | Operation |
---|---|
= | Equal |
^= | Not equal |
The results of the comparisons provide the information indicated below for each data type.
Condition Data
Two condition values are equal if they identify the same condition values.Entry Data
Two entry values are equal if they identify the same entry point in the same block activation of a procedure.File Data
Two values defined with the FILE attribute are equal if they identify the same file constant.Label Data
Two label values are equal if they identify the same statement in the same block activation.A label that identifies a null statement is not equal to the label of any other statement.
Offset Data
Two offset values are equal if they identify the same storage location or if they are both null.Pointer Data
Two pointer values are equal if they identify the same storage location or if they are both null.
The concatenation operator produces a single string from two strings specified as operands. The concatenation operator is two vertical bars (||).
The operands must both be character strings or both be bit strings. The result of the operation is a string of the same type as the operands.
CONCAT: PROCEDURE OPTIONS(MAIN); DECLARE OUTFILE STREAM OUTPUT PRINT FILE; PUT FILE(OUTFILE) SKIP LIST('ABC'||'DEF'); PUT FILE(OUTFILE) SKIP LIST('001'B||'110'B); PUT FILE(OUTFILE) SKIP LIST((3)'001'B||'07'B3); END CONCAT; |
The program CONCAT writes the following output to the file OUTFILE.DAT:
ABCDEF '001110'B '001001001000111'B |
Note that the exclamation point can be used in place of the vertical
bar, for compatibility with other PL/I implementations.
6.3 Precedence of Operators and Expression Evaluation
The precedence, or priority, of operators defines the order in which expressions are evaluated when they contain more than one operator. Table 6-2 gives the priority of PL/I operators. Low numbers indicate high priority. For example, the exponentiation operator (**) has the highest priority (1), so it is performed first, and the OR ELSE operator (|:) has the lowest priority (9), so it is performed last.
Operator | Priority | Left/Right Associative | Order of Evaluation |
---|---|---|---|
() | 0 | N/A | deepest first |
** | 1 | right | left to right |
+ (prefix) | 1 | N/A | N/A |
- (prefix) | 1 | N/A | N/A |
^ (prefix) | 1 | N/A | N/A |
* | 2 | left | left to right |
/ | 2 | left | left to right |
+ (infix) | 3 | left | left to right |
- (infix) | 3 | left | left to right |
|| | 4 | left | left to right |
> | 5 | left | left to right |
< | 5 | left | left to right |
^> | 5 | left | left to right |
^< | 5 | left | left to right |
= | 5 | left | left to right |
^= | 5 | left | left to right |
<= | 5 | left | left to right |
>= | 5 | left | left to right |
& | 6 | left | left to right |
| | 7 | left | left to right |
^ (infix) | 7 | left | left to right |
&: | 8 | left | left to right across entire expression |
|: | 9 | left | left to right across entire expression |
Expressions are evaluated from left to right, with the following qualifications:
A |: B &: C |
(A |: (B &: C)) |
A & USER_FUNCTION(ALPHA,BETA) |
A + B + FUNC(I) + C |
Conversion is the changing of a data item from one data type to another. Data conversion in PL/I takes place in many contexts, not all of them obvious ones. Program results that seem improper may in fact be caused by data conversion at some point in the program's execution. This section discusses the following topics:
PL/I can perform data conversions in the following contexts:
If an attempt is made to assign a value to a target for which there is no defined conversion, the compiler generates a diagnostic message. For example:
F = '133.45'; |
F = 'ABCD'; |
Table 6-3 illustrates the contexts in which PL/I performs conversions. The table also lists the built-in conversion functions, such as BINARY and CHARACTER, which you can use when you want to explicitly indicate a conversion and to specify such characteristics as the precision or string length of the converted result.
Context | Conversion Performed |
---|---|
target = expression; | In an assignment statement, the given expression is converted to the data type of the target. |
entry-name
RETURNS (attribute...); . . . |
In a RETURN statement, the specified value is converted to the data type specified by the RETURNS option on the PROCEDURE statement. |
RETURN (value); | |
x + y
x - y x * y x / y x**y x||y x & y x | y x&:y x|:y x ^ y x > y x < y x = y x^=y |
In any expression, if operands do not have the required data type, they are converted to a common data type before the operation. For most operators, the data types of all operands must be identical. |
BINARY (expression)
BIT (expression) CHARACTER (expression) DECIMAL (expression) FIXED (expression) FLOAT (expression) OFFSET (variable) POINTER (variable) |
PL/I provides built-in functions that perform specific conversions. |
PUT LIST (item,...); | Items in a PUT LIST statement are converted to character-string data. |
GET LIST (item,...); | Character-string input data is converted to the data type of the target item. |
PAGESIZE (expression)
LINESIZE (expression) SKIP (expression) LINE (expression) COLUMN (expression) format items A, B, E, F, and X TAB (expression) |
Values specified for various options to PL/I statements must be converted to integer values. |
DO control-variable... | Values are converted to the attributes of the control variable. |
parameter | Actual parameters are converted to the type of the formal parameter, if necessary. |
INITIAL attribute | Initial values are converted to the type of the variable being initialized. |
Even though arithmetic operands can be of different arithmetic types, all operations will be performed on objects of the same type. Any set of operands of different arithmetic types has an associated derived type, as follows:
Table 6-4 gives the derived data type for two arithmetic operands of different types. (Note that the types derived from FIXED DECIMAL in Table 6-4 are also derived when one operand is pictured.)
Type of Operand 1 | Type of Operand 2 | Derived Type |
---|---|---|
FIXED BINARY | FLOAT BINARY | FLOAT BINARY |
FIXED BINARY | FLOAT DECIMAL | FLOAT BINARY |
FIXED DECIMAL | FLOAT DECIMAL | FLOAT DECIMAL |
FIXED DECIMAL | FLOAT BINARY | FLOAT BINARY |
FIXED BINARY | FIXED DECIMAL | FIXED BINARY |
All arithmetic operations except exponentiation are performed in the derived type of the two operands. Exponential operations are performed in a data type that is based on the derived type of the operands. All operations, including exponentiation, have results of the same type as that in which they are performed.
The result of an arithmetic operation can be assigned to a target variable of any computational type. The result is converted to the target type, following the rules in Section 6.4.5.
Conversion | Function |
---|---|
Arithmetic to bit | BIT |
Arithmetic to character | CHARACTER |
Arithmetic or character to fixed-point arithmetic | FIXED |
Bit to arithmetic | BINARY |
Bit to character | CHARACTER |
Character to bit | BIT |
Character to decimal | DECIMAL |
Character to float | FLOAT |
Arithmetic or character to binary | BINARY |
During assignment, PL/I automatically converts the derived data type of
an expression to the data type of a target, if necessary.
In assignments, conversions are defined between any two computational types.
However, a conversion during assignment results in an error if PL/I
cannot perform it in a
meaningful way. For example, you can assign the string '123.4' to a
fixed decimal variable; you cannot, however, assign the string 'ABCD'
to the same variable. Similarly, an assignment of an arithmetic type to
a fixed variable results in the FIXEDOVERFLOW condition if integral
digits are lost.
6.4.6 Assignment to Arithmetic Variables
Expressions of any computational type can be assigned to arithmetic
variables. The conversion rules for each source type are described in
the following sections.
6.4.6.1 Arithmetic to Arithmetic Conversions
A source expression of any arithmetic type can be assigned to a target variable of any arithmetic type. Note the following qualifications:
In the following examples, the specified source values are converted to FIXED DECIMAL(4,1):
Source Value | Converted Value |
---|---|
25.505 | 25.5 |
-2.562 | -2.5 |
101 | 101.0 |
5365 | FIXEDOVERFLOW - value undefined |
Let p be the precision of the floating-point target. If the source value is an integer that can be represented exactly in p digits, then the source value is converted to floating-point binary with no loss of accuracy.
Otherwise, the source value is converted to floating-point binary with
rounding to precision p.
6.4.6.1.3 Conversions from FIXED BINARY to Other Data Types
Conversions from FIXED BINARY to other data types follow the rules outlined below. Notice that these rules assume both precision and scale.
Precisions of the source and target are (p,q) and (p1,q1), respectively. The precision of the result is (p2,q2).
Target | Result |
---|---|
FIXED DECIMAL(p1,q1) | p2=1+CEIL(p1/3.32) and q2=CEIL(q1/3.32). |
FIXED BINARY(p1,q1) | Precision and scale of the source are maintained during conversion; therefore, padding or truncation can occur. If nonzero bits are lost on the left, the result is undefined. |
FLOAT DECIMAL(p1) | p2=CEIL(p1/3.32). The exponent indicates any fractional value. |
FLOAT BINARY(p1) | p2=p1. The mantissa indicates any fractional value. |
PICTURE | The target implies FIXED DECIMAL and is converted accordingly. |
CHARACTER | The binary precision (p,q) is converted to a FIXED DECIMAL with precision (p1,q1), where p1=1+CEIL(p/3.32) and q1=CEIL(q/3.32). Then the rules for conversion from FIXED DECIMAL to CHARACTER are in effect. |
BIT | The binary precision (p,q) is converted to an intermediate bit string where the size or precision is p - q. Then the intermediate bit string is converted to BIT(n). If (p-q) is negative or zero, the result is a null bit string. |
If the scale factor is negative, substitute the FLOOR value for CEIL in
the above calculations which contain q's.
6.4.6.2 Pictured to Arithmetic Conversions
In PL/I all pictured values have the associated attributes FIXED
DECIMAL(p,q), where p is the total number of characters in the picture
specification that specify decimal digits, and q is the total number of
these digits that occur to the right of the V character. If the picture
specification does not include a V character, then q is zero. This
value is assigned to the target, following the PL/I rules for
arithmetic to arithmetic conversion.
6.4.6.3 Bit-String to Arithmetic Conversions
When a bit-string value is assigned to an arithmetic variable, PL/I treats the bit string as a fixed-point binary value. A string of type BIT(n) is converted to a FIXED BINARY value.
If the converted value is greater than or equal to the largest possible FIXED BINARY value, then FIXEDOVERFLOW is signaled. The leftmost bit in the bit string (as output by PUT LIST) is the most significant bit in the fixed-point binary value, not its sign. If the bit string is null, the fixed-point binary value is zero.
The intermediate fixed-point binary value is then converted to the target arithmetic type.
Note that bit strings are stored internally with the leftmost bit in the lowest address. The conversion to an arithmetic type must reverse the bits from this representation; therefore, you should avoid this conversion when performance is a consideration.
CONVTB: PROCEDURE OPTIONS(MAIN); DECLARE STATUS FIXED BINARY(8); DECLARE STATUS_D FIXED DECIMAL(10); DECLARE OUT PRINT FILE; OPEN FILE(OUT) TITLE('CONVTB.OUT'); ON FIXEDOVERFLOW PUT SKIP FILE(OUT) LIST('Fixedoverflow:'); STATUS = '1001101'B; PUT SKIP FILE(OUT) LIST(STATUS); STATUS_D = '001101'B; PUT SKIP FILE(OUT) LIST(STATUS_D); STATUS = '1232'B2; PUT SKIP FILE(OUT) LIST(STATUS); STATUS = 'FF'B4; PUT SKIP FILE(OUT) LIST(STATUS); STATUS_D = '10111111111111111111111111111111'B; END CONVTB; |
Note that because the program CONVTB performs implicit conversions, the compiler issues WARNING messages. (Linking and running are accomplished successfully because the conversions are valid.)
The program CONVTB produces the following output:
77 13 110 255 Fixedoverflow: |
The leftmost bit of all the bit-string constants is treated as the most
significant numeric bit, not as a sign. For instance, the hexadecimal
constant <BIT_STRING>(FF)B4 is converted to 255 instead of -127.
The last assignment to STATUS_D signals the FIXEDOVERFLOW condition
because the bit-string constant, when represented as a binary integer,
is too large to represent. The resulting value of STATUS_D is
undefined.
6.4.6.4 Character-String to Arithmetic Conversions
When a character string is assigned to an arithmetic value, PL/I creates an intermediate numeric value based on the characters in the string. The type of this intermediate value is the same as that of an ordinary arithmetic constant comprising the same characters; for example, 342.122E-12 and <BIT_STRING>(342.122E-12) are both floating-point decimal.
The character string can contain any series of characters that describes a valid arithmetic constant. That is, the character string can contain any of the numeric digits 0 through 9, a plus (+) or minus (-) sign, a decimal point (.), and the letter E. If the character string contains any invalid characters, the CONVERSION condition is signaled. See the following examples.
If the implied data type of the character string does not match the data type of the arithmetic target, PL/I converts the intermediate value to the data type of the target, following the PL/I rules for arithmetic to arithmetic conversions. In conversions to fixed point, FIXEDOVERFLOW is signaled if the character string specifies too many integral digits. Excess fractional digits are truncated without signaling a condition.
If the source character string is null or contains all spaces, the resulting arithmetic value is zero.
DECLARE SPEED FIXED DECIMAL (9,4); SPEED = '23344.3882'; /* string converted to 23344.3882 */ SPEED = '32423.23SD'; /* CONVERSION condition */ SPEED = '4324324.3933'; /* FIXEDOVERFLOW condition */ SPEED = '1.33336'; /* string converted to 1.3333 */ |
In the conversion of any data type to a bit string, PL/I first converts the source data item to an intermediate bit-string value. Then, based on the length of the target string, it does the following:
The next sections describe how PL/I arrives at the intermediate
bit-string value for each data type.
6.4.7.1 Arithmetic to Bit-String Assignments
In converting an arithmetic value sv to a bit-string value, PL/I performs the following steps:
Source | Precision p |
---|---|
FIXED BINARY(r,s) | r - s |
FLOAT BINARY(r) | r |
FIXED DECIMAL(r,s) | ceil((r-s)*3.32) |
FLOAT DECIMAL(r) | ceil(r*3.32) |
Bit strings are stored internally with the leftmost bit in the lowest address. The conversion must reverse the bits from this representation and should therefore be avoided when performance is a consideration. Note also that during the conversion, the sign of the arithmetic value and any fractional digits are lost.
CONVB: PROCEDURE OPTIONS(MAIN); DECLARE NEW_STRING BIT(10); DECLARE LONGSTRING BIT(16); DECLARE OUT PRINT FILE; OPEN FILE(OUT) TITLE('CONVB1.OUT'); NEW_STRING = 35; PUT FILE(OUT) SKIP LIST('35 converted to BIT(10):',NEW_STRING); NEW_STRING = -35; PUT FILE(OUT) SKIP LIST('-35 converted to BIT(10):',NEW_STRING); NEW_STRING = 23.12; PUT FILE(OUT) SKIP LIST('23.12 converted to BIT(10):',NEW_STRING); NEW_STRING = .2312; PUT FILE(OUT) SKIP LIST('.2312 converted to BIT(10):',NEW_STRING); NEW_STRING = 8001; PUT FILE(OUT) SKIP LIST('8001 converted to BIT(10):',NEW_STRING); LONGSTRING = 8001; PUT FILE(OUT) SKIP LIST('8001 converted to BIT(16):',LONGSTRING); END CONVB; |
Note that because the program CONVB performs implicit conversions, the compiler issues WARNING messages. (Linking and running are accomplished successfully because the conversions are valid.)
The program CONVB produces the following output:
35 converted to BIT(10): '0100011000'B -35 converted to BIT(10): '0100011000'B 23.12 converted to BIT(10): '0010111000'B .2312 converted to BIT(10): '0000000000'B 8001 converted to BIT(10): '0111110100'B 8001 converted to BIT(16): '0111110100000100'B |
The values 35 and -35 produce the same bit string because the sign is
lost in the conversion. In the first assignment, 35, which is FIXED
DECIMAL(2,0), is converted to FIXED BINARY(7,0) and then to a 7-bit
string (<BIT_STRING>(0100011)B). Three additional bits are
appended to this intermediate bit string when it is assigned to
NEW_STRING. Notice that the low-order bit of 8001 is lost when the
constant is assigned to a BIT(10) variable.
6.4.7.2 Pictured to Bit-String Conversions
If the source value is pictured, its associated fixed-point decimal
value is extracted. The fixed-point decimal value is then converted to
a bit string, following the previous rules for arithmetic to bit-string
conversion.
6.4.7.3 Character-String to Bit-String Conversions
PL/I can convert a character string of 0s and 1s to a bit string. Any character in the character string other than 0 or 1, including spaces, will signal the CONVERSION condition.
PL/I converts each 0 or 1 character in the character string to a 0 or a 1 bit in the corresponding position (as represented by PUT LIST) in the intermediate bit string.
If the source is a null character string, the intermediate string is a null bit string.
DECLARE NEW_STRING BIT(4); NEW_STRING = '0010'; /* NEW_STRING = '0010'B */ NEW_STRING = '11'; /* NEW_STRING = '1100'B */ NEW_STRING = 'AS110'; /* CONVERSION condition */ |
In the conversion of any data type to a character string, PL/I first converts the source value to an intermediate character-string value. Then it does one of the following:
The rules for how PL/I arrives at the intermediate string for
conversion of each data type are described below. Examples illustrate
the intermediate value as well as the resulting value.
6.4.8.1 Arithmetic to Character-String Conversions
The manner in which PL/I converts an arithmetic data item depends on
the data type of the item, as described below.
6.4.8.1.1 Conversion from Fixed-Point Binary or Fixed-Point Decimal
If the data item source value is of type FIXED BINARY(p1,q1), PL/I first converts it to type FIXED DECIMAL(p2,q2), where:
p2 = ceil(p1/3.32)+1
q2 = ceil(q1/3.32)
PL/I converts a value with attributes FIXED DECIMAL(p,q) to an intermediate string of length p+3. The numeric value is right-justified in the string. If the value is negative, a minus sign immediately precedes the value. If q is greater than zero, the value contains a decimal point followed by q digits. When p equals q, a 0 character precedes the decimal point. When q equals zero, a value of zero is represented by the 0 character.
Alternatively, the format of the intermediate string can be described by picture specifications, as follows:
'BB(p)-9' |
'-9V.(q)9' |
'B(p-q)-9V.(q)9' |
DECLARE STRING_1 CHARACTER (8), STRING_2 CHARACTER (4); STRING_1 = 283472.; /* intermediate string = ' 283472', STRING_1 = ' 28347' */ STRING_2 = 283472.; /* intermediate string = ' 283472', STRING_2 = ' 2' */ STRING_2 = -283472.; /* intermediate string = ' -283472', STRING_2 = ' -2' */ STRING_2 = -.003344; /* intermediate string = '-0.003344', STRING_2 = '-0.0' */ STRING_2 = -283.472; /* intermediate string = ' -283.472', STRING_2 = ' -28' */ STRING_2 = 283.472; /* intermediate string = ' 283.472', STRING_2 = ' 28' */ |
If the value is a G-floating-point number, three characters are allocated to the exponent, and the length of the string is p+7. |
If the number is negative, the first character is a minus sign; otherwise, the first character is a space. The subsequent characters are a single digit (which can be 0), a decimal point, p-1 fractional digits, the letter E, the sign of the exponent (always + or -), and the exponent digits. The exponent field is of fixed length, and the zero exponent is shown as all zeros in the exponent field.
CONCH: PROCEDURE OPTIONS(MAIN); DECLARE OUT PRINT FILE; OPEN FILE(OUT) TITLE('CONCH.OUT'); PUT SKIP FILE(OUT) EDIT('''',25E25,'''') (A); PUT SKIP FILE(OUT) EDIT('''',-25E25,'''') (A); PUT SKIP FILE(OUT) EDIT('''',1.233325E-5,'''') (A); PUT SKIP FILE(OUT) EDIT('''',-1.233325E-5,'''') (A); END CONCH; |
The program CONCH produces the following output:
' 2.5E+26' '-2.5E+26' ' 1.233325E-05' '-1.233325E-05' |
The PUT statement converts its output sources to character strings,
following the rules described in this section. (The output strings are
surrounded with apostrophes to make the spaces distinguishable.) In
each case, the width of the quoted output field (that is, the length of
the converted character string) is the precision of the floating-point
constant plus 6.
6.4.8.2 Pictured to Character-String Conversion
If the source value is pictured, its internal, character-string
representation is used without conversion as the intermediate character
string.
6.4.8.3 Bit-String to Character-String Conversion
When PL/I converts a bit string to a character string, it converts each bit in the bit string (as represented by PUT LIST) to a 0 or 1 character in the corresponding position of the intermediate character string.
If the bit string is a null string, the intermediate character string is also a null string.
DECLARE STRING_1 CHARACTER (4), STRING_2 CHARACTER (8); STRING_1 = '1010'B; /* STRING_1 = '1010' */ STRING_2 = '1010'B; /* STRING_2 = '1010 ' */ STRING_1 = '010011'B; /* STRING_1 = '0100' */ |
A source expression of any computational type can be assigned to a pictured variable. The target pictured variable has a precision (p), which is defined as the number of characters in the picture specification that specify decimal digits. The target also has a scale factor (q), which is defined as the number of picture characters that specify digits and occur to the right of the V character in the picture specification. If the picture specification contains no V character, or if all digit-specification characters are to the left of V, then q is zero.
The source expression is converted to a fixed-point decimal value v of precision (p,q), following the PL/I rules for the source data type. This value is then edited to a character string s, as specified by the picture specification, and the value s is assigned to the pictured target.
When the value v is being edited to the string s, the CONVERSION
condition is signaled if the value of v is less than zero and the
picture specification does not contain one of the characters S, +, -,
T, I, R, CR, or DB. The value of s is then undefined. FIXEDOVERFLOW is
signaled if the value v has more integral digits than are specified by
the picture specification of the target.
6.4.10 Conversions Between Offsets and Pointers
Offset variables are given values by assignment from existing offset values or from conversion of pointer values. Pointer variables are given values by assignment from existing pointer values or from conversion of offset values.
The OFFSET built-in function converts a pointer value to an offset value. The POINTER built-in function converts an offset value to a pointer.
PL/I also automatically converts a pointer value to an offset value, and vice versa, in an assignment statement. The following assignments are valid:
In the third and fourth assignments above, the offset variable must have been declared with an area reference.
A procedure is the basic executable program unit in PL/I. It consists of a sequence of statements, headed by a PROCEDURE statement and terminated by an END statement, that define an executable set of program instructions. There are three kinds of procedures:
Subroutines and function procedures can be passed data from the invoking procedure by means of an argument list.
This chapter discusses the following topics:
The PROCEDURE statement defines the beginning of a procedure block and specifies the parameters, if any, of the procedure. If the procedure is invoked as a function, the PROCEDURE statement also specifies the data type attributes of the value that the function returns to its point of invocation. The PROCEDURE statement can denote the beginning of either an internal or an external subroutine or function.
The format of the PROCEDURE statement is as follows:
⎧PROCEDURE⎫ entry-name: ⎨ ⎬ [(parameter,...)] ⎩PROC ⎭ [OPTIONS(option,...)] ⎰NONRECURSIVE⎱ ⎱RECURSIVE ⎰
entry-name
An identifier denoting the entry label of the procedure. The label cannot be subscripted. The PROCEDURE statement declares the entry name as an entry constant. The scope of the name is INTERNAL if the procedure is contained in any other block, and EXTERNAL if the procedure is not contained in any other block.parameter,...
One or more parameters (separated by commas) that the procedure expects when it is activated. Each parameter specifies the name of a variable declared in the procedure headed by this PROCEDURE statement. The parameters must correspond, one-to-one, with arguments specified for the procedure when it is invoked with a CALL statement or in a function reference.OPTIONS (option,...)
An option that specifies one or more options, separated by commas:
RECURSIVE or NONRECURSIVE
An option that indicates (for program documentation) that the procedure will or will not be invoked recursively. In standard PL/I, the RECURSIVE option must be specified for a procedure to be invoked recursively.RETURNS (returns-descriptor)
An option specifying that the procedure is invoked by a function reference, as well as specifying the attributes of the function value returned. One of the possible attributes is TYPE. The syntax of the TYPE attribute is:
[(TYPE (reference)];
RETURNS must be specified for functions. It is invalid for procedures that are invoked by CALL statements.
For valid return descriptors, see the RETURN statement section of Section 7.7.
A function is a procedure that returns a value and that receives control when its name is referenced in an expression. There are two types of functions:
A user-written function must have the following elements:
For example:
ADDER: PROCEDURE (X,Y) RETURNS (FLOAT); DECLARE (X,Y) FLOAT; RETURN (X+Y); END; |
The function ADDER has two parameters, X and Y. They are floating-point binary variables declared within the function. When the function is invoked by a function reference, it must be passed two arguments to correspond to these parameters. It returns a floating-point binary value representing the sum of the arguments. The function ADDER can be referenced as follows:
TOTAL = ADDER(5,6); |
The arguments in the reference to ADDER are converted to FLOAT.
If a function has no parameters, you must specify a null argument list; otherwise, the compiler treats the reference as a reference to an entry constant. Specify a null argument list as follows:
GETDATE = TIME_STAMP(); |
This assignment statement contains a reference to the function TIME_STAMP, which has no parameters.
This rule applies to PL/I built-in functions as well; however, if you declare a PL/I built-in function explicitly with the BUILTIN attribute, you need not specify the empty argument list. For example:
DECLARE P POINTER, . . . P = NULL; |
This example assigns a null pointer value to P. been as follows:
P = NULL(:POINTER:); |
The CALL statement transfers control to an entry point of a procedure and optionally passes arguments to the procedure. The format of the CALL statement is as follows:
CALL entry-name [(argument,...)]; |
entry-name
The name of an external or internal procedure that does not have the RETURNS attribute. The entry name can also be an entry variable or a reference to a function that returns an entry value.[(argument,...)]
The argument list to be passed to the called procedure. If specified, the arguments must correspond to the parameters specified in the PROCEDURE statement that identifies the entry name of the called procedure.You must enclose arguments in parentheses. Multiple arguments must be separated by commas.
You can use the CALL statement to call an internal or external procedure. The following example illustrates a main procedure, CALLER, and a call to an internal procedure, PUT_OUTPUT. PUT_OUTPUT has two parameters, INSTRING and OUTFILE, that correspond to the arguments LINE and DEVICE specified in the CALL statement.
CALLER: PROCEDURE OPTIONS(MAIN); . . . CALL PUT_OUTPUT(LINE,DEVICE); . . . PUT_OUTPUT:PROCEDURE(INSTRING,OUTFILE); . . . END PUT_OUTPUT; END CALLER; |
A PL/I procedure can invoke other procedures, as well as pass values to and receive them from the invoked procedure. Values are passed to an invoked procedure by means of arguments written in the procedure invocation. Values are returned to the invoking procedure by means of parameters and also, in the case of functions, by specifying a value in the function's RETURN statement.
You can specify arguments for a subroutine (invoked by a CALL statement) or for a function (invoked by a function reference). Subroutines and functions return values by different means.
Example 7-1 illustrates the relationship between arguments (specified in a CALL statement) or function reference and parameters (specified in a PROCEDURE statement).
Example 7-1 Parameters and Arguments |
---|
CALLER:PROCEDURE; DECLARE COMPUTER EXTERNAL ENTRY (1) (FIXED BINARY (7), CHARACTER (80) VARYING); CALL COMPUTER (5,'ABC'); (2) END CALLER; COMPUTER:PROCEDURE (X,Y); (3) DECLARE X FIXED BINARY (7); (4) DECLARE Y CHARACTER (80) VARYING; END COMPUTER; |
test: procedure options(main); declare a fixed bin(31); a = 100; call subroutine(a,100); put skip list (a,100); end test; subroutine: procedure(x,y); declare (x,y) fixed bin(31); x = 101; y = 101; end subroutine; |
The result of this example would be:
101 100 |
The general rules listed below for specifying parameters are followed by specific rules that pertain only to certain data types.
AUTOMATIC | EXTERNAL | |
BASED | STATIC | |
DEFINED | INITIAL |
If the name of an array variable is passed as an argument, the corresponding parameter descriptor or parameter declaration must specify the same number of dimensions as the argument variable. You can declare the bounds of a dimension for an array parameter using asterisks (*) or optionally signed integer constants. If the bounds are specified with integer constants, they must match exactly the bounds of the corresponding argument. An asterisk indicates that the bounds of a dimension are not known. (If one dimension contains an asterisk, all the dimensions must contain asterisks.) For example:
DECLARE SUMUP ENTRY ((*) FIXED BINARY); |
This declaration indicates that SUMUP's argument is a one-dimensional array of fixed-point binary integers that can have any number of elements. Any one-dimensional array of fixed-point binary integers can be passed to this procedure.
All the data type attributes of the array argument and parameter must match.
Arrays are always passed by reference. They cannot be passed by dummy argument.
If the name of a structure variable is passed as an argument, the corresponding parameter descriptor or declaration must be identical, in terms of structure levels, members' sizes, and members' data types. The level numbers do not have to be identical, but the levels must be logically equivalent. You can specify array bounds and string lengths with asterisks or with optionally signed integer constants. The following example shows the parameter descriptor for a structure variable:
DECLARE SEND_REC ENTRY (1, 2 FIXED BINARY(31), 2 CHARACTER(40) VARYING, 2 PICTURE '999V99'); |
The written argument in the invocation of the external procedure SEND_REC must have the same structure, and its members must have the same data types.
Structures are always passed by reference. They cannot be passed by dummy argument.
If a character-string variable is passed as an argument, the corresponding parameter descriptor or parameter declaration can specify the length using an asterisk (*) or an optionally signed nonnegative integer constant. For example:
COPYSTRING: PROCEDURE (INSTRING,COUNT); DECLARE INSTRING (CHARACTER(*)); |
The asterisk in the declaration of this parameter indicates that the string can have any length. The string is fixed length unless VARYING is also included in the declaration.
Entry, File, and Labels Parameters
An entry, file, or label can be passed as an argument. The actual
parameter is a variable.
7.5.2 Argument Passing
This section describes how PL/I passes an argument to procedures written in PL/I.
The number of arguments in the argument list must equal the number of parameters of the invoked entry point. The compiler checks that the count matches as follows:
You specify arguments for a subroutine or function by enclosing the arguments in parentheses following the procedure or entry-point name. For example, a procedure call can be written as follows:
CALL COMPUTER (A,B,C); |
The variables A, B, and C in this example are arguments to be passed to the procedure COMPUTER. The procedure COMPUTER might have a parameter list like this:
COMPUTER: PROCEDURE (X, Y, Z); DECLARE (X,Y,Z) FLOAT; |
The parameters X, Y, and Z, specified in the PROCEDURE statement for the subroutine COMPUTER, are the parameters of the subroutine. PL/I establishes the equivalence of the arguments A, B, and C to the parameters X, Y, and Z.
When a PL/I procedure is invoked, each of its parameters is associated with a variable determined by the corresponding written argument of the procedure call. This variable is the actual argument for this procedure invocation. It can be one of the following:
The data type of the actual argument is the same as that of the corresponding parameter. When a written argument is a variable reference, PL/I matches the variable against the corresponding parameter's data type according to the rules given under the heading "Argument Matching," below. If they match, the actual argument is the variable denoted by the written argument. That is, the parameter denotes the same storage as the written variable reference. If they do not match, the compiler creates a dummy argument and assigns to it the value of the written argument.
A dummy argument is a unique temporary variable allocated by the compiler, which exists only for the duration of the procedure invocation.
When the written argument is a constant or an expression, the actual argument is always a dummy argument. The value of the written argument is assigned to the dummy argument following the rules of data type conversion before the call (this is described later). The data type of the written argument must be valid for assignment to the data type of the dummy argument.
An array, structure, or area argument must be a variable reference that matches the corresponding parameter. It cannot be a reference to an unconnected array. A dummy argument is never created for an array, structure, or area.
A written argument that is a variable reference is passed by reference only if the argument and the corresponding parameter have identical data types:
When the data type of a written argument is suitable for conversion to
the data type of the corresponding parameter descriptor, PL/I performs
the conversion of the argument to a dummy argument using the rules
described in Section 5.4.3.
7.6 Calling External and Internal Procedures
An external procedure is one whose text is not contained in any other block. The source text of an external procedure can be compiled separately from that of a calling procedure. The differences between internal and external procedures are as follows:
Example 7-2 illustrates the use of an internal procedure:
Example 7-2 Invoking an Internal Procedure |
---|
MAINP: PROCEDURE OPTIONS (MAIN); COMPUTE: PROCEDURE; ADD_NUMBERS: PROCEDURE; END ADD_NUMBERS; END COMPUTE; PRINT_REPORT: PROCEDURE; END PRINT_REPORT; END MAINP; |
In Example 7-2, the procedures COMPUTE and PRINT_REPORT are internal to the procedure MAINP, and the procedure ADD_NUMBERS is internal to the procedure COMPUTE. MAINP can invoke the procedures COMPUTE and PRINT_REPORT, but not ADD_NUMBERS. COMPUTE and PRINT_REPORT can invoke one another. ADD_NUMBERS can call COMPUTE and PRINT_REPORT.
Example 7-3 illustrates the use of an external procedure:
Example 7-3 Invoking an External Procedure |
---|
WINDUP: PROCEDURE; DECLARE PITCH EXTERNAL ENTRY (CHARACTER(15) VARYING, FIXED BINARY(7) ); CALL PITCH (PLAYER_NAME,NUMBER_OF_OUTS); |
The procedure WINDUP declares the procedure PITCH with the EXTERNAL and ENTRY attributes. The text of PITCH is in another source program that is separately compiled. When the object module that contains WINDUP is linked, the linker must be able to locate the object module that contains PITCH. You can accomplish this by including both object modules on the linker command line, or by placing PITCH in an object module library and including the library on the linker command line.
When a CALL statement or function reference invokes an entry point in an external procedure, the entry constant must be declared with the ENTRY attribute, as in the example above. Such a declaration must also describe the parameters for that entry point, if any. For example:
DECLARE PITCH ENTRY (CHARACTER(*), FIXED BINARY(15)); |
The identifier PITCH is declared as an entry constant. When the procedure containing this declaration is linked to other procedures, one of them must define an entry point named PITCH as the label of a PROCEDURE statement.
The parameter descriptors define the data types of the parameters for the entry point PITCH. Arguments of these types must be supplied when PITCH is invoked.
If PITCH is to invoke a function, the DECLARE statement must also include a RETURNS attribute describing the attributes of the returned value, as follows:
DECLARE PITCH ENTRY (CHARACTER(*), FIXED BINARY(15)) RETURNS(FIXED); |
Within the scope of this DECLARE statement, the entry constant PITCH must be used in a function reference. The function reference will invoke the external entry point, and a returned fixed-point binary value will become the value of the function reference.
A PL/I program can invoke an external procedure that is not written in PL/I. A common instance is the use of a system service by a PL/I program to obtain some system function not available directly through PL/I. Or, a PL/I program can invoke an external procedure written in another language that provides an application-specific function.
If the external procedure's name is not a valid PL/I identifier or vice
versa, the EXTERNAL attribute may be followed by a parenthesized string
specifying the external name.
7.7 Terminating Procedures
The execution of subroutines and functions can be terminated with the following statements:
The RETURN statement terminates execution of the current procedure. The format of the RETURN statement is as follows:
RETURN [ (return-value) ]; |
return-value
The value to be returned to the invoking procedure. If the current procedure was invoked by a function reference, a return value must be specified. If the current procedure was invoked by a CALL statement, a return value is invalid.A return value can be any scalar arithmetic, bit-string, or character-string expression; it can also be an entry, pointer, or label expression or other noncomputational expression. The return value must be valid for conversion to the data type specified in the RETURNS option of the function.
The action taken by the RETURN statement depends on the context of the procedure activation, as follows:
The RETURN statement must not be immediately contained in an ON-unit or in a begin block that is immediately contained in an ON-unit.
The statements described in this chapter direct the run-time flow of execution from statement to statement. They are the DO, BEGIN, END, IF, SELECT, GOTO, LEAVE, STOP, null, ON, SIGNAL, and REVERT statements.
The remainder of the chapter is devoted to handling conditions that
could arise during the execution of your program.
8.1 DO Groups and Statements
A DO-group is a sequence of PL/I statements delimited by a DO statement and its corresponding END clause. The statements in a DO-group are executed as the result of an unconditional DO statement or as the result of the successful test of a conditional DO.
For example:
IF A > B THEN DO; . . . END; |
The statements that occur between the DO and the END are a DO-group. After all statements to be executed in this conditional DO-group are complete, execution continues with the next executable statement following the END statement.
Normally, all the statements contextually nexted one level below the DO in the group are executed. However, control can be transferred out of a DO-group in the following ways:
The DO statement begins a sequence of statements to be executed in a group; the group ends with the nonexecutable statement END. DO-groups have several formats. These formats are described individually under the following subheadings:
A simple DO statement is a noniterative DO. The statements nested directly between the DO statement and its corresponding END statement are executed once. PL/I treates these nested statements as if they are one statement. After all statements in the group are executed, control passes to the next executable statement in the program.
The format of a simple DO statement is:
IF A < B THEN DO; PUT LIST ('More data needed'); GET LIST (VALUE); A = A + VALUE; END; |
The simple DO statement is commonly used as the action of the THEN
clause of an IF statement, as shown above, or of an ELSE option.
8.1.2 DO WHILE
A DO WHILE statement causes a group of statements to be repeatedly executed as long as a particular condition is satisfied. When the condition is not true, the group is not executed.
The format of the DO WHILE statement is:
test-expression
Any expression that yields a single-bit scalar value. If the value is a 1, then the test expression is true; otherwise, the test expression is false. The test expression must be enclosed in parentheses. (Comparison operations yield a value with the type BIT(1).)This expression is evaluated before each execution of the DO-group. It must have a true value in order for the DO-group to be executed. Otherwise, control passes over the DO-group to the next executable statement following the END statement that terminates the group.
DO WHILE (A < B); . . . END; |
This DO-group is executed as long as the value of the variable A is less than the value of the variable B.
DO WHILE (LIST->NEXT ^= NULL(:POINTER:)); . . . END; |
This DO-group is executed until a forward pointer in a linked list has a null value.
DECLARE EOF BIT(1) INITIAL('0'B); . . . ON ENDFILE(INFILE) EOF = '1'B; READ FILE(INFILE) INTO(INREC); DO WHILE (^EOF); . . . READ FILE(INFILE) INTO(INREC); END; |
This DO-group reads records from the file INFILE until the end of the
file is reached. At the beginning of each iteration of the DO-group,
the expression ^EOF is evaluated; the expression is
<BIT_STRING>(1)B until the ENDFILE ON-unit sets the value of EOF
to <BIT_STRING>(1)B.
8.1.3 DO UNTIL
A DO UNTIL statement causes a group of statements to be repeatedly executed until a particular condition is satisfied. That is, while the condition is false, the group is repeated.
The format of the DO UNTIL statement is:
test-expression
Any expression that yields a scalar value. If any bit of the value is 1, then the test expression is true; otherwise the test expression is false. The test expression must be enclosed in parentheses. (Comparison operations yield a value having the type BIT(1).)This expression is evaluated after each execution of the DO-group. It must have a false value for the DO-group to be repeated. Otherwise, control passes to the next executable statement following the END statement that terminates the DO-group. The test expression must be enclosed in parentheses.
Both the WHILE and UNTIL options check the status of test expressions, but they differ in that the WHILE option tests the value of the test expression at the beginning of the DO-group, and UNTIL tests the value of the test expression at the end of the DO-group. Therefore, a DO-group with the UNTIL option and no WHILE option will always be executed at least once, but a DO-group with the WHILE option may never be executed. |
DO UNTIL (A=0); . . . END; |
This DO-group is executed at least once and continues as long as the value of A is not equal to zero.
DO UNTIL (K<ALPHA); . . . END; |
This DO-group is executed at least once and continues as long as the
value of the variable K is greater than or equal to the value of the
variable ALPHA.
8.1.4 Controlled DO
A controlled DO statement identifies a variable whose value controls the execution of the DO-group and defines the conditions under which the control variable is to be modified and repeatedly tested.
The format of the controlled DO statement is:
DO control-variable = start-value ⎡TO end-value [BY modify-value]⎤ ⎣ BY modify-value ⎦ [ WHILE(test-expression) ] [ UNTIL(test-expression) ] ; . . . END;
control-variable
A reference to a variable whose current value, as compared to the end value specified in the TO option, determines whether the DO-group is executed. If none of the options are specified, the DO-group is executed a single time regardless of the value of the control variable. The control variable must be of an integer type or a pointer.start-value
An expression specifying the initial value to be given to the control variable. Evaluation of this expression must yield an integer or pointer value.end-value
An expression giving the value to be compared with the control variable during successive iterations. Evaluation of this expression must yield an integer or pointer value. This expression is evaluated exactly once when the statement is executed for the first time. Thus if the end value changes as the loop progresses, only this initial value is used.modify-value
An expression giving a value by which the control value is to be modified. Evaluation of this expression must yield an integer or pointer value. This expression is evaluated exactly once when the statement is executed for the first time. Thus if the modify value changes as the loop progresses, only this initial value is used. If the BY option is not specified, the modify value is 1 by default.WHILE (test-expression)
An option specifying a condition that further controls the execution of the DO-group. The condition must be true at the beginning of each DO-group iteration for the DO-group to be executed. The specified test expression must yield a single-bit scalar value. If the value is a 1, then the test expression is true; otherwise, the test expression is false. The test expression must be enclosed in parentheses.UNTIL (test-expression)
An option specifying a condition that further controls the execution of the DO-group. This expression is evaluated at the end of each interation of the DO-group, before the BY clause is applied to the control variable. The condition must be false at the end of a DO-group execution for the next DO-group iteration to be executed. The specified test expression must yield a scalar value. If any bit in the value is a 1, then the test expression is true; otherwise, the test expression is false. The test expression must be enclosed in parentheses.
If the TO, WHILE, and UNTIL options are omitted, the controlled DO statement specifies no means for terminating the group; the execution of the group must be terminated by a statement or condition occurring within the group. |
This DO-group will prompt the user for integer input values, and add each input value to the current sum. When the sum is greater than 100, the DO-group will exit.
DECLARE (NEXT_VALUE,SUM) FIXED BIN; SUM = 0; DO UNTIL ( SUM > 100 ); GET LIST (NEXT_VALUE) OPTIONS (PROMPT ('Next value to add? ')); SUM = SUM + NEXT_VALUE; END; PUT SKIP LIST ('The total sum is ',SUM); |
The controlled DO-group is executed by the following steps:
control variable = control variable + modify value; |
DO I = 2 TO 100 BY 2; |
This DO-group is executed 50 times, with values for I of 2, 4, 6, and so on.
DO I = LBOUND(ARRAY,1) TO HBOUND(ARRAY,1); |
This DO-group is executed as many times as there are elements in the array variable ARRAY, using the subscript values of the array's elements for the values of I.
DO I = 1 BY 1 WHILE (X < Y); |
This DO-group continues to be executed with successively higher values for I while the value of X is less than the value of Y.
DO I = 1 BY 1 WHILE (X < Y) UNTIL (X = 12); |
This DO-group resembles the DO-group in the preceding example, except that the DO-group continues to be executed while the value of X is less than the value of Y or until the value of X is equal to 12.
A controlled DO statement that does not specify a TO or BY option results in a single iteration of the following DO-group. For example:
DO X = 1 WHILE (A); |
Even if A is true, this DO-group executes a single time only. If A is false, it is not executed at all. Because there is no expression to change the value of X, the DO-group will not be executed again.
DO X = 1; |
This DO-group executes a single time only, regardless of the value of X.
8.1.5 DO REPEAT
The DO REPEAT statement executes a DO-group repetitively for different values of a control variable. The control variable is assigned a start value that is used on the first iteration of the group. The REPEAT expression is evaluated before each subsequent iteration, and its result is assigned to the control variable. A WHILE clause can also be included. If it is included, the WHILE expression is evaluated before each iteration (including the first), but after the control variable has been assigned. The format of the DO REPEAT statement is:
variable
A reference to a control variable. The control variable can be any scalar variable.start-value
An expression specifying the initial value to be given to the control variable. The evaluation of this expression must yield a value that is valid for assignment to the control variable.expression
An expression giving the value to be assigned to the control variable on reiterations of the DO REPEAT group. The expression is evaluated before each iteration after the first. Evaluation of this expression must yield a result that is valid for assignment to the control variable.WHILE (test-expression)
An option specifying a condition that controls the termination of the DO REPEAT group. The DO REPEAT group continues while the condition is true. The specified test expression must yield a scalar value. If any bit of the value is 1, then the test expression is true; otherwise, the test expression is false. The test expression must be enclosed in parentheses.This expression is evaluated each time control reaches the DO statement; the test expression must have a true value in order for the DO-group to be executed. Otherwise, control passes to the next executable statement following the END statement that terminates the DO-group.
See Section 8.1.4 for a discussion of this option when used with the controlled DO statement.
UNTIL (test-expression)
An option specifying a condition that further controls the termination of the DO REPEAT group. The DO REPEAT group continues until the condition is true. The specified test expression must yield a scalar value. If any bit in the value is 1, then the test expression is true; otherwise, the test expression is false. The test expression must be enclosed in parentheses.This expression is evaluated after the first execution of the DO-group; the test expression must have a true value in order for the DO-group to be executed a second time. Otherwise, control passes to the next executable statement following the END statement that terminates the DO-group.
If the WHILE and UNTIL options are omitted, the DO REPEAT statement specifies no means for terminating the group; the execution of the group must be terminated by a statement or condition occurring within the group. |
A DO REPEAT group is executed by the following steps:
This example has the same effect as the following controlled DO statement:
DO I = 1 TO 100 BY 2; |
The most common use of the DO REPEAT statement is in the manipulation of lists. For example:
DO P = LIST_HEAD REPEAT (P->LIST.NEXT) WHILE ( P ^= NULL(:POINTER:) ); |
In this example, the pointer P is initialized with the value of the pointer control variable LIST_HEAD. The DO-group is then executed with this value of P. The REPEAT option specifies that each time control reaches the DO statement after the first execution of the DO-group, P is to be set to the value of LIST.NEXT in the structure currently pointed to by P.
DECLARE INPUT_STRING CHARACTER(80); DO LOOP; GET LIST(STRING); IF STRING = 'END' THEN LEAVE; PUT LIST(STRING); END; |
BEGIN [OPTIONS (option,...)]; |
A begin block is a sequence of statements headed by a BEGIN statement and terminated by an END statement. A begin block can be used wherever a single executable statement is valid, for instance, in an ON-unit. The statements in a begin block can be any PL/I statements, and begin blocks can contain DO-groups, DECLARE statements, procedures, and other (nested) begin blocks.
A begin block provides a convenient way to localize variables. Variables declared as internal within a begin block are not allocated storage until the block is activated. When the block terminates, storage for internal automatic variables is released. A begin block terminates in the following situations:
A begin block differs from a DO-group chiefly in its ability to localize variables. Variables declared within DO-groups are not localized to the group (unless the group contains a begin block or procedure that declares internal variables). Begin blocks are preferable when you want to restrict the scope of variables. Furthermore, there are some cases (such as ON-units) in which DO-groups cannot be used. Otherwise, DO-groups are often more efficient, because they do not have the overhead associated with block activation. In general, you should use a DO-group instead of a begin block unless there are declarations present or you require multiple statements in an ON-unit.
A begin block can designate a series of statements to be executed depending on the success or failure of a test in an IF statement. For example:
IF A = B THEN BEGIN ; . . . END; |
A begin block also provides the only way to denote a series of statements to be executed when an ON condition is signaled. For example:
ON ERROR BEGIN; [statement ...] END; |
See Section 1.4 for more details on blocks.
8.3 END Statement
The END statement marks the end of the block or group headed by the most recent BEGIN, DO, SELECT, or PROCEDURE statement.
The format of the END statement is:
END [label-reference]; |
label-reference
A reference to the unsubscripted label on the PROCEDURE, BEGIN, SELECT, or DO statement for which the specified END statement is the termination. A label is not required. If specified, the label reference must match only one label, which is the label of the most recent BEGIN, DO, SELECT, or PROCEDURE statement that is not already matched with an END statement. If the label reference is omitted, the most recent textual, non-terminated PROC, BEGIN, SELECT, or DO statement is matched by default.
Note that a procedure declared with the RETURNS option must execute a RETURN statement before it encounters the END statement marking the end of the procedure.
When the END statement is encountered, one of the following actions is performed, depending on the type of block or group that it terminates:
The IF statement tests an expression and performs a 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 expression that yields a scalar bit-string value. If any bit of the value is 1, then the test expression is true; otherwise, the test expression is false.action
Any of the following:
- Any unlabeled statement except the nonexecutable statements: DECLARE, END, FORMAT, or PROCEDURE
- An unlabeled DO-group or begin block
The IF statement evaluates the test expression. If the expression is true, the action specified following the keyword THEN is executed. Otherwise, the action, if any, specified following the ELSE keyword is executed.
IF A < B THEN BEGIN; |
The begin block following this statement is executed if the value of the variable A is less than the value of the variable B.
IF ^SUCCESS THEN CALL PRINT_ERROR; ELSE CALL PRINT_SUCCESS; |
The IF statement defines the action to be taken if the variable SUCCESS
has a false value (the THEN clause) and the action to be taken
otherwise (the ELSE clause).
8.4.1 Nested IF Statements
The action specified in a THEN or an ELSE clause can be another IF statement.
An ELSE clause is matched with the nearest preceding IF/THEN that is not itself matched with a preceding ELSE. For example:
IF ABC THEN IF XYZ THEN GOTO GBH; ELSE GOTO THESTORE; ELSE GOTO HOME; |
In this example, the first ELSE clause is executed if ABC is true and XYZ is false. The second ELSE clause is executed if ABC is false.
In some cases, proper matching of IF and ELSE can require a null statement (a semicolon) as the target of an ELSE. For example:
IF ABC THEN IF XYZ THEN GOTO HOME; ELSE; ELSE GOTO THESTORE; |
In this example, the ELSE GOTO THESTORE statement is executed if ABC is
false.
8.5 SELECT Statement
The SELECT statement tests a series of expressions and performs a specified action depending on the result of the test. The statement has two forms: in the first form, the expressions in a WHEN clause are tested for truth; in the second form, the expressions in a WHEN clause are compared to see if any have the same value as another specified expression called the select expression. Any of the expressions can be, but need not be, constants. An optional OTHERWISE clause is available to name an action to be performed if none of the preceding expressions have satisfied the condition specified.
The two forms of the SELECT statement and the OTHERWISE clause are described in more detail below.
The general form of the SELECT statement is:
select-expression
An expression that can be evaluated to any type of value.case-expression,...
One or more expressions to be tested, evaluating to bit-string values, or, if a select expression is used, with values that will be compared to the select expression's value.action
Any statement (including a null statement, another SELECT statement, a DO-group, or a BEGIN-END block) except a DECLARE, END, FORMAT, or PROCEDURE statement.
Depending on whether you use a select expression or not, SELECT has two different forms, which are explained in detail below.
SELECT Without a Select Expression
The first form of the SELECT statement omits the select expression. In this form, the expressions in a WHEN clause are evaluated, and a specified action is performed if the result of any test is true (or, if ALL is specified, the results of all tests are true); an expression is true if it evaluates to a bit string containing any bit with the value of '1'B. In the usual case, the test for truth results in a bit string containing one bit: '1'B for true or '0'B for false.
When the keyword ANY (the default) appears in the WHEN clause, then if any one of the expressions evaluates to true the corresponding action is performed. No further expressions in that WHEN clause or in subsequent WHEN clauses are evaluated (and thus the expressions need not have unique values), and no subsequent actions are performed.
The WHEN clauses are checked in the order listed. However, the expressions within one WHEN clause might be evaluated in any order, and not all these expressions are necessarily evaluated. As soon as any expression is found true, subsequent expressions are not evaluated.
If the keyword ALL appears in the WHEN clause, the action is performed only if all expressions in that WHEN clause evaluate to true. Once one action is performed, no subsequent WHEN clauses are evaluated and no subsequent actions are performed. If any expression in the WHEN clause does not result in a true value, no further expressions in that clause are evaluated and the action is not performed.
Following is an example of the first form of SELECT:
SELECT; WHEN ANY (A=10,A=20,A=30) B=B+1; WHEN (A=50) B=B+2; WHEN (A=60) B=B+3; WHEN (A=70) B=B+4; WHEN (A=80) B=B+5; WHEN (A=90) B=B+6; WHEN ALL (A>90,A<500) B=B+10; OTHERWISE B=B+C; END; |
The SELECT statement defines the action to be taken if the variable A has any of the values specified in the WHEN clauses (or, in the case of the WHEN ALL clause, if A is both greater than 90 and less than 500). If none of the WHEN clauses is true, the action specified in the OTHERWISE clause (B=B+C) is performed.
SELECT With a Select Expression
The second form of the SELECT statement has a select expression after the keyword SELECT. This form of the SELECT statement evaluates expressions in the WHEN clauses and then compares their values to the value of the select expression (instead of testing the expressions for truth or falsity, as in the first form of SELECT). It performs a specified action if any expression has the same value as the select expression (or, if ALL is used, all expressions have the same value as the select expression). In this form of the SELECT statement, as in the previous form, the expressions in a WHEN clause might be evaluated in any order, and not all the expressions are necessarily evaluated.
Following is an example of the second form of SELECT:
SELECT(A); WHEN (50) C=C+1; WHEN ANY (60,61,62,B+C) C=C+2; WHEN ALL (70,D) C=C+3; OTHERWISE C=C+D; END; |
The SELECT statement defines the action to be taken if the select
expression (A in the example) evaluates to any or all of the values of
the expressions following a WHEN clause. The first action (the
assignment statement C=C+1) will be performed if A has a current value
of 50. In that case, none of the subsequent WHEN clauses will be
evaluated. The second WHEN clause includes the ANY keyword, and so the
second action will be performed if A evaluates to or equals 60 or 61 or
62 or the sum of B and C. If neither the first nor the second action is
performed, the third WHEN clause's expressions are tested. The third
WHEN clause includes the ALL keyword, so the third action will be
performed only if A equals both 70 and D. If none of the WHEN clauses
causes an action to be performed, then the action in the OTHERWISE
clause (the assignment statement C=C+D) will be performed.
8.5.2 OTHERWISE Clause
If none of the WHEN clauses causes the corresponding action to be performed, the action specified in the optional OTHERWISE clause is performed; but if the OTHERWISE clause is omitted, an ERROR condition is signalled. OTHERWISE can be followed by a semicolon (a null statement) to cause execution to continue and avoid an ERROR condition when you do not want to specify an action after OTHERWISE. For example:
OTHERWISE; |
After an action is performed following a WHEN or OTHERWISE clause,
control passes to the next executable statement following the END
statement that terminates the SELECT statement, unless normal flow is
altered within the action.
8.5.3 Nested SELECT Statements
Note that the action specified in a WHEN or OTHERWISE clause can be another SELECT statement, resulting in nested SELECT statements, as in the following example:
SELECT; WHEN (condition A) SELECT; WHEN (condition A1) statement 1; WHEN (condition A2) statement 2; END; WHEN (condition B) SELECT; WHEN (condition B1) statement 3; WHEN (condition B2) statement 4; OTHERWISE statement 5; END; OTHERWISE statement 6; END; |
In this example, statement 1 is executed when both condition A and condition A1 are true. Statement 2 is executed when both condition A and condition A2 are true and A1 is false. If A is true but neither A1 nor A2 is true, an ERROR condition is reported because no OTHERWISE clause exists within this SELECT statement.
If condition A is false, condition B is checked. If B is true but B1 and B2 are both false, statement 5 (in the corresponding OTHERWISE clause) is executed. If conditions A and B are both false, statement 6 (in the outermost OTHERWISE clause) is executed.
If you want to avoid the possibility that execution could be stopped by an ERROR condition, which occurs in this example if condition A is true and A1 and A2 are false, you can put in an OTHERWISE clause with a null statement (a semicolon) as its action, which would cause control to pass to the first executable statement following the end of the outermost SELECT statement.
An END statement must terminate each SELECT statement.
8.6 GOTO Statement
The GOTO statement causes control to be transferred to a labeled statement in the current or any outer procedure.
The format of the GOTO statement is:
⎧GOTO ⎫ ⎨ ⎬ label-reference [OTHERWISE]; ⎩GO TO⎭
label-reference
A label constant or an expression that, when evaluated, yields a label value. A label value denotes a statement in the program.The specified label canot be the label of an FORMAT or PROCEDURE statement. The label reference specified in a GOTO statement can be any of the following:
- An unsubscripted label constant. For example:
GOTO ALPHA; . . . ALPHA:- A subscripted label constant, for which the subscript is specified with an integer constant or a variable expression. For example:
GOTO PROCESS(1); . . . PROCESS(1):- A label variable that, when evaluated, yields a label value. For example:
DECLARE PROCESS LABEL VARIABLE; . . . PROCESS = BILLING; . . . GOTO PROCESS;- A subscripted label variable that, when evaluated, yields a label value. For example:
DECLARE X(5) LABEL; X(1) = NEXT; GOTO X(1);In the case of a label variable, the resulting label value must designate an existing block activation. (Similarly, a label constant must designate an existing block activation.) If the designated block activation is the current block activation, the GOTO statement causes a local GOTO. No special processing occurs.
OTHERWISE
This option can be used only when the label-reference is a subscripted label with a variable subscript. If present in any other case, it will be reported as an error.If the variable subscript is out of range and the OTHERWISE option is present, the statement following the GOTO will be executed next. If the OTHERWISE option is not specified and the subscript of the last label is not an asterisk (*), the subscript is reported out of range at run-time and the process will be terminated.
If the specified label value is not in the current block, the GOTO statement is considered a nonlocal GOTO. The following can occur:
The following example shows the use of the GOTO statement:
RESTART:; . . . BEGIN; ON ERROR GOTO RESTART; . . . END; |
The GOTO statement provides a transfer address for the current procedure when the ERROR condition is signaled.
DECLARE PROCESS(5) LABEL VARIABLE; . . . GOTO PROCESS(2); |
The GOTO statement evaluates the label reference and transfers control to the label constant corresponding to the second element of the array PROCESS. PROCESS consists of label variables.
The following restrictions apply to the use of labels and label data:
For more information on labels, see Section 3.7.
8.7 LEAVE Statement
The LEAVE statement causes control to be transferred out of the immediately containing DO-group or out of the containing DO-group whose label is specified with the statement.
The format of the LEAVE statement is:
LEAVE [label-reference]; |
label-reference
A reference to a label on a DO statement that heads a containing DO-group. The label reference can be a label constant or a subscripted label constant for which the subscript is specified with an integer constant. The label reference cannot be a label variable, nor can it be a subscripted label constant for which the subscript is specified with a variable.
On execution, a LEAVE statement with no label reference causes control to be transferred to the first statement following the END statement that terminates the immediately containing DO-group. If the LEAVE statement has a label, control is passed to the first executable statement following the END statement for the corresponding label indicated in the LEAVE statement. Thus, the LEAVE statement provides an alternative means of terminating execution of a DO-group. In the case of a LEAVE statement with a label reference, several nested DO-groups can be terminated as control transfers outside the referenced DO-group.
The following restrictions apply to the use of the LEAVE statement:
The following example shows a LEAVE statement without a label reference:
DO I = 1 TO 100; . . . IF COMMAND = 'QUIT' THEN LEAVE; . . . END; PUT LIST ('Job finished'); |
In this example, the LEAVE statement transfers control directly to the PUT statement if the condition in the IF statement is satisfied.
The next example shows a LEAVE statement with a label reference:
LOOP1: DO WHILE (MORE); . . . LOOP2: DO I = 1 TO 12; . . . IF QUAN(I) > 150 THEN LEAVE LOOP1; END; /* Loop 2 */ . . . END; /* Loop 1 */ |
In this example, the LEAVE statement transfers control to the first statement beyond the END statement that terminates LOOP1.
The following examples show some invalid uses of the LEAVE statement:
LEAVE; /* LEAVE statement must be in */ /* DO-group */ DO; BEGIN; LEAVE; /* LEAVE statement must be in */ END; /* same block as DO statement */ END; ON ENDFILE(SYSIN) LEAVE; /* ON-unit is separate block */ DECLARE LABVAR LABEL VARIABLE; LABVAR = LOOP; LOOP: DO I = 1 TO 10; LEAVE LABVAR; /* Label reference cannot be a variable */ END; LAB(1): DO; LAB(2): DO; I = 1; LEAVE LAB(I); /* Subscript must be a constant */ END; END; |
STOP; |
The STOP statement signals the FINISH condition, and closes all open
files. If the main procedure has the RETURNS attribute, no return value
is obtainable.
8.9 Null Statement
The null statement performs no action. Its format is:
; |
The null statement usually serves as the target statement of a THEN or ELSE clause in an IF statement, as the target of a WHEN or OTHERWISE clause in a SELECT statement, or as an action in an ON-unit. The following examples illustrate these uses.
IF A < B THEN GOTO COMPUTE; ELSE ; |
In this example, no action takes place if A is greater than or equal to B; execution continues at the statement following ELSE ;. A construction of this type may be necessary when IF statements are nested (see Section 8.4.1).
SELECT; WHEN (condition A,B,C) GOTO FILE_READ; WHEN (condition D,E) GOTO UPDATE; OTHERWISE; END; |
In this example, control is passed to the next executable statement after END if conditions A, B, C, D, and E are not true.
ON ENDPAGE(SYSPRINT); |
In this example, no action takes place upon execution of the ON-unit; the I/O operation that caused the ENDPAGE condition continues.
The null statement can also be used to declare two labels for the same executable statement, as in the following example:
LABEL1: ; LABEL2: statement ... |
A PL/I condition is any occurrence that causes the interruption of a program and a signal. When a condition is signaled, PL/I initiates a search for a user-written program unit called an ON-unit to handle the condition.
An ON condition is any one of several named conditions whose occurrence during the execution of a program interrupts the program. When a condition occurs or is signaled, a statement or sequence of statements, called an ON-unit, is executed. The SYSTEM option can be specified in the ON statement, causing the default system condition handling to be executed.
The following list of condition handling topics are discussed in subsequent sections.
The ON statement defines the action to be taken when a specific condition or conditions are signaled during the execution of a program. The ON statement is an executable statement. It must be executed before the statement that signals the specified condition.
The format of the ON statement is:
⎧on-unit⎫ ON condition-name,... [SNAP]⎨ ⎬ ⎩SYSTEM ⎭
condition-name,...
The name or names of the specific conditions for which an ON-unit or the SYSTEM option is specified. There is a keyword name associated with each condition. Successive keyword names must be separated by commas. The conditions are summarized in Table 8-1; each condition is described in an individual entry in this manual.SNAP
This option invokes the debugger and causes a traceback of all active routines to be displayed when the condition is raised. If you use the SNAP option, you should specify the debug qualifier on both the PLI compiler and the linker in order to have all the debugger symbol table information accessible.on-unit
The action to be taken when the specified condition or conditions are signaled. An ON-unit can be:
- Any single, unlabeled statement except DECLARE, DO, END, FORMAT, IF, ON, PROCEDURE, RETURN, or SELECT.
- An unlabeled begin block.
- A null statement (a semicolon alone), which causes program execution to continue as if the condition had been handled.
Only the most recent ON-unit established for a given condition can be active. If two successive ON statements are executed for the same condition, the second ON statement nullifies the first.
If no ON-unit is established for a particular condition, the condition ERROR is signaled. If no ON-unit is established for ERROR condition, the default system ON-unit is executed. See Section 8.10.5.
SYSTEM
This option invokes the default system condition handling for the specified condition, overriding any existing ON-unit for the condition. See Section 8.10.6.
The SIGNAL statement causes a specified condition to be signaled, which causes the system to search for and execute an ON-unit to handle the condition. See Section 8.10.8.
The format of the SIGNAL statement is:
SIGNAL condition-name; |
condition-name
The name of the condition to be signaled. It must be one of the keywords listed in Table 8-1. Each of these conditions is described in its own section.
Most conditions occur as a result of a hardware trap or fault, or as a
result of signaling by PL/I run-time procedures. You can use the SIGNAL
statement within a program as a general-purpose communication
technique. In particular, CONDITION conditions let
you signal unique user-defined condition values.
8.10.3 REVERT Statement
The REVERT statement cancels an ON-unit established for a specified condition or conditions in the current block only.
The format of the REVERT statement is as follows:
REVERT condition-name,...; |
condition-name,...
The keyword name or names associated with the condition or conditions for which the ON-unit is to be reverted. Successive names must be separated by commas. The valid condition names are the same as for the ON statement.
If no ON-unit is established for a specified condition for the current block, the REVERT statement has no effect.
The REVERT statement does not cancel all ON-units that may be active at the same time it is executed (see Section 8.10.8), only a handler in the current block. If you want to temporarily block activation of all user-written ON-units for a condition, define an ON-unit with the SYSTEM option for the given condition.
An ON-unit can be re-established after execution of a REVERT statement by subsequently executing an ON statement.
When you run the program, the following conditions result:
Handled condition = 1156 Handled ZERODIVIDE Handled condition = 1156 Handled ZERODIVIDE PL/I ERROR condition. |
Most, but not all, ON conditions are associated with errors. The types of conditions for which you can establish ON-units are grouped in the following categories.
Table 8-1 summarizes ON conditions. Each condition is described individually in the sections that follow.
Condition Name | Function |
---|---|
AREA | Handles a condition that occurs during an operation on an area |
BEGINPAGE | Handles beginning-of-page for a specified file with PRINT attribute |
CONDITION | Handles programmer-defined conditions |
CONVERSION | Handles data conversion errors |
ENDFILE | Handles end-of-file for a specified file |
ENDPAGE | Handles end-of-page for a specified file with PRINT attribute |
ERROR | Handles miscellaneous error conditions and conditions for which no specific ON-unit exists |
FINISH | Handles program exit when the main procedure executes a RETURN statement, when any block executes a STOP statement, or when the program exits due to an error that is not handled by an ON-unit |
FIXEDOVERFLOW | Handles fixed-point decimal and integer overflow exception conditions |
KEY | Handles any error involving the key during keyed access to a specified file |
RECORD | Handles any error involving an incorrect record length |
OVERFLOW | Handles floating-point overflow exception conditions |
STORAGE | Handles a condition that occurs during allocation of a based variable other than in an area |
TRANSMIT | Handles operating system or programmer-specified condition values |
UNDEFINEDFILE | Handles any errors in opening a specified file |
UNDERFLOW | Handles floating-point underflow exception conditions |
ZERODIVIDE | Handles divide-by-zero exception conditions |
On a normal return from an AREA condition,
if it resulted from an allocation, the allocation is retried. Otherwise,
execution continues from the point at which the condition was raised.
8.10.4.2 BEGINPAGE Condition Name
The BEGINPAGE condition name can be specified in an ON, SIGNAL, or REVERT statement to designate an beginning-of-page condition or ON-unit for a specific print file.
The format of the BEGINPAGE condition name is:
BEGINPAGE (file-reference) |
file-reference
The name of the file constant or file variable for which the BEGINPAGE ON-unit is to be established. If the name of a file variable is specified, the variable must be resolved to the name of a file constant when the condition is signaled. The file must have the PRINT attribute.
PL/I signals the BEGINPAGE condition when a PUT statement attempts to output the first line of for an output page. When the BEGINPAGE condition is signaled, the current line number associated with the file is 1. An BEGINPAGE ON-unit allows you to provide special processing before output continues on a new page. For example:
ON BEGINPAGE (PRINTFILE) BEGIN; PUT FILE (PRINTFILE) PAGE; PUT FILE (PRINTFILE) LIST(HEADER_LINE); PUT FILE (PRINTFILE) SKIP(2); END; |
The ON-unit for the BEGINPAGE condition for the file PRINTFILE outputs a page eject and a header line for the new output page.
To cause PL/I to ignore the BEGINPAGE condition when a large amount of output is written to a terminal, you can use the following ON-unit, that contains only the null statement:
ON BEGINPAGE(SYSPRINT); |
This is optional because PL/I ignores the BEGINPAGE condition on SYSPRINT by default.
An ON-unit established to handle beginning-of-page conditions can reference the ONFILE built-in function to determine the name of the file constant for which the condition was signaled.
If the ON-unit does not transfer control elsewhere in the program, the line number is set to 1 and the program continues execution of the PUT statement. If the BEGINPAGE condition was signaled during data transmission, the data is written on the new current line. If the BEGINPAGE condition was caused by a LINE or a SKIP option on the PUT statement, then the action specified by these options is ignored on return.
If the BEGINPAGE condition is signaled during file processing, PL/I
starts output on a new page and continues processing. An exception is
made for SYSPRINT which is to take no action. If the BEGINPAGE condition
is signaled as a result of a SIGNAL statement, the statement following
the SIGNAL statement is executed and no page is output by default.
8.10.4.3 CONDITION Condition Name
The CONDITION condition name is used for ON-units to handle programmer-defined conditions. There is no way to distinguish between multiple programmer-defined conditions if they are specified in the same ON statement.
The format of the CONDITION condition name is:
CONDITION (cond-name) |
cond-name
A name declared with the CONDITION attribute.
The CONVERSION condition name can be specified in an ON, SIGNAL, or REVERT statement to designate a CONVERSION condition or ON-unit.
PL/I signals the CONVERSION condition when the source character data in a conversion to bit-string or arithmetic data contains characters that are not valid in the specified context. In particular, the CONVERSION condition is raised when a character string is being converted and one of the following conditions is true:
The CONVERSION condition can be raised either by a non-I/O conversion, such as an explicit conversion using a built-in function or an implicit conversion generated by the compiler, or by an I/O conversion in a GET statement. For example, A = BIT('1014') would cause the CONVERSION condition to be raised, because 4 is not a valid binary digit. Likewise, a GET statement with an arithmetic target would also cause the CONVERSION condition to be raised if the characters '12K45' appeared in the input field, because 'K' is not a valid numeric character.
You can use the ONSOURCE built-in function and pseudo-variables inside an ON CONVERSION ON-unit. The ONSOURCE built-in function returns the source string that caused the CONVERSION condition to be raised. You can use the ONSOURCE pseudo-variable to change the value of the conversion.
If the CONVERSION condition was raised during a conversion required by the GET statement, the ONFILE built-in function returns the name of the file constant inside the CONVERSION ON-unit. If the CONVERSION condition was not raised during a conversion required by the GET statement, the ONFILE built-in function returns a null string.
A normal return from a CONVERSION condition will cause the conversion to be reattempted if the ONSOURCE pseudo-variable has had a new value assigned. If the ONSOURCE value has not been modified, the ERROR condition is raised instead.
The target of the conversion is undefined when the CONVERSION condition is raised.
The retry attempted on a normal return is for the single field that was in error. Attempts to assign a string containing, for example, a comma list of values will not be used for successive data items in a GET statement.
The actual value modified by the ONSOURCE pseudo-variable is
a temporary value that is discarded once the conversion is complete, or
the control flow cannot return to the point of the error. This means
that invalid data stored in a character string variable will cause the
CONVERSION condition to be raised each time the value is converted, not
just the first time the conversion is attempted, regardless of
modifications to the ONSOURCE pseudo-variable inside the
CONVERSION ON-unit.
8.10.4.5 ENDFILE Condition Name
The ENDFILE condition name can be specified in an ON, SIGNAL, or REVERT statement to designate an end-of-file condition or ON-unit for a specific file.
PL/I signals the ENDFILE condition when a GET or READ statement attempts an input operation on a file or device after the last data item has been input.
The format of the ENDFILE condition name is:
ENDFILE (file-reference) |
file-reference
The name of a file constant or file variable for which the ENDFILE ON-unit is established. If the name of a file variable is specified, the variable must be resolved to the name of a file constant when the condition is signaled.
An ENDFILE ON-unit can be established for any input file. For any particular file, the meaning of the end-of-file condition depends on the type of device. For example, end-of-file is signaled for a terminal device when the Ctrl/Z character is read.
For a stream file, an end-of-file condition is signaled whenever a GET statement attempts to access an empty file or attempts to access a file whose last input field has been read.
For a record file, an end-of-file condition is signaled when a READ statement is executed with the file at the end-of-file position or when a read is attempted beyond the last record in the file. For example:
ON ENDFILE (RECEIPTS) EOF = '1'B; EOF = '0'B; OPEN FILE (RECEIPTS) RECORD SEQUENTIAL; READ FILE (RECEIPTS) INTO (RECORD); DO WHILE (^EOF); . . . READ FILE (RECEIPTS) INTO (RECORD); END; |
In this example, the ON statement establishes the default action to be taken when the last record in the input file has been processed: the flag EOF is set to '1'B.
An ON-unit established to handle end-of-file conditions can reference the ONFILE built-in function to determine the name of the file constant for which the condition was signaled.
If the ON-unit for the ENDFILE condition does not transfer control elsewhere in the program, control returns to the statement following the GET or READ statement that caused the condition to be signaled.
When the ENDFILE condition is signaled, it remains in effect until the file is closed. Subsequent GET or READ statements for the file cause the ENDFILE condition to be signaled repeatedly.
ENDPAGE (file-reference) |
file-reference
The name of the file constant or file variable for which the ENDPAGE ON-unit is to be established. If the name of a file variable is specified, the variable must be resolved to the name of a file constant when the condition is signaled. The file must have the PRINT attribute.
PL/I signals the ENDPAGE condition when a PUT statement attempts to output a line beyond the last line specified for an output page. When the ENDPAGE condition is signaled, the current line number associated with the file is the page size plus 1. An ENDPAGE ON-unit allows you to provide special processing before output continues on a new page. For example:
ON ENDPAGE (PRINTFILE) BEGIN; PUT FILE (PRINTFILE) PAGE; PUT FILE (PRINTFILE) LIST(HEADER_LINE); PUT FILE (PRINTFILE) SKIP(2); END; |
The ON-unit for the ENDPAGE condition for the file PRINTFILE outputs a page eject and a header line for the new output page.
To cause PL/I to ignore the ENDPAGE condition when a large amount of output is written to a terminal, you can use the following ON-unit, that contains only the null statement:
ON ENDPAGE(SYSPRINT); |
This is optional because PL/I ignores the ENDPAGE condition on SYSPRINT by default. You cannot catch the ENDPAGE(SYSPRINT) condition.
An ON-unit established to handle end-of-page conditions can reference the ONFILE built-in function to determine the name of the file constant for which the condition was signaled.
If the ON-unit does not transfer control elsewhere in the program, the line number is set to 1 and the program continues execution of the PUT statement. If the ENDPAGE condition was signaled during data transmission, the data is written on the new current line. If the ENDPAGE condition was caused by a LINE or a SKIP option on the PUT statement, then the action specified by these options is ignored on return.
An ENDPAGE condition can occur only once per page of output. If the ON-unit specified does not specify a new page, then execution and output continue. The current line number can increase indefinitely; PL/I does not signal the ENDPAGE condition again. However, if a LINE option on a PUT statement specifies a line number that is less than that of the current line, a new page is output and the current line is set to 1.
If the ENDPAGE condition is signaled during file processing, PL/I
starts output on a new page and continues processing. An exception is
made for SYSPRINT which is to take no action. If the ENDPAGE condition
is signaled as a result of a SIGNAL statement, the statement following
the SIGNAL statement is executed and no page is output by default.
8.10.4.7 ERROR Condition Name
The ERROR condition name can be specified in an ON, SIGNAL, or REVERT statement to designate an error condition or ON-unit.
PL/I signals the ERROR condition in the following contexts:
When any condition is signaled for which no specific ON-unit is established, the default PL/I action for all conditions except as noted below is to signal the ERROR condition.
When any ON-unit is executed, the ON-unit can reference the built-in function ONCODE. This function returns the numeric condition value associated with the specific error that signaled the condition.
If an ERROR ON-unit does not handle the condition, the program is
terminated.
8.10.4.8 FINISH Condition Name
The FINISH condition name can be specified in an ON, SIGNAL, or REVERT statement to designate a FINISH condition or a FINISH ON-unit.
PL/I signals the FINISH condition in the following contexts:
If a FINISH ON-unit that executes as a result of a SIGNAL FINISH
statement does not execute a nonlocal GOTO statement, control returns
to the statement following SIGNAL FINISH. If the FINISH ON-unit
executes as a result of any of the other three causes listed above, the
program terminates.
8.10.4.9 FIXEDOVERFLOW Condition Name
The FIXEDOVERFLOW condition name can be specified in an ON, SIGNAL, or REVERT statement to designate a fixed overflow condition or ON-unit.
PL/I signals the FIXEDOVERFLOW condition in the following circumstances:
The value resulting from an operation that causes this condition is undefined.
Value of ONCODE
An ON-unit
that receives control when FIXEDOVERFLOW is signaled can reference the
ONCODE built-in function to determine which condition is actually
signaled.
If the ON-unit does not transfer control elsewhere in the program,
control returns to the point at which the condition was signaled.
8.10.4.10 KEY Condition Name
The KEY condition name can be specified in an ON, SIGNAL, or REVERT statement to designate a key error condition or ON-unit for a specific file.
The format of the KEY condition name is:
KEY (file-reference) |
file-reference
A reference to the file constant or file variable for which the ON-unit is to be established. If the name of a file variable is specified, the variable must be resolved to the name of a file constant when the condition is signaled.
PL/I signals the KEY condition during an operation on a keyed file when an error occurs in processing a key. Some examples of errors for which PL/I signals the KEY condition follow:
An ON-unit established to handle the KEY condition can obtain information about the condition by invoking the following built-in functions:
If the ON-unit does not execute a nonlocal GOTO, control returns to the
statement immediately following the statement that caused the KEY
condition.
8.10.4.11 OVERFLOW Condition Name
The OVERFLOW condition name can be specified in an ON, REVERT, or SIGNAL statement to designate an ON condition or ON-unit for floating-point overflow conditions.
The exponent of a floating-point value is adjusted, if possible, to represent the value with the specified precision. That is, the precision is maximized and the exponent is minimized.
PL/I signals the OVERFLOW condition when the result of an arithmetic operation on a floating-point value exceeds the maximum exponent size allowed by the hardware.
The value resulting from an operation that causes this condition is undefined.
Control returns to the point of the interruption.
8.10.4.12 RECORD Condition Name
The RECORD condition is raised during a READ, REWRITE, or WRITE statement when a record is truncated because the internal and external record lengths are incompatible.
The SIZE condition is raised when high-order (that is, leftmost) significant binary or decimal digits are lost in an attempted assignment to a variable or an intermediate result or in an input/output operation. This loss can result from a conversion involving different data types, different bases, different scales, or different precisions.
The STORAGE condition is raised when an error has been detected during
allocation of a based variable other than to
an area. The ONCODE value is the error returned bythe operating system. The most
common cause is the exhaustion of virtual memory; another cause might
be an erroneous attempt to allocate a negative amount of storage.
8.10.4.15 TRANSMIT Condition Name
The TRANSMIT condition is raised when an I/O error occurs during a GET, PUT, READ, REWRITE, WRITE, or DELETE statement.
The UNDEFINEDFILE condition name can be specified in an ON, SIGNAL, or REVERT statement to designate an undefined file condition or ON-unit for a specific file.
The format of the UNDEFINEDFILE condition name is:
⎧UNDEFINEDFILE⎫ ⎨ ⎬ declaration [,declaration,...]; ⎩UNDF ⎭
file-reference
A reference to a file constant or file variable for which the ON-unit is established.If the name of a file variable is specified, the variable must be resolved to the name of a file constant when the condition is signaled.
PL/I signals the UNDEFINEDFILE condition when a file cannot be opened. Following are some examples of errors that cause the UNDEFINEDFILE condition:
The UNDEFINEDFILE condition lets you establish an ON-unit to provide processing when a file cannot be opened, for example, to provide a default file if no file is specified at run time.
X: PROCEDURE (FILENAME); DECLARE FILENAME CHARACTER (128) VARYING; DECLARE INPUT_FILE FILE INPUT; ON UNDEFINEDFILE (INPUT_FILE) OPEN FILE (INPUT_FILE) TITLE ('/dev/stdin'); OPEN FILE (INPUT_FILE) TITLE (FILENAME); |
In this example, the procedure X expects a file specification string to be passed as an argument. If no argument is passed, or if the argument is not a valid file specification, the OPEN statement fails. The UNDEFINEDFILE ON-unit signals the ERROR condition.
An ON-unit established to handle the UNDEFINEDFILE condition can obtain information about the condition by invoking the following built-in functions:
The action taken on a normal return from the UNDEFINEDFILE condition depends on whether the file was opened explicitly or implicitly.
If the UNDEFINEDFILE condition was signaled following an explicit OPEN statement for a file, then the normal action following the ON-unit execution is for the program to continue. If the ON-unit does not transfer control elsewhere in the program, control returns to the statement following the OPEN statement that caused the condition to be signaled.
If the UNDEFINEDFILE condition was signaled during an implicit open attempt, the run-time system tests the state of the file. If the file is not open, the ERROR condition is signaled. If the file was opened by the ON-unit, execution of the I/O statement continues.
If an ON-unit receives control when an explicit OPEN results in the
UNDEFINEDFILE condition, and the ON-unit does not handle the condition
by opening the file or by transferring control elsewhere in the
program, control returns to the statement following the OPEN. Then, if
an attempt is made to access the file with an I/O statement, the
UNDEFINEDFILE condition is signaled again when PL/I attempts the
implicit open of the file. This time, PL/I signals the ERROR condition
on completion of the ON-unit.
8.10.4.17 UNDERFLOW Condition Name
PL/I signals the UNDERFLOW condition (which can be abbreviated UFL) when the absolute value of the result of an arithmetic operation on a floating-point value is smaller than the minimum value that can be represented by the hardware.
On completion of the ON-unit, control is returned to the point of the interrupt. Continued execution is unpredictable.
The value resulting from an operation that causes the UNDERFLOW
condition is undefined. (The value would be set to zero only if
UNDERFLOW were not specified in the procedure options.)
8.10.4.18 ZERODIVIDE Condition Name
The ZERODIVIDE condition name can be specified in an ON, REVERT, or SIGNAL statement to designate a divide-by-zero condition or ON-unit.
PL/I signals the ZERODIVIDE condition when the divisor in a division
operation has a value of zero. The value resulting from such an
operation is undefined.
8.10.5 Default PL/I ON-Unit
PL/I defines a default ON-unit for the procedure that is designated as the main procedure. This is why there must be exactly one procedure with OPTIONS(MAIN) specified in any executable image. This default ON-unit performs the following actions depending on the condition signaled. Note that the severity of the signal is determined by the low three bits of the condition code.
ON ENDFILE (ACCOUNTS) GOTO CLOSE_FILES; |
This ON statement defines an ON-unit for an ENDFILE (end-of-file) condition in the file specified by the name ACCOUNTS. The ON-unit consists of a single statement, a GOTO statement.
After an ON-unit is established by an ON statement for a condition, it remains in effect for the activation of the current block and all its dynamically descendant blocks, unless one of the following occurs:
ON OVERFLOW BEGIN . . . ON OVERFLOW BEGIN . . . END; END; |
An ON-unit can consist of a single simple statement, a group of statements in a begin block, or a null statement.
Simple Statements in ON-Units
The following ON statement specifies a single statement in the ON-unit:
ON ERROR GOTO WRITE_ERROR_MESSAGE;This ON statement specifies a GOTO statement that transfers control to the label WRITE_ERROR_MESSAGE in the event of the ERROR condition.
A simple statement must not be labeled and must not be any of the following:
DECLARE IF DEFINE CONSTANT ON DO PROCEDURE END RETURN FORMAT SELECT Begin Blocks in ON-Units
An ON-unit can also consist of a sequence of statements in a begin block. For example:
ON ENDFILE (SYSIN) BEGIN; CLOSE FILE (TEMP); CALL PRINT_STATISTICS(TEMP); END;This ON-unit consists of CLOSE and CALL statements that request special processing when the end-of-file condition occurs during reading of the default system input file, SYSIN.
If a BEGIN statement is specified for the ON-unit, the BEGIN statement must not be labeled. The begin block can contain any statement except a RETURN statement.
Null Statements in ON-Units
A null statement specified for an ON-unit indicates that no processing is to occur when the condition occurs. Program execution continues as if the condition had been handled. For example:
ON ENDPAGE(SYSPRINT);This ON-unit causes PL/I to continue output on a terminal regardless of the number of lines that have been output.
When a condition is signaled during the execution of a PL/I procedure, PL/I searches for an ON-unit to respond to the condition. This occurs unless you have used the SYSTEM option in an ON statement for the condition; the SYSTEM option causes the system default action to be executed regardless of the existence of any ON-unit.) PL/I first searches the current block, that is, the block in which the condition occurred. If no ON-unit exists in this block for the specific condition, it searches the block that activated the current block (its parent), and then the block that activated that block, and so on.
PL/I executes the first ON-unit it finds, if any, that can handle the
specified condition. If no ON-unit for the specific condition is found,
default PL/I condition handling is performed.
8.10.9 Completion of ON-Units
The ON-unit can complete its execution in any of the following ways:
Descriptions of each ON condition in this manual indicate the action that PL/I takes on completion of an ON-unit associated with the condition.
PL/I provides two distinct types of I/O processing, each of which handles input and output data in a different manner, and each of which has a unique set of I/O statements. These types of I/O are:
When a file is read or written with stream I/O, the data is treated as if it formed a continuous stream. Individual fields of data within the stream are delimited by commas, spaces, and record boundaries. A stream I/O statement specifies one or more fields to be processed in a single operation.
When a file is read or written with record I/O, however, a single record is processed upon the execution of an I/O statement.
This chapter describes I/O concepts that apply to both stream and
record I/O.
9.1 Opening and Closing Files
This section discusses the following:
A file declaration specifies an identifier, the FILE attribute, and one or more file description attributes that describe the type of I/O operation that will be used to process the file.
A file is denoted in an I/O statement by the FILE option as follows:
FILE(file-reference) |
file-reference
The name specified in the file's declaration. For example:
DECLARE INFILE FILE SEQUENTIAL INPUT; OPEN FILE(INFILE); |
Here, INFILE is the name of a file constant. A file constant is an identifier declared with the FILE attribute and without the VARIABLE attribute. Except for the default file constants SYSIN and SYSPRINT, all files must be declared before they can be opened and used.
By default, all file constants have the EXTERNAL attribute. Any
external procedure that declares the identifier with the FILE attribute
and without the INTERNAL attribute can access the same file constant
and, therefore, the same physical file.
9.1.2 File Variables
In PL/I, you can also refer to files using file variables and file-valued functions. For example:
DECLARE ANYFILE FILE VARIABLE; . . . ANYFILE = INFILE; OPEN FILE(ANYFILE); |
If INFILE is declared as in the previous example, the OPEN statement opens the file INFILE.
A file variable can also be given a value by receiving a file constant or variable passed as an argument, or by receiving a file constant or variable as the value of a function. For example:
GETFILE: PROCEDURE (PRINTFILE); DECLARE PRINTFILE FILE VARIABLE; |
This file variable is given a value when the procedure GETFILE is
invoked.
9.1.3 Opening a File
A file is opened explicitly by an OPEN statement or implicitly by a READ, WRITE, REWRITE, DELETE, PUT, or GET statement issued for a file that is not open.
The OPEN statement explicitly opens one or more PL/I files with a specified set of attributes that describe the file and the method for accessing it. The format of the OPEN statement is as follows:
FILE(file-reference)
A reference to the file to be opened. If the file is already open, the OPEN statement has no effect. Therefore, if you want to change any attributes of an open file, you should first close it, and then reopen it with the new attributes.file-description-attribute
The attributes and options of the file. The attributes specified are merged with the permanent attributes of the file specified in its declaration, if any. Then, default rules are applied to the union of these sets of attributes to complete the set of attributes in effect while the file is open.The attributes you can specify with the OPEN statement are as follows:
DIRECT ENVIRONMENT(option,...) RECORD INPUT SEQUENTIAL KEYED STREAM OUTPUT UPDATE The attributes are described in Chapter 2.
The OPEN options are described in Section 9.1.3.1.
DECLARE INFILE FILE, STATE_FILE FILE KEYED; OPEN FILE (INFILE), FILE (STATE_FILE) UPDATE; . . . CLOSE FILE (STATE_FILE); OPEN FILE (STATE_FILE) INPUT SEQUENTIAL; |
The DECLARE and OPEN statements for INFILE do not specify any file description attributes; PL/I applies the default attributes STREAM and INPUT. If any statement other than GET is used to process this file, the ERROR condition is signaled.
The file STATE_FILE is declared with the KEYED attribute. With the first OPEN statement that specifies this file, it is given the UPDATE attribute and opened for updating; that is, READ, WRITE, REWRITE, and DELETE statements can be used to operate on records in the file. The KEYED attribute implies the SEQUENTIAL attribute; thus, records in the file can be accessed sequentially or by key.
The second OPEN statement specifies the INPUT and SEQUENTIAL attributes. During this opening, the file can be accessed by sequential and keyed READ statements; REWRITE, DELETE, and WRITE statements cannot be used.
DECLARE COPYFILE FILE OUTPUT; OPEN FILE(COPYFILE) TITLE('COPYFILE.DAT'); |
The file specified by the file constant COPYFILE is opened for output.
Each time this program is run, it creates a new version of the file
COPYFILE.DAT.
9.1.3.1 OPEN Statement Options
The options that you can use in the OPEN statement are:
The LINESIZE option specifies the maximum number of characters that can be output on a single line when the PUT statement writes data to a file with the STREAM and OUTPUT attributes. The format of the LINESIZE option is:
LINESIZE(expression) |
expression
A fixed-point binary expression in the range 1 to 32767, giving the number of characters per line. If the expression is outside this range, a run-time error occurs.
The value specified in the LINESIZE option is used as the output line length for all subsequent output operations on the stream file, and it overrides the system default line size.
The default line size is as follows:
The line size is used by output operations to determine whether output will be placed on the current line or on the next line.
The PAGESIZE option is used in the OPEN statement to specify the maximum number of lines that can be written to a print file without signaling the ENDPAGE condition. The format of the PAGESIZE option is:
PAGESIZE(expression) |
expression
A fixed-point binary expression in the range 1 through 32767, giving the number of lines per page. If the expression is outside this range, a run-time error occurs.
The value specified in the PAGESIZE option is used as the output page length for all subsequent output operations on the print file, and overrides the system default page size. The default page size is implementation-dependent.
During output operations, the ENDPAGE condition is signaled the first time that the specified page size is exceeded.
The PAGESIZE option is valid only for print files.
The TITLE option is specified in an OPEN statement to designate the external file specification of the file to be associated with the PL/I file. The TITLE option is specified only on the OPEN statement for a file. Its format is as follows:
TITLE(expression) |
expression
A character-string expression which represents an external file specification for the file.
For details on how the file specification is determined see the
Section 9.1.3.4.
9.1.3.2 Effects of Opening a File
Opening a file in PL/I has the following effects:
Each of these steps is described in more detail below. If an error
occurs during the opening of a file, the UNDEFINEDFILE condition is
signaled.
9.1.3.3 Establishing the File's Attributes
The description attributes specified when a file is opened are merged with the file's permanent attributes. Duplicate specification of an attribute is allowed only for an attribute that does not specify a value.
An incomplete set of attributes is augmented with implied attributes. Table 9-1 summarizes the attributes that can be added to an incomplete set.
Attribute | Implied Attributes |
---|---|
DIRECT | RECORD KEYED |
KEYED | RECORD |
STREAM OUTPUT | |
SEQUENTIAL | RECORD |
UPDATE | RECORD |
If the set of attributes is still not complete, PL/I uses the following steps to complete the set:
The completed set of attributes applies only for the current opening of
the file. The file's permanent attributes, specified in the declaration
of the file, are not changed.
9.1.3.4 Determining the File Specification
PL/I uses the value of the TITLE option to determine the file specification, that is, the actual name of the file or device on which the I/O is to be performed. The determination of the file specification depends on the following system-specific functions:
A file opening accesses an existing file if the file specified by the TITLE option actually exists and if the following attributes are present:
Whenever PL/I accesses an existing file, the file's
organization is checked for compatibility with the PL/I attributes
specified. If any incompatibilities exist, the UNDEFINEDFILE condition
is signaled.
9.1.3.6 Creating a File
A file opening creates a new file if the following are all true:
You can specify the organization and record format of a new file with ENVIRONMENT options. If no ENVIRONMENT options are given, the new file's organization is determined as follows:
When a file is opened with the RECORD and OUTPUT attributes, only WRITE statements can be used to access the file. If the file has the KEYED attribute as well, the WRITE statements must include the KEYFROM option.
Attribute | Description |
---|---|
DIRECT | Records in the file will be accessed randomly. |
INPUT | The file is an input file and will only be read. |
KEYED | Records in the file will be accessed by key. |
OUTPUT | The file is an output file and will only be written. |
The file will be output on a printer or terminal. | |
RECORD | The file will be accessed with record I/O statements. |
SEQUENTIAL | Records in the file will be accessed sequentially. |
STREAM | The file will be accessed with stream I/O statements. |
UPDATE | The file will be accessed for both reading and writing, and records can be rewritten and deleted. |
For detailed descriptions of these attributes, see Chapter 2.
9.1.5 Closing a File
The CLOSE statement dissociates PL/I files from the physical files with which they were associated when opened. The format of the CLOSE statement is as follows:
FILE(file-reference)
A file to be closed. If the file is already closed, the CLOSE statement has no effect.ENVIRONMENT(option,...)
One or more ENVIRONMENT options, separated by commas.
This CLOSE statement closes the file constant INFILE:
CLOSE FILE(INFILE); |
This CLOSE statement closes two files specified in a comma list, each with a different ENVIRONMENT option:
CLOSE FILE(A) ENV(DELETE), FILE(B) ENV(REVISION_DATE(X)); |
Another example of a CLOSE statement is:
DECLARE STATE_FILE FILE KEYED; OPEN FILE(STATE_FILE) DIRECT UPDATE; . . . CLOSE FILE(STATE_FILE); OPEN FILE(STATE_FILE) INPUT SEQUENTIAL; |
The file STATE_FILE is declared with the KEYED attribute. The first OPEN statement that specifies this file is given the DIRECT and UPDATE attributes and opened for updating; the file can be accessed only by key.
The CLOSE statement closes the file. The second OPEN statement
specifies the INPUT and SEQUENTIAL attributes; the file can now be
accessed sequentially.
9.2 Stream I/O
Stream I/O is one of the two general kinds of I/O performed by PL/I. Stream input and output is performed by the statements GET and PUT, respectively. Both statements can perform either list-directed or edit-directed operations.
In stream I/O, more than one record or line can be processed by a single statement, and, conversely, multiple statements can process a single line or record. In contrast, record I/O only processes one record of a file in each READ or WRITE statement.
Table 9-3 summarizes the file description attributes and access modes for stream files.
Attributes Specified | Attributes Implied | Valid Devices and File Organizations | Usage |
---|---|---|---|
STREAM OUTPUT |
Individual data values are written with PUT statements that convert the
values to character strings and automatically format the strings into
lines, or records. A PUT statement can fill part or all of one or more
lines. Data conversion and alignment within lines can use the default
processing provided by the PUT LIST form of the PUT statement or can be
explicitly controlled by format specifications in the PUT EDIT form of
the PUT statement. The output fields can be aligned to specific tab
positions.
The PAGESIZE and LINESIZE options can be specified to control the formatting of lines on pages. The ENDPAGE condition is signaled when the end-of-page is reached. |
||
STREAM INPUT | Individual data items are read by GET statements. A single GET statement can process all or part of one or more lines or records. The format of an input field can be determined by the default processing provided by the GET LIST form of the GET statement or can be explicitly controlled by format specifications in the GET EDIT form of the GET statement. | ||
STREAM OUTPUT | This form of stream output is similar to that provided when PRINT is specified, except that tab positioning and page formatting are not provided. Moreover, when string values are written with the PUT LIST form of the PUT statement, they are enclosed in apostrophes. Files that are created with these attributes can be read back in with GET LIST statements when the file is opened with the STREAM and INPUT attributes. |
Successive GET statements acquire their input from the same line or record until all the characters in the line have been read, unless the program explicitly skips to the next line. When necessary, a single GET statement will read multiple lines to satisfy its input-target list. A single input data item cannot cross a line unless it is a character string enclosed in apostrophes or unless the ENVIRONMENT option IGNORE_LINE_MARKS is in effect for the input file. This option produces stream input operations that match exactly with standard PL/I. However, the option is usually not necessary; most programs produce the expected results without it.
Successive PUT statements write their output to the same line or record until the line size is reached or until the program explicitly skips to a new line. A single PUT statement will write as many records as necessary to satisfy its output-source list. Any single data item that will not fit on the current line is split across lines.
The next sections describe the following aspects of stream I/O:
A stream file is a file of text, divided into lines. For every stream file used in a program, PL/I maintains the following information:
Input operations can begin at any position from the current position onward. The default is the current position. To acquire data from a different position, you can do the following:
Because stream files are sequential files, output operations always place data at the end of the file. You can do the following additional formatting of output with any stream output file:
If the output file is a print file (that is, has the attributes STREAM, OUTPUT, and PRINT, or is the default file SYSPRINT), the following additional information is maintained for the file:
Terminals should always be declared as print files when used for output
(see Section 9.2.6.)
9.2.2 Input by the GET Statement
The GET statement acquires data from an input stream, which is either a stream file or a character-string expression. The input file can be a file declared with the STREAM attribute or the default file SYSIN, usually associated with the user's default input device. See Section 9.2.6 for more information.
This section describes the syntax, options, and execution of GET
statements.
9.2.2.1 Syntax Summary of the GET Statement
The GET statement has several forms; they are:
GET EDIT (input-target, ...) (format-specifier,...) ⎡ FILE(file-reference) ⎤ ⎢ [SKIP [(expression))]]⎥ ⎢ ⎥ ⎣ STRING(reference) ⎦ GET LIST (input-target, ...) ⎡ FILE(file-reference) ⎤ ⎢ [SKIP [(expression))]]⎥ ⎢ ⎥ ⎣ STRING(reference) ⎦ GET [FILE(file-reference)]* SKIP[(expression);
The form of the GET EDIT statement is as follows:
*Syntax elements common to two or more forms
input-target
The names of one or more variables to be assigned values from the input stream. Multiple input targets must be separated by commas.An input target has one of the following forms:
reference
The reference is to a scalar or aggregate variable of any computational type. If the reference is to an array, data is assigned to array elements in row-major order. If the reference is to a structure, data is assigned to structure members in the order of their declaration.
(input-target,... DO reference=expression [TO expression]
[BY expression] [WHILE(expression)] [UNTIL(expression)])Another form is:
(input-target,... DO reference=expression
[REPEAT expression] [WHILE(expression)] [UNTIL(expression)])The input target can be any of these forms, and the references and expressions are as for the DO statement. Notice that the parentheses surrounding the input target are in addition to the parentheses surrounding the entire input list.
For a discussion of the matching of format items to input targets and of the use of DO specifications, see Section 9.2.4.13.
format-specification
A list of format items to control the conversion of data items in the input list. You can use data format items, control format items, or remote format items. For each variable name in the input-target list, there is a corresponding data format item in the format-specification list that specifies the width of the field and controls the data conversion. See Section 9.2.4 for format items.FILE (file-reference)
An option specifying that the input stream is a file; the reference is to a declared file variable or constant. If neither the FILE option nor the STRING option is specified, PL/I assumes the file SYSIN. This file is the standard inputIf a file is specified and is not currently open, PL/I opens it with the attributes STREAM and INPUT. The UNDEFINEDFILE condition is signaled if the file cannot be opened.
SKIP [(expression)]
An option that advances the input file a specified number of lines before processing the input list. This option can be used only with the implied or explicit FILE option. If the expression is specified, it indicates the number of lines to be advanced; if it is omitted, the default is to skip to the next line. The SKIP option is always executed first, before any other input or positioning of the input file, regardless of its position in the statement.OPTIONS (option,...)
An option that specifies one or more options.STRING(expression)
An option specifying that the input stream is a character-string expression. The STRING option cannot be used with the FILE, OPTIONS, or SKIP option.The GET STRING statement acquires a string from a character-string variable and assigns it to one or more input targets. If more than one input target is listed, the characters in the string should include any punctuation (comma or space separators or apostrophes) that would be required if the character string were in an external file.
The GET EDIT statement acquires fields of character-string data from an input stream, which can be a stream file or a character-string expression. The stream file can be a declared file or the default file SYSIN. GET EDIT converts the character strings under control of a format specification and assigns the resulting values to a specified list of input targets (variables). It also allows input of characters from selected positions in the input stream.
The form of the GET EDIT statement is as follows:
GET EDIT (input-target, ...) (format-specifier,...) ⎡ FILE(file-reference) ⎤ ⎢ [SKIP [(expression))]]⎥ ⎢ ⎥ ⎣ STRING(reference) ⎦
The syntax is described in more detail in Section 9.2.2.1.
GET EDIT (FIRST,MID_INITIAL,LAST) (A(12),A(1),A(20)); |
This statement reads the next 12 characters from the default stream input file (SYSIN) and assigns the string to FIRST. It then reads the next character into MID_INITIAL, and then the next 20 characters into LAST.
GET EDIT (SOCIAL_SECURITY) (A(12)) FILE (SOCIAL) SKIP (12); |
This statement opens the stream file SOCIAL if the file was closed, advances 12 lines, reads the first 12 characters of the line, and assigns the characters to the variable SOCIAL_SECURITY.
GET EDIT (N, (A(I) DO I=1 TO N)) (F(4),SKIP,100 F(10,5)); |
GET EDIT (NAME.FIRST,NAME.LAST) (A(10),X(3),A(20)) STRING('Philip A. Rothberg '); |
This statement assigns <BIT_STRING>(Philip ) to the structure
member NAME.FIRST, skips the middle initial, period, and space, and
assigns <BIT_STRING>(Rothberg ) to NAME.LAST.
9.2.2.3 GET LIST
The GET LIST statement acquires character-string data from an input stream, which can be a stream file or a character-string expression. The stream file can be a declared file or the default file SYSIN. The acquired character strings are assigned to input targets named in the GET LIST statement, after being converted automatically to the targets' data types.
Use the GET LIST statement to read unformatted data from a stream file or character string. Because GET LIST does not require that data be aligned in specific columns, it is useful for acquiring input from a terminal.
The form of the GET LIST statement is as follows:
GET LIST (input-target, ...) ⎡ FILE(file-reference) ⎤ ⎢ [SKIP [(expression))]]⎥ ⎢ ⎥ ⎣ STRING(reference) ⎦
The syntax is described in more detail in Section 9.2.2.1.
The items to be read into the input targets are separated by a space or a single comma. Multiple spaces are treated as a single space, and a comma can be surrounded by spaces. The following rules apply:
isn't or 'isn''t' |
GETS: PROCEDURE OPTIONS(MAIN); DECLARE NAME CHARACTER(80) VARYING; DECLARE AGE FIXED; DECLARE (WEIGHT,HEIGHT) FIXED DECIMAL(5,2); DECLARE SALARY PICTURE '$$$$$$V.$$'; DECLARE DOSAGE FLOAT; DECLARE INFILE STREAM INPUT FILE; DECLARE OUTFILE PRINT FILE; GET FILE(INFILE) LIST(NAME,AGE,WEIGHT,HEIGHT,SALARY,DOSAGE); PUT FILE(OUTFILE) LIST(NAME,AGE,WEIGHT,HEIGHT,SALARY,DOSAGE); END GETS; |
If the file INFILE.DAT contains the following data:
'Thomas R. Dooley',33,150.60,5.87,15000.50,4E-6, |
Thomas R. Dooley 33 150.60 5.87 $15000.50 4.0000000E-06 |
In the input file (INFILE.DAT), the string <BIT_STRING>(Thomas R. Dooley) is surrounded by apostrophes so that the spaces between words will not be interpreted as field separators.
GSTR: PROCEDURE OPTIONS(MAIN); DECLARE STREXP CHARACTER(80) VARYING; DECLARE (A,B,C,D,E) FIXED; DECLARE OUTFILE STREAM OUTPUT FILE; OPEN FILE(OUTFILE) TITLE('GSTR.OUT'); STREXP = '1,2,3,4,5'; GET STRING(STREXP) LIST(A,B,C,D,E); PUT FILE(OUTFILE) LIST(A,B,C,D,E); END GSTR; |
The program GSTR writes the following output to GSTR.OUT:
1 2 3 4 5 |
For other examples, see Section 9.2.6.
GET [FILE(file-reference)] SKIP [(expression)] ; |
The syntax is described in more detail in Section 9.2.2.1.
9.2.2.5 Execution of the GET Statement
When a GET statement is executed, the first action is to evaluate the FILE option, if there is one. For example:
GET FILE(INFILE) LIST(A); |
If INFILE references an open file, PL/I checks that the file has the INPUT and STREAM attributes.
If INFILE has not been opened, PL/I implicitly opens the file with the attributes INPUT and STREAM.
If the associated file does not exist, or if for any reason the associated file cannot be opened, the UNDEFINEDFILE condition is signaled.
If the statement has a STRING option instead of a FILE option, the reference in the STRING option is evaluated.
If the statement has neither a FILE option nor a STRING option, it is taken to refer to the default file constant SYSIN. SYSIN is declared by default with the STREAM INPUT attributes, and it is normally used for input from a terminal (see Section 9.2.6).
If the input stream is a file, the next action is to execute the SKIP option, if there is one. The SKIP option cannot be used with the STRING option. Note that a GET statement can perform a SKIP operation even if it performs no data input. For example:
GET FILE(INFILE) SKIP(2); |
A GET statement that has the EDIT or LIST option performs input from the stream to a list of input targets, which must be variables of computational data types. If the input target is an aggregate variable, then input is assigned to each element of the aggregate; input values are assigned to array elements in row-major order and to structure members in the order of their declaration. An input target can also contain a DO construct that further controls the assignment. Because a stream consists only of characters, and the input targets are not necessarily character-string variables, an input field must be selected from the input stream for each target and must be converted, if necessary, to the type of the target.
In edit-directed (GET EDIT) statements, the selection and assignment of the input field are controlled by a format item that corresponds to the input target. In the default case, which applies to terminal input and to input from most stream files, a data format item assumes that the end of the input field has occurred if it encounters the end of a record in an input file or the end of a line when the input is from a terminal.
For example, a common technique for reading lines of varying length from a terminal is to deliberately use a format item that specifies a field wider than the column width of the terminal. If a carriage return is typed in response to an input request for GET EDIT, or if the end of a record is immediately encountered, the requested field width is filled with spaces and assigned to the input target under the control of the corresponding format item. (Note that all spaces will cause an error for B format items.) However, if the input stream is a character-string expression (GET STRING), the ERROR condition is signaled if the format item causes the end of the input string to be reached in the middle of an input field.
In list-directed (GET LIST) statements, an input field is acquired by examining the input stream for the next character that is not a space character. The following actions are taken depending on the character found:
If the GET LIST statement attempts to read a file after its last input
field has been read, or if it attempts to read an empty file, the
ENDFILE condition is signaled. If the GET LIST statement attempts to
read a character string after its last field has been read, or if it
attempts to read a null string, the ERROR condition is signaled.
9.2.3 Output by the PUT Statement
The PUT statement transfers data from the program to the output stream. The output stream can be either a stream file or a character-string variable. The output file can be a declared file or the default file SYSPRINT.
This entry describes the syntax, options, and execution of PUT
statements.
9.2.3.1 Syntax Summary of the PUT Statement
The PUT statement has several forms; they are:
PUT EDIT (output-source, ...) (format-specification,...) ⎡ FILE(file-reference) ⎤ ⎢ [PAGE] [LINE(expression)⎥ ⎢ [SKIP [(expression))]]⎥ ⎢ ⎥ ⎣ STRING(reference) ⎦ PUT LIST (output-source, ...) ⎡ FILE(file-reference) ⎤ ⎢ [PAGE] [LINE(expression)⎥ ⎢ [SKIP [(expression))]]⎥ ⎢ ⎥ ⎣ STRING(reference) ⎦ PUT [FILE(file-reference)*] PAGE; PUT [FILE(file-reference)*] SKIP [(reference)];
*Syntax elements common to two or more forms
output-source
A construct that specifies one or more expressions to be placed in the output stream. Multiple output sources must be separated by commas.An output source has the following forms:
expressionThe expression is of any computational type, including a reference to a scalar or aggregate variable. If the reference is to an array, data is output from array elements in row-major order. If the reference is to a structure, data is output from structure members in the order of their declaration.
(output-source,... DO reference=expression
[TO expression][BY expression][WHILE(expression)][UNTIL(expression)])Another form is:
(output-source,... DO reference=expression
[REPEAT expression][WHILE (expression)][UNTIL(expression)])The output source can be any of these forms, and the references and expressions are as for the DO statement. Notice that the parentheses surrounding this form of output source are in addition to the parentheses surrounding the entire output-source list.
For a discussion of the matching of format items to output sources and of the use of DO specifications, see Section 9.2.4.13.
format-specification
A list of format items to control the conversion of data items in the output list. Format items can be data format items, control format items, or remote format items. For each variable name in the output-source list, there is a corresponding data format item in the format-specification list that specifies the width of the output field and controls the data conversion (see Section 9.2.4.13 and Section 9.2.4).FILE(file-reference)
An option that specifies that the output stream be a stream file; the reference is to a declared file variable or constant. If neither the FILE option nor the STRING option is specified, PL/I uses the default file SYSPRINT. SYSPRINT is associated with the standard output file, which in turn is generally associated with the user's terminal.If a file is specified, and it is not currently open, PL/I opens the file with the attributes STREAM and OUTPUT.
PAGE
An option that advances the output file to a new page before any data is transmitted. The PAGE option can be used only with implied or explicit print files. The file is positioned at the beginning of the next page, and the current page number is incremented by 1. The PAGE, LINE, and SKIP options are always executed, in that order, before any other output or file-positioning operations. The page size is either the default value or the specific value that you have established for the file (See Section 9.1.3). The PAGESIZE option can be used only with print files.LINE (expression)
An option that advances the output file to a specified line. You can use the LINE option only with implied or explicit print files. The expression must yield an integer i. Blank lines are inserted in the output file such that the next output data appears on the ith line of a page.If the file is currently positioned at the beginning of line i, no operation is performed by the LINE option.
If the file is currently positioned before line i, and i is less than or equal to the page size, then blank lines are inserted following the current line until line i is reached.
If the file is currently positioned at or beyond line i, and the file is not at the beginning of line i, then the remainder of the page (the portion between the current line and the current page size) is filled with blank lines. The ENDPAGE condition is signaled.
When the LINE option is used within an ENDPAGE ON-unit, it causes a skip to the next page.
SKIP [(expression)]
An option that advances a specified number of lines from the current line. You can use the SKIP option only with the implied or explicit FILE option. The expression must yield an integer i, which must not be negative and must be greater than zero except for print files. If the expression is omitted, i equals 1.If the file is not a print file, i-1 blank lines are inserted following the current line, and subsequent output of data begins at the beginning of (current line)+i.
If the file is a print file, i=0 causes a return to the beginning of the current line. If i is greater than zero, and either the current line exceeds the page size or the page size is greater than or equal to the current line plus i, then i-1 blank lines are inserted. Otherwise, the remainder of the current page is filled with blank lines, and the ENDPAGE condition is signaled.
On output devices with the space-suppression feature, SKIP(0) can be used to cause overprinting, underscoring, and so forth. For further information on pages in stream files, see Section 9.2.6.
STRING(reference)
An option that specifies that the output stream be the referenced character-string variable. The STRING option cannot be used in the same statement with FILE, OPTIONS, PAGE, LINE, or SKIP.
The PUT EDIT statement takes output sources (variables and expressions) from the program, converts the results to characters under control of a format specification, and places the resulting character strings in the output stream. The output stream is either a stream file or a character-string variable.
With PUT EDIT, the format of the output data is controlled by the program.
The form of the PUT EDIT statement is as follows:
PUT EDIT (output-source, ...) (format-specification,...) ⎡ FILE(file-reference) ⎤ ⎢ [PAGE] [LINE(expression)⎥ ⎢ [SKIP [(expression))]]⎥ ⎢ ⎥ ⎣ STRING(reference) ⎦
The syntax is described in more detail in Section 9.2.3.1.
PUTE: PROCEDURE OPTIONS(MAIN); DECLARE SOURCE FIXED DECIMAL(7,2); DECLARE OUTFILE PRINT FILE; OPEN FILE(OUTFILE) TITLE('PUTE.OUT'); SOURCE = 12345.67; PUT SKIP FILE(OUTFILE) EDIT(SOURCE) (F(8,2)); PUT SKIP FILE(OUTFILE) EDIT(SOURCE) (E(13)); PUT SKIP FILE(OUTFILE) EDIT(SOURCE) (A); PUT SKIP FILE(OUTFILE) EDIT('American: ',SOURCE) (A,P'ZZ,ZZZV.ZZ'); PUT SKIP FILE(OUTFILE) EDIT('European: ',SOURCE) (A,P'ZZ.ZZZV,ZZ'); END PUTE; |
The program PUTE writes the following output to PUTE.OUT:
12345.67 1.234567E+04 12345.67 American: 12,345.67 European: 12.345,67 |
The PUT LINE statement advances a print file to a specified line. Its format is as follows:
PUT [FILE (file-reference)] LINE (expression); |
file-reference
A reference to the file to which the statement applies. The file must be a print file.
The syntax is described in more detail in Section 9.2.3.1.
9.2.3.4 PUT LIST
The PUT LIST statement specifies a list of output sources (variables and expressions) whose results are converted to character strings and transmitted to the output stream. If the output file is a print file, the output character strings are placed at the start of the next tab stop, where a tab stop is in column 1, 9, 17, and so on. Otherwise, the strings are separated by spaces.
With PUT LIST, the conversion of the output sources and formatting of the output data are automatic and follow the PL/I rules for conversion to character strings.
The form of the PUT LIST statement is as follows:
PUT LIST (output-source, ...) ⎡ FILE(file-reference) ⎤ ⎢ [PAGE] [LINE(expression)⎥ ⎢ [SKIP [(expression))]]⎥ ⎢ ⎥ ⎣ STRING(reference) ⎦
The syntax is described in more detail in Section 9.2.3.1.
PUTL: PROCEDURE OPTIONS(MAIN); DECLARE I FIXED BINARY, F FLOAT, P PICTURE '99V.99', S CHAR(10); DECLARE INFILE STREAM INPUT FILE; DECLARE OUTFILE PRINT FILE; OPEN FILE(INFILE) TITLE('PUTL.IN'); OPEN FILE(OUTFILE) TITLE('PUTL.OUT'); GET FILE(INFILE) LIST (I,F,P,S); PUT FILE(OUTFILE) SKIP LIST (I,F,P,S); END PUTL; |
Assume that the file PUTL.IN contains the following data:
2,3.54,22.33,'A string' |
2 3.5400000E+00 22.33 A string |
For print files, each output item is written at the next tab position.
Floating-point values are represented in floating-point notation.
Character values are not enclosed in apostrophes.
9.2.3.5 PUT PAGE
The PUT PAGE statement positions the output file at the start of a new page. This statement is valid only for print files, that is, files that have been opened with the PRINT attribute.
The form of the PUT PAGE statement is:
PUT [FILE(file-reference)] PAGE; |
The syntax is described in more detail in Section 9.2.3.1.
PUT FILE(REPORT) PAGE SKIP LINE(2); |
The PUT statement advances the file REPORT to the beginning of the next
page, advances to line 2, and skips to the beginning of the next line
(3).
9.2.3.6 PUT SKIP
The PUT SKIP statement positions the output file at the start of a new line.
The form of the PUT SKIP statement is as follows:
PUT [FILE(file-reference)] SKIP [(expression)]; |
The syntax is described in more detail in Section 9.2.3.1.
9.2.3.7 Execution of the PUT Statement
When a PUT statement is executed, the first action is to evaluate the FILE or STRING option, if there is one. If the statement has a FILE option and is not already open, the referenced file is either opened or created with the STREAM and OUTPUT attributes. The file is opened if it has the APPEND attribute; otherwise, it is created.
If neither the FILE option nor the STRING option is present, the output stream is assumed to be the default file SYSPRINT.
If the output stream is a file, the next action is to execute any of the options PAGE, LINE, and SKIP that occur in the statement, in that order. The output stream must be a file if any of these options are included, and it must be a print file if LINE or PAGE is included. Note that a PUT statement can contain one or more of these options even if it performs no data output. For example:
PUT FILE(OUT) PAGE LINE(20); |
However, if the statement also has a LIST or EDIT option, it then writes out a list of output sources, which must be variables, constants, or other expressions of computational data types. Because a stream consists only of characters, each output source is converted to a character string before being written out, as follows:
The converted output source is then written to the output stream, as follows:
In PL/I, formatted input and output data is transferred with the GET EDIT and PUT EDIT statements, which include a format specification made up of format items.
PL/I format items are categorized as follows:
The data format items refer to a field of characters in the stream. Each data format item specifies the field width in characters and either the manner in which the field is used to represent a value (output) or the manner in which the characters in the field are to be interpreted (input). Because the representation or interpretation is under control of the format items, certain symbols used in the stream with GET LIST and PUT LIST are not used with GET EDIT or PUT EDIT:
The following guidelines apply to errors and mismatches that occur between the actual data values and the fields specified by data format items:
The rest of this section describes each format item in detail. The following subsections describe format-specification lists and the FORMAT statement.
A [(w)] |
w
A nonnegative integer or an integer expression that specifies the width in characters of the field in the stream. If it is not included (PUT EDIT only), the field width equals the length of the converted output source.
The value w must be included when the A format item is used with GET EDIT. If w has a positive value, a character-string value comprising the next w characters in the input stream is acquired and assigned to the input variable.
The acquired character string is converted, if necessary, to the data type of the input target, following the PL/I data conversion rules. Apostrophes should enclose the stream data only if the apostrophes are intended to be acquired as part of the data.
The output source associated with an A format item is converted, if necessary, to a string of characters. The result is assigned to a string of w characters, which are placed in the output stream. If w is omitted, the length of the output string equals the length of the converted output source. If w is zero, the A format item and the associated output source are skipped.
Output strings are not surrounded automatically by apostrophes. The converted output source is truncated or appended with trailing spaces, according to the value of w. The conversion of a computational data item to a character string is performed following the PL/I data conversion rules (see Section 6.4).
The next tables show the relationship between the internal and external representations of characters that are read or written with the A format item.
The input stream shown in the following table is a field of characters beginning at the current position in the stream and continuing to the right. The # character is used to signify a space. The target type is the type of the variable to which the input value is assigned.
Format Item | Input Stream | Target Type | Target Value |
---|---|---|---|
A(10) | ##SHRUBBERY#... | CHAR(10) | ##SHRUBBER |
A(6) | ##SHRUBBERY#... | CHAR(10) | ##SHRU#### |
A(6) | ##SHRUBBERY#... | CHAR(10) VAR | ##SHRU |
A(10) | ##1.2345####... | DECIMAL(4,1) | 001.2 |
A(5) | ##1.2345####... | DECIMAL(4,2) | 01.20 |
A(6) | ##1.2345####... | DECIMAL(4,2) | 01.23 |
The output source value shown in the table that follows is either a constant or the value of a variable that is written with the associated format item.
Output Source Value | Format Item | Output Value |
---|---|---|
'STRING' | A(10) | STRING#### |
'STRING' | A | STRING |
1.2345 | A(2) | ## |
1.2345 | A | ##1.2345 |
-1.2345 | A(4) | #-1. |
-1.2345 | A | #-1.2345 |
'' | A(10) | ########## |
'' | A | [no output] |
0 | A(3) | ### |
0 | A | ###0 |
-12345 | A(6) | ##-123 |
-12345 | A | ##-12345 |
The B format items B, B1, B2, B3, and B4 describe representations of bit strings in an input or output stream. Note that the B can be typed lowercase. The form of the B format items is as follows:
B[m] (w) |
m
The integer 1, 2, 3, or 4, specifying the radix factor, that is, 2m. B and B1 have the same meaning. When the radix factor is omitted or is 1, the bit string is represented by the characters 0 and 1 (binary) in the stream. When the radix factor is 2, the bit string is represented by the characters 0, 1, 2, and 3 (base 4). When the radix factor is 3, the bit string is represented by the characters 0, 1, 2, 3, 4, 5, 6, and 7 (octal). When the radix factor is 4, the bit string is represented by the characters 0 through 9 and A through F (hexadecimal).w
A nonnegative integer or integer expression that specifies the width in characters of the field in the stream.
The interpretation of the B format items on input and output is described below.
The value w must be included when the B format items are used with GET EDIT. The number of characters specified by w is acquired. The input characters are converted to an intermediate bit string of length w*m. If the input target is not a bit-string variable, then this intermediate bit string is converted to the type of the input target, following the PL/I conversion rules (for details, see Section 6.4).
The string of characters in the stream can be preceded or followed by spaces, which are ignored. All characters in the input field (except any leading and trailing spaces) must be those implied by the radix factor; otherwise, a CONVERSION condition is signaled. Consequently, input strings should not be enclosed in apostrophes and should not include the suffix Bm.
The output source is converted, if necessary, to a bit string, following the PL/I rules for converting data to bit strings (see Section 6.4). If the length of the resulting bit string is not a multiple of the radix factor (m), the bit string is padded with zeros on the right to make its length the next higher multiple.
The bit string is then converted to a character representation appropriate to the radix factor and placed in the output stream. The character representation is left-justified in the field specified by w and is truncated or padded with spaces on the right if necessary. If w is not included, the output string has the same length as the converted output source. If w is zero, the B format item and its associated output source are skipped.
BFORMAT_XM: PROCEDURE OPTIONS(MAIN); /* This program prints incorrect values for an integer */ DECLARE I FIXED BINARY(31); DECLARE BFORM STREAM OUTPUT PRINT FILE; I = 5; OPEN FILE(BFORM) TITLE('BFORMXM.OUT'); PUT SKIP FILE(BFORM) EDIT ('Decimal:',I) (A,X,F(2)); PUT SKIP FILE(BFORM) EDIT ('Binary:',I) (A,X,B); PUT SKIP FILE(BFORM) EDIT ('Base 4:',I) (A,X,B2); PUT SKIP FILE(BFORM) EDIT ('Octal:',I) (A,X,B3); PUT SKIP FILE(BFORM) EDIT ('Hexadecimal:',I) (A,X,B4); END BFORMAT_XM; |
This program produces the following output:
Decimal: 5 Binary: 0000000000000000000000000000101 Base 4: 0000000000000022 Octal: 00000000024 Hexadecimal: 0000000A |
The base 4, octal, and hexadecimal representations of I are incorrect because the precision of I (31) is not a multiple of 2, 3, or 4. For the B2 and B4 format items, an extra zero bit was appended to the intermediate bit string, in effect multiplying the value of the string by 2. For B3, two extra bits were appended to make the string 33 bits long and thus divisible into an exact number of 3-bit segments. To avoid this problem, the precision of the output source must be a number that is evenly divisible by any radix factor with which it is to be written out, as in the following example:
BFORMAT_XM: PROCEDURE OPTIONS(MAIN); /* This program prints correct values for an integer */ DECLARE I FIXED BINARY(24); /* 24 is a multiple of 2*3*4 */ DECLARE BFORM STREAM OUTPUT PRINT FILE; I = 5; OPEN FILE(BFORM) TITLE('BFORMXM5.OUT'); PUT SKIP FILE(BFORM) EDIT ('Decimal:',I) (A,X,F(2)); PUT SKIP FILE(BFORM) EDIT ('Binary:',I) (A,X,B); PUT SKIP FILE(BFORM) EDIT ('Base 4:',I) (A,X,B2); PUT SKIP FILE(BFORM) EDIT ('Octal:',I) (A,X,B3); PUT SKIP FILE(BFORM) EDIT ('Hexadecimal:',I) (A,X,B4); END BFORMAT_XM; |
This version of the program produces the following output:
Decimal: 5 Binary: 000000000000000000000101 Base 4: 000000000011 Octal: 00000005 Hexadecimal: 000005 |
The output values are correct representations of I because the precision (24) is evenly divisible by 2, 3, or 4.
The tables below show the relationship between the internal and external representations of characters that are read or written with the B format item.
The input stream shown in the following table is a field of characters beginning at the current position in the stream and continuing to the right. The # character is used to signify a space. The target type is the type of the variable to which the input value is assigned.
Format Item | Input Stream | Target Type | Target Value |
---|---|---|---|
B(12) | 111000111110... | BIT(12) | '111000111110'B |
B(12) | ######110011... | BIT(12) | '110011000000'B |
B2(6) | 123123... | BIT(12) | '011011011011'B |
B3(4) | 1775... | BIT(12) | '001111111101'B |
B4(3) | 1FA... | BIT(12) | '000111111010'B |
The output source value shown in the following table is either a constant or the value of a variable that is written out with the associated format item.
Output Source Value | Format Item | Output Value |
---|---|---|
4095 | B | 111111111111 |
4095 | B(11) | 11111111111 |
4095 | B2 | 333333 |
4095 | B3 | 7777 |
4095 | B4 | FFF |
The COLUMN format item sets a stream file to a specific character position within a line. In other words, COLUMN determines the position at which the next data will be output or from which the next data will be input. The COLUMN format item refers to an absolute character position in a line; for information on how to refer to a relative position, see Section 9.2.4.12.
The form of the COLUMN format item is:
⎧COLUMN⎫ ⎨ ⎬ (w) ⎩COL ⎭
w
A nonnegative integer or expression that identifies the wth position from the beginning of the current line. The value of the converted expression must be zero or positive. If the value of the converted expression is zero, a value of 1 is assumed.If the file is already at the specified position, no operation is performed. If the file is already beyond the specified position, the format item is applied to the next line.
The interpretation of the COLUMN format item on input and output is given below.
The file is positioned at the column specified by w. Characters between the beginning of the line and this column are ignored. If the file is already positioned beyond the specified column, the remainder of the line is skipped and the format item is applied to the next line.
The file is positioned at the column specified by w. Within the current line, positions between the wth column and the position of the last output data are filled with spaces.
If the file is already positioned beyond the specified column, the format item is applied to the next line. If w exceeds the line size, a value of 1 is assumed. See Section 9.1.
COL: PROCEDURE OPTIONS(MAIN); DECLARE IN STREAM INPUT FILE; DECLARE OUT STREAM OUTPUT FILE; DECLARE LETTER CHARACTER(1); PUT FILE(OUT) SKIP EDIT('123456789012345678901234567890') (A); PUT FILE(OUT) SKIP EDIT('COL1','COL28') (A,COL(28),A); GET FILE(IN) EDIT (LETTER) (A(1)); PUT FILE(OUT) SKIP(2) LIST('Letter in column 1:',LETTER); GET FILE(IN) EDIT (LETTER) (COL(25),A(1)); PUT FILE(OUT) SKIP LIST ('Letter in column 25:',LETTER); END COL; |
If the stream input file IN.DAT contains the following text:
ABCDEFGHIJKLMNOPQRSTUVWXYZ |
123456789012345678901234567890 COL1 COL28 'Letter in column 1:' 'A' 'Letter in column 25:' 'Y' |
The E format item describes the representation of a fixed- or floating-point value as a decimal floating-point number in a stream.
The form of the item is:
E(w[,d]) |
w
A nonnegative integer or expression that specifies the total width in characters of the field in the stream.d
An optional nonnegative integer or expression that specifies the number of fractional digits in the stream representation and whose value is interpreted depending on GET EDIT and PUT EDIT statements as described below.
Used with GET EDIT, the E format item acquires a character-string value representing a floating-point decimal value and assigns it, with necessary conversions, to an input target of any computational type. If w is zero, no operation is performed on the input stream, and a null character string is converted and assigned to the input target.
For input, floating-point values can be represented in the stream in the following forms:
Form | Example |
---|---|
mantissa | 124333 |
sign mantissa | -123.333 |
sign mantissa sign exponent | -123.333-12 |
sign mantissa E exponent | -123.333E12 |
sign mantissa E sign exponent | -123.343E-12 |
The mantissa is a fixed-point decimal constant, the sign is a plus (+) or minus (-) symbol, and the exponent is a decimal integer. A zero exponent is assumed if both the letter E and the exponent are omitted.
If, on input, the mantissa includes a decimal point, it overrides the specification of d. If no decimal point is included, then d specifies the number of fractional digits.
The value of w should be large enough to include the mantissa, the optional decimal point in the mantissa, the signs on the exponent and mantissa, the optional letter E, and the exponent. If the field width is too narrow, the stream representation is truncated on the right; if the field width is too wide, excess characters are acquired on the right and may contain invalid input.
Spaces can precede or follow the value in the stream and are ignored. If the entire field contains spaces, zero is assigned to the input target. If the stream representation is not one of the acceptable forms, an ERROR condition is signaled.
Used in a PUT EDIT statement, the E format item converts an output source of any computational type to the following form for representation in the stream:
[-] digit . [fractional-digits] E sign exponent |
Typical representations are as follows:
1.E+07 3.33E-10 -2.7186E+00 |
If d is omitted from the format item, then d = s - 1, where s is the precision of the output source expressed in decimal. The decimal value is rounded before being written out.
The exponent is ordinarily a 2-digit decimal integer and is always signed. The exponent is adjusted so that the first digit of the mantissa is not zero, except that the value 0 is represented as:
0.0000...E+00 |
To account for negative values with fractional digits, the specified width integer should be 7 greater than the number of digits to be represented in the mantissa: one character for the preceding minus sign, one for the decimal point in the mantissa, one for the letter E, one for the sign of the exponent, and two for the exponent itself.
If the number's representation is shorter than the specified field, the representation is right-justified in the field and the number is extended on the left with spaces.
If the field specified by w is too narrow, an ERROR condition is signaled.
The next tables show the relationship between the internal and external representations of numbers that are read or written with the E format item.
The input stream shown in this table is a field of characters beginning at the current position in the stream and continuing to the right. The target type is the type of the variable to which the input value is assigned.
Format Item | Input Stream | Target Type | Target Value |
---|---|---|---|
E(6,0) | 124333... | DECIMAL(10,2) | 124333.00 |
E(6,0) | -123333... | DECIMAL(10,2) | -12333.00 |
E(8) | -123.333... | DECIMAL(8,5) | -123.33300 |
E(11) | -123.333-12... | FLOAT DEC(7) | -1.233330E-10 |
E(11,3) | -123343E-12... | FLOAT DEC(15) | -1.23343000000000E-10 |
Output Examples
The output source value shown in the table is either a constant or the value of a variable that is written out with the associated format item. The # character is used to signify a space.
Output Source Value | Format Item | Output Value |
---|---|---|
-12234 | E(11) | -1.2234E+04 |
-12234 | E(11,2) | ##-1.22E+04 |
12234 | E(11) | #1.2234E+04 |
12234 | E(11,2) | ###1.22E+04 |
-12.234 | E(11,1) | ###-1.2E+01 |
-1.23456E3 | E(12) | -1.23456E+03 |
-1.23456E3 | E(12,2) | ###-1.23E+03 |
F(w[,d]) |
w
A nonnegative integer or expression that specifies the total width in characters of the field in the stream.d
An optional nonnegative integer or expression that specifies the number of fractional digits in the stream representation and whose value is interpreted depending on GET EDIT and PUT EDIT statements as described below.
Used with GET EDIT, the F format item acquires a fixed-point decimal value from the next w characters in the stream and converts it to an input target of any computational type. Fixed-point decimal values can be represented in the stream in the following forms:
The number is a fixed-point decimal constant, and the sign is a plus (+) or minus (-) symbol.
The following are valid representations:
A CONVERSION condition is signaled if the field is not blank and does not contain a valid representation; otherwise, the fixed-point decimal number is extracted from the field and is assigned to the input target, with any necessary conversions. A decimal point included in the number overrides the specification of d. If no decimal point is included, d specifies the number of fractional digits. If d is omitted, it is assumed to be zero.
The value w should be only large enough to include the number, the optional decimal point in the number, and the optional sign. If w is too small, the stream representation is truncated on the right. If w is too large, extra characters, which might include invalid syntax, are acquired.
If w is zero, a null character string is converted and assigned to the input target, and no operation is performed on the stream.
Spaces can precede or follow the number in the stream and are ignored. If the entire string contains spaces or is a null string, the fixed-point decimal constant 0 is converted and assigned to the input target.
Used in a PUT EDIT statement, the F format item converts an output source of any computational type to one of the following forms for representation in the stream:
Typical representations are:
3234 0.23432 3.33 -3234.33 |
The decimal value is rounded before being written out. If d is omitted from the format item, the decimal point is not shown, and only the integral part of the number is shown.
If d is larger than the number of fractional digits to be output, trailing zeros are appended to the output number. All leading zeros to the left of the decimal point are suppressed unless the integral part of the number is zero, in which case one zero appears to the left of the decimal point.
To account for negative values with fractional digits, the specified width integer should be 2 greater than the number of digits to be represented: one character for the preceding minus sign and one for the decimal point in the number.
If the number's representation is shorter than the specified field, the representation is right-justified in the field, and the number is extended on the left with spaces.
If the field is too narrow to represent the integral portion of the output number, an ERROR condition is signaled.
The tables below show the relationship between the internal and external representations of numbers that are read or written with the F format item.
Input Examples
The input stream shown in this table is a field of characters beginning at the current position in the stream and continuing to the right. The target type is the type of the variable to which the input value is assigned.
Format Item | Input Stream | Target Type | Target Value |
---|---|---|---|
F(10,2) | -123456.78... | DECIMAL(10,2) | -123456.78 |
F(10,4) | -1234.56789... | DECIMAL(10,2) | -1234.56 |
F(8,5) | -.123456789... | DECIMAL(5,5) | -0.12345 |
F(10) | 1234.56789... | FLOAT DEC(7) | 1.234568E+03 |
Output Examples
The output source value shown in this table is either a constant or the value of a variable that is written out with the associated format item. The # character is used to specify a space.
Output Source Value | Format Item | Output Value |
---|---|---|
-12.234 | F(3,0) | -12 |
-12.234 | F(6,2) | -12.23 |
-12.234 | F(7,3) | -12.234 |
-1.23456E3 | F(8) | ###-1235 |
-1.23456E3 | F(8,2) | -1234.56 |
'1000'B3 | F(4) | #512 |
'1000000000000000'B | F(5) | 32768 |
'100000'B3 | F(5) | 32768 |
'ABCEDF'B4 | F(10) | ##11259615 |
The F format item describes a line of characters in a stream.
The form of the item is:
L |
Used with GET EDIT, the L format item acquires all characters up to the end of the line and converts it to a character string.
Output with PUT EDITUsed in a PUT EDIT statement, the L format item converts an output source of a character string type to characters and outputs them, padded with blanks if necessary, to fill the remainder of the output line.
The LINE format item sets a print file to a specific line. It can be used only with print files and the PUT EDIT statement. If necessary, blank lines are inserted between the current file position and the specified line, and subsequent output begins on the specified line.
The LINE format item identifies an absolute line position on the current output page; to specify a line position relative to the current line, see SKIP format item.
The form of the LINE format item is:
LINE(w) |
w
An integer, or an expression, that specifies a line on the current page, where line 1 is the first line.
When the LINE format item is executed, the current line is determined. The current line is 1 if the file is at the beginning of a new page. Otherwise, the current line is n+1, where n is the number of complete lines already on the page. The position in the file is then changed as follows:
The picture format item (P) describes a field of characters in the input or output stream. The field can be an input field acquired with GET EDIT or an output field transmitted by PUT EDIT. With GET EDIT, the P format item acquires a pictured value from the input stream. With PUT EDIT, the P format item edits an output source to a specified picture format.
The form of the P format item is:
P 'picture' P'picture' |
'picture'
A picture of the same syntax as for the PICTURE data attribute. The syntax is summarized in PICTURE attribute (see Section 2.2.38. The field width is the total number of characters, exclusive of V, in the picture.
The interpretation of the P format item, for input and output, is given below.
Used with the GET EDIT statement, the P format item acquires a pictured value (a field of characters) from the stream file, extracts its fixed-point decimal value, and assigns the value to an input target of any computational type. The picture describes a field of w characters, where w is the total number of picture characters in the picture, exclusive of the V character.
A string of w characters is acquired from the input stream and validated against the picture specified in the format item. The string is valid if it corresponds to an internal representation that would be created by the specified picture if the picture were used to declare a variable of type PICTURE. If the string is valid, its fixed-point decimal value is extracted and assigned to the input target. If necessary, the value is converted to the type of the input target, following the usual rules (see Section 6.4). If the string is not valid, the CONVERSION condition is signaled.
When no decimal point appears in the input stream item, the scale factor of the item is assumed to be the number of digit positions specified to the right of the V character in the picture. If no V character appears, the scale factor is zero.
Used with the PUT EDIT statement, the P format item outputs a source of any computational type in the specified format. If necessary, the output source is first converted to a fixed-point decimal value, following the PL/I conversion rules. The fixed-point decimal value is then edited by the picture specified in the format item. The P format item therefore describes an output field of w characters, where w is the total number of characters in the picture, exclusive of the V character. If the output source is a pictured value, then its extracted fixed-point decimal value must be capable of being edited by the picture specified in the P format item. Otherwise, the ERROR condition is signaled.
The following tables show the relationship between the internal and external representations of numbers that are read or written with the P format item.
Input Examples
The input stream shown in this table is a field of characters beginning at the current position in the stream and continuing to the right. The # character is used to specify a space. The target type is the type of the variable to which the input value is assigned.
Format Item | Input Stream | Target Type | Target Value |
---|---|---|---|
P'$$$,$$$,$$9V.99DB' | $10,987,654.00DB... | DECIMAL(10,2) | -10987654.00 |
P'$$$,$$$,$$9V.99DB' | ########$10.99##... | DECIMAL(10,2) | 10.99 |
P'SSSSV.SSSSS' | ##-1.12345... | DECIMAL(8,5) | -1.12345 |
P'SSSSV.SSSSS' | +100.12345... | DECIMAL(8,5) | 100.12345 |
P'SSSSV.SSSSS' | #100.12345... | DECIMAL(8,5) | [CONVERSION] |
P'SSSSV.SSSSS' | +1001.2345... | DECIMAL(8,5) | [CONVERSION] |
The last two cases signal the CONVERSION condition. In the first case, the input field has a space instead of a plus symbol or minus symbol in the first position. In the second case, the input field has four digits to the left of the period, and the P format item specifies a maximum of three. The P format item in both cases uses drifting strings of S characters, and, if used to declare a picture variable, the specification could create several different character representations. However, the specification could not have created the last two input fields shown, and they are therefore invalid values.
Note that in the second line in the table, the characters "$10.99" must be surrounded with the number of spaces shown. The drifting dollar signs and the comma insertion characters always specify either digits, the characters themselves, or spaces. Similarly, the characters "DB" in the picture specification specify either these characters or the same number of spaces. If the pictured input value did not contain these spaces, it would be invalid.
Output Examples
The output source value shown in this table is either a constant or the value of a variable that is written out with the associated format item.
Output Source Value Format Item Output Value -12234 P'$$$$$$DB' $12234DB -12234 P 'SSSSSSV.SS' -12234.00 -12.234 P 'T9V.999' J2.234 -1.23456E3 P '-9999V.99' -1234.56 -1.23456E3 P '+ZZZ9V.99' #1234.56
The PAGE format item is used with print files to begin a new page. See Section 9.2.6 for more information on print files.
The form of the PAGE format item is:
PAGE |
Subsequent output begins on line 1 of the next page, and the current
page number for the print file is incremented by 1.
9.2.4.10 R Format Item
The R (remote) format item specifies the label of a FORMAT statement from which some or all of a format specification is obtained by a GET EDIT or PUT EDIT statement.
The form of the R format item is:
R (label) |
label
The label of a FORMAT statement within the same block as the GET EDIT or PUT EDIT statement. If the item occurs in a recursive procedure, the R item and FORMAT statement must occur in the same recursion.
The FORMAT statement describes a remote format-specification list to be used by GET EDIT or PUT EDIT statements. The FORMAT statement and remote (R) format item are useful when the same format specification is used by a large number of GET EDIT or PUT EDIT statements, or both. In this case, a change to the format specification can be made in the single FORMAT statement, rather than in each GET or PUT statement.
The form of the FORMAT statement is:
label: FORMAT (format-specification,...); |
label
A valid PL/I label. A label is required and is specified in the GET EDIT or PUT EDIT statement that contains an R format item in its format-specification list.format-specification
A list of one or more format items that match corresponding input targets in a GET EDIT statement, or output sources in a PUT EDIT statement.
Although the FORMAT statement can contain another R format item, the following restrictions apply:
RFRM: PROCEDURE OPTIONS(MAIN); DECLARE SYSIN STREAM INPUT FILE; DECLARE SYSPRINT PRINT FILE; DECLARE SALARY PICTURE '$$$$$$$$9V.99'; DECLARE (FIRST,MID,LAST) CHARACTER(80) VARYING; DECLARE 1 HIRING, 2 DATE CHARACTER(20) VARYING, 2 EXPERIENCE FIXED, 2 SALARY PICTURE '$$$$$$$$9V.99'; OPEN FILE(SYSIN) TITLE('RFRM.IN'); OPEN FILE(SYSPRINT) TITLE('RFRM.OUT'); GET EDIT(SALARY,FIRST,MID,LAST,DATE,EXPERIENCE,HIRING.SALARY) (F(8,2),R(PERSONNEL_FORMAT)); PUT SKIP LIST(LAST||', '||FIRST||' '||MID||':', 'Hired '||DATE||' at '||HIRING.SALARY); PUT SKIP LIST(EXPERIENCE,' years prior experience'); PUT SKIP LIST('Present salary: '||SALARY); PERSONNEL_FORMAT: FORMAT(R(NAME),A(20),SKIP,F(2),X,F(8,2)); NAME: FORMAT(3(SKIP,A(80))); END RFRM; |
Assume the file RFRM.IN contains the following data:
25005.50 Thomasina A. Delacroix 6 July 1976 2 15003.65 |
The following output, with spacing as shown, will be written to the print file RFRM.OUT:
Delacroix, Thomasina A.: Hired 6 July 1976 at $15003.65 2 years prior experience Present salary: $25005.50 |
The SKIP format item sets a stream file to a new position relative to the current line. It is used with input and output files.
The form of the SKIP format item is:
SKIP [(w)] |
w
An integer, or an expression, giving the number of lines to be skipped; the expression must not convert to a negative integer and must be greater than zero, except for print files. If w is omitted, a value of 1 is assumed.
If w is 1 or is omitted, the file is positioned at the beginning of the next line. If w is greater than 1, w-1 lines are skipped on input, but the ENDFILE condition is signaled if the end of the file is encountered first. On output, w-1 blank lines are inserted. In both cases, the new position is the beginning of (current line)+w.
If w is zero, the file is repositioned at the beginning of the current
line, allowing overprinting of the line. If w is greater than zero, and
either the current line exceeds the page size or the page size is
greater than or equal to the current line plus w, then w-1 blank lines
are inserted. Otherwise, the remainder of the page (the portion between
the current line and the page size) is filled with blank lines, and the
ENDPAGE condition is signaled.
9.2.4.12 TAB Format Item
The TAB format item sets a print file to a specified tab stop. It is used only for output to print files. Within a line, tab stops always occur every eight columns, starting at column 1. The form of the TAB format item is:
TAB [(w)] |
w
An integer, or an expression, that identifies the wth tab stop from the current position; w must not be negative. If w equals zero, no operation is performed. If w is omitted, a value of 1 is assumed.
When the TAB format item is executed, the current column (cc) is determined. If the current position is the beginning of a line, page, or file, then cc is one. Otherwise, cc is the column in the current line at which the next output character would appear. For example, if seven characters have already been written on a line, then the cc is column 8; this is where the next output would occur. The file is then repositioned in one of the following ways:
TAB: PROCEDURE OPTIONS(MAIN); DECLARE OUT STREAM OUTPUT PRINT FILE; OPEN FILE(OUT) LINESIZE(60); PUT FILE(OUT) SKIP EDIT('123456789012345678901234567890') (A); PUT FILE(OUT) SKIP EDIT('COL1','?') (A,TAB(2),A); PUT FILE(OUT) EDIT('!') (TAB(20),A); PUT FILE(OUT) SKIP EDIT('*') (TAB(1),A); PUT FILE(OUT) EDIT('abcdefg') (A); /* cc now = 17 */ PUT FILE(OUT) EDIT('&') (TAB(6),A); END TAB; |
The program TAB writes the following output to the print file OUT.DAT:
123456789012345678901234567890 COL1 ? ! *abcdefg & |
The question mark appears in column 17, which is the second tab stop following the string <BIT_STRING>(COL1). The exclamation point appears in column 1 of the next line because there are fewer than 20 tab stops on the remainder of the line. In the third PUT EDIT statement, the SKIP option first resets the current column to zero. When the TAB format item is executed, it must position the file to the first tab stop that is between column 1 (cc+1) and the end of the line; therefore, the file is positioned, and the asterisk appears, in column 9. Similarly, the fourth statement writes out the string <BIT_STRING>(abcdefg), after which the current column is 17, a tab stop. Because the line size has been established as 60, there are only five tab stops between cc+1 and the end of the line: 25, 33, 41, 49, and 57. Therefore, the format item TAB(6) in the last PUT EDIT statement causes a skip to the next line, and the ampersand appears in column 1.
X [(w)] |
w
An integer, or an expression, that specifies a number of consecutive character positions in the stream; w must not yield a negative integer value. If w yields zero, no operation is performed. If the w is omitted, its value is assumed to be 1.
On input, the next w columns after the current column are skipped.
On output, w spaces are inserted following the current column.
When the output stream is a file, and the end of the current line is reached, the output of spaces continues on the next line until w spaces have been output. The size of the current line is either the default value or the specific value you have established for the file (see Section 9.1 for LINESIZE option). If the file is a print file, the ENDPAGE condition is signaled if the page size is reached; on normal return from the ENDPAGE ON-unit, output of spaces continues at the top of the next page until w spaces have been output.
If the output stream is a character-string variable, w spaces are written to the variable. The ERROR condition is signaled if the maximum length of the string is exceeded.
XFOR: PROCEDURE OPTIONS(MAIN); DECLARE INLINE CHARACTER(80) VARYING; DECLARE FIRSTWORD CHARACTER(80) VARYING; DECLARE OUTFILE PRINT FILE; DECLARE SPACE1 FIXED; GET EDIT(INLINE) (A(1000)); SPACE1 = INDEX(INLINE,' '); /* position of first wordbreak */ FIRSTWORD = SUBSTR(INLINE,1,SPACE1-1); PUT STRING(FIRSTWORD) EDIT (FIRSTWORD,'-FIRST WORD TYPED') (A,X(2),A); PUT SKIP FILE(OUTFILE) LIST(FIRSTWORD); END XFOR; |
The GET EDIT statement in the program XFOR inputs a complete line from a user's terminal, after issuing and receiving an answer to the prompt <BIT_STRING>(Line>). Assume that the interaction is as follows:
Line> beautiful losers[Return] |
beautiful -FIRST WORD TYPED |
The X format item has correctly inserted two spaces between <BIT_STRING>(beautiful) and <BIT_STRING>(-FIRST WORD TYPED).
XFOR2: PROCEDURE OPTIONS(MAIN); DECLARE INLINE CHARACTER(80) VARYING; DECLARE OUTFILE2 PRINT FILE; GET EDIT(INLINE) (X(10),A(1000)); PUT SKIP FILE(OUTFILE2) LIST(INLINE); END XFOR2; |
In the program XFOR2, the GET EDIT statement skips the first 10 characters typed after the prompt and then inputs the remainder of the line. Assume that the interaction is:
Line> ABCDEFGHIJKLMNOPQRSTUVWXYZ[Return] |
KLMNOPQRSTUVWXYZ |
The first 10 letters (A to J) have been ignored on input.
9.2.4.14 Format Specifications
In the GET EDIT, PUT EDIT, and FORMAT statements, format items are used singly or in combination to create format specifications. The syntax of a format specification is as follows:
⎧format-item ⎫ ⎨iteration-factor format-item ⎬ ⎩iteration-factor(format-specification,...)⎭
The iteration factor is an integer or an integer expression that repeats the following format item or the following list of format specifications. Expressions must be enclosed in parentheses. If an integer iteration factor precedes a single format item that is not in parentheses, the iteration factor and the format item must be separated by a space. For example:
PUT EDIT (A) (F(5,2)); |
PUT EDIT (A,B) (2F(5,2)); |
Or:
PUT EDIT (A,B) (2 (F(5,2))); |
An iteration factor can also repeat an entire list of format specifications:
PUT EDIT ( (A(I) DO I = 1 TO 10) ) /* 10 array elements */ ( 2( F(5,2),2(F(7,2),E(8)) ) ); /* 10 format items */ |
Expanded into individual format items, this specification looks like this:
F(5,2),F(7,2),E(8),F(7,2),E(8),F(5,2),F(7,2),E(8),F(7,2),E(8) |
If an expression is used as the iteration factor, it must be enclosed in parentheses, but does not require spaces. For example:
PUT EDIT (A) ((Z*4)F(5,2)); |
In general, data listed in the GET EDIT or PUT EDIT statement is matched to the expanded list of data format items, from left to right, until the end of the input-target or output-source list is reached. Matching occurs only between I/O data and data format items; control format items are executed only if they are encountered while the matching is in progress.
Format-specification lists are used in GET EDIT, PUT EDIT, and FORMAT statements to control the conversion of data between the program and the input or output stream and to precisely control positioning within the input or output stream. This entry describes the syntax of format-specification lists and the manner in which a format list is processed to acquire or transmit data.
This section briefly describes rules and constraints for format-specification lists.
How Edit-Directed Operations Are Performed
This section describes the manner in which format items are matched to input targets or output sources.
All edit-directed input statements include the following syntax:
EDIT (input-target,...) (format-specification,...) |
All edit-directed output statements include the following syntax:
EDIT (output-source,...) (format-specification,...) |
format-specification
One of the following:
- A single control or data format item.
- A construct containing an iteration factor followed by one or more format items.
- An R format item, which specifies the label of a FORMAT statement. In effect, the entire format-specification list in the FORMAT statement is acquired and inserted at the position of the R format item.
Except for picture (P) and remote (R) format items, arguments to format items can be integer expressions.
input-target
One of the following:
- A variable reference, which can be to a scalar or aggregate variable of any computational data type
- A construct with the following syntax:
(input-target,... DO reference=expression[TO expression]
[BY expression] [WHILE(expression)][UNTIL(expression)] )
- A construct with the following syntax:
(input-target,... DO reference=expression[REPEAT
expression] [WHILE (expression)][UNTIL(expression)] )
output-source
One of the following:
- Any expression with a computational value, including references to scalar or aggregate variables of any computational type
- A construct containing a DO specification, as shown for input targets
When PL/I performs an edit-directed operation, it examines the list of input targets or output sources, beginning with the first in the list. If the target or source is an array or structure or contains a DO specification, it is expanded to form a list of individual data items; an array is expanded in row-major order, a structure is expanded in the order of its declaration, and items preceding a DO specification are expanded according to the DO specification.
Within a single target or source, items at the innermost level of parentheses are processed first.
Given a list of one or more data items contained in the first target or source, PL/I processes the data items from left to right. Beginning with the leftmost data item, and for each subsequent item, PL/I executes format items until the data item has been either assigned a value from the input stream, or converted to a character representation and placed in the output stream. Control format items are therefore executed in the order in which they occur in the format-specification list. With the first target or source, the execution of format items begins with the leftmost format item in the format-specification list. If the end of the format-specification list is reached, PL/I returns to the leftmost format item and continues.
When all items contained in the first target or source have been processed, PL/I operates on the next target or source. The target or source is evaluated, and PL/I then examines the format-specification list, beginning where the previous operation stopped.
This processing continues until all data items in the input-target or output-source list have been processed, at which point the edit-directed statement terminates. If this occurs while PL/I is in the middle of the list of format items, the format items to the right are not executed.
The following examples show typical edit-directed operations. All cases shown are for input (GET EDIT), but the operations for PUT EDIT are similar. The simple cases, shown first, are with input targets that are scalar variable references. The next cases shown are with aggregate (array and structure) references. The last cases shown are with DO specifications.
Scalar Variables
The following examples have input targets that are scalar variables:
GET EDIT (A,B,C,D) (A(12),F(5,2),F(6,2),A(14));This statement acquires four values from the input stream: a 12-character string, a 5-digit fixed-point decimal number, a 6-digit fixed-point decimal number, and a 14-character string, and assigns these values, with any necessary conversions, to the target variables A, B, C, and D, respectively.
GET EDIT (A,B,C,D) (A(12));This statement acquires four 12-character strings and assigns them (with conversions, if necessary) to the targets A, B, C, and D.
GET EDIT (A,B,C,D) (A(12), 2 F(5,2), A(14));This statement acquires a 12-character string, two fixed-point decimal numbers, and a 14-character string, in that order, and assigns them to A, B, C, and D. (You can use embedded spaces in format lists, as elsewhere, for clarity; the space between 2 and F(5,2) is required.)
GET EDIT (A,B,C,D,E) ( 2( A(12),A(14) ), A(20) );This statement acquires, in order, a 12-character string, a 14-character string, another 12-character string, another 14-character string, and a 20-character string, and assigns the strings, in that order, to A, B, C, D, and E.
GET EDIT (A,B,C,D,E) ( 2( A(12),A(14) ), SKIP, A(20) );This statement performs the same operation as in the previous example, but acquires the 20-character string from the next line.
Aggregates
The following examples have input targets that are references to array and structure variables:
A is an array of five elements or a structure with five scalar members. This statement expands A to a list of individual data items. Then it acquires, in order, a 12-character string, a 14-character string, another 12-character string, another 14-character string, and a 20-character string, and assigns the strings, in that order, to the elements A(1) through A(5) (if an array) or to the five members of structure A in the order in which the members are declared.
GET EDIT (A) ( 2( A(12),A(14) ), A(20) );
Both A and B are aggregates with five elements or members. For A, this statement performs the same operation as in the previous example, and then repeats the operation for B, using the same format list each time. Because there are five format items specified, and the aggregates both have five elements or members, strings of the same length are acquired for corresponding elements of A and B.
GET EDIT (A,B) ( 2( A(12),A(14) ), A(20) );
NAME is a structure declared as:
GET EDIT (NAME) (SKIP,A(20),SKIP,A(80));
DECLARE 1 NAME 2 FIRST CHARACTER(20) VARYING, 2 LAST CHARACTER(80) VARYING;This statement skips to the next line and acquires a 20-character string. It assigns the string to NAME.FIRST. This statement skips to the next line and acquires an 80-character string. This statement assigns that string to NAME.LAST.
Both A and B are 4-element arrays. From the current line, this statement executes A(12), A(14), A(12), and A(14), in that order, and assigns the results to A(1) through A(4). This statement then skips to the next line and executes A(20), A(12), A(14), and A(12), in that order, and assigns the results to B(1) through B(4); the list of data items is now exhausted, so this statement does not execute SKIP a second time.
GET EDIT (A,B) ( 2( A(12),A(14) ), SKIP, A(20) );DO Specifications
The following examples have input targets that include DO specifications. The DO specifications control the assignment of input values to variables that are arrays and based structures.
B is a 10-element array. Notice that the parentheses surrounding the first input target are in addition to the parentheses surrounding the entire input-target list. This statement executes the format items A(12), A(14), A(12), and A(14), in that order, and assigns the resulting strings to elements B(10), B(8), B(6), and B(4), respectively. This statement then executes A(20) and assigns the result to B(1).
GET EDIT ( (B(I) DO I=10 TO 4 BY -2) , B(1) ) ( 2( A(12),A(14) ), A(20) );
A is a two-dimensional array of 20 rows and 10 columns. Two hundred decimal integers are acquired and assigned to the array elements in the order A(1,1), A(1,2),... ,A(20,10). Elements with odd-numbered columns receive 5-digit integers, and those with even-numbered columns, 6-digit integers. Because the DO specifications specify row-major order, the same operation is performed by the next example.
GET EDIT ( ( (A(I,J) DO J=1 TO 10) DO I=1 TO 20) ) (F(5),F(6));
GET EDIT (A) (F(5),F(6));Because row-major order is the default, nested DO specifications are used to change the order in which values are assigned.
The example has the same effect as the following DO-group:
DO I = 1 TO 20; DO J = 1 TO 10; GET EDIT(A(I,J)) (F(5),F(6)); END; END;Compared with a DO construct in the input-target list, however, the use of nested DO groups is much less efficient in execution speed. In addition, the identical effect is not generally true for all stream I/O statements. For instance:
GET SKIP EDIT(input-target,...) (format-specification,...);This statement has different effects in the two cases. If it occurs in a pair of nested DO groups, as shown previously, the SKIP option is executed on each iteration of the innermost DO group. If instead the DO specifications are in the input-target list, the SKIP option is executed only once, before any other input processing is performed.
CURRENT and FIRST are pointers, and PERSON is a based structure declared as:
GET EDIT ( ( CURRENT->PERSON.NAME DO CURRENT = FIRST REPEAT CURRENT->PERSON.NEXT WHILE (CURRENT ^= NULL) ) ) (A(80));
DECLARE /* Based structure for list elements: */ 1 PERSON BASED, 2 NEXT POINTER, /* Pointer to next element: */ 2 NAME CHARACTER(80) VARYING; DECLARE /* Pointers to first and current list elements: */ (FIRST,CURRENT) POINTER;The GET EDIT statement acquires 80-character strings from the input stream and assigns each to a list member PERSON.NAME. On the first input operation, the string is assigned to FIRST->PERSON.NAME. On subsequent iterations of the DO specification, the pointer to the next element, PERSON.NEXT, is assigned to CURRENT before the input operation. Before each input operation, including the first, the WHILE clause tests to determine whether the end of the queued list has been reached (indicated by the null pointer).
The DO REPEAT construct is often used in this type of application. You should provide a WHILE or UNTIL clause in this or any DO REPEAT construct to be sure that the operation has a defined termination. However, the WHILE or UNTIL clause is not required.
If the input or output stream is a character string, the processing is similar to the processing of files, but the positioning options are more limited:
In most applications, the terminal is treated as a stream file. You can explicitly declare a stream file to be associated with a user's terminal. The stream input and output statements, GET and PUT, use the default PL/I files SYSIN (the terminal) and SYSPRINT, respectively, when no file reference is included in the statement.
This file is associated with the standard input file,
which in turn is usually assigned to the user's terminal. The PL/I
print file SYSPRINT is associated with the standard
output, which, in interactive mode, is also assigned to the user's
terminal.
9.2.6.1 Simple Input from a Terminal
Simple input from a terminal is accomplished with the GET LIST statement, which in its simple form has the following format:
GET LIST (input-target,...); |
Because this statement has no reference to a specific file, the default file SYSIN (the terminal) is assumed. When this GET LIST statement is executed in a program, the program pauses until enough values are typed by the user to satisfy the input-target list.
The user must separate the values with the Return key, spaces, or commas. The user must press the Return key to send the typed line to the program.
In the context of simple terminal input, the input targets are usually simple variable references. For example:
GET LIST (SALARY,CONTRIBUTION(42),PAYROLL.DEDUCTION); |
15500,500,1200[Return] 15500[Return]500[Return]1200[Return] 15500 500[Return]1200[Return] |
If you press Return in response to an input request from GET LIST, the null character string '' is assigned to the input target. If you press Return in response to an input request from GET EDIT, the requested field width is filled with spaces and assigned to the input target under control of the corresponding format item. (Note that an all-space field causes an error for B formats.)
For full details on input targets, see the GET LIST statement ( Section 9.2.2).
PUT LIST (output-source,...); |
The output sources in simple cases are expressions, including variable references. The PUT LIST statement converts the results of the expressions to the appropriate character representations and sends the character strings to the terminal. For instance:
PUT LIST (A,B,C); |
The file SYSPRINT, used as the default output stream by PUT LIST, is a
print file, and the terminal has the characteristics of print files.
9.2.6.3 Print File
A print file is a stream output file that is intended for output on a terminal, line printer, or other output device. You can declare any stream output file to be a print file by using the PRINT attribute. The default stream output file, SYSPRINT, is a print file.
The following list describes the special features of print files, as opposed to ordinary stream output files:
SIMPLE_INPUT: PROCEDURE OPTIONS (MAIN); /* Simple input from user's terminal */ DECLARE BADGE_NUMBER FIXED DECIMAL (5), SOCIAL_SECURITY_NUMBER CHARACTER(11); GET LIST (BADGE_NUMBER,SOCIAL_SECURITY_NUMBER); PUT LIST (BADGE_NUMBER,SOCIAL_SECURITY_NUMBER); END SIMPLE_INPUT; |
PL/I does not display a prompt character on the terminal when a program executes a GET or READ statement. Consequently, it is difficult to tell that a program is trying to read data unless the program executes an output statement containing a prompting message. The program SIMPLE_INPUT would be easier to use if the following statement appeared immediately before GET LIST:
PUT SKIP LIST('Enter badge number, social security number:'); |
The cursor remains on the same line after the prompt is displayed, so the input can be entered on the same line. The completed line might be as:
Enter badge number, social security number: 7,116-40-0482[Return] |
TIN: PROCEDURE OPTIONS(MAIN); DECLARE STRING CHAR(10) VARYING, I FIXED BINARY STATIC INITIAL(0), A FLOAT BINARY; DECLARE EOF BIT STATIC INITIAL('0'B); ON ENDFILE(SYSIN) EOF = '1'B; PUT SKIP LIST('Enter string, integer, float>'); GET LIST(STRING,I,A); DO WHILE(^EOF); /* stop when EOF character is typed */ PUT SKIP LIST(STRING,I,A); PUT SKIP LIST('Enter string, integer, float>'); GET LIST(STRING,I,A); END; END TIN; |
Here, the user is prompted to enter three values from the default file SYSIN. The three values are immediately written out to the default file SYSPRINT. This sequence continues until the user answers the prompt with a EOF character, which signals the ENDFILE condition for SYSIN; the program then terminates. The following is a sample dialog with the program:
Enter string, integer, float> JONES,27,3.75[Return] JONES 27 3.7500000E+00 Enter string, integer, float> JONES 27 3.75[Return] JONES 27 3.7500000E+00 Enter string, integer, float> JONES[Return] 27[Return]3.75[Return] JONES 27 3.7500000E+00 Enter string, integer, float> DOOLEY[Return][Return]4E-6[Return] DOOLEY 0 4.0000000E-06 Enter string, integer, float> [Ctrl/Z] $ |
Notice that input fields are separated by commas, spaces, or the RETURN key. Notice also that entering a blank line after <BIT_STRING>(DOOLEY) causes the program to set the value of I to zero.
The following topics are of interest in terminal I/O applications:
Record I/O is performed by the READ, WRITE, DELETE, and REWRITE statements. In record I/O, each I/O statement processes an entire record. (In stream I/O, more than one line or record can be processed by a single statement.) Table 9-4 summarizes the file description attributes that apply to record I/O.
PL/I OPEN statement.
The READ statement reads a record from a file, either the next record or a record specified by the KEY option. The file must have either the INPUT or the UPDATE attribute.
The format of the READ statement is:
READ FILE (file-reference) ⎰INTO (variable-reference)⎱ ⎱SET (pointer-variable) ⎰ ⎡KEY (expression) ⎤ ⎣KEYTO (variable-reference)⎦
FILE(file-reference)
The file from which the record is to be read. If the file is not currently open, PL/I opens the file with the implied attributes RECORD and INPUT. The implied attributes are merged with the attributes specified in the file's declaration.INTO (variable-reference)
An option that specifies that the contents of the record are to be assigned to the specified variable. The variable must be an addressable variable. The INTO and SET options are mutually exclusive.
- If the variable has the VARYING attribute and the file does not
- For any other type of variable, the record is copied into the variable's storage. If the record is not exactly the same size as the target variable, as much of the record as will fit is copied into the variable and the ERROR condition is signaled.
SET (pointer-variable)
An option that specifies that the record should be read into a buffer allocated by PL/I and that the specified pointer variable should be assigned the value of the location of the buffer in storage. The SET and INTO options are mutually exclusive.This buffer remains allocated until the next operation on the file but no longer. Therefore, do not use either the pointer value or the buffer after the next operation on the file. The only valid use of the buffer during a subsequent I/O operation is in a REWRITE statement. In this case, you can rewrite the record from the buffer before the buffer is deallocated.
KEY (expression)
An option that specifies that the record to be read is to be located using the key specified by the expression. The file must have the KEYED attribute. The key value must have a computational data type. The KEY and KEYTO options are mutually exclusive.KEYTO (variable-reference)
An option that specifies that the key of the record being read is to be assigned to the designated variable. The value of the key is converted from the data type implied by the file's organization to the data type of the variable. The variable must have a computational data type but cannot be an unaligned bit string or an aggregate consisting entirely of unaligned bit strings. The KEYTO and KEY options are mutually exclusive.KEYTO is specified only for a file that has both the KEYED and SEQUENTIAL attributes.
If the file is accessed sequentially, the READ statement reads the file's next record. If the next-record position is at the end-of-file, the ENDFILE condition is signaled.
After a successful read operation, the file's current record position denotes the record that was just read. The next-record position denotes the following record or, if there is no following record, the end-of-file.
If any error other than an incorrect record size occurs, the current record becomes undefined and the next record is the same as it was before the read operation was attempted.
COPY: PROCEDURE; DECLARE INREC CHARACTER(80) VARYING, ENDED BIT(1) STATIC INIT('0'B), (INFILE,OUTFILE) FILE; OPEN FILE (INFILE) RECORD INPUT TITLE('RECFILE.DAT'); OPEN FILE (OUTFILE) RECORD OUTPUT TITLE('COPYFILE.DAT'); ON ENDFILE(INFILE) ENDED = '1'B; READ FILE(INFILE) INTO (INREC); DO WHILE (^ENDED); WRITE FILE (OUTFILE) FROM (INREC); READ FILE (INFILE) INTO (INREC); END; CLOSE FILE(INFILE); CLOSE FILE(OUTFILE); RETURN; END; |
The program COPY reads a file with variable-length records into a character string with the VARYING attribute and writes the records to a new output file.
It uses a DO-group to read the records in the file sequentially until the end-of-file is reached. It uses the ON statement to establish the action to be taken when the end-of-file occurs: it sets the bit ENDED to <BIT_STRING>(1)B so that the DO-group will not be executed again.
The VARYING character-string variable INREC has a maximum length of 80 characters. If any record in the file is more than 80 characters, the RECORD condition is signaled. If no RECORD ON-unit exists, the program exits.
DECLARE 1 STATE, 2 NAME CHARACTER(30), 2 CAPITAL, 3 NAME CHARACTER(20), . . . 2 SYMBOLS, 3 FLOWER CHARACTER(30), 3 BIRD CHARACTER(30), STATE_FILE FILE, INPUT_NAME CHARACTER(30) VARYING; . . . OPEN FILE(STATE_FILE) KEYED; PUT SKIP LIST('State?'); GET LIST(INPUT_NAME); READ FILE(STATE_FILE) INTO(STATE) KEY(INPUT_NAME); PUT SKIP LIST('The flower of',STATE.NAME,'is the', STATE.SYMBOLS,STATE.SYMBOLS.FLOWER); |
This example shows the use of a keyed READ statement to access a record access, and the READ statement specifies the key of interest in the KEY option. The value for this option is determined at run time by a GET statement. In the READ statement, the contents of a record from the file STATE_FILE are read into the structure STATE.
PRINT_DATA: PROCEDURE OPTIONS(MAIN); DECLARE 1 EMPLOYEE BASED (EP), 2 NAME, 3 LAST CHAR(30), 3 FIRST CHAR(20), 3 MIDDLE_INIT CHAR(1), 2 DEPARTMENT CHAR(4), 2 SALARY FIXED DECIMAL (6,2), EP POINTER, EMP_FILE FILE; DECLARE EOF BIT(1) STATIC INIT('0'B), NUMBER FIXED BIN(31); ON ENDFILE(EMP_FILE) EOF = '1'B; OPEN FILE(EMP_FILE) INPUT SEQUENTIAL KEYED; READ FILE(EMP_FILE) SET(EP) KEYTO(NUMBER); DO WHILE (^EOF); PUT SKIP LIST('EMPLOYEE',NUMBER, EMPLOYEE.NAME.FIRST,EMPLOYEE.NAME.LAST, EMPLOYEE.NAME.MIDDLE_INIT); READ FILE(EMP_FILE) SET(EP) KEYTO(NUMBER); END; CLOSE FILE(EMP_FILE); END; |
This program accesses a relative file sequentially with READ statements
and obtains the key value of each record, that is, the relative record
number. The records in the file EMP_FILE are arranged according to
employee numbers. Each employee number corresponds to a relative record
number in the file. The READ statements read records into the based
structure EMPLOYEE and set the pointer EP to the location of the
allocated buffer. The READ statements specify the KEYTO option to
obtain the record number of each record. The procedure prints the
employee numbers and names. When the last record has been read, the
program closes the input file and exits.
9.3.2 WRITE Statement
The WRITE statement adds a record to a file, either at the end of a file that has the SEQUENTIAL and OUTPUT attributes, or in a specified key position in a file that has the KEYED and OUTPUT attributes or the KEYED and UPDATE attributes. The format of the WRITE statement is:
FILE (file-reference)
A reference to the file to which the record is to be written. If the file is not currently open, the WRITE statement opens the file with the implied attributes RECORD, OUTPUT, and SEQUENTIAL; these attributes are merged with the attributes specified in the file's declaration.FROM (variable-reference)
A reference to the variable containing data for the output record. The variable must be addressable.If the variable has the VARYING or the AREA attribute, the WRITE statement writes only the current value of the varying string or the area into the specified record. In all other cases, the WRITE statement writes the entire storage of the variable. If the contents of the variable do not fit the specified record size, the WRITE statement outputs as much of the variable as will fit, and the ERROR condition is signaled.
KEYFROM (expression)
An option specifying that the record to be written is to be positioned in the file according to the key specified by the expression. The file must have the KEYED attribute.The nature of the key depends on the file's organization, as follows:
- If the file is a relative file or a sequential disk file with fixed-length records, the key value is a fixed binary value indicating the relative record number of the record to be written. record's primary key. PL/I copies the key value specified into the correct key field position (or positions, if segmented keys are used). the specified expression is converted to the data type of the key. If the specified key value cannot be converted to the data type of the key, the KEY condition is signaled.
TRUNC: PROCEDURE; DECLARE INREC CHARACTER(80) VARYING, OUTREC CHARACTER(80), ENDED BIT(1) STATIC INIT('0'B), (INFILE,OUTFILE) FILE; OPEN FILE (INFILE) RECORD INPUT TITLE('RECFILE.DAT'); OPEN FILE (OUTFILE) RECORD OUTPUT TITLE('TRUNCFILE.DAT'); ON ENDFILE(INFILE) ENDED = '1'B; READ FILE(INFILE) INTO (INREC); DO WHILE (^ENDED); OUTREC = INREC; WRITE FILE (OUTFILE) FROM (OUTREC); READ FILE (INFILE) INTO (INREC); END; CLOSE FILE(INFILE); CLOSE FILE(OUTFILE); RETURN; END; |
This program reads a file with variable-length records into a character string with the VARYING attribute and creates a sequential output file in which each record has a fixed length of 80 characters.
The ENVIRONMENT attribute for the file OUTFILE specifies the record format and length of each fixed-length record.
When records are written to a file with fixed-length records, the variable specified in the FROM option must have the same length as the records in the target output file. Otherwise, the ERROR condition is signaled. Thus, in this example, each record read from the input file is copied into a fixed-length character-string variable for output.
Each time this program is executed, it creates a new version of the file TRUNCFILE.DAT.
ADD_EMPLOYEE: PROCEDURE; DECLARE 1 EMPLOYEE, 2 NAME, 3 LAST CHAR(30), 3 FIRST CHAR(20), 3 MIDDLE_INIT CHAR(1), 2 DEPARTMENT CHAR(4), 2 SALARY FIXED DECIMAL (6,2), EMP_FILE FILE; DECLARE MORE_INPUT BIT(1) STATIC INIT('1'B), NUMBER FIXED DECIMAL (5,0); OPEN FILE(EMP_FILE) DIRECT UPDATE; DO WHILE (MORE_INPUT); PUT SKIP LIST('Employee Number'); GET LIST (NUMBER); PUT SKIP LIST ('Name (Last, First, Middle Initial)'); GET LIST (EMPLOYEE.NAME.LAST,EMPLOYEE.NAME.FIRST, EMPLOYEE.NAME.MIDDLE_INIT); PUT SKIP LIST('Department'); GET LIST (EMPLOYEE.DEPARTMENT); PUT SKIP LIST('Starting salary'); GET LIST(EMPLOYEE.SALARY); WRITE FILE (EMP_FILE) FROM (EMPLOYEE) KEYFROM(NUMBER); PUT SKIP LIST('More?'); GET LIST(MORE_INPUT); END; CLOSE FILE(EMP_FILE); RETURN; END; |
This procedure adds records to the existing relative file EMP_FILE. The file is organized by employee numbers, and each record occupies the relative record number in the file that corresponds to the employee number.
The file is opened with the DIRECT and UPDATE attributes, because
records to be written will be chosen by key number. Within the
DO-group, the program prompts for data for each new record that will be
written to the file. After the data is input, the WRITE statement
specifies the KEYFROM option to designate the relative record number.
The number itself is not a part of the record but will be used to
retrieve the record when the file is accessed for keyed input.
9.3.3 DELETE Statement
The DELETE statement deletes a record from a file. This record can be the current record, the record specified by the KEY option, or the record specified by the RECORD_ID option. The file must have the UPDATE attribute.
The format of the DELETE statement is:
DELETE FILE(file-reference) [KEY (expression)] |
FILE(file-reference)
A reference to the file from which the specified record is to be deleted. If the file is not currently opened, PL/I opens the file with the implied attributes RECORD and UPDATE; these attributes are merged with the attributes specified in the file's declaration.KEY (expression)
An option specifying that the record to be deleted will be located by the key specified in the expression. The file must have the KEYED attribute.
The next record is set to denote the record following the deleted record. The current record is undefined.
The program BAD_RECORD, below, deletes an erroneous record in an in the file is the name of a state.
BAD_RECORD: PROCEDURE OPTIONS(MAIN); DECLARE STATE_FILE FILE KEYED UPDATE; OPEN FILE(STATE_FILE) TITLE('STATEDATA.DAT'); DELETE FILE(STATE_FILE) KEY('Arklansas'); CLOSE FILE(STATE_FILE); RETURN; END; |
The file is opened with the UPDATE attribute, and the OPEN statement
gives the file specification of the file from which the record is to be
deleted.
9.3.4 REWRITE Statement
The REWRITE statement replaces a record in a file. The record is either the current record or the record specified by the KEY option. The file must have the UPDATE attribute. The format of the REWRITE statement is:
FILE(file-reference)
The file that contains the record to be replaced. If the file is not open, it is opened with the implied attributes RECORD and UPDATE; these attributes are merged with the attributes specified in the file's declaration.FROM (variable-reference)
An option giving the variable that contains the data needed to rewrite the record. The variable must be an addressable variable.If the FROM option is not specified, there must be a currently allocated buffer from an immediately preceding READ statement that specifies the SET option, and this file must have the SEQUENTIAL attribute. In this case, the record is rewritten from the buffer containing the record that was read. Note that if the file organization is sequential, the record being rewritten must be the same length as the one read.
If the variable has the VARYING or the AREA attribute, the REWRITE statement writes only the current value of the varying string or area into the specified record. In all other cases, the REWRITE statement writes the variable's entire storage.
KEY (expression)
An option specifying that the record to be rewritten is to be located using the key specified by expression. The file must have the KEYED attribute. The expression must have a computational data type. The FROM option must be specified.The nature of the key depends on the file's organization, as follows:
- If the file is a relative file or a sequential disk file with fixed-length records, the key is a fixed binary value indicating the relative record number of the record to be rewritten. value that is contained within a record. The data type of the key and its location within the record are as specified when the file was created. The primary key field in the record cannot be modified.
The value of the specified expression is converted to the data type of the key. If no record with the specified key exists, if the value specified is not valid for conversion to the data type of the key, or modified, the KEY condition is signaled.
The next record position is set to denote the record immediately following the record that was rewritten or, if there is no following record, the end-of-file.
The current record is set to designate the record just rewritten.
The procedure NEW_SALARY, below, updates the salary field in a relative file containing employee records. The procedure receives two input parameters: the employee number and the new salary. The employee number is the key value for the records in the relative file.
NEW_SALARY: PROCEDURE (EMPLOYEE_NUMBER,PAY); DECLARE EMPLOYEE_NUMBER FIXED DECIMAL(5,0), PAY FIXED DECIMAL (6,2); DECLARE 1 EMPLOYEE, 2 NAME, 3 LAST CHAR(30), 3 FIRST CHAR(20), 3 MIDDLE_INIT CHAR(1), 2 DEPARTMENT CHAR(4), 2 SALARY FIXED DECIMAL (6,2), EMP_FILE FILE; OPEN FILE(EMP_FILE) DIRECT UPDATE; READ FILE(EMP_FILE) INTO(EMPLOYEE) KEY (EMPLOYEE_NUMBER); EMPLOYEE.SALARY = PAY; REWRITE FILE(EMP_FILE) FROM(EMPLOYEE) KEY(EMPLOYEE_NUMBER); CLOSE FILE(EMP_FILE); RETURN; END; |
In this example, the KEY option is specified in the READ statement that obtains the record of interest and in the REWRITE statement that replaces the record, with its new information, in the file. The FROM and KEY options must both be specified on the REWRITE statement.
The sample program CHANGE_HEADER, below, changes the contents of the first record in the sequentially organized file TITLE_PAGE. The file consists of 80-byte, fixed-length records.
CHANGE_HEADER: PROCEDURE OPTIONS(MAIN); DECLARE TITLE_PAGE FILE SEQUENTIAL UPDATE, INREC CHARACTER(80) BASED(P), P POINTER; OPEN FILE(TITLE_PAGE); READ FILE(TITLE_PAGE) SET(P); INREC = 'Summary of Courses for Fall 1980'; REWRITE FILE(TITLE_PAGE); CLOSE FILE(TITLE_PAGE); RETURN; END; |
In this example, the READ statement specifies the SET option. The input
record is read into a buffer, INREC, that is a based character-string
variable. The assignment statement modifies the buffer, and the REWRITE
statement rewrites the record. Because the REWRITE statement does not
specify a FROM option, PL/I uses the contents of the buffer to rewrite
the current record in the file (that is, the record that was just read).
9.3.5 Position Information for a Record File
When a record file is open, PL/I maintains the following position information:
When a file is opened the current record is undefined and the next record designates the first record in the file or, if the file is empty, the end-of-file.
After a sequential read operation, the current record designates the record just read. The next record indicates the following record or, if there are no more records, the end-of-file.
After a keyed I/O statement, that is, an I/O statement that specifies the KEY or KEYFROM option, the current record and next record are set as described in the following chart. (X is the record specified by key and X+1 is the next record or, if there are no more records, the end-of-file.)
Statement | Current Record | Next Record |
---|---|---|
READ | X | X+1 |
WRITE | X | X+1 |
REWRITE | X | X+1 |
DELETE | undefined | X+1 |
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.
Subset G includes only one preprocessor statement, %INCLUDE. This
statement copies the text of an external file into the source file at
compile time. Non-standardized versions of the preprocessor include other
statements beginning with % that can control the course of compilation,
issue user-generated diagnostic messages, and selectively control listings
and formats.
10.1 %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
Examplesfile-spec
A file specification enclosed in apostrophes interpreted as the complete file specification or path.module-name
An implementation-dependent sequence of characters having an implementation-dependent meaning.
%INCLUDE 'SUM.PLI'; |
This statement copies the contents of the file SUM.PLI into the current file during compilation.
%INCLUDE SYSTEM_PROCEDURES; |
Includes a module named SYSTEM_PROCEDURES, whose contents is implementation-dependent.
PL/I provides a set of predefined functions, subroutines, and variables called built-in functions, built-in subroutines, and pseudo-variables respectively.
This chapter describes the built-in functions, subroutines, and
pseudo-variables that you can use in your PL/I programs.
11.1 Built-In Function Arguments
Built-in functions are similar to operators, and their arguments are similar to operands. Built-in function arguments, if arithmetic, are converted to their derived type before the function reference is evaluated. All evaluations of built-in functions are performed in the result type. The arguments are converted again from the derived type to the result type if necessary. The precision of the result is the greater of the precisions of the two arguments.
For instance, all the mathematical functions return floating-point values; their arguments are converted to floating point (binary or decimal, depending on the base of the argument) before the operation is performed. For example:
DCL J FIXED BINARY(8); FT = ATAN(J,2); |
Here the derived type of J and 2 is fixed-point binary. The converted precision of 2 is ceil(1/3.32)+1 , or 2. The result type is FLOAT BINARY(8). Both arguments are then converted to FLOAT BINARY(8), and the ATAN operation is performed.
Note the following restrictions on built-in function arguments:
Built-in functions, like other operations, can signal conditions. The
mathematical functions, which are computed in floating point, can
signal OVERFLOW and UNDERFLOW under the appropriate conditions.
Functions that are computed in fixed point can signal FIXEDOVERFLOW. In
general, string and other functions signal ERROR if a result cannot be
computed. Refer to Section 8.10.4 for more information about condition
handling.
11.3 Summary of Built-In Functions
The built-in functions are summarized in PL/I, according to the following categories:
Section 11.4 provides detailed descriptions of the functions listed in PL/I.
Category | Function Reference | Value Returned |
---|---|---|
Arithmetic | ABS(x) | Absolute value of x |
ADD(x,y,p[,q]) | Value of x+y, with precision p and scale factor q | |
CEIL(x) | Smallest integer greater than or equal to x | |
DIVIDE(x,y,p[,q]) | Value of x divided by y, with precision p and scale factor q | |
FLOOR(x) | Largest integer that is less than or equal to x | |
MAX(x,y) | Larger of the values x and y | |
MIN(x,y) | Smaller of the values x and y | |
MOD(x,y) | Value of x modulo y | |
MULTIPLY(x,y,p[,q]) | Value of x*y, with precision p and scale factor q | |
ROUND(x,k) | Value of x rounded to k digits | |
SIGN(x) | -1, 0, or 1 to indicate the sign of x | |
SUBTRACT(x,y,p[,q]) | Value of x-y, with precision p and scale factor q | |
TRUNC(x) | Integer portion of x | |
Mathematical | ACOS(x) | Arc cosine of x (angle, in radians, whose cosine is x) |
ASIN(x) | Arc sine of x (angle, in radians, whose sine is x) | |
ATAN(x) | Arc tangent of x (the angle, in radians, whose tangent is x) | |
ATAND(x,y) | Arc tangent of x (the angle, in radians, whose sine is x and whose cosine is y) | |
ATANH(x) | Hyperbolic arc tangent of x | |
COS(x) | Cosine of radian angle x | |
COSD(x) | Cosine of degree angle x | |
COSH(x) | Hyperbolic cosine of x | |
EXP(x) | Base of the natural logarithm, e, to the power x | |
EXPONENT(x) | Exponent of the representation base (x is BINARY FLOAT) | |
LOG(x) | Logarithm of x to the base e | |
LOG10(x) | Logarithm of x to the base 10 | |
LOG2(x) | Logarithm of x to the base 2 | |
SIN(x) | Sine of the radian angle x | |
SIND(x) | Sine of the degree angle x | |
SINH(x) | Hyperbolic sine of x | |
SINH(x) | Hyperbolic sine of x | |
SQRT(x) | Square root of x | |
TAN(x) | Tangent of the radian angle x | |
TAND(x) | Tangent of the degree angle x | |
TANH(x) | Hyperbolic tangent of x | |
String-Handling | BOOL(x,y,z) | Result of Boolean operation z performed on x and y |
COLLATE() | Character set | |
COPY(s,c) | c copies of specified string, s | |
EVERY(s) | Boolean value indicating whether every bit in bit string s is '1'B | |
HIGH(c) | String of length c of repeated occurrences of the highest character in the collating sequence | |
INDEX(s,c[,p]) | Position of the character string c within the string s, starting at position p | |
ISOCHAR(n1,n2) | Character whose Latin-1 code is n1*16+n2, where n1 is 0, 1, 8, or 9 and n2 is between 0 and 15 inclusive | |
LENGTH(s) | Number of characters or bits in the string s | |
LOW(c) | String of length c of repeated occurrences of the lowest character in the collating sequence | |
MAXLENGTH(s) | Maximum length of varying string s | |
REVERSE(s) | Reverse of the source character string or bit string | |
SEARCH(s1,s2[,n]) | Index of the first element of s2 that appears in s1, starting from n (default 1) | |
SOME(s) | Boolean value indicating whether at least one bit in bit string s is '1'B | |
STRING(s) | Concatenation of values in array or structure s | |
SUBSTR(s,i[,j]) | Part of string s beginning at i for j characters | |
TALLY(s1,s2) | Count of times s2 appears in s1, including overlaps | |
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 | |
Conversion | BINARY(x[,p[,q]]) | Binary value of x with precision p and scale factor q |
BIT(s[,l]) | Value of s converted to a bit string of length l | |
CHARACTER(s[,l]) | Value of s converted to a character string of length l | |
DECIMAL(x[,p[,q]]) | Decimal value of x | |
EDIT(n,ps) | Converts arithmetic value x to a string based on picture string ps | |
FIXED(x,p[,q]) | Fixed arithmetic value of x | |
FLOAT(x,p) | Floating arithmetic value of x | |
UNSPEC(x[,p[,l]]) | Internal coded form of x, located at position p with length l | |
Conditions | ONCODE() | Error code of the most recent run-time error |
ONFILE() | Name of file constant for which the most recent ENDFILE, ENDPAGE, KEY, or UNDEFINEDFILE condition was signaled | |
ONKEY() | Value of key that caused KEY condition | |
ONSOURCE() | Field containing the erroneous character when the CONVERSION condition was raised | |
Array-Handling | DIMENSION(x[,n]) | Extent of the nth dimension of x |
HBOUND(x[,n]) | Higher bound of the nth dimension of x | |
LBOUND(x[,n]) | Lower bound of the nth dimension of x | |
PROD(x) | Arithmetic product of all the elements in x | |
SUM(x) | Arithmetic sum of all the elements in x | |
Storage | ADDR(x) | Pointer identifying the storage referenced by x |
EMPTY() | An empty area value | |
OFFSET(p,a) | An offset into the location in area a pointed to by pointer p | |
POINTER(o,a) | A pointer to the location at offset o within area a | |
Timekeeping | DATE() | System date in the form YYMMDD |
DATETIME() | System date and time in the form CCYYMMDDHHMMSSXX | |
TIME() | System time of day in the form HHMMSSXX | |
File Control | FILEOPEN(f) | '1'B if file f is open, '0'B if closed |
LINENO(x) | Line number of the print file identified by x | |
PAGENO(x) | Page number of the print file identified by x | |
Miscellaneous | CURRENTSIZE(x) | Allocated size of x in implementation-dependent units; includes in-use parts of areas and varying strings |
IDENTICAL(x,y) | A bit string whose bits indicate which corresponding elements of x and y are the same | |
NULL | Null pointer | |
VALID(p) | Boolean value indicating whether the pictured variable p has a value consistent with its picture specification |
ABS(x) |
A = 3.567; Y = ABS(A); /* Y = +3.567 */ A = -3.567; Y = ABS(A); /* Y = +3.567 */ ROOT = SQRT (ABS(TEMP)); |
The last example shows a common use for the ABS built-in function: to
ensure that an expression has a positive value before it is used as an
argument to the square root (SQRT) built-in function.
11.4.2 ACOS
The ACOS built-in function returns a floating-point value that is the arc (inverse) cosine of an arithmetic expression x. The arc cosine is computed in floating point. The returned value is an angle w such that:
0 <= w <= Pi sign
The absolute value of x, after its conversion to floating point, must be less than or equal to 1. The format of the function is:
ACOS(x) |
The ADD built-in function returns the sum of two arithmetic expressions x and y, with a specified precision p and an optionally specified scale factor q. The format of the function is:
ADD(x,y,p[,q]) |
p
An unsigned integer constant greater than zero and less than or equal to the maximum precision of the result type:q
An integer constant less than or equal to the specified precision. The scale factor can be optionally signed when used in fixed-point binary addition. The scale factor for fixed-point binary must be 0. The scale factor for fixed-point decimal data must be in the range 0 to p. If you omit q, the default value is zero. You should not use a scale factor for floating-point arithmetic.
Expressions x and y are converted to their derived type before the addition is performed.
For example:
ADDBIF: PROCEDURE OPTIONS (MAIN); DECLARE X FIXED DECIMAL (8,3), Y FIXED DECIMAL (8,3), Z FIXED DECIMAL (9,3); X=9500.374; Y=2278.897; Z = ADD (X,Y,9,3); PUT SKIP LIST ('TOTAL =',Z); END; |
This program prints the following:
TOTAL = 11779.271 |
The ADDR built-in function returns a pointer to storage denoted by a specified variable. The variable reference must be addressable. The format of the function is:
ADDR(reference) |
If the reference is to a parameter (or any element or member of a
parameter), the pointer value obtained must not be used after return
from the parameter's procedure invocation. (This could occur, for
example, if the pointer were saved in a static variable or returned as
a function value.)
11.4.6 [omitted]
11.4.7 [omitted]s
11.4.8 ASIN
The ASIN built-in function returns a floating-point value that is the arc (inverse) sine of an arithmetic expression x. The arc sine is computed in floating point. The returned value is an angle w such that:
-Pi sign/2 <= w <= Pi sign/2
The absolute value of x, after its conversion to floating point, must be less than or equal to 1. The format of the function is:
ASIN(x) |
The ATAN built-in function returns a floating-point value that is the arc tangent of an arithmetic expression y or an arc tangent computed from two arithmetic expressions y and x. The arc tangent is computed in floating point. If two arguments are supplied, they must both have nonzero values after they have been converted to floating point.
The format of the function is:
ATAN(y[,x]) |
The returned value represents an angle in radians.
If x is omitted, the returned value v equals arc tangent(s), such that:
-Pi sign/2 < v < Pi sign/2
Here, s is the value of expression y after its conversion to floating point.
If x is present, the returned value v equals arc tangent(s/r), such
that if s >= 0 , then 0 <= v <= Pi sign , and if s < 0 , then -Pi sign
< v < 0 , where s and r are, respectively, the values of expressions y
and x after their conversion to floating point.
11.4.10 ATAND
The ATAND built-in function returns a floating-point value that is the arc tangent of a single arithmetic expression y or an arc tangent computed from two arithmetic expressions y and x. The arc tangent is computed in floating point. If two arguments are supplied, they must both have nonzero values after their conversion to floating point.
The format of the function is:
ATAND(y[,x]) |
The floating-point value returned (which represents an angle in degrees) equals:
ATAN(y,x)*180/Pi sign
11.4.11 ATANH
The ATANH built-in function returns a floating-point value that is the inverse hyperbolic tangent of an arithmetic expression x. After its conversion to floating point, the absolute value of the argument x must be less than 1.
The format of the function is:
ATANH(x) |
The BINARY built-in function converts an arithmetic or string expression x to its binary representation, with an optionally specified precision p and scale factor q. The returned value is either fixed- or floating-point binary, depending on whether x is a fixed- or floating-point expression.
The format of the function is:
p
The precision p, if specified, must be an integer constant greater than zero and less than or equal to the maximum precision of the result type.The precision p must be specified if x is a fixed-point value with fractional digits.
q
The scale factor q, if specified, must be 0.
The result type is fixed- or floating-point binary, depending on whether the argument x is a fixed- or floating-point expression. (If the argument is a bit- or character-string expression, the result type is fixed-point binary.)
The argument x is converted to the result type, giving a value v, following the PL/I rules for conversion.
The returned value is the value v, with precision p, and scale factor
q. If p is omitted (integer and floating-point arguments only), the
precision of the returned value is the converted precision of x.
FIXEDOVERFLOW, OVERFLOW, or UNDERFLOW is signaled if appropriate.
11.4.13 BIT
The BIT built-in function converts an arithmetic or string expression x to a bit string of an optionally specified length. If x is a string expression, it must consist of 0s and 1s. If the length is specified, it must be a nonnegative integer. If the length is omitted, the returned value has a length determined by the PL/I rules for conversion to bit strings.
The format of the function is:
BIT(x[,length]) |
The BOOL built-in function performs a Boolean operation on two bit-string arguments and returns the result as a bit string with the length of the longer argument.
The format of the function is:
BOOL(string-1,string-2,operation-string) |
string-1
A bit-string expression of any length.string-2
A bit-string expression of any length.operation-string
A bit-string expression that is converted to length 4. Each bit in the operation string specifies the result of comparing two corresponding bits in string-1 and string-2. Specify bit positions in the operation string from left to right to define the operation, as in the following truth table:
String-1 Bit | String-2 Bit | Result of Boolean Operation |
---|---|---|
0 | 0 | Bit 1 of operation string |
0 | 1 | Bit 2 of operation string |
1 | 0 | Bit 3 of operation string |
1 | 1 | Bit 4 of operation string |
Thus, an AND operation, for instance, would be specified by the operation-string '0001'B.
If string-1 and string-2 are of different lengths, the shorter is extended on the right with zeros to the length of the longer.
X = '101010'B; Y = '110011'B; CHECK = BOOL (X,Y,'0110'B); |
The operation string is '0110'B, which defines an EXCLUSIVE OR operation. The operation is performed as follows on the corresponding bits in the strings X and Y: The leftmost bit in X is 1 and the leftmost bit in Y is 1. The truth table above specifies that when the two corresponding bits in the two strings are both 1, then bit 4 of the operation string will be the result; in this case, bit 4 of the operation string '0110'B is 0. Thus, 0 is the first bit of the value to be returned. The second bit of X is 0 and of Y is 1. The truth table specifies that when the bit in the first string is 0 and in the second string is 1, the result will be bit 2 of the operation string. Here, bit 2 of the operation string '0110'B is 1, and so 1 is the second bit of the value to be returned. The operation continues in this manner with each two corresponding bits in the strings. The value returned is <BIT_STRING>(011001)B.
Figure 11-1 illustrates this example.
Figure 11-1 Example of the BOOL Built-In Function
The CEIL function returns the smallest integer that is greater than or equal to an arithmetic expression x. Its format is:
CEIL(x) |
If x is a floating-point expression, a floating-point value is returned with the same precision as x. If x is a fixed-point expression, the returned value is a fixed-point value of the same base as x and with:
precision = p-q+1
scale factor = 0
Here, p and q are the precision and scale factor of x.
A = 4.3; Y = CEIL(A); /* Y = 5 */ A = -4.3; Y = CEIL(A); /* Y = -4 */ |
The CHARACTER built-in function converts an arithmetic or string expression x to a character string of an optionally specified length. If the length is specified, it must be a nonnegative integer. If the length is omitted, the length of the returned value is determined by the PL/I rules for conversion to character strings. The format of the function is:
⎧CHARACTER⎫ ⎨ ⎬ (x[,length]) ⎩CHAR ⎭
CHAR: PROCEDURE OPTIONS(MAIN); DECLARE EXPRES FIXED DECIMAL(7,5); DECLARE OUTPUT PRINT FILE; EXPRES = 12.34567; OPEN FILE(OUTPUT) TITLE('CHAR2.OUT'); PUT SKIP FILE(OUTPUT) LIST('No length argument: ',CHARACTER(EXPRES)); PUT SKIP FILE(OUTPUT) LIST('Length = 4: ',CHARACTER(EXPRES,4)); END CHAR; |
The program CHAR produces the following output:
No length argument: 12.34567 Length = 4: 12 |
In the first PUT LIST statement, CHARACTER has only one argument, so
the entire string is written out. The string
<BIT_STRING>(12.34567) is actually preceded by two spaces; this
is the case with any nonnegative number converted to a character
string. In the second PUT LIST statement, CHARACTER has a length
argument of 4, so the first four characters of the converted string are
written out as ' 12'.
11.4.19 COLLATE
The COLLATE built-in function returns a 256-character string consisting of the character set in ascending order. Its format is:
COLLATE() |
The COPY built-in function copies a given string a specified number of times and concatenates the result into a single string. Its format is:
COPY(string,count) |
string
Any bit- or character-string expression. If the expression is a bit string, the result is a bit string. Otherwise, the result is a character string.count
Any expression that yields a nonnegative integer. The specified count controls the number of copies of the string that are concatenated, as follows:
Value of Count | String Returned |
---|---|
0 | A null string |
1 | The string argument |
n | Concatenated copies of the string argument |
COPY('12',3) |
The COS function returns a floating-point value that is the cosine of an arithmetic expression x, where x represents an angle in radians. The cosine is computed in floating point. The format of the function is:
COS(x) |
The COSD built-in function returns a floating-point value that is the cosine of an arithmetic expression x, where x is an angle in degrees. The cosine is computed in floating point. The format of the function is:
COSD(x) |
The COSH built-in function returns a floating-point value that is the hyperbolic cosine of an arithmetic expression x. The hyperbolic cosine is computed in floating point. The format of the function is:
COSH(x) |
The CURRENTSIZE built-in function returns the amount of storage in implementation-defined units that is associated with a variable. Unlike the DECLARESIZE enquiry, it is an error to invoke CURRENTSIZE when there is no generation of storage. The value returned by CURRENTSIZE is often used in implementing specialized storage management schemes and is expected to be consistent with the value that DECLARESIZE would have returned if it was invoked immediately before the allocation. Since the value returned depends on both storage mappings and the units in which storage sizes are expressed, a program using this built-in function may be dependent on a particular implementation.
The format of the function is:
CURRENTSIZE(x) |
The DATE built-in function returns a 6-character string in the form yymmdd, where:
yy | Is the current year (00-99) |
mm | Is the current month (01-12) |
dd | Is the current day of the month (01-31) |
Its format is:
DATE() |
The date returned is the run-time date.
11.4.26 DATETIME
The DATETIME built-in function returns a 16-character string in the form ccyymmddhhmmssxx, where:
cc | Is the current century (00-99) |
yy | Is the current year (00-99) |
mm | Is the current month (01-12) |
dd | Is the current day of the month (01-31) |
hh | Is the current hour (00-23) |
mm | Is the minutes (00-59) |
ss | Is the seconds (00-59) |
xx | Is the hundredths of seconds (00-99) |
The format of the function is:
DATETIME() |
The date and time returned is the run-time date and time.
Note that the DATETIME function is identical to the century concatenated with DATE() and TIME().
INIT: PROCEDURE (ARRAY); DECLARE ARRAY(*) FIXED, I FIXED; DO I = 1 TO DIM(ARRAY); ARRAY(I) = I; END; |
This procedure is passed a one-dimensional array of an unknown extent.
The DIMENSION built-in function is used as the end value in a
controlled DO statement. This DO-group assigns integral values to each
element of the array ARRAY so that the first element has the value 1,
the second element has the value 2, and so on to the last element of
the array. (Because the array is one-dimensional, the optional second
parameter is omitted and defaults to 1.)
11.4.30 DIVIDE
The DIVIDE built-in function divides an arithmetic expression x by an arithmetic expression y and returns the quotient with a specified precision p and an optionally specified scale factor q. The scale factor q must be an integer following these rules:
The expressions x and y are converted to their derived types before the division is performed. If y is zero after this conversion, the ZERODIVIDE condition is signaled. The quotient has the derived type of the two arguments.
The format of the function is:
DIVIDE(x,y,p[,q]) |
The EDIT built-in function converts an arithmetic expression x to a string whose contents is the same as if x were assigned to a variable declared with the picture y (a string expression).
EDIT example FIXME
The EMPTY built-in function returns an empty area value for use in initializing areas. Its format is:
EMPTY() |
The EMPTY built-in function is useful in initializing the contents of an area. It is normally much faster than the FREE statement is in freeing all the variables in an area (freeing all the area's storage). Note that an area value must be assigned to an area before the area is used.
The following is an example of its use in a declaration:
DECLARE A AREA(1024) STATIC INITIAL(EMPTY()); |
The EVERY built-in function determines whether every bit in a bit string is '1'B. In other words, it performs a logical AND operation on the elements of the bit string. The format of the function is:
EVERY(bit-string) |
The function returns the value '1'B if all bits in the bit-string
argument are '1'B. It returns '0'B if one or more bits in the argument
are '0'B or if the argument is the null bit string.
11.4.34 EXP
The EXP built-in function returns a floating-point value that is the base e to the power of an arithmetic expression x. The computation is performed in floating point. The format of the function is:
EXP(x) |
The EXPONENT built-in function returns a fixed binary that is the exponent part of x. The format of the function is:
EXPONENT(x) |
The FILEOPEN built-in function takes an argument that is a file. If the file is open, it returns '1'B; if the file is not open, it returns '0'B. The format of the function is
FILEOPEN(f) |
The argument is a file.
The result will be a bit string of length 1.
The FIXED built-in function converts an arithmetic or string expression x to a fixed-point arithmetic value with a specified precision p and, optionally, a scale factor q.
The format of the function is:
FIXED(x,p[,q]) |
p
The number of bits used to represent the arithmetic value. The precision must be positive.q
An non-negative integer decimal value. If q is omitted, it is assumed to be zero. The scale factor q must be less than or equal to the specified precision.
The result type is fixed-point binary or decimal, depending on whether x is binary or decimal. (If x is a bit string, the result type is fixed-point binary; if x is a character string, the result type is fixed-point decimal.)
The expression x is converted to a value v of the result type,
following the PL/I rules. The returned value is v with precision p and
scale factor q. If q is omitted, the returned value has the converted
precision of x and a scale factor of zero. FIXEDOVERFLOW is signaled if
appropriate.
11.4.37 FLOAT
The FLOAT built-in function converts a string or arithmetic expression x to floating point, with a specified precision p. The precision p must be an integer constant that is greater than zero and less than or equal to the maximum precision of the result type.
If x is a character string, it can contain any series of characters that describes a valid arithmetic constant. That is, the character string can contain any of the numeric digits 0 through 9, a plus (+) or minus (-) sign, a decimal point (.), and the letter E. If the character string contains any invalid characters, the CONVERSION condition is signaled.
The format of the function is:
FLOAT(x,p) |
The result type is floating-point binary or decimal, depending on whether x is a binary or decimal expression. (If x is a bit-string expression, the result type is floating-point binary; if x is a character-string expression, the result type is floating-point decimal.)
The expression x is converted to a value of the result type, following
the PL/I conversion rules, and of the specified precision. UNDERFLOW or
OVERFLOW is signaled if appropriate.
11.4.38 FLOOR
The FLOOR built-in function returns the largest integer that is less than or equal to an arithmetic expression x. The format is:
FLOOR(x) |
If x is a floating-point expression, the returned value is a floating-point value. If x is a fixed-point expression, the returned value is a fixed-point value with the same base as x and with the following attributes:
precision = p-q+1
scale factor = 0
Here, p and q are the precision and scale factor of x.
For example:
FLOOR_DEMO: PROC OPTIONS(MAIN); PUT LIST (FLOOR(3)); PUT LIST (FLOOR(-3.323)); PUT LIST (FLOOR(3.456E9)); END; |
This program prints the following values:
3 -4 3.456E+09 |
The HBOUND built-in function returns a fixed-point binary integer that is the upper bound of an array dimension. The format is:
HBOUND(reference[,dimension]) |
reference
A reference to an array variable.dimension
An integer constant indicating a dimension of the specified array. If the dimension is not specified, the dimension parameter defaults to 1. Thus, HBOUND(A) is equivalent to HBOUND(A,1).
The HIGH built-in function returns a string of specified length that consists of repeated appearances of the highest character in the collating sequence. The format is:
HIGH(length) |
length
The specified length of the returned string.
The string returned is of the length specified.
11.4.41 IDENTICAL
The IDENTICAL built-in function accepts two aggregates with corresponding element types and sizes and returns a bit string, such that if the nth elements of the aggregates have the same value, the nth bit is '1'B, but if they do not have the same value, the nth bit is '0'B. This comparison is performed for all elements.
IDENTICAL example FIXME
The INDEX built-in function returns a fixed-point binary integer that indicates the position of the leftmost occurrence of a specified substring within a string. If the substring is not found, or if the length of either argument is zero, the INDEX function returns zero. This function is case-sensitive.
The format of the function is:
INDEX(string,substring[,starting-position]) |
string
The string to be searched for the given substring. It can be either a character-string or a bit-string expression.substring
The substring to be located. It must have the same string data type as the string argument.starting-position
A positive integer in the range 1 to n+1, where n is the length of the string. It specifies the leftmost position from which the search is to begin. (By default, the search begins at the left end of the string.)
#1 |
---|
DECLARE RESULT FIXED BINARY(31); RESULT = INDEX ('ABCDEF','DEF'); |
RESULT is given the value 4 because the substring 'DEF' begins at the fourth position in 'ABCDEF'.
#2 |
---|
RESULT = INDEX('SHARP FORTUNE','R'); |
RESULT is given the value 4 because the leftmost occurrence of 'R' is at the fourth position in 'SHARP FORTUNE'.
#3 |
---|
RESULT = INDEX('SHARP FORTUNE','R',5); |
The optional starting-position parameter specifies that the search begins at the fifth position of 'SHARP FORTUNE'. Thus, RESULT is given the value 9: the first R is ignored, so the first recognized occurrence of 'R' is found in the ninth position.
#4 |
---|
RESULT = INDEX('0000101100001011','1011'); |
RESULT is given the value 5 because the leftmost occurrence of '1011' is at the fifth position in '0000101100001011'.
#5 |
---|
NEW_STRING = '315-54-3159'; IF INDEX(NEW_STRING,'-')=4 THEN PUT LIST('SOCIAL SECURITY NUMBER'); |
The INDEX function is used to determine whether or not a string is a Social Security number. The function finds the location of the first hyphen in the string.
The ISOCHAR procedure accepts two integer arguments x and y and returns a character string whose sole character has the encoding x*16+y according to the ASCII, ANSI X3.64, or Unicode encodings. (In this way, it is portable to EBCDIC or other non-ASCII-compatible encodings.) The value of x must be 0, 1, 8, or 9, and the value of y must be 0 to 15 inclusive, so the character is always a control character.
The format of the function is
ISOCHAR(x,y) |
The arguments are both FIXED BINARY values with a precision of at least 4.
The result will be a CHARACTER value of length 1.
The LBOUND built-in function returns a fixed-point binary integer that is the lower bound of an array dimension. The format is:
LBOUND(reference[,dimension]) |
reference
A reference to an array variable.dimension
An integer constant indicating the dimension of the specified array. If the dimension is not specified, the dimension parameter defaults to 1. Thus, LBOUND(A) is equivalent to LBOUND(A,1).
The LENGTH built-in function returns a fixed-point binary integer that is the number of characters or the number of bits in a specified character- or bit-string expression. If the string is a varying-length character string, the function returns its current length. (To determine the maximum length of a varying-length character string, use the MAXLENGTH built-in function.)
The format of the function is:
LENGTH(string) |
LINENO(reference) |
If the referenced print file is closed, the returned value is the last
value from the previous opening. If the file was never opened, the
returned value is zero.
11.4.48 LOG
The LOG built-in function returns a floating-point value that is the base e (natural) logarithm of an arithmetic expression x. The computation is performed in floating point. The expression x must be greater than zero after its conversion to floating point.
The format of the function is:
LOG(x) |
The LOG10 built-in function returns a floating-point value that is the base 10 logarithm of arithmetic expression x. The computation is performed in floating point. The expression x must be greater than zero after its conversion to floating point.
The format of the function is:
LOG10(x) |
The LOG2 built-in function returns a floating-point value that is the base 2 logarithm of an arithmetic expression x. The computation is performed in floating point. The expression x must be greater than zero after its conversion to floating point.
The format of the function is:
LOG2(x) |
The LOW built-in function returns a string of specified length that consists of repeated appearances of the lowest character in the collating sequence. The format is:
LOW(length) |
length
The specified length of the returned string.
The string returned is of the length specified.
11.4.52 [omitted]
11.4.53 MAX
The MAX built-in function returns the larger of two arithmetic expressions x and y. The format of the function is:
MAX(x,y) |
The expressions x and y are converted to their derived type before the operation is performed (for a discussion of derived types see Section 6.4.2. If the derived type is floating point, the value returned is also floating point, with the larger precision of the two converted arguments. If the derived type is fixed point, the returned value is a fixed-point value with the base of the derived type. The value has the following attributes:
precision = max(px-qx,py-qy)+max(qx,qy))
scale factor = max(qx,qy) Here, px,qx and py,qy are the converted
precisions and scale factors of x and y, respectively.
11.4.54 MAXLENGTH
The MAXLENGTH built-in function returns a fixed binary number representing the maximum possible length of a varying-length character string. The format is:
MAXLENGTH (string) |
string
A reference to a character string or a bit string. If it is anything other than a varying-length character string, the MAXLENGTH function returns a result identical to the result that would be returned by the LENGTH built-in function.
For example:
MAXLENGTH_EXAMPLE: PROCEDURE OPTIONS(MAIN); DCL CHAR_VAR CHARACTER(10) VARYING; CHAR_VAR = 'String'; CALL SAMPLE(CHAR_VAR); END MAXLENGTH_EXAMPLE; SAMPLE: PROCEDURE(STRING); DCL STRING CHAR(*) VARYING; PUT LIST(LENGTH(STRING),MAXLENGTH(STRING)); END SAMPLE; |
The program prints the following:
6 10 |
The MIN built-in function returns the smaller of two arithmetic expressions x and y. The format is:
MIN(x,y) |
The expressions x and y are converted to their derived type before the operation is performed (for a discussion of derived types see Section 6.4.2. If the derived type is floating point, the value returned is also floating point, with the larger precision of the two converted arguments. If the derived type is fixed point, the returned value is a fixed-point value with the base of derived type. The value has the following attributes:
precision = max(px-qx,py-qy)+max(qx,qy)
scale factor = max(qx,qy)
Here, px,qx and py,qy are the converted precisions and scale factors of
x and y.
11.4.56 MOD
The MOD built-in function returns, for an arithmetic expression x and nonnegative arithmetic expression y, the value r that equals x modulo y. That is, r is the smallest positive value that must be subtracted from x to make the remainder of x divided by y exactly 0.
The format of the function is:
MOD(x,y) |
The expressions x and y are converted to their derived type before the operation is performed.
If the derived type is unscaled fixed point, then the precision of the result is the precision of the second operand.
If the derived type is floating point, the returned value is an approximation in floating point, with the larger of the precisions of the two converted arguments.
The value returned is:
u-w*floor(u/w)
The arguments u and w become the arguments x and y, respectively, after conversion to their derived type. If w is zero, u is converted to the precision described below, which can signal FIXEDOVERFLOW.
If x and y are fixed-point expressions, a fixed-point value is returned. The value has the following attributes:
precision = pw-qw+max(qu,qw) scale factor = max(qu,qw)
Here, qu is the scale factor of u, pw is the precision of w, and qw is the scale factor of w. The FIXEDOVERFLOW condition is signaled if the following is true:
pw-qw + max(qu,qw) > the largest possible value
MODEX: PROCEDURE OPTIONS(MAIN); DECLARE OUTMOD PRINT FILE; ON FIXEDOVERFLOW PUT FILE(OUTMOD) SKIP LIST('FIXEDOVERFLOW signaled'); PUT FILE(OUTMOD) SKIP LIST(MOD(28,128)); PUT FILE(OUTMOD) SKIP LIST(MOD(130,128)); PUT FILE(OUTMOD) SKIP LIST(MOD(-28,128)); PUT FILE(OUTMOD) SKIP LIST(MOD(4.5,.758)); PUT FILE(OUTMOD) SKIP LIST(MOD(-4.5,.758)); PUT FILE(OUTMOD) SKIP LIST(MOD(1.5E-3,-1.4E-3)); PUT FILE(OUTMOD) SKIP LIST(MOD(28,0)); END MODEX; |
The program MODEX writes the following output to OUTMOD.DAT:
28 2 100 0.710 0.048 -1.3E-03 FIXEDOVERFLOW signaled 8 |
The last PUT statement attempts to take MOD(28,0). The constants 28 and 0 are both fixed-point decimal expressions, with precisions (2,0) and (1,0), respectively. Therefore, the attributes of the returned value are determined to be FIXED DECIMAL. The value has the following attributes:
precision = 1-0+max(0,0) = 1
scale factor = max(0,0) = 0
Although 28 modulo 0 is 28, MOD(28,0) signals FIXEDOVERFLOW because 28
cannot be represented in the result precision. (The value of the
function is therefore undefined.)
11.4.57 MULTIPLY
The MULTIPLY built-in function multiplies two arithmetic expressions x and y, and returns the product of the two values with a specified precision p and an optionally specified scale factor q.
The format of the function is:
MULTIPLY(x,y,p[,q]) |
p
An integer constant greater than zero and less than or equal to the maximum precision of the result type.q
0 when used with fixed-point binary multiplication. The scale factor for fixed-point decimal multiplication has a range 0 through p. A scale factor is not to be used with floating-point arithmetic. If no scale factor is designated, q defaults to zero.
Expressions x and y are converted to their derived type before the multiplication is performed.
For example:
MULT: PROCEDURE OPTIONS (MAIN); DECLARE I_RATE FIXED DECIMAL(31,4), PRINCIPAL FIXED DECIMAL(31,2), OWED FIXED DECIMAL(31,6); I_RATE = .1514; PRINCIPAL = 27688.25; OWED = MULTIPLY (I_RATE,PRINCIPAL,31,6); PUT SKIP LIST ('INTEREST OWED =',OWED); END; |
Interest rates are calculated to six decimal places and the following string is printed:
INTEREST OWED = 4192.001050 |
The NULL enquiry function returns a null pointer value. The format is:
NULL(:POINTER:) |
IF NEXT_POINTER = NULL(:POINTER:) THEN CALL FINISH; |
The IF statement checks whether the pointer variable NEXT_POINTER is null; if so, the CALL statement is executed.
The NULL(:OFFSET:) enqiry function can be used for offset variables.
11.4.59 OFFSET
The OFFSET built-in function converts a pointer to an offset relative to a designated area. If the pointer is null, the result is null. The format of the function is:
OFFSET(pointer,area) |
pointer
A reference to a pointer variable whose current value either represents the location of a based variable within the specified area or is null.area
A reference to a variable declared with the AREA attribute. If the specified pointer is not null, it must designate a storage location within this area.
DECLARE MAP_SPACE AREA (2048), START OFFSET (MAP_SPACE), QUEUE_HEAD POINTER; . . . START = OFFSET (QUEUE_HEAD,MAP_SPACE); |
The offset variable START is associated with the area MAP_SPACE. The
OFFSET built-in function converts the value of the pointer to an offset
value.
11.4.60 [omitted]
11.4.61 [omitted]
11.4.62 ONCODE
The ONCODE built-in function returns a fixed-point binary integer that is the status value of the most recent run-time error that signaled the current ON condition. You can use the function in any ON-unit to determine the specific error that caused the condition. If the function is used within any context outside an ON-unit, it returns a zero. The format is:
ONCODE() |
The ONFILE built-in function returns the name of the file constant for which the current file-related condition was signaled. The format is:
ONFILE() |
This built-in function can be used in an ON-unit established for any of the following conditions:
The returned value is a varying-length character string. The ONFILE
function returns a null string if referenced outside an ON-unit, within
an ON-unit that is executed as a result of a SIGNAL statement, or
within a CONVERSION ON-unit that was not entered because of a
conversion in a GET statement.
11.4.64 ONKEY
The ONKEY built-in function returns the key value that caused the KEY condition to be signaled during an I/O operation to a file that is being accessed by key. Its format is:
ONKEY() |
This built-in function can be used in an ON-unit established for these conditions:
The returned key value is a varying-length character string. The ONKEY
built-in function returns a null string if referenced outside an
ON-unit or within an ON-unit executed as a result of the SIGNAL
statement.
11.4.65 ONSOURCE
The ONSOURCE built-in function returns the source string that was being converted when the CONVERSION condition was raised. If no CONVERSION condition is active, the return value is a null string.
The format of the function is:
ONSOURCE() |
The PAGENO built-in function returns a FIXED BINARY(15) integer that is the current page number in the referenced print file. The print file must be open. The format of the function is:
PAGENO(reference) |
The POINTER built-in function returns a pointer to the location identified by the referenced offset and area. The format is:
⎧POINTER⎫ ⎨ ⎬ (offset,area) ⎩PTR ⎭
offset
A reference to an offset variable whose current value either represents the offset of a based variable within the specified area or is null.area
A reference to a variable that is declared with the AREA attribute and with which the specified offset value is associated.
The returned value is of type POINTER. If the offset value is null, the result is null.
DECLARE MAP_SPACE AREA (2048), START OFFSET (MAP_SPACE), P POINTER; . . . P = POINTER (START,MAP_SPACE); |
The POINTER built-in function converts the value of the offset variable
START (in the area MAP_SPACE) to a pointer value.
11.4.68 [omitted]
11.4.69 [omitted]
11.4.70 PROD
The PROD built-in function takes an array as an argument and returns the arithmetic product of all the elements in the array. The array must have the FIXED or the FLOAT attribute. The format of the PROD built-in function is:
PROD(array-variable); |
If the array has the attributes FIXED(p,0), the result will have the attributes FIXED(p,0). If the array has the attributes FLOAT(p), the result will also have the attributes FLOAT(p). If the array has the attributes FIXED(p,q) with q not equal to 0, the result will have the attributes FLOAT(p).
The result will have the same base attribute as the array, either DECIMAL or BINARY.
Note that the PROD built-in function does not perform matrix
multiplication of two arrays.
11.4.71 [omitted]
11.4.72 [omitted]
11.4.73 REVERSE
The REVERSE built-in function reverses the characters or bits in a string. It takes one argument, which is either a character string (fixed or varying) or a bit string. It returns a string of the same type and size as its argument, with all the characters (bytes) or bits reversed.
The format of the function is:
REVERSE(string-expression); |
string-expression
An expression that evaluates to a character string or a bit string.
DECLARE X CHARACTER(4) VARYING, Y BIT(8); X = REVERSE('abc') Y = REVERSE('00010101'B) |
The ROUND built-in function rounds a fixed-point or floating-point expression or pictured value to a specified number of binary or decimal places. The format is:
ROUND(expression,position) |
expression
An arithmetic expression that yields a fixed-point binary or decimal value; or a pictured value with fractional digits. A binary value can have a positive or negative non-zero scale factor, but a decimal value must have a positive non-zero scale factor.position
A nonnegative integer constant specifying the number of binary or decimal places in the rounded result.
Where the arguments are an expression of type FIXED BINARY(p,q) or type FIXED DECIMAL(p,q) and position k, the returned value is the rounded value with the following attributes:
precision = p-q+k+1
scale factor = k
For a fixed binary number, the rounded value is:
ROUND(x,k) = sign(x)*(2-k+1)*floor(abs(x)*(2k)+1)
For a fixed decimal number, the rounded value is:
ROUND(x,k) = sign(x)*(10-k)*floor(abs(x)*(10k)+0.5)
The following sample program shows rounding of scaled fixed binary numbers:
r: procedure options(main); declare fb1 fixed decimal(31, 8), fb2 fixed decimal(31, 4), fb3 fixed decimal(31, 2), fb4 fixed decimal(31, 2), fb5 fixed binary(31, 2); fb1 = 16.8750; /* 7/8 */ fb2 = 15.4375; /* 7/16 */ fb3 = 128.25; /* 128 1/4 */ fb4 = 128.75; /* 128 3/4 */ fb5 = -128.75; /* -128 3/4 */ put skip edit ('round(16.8750, 2)=',round(fb1,2)) (a,col(20),f(15,5)); put skip edit ('round(16.8750, 3)=',round(fb1,3)) (a,col(20),f(15,5)); put skip edit ('round(15.4375, 1)=',round(fb2,1)) (a,col(20),f(15,5)); put skip edit ('round(128.25,0)=',round(fb3,0)) (a,col(20),f(15,5)); put skip edit ('round(128.75,0)=',round(fb4,0)) (a,col(20),f(15,5)); put skip edit ('round(-128.75,0)=',round(fb5,0)) (a,col(20),f(15,5)); end r; |
This program produces the following output. Note that 16.87500 equals 16 7/8 in fractional notation and that 15.50000 equals 15 1/2 in fractional notation.
round(16.8750, 2)= 17.00000 round(16.8750, 3)= 16.87500 round(15.4375, 1)= 15.50000 round(128.25,0)= 128.00000 round(128.75,0)= 129.00000 round(-128.75,0)= -129.00000 |
The following example shows rounding of scaled fixed decimal numbers:
A = 1234.567; Y = ROUND(A,1); /* Y = 1234.6 */ Y = ROUND(A,0); /* Y = 1235 */ A = -1234.567; Y = ROUND(A,2); /* Y = -1234.57 */ |
The SEARCH built-in function takes two character-string arguments and attempts to locate the first character in the first string that is also present in the second string. The search for a match is carried out from left to right. If one character is matched, the function returns the position of that character in the first string. This function is case sensitive.
The format is:
SEARCH(string-1,string-2[,starting-position]) |
string-1
A character-string expression. One character in the string is to be matched, if possible, in the second string.string-2
A character-string expression to be compared, character by character, with each character in the first string, in order, until one matching character is found.starting-position
A positive integer in the range 1 to n+1, where n is the length of the first string. It specifies the leftmost character in the first string from which the search is to begin. If starting-position is specified, any characters to the left of that position in the first string are ignored. (By default, the search begins with the leftmost character in the first string.)
The returned value is a positive integer representing the position in string-1 of the first character that is also found in string-2. If no match is found, the returned value is zero.
DECLARE STR1 CHARACTER(20) VARYING, STR2 CHARACTER(10) INITIAL ('ABCDEFGHIJ'), X FIXED DECIMAL(2); STR1 = 'BARBARA'; X = SEARCH (STR1,STR2); |
In this example, X is given the value 1 because the first character ('B') in STR1 ('BARBARA') is found in STR2 ('ABCDEFGHIJ').
STR1 = '12-GEORGE'; X = SEARCH (STR1,STR2); |
Here, X is given the value 4. 'G' is in the fourth position in '12-GEORGE' and is the first character in STR1 that is also present in STR2 ('ABCDEFGHIJ').
X = SEARCH (STR1,STR2,6); |
X is given the value 8. The starting-position parameter, 6, causes the search to begin with the sixth character in '12-GEORGE', and thus the first matching character is the second 'G', which is in the eighth position.
PUT LIST (SEARCH('ZZZBAD','ABCD')); |
The function returns the value 4 because the position of 'B' in 'ZZZBAD' is 4, and 'B' is the leftmost matching character. Here, constants are used instead of variables.
PUT LIST (SEARCH('ABCD','ZZZBAD')); |
This statement is the same as the preceding one except that the parameters are reversed. Now the value returned is 1 instead of 4 because 'A', the first character in 'ABCD', is matched. Note that the order in which the parameters are given is crucial. Note also that duplicate characters in the second string never change the result.
PUT LIST (SEARCH (' TEST 123','0123456789')); |
The function returns the value 9 because '1', which is in the ninth
position, is the first character matched in the second string.
11.4.77 SIGN
The SIGN built-in function returns 1, -1, or 0, indicating whether an arithmetic expression is positive, negative, or zero, respectively. The returned value is a fixed-point binary integer. The format of the function is:
SIGN(expression) |
The SIN built-in function returns a floating-point value that is the sine of an arithmetic expression x, where x is an angle in radians. The sine is computed in floating point. The format of the function is:
SIN(x) |
The SIND built-in function returns a floating-point value that is the sine of an arithmetic expression x, where x represents an angle in degrees. The sine is computed in floating point. The format of the function is:
SIND(x) |
The SINH built-in function returns a floating-point value that is the hyperbolic sine of an arithmetic expression x. The hyperbolic sine is computed in floating point. The format of the function is:
SINH(x) |
The SOME built-in function allows you to determine whether at least one bit in a bit string is '1'B. In other words, it performs a logical OR operation on the elements of the bit string. The format of the SOME built-in function is:
SOME(bit-string) |
The function returns the value '1'B if one or more bits in the
bit-string argument are '1'B. It returns '0'B if every bit in the
argument is '0'B or if the argument is the null bit string.
11.4.82 SQRT
The SQRT built-in function returns a floating-point value that is the square root of an arithmetic expression x. The square root is computed in floating point. After its conversion to floating point, x must be greater than or equal to zero.
The format of the function is:
SQRT(x) |
The STRING built-in function concatenates the elements of an array or structure and returns the result. Elements of a string array are concatenated in row-major order. Members of a structure are concatenated in the order in which they were declared.
The format of the STRING built-in function is:
STRING(reference) |
reference
A reference to a variable that is suitable for bit-string or character-string overlay defining. Briefly, a variable is suitable if it consists entirely of characters or bits, and these characters or bits are packed into adjacent storage locations, without gaps.
The string returned is of type CHARACTER or BIT, depending on whether the reference is suitable for character- or bit-string overlay defining. The length of the string is the total number of characters or bits in the base reference.
STRING_BIF_EXAMPLE: PROCEDURE; DECLARE NEW_NAME CHARACTER(40); DECLARE 1 FULL_NAME, 2 FIRST_NAME CHARACTER(10), 2 MIDDLE_INITIAL CHARACTER(3), 2 LAST_NAME CHARACTER(27); FIRST_NAME = 'MABEL'; MIDDLE_INITIAL = 'S.'; LAST_NAME = 'MERCER'; NEW_NAME = STRING(FULL_NAME); /* NEW_NAME = 'MABEL S. MERCER ' where is a space */ END STRING_BIF_EXAMPLE; |
The SUBSTR built-in function returns a specified substring from a string. The format is:
SUBSTR(string,position[,length]) |
string
A bit- or character-string expression.position
An integer expression that indicates the position of the first bit or character in the substring. The position must be greater than or equal to 1 and less than or equal to LENGTH(string) + 1.length
An integer expression that indicates the length of the substring to be extracted. If not specified, length is:LENGTH(string) - position + 1
In other words, if length is not specified, the substring is extracted beginning at the indicated position and ending at the end of the string.
The length must satisfy the following condition:
0 <= length <= LENGTH(string) - position + 1
The returned substring is of type BIT(length) or CHARACTER(length), depending on the type of the string argument. If the length argument is zero, the result is a null string.
DECLARE (NAME,LAST_NAME) CHARACTER(20), START FIXED BINARY(31); NAME = 'ISAK DINESEN'; /* NAME = 'ISAK DINESEN ' */ START = INDEX(NAME,' ')+1; /* START = 6 */ LAST_NAME = SUBSTR(NAME,START); /* default length = LENGTH(NAME)-START+1 =15 */ /* LAST_NAME = 'DINESEN ' */ |
SUBTRACT(x,y,p[,q]) |
p
An unsigned integer constant greater than zero and less than or equal to the maximum precision of the result type.q
An integer constant less than or equal to the specified precision. The scale factor can be optionally signed when used in fixed-point binary subtraction. The scale factor for fixed-point binary must be 0. The scale factor for fixed-point decimal data must be in the range 0 through p. If you omit q, the default value is zero. Do not use a scale factor for floating-point arithmetic.
Expressions x and y are converted to their derived type before the subtraction is performed.
For example:
SUBTRACTBIF: PROCEDURE OPTIONS (MAIN); DECLARE X FIXED DECIMAL (8,3), Y FIXED DECIMAL (8,3), Z FIXED DECIMAL (9,3); X=9500.374; Y=2278.897; Z = SUBTRACT (X,Y,9,3); PUT SKIP LIST ('DIFFERENCE =',Z); END; |
This program prints:
DIFFERENCE = 7221.477 |
The SUM built-in function takes an array as an argument and returns the arithmetic sum of all the elements in the array. The array must have the FIXED or the FLOAT attribute. The format of the SUM built-in function is:
SUM(array-variable) |
If the array has the attributes FIXED(p,q), the result will have the attributes FIXED(p,q). If the array has the attributes FLOAT(p), the result will also have the attributes FLOAT(p).
The result will have the same base attribute as the array, either
DECIMAL or BINARY.
11.4.87 TALLY
The TALLY built-in function returns a fixed binary value that is the number of times the string y appears in the string x.
The format of the function is:
TALLY(x,y) |
The TAN built-in function returns a floating-point value that is the tangent of an arithmetic expression x, where x represents an angle in radians. The tangent is computed in floating point. After its conversion to floating point, x must not be an odd multiple of Pi sign/2 .
The format of the function is:
TAN(x) |
The TAND built-in function returns a floating-point value that is the tangent of an arithmetic expression x, where x represents an angle in degrees. The tangent is computed in floating point. After its conversion to floating point, x must not be an odd multiple of 90.
The format of the function is:
TAND(x) |
The TANH built-in function returns a floating-point value that is the hyperbolic tangent of an arithmetic expression x. The hyperbolic tangent is computed in floating point. The format of the function is:
TANH(x) |
The TIME built-in function returns an 8-character string representing the current time of day in the following form:
hhmmssxx |
hh
The current hour (00-23)mm
The minutes (00-59)ss
The seconds (00-59)xx
Hundredths of seconds (00-99)
The format of the TIME built-in function is:
TIME() |
If TIME is used as a preprocessor built-in function, the time returned
is the time when the program was compiled; otherwise the function
returns the time at run time.
11.4.92 TRANSLATE
Given a character-string argument, the TRANSLATE built-in function replaces occurrences of an old character with a corresponding translation character and returns the resulting string. The format is:
TRANSLATE(original,translation[,old-chars]) |
original
A character-string expression in which specific characters are to be translated.translation
A character-string expression giving replacement characters for corresponding characters in old-chars.old-chars
A character-string expression indicating which characters in the original are to be replaced. If old-chars is not specified, the default is COLLATE().If the translation is shorter than old-chars, the translation is padded on the right with spaces to the length of old-chars before any translation occurs. If the translation is longer than old-chars, its excess characters (on the right) are ignored.
The following steps are performed for each character (beginning at the left) in the original:
The string returned is of type CHARACTER(length), where length is the length of the original string. If the original string is a null string, the returned value is a null string.
TRANSLATE_XM: PROCEDURE OPTIONS(MAIN); DECLARE NEWSTRING CHARACTER(80) VARYING; DECLARE TRANSLATION CHARACTER(128); DECLARE I FIXED; DECLARE COLLATE BUILTIN; /* translate space to '0': */ NEWSTRING = TRANSLATE('1 2','0',' '); PUT SKIP LIST(NEWSTRING); /* translate letter 'F' to 'E': */ NEWSTRING = TRANSLATE('BFFLZFBUB','E','F'); PUT SKIP LIST(NEWSTRING); /* change case of letters in sentence */ TRANSLATION = COLLATE; DO I=66 TO 91; /* replace upper with lower */ SUBSTR(TRANSLATION,I,1) = SUBSTR(COLLATE,I+32,1); END; DO I=98 TO 123; /* replace lower with upper */ SUBSTR(TRANSLATION,I,1) = SUBSTR(COLLATE,I-32,1); END; NEWSTRING = TRANSLATE('THE QUICK BROWN fox JUMPS OVER THE LAZY dog',TRANSLATION); PUT SKIP LIST(NEWSTRING); END TRANSLATE_XM; |
The first reference translates the string <BIT_STRING>(1 2) to <BIT_STRING>(102). The second reference translates <BIT_STRING>(BFFLZFBUB) to <BIT_STRING>(BEELZEBUB). The third reference produces the following new sentence:
'the quick brown FOX jumps over the lazy DOG' |
The TRIM built-in function accepts a character string as an argument and returns a character string that consists of the input string with specified characters removed from the left and right. If you supply only one argument, TRIM removes blanks from the left and right of the argument. If you supply second and third arguments, TRIM removes characters specified by those arguments from the left and right of the string, respectively.
The format of the TRIM built-in function is:
TRIM (input-string,[beginning-chars,end-chars]) |
input-string
A character-string variable or constant. This argument supplies the string from which characters are to be trimmed.beginning-chars
A character-string variable or constant. This argument specifies characters to be trimmed from the left of the input string. If a character that is in the first position in the input string is also present anywhere in beginning-chars, that character is removed from the input string. This process is repeated until a character is encountered on the left of the input string that is not present in beginning-chars, or until the characters in the input string are exhausted.end-chars
A character-string variable or constant. This argument specifies characters to be trimmed from the right of the input string. The process of removing characters from the right is identical to that of removing characters from the left, except that the character in the last position is examined.
The TRIM built-in function accepts either one or three arguments. Any of the arguments can consist of a null string; specifically, if beginning-chars or end-chars is null, no characters are removed from the corresponding end of the input string.
When only one argument is supplied, TRIM removes blanks from both ends of that argument. In other words, the following two expressions are equivalent:
TRIM(S) TRIM(S,' ',' ') |
The returned value is a character string with characters removed from the ends.
The following examples illustrate the use of the TRIM built-in function.
Text | Returned String |
---|---|
TRIM ('ABC') | 'ABC' |
TRIM(' ABC') | 'ABC' |
TRIM(' ABC ') | 'ABC' |
TRIM('ABC ') | 'ABC' |
TRIM(' ABCDEF','','E') | ' ABCDEF' |
TRIM(' ABCDEF','','FE') | ' ABCD' |
TRIM(' ABCDEF ','ABC','EDF') | ' ABCDEF ' |
TRIM('ABCDEF','CADB','FE') | '' |
TRIM(' ABCDEF ','ABC ',' EDF') | '' |
TRIM('AAAABCCXCCDDDDEFFFF','AC','DF') | 'BCCXCCDDDDE' |
The TRUNC built-in function changes all fractional digits in an arithmetic expression x to zeros and returns the resulting integer value. Its format is:
TRUNC(x) |
If x is a floating-point expression, the returned value is a floating-point value. If x is a fixed-point expression, the returned value is a fixed-point value with the same base as x. The value has the following attributes:
precision = p-q+1
scale factor = 0
scale factor = 0
Here, p and q are the precision and scale factor of x.
11.4.95 UNSPEC
The UNSPEC built-in function returns a implementation-dependent bit string representing the internal coded value of the referenced variable, or a specified part of that variable. The variable can be an aggregate or a scalar variable of any type. The format of the function is:
UNSPEC(reference[,position[,length]]) |
The returned value is a bit string whose length is the number of bits
occupied by the referenced variable or by that part of the variable
specified by the optional parameters, position and length. The length
of the bit string must be less than or equal to the maximum length for
bit-string data. The returned bit string contains the contents of the
storage of the referenced variable (or the specified part of the
variable), the first bit in storage being the first bit in the returned
value.
11.4.96 VALID
The VALID built-in function determines whether the argument x, a pictured variable, has a value that is valid with respect to its picture specification. A value is valid if it is any of the character strings that can be created by the picture specification. The function returns <BIT_STRING>(0)B if x has an invalid value and <BIT_STRING>(1)B if it has a valid value. The function can be used whenever a data item is read in with a record input (READ) statement, to ensure that the input data is valid. The format of the function is:
VALID(x) |
x
A reference to a variable declared with the PICTURE attribute.
Note that pictured data is always validated (and thus, the VALID function is unnecessary) when it is read in with the GET EDIT statement and the P format item; the CONVERSION condition is signaled if the data does not conform to the picture given in the P format item. If GET LIST is used (or GET EDIT with a format item other than P), the input value is converted to conform to the pictured input target.
VALP: PROCEDURE OPTIONS(MAIN); DECLARE INCOME PICTURE '$$$$$$V.$$'; DECLARE MASTER RECORD FILE; DECLARE I FIXED; DO I = 1 TO 2; READ FILE(MASTER) INTO(INCOME); IF VALID(INCOME) THEN; ELSE PUT SKIP LIST('Invalid input:',INCOME); END; END VALP; |
Asume that the file MASTER.DAT contains the following data:
$15000.50 50000.50 |
Invalid input: 50000.50 |
The picture <BIT_STRING>($$$$$$V.$$) specifies a fixed-point
decimal number of up to seven digits, two of which are fractional. To
be valid, a pictured value must consist of nine characters: the first
digit must be immediately preceded by a dollar sign, the number must
contain a period before the fractional digits, and each position
specified by a dollar sign must contain either that sign, a digit, or a
space. The second record in MASTER.DAT can be assigned by the READ
statement because it has the correct size; however, the pictured value
is invalid because it does not contain a dollar sign.
11.4.97 [omitted]
11.4.98 [omitted]
11.4.99 VERIFY
The VERIFY built-in function compares a string with a character-set string and verifies that all characters appearing in the string also appear in the character-set string. The function returns the value zero if they all appear. If not, the function returns a fixed-point binary integer that indicates the position of the first character in the string that is not present in the character-set string. The comparison is done character by character and left to right, and as soon as one nonmatching character is found in the first string, no more characters are compared. The function is case sensitive.
The format of the function is:
VERIFY(string,character-set-string[,starting-position]) |
string
A character-string expression representing the string to be checked.character-set-string
A character-string expression containing the set of characters with which the characters in the first string are to be compared.starting-position
A positive integer in the range 1 to n+1, where n is the length of the first string. It specifies the leftmost position in the first string to be compared with the character-set-string. (By default, the comparison starts at the left end of the first string.)
#1 |
---|
STRING = 'HOW MUCH IS 1 PLUS 2'; ALPHABET = 'abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ '; A = VERIFY(STRING,ALPHABET); |
The value of the variable ALPHABET is a string containing the 26 lowercase letters, the 26 uppercase letters, and the space character. The function returns a value of 13, indicating the position of the character '1', which is the first nonalphabetic and nonspace character in STRING.
#2 |
---|
A = VERIFY(STRING,' '); |
This example finds the first nonspace character in a string by using the space character as a test string. Note that constants can be used as the string parameters.
#3 |
---|
NEWSTRING = 'ALL LETTERS'; A = VERIFY(NEWSTRING,ALPHABET); |
VERIFY returns a value of zero because all characters in the string NEWSTRING are present in the string ALPHABET.
#4 |
---|
NEWSTRING = '9 LETTERS'; A = VERIFY (NEWSTRING,ALPHABET,2); |
The optional starting-position parameter specifies that the comparison begins at position 2 in NEWSTRING. VERIFY returns a value of zero because all characters beginning with the second character in the string NEWSTRING are present in the string ALPHABET. If the starting-position parameter had not been specified, VERIFY would have returned a value of 1, because the first character ('9') in NEWSTRING is not present in ALPHABET.
DECLARESIZE(:x:) |
11.5.2 EPSILON enquiry function
The EPSILON enquiry function returns the interval separating successive values representable by a floating-point data type given as its argument.
The format of the enquiry is:
EPSILON(:x:) |
11.5.3 MAXVAL enquiry function
The MAXVAL enquiry function returns the maximum finite value representable by a floating-point data type given as its argument.
The format of the enquiry is:
MAXVAL(:x:) |
11.5.4 MINVAL enquiry function
The MINVAL enquiry function returns the minimum finite value representable
by a floating-point data type given as its argument.
The format of the enquiry is:
MINVAL(:x:) |
11.5.5 NULL enquiry function
The NULL enquiry function accepts an argument which is one of the keywords
ENTRY, FILE, LABEL, OFFSET, or POINTER and returns a null pointer of
the specified type.
The format of the enquiry is any of the following:
NULL(:ENTRY:) NULL(:FILE:) NULL(:LABEL:) NULL(:OFFSET:) NULL(:POINTER:) |
The RADIX enquiry function returns the model base used to represent a floating-point data type given as its argument.
The format of the enquiry is:
RADIX(:x:) |
A pseudo-variable can be used, in certain assignment contexts, in place of an ordinary variable reference. For example:
SUBSTR(S,2,1) = 'A'; |
A pseudo-variable can be used wherever the following three conditions are true:
The principal contexts in which pseudo-variables are used are:
Note that a pseudo-variable cannot be used in an argument list. In the following example, SUBSTR is not interpreted as a pseudo-variable:
CALL P(SUBSTR(S,2,1)); |
Here, SUBSTR is interpreted as a built-in function reference, rather than as a pseudo-variable. The actual argument passed to procedure P is a dummy argument containing the second character of string S.
The following are pseudo-variables:
The next sections describe these pseudo-variables in alphabetic order.
11.6.1 EXPONENT
The EXPONENT pseudo-variable refers to the exponent of a specified arithmetic reference. Assignment to the pseudo-variable modifies only the exponent. The format of the pseudo-variable (in an assignment statement) is:
EXPONENT(x) = expression; |
x
An arithmetic reference whose exponent part is to be modified.expression
An expression whose value becomes the exponent of x.
The ONSOURCE pseudo-variable can be used to replace the entire ONSOURCE value that caused a CONVERSION condition to be raised. An attempt to assign a value to the ONSOURCE pseudo-variable when there is no active CONVERSION condition causes the ERROR condition to be raised.
The format of the pseudo-variable is:
ONSOURCE() |
The ONSOURCE value is a fixed-length string value. An assignment of a longer string is truncated, and an assignment of a shorter string is padded with blanks on the right to the necessary length.
See Section 8.10.4.4 for more information about CONVERSION condition name.
11.6.4 PAGENO Pseudo-variable
The PAGENO pseudo-variable refers to the page number of the referenced print file. Assignment to the pseudo-variable modifies the current page number. The format of the PAGENO pseudo-variable in an assignment statement is:
PAGENO(reference) = expression; |
reference
A reference to a file for which the page number is to be set. The file must be open and must be a print file.
PAGENO(reference) is a FIXED BINARY(15) variable; however, values
assigned to it must not be negative.
11.6.5 [omitted]
11.6.6 STRING Pseudo-variable
The STRING pseudo-variable interprets a suitable reference as a reference to a fixed-length string. By using it, you can modify an entire aggregate with a single string assignment or assign the aggregate to a pictured variable as if it were a character-string variable. The format of the pseudo-variable (in an assignment statement) is:
STRING(reference) = expression; |
reference
A reference to a variable that is suitable for character-string (or bit-string) overlay defining (see Section 5.5.6.1 and Section 5.8.2). The length of the pseudo-variable is equal to the total number of characters (or bits) in the scalar or aggregate denoted by the reference. This length must be less than or equal to the maximum length for character-string (or bit-string) data.
Assignment to the STRING pseudo-variable modifies the entire storage denoted by the reference.
STRING_PSD_EXAMPLE: PROCEDURE; DECLARE 1 NAME, 2 FIRST CHARACTER(10), 2 MIDDLE_INITIAL CHARACTER(3) 2 LAST CHARACTER(10); STRING(NAME)='FRANKLIN D. ROOSEVELT'; /* NAME.FIRST - 'FRANKLIN D'; NAME.MIDDLE_INITIAL = '. R'; NAME.LAST = 'OOSEVELT '; */ END STRING_PSD_EXAMPLE; . . . DECLARE 1 FLAGS, 2 (A,B,C) BIT(1); STRING(FLAGS) = '0'B; /* sets all three flags false */ . . . DECLARE P PICTURE /Z.ZZZV,ZZDB'; GET EDIT (STRING(P)) (A(10)); /* assigns 10 characters from SYSIN to P, without conversion */ |
The SUBSTR pseudo-variable refers to a substring of a specified string variable reference. Assignment to the pseudo-variable modifies only the substring. The format of the pseudo-variable (in an assignment statement) is:
SUBSTR(reference,position[,length]) = expression; |
reference
A reference to a bit- or character-string variable. If the reference is to a varying-length character string, the substring defined by the position and length arguments must be within the current value of the string. Assignment to the SUBSTR pseudo-variable does not change the length of a varying string.position
An integer expression indicating the position of the first bit or character in the substring. The length must satisfy the following condition:1 <= position <= LENGTH(reference) + 1
length
An integer expression that indicates the length of the substring. If not specified, length is:length = LENGTH(reference) - position + 1
In other words, if length is not specified, the substring begins at the indicated position and ends at the end of the string. The length must satisfy the following condition:
0 <= length <= LENGTH(reference) - position + 1
Note that the following two lines are equivalent:
SUBSTR(r,p,l) = v; r = SUBSTR(r,1,p-1)||SUBSTR(v||SUBSTR(r,p+length(v)),1,l)||SUBSTR(r,p+l); |
Assignment to the SUBSTR pseudo-variable does not change the length of reference.
DECLARE (NAME,NEW_NAME) CHARACTER(20) VARYING; NAME = 'ISAK DINESEN'; NEW_NAME = NAME; SUBSTR(NEW_NAME,4) = 'AC NEWTON'; /* NEW_NAME = 'ISAAC NEWTON' */ |
The UNSPEC pseudo-variable interprets a reference to a scalar or aggregate element variable as a reference to a bit string. The format of the pseudo-variable (in an assignment statement) is:
UNSPEC(reference[,position[,length]]) = expression; |
reference
A reference to a scalar or aggregate variable. The length of its storage in bits must be less than or equal to the maximum length for bit-string data.
In an assignment of the form
UNSPEC(reference) = expression; |
the value of the expression is converted to a bit string if necessary and copied into the storage of the reference. The value is truncated or zero-extended as necessary to match the length of the storage.
To prevent zero-extending a value that is shorter than the variable, you can use the position parameter or both the position parameter and the length parameter. Then only the specified bits in the variable will be assigned a new value, and the other bits will remain as they were. Note that a position parameter of 1 refers to the low-order bit of the variable's storage, not the high-order bit.
DECLARE X FIXED BINARY (15); UNSPEC(X) = '110'B; |
UNSPEC(X,1,3) = '101'B; |
Keyword | Abbreviation | Use |
---|---|---|
A | Format item | |
ABS | Built-in function | |
ACOS | Built-in function | |
ADD | Built-in function | |
ADDR | Built-in function | |
ALIGNED | Attribute | |
ALLOCATE | ALLOC | Statement |
AREA | Data attribute, Condition name | |
ASIN | Built-in function | |
ATAN | Built-in function | |
ATAND | Built-in function | |
ATANH | Built-in function | |
AUTOMATIC | AUTO | Attribute |
B | Format item | |
B1 | Format item | |
B2 | Format item | |
B3 | Format item | |
B4 | Format item | |
BASED | Attribute | |
BEGIN | Statement | |
BINARY | BIN | Data attribute, Built-in function |
BIT | Data attribute, Built-in function | |
BOOL | Built-in function | |
BUILTIN | Attribute | |
BY | DO option | |
CALL | Statement | |
CEIL | Built-in function | |
CHARACTER | CHAR | Data attribute, Built-in function |
CLOSE | Statement | |
COLLATE | Built-in function | |
COLUMN | COL | Format item |
CONDITION | COND | Attribute, Condition name |
CONVERSION | CONV | Condition name, Condition prefix |
COPY | Built-in function | |
COS | Built-in function | |
COSD | Built-in function | |
COSH | Built-in function | |
CURRENTSIZE | Built-in function | |
DATE | Built-in function | |
DATETIME | Built-in function | |
DECIMAL | DEC | Data attribute, Built-in function |
DECLARE | DCL | Statement |
DECLARESIZE | DCLSIZE | Enquiry function |
DEFINE CONSTANT | Statement | |
DEFINED | DEF | Attribute |
DIMENSION | DIM | Attribute, Built-in function |
DIRECT | File attribute, OPEN option | |
DIVIDE | Built-in function | |
DO | Statement, GET and PUT I/O specifier | |
E | Format item | |
EDIT | GET option, PUT option, Built-in function | |
ELSE | Keyword of the IF statement | |
EMPTY | Built-in function | |
END | Statement | |
ENDFILE | Condition name | |
ENDPAGE | Condition name | |
ENTRY | Attribute | |
ENVIRONMENT | ENV | File attribute, Option of the OPEN, CLOSE, READ, WRITE, REWRITE, and DELETE statements |
EPSILON | Enquiry function | |
ERROR | Condition name | |
EVERY | Built-in function | |
EXP | Built-in function | |
EXTERNAL | EXT | Attribute |
F | Format item | |
FILE | Attribute, Option of the GET, PUT, READ, WRITE, DELETE, REWRITE, OPEN, and CLOSE statements | |
FILEOPEN | Built-in function | |
FINISH | Condition name | |
FIXED | Data attribute, Built-in function | |
FIXEDOVERFLOW | FOFL | Condition name, Condition prefix |
FLOAT | Data attribute, Built-in function | |
FLOOR | Built-in function | |
FORMAT | Statement | |
FREE | Statement | |
FROM | WRITE option, REWRITE option | |
GET | Statement | |
GOTO | GO TO | Statement |
HBOUND | Built-in function | |
HIGH | Built-in function | |
IDENTICAL | Built-in function | |
ISOCHAR | Built-in function | |
IF | Statement | |
IN | ALLOCATE option, FREE option | |
INDEX | Built-in function | |
INITIAL | INIT | Attribute |
INPUT | File attribute, OPEN option | |
INTERNAL | INT | Attribute |
INTO | READ option | |
KEY | Condition name, READ option, DELETE option, REWRITE option | |
KEYED | File attribute, OPEN option | |
KEYFROM | WRITE option | |
KEYTO | READ option | |
L | Format item | |
LABEL | Attribute | |
LBOUND | Built-in function | |
LEAVE | Statement | |
LENGTH | Built-in function | |
LINE | PUT option, Format item | |
LINENO | Built-in function | |
LINESIZE | OPEN option | |
LIST | Attribute, GET option, PUT option | |
LOG | Built-in function | |
LOG10 | Built-in function | |
LOG2 | Built-in function | |
LOOP | DO option | |
LOW | Built-in function | |
MAX | Built-in function | |
MAXVAL | Enquiry function | |
MAXLENGTH | Built-in function | |
MIN | Built-in function | |
MINVAL | Enquiry function | |
MOD | Built-in function | |
MULTIPLY | Built-in function | |
NOCONVERSION | NOCONV | Condition prefix |
NOFIXEDOVERFLOW | NOFOFL | Condition prefix |
NONRECURSIVE | PROCEDURE option | |
NONVARYING | NONVAR | Attribute |
NOOVERFLOW | NOOFL | Condition prefix |
NOSIZE | Condition prefix | |
NOUNDERFLOW | NOUFL | Condition prefix |
NOZERODIVIDE | NOZDIV | Condition prefix |
NULL | Built-in function, Enquiry function | |
OFFSET | Data attribute, Built-in function | |
ON | Statement | |
ONCODE | Built-in function | |
ONFILE | Built-in function | |
ONKEY | Built-in function | |
ONSOURCE | Built-in function, Pseudo-variable | |
OPEN | Statement | |
OPTIONS | File attribute, Option of the PACKAGE, PROCEDURE, and BEGIN statements | |
OTHERWISE | OTHER | Keyword of the SELECT statement |
OUTPUT | File attribute, OPEN option | |
OVERFLOW | OFL | Condition name, Condition prefix |
P | Format item | |
PAGE | PUT option, Format item | |
PAGENO | Built-in function, Pseudo-variable | |
PAGESIZE | OPEN option | |
PARAMETER | PARM | Attribute |
PICTURE | PIC | Data attribute |
POINTER | PTR | Data attribute, Built-in function |
File attribute, OPEN option | ||
PROCEDURE | PROC | Statement |
PROD | Built-in function | |
PUT | Statement | |
R | Format item | |
RADIX | Enquiry function | |
READ | Statement | |
RECORD | Condition name, File attribute, OPEN option | |
RECURSIVE | PROCEDURE option | |
REFER | Attribute | |
REPEAT | DO option | |
RETURN | Statement | |
RETURNS | Entry attribute, PROCEDURE option | |
REVERSE | Built-in function | |
REVERT | Statement | |
REWRITE | Statement | |
ROUND | Built-in function | |
SELECT | Statement | |
SEQUENTIAL | SEQL | File attribute, OPEN option |
SET | READ option, ALLOCATE option | |
SIGN | Built-in function | |
SIGNAL | Statement | |
SIN | Built-in function | |
SIND | Built-in function | |
SINH | Built-in function | |
SIZE | Condition name, Condition prefix | |
SIZETO | READ option | |
SKIP | GET option, PUT option, Format item | |
SNAP | ON statement option | |
SOME | Built-in function | |
SQRT | Built-in function | |
STATIC | Attribute | |
STOP | Statement | |
STORAGE | Condition name | |
STREAM | File attribute, OPEN option | |
STRING | GET option, PUT option, Built-in function, Pseudo-variable | |
SUBSTR | Built-in function, Pseudo-variable | |
SUBTRACT | Built-in function | |
SUM | Built-in function | |
SYSIN | Default input file | |
SYSPRINT | Default output file | |
SYSTEM | ON statement option | |
TAB | Format item | |
TALLY | Built-in function | |
TAN | Built-in function | |
TAND | Built-in function | |
TANH | Built-in function | |
THEN | Keyword of the IF statement | |
TIME | Built-in function | |
TITLE | OPEN option | |
TO | DO option | |
TRANSLATE | Built-in function | |
TRANSMIT | Condition name | |
TRIM | Built-in function | |
TRUNC | Built-in function | |
UNALIGNED | UNAL | Attribute |
UNDEFINEDFILE | UNDF | Condition name |
UNDERFLOW | UFL | Condition name, Condition prefix |
UNION | Attribute | |
UNSPEC | Built-in function, Pseudo-variable | |
UNTIL | DO option | |
UPDATE | File attribute, OPEN option | |
VALID | Built-in function | |
VARIABLE | Attribute | |
VARYING | VAR | Attribute |
VERIFY | Built-in function | |
WHEN | Keyword of the SELECT statement | |
WHILE | DO option | |
WRITE | Statement | |
X | Format item | |
ZERODIVIDE | ZDIV | Condition name, Condition prefix |
variable-reference [SET(locator-reference)][IN(area-reference)] |
target = expression; |
|
CALL entry-name [(argument,...)]; |
declaration:
[level] declaration-item |
declaration-item:
declaration:
declaration-item |
declaration-item:
DELETE FILE(file-reference) [KEY (expression)][ENVIRONMENT(option,...)] |
DO
END [label-reference]; |
label:
FORMAT (format-specification,...); |
FREE variable-reference [IN area-reference],...; |
GET EDIT (input-target,...)(format-specification,...)
GET LIST (input-target,...)
GET [FILE(file-reference)] SKIP [(expression)];
IF test-expression THEN action [ELSE action]; |
%INCLUDE
LEAVE [label-reference]; |
;
ON condition-name,...[SNAP]
entry-name:
package-name:
PUT EDIT (output-source,...) (format-specification,...)
PUT [FILE(file-reference)] LINE(expression);
PUT LIST (output-source,...)
PUT [ FILE(file-reference)] PAGE;
PUT [ FILE(file-reference)] SKIP [(expression)];
RETURN [ (return-value) ]; |
REVERT condition-name,...; |
SIGNAL condition-name; |
STOP; |
B.2 Attributes
Computational Data Type Attributes
The following attributes define arithmetic and string data:
These attributes can be specified for all elements of an array and for individual members of a structure.
Noncomputational Data Type Attributes
The following attributes apply to program data that is not used for computation:
Storage Class and Scope Attributes
The following attributes control the allocation and use of storage for a data variable and define the scope of the variable:
The following attributes can be applied to the major or minor members of a structure:
The following attributes can be applied to file constants and used in OPEN statements:
The following attributes can be applied to identifiers of entry points:
The following attributes can be applied to data declarations:
Category | Symbol | Operation |
---|---|---|
Arithmetic
operators |
+
- / * ** |
Addition or prefix plus
Subtraction or prefix minus Division Multiplication Exponentiation |
Relational
(or comparison) operators |
>
< = ^> ^< ^= >= <= |
Greater than
Less than Equal to Not greater than Not less than Not equal to Greater than or equal to Less than or equal to |
Bit-string
(or logical) operators |
^ (prefix)
& | &: |: ^ (infix) |
Logical NOT
Logical AND Logical OR Logical AND THEN Logical OR ELSE Logical EXCLUSIVE OR |
Concatenation
operator |
|| | String concatenation |
For any of the operators, the tilde character (~) can be used instead of a circumflex (^), and an exclamation point (!) can be used instead of a vertical bar (|). |
The following table gives the priority of PL/I operators. Low numbers indicate high priority. For example, the exponentiation operator (**) has the highest priority (1), so it is performed first, and the OR ELSE operator (|:) has the lowest priority (9), so it is performed last.
Operator | Priority | Left/Right Associative | Order of Evaluation |
---|---|---|---|
() | 0 | N/A | deepest first |
** | 1 | right | left to right |
+ (prefix) | 1 | N/A | N/A |
- (prefix) | 1 | N/A | N/A |
^ (prefix) | 1 | N/A | N/A |
* | 2 | left | left to right |
/ | 2 | left | left to right |
+ (infix) | 3 | left | left to right |
- (infix) | 3 | left | left to right |
|| | 4 | left | left to right |
> | 5 | left | left to right |
< | 5 | left | left to right |
^> | 5 | left | left to right |
^< | 5 | left | left to right |
= | 5 | left | left to right |
^= | 5 | left | left to right |
<= | 5 | left | left to right |
>= | 5 | left | left to right |
& | 6 | left | left to right |
| | 7 | left | left to right |
^ (infix) | 7 | left | left to right |
&: | 8 | left | left to right across entire expression |
|: | 9 | left | left to right across entire expression |
The following table discusses the contexts in which PL/I performs data conversion.
Contexts in Which PL/I Converts Data
Context | Conversion Performed |
---|---|
target = expression; | In an assignment statement, the given expression is converted to the data type of the target. |
entry-name
RETURNS (attribute...); . . . |
In a RETURN statement, the specified value is converted to the data type specified by the RETURNS option on the PROCEDURE statement. |
RETURN (value); | |
x + y
x - y x * y x / y x**y x||y x & y x | y x&:y x|:y x ^ y x > y x < y x = y x^=y |
In any expression, if operands do not have the required data type, they are converted to a common data type before the operation. For most operators, the data types of all operands must be identical. |
BINARY (expression)
BIT (expression) CHARACTER (expression) DECIMAL (expression) FIXED (expression) FLOAT (expression) OFFSET (variable) POINTER (variable) |
Subset G built-in functions that perform specific conversions. |
PUT LIST (item,...); | Items in a PUT LIST statement are converted to character-string data. |
GET LIST (item,...); | Character-string input data is converted to the data type of the target item. |
PAGESIZE (expression)
LINESIZE (expression) SKIP (expression) COLUMN (expression) format items A, B, E, F, and X TAB (expression) |
Values specified for various options to PL/I statements must be converted to integer values. |
DO control-variable... | Values are converted to the attributes of the control variable. |
parameter | Actual parameters are converted to the type of the formal parameter if necessary. |
INITIAL attribute | Initial values are converted to the type of the variable being initialized. |
PL/I has the following pseudo-variables:
A pseudo-variable can be used, in certain assignment contexts, in place of an ordinary variable reference. For example:
SUBSTR(S,2,1) = 'A'; |
A pseudo-variable can be used wherever the following three conditions are true:
Pseudo-variables are used most often in the following locations:
Note that a pseudo-variable cannot be used in preprocessor statements or in an argument list. For example:
CALL P(SUBSTR(S,2,1)); |
Here, SUBSTR is interpreted as a built-in function reference, not as a
pseudo-variable. The actual argument passed to procedure P is a dummy
argument containing the second character of string S.
Appendix C
Implementation-defined Features
The PL/I language features listed below are termed implementation-defined : their specification is regarded as completing the definition of the language for a particular implementation. A brief description of each feature is given.