Most built-in operators follow C/C++ syntax, though sometimes alternatives are available. Many operators can be overloaded. That is, structs and classes can often freely define what happens when you use a particular operator on them.
Currently, operator precedence, from highest to lowest, are:
| Precedence | Associativity | Operator | Overloadable | Description |
|---|---|---|---|---|
| N/A | None | (...) |
No | Tuple, subexpression |
[...] |
No | List | ||
{...} |
No | Dictionary (NIY) | ||
|...| |
Yes | Absolute value (NIY) | ||
||...|| |
Yes | Length (NIY) | ||
| 1 | Left | . |
See note 1 | Member reference |
?. |
See note 1 | Null-safe member reference | ||
??. |
See note 1 | Auto-allocating member reference | ||
| Postfix | (...) |
Yes (NIY) | Call | |
[...] |
See note 2 | Index | ||
| 2 | Prefix | @ |
No | Metaexpression |
* |
No | Expansion (NIY) | ||
| 3 | Postfix | <...> |
No | Generic type argument |
| 4 | Prefix | (:...) |
No | Static typecast |
ref |
No | Reference of | ||
sizeof |
No | (Static) Size of | ||
typeid |
Yes | (Dynamic) Type ID of | ||
| 5 | Left | as |
No | Dynamic typecast |
| 6 | Postfix | ! |
Yes | Factorial (NIY) |
~ |
Yes | Inverse (NIY) | ||
' |
Yes | Transpose (NIY) | ||
++ |
Yes | Post-increment | ||
-- |
Yes | Post-decrement | ||
| 7 | Right | ^ |
Yes | Power (NIY) |
| 8 | Prefix | + |
Yes | Copy |
- |
Yes | Negation | ||
~ |
Yes | Bitwise NOT | ||
++ |
Yes | Pre-increment | ||
-- |
Yes | Pre-decrement | ||
| 9 | Left | * |
Yes | Multiplication (matrix product) |
** |
Yes | Multiplication (dot product) (NIY) | ||
>< |
Yes | Multiplication (cross product) (NIY) | ||
/ |
Yes | Right division | ||
\ |
Yes | Left division (NIY) | ||
% |
Yes | Remainder | ||
*. |
Yes | Elementwise multiplication (NIY) | ||
/. |
Yes | Elementwise right division (NIY) | ||
\. |
Yes | Elementwise left division (NIY) | ||
div |
Yes | Euclidean division (NIY) | ||
mod |
Yes | Modulo (NIY) | ||
| 10 | Left | + |
Yes | Addition |
- |
Yes | Subtraction | ||
| 11 | Left | << |
Yes | Bit shift left |
>> |
Yes | Bit shift right | ||
| 12 | Left | & |
Yes | Bitwise AND |
| 13 | Left | <> |
Yes | Bitwise XOR |
| 14 | Left | | |
Yes | Bitwise OR |
| 15 | Left | ?? |
No | Null coalescing |
| 16 | None | :: |
No | Slice |
.. |
No | Range | ||
| 17 | None | == |
Yes | Equal to |
!= |
Yes | Not equal to | ||
=== |
No | Alias of | ||
!== |
No | Not alias of | ||
< |
Yes | Less than | ||
> |
Yes | Greater than | ||
<= |
Yes | Less than or equal to | ||
>= |
Yes | Greater than or equal to | ||
<=> |
Yes | Three-way comparison | ||
~= |
Yes | Pattern match (NIY) | ||
in |
Yes | Contained in | ||
not in |
Yes | Not contained in | ||
is |
See note 3 | (Dynamic) Derived type of | ||
is not |
See note 3 | (Dynamic) Not derived type of | ||
| 18 | Prefix | !, not |
Yes | Logical NOT |
| 19 | Left | &&, and |
Yes | Logical AND |
| 20 | Left | ||, or |
Yes | Logical OR |
| 21 | Right | ? : |
No | Conditional |
| 22 | Right | -> |
No | Anonymous function (NIY) |
| 23 | Right | <<< |
Yes | Receive (NIY) |
>>> |
Yes | Send (NIY) | ||
:= |
See note 4 | Inline assignment | ||
| 24 | None | throw |
No | Throw exception |
Notes:
is operator can be overloaded, is null and is not null is exempt from any overloading, and can thus be used as a reliable way of checking if a reference is null.The period, ., is used to refer to named members of objects, classes, namespaces, etc. For example, if universe is a reference to an object with an instance variable called answer, then you can say
universe.answer = 42
Following a reference in order to access the actual object is called dereferencing. Referencing and dereferencing is automatic, but depends on the types used (thus, typecasts affect the way this is done).
However, if an object reference is null (doesn't refer to anything), and the member is not static, then doing the above unconditionally will probably cause problems (most likely crash). If you're dealing with nullable references, then you'll usually have to explicitly check for null, but some operators are available to make the job easier.
is null or === null, since these constructs cannot be overloaded.?? (as seen in C#) allows you to substitute another object when the one you want is null. In the expression a ?? b, a is used if it's non-null, otherwise b is used.?. (as seen in Groovy) only follows the reference if it is not null. If it is null, then the expression (data access or method call) evaluates to the default value for the inferred expression type, usually null or zero. In the expression car?.drive(), the drive method is only called if car isn't a null reference.car?.new(), a new car object is allocated and assigned to car if it was null before this.??. will, if the reference is null, allocate a new object of the required type and assign it to the reference. Then it follows the reference as normal. In the expression car??.drive(), a new car object is allocated (using the default allocator) and assigned to car if it was null, and then the drive method is called, regardless of whether car was previously null or not.To call a function, method, or delegate, enclose its arguments in parentheses after its name (or other reference to it). Even if there are no arguments, the parentheses are necessary. For example,
exit()
calls the exit function with no arguments.
f = fac(2)
calls the fac function with one integer argument, and assigns its return value to the variable called f.
Many objects that can contain other objects, such as arrays, support indexed references as a means of referring to a particular contained object. In this case, enclose the index in square brackets after the container reference. For example, if prime_list were an array of prime numbers, then
prime = prime_list[3]
would give you the 3rd prime in this list.
Metaexpressions are evaluated at compile time, and can refer to information available to the compiler that wouldn't normally be available at runtime. A simple example is the source code filename (similar to the __FILE__ macro in C/C++). Metaexpressions are useful when used in metamethods, or in implicit parameters to ordinary methods.
The form @expression results in a regular parser token for the compiler. If the expression creates a string, then it is converted to an identifier (a reference to a named object).
The form @#expression results in a string. If the expression refers to an object, then the name of that object is converted to a string.
To manipulate an object's name as a string inside the metaexpression itself, refer to the object's name property, e.g., owner.name. This is useful for constructing new identifiers, e.g., @(owner.name + "Child").
Special information that can be used in metaexpressions include:
| Name | Type | Description |
|---|---|---|
| function | Function object | Enclosing function or method |
| owner | Class object | Enclosing class or struct |
| super | Class object | Base class or struct |
| module | String | Module name |
| file | String | Source file name |
| line | Integer | Source line number |