This is the subset of Pascal that we have learned so far in Programming I. I'll add more language elements gradually as we learn them in this class.
For more information, see the full reference guide at the Free Pascal site.
{$mode delphi}
Enables the Delphi dialect of Free Pascal. This has several consequences, some of which are quite important:
integer represents a 32-bit signed integer (-2,147,483,648 ≤ i ≤ 2,147,483,647) rather than a 16-bit signed integer (-32,768 ≤ i ≤ 32,767).
string values are not limited to 255 characters.
I recommend that you put this directive at the top of every program.
{$r+}
Enable range checking. On every array or string access a[i], the runtime checks that the index i is within the bounds of the array or string. If it isn't, the program will terminate with an error.
I recommend that you put this directive at the top of every program.
There are three different syntaxes for comments in a Pascal program:
{ 1. this is a comment, and may span multiple lines } (* 2. this comment can also span multiple lines *) // 3. this is a single-line comment
Comments using syntaxes (1) and (2) may be nested.
Identifiers are the names of variables and constants in a Pascal program. They may contain letters, digits and the underscore character ('_'), and may not begin with a digit.
A boolean
value is either true
or false
.
An integer
is a 16-bit signed value (-32,768 ≤ i ≤ 32,767) by
default.
If you have enabled Delphi mode (which I recommend), an integer
is a 32-bit signed value (-2,147,483,648 ≤ i ≤ 2,147,483,647).
An int64
is a 64-bit signed value (-9,223,372,036,854,775,808 ≤ i ≤
9,223,372,036,854,775,807).
A real
is a floating-point number, i.e. a number such as 2.347868
that can have digits after the decimal point.
real
values have a much larger range than integers:
for example, a real
can contain the number 1050
. However, real
values will not be precisely accurate
when values are very large or small: they have only 15-16 significant
digits (possibly less on some platforms).
A char
is a single-byte (non-Unicode) character. Any ASCII
value can fit in a char
; this includes all the
characters you can type on a standard English-language keyboard.
A string
holds a sequence of characters. Without Delphi mode, a string
is limited to 255 characters. If you enable Delphi mode, a string
may have any length.
String constants are delimited with single quotes, e.g. 'the
red apple'
. To embed a
single quote in a string, type it twice: 'she
didn''t get your letter'
.
A string may contain non-ASCII characters such as 'ř'. Such characters are represented using multiple bytes (char values) in the string. Taken together, these bytes define a single Unicode character. (Precisely, this happens using a character encoding such as UTF-8. Such encodings are beyond the scope of this course.)
The length() function returns the length of a string. s[i] retrieves the i-th character of string s. The first character has index 1.
Strings are mutable: you can update character i by assigning to s[i].
An array
holds an indexed set of values of the same type. Arrays come
in two forms, static and dynamic.
A static array holds a fixed number of elements. You can declare a static array like this:
var a: array[1..100] of integer;
The array indices don't need to begin with 1. They can start at 0 or any other value:
var a: array[0..49] of boolean;
You may specify a set of initial values to be stored in a static array:
var a: array[1..3] of string = ('down', 'the', 'road');
The array indices may even be characters:
var a: array['a'..'z'] of integer;
A dynamic array holds a variable number of elements and can grow or shrink as the program runs. You can declare a dynamic array like this:
var a: array of integer;
Before you can use a dynamic array, you must set its length by
calling setLength
:
setLength(a, 100);
Dynamic arrays are always indexed from 0, so after the call above the
array will hold values with indices from 0 to 99. When growing a
dynamic array, setLength
sets any newly allocated
elements to zero.
You can use the syntax a[i]
to read from or write to
a static or dynamic array:
a[44] := 33; a[44] := a[44] + 1;
Both static and dynamic arrays can be multidimensional.
You can declare a multidimensional static array using either of the following syntaxes:
var a: array[1..100][1..20] of integer; b: array[1..100, 1..20] of integer;
The declarations above are equivalent.
Here's how to declare a multidimensional dynamic array:
var d: array of array of integer;
To set the dimensions of such an array, call setLength
and pass each dimension as a separate parameter:
setLength(d, 100, 20);
You can access elements of a multidimensional array using either of
two syntaxes: a[i][j]
or a[i, j]
.
A function or procedure parameter can have an array type with no bounds:
function sum(a: array of integer): integer;
This looks like a dynamic array type, but in this context this is an open array parameter. You can pass either a static or dynamic array to a function that expects an open array.
Just like dynamic arrays, open arrays are always indexed starting from 0, even if their source array has a different indexing base. For example:
procedure first(a: array of integer); begin writeln('low = ', low(a), ', high = ', high(a), ', first = ', a[0]); end; var a: array[1..5] of integer = (2, 4, 6, 8, 10); begin first(a); ...
This program will print
low = 0, high = 4, first = 2
You may pass a partial array to an open array parameter. For example, in the program above we could call
first(a[3..5]);
This will print
low = 0, high = 2, first = 6
You may define enumerated types, which have a fixed number of constant values:
type day = (monday, tuesday, wednesday, thursday, friday, saturday, sunday); var d: day = wednesday; i: integer;
Enumerated values are stored internally as integers.
Certain types are considered to be ordinal types: these
include boolean
, all integer types, char
,
and enumerated types. Values of ordinal types can be converted
to/from integers. They can be used
with comparison operators such as < and >
as array indices
in case
statements
as for
loop variables
with the in
operator
When you declare a variable of any ordinal type, you can specify a range of values that it may hold:
var i: 10 .. 15; c: 'a' .. 'z';
The runtime will check that values are actually in these ranges only
if you have enabled range checking with the {$r+}
directive.
A record is a compound object that is composed of a set of fields. Each distinct record type defines the names and types of the fields that it contains. For example:
type book = record title: string; author: string; pages: integer; end;
You can access a record's fields using the '.'
operator.
For example:
var b: book; begin b.title := 'war and peace'; b.author := 'leo tolstoy'; b.pages := 1440; … writeln('title = ', b.title);
You can initialize a variable of any record type as you declare it:
var b: book = (title: 'war and peace'; author: 'leo tolstoy'; pages: 1440);
A pointer is an indirect reference to a variable. The type ^T means a pointer to type T. For example:
type pos = record x, y: real; end; var p: ^pos; // a pointer to a pos
You can use the new
function to allocate memory
dynamically. new
takes an argument of any pointer type:
new(p); // now p points to a new dynamically allocated pos
The ^ (“circumflex” or “hat”) operator yields the value that a pointer points to:
p^.x := 4; p^.y := 5; writeln(p^.x + p^.y); // writes 9
The dispose
function deletes memory that was allocated
with new
:
dispose(p); // free the card
The special pointer value nil
points to nothing. If you attempt to access ^p where p is nil
,
your program will crash.
If you want to pass a pointer to a function or procedure, or return a pointer from a function, you must declare a named pointer type:
type ppos = ^pos; // pointer to pos function foo(a: ^pos): ^pos; // will not compile! function foo(a: ppos): ppos; // this is fine
The text
type represents a text file:
var novel: text;
You can read or write to a text file using the procedures described
in the quick library reference: assign
, reset
and so on.
Pascal will implicitly (automatically) convert between certain types:
char
→ string
integer
, int64
→ real
integer
→ int64
int64
→ integer
For example:
var c: char; s: string; i: integer; r: real; begin readln(c); readln(i); s := c; // char converted to string r := i; // integer converted to real
not
and
or
These are useful in if
statements: if (x >
3) and ((y < 0) or (y > 10)) then
...
+ |
addition |
- |
subtraction |
* |
multiplication |
/ |
floating-point division |
div |
integer division |
mod |
integer remainder |
+, - and * can operate on integers or reals. If both operands are integers, these operators will return an integer; otherwise they return a real value.
/ can operate on integers or reals; it always returns a real.
div
and mod
can operate only on
integers, and return an integer.
You can also use + to concatenate two strings. (+ will not concatenate a string with a non-string value, unlike in some other languages.)
= |
equal |
<> |
not equal |
< |
less than |
> |
greater than |
<= |
less than or equal |
>= |
greater than or equal |
These operators can compare any values of any primitive type (boolean, integer, real, char, string). Strings are compared alphabetically.
The assignment operator := assigns a new value to a variable:
x := a + b;
The variable itself may appear on the right-hand side:
x := x + 1;
The assignment operators +=
, -=
, *=
and /=
are a convenient shorthand. As an example,
x += 4;
is equivalent to
x := x + 4;
The other operators (-=
, *=
, /=
)
work similarly.
The if
statement executes one or more statements if a
condition is true:
if x > 3 then begin y := x; z := x + 1; end;
If there is only one statement to be executed, you may omit the begin
and end
.
An if
statement may optionally include an else
clause indicating one or more statements to be executed if the
condition is false:
if x > 3 then y := x + 1 else begin z := x + 1; y := x – 1; end;
Warning: do not put a semicolon before the else, or the compiler will complain! This is the one place in Pascal where a statement must not be followed by a semicolon.
A case
statement uses a given value (of any ordinal
type) to choose which of a set of statements to execute:
var c: integer; ... readln(c); case c of 2..10: writeln(c); 11: writeln('jack'); 12: writeln('queen'); 13: writeln('king'); 14: begin writeln('ace'); aces := aces + 1; end else writeln('unknown card'); end;
The else
clause is optional. If it is not present, and
the supplied value does not match any case labels, then the entire
case
statement is skipped.
Note that you must use begin
/end
to wrap
multiple statements in any case group except the else
group, in which begin
/end
are optional.
In the main begin
/end
block, the exit
statement exits the program immediately:
exit;
If there is no enclosing procedure or function, the program exits.
In a function, exit can take a parameter indicating a value to be returned from the function:
exit(4);
A for…to
statement loops over successive values. It
looks like this:
for i := 1 to n do begin writeln(i); writeln(i * 2); end;
You must declare the loop variable (i
in this case) in a
var
section before you can use it in a for
statement. The loop variable may be of type integer
,
char
, or even boolean
.
If there is only one statement in the loop body, you can omit the
begin
and end
.
To loop through decreasing values, use downto
rather
than to
:
for i := 10 downto 1 do writeln(i); writeln('liftoff');
The value of the loop variable is undefined after a for
loop has completed execution.
The for…in
statement loops over successive elements
of an array or string:
var a: array[1..100] of integer; sum: integer = 0; … for v in a do sum := sum + v;
The preceding for
loop is equivalent to
for i := low(a) to high(i) do sum := sum + a[i];
A while
statement loops for as long as a condition is
true. It looks like this:
while i > 0 do begin writeln(i); i := i div 2; end;
If there is only one statement in the loop body, you can omit the
begin
and end
.
If the condition in a while
loop is false at the
beginning, the loop body is skipped: no iterations will run.
The repeat statement loops until a condition becomes true. It looks like this;'
repeat writeln(x); x := x * 2; until x > 100;
Note that the body of a repeat
statement is not enclosed
in begin
and end
.
The body of a repeat statement always executes at least once.
The break
statement breaks out of the nearest
enclosing for, while, or repeat loop.
A type declaration introduces a new name for a type:
type int = integer; array3 = array[1..3] of integer; var i: int; a: array3;
A constant declaration introduces a name for a constant:
const Seconds = 60;
The expression in a constant declaration is evaluated at compile time. It may contain other constants and operators, but no variables:
const Microseconds = Seconds * 1000 * 1000; Three = trunc(pi);
A variable declaration introduces a variable and gives it a type:
var x: integer; r: real;
You may declare several variables of the same type together:
var s, t, u: string;
When declaring a single variable, you may give it an intial value:
var a: integer = 4;
A function declaration introduces a function:
function add(x: integer; y: integer): integer; begin add := x + y; end;
A procedure declaration is similar, but procedures return no value:
procedure exclaim(s: string, n: integer); var i: integer; begin for i := 1 to n do writeln(s); end;
Variables declared inside a function or procedure are known as local variables, and are not visible outside it.
A function or procedure may read and write variables declared outside it, as long as they appear before the declaration of the function or procedure itself.
By default arguments are passed by value, which means that the receiving function gets its own local copy of each argument and cannot modify a variable passed to the function.
If you precede an parameter declaration with var
, the
caller’s variable will be passed by reference, so the caller
can modify it:
procedure increment(var x: integer; by: integer); begin x := x + by; end; … var n: integer = 4; begin increment(n, 2); // now n is 6
A parameter preceded with out
lets a function return
a value by setting a variable that is passed by reference. This is
similar to var
, but does not let the function receive a
value from the caller. For example:
// Return the first and last character of a string function firstAndList(s: string; out first: char; out last: char); begin first := s[1]; last := s[length(s)]; end;
out
parameters are available only if you enable Delphi
mode.
A function cannot modify a parameter that is preceded with const
.
This means that Pascal does not need to make a local copy of the
value that is passed. In particular, this makes passing an array much
more efficient, since internally it can be passed by reference.
For example:
function sum(const a: array of integer): integer; var s: integer = 0; v: integer; begin for v in a do s := s + v; sum := v; end;
A top-level program has this structure:
program add; uses character, crt; type int = integer; const Factor = 20; var x, y: int; function foo(a: integer, b: integer): boolean; ... begin clrScr; readln(x, y); writeln(x + y); end.
The program
element at the very beginning is optional,
as are all declaration sections (uses, type, const, var,
function
). Declarations may appear in any order.
The begin
and end
are required, as is
the period at the end.
Free Pascal lets you divide code into modules using units. A unit is defined in a Pascal source file that looks like this:
unit myUnit; interface type abc = string; procedure honk(s: abc); function add(i, j: integer): integer; implementation procedure honk(s: abc); begin writeln('honk: ', s); end; function add(i, j: integer): integer; begin add := i + j; end; end.
The unit
declaration at the top of the file specifies
the name of the unit. It must be the same as the source file name
without the '.pas' extension.
The interface
section declares types, procedures and
functions that will be exported by the unit. Procedures and functions
declared in this section must be implemented in the following
implementation
section.
A unit ends with the end
keyword followed by a
period.
A program that uses a unit may call only the procedures and
functions declared in the interface
section. Any other
procedures and functions in the implementation
section
are private to the unit.