Terms, Expressions, and Operators

Expressions in Rexx are a general mechanism for combining one or more pieces of data in various ways to produce a result, usually different from the original data. All expressions evaluate to objects.

Everything in Rexx is an object. Rexx provides some objects, which are described in later sections. You can also define and create objects that are useful in particular applications—for example, a menu object for user interaction. See Modeling Objects for more information.

Terms and Expressions

Terms are literal strings, symbols, message terms, function calls, or subexpressions interspersed with zero or more operators that denote operations to be carried out on terms.

Literal strings, which are delimited by quotation marks, are constants.

Symbols (no quotation marks) are translated to uppercase. A symbol that does not begin with a digit or a period can be the name of a variable; in this case the value of that variable is used. A symbol that begins with a period can identify an object that the current environment provides; in this case, that object is used. Otherwise a symbol is treated as a constant string. A symbol can also be compound.

Message terms are described in Message Terms.

Function calls (see Functions), which are of the following form:

                    +-,--------------+
                    V                |
>>-symbolorstring(----+------------+-+--)----------------------><
                      +-expression-+

The symbolorstring is a symbol or literal string.

An expression consists of one or more terms. A subexpression is a term in an expression surrounded with a left and a right parenthesis.

Evaluation of an expression is left to right, modified by parentheses and operator precedence in the usual algebraic manner (see Parentheses and Operator Precedence). Expressions are wholly evaluated, unless an error occurs during evaluation.

As each term is used in an expression, it is evaluated as appropriate. The result is an object. Consequently, the result of evaluating any expression is itself an object (such as a character string).

Operators

An operator is a representation of an operation, such as an addition, to be carried out on one or two terms. Each operator, except for the prefix operators, acts on two terms, which can be symbols, strings, function calls, message terms, intermediate results, or subexpressions. Each prefix operator acts on the term or subexpression that follows it. Whitespace characters (and comments) adjacent to operator characters have no effect on the operator; thus, operators constructed from more than one character can have embedded whitespace and comments. In addition, one or more whitespace characters, if they occur in expressions but are not adjacent to another operator, also act as an operator. The language processor functionally translates operators into message terms. For dyadic operators, which operate on two terms, the language processor sends the operator as a message to the term on the left, passing the term on the right as an argument. For example, the sequence

say 1+2

is functionally equivalent to:

say 1~"+"(2)

The blank concatenation operator sends the message " " (a single blank), and the abuttal concatenation operator sends the "" message (a null string). When the ¬ character is used in an operator, it is changed to a \. That is, the operators ¬= and \= both send the message \= to the target object.

For an operator that works on a single term (for example, the prefix - and prefix + operators), Rexx sends a message to the term, with no arguments. This means -z has the same effect as z~"-".

See Operator Methods for operator methods of the Object class and Arithmetic Methods for operator methods of the String class.

There are four types of operators:

String Concatenation

The concatenation operators combine two strings to form one string by appending the second string to the right-hand end of the first string. The concatenation may occur with or without an intervening blank. The concatenation operators are:

(blank)

Concatenate terms with one blank in between

||

Concatenate without an intervening blank

(abuttal)

Concatenate without an intervening blank

You can force concatenation without a blank by using the || operator.

The abuttal operator is assumed between two terms that are not separated by another operator. This can occur when two terms are syntactically distinct, such as a literal string and a symbol, or when they are only separated by a comment.

Examples:

An example of syntactically distinct terms is: if Fred has the value 37.4, then Fred"%" evaluates to 37.4%.

If the variable PETER has the value 1, then (Fred)(Peter) evaluates to 37.41.

The two adjoining strings, one hexadecimal and one literal, "4a 4b"x"LMN" evaluate to JKLMN.

In the case of

Fred/* The NOT operator precedes Peter. */¬Peter

there is no abuttal operator implied, and the expression is not valid. However,

(Fred)/* The NOT operator precedes Peter. */(¬Peter)

results in an abuttal, and evaluates to 37.40.

Arithmetic

You can combine character strings that are valid numbers (see Numbers) using the following arithmetic operators:

+

Add

-

Subtract

*

Multiply

/

Divide

%

Integer divide (divide and return the integer part of the result)

//

Remainder (divide and return the remainder—not modulo, because the result can be negative)

**

Power (raise a number to a whole-number power)

Prefix -

Same as the subtraction: 0 - number

Prefix +

Same as the addition: 0 + number

See Numbers and Arithmetic for details about precision, the format of valid numbers, and the operation rules for arithmetic. Note that if an arithmetic result is shown in exponential notation, it is likely that rounding has occurred.

Comparison

The comparison operators compare two terms and return the value 1 if the result of the comparison is true, or 0 otherwise.

The strict comparison operators all have one of the characters defining the operator doubled. The ==, \==, and ¬== operators test for an exact match between two strings. The two strings must be identical (character by character) and of the same length to be considered strictly equal. Similarly, the strict comparison operators such as >> or << carry out a simple character-by-character comparison, with no padding of either of the strings being compared. The comparison of the two strings is from left to right. If one string is shorter than the other and is a leading substring of another, then it is smaller than (less than) the other. The strict comparison operators also do not attempt to perform a numeric comparison on the two operands.

For all other comparison operators, if both terms involved are numeric, a numeric comparison (see Numeric Comparisons) is effected. Otherwise, both terms are treated as character strings, leading and trailing whitespace characters are ignored, and the shorter string is padded with blanks on the right.

Character comparison and strict comparison operations are both case-sensitive, and the exact collating order might depend on the character set used for the implementation. In an ASCII environment, such as Windows and *nix, the ASCII character value of digits is lower than that of the alphabetic characters, and that of lowercase alphabetic characters is higher than that of uppercase alphabetic characters.

The comparison operators and operations are:

=

True if the terms are equal (numerically or when padded)

\=, ¬=

True if the terms are not equal (inverse of =)

>

Greater than

<

Less than

><

Greater than or less than (same as not equal)

<>

Greater than or less than (same as not equal)

>=

Greater than or equal to

\<, ¬<

Not less than

<=

Less than or equal to

\>, ¬>

Not greater than

==

True if terms are strictly equal (identical)

\==, ¬==

True if the terms are not strictly equal (inverse of ==)

>>

Strictly greater than

<<

Strictly less than

>>=

Strictly greater than or equal to

\<<, ¬<<

Strictly not less than

<<=

Strictly less than or equal to

\>>, ¬>>

Strictly not greater than

Note: Throughout the language, the NOT (¬) character is synonymous with the backslash(\). You can use the two characters interchangeably, according to availability and personal preference. The backslash can appear in the following operators: \ (prefix not),\=, \==, \<, \>, \<<, and \>>.

Logical (Boolean)

A character string has the value false if it is 0, and true if it is 1. The logical operators take one or two such values and return 0 or 1 as appropriate. Values other than 0 or 1 are not permitted.

&

AND — returns 1 if both terms are true.

|

Inclusive OR — returns 1 if either term or both terms are true.

&&

Exclusive OR — returns 1 if either term, but not both terms, is true.

Prefix \, ¬

Logical NOT— negates; 1 becomes 0, and 0 becomes 1.

Parentheses and Operator Precedence

Expression evaluation is from left to right; parentheses and operator precedence modify this:

For example, * (multiply) has a higher priority than + (add), so 3+2*5 evaluates to 13 (rather than the 25 that would result if a strict left-to-right evaluation occurred). To force the addition to occur before the multiplication, you could rewrite the expression as (3+2)*5. Adding the parentheses makes the first three tokens a subexpression. Similarly, the expression -3**2 evaluates to 9 (instead of -9) because the prefix minus operator has a higher priority than the power operator.

The order of precedence of the operators is (highest at the top):

~   ~~\(message send)
+   -   ¬   \(prefix operators)
**(power)
*   /   %   //(multiply and divide)
+   -(add and subtract)
(blank)   ||   (abuttal)(concatenation with or without blank)
=   >   <(comparison operators)
==   >>   << 
\=   ¬= 
><   <> 
\>   ¬> 
\<   ¬< 
\==   ¬== 
\>>   ¬>> 
\<<   ¬<< 
>=   >>= 
<=   <<= 
&(and)
|   &&(or, exclusive or)

Examples:

Suppose the symbol A is a variable whose value is 3, DAY is a variable whose value is Monday, and other variables are uninitialized. Then:

A+5                  ->    "8"
A-4*2                ->    "-5"
A/2                  ->    "1.5"
0.5**2               ->    "0.25"
(A+1)>7              ->    "0"         /* that is, False */
" "=""               ->    "1"         /* that is, True  */
" "==""              ->    "0"         /* that is, False */
" "\==""             ->    "1"
/* that is, True  */
(A+1)*3=12           ->    "1"         /* that is, True  */
"077">"11"           ->    "1"         /* that is, True  */
"077" >> "11"        ->    "0"         /* that is, False */
"abc" >> "ab"        ->    "1"         /* that is, True  */
"abc" << "abd"       ->    "1"         /* that is, True  */
"ab " << "abd"       ->    "1"         /* that is, True  */
Today is Day         ->    "TODAY IS Monday"
"If it is" day       ->    "If it is Monday"
Substr(Day,2,3)      ->    "ond"    /* Substr is a function */
"!"xxx"!"            ->    "!XXX!"

Note: The Rexx order of precedence usually causes no difficulty because it is the same as in conventional algebra and other computer languages. There are two differences from common notations:

  • The prefix minus operator always has a higher priority than the power operator.

  • Power operators (like other operators) are evaluated from left to right.

For example:

-3**2     ==  9  /* not -9  */
-(2+1)**2 ==  9  /* not -9  */
2**2**3   == 64  /* not 256 */

Message Terms

You can include messages to objects in an expression wherever a term, such as a literal string, is valid. A message can be sent to an object to perform an action, obtain a result, or both.

A message term can have one of the following forms:

>>-receiver-+- ~ --+-messagename--+---------+------------------->
            +- ~~ -+              +-:symbol-+

>--+--------------------------+--------------------------------><
   +-(--+----------------+--)-+
        | .-,----------+ |
        | V            | |
        +---expression-+-+
>>-receiver[--+----------------+--]----------------------------><
              | +-,----------+ |
              | V            | |
              +---expression-+-+

The receiver is a term (see Terms and Expressions for a definition of term). It receives the message. The ~ or ~~ indicates sending a message. The messagename is a literal string or a symbol that is taken as a constant. The expressions (separated by commas) between the parentheses or brackets are the arguments for the message. The receiver and the argument expressions can themselves include message terms. If the message has no arguments, you can omit the parentheses.

The left parenthesis, if present, must immediately follow a token (messagename or symbol) with no blank in between them. Otherwise, only the first part of the construct is recognized as a message term. (A blank operator would be assumed at that point.) Only a comment (which has no effect) can appear between a token and the left parenthesis.

You can use any number of expressions, separated by commas. The expressions are evaluated from left to right and form the arguments during the execution of the called method. Any ARG, PARSE ARG, or USE ARG instruction or ARG() built-in function in the called method accesses these objects while the called method is running. You can omit expressions, if appropriate, by including extra commas.

The receiver object is evaluated, followed by one or more expression arguments. The message name (in uppercase) and the resulting argument objects are then sent to the receiver object. The receiver object selects a method to be run based on the message name (see Classes and Inheritance), and runs the selected method with the specified argument objects. The receiver eventually returns, allowing processing to continue.

If the message term uses ~, the receiver method must return a result object. This object is included in the original expression as if the entire message term had been replaced by the name of a variable whose value is the returned object.

For example, the message POS is valid for strings, and you could code:

c="escape"
a="Position of 'e' is:" c~pos("e",3)
/* would set A to "Position of 'e' is: 6" */

If the message term uses ~~, the receiver method need not return a result object. Any result object is discarded, and the receiver object is included in the original expression in place of the message term.

For example, the messages INHERIT and SUBCLASS are valid for classes (see The Class Class) and, assuming the existence of the Persistent class, you could code:

account = .object~subclass("Account")~~inherit(.persistent)
/* would set ACCOUNT to the object returned by SUBCLASS, */
/* after sending that object the message INHERIT */

If the message term uses brackets, the message [] is sent to the receiver object. (The expressions within the brackets are available to the receiver object as arguments.) The effect is the same as for the corresponding ~ form of the message term. Thus, a[b] is the same as a~"[]"(b).

For example, the message [] is valid for arrays (see The Array Class) and you could code:

a = .array~of(10,20)
say "Second item is" a[2]   /* Same as: a~at(2)              */
/* or a~"[]"(2)                  */
/* Produces: "Second item is 20" */

A message can have a variable number of arguments. You need to specify only those required. For example, "ESCAPE"~POS("E") returns 1.

A colon (:) and symbol can follow the message name. In this case, the symbol must be the name of a variable (usually the special variable SUPER--see page SUPER) or an environment symbol (see Environment Symbols). The resulting value changes the usual method selection. For more information, see Changing the Search Order for Methods.

Message Sequences

The ~ and ~~ forms of message terms differ only in their treatment of the result object. Using ~ returns the result of the method. Using ~~ returns the object that received the message. Here is an example:

/* Two ways to use the INSERT method to add items to a list */
/* Using only ~ */
team = .list~of("Bob","Mary")
team~insert("Jane")
team~insert("Joe")
team~insert("Steve")
say "First on the team is:" team~firstitem     /* Bob   */
say "Last on the team is:" team~lastitem       /* Steve */
/* Do the same thing using ~~ */
team=.list~of("Bob","Mary")
/* Because ~~ returns the receiver of the message       */
/* each INSERT message following returns the list       */
/* object (after inserting the argument value).         */
team~~insert("Jane")~~insert("Joe")~~insert("Steve")
say "First on the team is:" team~firstitem     /* Bob   */
say "Last on the team is:" team~lastitem       /* Steve */

Thus, you would use ~ when you want the returned result to be the receiver of the next message in the sequence.