Chapter 6: Expressions

This chapter explains expressions and how they are used.

Introduction

An expression is a representation of a value or of the computation of a value. Expressions are classified as either element (scalar) expressions or array expressions.

Element expressions represent element values, including elementary names within a structure or a subscripted name that specifies a single element of an array. Array expressions represent either an entire array or a cross-section of an array. Element variables and array variables may be used in the same expression.

Expressions are used for the following reasons:

All expressions, except for single constants and references, are made up of an operator and one or more operands. An operator requires specific types of operands (arithmetic, character-string, or bit-string) and produces a result of a specific type. Operands can be constants, variable references, user-defined function references, built-in function references, or other expressions that match the type required by the operator.

Operators can be either infix or prefix. An infix operator is written between its two operands, and a prefix operator is written in front of its operands, as shown in the following table of operators.

Operator    Category    Example
+  –  *  /  **arithmetic infixB+C
+  –arithmetic prefix-C
=   ^=   >   <   >=   <=   ^<  ^>relationalB=C
|  or  &bit-string infixBIC
^bit-string prefix^C
||  or  !!concatenateB||C

One expression can contain both an infix and a prefix operator. For example:

A*-B

Notes: Open PL/I supports the use of a tilde (~) as an operator symbol equivalent to a caret (^).

Open PL/I supports the use of an exclamation point (!) as an operator symbol equivalent to a single bar (|), and a double exclamation point (!!) as equivalent to a double bar (||).

Order of Evaluation

When an expression contains more than one operator, the order in which each expression is evaluated is determined by the presence or absence of parentheses and by the priority of the expression's operators.

The following table shows the operators in order of descending priority. Operators listed on the same line have equal priority.

Operators
**
^ – (prefix unary operators)
*/
+ – (binary operators)
|| or !!
= ^=  >  <  >=  <= ^<  ^>
&
|

Notes: Open PL/I supports the use of a tilde (~) as an operator symbol equivalent to a caret (^).

Open PL/I supports the use of an exclamation point (!) as an operator symbol equivalent to a single bar (|), and a double exclamation point (!!) as equivalent to a double bar (||).

In expressions without parentheses, operators are evaluated in order of decreasing priority. For example:

B**3+C<D

is evaluated as:

((B**3)+C)<D

Operations of equal priority without parentheses are generally evaluated from left to right. For example:

A*B/C

is evaluated as:

(A*B)/C

The only exception to this rule is the prefix operators and the exponentiation operator (**), which are evaluated from right to left. For example:

A**B**C

is evaluated as:

A**(B**C)

If the expression contains parentheses, the expression within the parentheses is evaluated first according to the previous rules of priority. Its resulting value is used as a single operand. For example:

A+B*C

is evaluated as:

A+(B*C)

because * has higher priority than +; however, parentheses can be used to force another order:

(A+B)*C

If the result of an expression can be determined without evaluating all of its operands, the operands are not necessarily evaluated. A program that depends on all operands being evaluated is invalid and may produce unexpected results when compiled with optimization enabled, or when moved to another implementation of PL/I. Likewise, a program that depends on some operands not being evaluated is invalid. For example:

IF A = 0 | B/A = 5 THEN …

In this example, the Compiler can evaluate B/A before checking to see if A = 0, and the complete expression may produce a ZERODIVIDE condition. However, the order of evaluation is not guaranteed by the Compiler. In particular, if A is in fact 0, the value B/A may or may not be computed. This optimization, in which parts of a conditional expression may cause other "unnecessary" parts not to be evaluated, is called "short circuit evaluation." Such evaluation is done at the discretion of the Compiler and its optimizer. To force A = 0 to be evaluated first, rewrite the example as follows:

IF A ^= 0 THEN
   IF B/A ^= 5 THEN 
      GOTO L;
      .
      .
      .
   L:

The remainder of this chapter describes each type of operator.

Array Expressions

Array expressions can be used only in assignment statements. The result of the evaluation of an array expression is an array. Operations on arrays are performed element by element in row-major order. All arrays referred to in an array expression must have identical dimensions and bounds. Arithmetic operators excluding ** can be used for operators for array cross-sections.

Array expressions may include any of the following:

The operation and data conversion rules are the same as for element expressions.

Prefix Operators and Arrays

An array expression that contains prefix operators results in an array with identical bounds, with each element reflecting the result of the operation performed on the original element. For example:

Array A before operationOperationArray A after operation
12-3-A-1-23
456-4-5-6
78-9-7-89

Infix Operators and Arrays

Array expressions contain an array, an infix operator, and either an element or another array. The result of such an expression is an array with identical bounds and dimensions that reflect the result of the operation on the elements of the original array.

Array and Element Operations

An array expression that contains an infix operator, array, and element results in an array with identical bounds, with each element reflecting the result of the operation performed on the original array element and the single element. For example:

Array A before operationOperationArray A after operation
12-3A = A * 224-6
45681012

The element operand may be an element of the array:

Array A before operationOperationArray A after operation
12-3A = A * A(1,3)-3-69
456364554

In the preceding example, the element A(1,3) was -3 in the original array. When that element was operated on and resulted in 9, 9 was used in the operations on the rest of the array.

Array and Array Operations

An array expression that contains an infix operator and two arrays results in an array with identical bounds, with each element reflecting the result of the operation performed on the corresponding elements of the original arrays. For example:

Array A*Array B=Result
12-323426-12
456567203042

Note: This is not the same as mathematical matrix multiplication.

Types of Operators

The types of operators are arithmetic, relational, bit-string, and concatenate.

Arithmetic Operators

The arithmetic operators and the operations they stand for are:

Operator     Operation
prefix +Plus
prefix –Minus
+Addition
Subtraction
*Multiplication
/Division
**Exponentiation

The arithmetic operators require arithmetic operands. Although these operands can be of different arithmetic types, all operations must be performed on objects of the same type. Pictured values are converted to fixed decimal values, but other nonarithmetic values must be converted to arithmetic values by one of the conversion built-in functions, such as BINARY, DECIMAL, FIXED, or FLOAT.

If the data types of two operands of an infix operator (other than **) differ, the operands are converted to a common arithmetic data type, as shown in the following table.

One Operand    Other Operand     Common Operand
fixedfloatfloat
binarydecimalbinary (for floating-point or integer result)
binarydecimaldecimal (for fixed-point noninteger result)

All arithmetic operations except for exponentiation are performed in the derived type of the two operands. (For the precision resulting from the conversion of an operand to its derived type, see Table 6-1.) Though two converted operands may have the same derived base and scale, they may each have different values for p and q (scale and base, respectively).

All arithmetic operations, including exponentiation, produce a result that has the same data type as the type in which the operations are performed. In all operations but exponentiation, the data type is derived.

If an arithmetic operation is performed in floating point, the precision of the result is the maximum of the precisions of the two operands for binary operators, and is equal to the precision of the operand for unary operators. If this is not the desired result precision, use the FLOAT() built–in function to increase the precision of one or both of the operands.

An exponentiation operation on floating-point decimal data is actually performed using the floating-point binary operation. Thus, the range of the operands and the result of the operation are limited to the ranges for floating-point binary numbers.

The remainder of this section defines a series of rules, called fixed-point precision rules, which effectively allow the result of an expression to be formed by aligning the decimal points of the two operands to produce a fixed-point result. The number of digits in the result is always limited by the maximum number of digits allowed by the implementation for the result base; however, as many fractional digits as are allowed by the implementation are preserved, except for the result of divide. (For details on this implementation, see your Open PL/I User's Guide.) For divide, all integer quotient digits are preserved and as many fractional digits as can be allowed by the implementation are preserved.

The results of all arithmetic operations, except for exponentiation, always have a precision that is the maximum of the precisions of the converted operands.

The rules for determining the precision of the results of arithmetic expressions are as follows:

  1. Plus and minus produce a result whose data type is the same as the converted operand.
  2. Add and subtract of fixed-point values produce a fixed-point result whose base is the common base and whose precision is:
    FIXED(P,Q)±FIXED(R,S)

    and yields

    P=MIN(N,1+MAX(P-Q,R-S)+MAX(Q,S)) 
    Q=MAX(Q,S)

    where:

    N is the implementation's maximum allowed precision for fixed-point values of the result base (described in your Open PL/I User's Guide)

    (P,Q) is the converted precision of the first operand

    (R,S) is the converted precision of the second operand

  3. For integer operands (Q=0 and S=0), the result precision formula reduces to:
    FIXED(P,0)±FIXED(R,0)

    and yields

    P=MIN(N,1+MAX(P,R)) 
    Q=0
  4. Multiplication of fixed-point values produces a fixed-point result whose base is the common base and whose precision is:
    FIXED(P,Q)*FIXED(R,S)

    and yields

    P=MIN(N,P+R+1) Q=Q+S

    where:

    N is the implementation's maximum allowed precision for fixed-point values of the result base (described in your Open PL/I User's Guide)

    (P,Q) is the converted precision of the first operand,

    (R,S) is the converted precision of the second operand

  5. For integer operands (Q=0 and S=0), the result precision formula reduces to:
    FIXED(P,0)/FIXED(R,0)

    and yields

    P=MIN(N,P+R+1) 
    Q=0
  6. Division of fixed-point values is possible only if both operands are fixed decimal. The result is a fixed decimal value of precision:
    FIXED(P,Q)/FIXED(R,S)

    and yields

    P=N 
    Q=N-P+Q-S

    where:

    N is the maximum fixed-point decimal precision

    (P,Q) is the precision of the first operand

    (R,S) is the precision of the second operand

  7. For integer operands (Q=0 and S=0), the previous formula reduces to:
    FIXED(P,0)/FIXED(R,0)

    and yields

    P=N 
    Q=N-P

    The formula produces a result quotient that has sufficient precision for all integral digits of the quotient and as many fractional digits as are allowed by the implementation (for Open PL/I's limits, see your Open PL/I User's Guide).

  8. The result of a division operation may have a large fractional part, and in such instances, may not be able to be added or multiplied with another value because alignment of the decimal point with the other value produces a result that is too big for LPI-PLI to support.
  9. The ADD, DIVIDE, and MULTIPLY built-in functions can be used to add, divide, and multiply fixed-point binary values, as well as fixed-point decimal values. These functions provide you with control over the result precision. See the section DECIMAL Function.
  10. Exponentiation produces a result whose base, scale, and precision depends on the operands. The result is determined as follows:

    Case 1:

    If the first operand is fixed-point and the second operand is an integer constant whose value is Y, and if (P+1)*Y-1 does not exceed N, the result is a fixed-point value whose base is that of the first operand and whose precision is:

    FIXED(P,Q)**Y     /* Y is an integer constant */

    and yields

    P=(P+1)*Y-1 
    Q=Q*Y
    P<=N

    where N is the maximum precision allowed for fixed-point values of the base of the first operand, (P,Q) is the precision of the first operand, and Y is the value of the second operand.

    Case 2:

    If the second operand is a fixed-point integer value, but Case 1 does not apply, the result is a floating-point value whose base is the base of the first operand and whose precision is:

    P=MIN(N,P)

    where N is the maximum precision allowed for floating-point values of the resulting base and P is the precision of the first operand.

    Case 3:

    If neither Case 1 nor Case 2 applies, the result is a floating-point value having the common base and whose precision is:

    P=MIN(N,MAX(P,R))

    where N is the maximum precision allowed for floating values having the result base, P is the converted precision of the first operand, and R is the converted precision of the second operand.

  11. The result of exponentiation is normally the first operand raised to the power of the second operand. The following are exceptions to this general rule.

    Given X is the value of operand one and Y is the value of operand two:

Relational Operators

The relational operators and the operations they stand for are as follows:

Operators    Operations
=Equal
^=Not equal
>Greater than
<Less than
>=Greater than or equal to
<=Less than or equal to
^<Not less than (equivalent to >=)
^>Not greater than (equivalent to <=)

Note: Open PL/I supports the use of a tilde (~) as an operator symbol equivalent to a caret (^).

The relational operators test the relationship of two operands. The result is always a Boolean value (a bit string of length 1). If the comparison is true, the resulting value is '1'B; if the comparison is false, the resulting value is '0'B. All relational operators are infix operators.

All operands used with relational operators must be scalar. If either operand is an arithmetic value or a pictured value, the operands are converted to a common arithmetic type as if they were operands of an add operator. If one operand is a pointer value and the other is an offset value, the offset is converted to a pointer, but only for = and ^= comparisons. In all other cases, the data types of the two operands must be equivalent.

When determining if two data types are equivalent, the ALIGNED, VARYING, RETURNS, UNALIGNED, VARIABLE, and string length attributes are ignored. Label, entry, file, and pointer data may be compared only for equality or inequality. Arithmetic and string data may be compared using any relational operator.

Arithmetic and pictured values are compared algebraically.

Character-string values are compared from left-to-right one character at a time until an inequality is found. The shorter value is effectively extended on the right with blank characters to make it the length of the longer value. Characters are compared using the collating sequence of the computer.

Bit-string values are compared from left-to-right one bit at a time until an inequality is found. The shorter value is effectively extended with zero bits on its right until it is the length of the longer value.

Pointer values are equal only if they address the same storage location.

Label and entry values are equal only if they designate the same statement and the same stack frame.

File values are equal only if they designate the same file control block.

Only comparisons of = and ^= are allowed for offset values. Area variables cannot be compared.

Bit-String Operators

The bit-string operators and the operations they stand for are as follows:

Operators    Operations
&and
|inclusive or
^not (complement)

Note: Open PL/I supports the use of a tilde (~) as an operator symbol equivalent to a caret (^).

Open PL/I supports the use of an exclamation point (!) as an operator symbol equivalent to a single bar (|).

Bit-string operators require bit-string operands. Operands of other data types must be converted to bit-string values using the BIT built-in function described in the section BIT Function.

The infix operators & and | operate on operands of dissimilar length by effectively extending the shorter value to the length of the longer value. This is done by appending zero bits to the right end of the shorter value.

The result of ^ is a bit string whose bits are the complement of the bits in the operand; that is, each 0 bit becomes a 1 bit and each 1 bit becomes a 0 bit.

The result of & and | is a bit string whose length is that of the larger operand. Each bit of the result is listed as follows:

First Operand     Second Operand     &    
0 0 0 0
0 1 0 1
1 0 0 1
1 1 1 1

For instance, if X is '01011'B and Y is '11001'B, ^X produces '10100'B, X&Y produces '01001'B, and X | Y produces '11011'B. X&'11'B would produce '01000'B.

The Concatenate Operator

The concatenate operator, || or !!, is used to concatenate two strings to produce a string result.

If both operands are bit strings, the concatenate operator produces a bit-string result; otherwise, both operands are converted to character strings, and a character-string result is produced.

The length of the string result is the sum of the lengths of the converted operands, as shown in the following example:

A = 'ABC';
B = 'XYZ';

In the previous example, A | | B produces 'ABCXYZ ', and A | ! 5 produces 'ABC     5'. The conversion rule that produces the blanks is explained in the section Arithmetic to Character-String Conversion.


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