2.2. Operators

Expressions are composed of operators and operands. Operators come in three flavours: with 1, 2, or 3 operands. Each operand in turn is an expression.

Each operator has a priority. For instance, the priority of the * is higher than that of +, causing 1 + 2 * 3 to be interpreted as first multiplying 2 by 3 followed by the addition of 1. Parentheses can be used to impose a different evaluation order: (1 + 2) * 3 will first add 1 and 2, multiplying the result with 3.

You can experiment with expressions by modifying the hello.t program to print something different from its familiar message. Here are a few examples; remember to run make to have the program rebuilt after you have modified the source.

[[[stdio out] print 1 + 2 * 3] nl];
[[[stdio out] print (1 + 2) * 3] nl];

And a more daring example---notice how print accepts more than one thing, grouped by parentheses and separated by commas.

[[[stdio out] print ("0 F = ", 5.0 / 9.0 * (0.0 - 32.0), " C")] nl];

Table 2-3 lists all operators. Operators nearer to the top have a higher priority than those below it. In the same group, between horizontal lines, operators have the same priority.

[Horizontal lines have disappeared.]

Table 2-3. Operators

++, --1rightincrement, decrement
-, ~, !1rightminus, inversion, not
*, /, %2leftmultiply, divide, modulo
+, -2leftadd, subtract
<<, >>2leftarithmetic shift
>>>2leftlogic shift right
&2leftbitwise and
|2leftbitwise or
^2leftbitwise exclusive-or
<, <=, >=, >2leftordered comparison
==, !=2leftequality comparison
&&2leftboolean and
||2leftboolean or
=, etc.2rightassignment (see text)

The unary minus returns the negation of its numeric argument. Thus, evaluating any of the expressions -1, -(1), and -(2 - 1) all return the value -1. Negation preserves the type of its argument: negating an int value results in another int value.

The bitwise inversion operator, ~, returns, given an integer numeric argument, an integer numeric value of the same type, but with all 0 bits replaced by 1 bits, and vice versa. Thus, ~0 returns -1.

The boolean not operator, !, returns FALSE iff its operand has the default value for its type, and TRUE otherwise. For example, the default value of numeric types is 0, so !0 will return TRUE and !456 will return FALSE.

The value returned by ! has the boolean type. The only values of this type are TRUE and FALSE, also known as YES and NO.

Apart from >>> and -> the binary operators perform the same function as their C, and many other languages, equivalents. >>> shifts a signed number in the same way as >> shifts an unsigned number in C. The implication operator, ->, performs the boolean implication: a -> b is equivalent to !a || b. Table 2-4 shows a few examples of their use.

Table 2-4. Examples of >>> and ->

128 >> 164
-1 >> 1-1
-1 >>> 10x7fffffff

The operators *, /, +, -, <, <=, >=, and > operate on any numeric type. The %, <<, >>, >>>, &, |, and ^ operate on integer numeric types. &&, ||, and -> operate on the boolean type. Furthermore, == and != operate on any type, including those to be introduced later on.

All binary operators are left associative, except the assignment operators, =, +=, etc., which is right-associative. The order of evaluation of the operands follows the associativity of the operator. Thus, 1 + 2 + 3 is interpreted as (1 + 2) + 3, and the 3 is only evaluated after the addition of 1 and 2---not that it makes any difference in this simple case. Furthermore, a = b = c means a = (b = c). (We will return to the assignment operator later on---they aren't much use now when constants and operators are the only building blocks at hand.)

The boolean operators are short-circuited. This means that if enough information is known about the operands that the result of the whole expression is known the evaluation of any remaining operands is skipped. Thus, FALSE && x returns FALSE without ever evaluating x, since its value does not matter.

There is only one ternary operator, ? :, also known as the if-then-else operator. To compute the maximum of a and b, one could use a > b ? a : b, meaning that if a is larger than b, the second expression (in this case a) will be evaluated and returned as the result; otherwise the result of the third expression (b) is the result of the whole expression.

In general, when used as x ? y : z, the type of x must be boolean, and the type of y must equal the type of z. Furthermore, only one of y and z will be evaluated.