Subsections
6. Expressions
Expressions occur in assignments or in tests. Expressions produce a value,
of a certain type.
Expressions are built with two components: Operators and their operands.
Usually an operator is binary, i.e. it requires 2 operands. Binary operators
occur always between the operands (as in X/Y). Sometimes an
operator is unary, i.e. it requires only one argument. A unary operator
occurs always before the operand, as in -X.
When using multiple operands in an expression, the precedence rules of
table (OperatorPrecedence) are used.
Table:
Precedence of operators
Operator |
Precedence |
Category |
Not, @ |
Highest (first) |
Unary operators |
* / div mod and shl shr as |
Second |
Multiplying operators |
+ - or xor |
Third |
Adding operators |
< <> < > <= >= in is |
Lowest (Last) |
relational operators |
When determining the precedence, the compiler uses the following rules:
- In operations with unequal precedences the operands belong to the
operater with the highest precedence. For example, in 5*3+7, the
multiplication is higher in precedence than the addition, so it is
executed first. The result would be 22.
- If parentheses are used in an epression, their contents is evaluated
first. Thus, 5*(3+7) would result in 50.
Remark: The order in which expressions of the same precedence are evaluated is not
guaranteed to be left-to-right. In general, no assumptions on which expression
is evaluated first should be made in such a case.
The compiler will decide which expression to evaluate first based on
optimization rules. Thus, in the following expression:
a := g(3) + f(2);
f(2) may be executed before g(3). This behaviour is distinctly
different from or .
If one expression must be executed before the other, it is necessary
to split up the statement using temporary results:
e1 := g(3);
a := e1 + f(2);
An expression applies relational operators to simple expressions. Simple
expressions are a series of terms (what a term is, is explained below), joined by
adding operators.
Expressions
The following are valid expressions:
GraphResult<>grError
(DoItToday=Yes) and (DoItTomorrow=No);
Day in Weekend
And here are some simple expressions:
A + B
-Pi
ToBe or NotToBe
Terms consist of factors, connected by multiplication operators.
Terms
Here are some valid terms:
2 * Pi
A Div B
(DoItToday=Yes) and (DoItTomorrow=No);
Factors are all other constructions:
Factors
Function calls are part of expressions (although, using extended syntax,
they can be statements too). They are constructed as follows:
Function calls
The variable reference must be a procedural type variable reference.
A method designator can only be used inside the method of an object. A
qualified method designator can be used outside object methods too.
The function that will get called is the function with a declared parameter
list that matches the actual parameter list. This means that
- The number of actual parameters must equal the number of declared
parameters.
- The types of the parameters must be compatible. For variable
reference parameters, the parameter types must be exactly the same.
If no matching function is found, then the compiler will generate an error.
Depending on the fact of the function is overloaded (i.e. multiple functions
with the same name, but different parameter lists) the error will be
different.
There are cases when the compiler will not execute the function call in an
expression. This is the case when you are assigning a value to a procedural
type variable, as in the following example:
Type
FuncType = Function: Integer;
Var A : Integer;
Function AddOne : Integer;
begin
A := A+1;
AddOne := A;
end;
Var F : FuncType;
N : Integer;
begin
A := 0;
F := AddOne; { Assign AddOne to F, Don't call AddOne}
N := AddOne; { N := 1 !!}
end.
In the above listing, the assigment to F will not cause the function AddOne
to be called. The assignment to N, however, will call AddOne.
A problem with this syntax is the following construction:
If F = AddOne Then
DoSomethingHorrible;
Should the compiler compare the addresses of F and AddOne,
or should it call both functions, and compare the result ? Free Pascal solves this
by deciding that a procedural variable is equivalent to a pointer. Thus the
compiler will give a type mismatch error, since AddOne is considered a
call to a function with integer result, and F is a pointer, Hence a type
mismatch occurs.
How then, should one compare whether F points to the function
AddOne ? To do this, one should use the address operator @:
If F = @AddOne Then
WriteLn ('Functions are equal');
The left hand side of the boolean expression is an address. The right hand
side also, and so the compiler compares 2 addresses.
How to compare the values that both functions return ? By adding an empty
parameter list:
If F()=Addone then
WriteLn ('Functions return same values ');
Remark that this behaviour is not compatible with Delphi syntax.
When you want to enter a set-type constant in an expression, you must give a
set constructor. In essence this is the same thing as when you define a set
type, only you have no identifier to identify the set with.
A set constructor is a comma separated list of expressions, enclosed in
square brackets.
Set constructors
All set groups and set elements must be of the same ordinal type.
The empty set is denoted by [], and it can be assigned to any type of
set. A set group with a range [A..Z] makes all values in the range a
set element. If the first range specifier has a bigger ordinal value than
the second the set is empty, e.g., [Z..A] denotes an empty set.
The following are valid set constructors:
[today,tomorrow]
[Monday..Friday,Sunday]
[ 2, 3*2, 6*2, 9*2 ]
['A'..'Z','a'..'z','0'..'9']
Sometimes it is necessary to change the type of an expression, or a part of
the expression, to be able to be assignment compatible. This is done through
a value typecast. The syntax diagram for a value typecast is as follows:
Typecasts
Value typecasts cannot be used on the left side of assignments, as variable
typecasts.
Here are some valid typecasts:
Byte('A')
Char(48)
boolean(1)
longint(@Buffer)
The type size of the expression and the size of the type cast must be the
same. That is, the following doesn't work:
Integer('A')
Char(4875)
boolean(100)
Word(@Buffer)
This is different from Delphi or Turbo Pascal behaviour.
The address operator @ returns the address of a variable, procedure
or function. It is used as follows:
Address factor
The @ operator returns a typed pointer if the $T switch is on.
If the $T switch is off then the address operator returns an untyped
pointer, which is assigment compatible with all pointer types. The type of
the pointer is ^T, where T is the type of the variable
reference.
For example, the following will compile
Program tcast;
{$T-} { @ returns untyped pointer }
Type art = Array[1..100] of byte;
Var Buffer : longint;
PLargeBuffer : ^art;
begin
PLargeBuffer := @Buffer;
end.
Changing the {$T-} to {$T+} will prevent the compiler from
compiling this. It will give a type mismatch error.
By default, the address operator returns an untyped pointer.
Applying the address operator to a function, method, or procedure identifier
will give a pointer to the entry point of that function. The result is an
untyped pointer.
By default, you must use the address operator if you want to assign a value
to a procedural type variable. This behaviour can be avoided by using the
-So or -S2 switches, which result in a more compatible Delphi or
Turbo Pascal syntax.
Operators can be classified according to the type of expression they
operate on. We will discuss them type by type.
Arithmetic operators occur in arithmetic operations, i.e. in expressions
that contain integers or reals. There are 2 kinds of operators : Binary and
unary arithmetic operators.
Binary operators are listed in table (binaroperators) , unary operators are
listed in table (unaroperators) .
Table:
Binary arithmetic operators
Operator |
Operation |
+ |
Addition |
- |
Subtraction |
* |
Multiplication |
/ |
Division |
Div |
Integer division |
Mod |
Remainder |
With the exception of Div and Mod, which accept only integer
expressions as operands, all operators accept real and integer expressions as
operands.
For binary operators, the result type will be integer if both operands are
integer type expressions. If one of the operands is a real type expression,
then the result is real.
As an exception : division (/) results always in real values.
Table:
Unary arithmetic operators
Operator |
Operation |
+ |
Sign identity |
- |
Sign inversion |
For unary operators, the result type is always equal to the expression type.
The division (/) and Mod operator will cause run-time errors if
the second argument is zero.
The sign of the result of a Mod operator is the same as the sign of
the left side operand of the Mod operator. In fact, the Mod
operator is equivalent to the following operation :
I mod J = I - (I div J) * J
but it executes faster than the right hand side expression.
Logical operators act on the individual bits of ordinal expressions.
Logical operators require operands that are of an integer type, and produce
an integer type result. The possible logical operators are listed in
table (logicoperations) .
Table:
Logical operators
Operator |
Operation |
not |
Bitwise negation (unary) |
and |
Bitwise and |
or |
Bitwise or |
xor |
Bitwise xor |
shl |
Bitwise shift to the left |
shr |
Bitwise shift to the right |
The following are valid logical expressions:
A shr 1 { same as A div 2, but faster}
Not 1 { equals -2 }
Not 0 { equals -1 }
Not -1 { equals 0 }
B shl 2 { same as B * 2 for integers }
1 or 2 { equals 3 }
3 xor 1 { equals 2 }
Boolean operators can be considered logical operations on a type with 1 bit
size. Therefore the shl and shr operations have little sense.
Boolean operators can only have boolean type operands, and the resulting
type is always boolean. The possible operators are listed in
table (booleanoperators)
Table:
Boolean operators
Operator |
Operation |
not |
logical negation (unary) |
and |
logical and |
or |
logical or |
xor |
logical xor |
Remark: Boolean expressions are ALWAYS evaluated with short-circuit
evaluation. This means that from the moment the result of the complete
expression is known, evaluation is stopped and the result is returned.
For instance, in the following expression:
B := True or MaybeTrue;
The compiler will never look at the value of MaybeTrue, since it is
obvious that the expression will always be true. As a result of this
strategy, if MaybeTrue is a function, it will not get called !
(This can have surprising effects when used in conjunction with properties)
There is only one string operator : +. It's action is to concatenate
the contents of the two strings (or characters) it stands between.
You cannot use + to concatenate null-terminated (PChar) strings.
The following are valid string operations:
'This is ' + 'VERY ' + 'easy !'
Dirname+'\'
The following is not:
Var Dirname = Pchar;
...
Dirname := Dirname+'\';
Because Dirname is a null-terminated string.
The following operations on sets can be performed with operators:
Union, difference and intersection. The operators needed for this are listed
in table (setoperators) .
Table:
Set operators
Operator |
Action |
+ |
Union |
- |
Difference |
* |
Intersection |
The set type of the operands must be the same, or an error will be
generated by the compiler.
The relational operators are listed in table (relationoperators)
Table:
Relational operators
Operator |
Action |
= |
Equal |
<> |
Not equal |
< |
Stricty less than |
> |
Strictly greater than |
<= |
Less than or equal |
>= |
Greater than or equal |
in |
Element of |
Left and right operands must be of the same type. You can only mix integer
and real types in relational expressions.
Comparing strings is done on the basis of their ASCII code representation.
When comparing pointers, the addresses to which they point are compared.
This also is true for PChar type pointers. If you want to compare the
strings the Pchar points to, you must use the StrComp function
from the strings unit.
The in returns True if the left operand (which must have the same
ordinal type as the set type) is an element of the set which is the right
operand, otherwise it returns False
root
2000-12-20