@head @title Expressions @grammar expression :: list-expression | single-expression single-expression :: simple-expression | self-expression | simple-name-expression | gvar | "(" expression ")" | binary-operation | boolean-operation | unary-operation | subscript-expression | call-expression | array-expression | tuple-expression | member-reference | super-reference | anonymous-function | cast-expression @end
Expressions perform all interesting computation in Alore programs. Parentheses can be used for grouping and for overriding operation evaluation order. @h2 Operator precedence
All Alore operators are listed below, from the highest to the lowest precedence, with each operator on the same line having the same precedence:
All operators except the exponentiation operator ** are left associative, i.e. a + b + c == (a + b) + c, whereas ** is right associative, i.e. a**b**c == a**(b**c).
The comma operator is used to form lists of arbitrary length. Associativity rules do not apply to the comma operator. @h2 Lvalues and rvalues
Expressions can be evaluated as either rvalues or lvalues. Lvalues are only expected on the left hand side of an assignment statement. Valid lvalues include:
All expressions are valid in an rvalue context, by contrast. Note that the left hand side of assignment may include rvalue expressions as the subexpressions of lvalue expressions (target of member reference and subscripts in subscript expressions).
The semantics of lvalue expressions are described in section @href{Assignment statement}. The descriptions below only apply to rvalue expressions, unless explicitly mentioned otherwise. @h2 Expressions in boolean context
Some expressions are evaluated in a boolean context. Such an expression is expected to evaluate to a boolean value, either True or False, as its value. If the expression evaluates to some other value, a @ref{std::TypeError} exception will be raised. @h2 Order of evaluation
Operations within a single expression are always evaluated in their natural order as specified by precedence and associativity rules: operations with a higher precedence are evaluated first, and consecutive operations with the same precedence are evaluated left to right (if the operations are left associative) or right to left (otherwise). The natural evaluation order of comma-separated lists is from left to right. Parentheses can be used to change the default operation evaluation order.
Variable and constant references within a single expression may be evaluated in an arbitrary order, but all the operands and arguments of operations must be evaluated before the operation is performed.
Otherwise, expressions and statements are always evaluated in their entirety in the order described in this document, unless some operation causes an exception to be raised. At the instant an exception is raised, any statements or expressions that are not fully evaluated are terminated. @h2 Simple expressions @grammar simple-expression :: int | float | str | "nil" @end
Integer literals evaluate to @ref{Int} objects. Correspondingly, float and string literals evaluate to @ref{Float} and @ref{Str} objects. The nil expression evaluates to the special nil object. All of these expressions are only valid as rvalues. @h2 Self expression @grammar self-expression :: "self" @end
The self expression evaluates to the self object, i.e. the object that is bound to the current evaluated method or the object whose getter, setter or initialization expression is being evaluated. The self expression is only valid as an rvalue. @h2 Simple name expressions @grammar simple-name-expression :: id @end
A simple name expression evaluates to either a local variable, a member of self, or a global definition. Local definitions take precedence over other definitions and member definitions take precedence over global definitions (see @href{Precedence of names} for details). Evaluating a local or global reference results in the value of the variable or constant. Evaluating a member reference is equivalent to evaluating the member reference self.id (and it is an error to access member create). It results in the evaluation of the getter for id or in the creation of a bound method object (see @href{Member references} for the details). @h2 Referencing global definitions @grammar gvar :: id ("::" id)+ | "::" id @end
A global reference evaluates to the value of a global definition. The form ::id refers to a global definition in the current module. Otherwise, the last id is the name of a global definition, and all elements before the last :: form the module prefix.
If the module prefix matches the name of a module imported in the current source file, the reference refers to that module. Otherwise, the prefix must form a unique suffix of the name of any module that was imported in the current file. In this case, the reference refers to that module. Otherwise, the reference is invalid and a compile error will be reported. @h2 Binary operations @grammar binary-operation :: expression binary-op expression binary-op :: "+" | "-" | "*" | "/" | "div" | "mod" | "**" | "==" | "!=" | "<" | "<=" | ">" | ">=" | "is" | "in" | "to" | ":" @end
Many of the binary operations are evaluated as method calls. The table below contains the mappings between these operators and methods. The operation will be performed by calling the method mentioned in the table on the left operand with the right operand as the argument (but see (3) in the note section for an exception). The return value of the chosen method is usually the result of the operation (but see (2) in the note section). If the method is not supported by the operand, a @ref{std::MemberError} exception will be raised.
Operator | Method | Notes |
---|---|---|
+ | _add | |
- | _sub | |
* | _mul | |
/ | _div | |
div | _idiv | |
mod | _mod | |
** | _pow | |
== | _eq | (1) |
!= | _eq | (1) (2) |
< | _lt | (1) |
> | _gt | (1) |
<= | _gt | (1) (2) |
>= | _lt | (1) (2) |
in | _in | (1) (3) |
Notes:
(1) | The operation is evaluated in a boolean context, i.e. it must return a boolean value, or a @ref{std::TypeError} exception will be raised. |
(2) | The result of the operation is the boolean negation of the value returned by the method. |
(3) | The _in method of the right operand is called, and the left operand is passed as the argument. |
In addition, the following binary operators are not mapped to method calls, but are evaluated directly:
Equality comparison against a literal nil value is treated specially. In binary operations with == or != as the operator and a literal nil expression as the right operand, the left and right operands are swapped before evaluation. @h2 Boolean operations @grammar boolean-operation :: expression ("and" | "or") expression @end
The "and" and "or" operators are short-circuit boolean operators. Their arguments are evaluated in a boolean context. The and operator evaluates to True if and only if both of the operands are True; otherwise, it evaluates to False. The or operator evaluates to True if either one or both of the operands is True; otherwise, it evaluates to False.
The boolean operators use short-circuit evaluation. If the first operand of an and operation evaluates to False or if the first operand of an or operation evaluates to True, the second operand is not evaluated at all. In this case, the value of the operation is the same as if the second operand evaluated to any boolean value, i.e. equivalent to the value of the first operand. @h2 Unary operations @grammar unary-operation :: ("-" | "not") expression @end
Both of the unary operations have unique semantics:
The subscript expression is evaluated by calling the _get method of the left operand with the right operand (index) as the only argument. The return value of that method is the value of the expression.
The subscript expression is also valid in an lvalue context; see section @href{Assignment statement} for details. @h2 Call expressions @grammar call-expression :: expression "(" args ")" args :: [ "*" expression ] | single-expression ("," single-expression)* [ "," "*" expression ] @end
The call expressions are used for executing the bodies of functions and methods. Additionally the evaluation of most operator expressions falls back to call expressions.
The expression before the left parenthesis evaluates to the called object. The expressions, if any, between the parentheses evaluate to the actual arguments. If the final argument expression is prefixed with *, this argument is a variable-length argument list. Arguments not prefixed with * are the fixed (actual) arguments.
If the called object is a global function, an anonymous function or a bound method, the formal argument list and the body of the function are defined by this object. Otherwise, the formal argument list and the body of the function are those of the _call method of the object being called. If this method is not defined, the object is not callable and an exception will be raised.
If the call expression has only fixed arguments and if all the formal arguments are fixed and required (without default values), the function call is evaluated as follows:
If the call expression contains a variable-length argument list expression, it must evaluate to an @ref{Array} or a @ref{Tuple} object (or an object of an Array subclass). The items of this sequence behave identically to fixed actual arguments, as if they were included at the end of the argument list as fixed arguments.
If some of the formal arguments are optional (i.e. have default argument values), these rules are followed:
If the formals include a variable-length argument list, any number of additional actual arguments are accepted beyond the fixed formal arguments. An Array object is created that holds these extra arguments, in the same order they appear in the argument list. This object is assigned to the formal argument preceded by *. If there are no extra arguments, an empty Array object will be created and assigned instead. @h2 Array constructors @grammar array-expression :: "[" comma-list "]" comma-list :: single-expression | list-expression list-expression :: single-expression ("," | ("," single-expression)+ [ "," ]) @end
Construct an @ref{Array} instance with the values of the subexpressions, separated by commas, as the items. The length of the array is equal to the number of items in the array expression. @h2 Tuple constructors @grammar tuple-expression :: "(" ")" | list-expression @end
Construct a @ref{Tuple} instance with the values of the subexpressions, separated by commas, as the items. The length of the tuple is equal to the number of items in the array expression.
An empty tuple is constructed using the () form. To construct a single-item tuple, a trailing comma must be provided. Tuple expressions have to parenthesized when used in contexts where comma has another meaning, such as in argument lists of call expressions. @h2 Member references @grammar member-reference :: expression "." id @end
This expression refers to the member id of an object, specified by the left operand expression. In an rvalue context, it is evaluated by either constructing a bound method object of type std::Function (if the member refers to a method) or by evaluating the getter method of the member (if the member refers to a member variable or constant). Otherwise, if the member is not defined, raise a @ref{std::MemberError} exception.
In an lvalue context, id must refer to a member variable (a member with a setter method), or otherwise a @ref{std::MemberError} exception will be raised. See section @href{Assignment statement} for details.
It is an error to access the member create using a member reference.
If the member in a dot expression refers to a method, the result of the operation is a @ref{std::Function} object that represents the method bound to the specific object. A bound method accepts the arguments specified in the method definition, and when the bound method is called, the body of the method is evaluated so that self refers to the bound object.
Getters and setters behave like methods that take either no arguments (getters) or a single argument (setters). Unlike methods, they can only be directly called; it is not possible to build bound getter or setter method objects. Is is also impossible to call getters or setters using the ordinary () operator; they are only called implicitly when reading a member or assigning to a member. @h2 Superclass member references @grammar super-reference :: "super" "." id @end
If the member id refers to a method in the current class, this is evaluated to a bound method object representing the definition of the method id in the superclass of the current class. The method must be defined in the superclass. Bound method references are only valid in an rvalue context.
If the member id refers to a member variable or constant in the current class, the getter method of the member defined in the superclass of the current class is called (in an rvalue context) or, in a similar fashion, the superclass setter method is called (in an lvalue context), giving the assigned value as the argument. The getter or setter must be defined in the superclass.
The super construct can only access public members defined in the superclass. All public members inherited by the superclass can be accessed, as well as any members defined or overridden in the superclass. @h2 Anonymous functions @grammar anonymous-function :: "def" "(" arg-list ")" br block "end" @end
This expression constructs a @ref{Function} object. The argument list and the body of the function are identical to ordinary function definitions (see section @href{Function definitions}). Anonymous functions may refer to local variables defined in the enclosing function or functions. An anonymous function object may outlive the invocation of the enclosing function(s). In that case the local variables defined in the enclosing function(s) that are referenced in an anonymous function will outlive the function invocation. @h2 Cast expressions @grammar cast-expression :: expression "as" cast-type cast-type :: gvar | "dynamic" @end
The cast expression has two variants with different semantics: