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:
  1. 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.
  2. 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);

6.1 Expression syntax

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

\begin{syntdiag}\setlength{\sdmidskip}{.5em}\sffamily\sloppy \synt{expression}
\...
... + \\
\verb+ is +
\)\synt{simple\ expression}
\end{displaymath}\end{syntdiag}

\begin{syntdiag}\setlength{\sdmidskip}{.5em}\sffamily\sloppy \synt{simple\ expre...
...\\
\(
\lit* + \\
\lit* - \\
\lit*{or} \\
\lit*{xor}
\)\>\end{syntdiag}
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

\begin{syntdiag}\setlength{\sdmidskip}{.5em}\sffamily\sloppy \synt{term}
\<[b] \...
...} \\
\lit*{and} \\
\lit*{shl} \\
\lit*{shr} \\
\lit*{as}
\)\>\end{syntdiag}
Here are some valid terms:
2 * Pi
A Div B
(DoItToday=Yes) and (DoItTomorrow=No);
Factors are all other constructions:

Factors

\begin{syntdiag}\setlength{\sdmidskip}{.5em}\sffamily\sloppy \synt{factor}
\( \l...
...onstructor}\\
\synt{value\ typecast}\\
\synt{address\ factor}
\)\end{syntdiag}

\begin{syntdiag}\setlength{\sdmidskip}{.5em}\sffamily\sloppy \synt{unsigned\ con...
...acter\ string} \\
\synt{constant\ identifier} \\
\lit*{Nil}
\)\end{syntdiag}

6.2 Function calls

Function calls are part of expressions (although, using extended syntax, they can be statements too). They are constructed as follows:

Function calls

\begin{syntdiag}\setlength{\sdmidskip}{.5em}\sffamily\sloppy \synt{function\ cal...
...egin{displaymath}
\synt{actual\ parameter\ list}
\end{displaymath}\end{syntdiag}

\begin{syntdiag}\setlength{\sdmidskip}{.5em}\sffamily\sloppy \synt{actual\ param...
...\<[b]
\synt{expression} \\
\lit* ,
\>
\end{displaymath}\lit* )\end{syntdiag}
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
  1. The number of actual parameters must equal the number of declared parameters.
  2. 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.

6.3 Set constructors

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

\begin{syntdiag}\setlength{\sdmidskip}{.5em}\sffamily\sloppy \synt{set\ construc...
...h}
\<[b] \synt{set\ group} \\ \lit* , \>
\end{displaymath}\lit* ]\end{syntdiag}

\begin{syntdiag}\setlength{\sdmidskip}{.5em}\sffamily\sloppy \synt{set\ group} \...
...\begin{displaymath}
\lit* {..} \synt{expression}
\end{displaymath}\end{syntdiag}
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']

6.4 Value typecasts

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

\begin{syntdiag}\setlength{\sdmidskip}{.5em}\sffamily\sloppy \synt{value\ typecast}
\synt{type\ identifier}
\lit* (
\synt{expression}
\lit* )\end{syntdiag}
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.

6.5 The @ operator

The address operator @ returns the address of a variable, procedure or function. It is used as follows:

Address factor

\begin{syntdiag}\setlength{\sdmidskip}{.5em}\sffamily\sloppy \synt{address facto...
...t{function\ identifier}\\
\synt{qualified\ method\ identifier}
\)\end{syntdiag}
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.

6.6 Operators

Operators can be classified according to the type of expression they operate on. We will discuss them type by type.

6.6.1 Arithmetic operators

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.

6.6.2 Logical operators

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 }

6.6.3 Boolean operators

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)

6.6.4 String operators

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.

6.6.5 Set operators

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.

6.6.6 Relational operators

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