Expressions are used to compute values with a resulting type. These values can then be assigned, tested, or ignored. Expressions can also have side effects.
Expression: AssignExpression AssignExpression , Expression AssignExpression: ConditionalExpression ConditionalExpression = AssignExpression ConditionalExpression += AssignExpression ConditionalExpression -= AssignExpression ConditionalExpression *= AssignExpression ConditionalExpression /= AssignExpression ConditionalExpression %= AssignExpression ConditionalExpression &= AssignExpression ConditionalExpression |= AssignExpression ConditionalExpression ^= AssignExpression ConditionalExpression ~= AssignExpression ConditionalExpression <<= AssignExpression ConditionalExpression >>= AssignExpression ConditionalExpression >>>= AssignExpression ConditionalExpression: OrOrExpression OrOrExpression ? Expression : ConditionalExpression OrOrExpression: AndAndExpression AndAndExpression || AndAndExpression AndAndExpression: OrExpression OrExpression && OrExpression OrExpression: XorExpression XorExpression | XorExpression XorExpression: AndExpression AndExpression ^ AndExpression AndExpression: EqualExpression EqualExpression & EqualExpression EqualExpression: RelExpression RelExpression == RelExpression RelExpression != RelExpression RelExpression is RelExpression RelExpression: ShiftExpression ShiftExpression < ShiftExpression ShiftExpression <= ShiftExpression ShiftExpression > ShiftExpression ShiftExpression >= ShiftExpression ShiftExpression !<>= ShiftExpression ShiftExpression !<> ShiftExpression ShiftExpression <> ShiftExpression ShiftExpression <>= ShiftExpression ShiftExpression !> ShiftExpression ShiftExpression !>= ShiftExpression ShiftExpression !< ShiftExpression ShiftExpression !<= ShiftExpression ShiftExpression in ShiftExpression ShiftExpression: AddExpression AddExpression << AddExpression AddExpression >> AddExpression AddExpression >>> AddExpression AddExpression: MulExpression MulExpression + MulExpression MulExpression - MulExpression MulExpression ~ MulExpression MulExpression: UnaryExpression UnaryExpression * UnaryExpression UnaryExpression / UnaryExpression UnaryExpression % UnaryExpression UnaryExpression: PostfixExpression & UnaryExpression ++ UnaryExpression -- UnaryExpression * UnaryExpression - UnaryExpression + UnaryExpression ! UnaryExpression ~ UnaryExpression delete UnaryExpression NewExpression ( Type ) UnaryExpression ( Type ) . Identifier ( Expression ) PostfixExpression: PrimaryExpression PostfixExpression . Identifier PostfixExpression ++ PostfixExpression -- PostfixExpression ( ArgumentList ) PostfixExpression [ Expression ] PrimaryExpression: Identifier .Identifier this super null true false NumericLiteral CharacterLiteral StringLiteral FunctionLiteral AssertExpression Type . Identifier AssertExpression: assert ( Expression ) ArgumentList: AssignExpression AssignExpression , ArgumentList NewExpression: new BasicType Stars [ AssignExpression ] Declarator new BasicType Stars ( ArgumentList ) new BasicType Stars new ( ArgumentList ) BasicType Stars [ AssignExpression ] Declarator new ( ArgumentList ) BasicType Stars ( ArgumentList ) new ( ArgumentList ) BasicType Stars Stars nothing * * Stars
i = ++i; c = a + (a = b); func(++i, ++i);If the compiler can determine that the result of an expression is illegally dependent on the order of evaluation, it can issue an error (but is not required to). The ability to detect these kinds of errors is a quality of implementation issue.
AssignExpression , ExpressionThe left operand of the , is evaluated, then the right operand is evaluated. The type of the expression is the type of the right operand, and the result is the result of the right operand.
ConditionalExpression = AssignExpressionThe right operand is implicitly converted to the type of the left operand, and assigned to it. The result type is the type of the lvalue, and the result value is the value of the lvalue after the assignment.
The left operand must be an lvalue.
ConditionalExpression += AssignExpression ConditionalExpression -= AssignExpression ConditionalExpression *= AssignExpression ConditionalExpression /= AssignExpression ConditionalExpression %= AssignExpression ConditionalExpression &= AssignExpression ConditionalExpression |= AssignExpression ConditionalExpression ^= AssignExpression ConditionalExpression <<= AssignExpression ConditionalExpression >>= AssignExpression ConditionalExpression >>>= AssignExpressionAssignment operator expressions, such as:
a op= bare semantically equivalent to:
a = a op bexcept that operand a is only evaluated once.
OrOrExpression ? Expression : ConditionalExpressionThe first expression is converted to bool, and is evaluated. If it is true, then the second expression is evaluated, and its result is the result of the conditional expression. If it is false, then the third expression is evaluated, and its result is the result of the conditional expression. If either the second or third expressions are of type void, then the resulting type is void. Otherwise, the second and third expressions are implicitly converted to a common type which becomes the result type of the conditional expression.
AndAndExpression || AndAndExpressionThe result type of an OrOr expression is bool, unless the right operand has type void, when the result is type void.
The OrOr expression evaluates its left operand. If the left operand, converted to type bool, evaluates to true, then the right operand is not evaluated. If the result type of the OrOr expression is bool then the result of the expression is true. If the left operand is false, then the right operand is evaluated. If the result type of the OrOr expression is bool then the result of the expression is the right operand converted to type bool.
OrExpression && OrExpressionThe result type of an AndAnd expression is bool, unless the right operand has type void, when the result is type void.
The AndAnd expression evaluates its left operand. If the left operand, converted to type bool, evaluates to false, then the right operand is not evaluated. If the result type of the AndAnd expression is bool then the result of the expression is false. If the left operand is true, then the right operand is evaluated. If the result type of the AndAnd expression is bool then the result of the expression is the right operand converted to type bool.
XorExpression | XorExpressionThe operands are OR'd together.
AndExpression ^ AndExpressionThe operands are XOR'd together.
EqualExpression & EqualExpressionThe operands are AND'd together.
RelExpression == RelExpression RelExpression != RelExpressionEquality expressions compare the two operands for equality (==) or inequality (!=). The type of the result is bool. The operands go through the usual conversions to bring them to a common type before comparison.
If they are integral values or pointers, equality is defined as the bit pattern of the type matches exactly. Equality for struct objects means the bit patterns of the objects match exactly (the existence of alignment holes in the objects is accounted for, usually by setting them all to 0 upon initialization). Equality for floating point types is more complicated. -0 and +0 compare as equal. If either or both operands are NAN, then both the == and != comparisons return false. Otherwise, the bit patterns are compared for equality.
For complex numbers, equality is defined as equivalent to:
x.re == y.re && x.im == y.imand inequality is defined as equivalent to:
x.re != y.re || x.im != y.imFor class objects, equality is defined as the result of calling Object.eq(). If one or the other or both objects are null, an exception is raised.
For static and dynamic arrays, equality is defined as the lengths of the arrays matching, and all the elements are equal.
RelExpression is RelExpressionThe is compares for identity. To compare for not identity, use !(e1 is e2). The type of the result is bool. The operands go through the usual conversions to bring them to a common type before comparison.
For operand types other than class objects, static or dynamic arrays, identity is defined as being the same as equality.
For class objects, identity is defined as the object references are for the same object. Null class objects can be compared with is.
For static and dynamic arrays, identity is defined as referring to the same array elements.
The identity operator is cannot be overloaded.
ShiftExpression < ShiftExpression ShiftExpression <= ShiftExpression ShiftExpression > ShiftExpression ShiftExpression >= ShiftExpression ShiftExpression !<>= ShiftExpression ShiftExpression !<> ShiftExpression ShiftExpression <> ShiftExpression ShiftExpression <>= ShiftExpression ShiftExpression !> ShiftExpression ShiftExpression !>= ShiftExpression ShiftExpression !< ShiftExpression ShiftExpression !<= ShiftExpression ShiftExpression in ShiftExpressionFirst, the integral promotions are done on the operands. The result type of a relational expression is bool.
For class objects, the result of Object.cmp() forms the left operand, and 0 forms the right operand. The result of the relational expression (o1 op o2) is:
(o1.cmp(o2) op 0)It is an error to compare objects if one is null.
For static and dynamic arrays, the result of the relational op is the result of the operator applied to the first non-equal element of the array. If two arrays compare equal, but are of different lengths, the shorter array compares as "less" than the longer array.
Operator | Relation |
---|---|
< | less |
> | greater |
<= | less or equal |
>= | greater or equal |
== | equal |
!= | not equal |
It is an error to have one operand be signed and the other unsigned for a <, <=, > or >= expression. Use casts to make both operands signed or both operands unsigned.
Useful floating point operations must take into account NAN values. In particular, a relational operator can have NAN operands. The result of a relational operation on float values is less, greater, equal, or unordered (unordered means either or both of the operands is a NAN). That means there are 14 possible comparison conditions to test for:
Operator | Greater Than | Less Than | Equal | Unordered | Exception | Relation |
---|---|---|---|---|---|---|
== | F | F | T | F | no | equal |
!= | T | T | F | T | no | unordered, less, or greater |
> | T | F | F | F | yes | greater |
>= | T | F | T | F | yes | greater or equal |
< | F | T | F | F | yes | less |
<= | F | T | T | F | yes | less or equal |
!<>= | F | F | F | T | no | unordered |
<> | T | T | F | F | yes | less or greater |
<>= | T | T | T | F | yes | less, equal, or greater |
!<= | T | F | F | T | no | unordered or greater |
!< | T | F | T | T | no | unordered, greater, or equal |
!>= | F | T | F | T | no | unordered or less |
!> | F | T | T | T | no | unordered, less, or equal |
!<> | F | F | T | T | no | unordered or equal |
ShiftExpression in ShiftExpressionAn associative array can be tested to see if an element is in the array:
int foo[char[]]; . if ("hello" in foo) .The in expression has the same precedence as the relational expressions <, <=, etc.
AddExpression << AddExpression AddExpression >> AddExpression AddExpression >>> AddExpressionThe operands must be integral types, and undergo the usual integral promotions. The result type is the type of the left operand after the promotions. The result value is the result of shifting the bits by the right operand's value.
<< is a left shift. >> is a signed right shift. >>> is an unsigned right shift.
It's illegal to shift by more bits than the size of the quantity being shifted:
int c; c << 33; error
MulExpression + MulExpression MulExpression - MulExpressionIf the operands are of integral types, they undergo integral promotions, and then are brought to a common type using the usual arithmetic conversions.
If either operand is a floating point type, the other is implicitly converted to floating point and they are brought to a common type via the usual arithmetic conversions.
If the first operand is a pointer, and the second is an integral type, the resulting type is the type of the first operand, and the resulting value is the pointer plus (or minus) the second operand multiplied by the size of the type pointed to by the first operand.
UnaryExpression * UnaryExpression UnaryExpression / UnaryExpression UnaryExpression % UnaryExpressionThe operands must be arithmetic types. They undergo integral promotions, and then are brought to a common type using the usual arithmetic conversions.
For integral operands, the *, /, and % correspond to multiply, divide, and modulus operations. For multiply, overflows are ignored and simply chopped to fit into the integral type. If the right operand of divide or modulus operators is 0, a DivideByZeroException is thrown.
For floating point operands, the operations correspond to the IEEE 754 floating point equivalents. The modulus operator only works with reals, it is illegal to use it with imaginary or complex operands.
& UnaryExpression ++ UnaryExpression -- UnaryExpression * UnaryExpression - UnaryExpression + UnaryExpression ! UnaryExpression ~ UnaryExpression delete UnaryExpression NewExpression ( Type ) UnaryExpression ( Type ) . Identifier ( Expression )
To allocate multidimensional arrays, the declaration reads in the same order as the prefix array declaration order.
char[][] foo; // dynamic array of strings ... foo = new char[][30]; // allocate 30 arrays of strings
(type) unaryexpressionThere is an ambiguity in the grammar, however. Consider:
(foo) - p;Is this a cast of a dereference of negated p to type foo, or is it p being subtracted from foo? This cannot be resolved without looking up foo in the symbol table to see if it is a type or a variable. But D's design goal is to have the syntax be context free - it needs to be able to parse the syntax without reference to the symbol table. So, in order to distinguish a cast from a parenthesized subexpression, a different syntax is necessary.
C++ does this by introducing:
dynamic_cast<type>(expression)which is ugly and clumsy to type. D introduces the cast keyword:
cast(foo) -p; cast (-p) to type foo (foo) - p; subtract p from foocast has the nice characteristic that it is easy to do a textual search for it, and takes some of the burden off of the relentlessly overloaded () operator.
D differs from C/C++ in another aspect of casts. Any casting of a class reference to a derived class reference is done with a runtime check to make sure it really is a proper downcast. This means that it is equivalent to the behavior of the dynamic_cast operator in C++.
class A { ... } class B : A { ... } void test(A a, B b) { B bx = a; error, need cast B bx = cast(B) a; bx is null if a is not a B A ax = b; no cast needed A ax = cast(A) b; no runtime check needed for upcast }D does not have a Java style instanceof operator, because the cast operator performs the same function:
Java: if (a instanceof B) D: if ((B) a)
PostfixExpression . Identifier PostfixExpression -> Identifier PostfixExpression ++ PostfixExpression -- PostfixExpression ( ArgumentList ) PostfixExpression [ Expression ]
Identifier .Identifier this super null true false NumericLiteral CharacterLiteral StringLiteral FunctionLiteral AssertExpression Type . Identifier
FunctionLiteral function ( ParameterList ) FunctionBody function Type ( ParameterList ) FunctionBody delegate ( ParameterList ) FunctionBody delegate Type ( ParameterList ) FunctionBodyFunctionLiterals enable embedding anonymous functions directly into expressions. For example:
int function(char c) fp; void test() { static int foo(char c) { return 6; } fp = foo; }is exactly equivalent to:
int function(char c) fp; void test() { fp = function int(char c) { return 6;}; }And:
int abc(int delegate(long i)); void test() { int b = 3; int foo(long c) { return 6 + b; } abc(foo); }is exactly equivalent to:
int abc(int delegate(long i)); void test() { int b = 3; abc(delegate int(long c) { return 6 + b; }); }If the Type is omitted, it is treated as void. When comparing with nested functions, the function form is analogous to static or non-nested functions, and the delegate form is analogous to non-static nested functions.
AssertExpression: assert ( Expression )Asserts evaluate the expression. If the result is false, an AssertError is thrown. If the result is true, then no exception is thrown. It is an error if the expression contains any side effects that the program depends on. The compiler may optionally not evaluate assert expressions at all. The result type of an assert expression is void. Asserts are a fundamental part of the Design by Contract support in D.