Reference Manual

Previous | Contents | Index |

Expressions and Data Type Conversions

An expression is a representation of a value or of the computation of a value, and an assignment gives the value contained in an expression to a variable. Together, expressions and assignments form the mechanism for performing computation.

This chapter describes the following topics:

- The assignment statement
- Operators and operands, the elements of an expression
- The manner in which expression evaluation takes place
- Conversion of operands and expressions
- Conversion of the data types of operands during expression evaluation and assignment

The assignment statement gives a value to a specified variable. The format of the assignment statement is:

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 pseudovariable (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.

- The expression is evaluated, producing a value to be assigned to the target. An expression can consist of many subexpressions and operations, each of which must be evaluated.
- Each target is evaluated. If a target contains a pseudovariable, any expressions in the argument list are evaluated.
- If the data type of the result does not match the data type of a target variable, the resulting value is converted to the data type of the target, if possible. The compiler issues a WARNING message to alert you to the implicit conversion.
- The value of the expression is assigned to the targets.

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:
- array-variable = expression;
where expression yields a scalar value. Every element of the array is assigned the resulting value. - array-variable-1 = array-variable-2;
where the specified array variables have identical data type attributes and dimensions. Each element in array-variable-1 is assigned the value of the corresponding element in array-variable-2. The storage occupied by the two arrays must not overlap.
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 plus sign can prefix an arithmetic value or variable. However, it does not change the sign of the operand.
- A minus sign reverses the sign of an arithmetic operand.
- The logical NOT (^) prefix operator performs a logical NOT operation on a bit-string operand; the bit value is complemented.

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:

- Arithmetic operators must have arithmetic operands; if the operands are of different arithmetic types, they are converted before the operation to a single type, called the derived data type. Section 6.4.2 describes this process.
- Logical operators must have bit-string operands.
- Relational operators must have two operands of the same type. (Note, however, that comparisons are allowed between offsets and pointers.)
- The operators greater than (>), less than (<), not greater than (^>), not less than (^<), greater than or equal to (>=), and less than or equal to (<=) are valid only with computational operands.
- The concatenation operator must have two bit-string operands or two character-string operands.

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.

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.

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.

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()) &: (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).

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.

Previous | Next | Contents | Index |