This is a summary of the subset of Pascal that we are learning and using in Programming I.
This is a work in progress. I'll add more language elements 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 is longint (4 bytes) rather than smallint (2 bytes).
string values are not limited to 255 characters.
Parameters can be preceded by out
.
I recommend that you put this directive at the top of every program.
{$r+}
Enable range checking. This has two consequences:
On every array access a[i], the runtime checks that the index i is within the bounds of the array a. If it isn't, the program will terminate with an error.
On every assignment to a variable whose type is a range
(e.g. var x: 5 .. 15
), the runtime checks that the
value being assigned is in the range. If it isn't, the program will
terminate with an error.
I recommend that you put this directive at the top of every program that uses arrays.
{$optimization tailrec}
Enable tail recursion optimization, which tells the Pascal compiler to transform tail-recursive functions into iterative code that uses a fixed amount of stack space.
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, constants, functions and procedures 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
.
A signed integer can be either positive, zero, or negative.
type |
size (bytes) |
size (bits) |
range |
---|---|---|---|
|
1 |
8 |
-128 … 127 |
|
2 |
16 |
-32,768 .. 32,767 |
|
4 |
32 |
-2,147,483,648 .. 2,147,483,647 |
|
8 |
64 |
-9,223,372,036,854,775,808 .. 9,223,372,036,854,775,807 |
* - integer is longint assuming you have remembered to enable Delphi mode.
In most programs you can simply use integer
for all
integer variables.
An unsigned integer can be zero or positive, but never negative.
type |
size (bytes) |
size (bits) |
range |
---|---|---|---|
|
1 |
8 |
0 .. 255 |
|
2 |
16 |
0 .. 65535 |
|
4 |
32 |
0 .. 4294967295 |
|
8 |
64 |
0 .. 18446744073709551615 |
Real types hold floating-point values. These types have a much larger range than integer types, but will not be precisely accurate when values are very large or small. (The name “real” is a slight misnomer since these values can never be irrational.)
type |
size (bytes) |
significant digits |
range |
---|---|---|---|
single |
4 |
7-8 |
1.5E-45 ... 3.4E38 |
real*, double |
8 |
15-16 |
5.0E-324 .. 1.7E308 |
* - real may be equal to single on some platforms.
In most programs you can simply use real
for all
non-integer numbers.
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 single-byte (non-Unicode) characters. It may be any length (unless you forgot to enable Delphi mode, in which case it will be limited to 255 characters).
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'
.
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 b: 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');
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. You can convert an enumerated value to or from an integer using a type cast expression:
i := integer(d); writeln(i); d := day(i);
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 variant record is a record whose field set can vary depending on the value of a tag field in the record. For example:
type shapeType = (square, rectangle, circle); shape = record centerX, centerY: real; case kind: shapeType of square: (side: real); rectangle: (length, height: real); circle: (radius: real); end;
In the example above, kind
is the tag field, and has
type shapeType
which is an enumerated type. A tag field
may alternatively have any ordinal type, such as integer or boolean.
To use a variant record, simply set its tag field and associated field values:
var s: shape; begin s.kind := rectangle; s.length := 5.0; s.height := 3.0; ...
Code that processes a variant record will often want to use a case
statement to branch based on its tag field:
case s.kind of square: area := s.side * s.side; rectangle: area := s.length * s.height; circle: area := pi * s.radius * s.radius; end;
A pointer is an indirect reference to a variable. The type ^T means a pointer to type T. For example:
var p: ^integer; // a pointer to an integer
The @ (“address of”) operator yields a pointer to a variable:
var i: integer; begin i := 7; p := @i; // now p points to i …
The ^ (“circumflex” or “hat”) operator yields the value that a pointer points to:
writeln(p^); // writes 7 p^ := 8; writeln(i); // writes 8
You can use the new
function to allocate memory
dynamically. new takes an argument of any pointer type. For example:
type suit = (clubs, hearts, diamonds, spades); card = record rank: 2 .. 14; suit: suit; end; var p: ^card; begin new(p); // now p points to a new dynamically allocated card p^.rank := random(13) + 2; p^.suit := suit(random(4)); …
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 pint = ^integer; // pointer to integer function foo(a: ^integer): ^integer; // will not compile! function foo(a: pint): pint; // this is fine
A function pointer is a value that refers to a function.
You can declare a function pointer type like this:
type intFun = function(a, b: integer): integer
Here is a function whose signature matches this function pointer type:
function add(a, b: integer): integer; begin add := a + b; end;
Use the @ (“address-of”) operator to get a pointer to a named function in your program:
var p: intFun; begin p := @add;
You can call a function through a function pointer:
writeln(p(3, 4)); // writes 7
The text
type represents a text file:
var novel: text;
The file
type represents a file on disk containing a
particular type of binary data:
var f: file of integer; g: file of real;
Often a file will hold a compound type:
type time = record year, month, day: integer; hour, minute, second: integer; end; var timestamps: file of time;
Note, however, that a file
cannot contain data of any of
these types:
string
dynamic arrays
any compound data type containing the above types
If you want to store character data in a binary file, you must use
a fixed-size character array, e.g. an array[1..100] of char
.
For information about the various library functions that operate
on files (assign
, reset
and so on), see the
Run-Time
Library Quick Reference.
not
and
or
These are useful in if
statements: if (x >
3) and ((y < 0) or (y > 10)) then
...
+ |
identity |
- |
negation |
+ |
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 ordinal type, and also strings. Strings are compared alphabetically.
It is not possible to compare values of compound types, such as arrays and records.
The in
opererator tests whether a value is in a
range:
if c in ['A'..'Z'] then ...
It may be used with values of any ordinal type.
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 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.
A goto statement branches to an arbitrary label in the current block of code. You must declare a label before you can use it:
var x: integer; label loop; begin x := 1; loop: writeln(x); x := x + 1; if x <= 5 then goto loop; end.
The exit
statement immediately exits the nearest
enclosing procedure or function:
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 of
any ordinal type. 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.
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 values
of an array:
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.
The continue
statement continues with the next
iteration of the nearest enclosing for, while, or
repeat loop.
If continue
is executed during the last iteration of
a for
loop, the loop exits. When continue
is executed in a while
or repeat
loop, the
loop condition is tested before beginning a new iteration; if it is
false (for while
) or true (for repeat
) then
the loop exits.
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, operators, and calls to
functions in the system
unit, 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 yell(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. There are several keywords you can use to change Pascal’s argument-passing behavior.
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 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.