www.digitalmars.com [Home] [Search] [D]

Last update Jan 24, 2002


Expressions

C and C++ programmers will find the D expressions very familiar, with a few interesting additions.

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:
		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
		new NewExpression
		( Type ) UnaryExpression
		( Type ) . Identifier

	PostfixExpression:
		PrimaryExpression
		PostfixExpression . Identifier
		PostfixExpression ++
		PostfixExpression --
		PostfixExpression ( ArgumentList )
		PostfixExpression [ Expression ]

	PrimaryExpression:
		Identifier
		this
		super
		null
		true
		false
		NumericLiteral
		StringLiteral
		AssertExpression
		Type . Identifier

	AssertExpression:
		assert ( Expression )

Evaluation Order

Expressions are generally evaluated left-to-right. This rule, however, is not necessarilly followed with commutative and associative operations. Nor is it followed with function arguments. Order of evaluation with CommaExpressions is respected.

It is an error to depend on order of evaluation in expressions other than comma separated expressions.

Expressions

	AssignExpression , Expression
	
The 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.

Assign Expressions

	ConditionalExpression = AssignExpression
	
The 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.

Assignment Operator Expressions

	ConditionalExpression += AssignExpression
	ConditionalExpression -= AssignExpression
	ConditionalExpression *= AssignExpression
	ConditionalExpression /= AssignExpression
	ConditionalExpression %= AssignExpression
	ConditionalExpression &= AssignExpression
	ConditionalExpression |= AssignExpression
	ConditionalExpression ^= AssignExpression
	ConditionalExpression <<= AssignExpression
	ConditionalExpression >>= AssignExpression
	ConditionalExpression <<<= AssignExpression
	
Assignment operator expressions, such as:
	a op= b
	
are semantically equivalent to:
	a = a op b
	
except that operand a is only evaluated once.

Conditional Expressions

	OrOrExpression ? Expression : ConditionalExpression
	
The 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.

OrOr Expressions

	AndAndExpression || AndAndExpression
	
The 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.

AndAnd Expressions

	OrExpression && OrExpression
	
The 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.

Bitwise Expressions

Bit wise expressions perform a bitwise operation on their operands. Their operands must be integral types. First, the default integral promotions are done. Then, the bitwise operation is done.

Or Expressions

	XorExpression | XorExpression
	
The operands are OR'd together.

Xor Expressions

	AndExpression ^ AndExpression
	
The operands are XOR'd together.

And Expressions

	EqualExpression & EqualExpression
	
The operands are AND'd together.

Equality Expressions

	RelExpression == RelExpression
	RelExpression != RelExpression
	
Equality 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 references means they refer to the same object. Equality for structs 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.im
	
and inequality is defined as equivalent to:
	x.re != y.re || x.im != y.im
	

Relational Expressions

	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
	
First, the integral promotions are done on the operands. The result type of a relational expression is bool.

Integer comparisons

Integer comparisons happen when both operands are integral types.
	Token		Relation
	<		less
	>		greater
	<=		less or equal
	>=		greater or equal
	==		equal
	!=		not equal
	

Floating point comparisons

If one or both operands are floating point, then a floating point comparison is performed.

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:

	Token		Relation
	<		less
	>		greater
	<=		less or equal
	>=		greater or equal
	==		equal
	!=		unordered, less, or greater
	!<>=		unordered
	<>		less or greater
	<>=		less, equal, or greater
	!<=		unordered or greater
	!<		unordered, greater, or equal
	!>=		unordered or less
	!>		unordered, less, or equal
	!<>		unordered or equal
	
Note: for floating point comparison operators, (a !op b) is not the same as !(a op b).

In Expressions

	ShiftExpression in ShiftExpression
	
An 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.

Shift Expressions

	AddExpression << AddExpression
	AddExpression >> AddExpression
	AddExpression <<< AddExpression
	
The 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
	

Add Expressions

	MulExpression + MulExpression
	MulExpression - MulExpression
	
If 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.

For the + operator, if both operands are arrays of a compatible type, the resulting type is an array of that compatible type, and the resulting value is the concatenation of the two arrays.

Mul Expressions

	UnaryExpression * UnaryExpression
	UnaryExpression / UnaryExpression
	UnaryExpression % UnaryExpression
	
The 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.

Unary Expressions

	& UnaryExpression
	++ UnaryExpression
	-- UnaryExpression
	* UnaryExpression
	- UnaryExpression
	+ UnaryExpression
	! UnaryExpression
	~ UnaryExpression
	delete UnaryExpression
	new NewExpression
	( Type ) UnaryExpression
	( Type ) . Identifier
	

Cast Expressions

In C and C++, cast expressions are of the form:
	(type) unaryexpression
	
There 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(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 foo
	
cast 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)
	

Postfix Expressions

	PostfixExpression . Identifier
	PostfixExpression -> Identifier
	PostfixExpression ++
	PostfixExpression --
	PostfixExpression ( ArgumentList )
	PostfixExpression [ Expression ]
	

Primary Expressions

	Identifier
	this
	super
	null
	true
	false
	NumericLiteral
	StringLiteral
	AssertExpression
	Type . Identifier
	

Nulls

The keyword null represents the null pointer value; technically it is of type (void *). It can be implicitly cast to any pointer type. The integer 0 cannot be cast to the null pointer. Nulls are also used for empty arrays.

Assert Expressions

	assert ( Expression )
	
Asserts evaluate the expression. If the result is false, an AssertException 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.
Copyright (c) 1999-2002 by Digital Mars, All Rights Reserved