Programming for All: Pascal Edition
Table of contents
Getting Started
To get started with the book, simply click on the links above to access each chapter. You can read the chapters directly on GitHub or clone the repository to your local machine to read offline.
Contributing
If you find any errors or have suggestions for improvement, feel free to open an issue or submit a pull request. Contributions are welcome and encouraged!
License
This book is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. You are free to share and adapt the content for non-commercial purposes, as long as you give appropriate credit and share your adaptations under the same license.
Acknowledgments
Special thanks to all contributors who have helped make this book possible.
Chapter 1: Introduction to Pascal
History and background
Pascal is a procedural programming language that Niklaus Wirth designed in the late 1960s and named after the French mathematician and philosopher Blaise Pascal. His aim was to encourage good programming practice through structured programming and data structuring.
Pascal started out as a teaching language, but its clarity and ease of use soon made it popular in industry too. Many later languages show its influence, including Ada, Modula-2, and early versions of Delphi, itself an influential language.
Getting started with Pascal
To write Pascal programs, you need a Pascal compiler. These are some popular choices:
- Free Pascal (FPC): an open-source compiler that supports several Pascal dialects and runs on many platforms.
- Turbo Pascal: an older compiler from Borland, important in Pascal’s history.
- Lazarus: an open-source integrated development environment (IDE) for Free Pascal.
Installing Free Pascal
The steps depend on your operating system.
Windows
- Download the Free Pascal installer from the official website.
- Run the installer and follow the on-screen instructions.
- When the installer finishes, open the command prompt and type
fpcto check that the installation worked.
macOS
- Download the Free Pascal installer from the official website.
- Open the downloaded
.dmgfile and drag the Free Pascal application to your Applications folder. - Open Terminal and type
fpcto verify the installation.
Linux
- Open your terminal.
- Install Free Pascal with your package manager. On Debian-based systems, for example:
sudo apt-get install fpc - Type
fpcin the terminal to check that it installed correctly.
Writing your first program
Your first program prints “Hello, World!” to the console. Create a file called hello.pas and add this code:
program HelloWorld;
begin
writeln('Hello, World!');
end.
Explanation
program HelloWorld;declares the name of the program.beginandend.mark the beginning and end of the program’s main block.writeln('Hello, World!');calls a built-in procedure that writes the given string to the console.
Compiling and running the program
Using the command line
- Open your command line interface (CLI).
- Go to the directory where you saved
hello.pas. - Compile the program with the Free Pascal Compiler:
fpc hello.pas - If there are no errors, the compiler creates an executable file named
hello. - Run the executable:
./hello # On Windows, use hello.exe
You should see Hello, World! printed on the console.
You’ve now written and run your first Pascal program. The next chapters cover Pascal’s syntax, data types, and control structures in more detail.
Chapter 2: Basic Pascal Syntax
Program structure
A Pascal program usually has these parts:
- Program heading: gives the program its name. It’s optional but recommended.
- Uses clause: lists the libraries or units the program uses. This is optional too.
- Declaration part: declares the program’s variables, constants, types, and procedures or functions.
- Begin-end block: the main block, which holds the executable statements.
Example
program ExampleProgram;
uses crt;
var
a, b, sum: integer;
begin
a := 10;
b := 20;
sum := a + b;
writeln('Sum is: ', sum);
end.
Variables and data types
Declaring variables
You declare variables in the var section, and each one must have a data type.
var
x: integer;
y: real;
z: char;
s: string;
Common data types
- Integer: whole numbers.
- Real: floating-point numbers.
- Char: single characters.
- String: sequences of characters.
- Boolean: True or False values.
Example
program VariableExample;
var
age: integer;
price: real;
initial: char;
name: string;
isStudent: boolean;
begin
age := 25;
price := 19.99;
initial := 'A';
name := 'Alice';
isStudent := true;
writeln('Age: ', age);
writeln('Price: ', price:0:2);
writeln('Initial: ', initial);
writeln('Name: ', name);
writeln('Is Student: ', isStudent);
end.
Constants and operators
Declaring constants
Constants go in the const section. Each one must be given a fixed value.
const
PI = 3.14159;
MAX_SIZE = 100;
Operators
Pascal has operators for arithmetic, relational, and logical operations:
- Arithmetic:
+,-,*,/,div,mod - Relational:
=,<>,<,<=,>,>= - Logical:
and,or,not
Example
program OperatorExample;
var
a, b, c: integer;
x, y: boolean;
begin
a := 10;
b := 20;
{ Arithmetic Operations }
c := a + b;
writeln('a + b = ', c);
c := b - a;
writeln('b - a = ', c);
c := a * b;
writeln('a * b = ', c);
c := b div a;
writeln('b div a = ', c);
c := b mod a;
writeln('b mod a = ', c);
{ Relational Operations }
if a < b then
writeln('a is less than b');
if a <> b then
writeln('a is not equal to b');
{ Logical Operations }
x := true;
y := false;
writeln('x and y = ', x and y);
writeln('x or y = ', x or y);
writeln('not x = ', not x);
end.
Chapter 3: Control Structures
Control structures decide the flow of a program: which statements run, and how many times. Pascal has several kinds, including conditional statements and loops.
Conditional statements
If-then statement
The if-then statement runs a block of code only when a condition is true.
if condition then
statement;
Example
program IfThenExample;
var
num: integer;
begin
num := 10;
if num > 5 then
writeln('num is greater than 5');
end.
If-then-else statement
With if-then-else, a condition decides which of two blocks of code runs.
if condition then
statement1
else
statement2;
Example
program IfThenElseExample;
var
num: integer;
begin
num := 10;
if num > 15 then
writeln('num is greater than 15')
else
writeln('num is not greater than 15');
end.
Case statement
The case statement picks one block of code out of many, based on the value of a variable.
case variable of
value1: statement1;
value2: statement2;
...
else statementN;
end;
Example
program CaseExample;
var
grade: char;
begin
grade := 'B';
case grade of
'A': writeln('Excellent');
'B': writeln('Good');
'C': writeln('Fair');
'D': writeln('Poor');
'F': writeln('Fail');
else
writeln('Invalid grade');
end;
end.
Loops
A loop runs a block of code repeatedly.
For loop
The for loop runs a block of code a fixed number of times.
for variable := start_value to end_value do
statement;
Example
program ForLoopExample;
var
i: integer;
begin
for i := 1 to 10 do
writeln(i);
end.
While loop
A while loop keeps repeating its block of code as long as a condition is true.
while condition do
statement;
Example
program WhileLoopExample;
var
i: integer;
begin
i := 1;
while i <= 10 do
begin
writeln(i);
i := i + 1;
end;
end.
Repeat-until loop
The repeat-until loop runs its block of code until a condition becomes true.
repeat
statement;
until condition;
Example
program RepeatUntilExample;
var
i: integer;
begin
i := 1;
repeat
writeln(i);
i := i + 1;
until i > 10;
end.
Nested loops
You can also nest loops by placing one loop inside another.
Example
program NestedLoopExample;
var
i, j: integer;
begin
for i := 1 to 3 do
begin
for j := 1 to 3 do
begin
writeln('i = ', i, ', j = ', j);
end;
end;
end.
Breaking out of loops
The break statement exits a loop early. The continue statement skips the rest of the current iteration and moves on to the next one.
Example: using break
program BreakExample;
var
i: integer;
begin
for i := 1 to 10 do
begin
if i = 5 then
break;
writeln(i);
end;
end.
Example: using continue
program ContinueExample;
var
i: integer;
begin
for i := 1 to 10 do
begin
if i = 5 then
continue;
writeln(i);
end;
end.
Chapter 4: Procedures and Functions
Procedures and functions are blocks of code that perform specific tasks. Organizing a program into them makes the code easier to read and maintain, and Pascal keeps both simple to declare and use.
Procedures
A procedure is a block of code that performs a task but doesn’t return a value.
Declaring procedures
procedure ProcedureName(parameter_list);
begin
{ procedure body }
end;
Example
program ProcedureExample;
procedure Greet(name: string);
begin
writeln('Hello, ', name);
end;
begin
Greet('Alice');
end.
The Greet procedure takes a string parameter, name, and prints a greeting.
Functions
A function is like a procedure, except that it returns a value.
Declaring functions
function FunctionName(parameter_list): return_type;
begin
{ function body }
FunctionName := return_value;
end;
Example
program FunctionExample;
function Add(a, b: integer): integer;
begin
Add := a + b;
end;
begin
writeln('Sum is: ', Add(5, 10));
end.
The Add function takes two integer parameters, a and b, and returns their sum.
Parameter passing
Pascal gives you more than one way to pass parameters to procedures and functions.
By value
When a parameter is passed by value, the procedure or function receives a copy of the actual parameter. Changing that copy inside the procedure or function has no effect on the actual parameter.
procedure ByValueExample(x: integer);
begin
x := x + 1;
end;
var
a: integer;
begin
a := 5;
ByValueExample(a);
writeln('a = ', a); { Output: a = 5 }
end.
By reference
When a parameter is passed by reference, the procedure or function works on the actual parameter itself, so any change it makes to the parameter changes the actual parameter too.
procedure ByReferenceExample(var x: integer);
begin
x := x + 1;
end;
var
a: integer;
begin
a := 5;
ByReferenceExample(a);
writeln('a = ', a); { Output: a = 6 }
end.
Example: by value and by reference
program ParameterPassingExample;
procedure IncrementByValue(x: integer);
begin
x := x + 1;
end;
procedure IncrementByReference(var x: integer);
begin
x := x + 1;
end;
var
a, b: integer;
begin
a := 5;
b := 5;
IncrementByValue(a);
IncrementByReference(b);
writeln('a (by value) = ', a); { Output: a (by value) = 5 }
writeln('b (by reference) = ', b); { Output: b (by reference) = 6 }
end.
Recursive procedures and functions
Recursion is when a procedure or function calls itself.
Example: factorial calculation
program FactorialExample;
function Factorial(n: integer): integer;
begin
if n = 0 then
Factorial := 1
else
Factorial := n * Factorial(n - 1);
end;
begin
writeln('Factorial of 5 is: ', Factorial(5)); { Output: 120 }
end.
To calculate the factorial of a number, the Factorial function calls itself.
Local and global variables
Local variables
A local variable is declared inside a procedure or function, and only that procedure or function can access it.
program LocalVariableExample;
procedure PrintLocalVariable;
var
localVar: integer;
begin
localVar := 10;
writeln('Local Variable: ', localVar);
end;
begin
PrintLocalVariable;
end.
Global variables
A global variable is declared outside every procedure and function. Any procedure or function in the program can access it.
program GlobalVariableExample;
var
globalVar: integer;
procedure PrintGlobalVariable;
begin
globalVar := 10;
writeln('Global Variable: ', globalVar);
end;
begin
PrintGlobalVariable;
end.
Chapter 5: Data Structures
Data structures organize and manage the data in a program. Pascal’s built-in data structures let you store and work with data efficiently, and this chapter covers three of them: arrays, records, and sets.
Arrays
An array is a collection of elements of the same type, stored in contiguous memory locations. It holds several values in a single variable, and you reach each value through its index.
Declaring arrays
var
arrayName: array[indexRange] of dataType;
Example
program ArrayExample;
var
numbers: array[1..5] of integer;
i: integer;
begin
{ Initializing array elements }
for i := 1 to 5 do
numbers[i] := i * 2;
{ Accessing array elements }
for i := 1 to 5 do
writeln('numbers[', i, '] = ', numbers[i]);
end.
Here numbers is an array of integers with indices from 1 to 5. The program sets each element in one loop and reads them back in another.
Multidimensional arrays
Pascal also supports multidimensional arrays. A common one is the two-dimensional array, which you can picture as a table of rows and columns.
Example
program MultiArrayExample;
var
matrix: array[1..3, 1..3] of integer;
i, j: integer;
begin
{ Initializing matrix elements }
for i := 1 to 3 do
for j := 1 to 3 do
matrix[i, j] := i * j;
{ Accessing matrix elements }
for i := 1 to 3 do
begin
for j := 1 to 3 do
write(matrix[i, j]:4);
writeln;
end;
end.
matrix is a two-dimensional array with 3 rows and 3 columns. Nested loops set its elements and then read them back.
Records
A record is a composite data type that groups data of different types together, which makes records a good fit for complex data structures.
Declaring records
type
RecordName = record
field1: dataType1;
field2: dataType2;
...
end;
Example
program RecordExample;
type
Person = record
name: string;
age: integer;
salary: real;
end;
var
employee: Person;
begin
{ Initializing record fields }
employee.name := 'Alice';
employee.age := 30;
employee.salary := 50000.0;
{ Accessing record fields }
writeln('Name: ', employee.name);
writeln('Age: ', employee.age);
writeln('Salary: ', employee.salary:0:2);
end.
Person is a record type with three fields: name, age, and salary. The employee variable is an instance of Person.
Sets
A set is a collection of elements of the same type, with no duplicates. Sets are handy whenever you need membership tests, union, intersection, or difference.
Declaring sets
var
setName: set of baseType;
Example
program SetExample;
type
Day = (Mon, Tue, Wed, Thu, Fri, Sat, Sun);
var
weekdays, weekend: set of Day;
d: Day;
begin
weekdays := [Mon, Tue, Wed, Thu, Fri];
weekend := [Sat, Sun];
{ Set operations }
if Tue in weekdays then
writeln('Tuesday is a weekday');
writeln('Weekend days: ');
for d in weekend do
write(d, ' ');
writeln;
end.
Day is an enumerated type for the days of the week, and weekdays and weekend are sets of type Day. The program checks whether a day is in a set, then iterates over a set’s elements.
Set operations
Pascal’s set operations include:
- Union:
+ - Intersection:
* - Difference:
- - Symmetric difference:
><
To add or remove a single element, use the procedures Include(s, x) and Exclude(s, x).
Example
program SetOperationsExample;
type
Day = (Mon, Tue, Wed, Thu, Fri, Sat, Sun);
DaySet = set of Day;
procedure PrintSet(const title: string; s: DaySet);
var
d: Day;
begin
write(title, ': ');
for d in s do
write(d, ' ');
writeln;
end;
var
setA, setB, resultSet: DaySet;
begin
setA := [Mon, Wed, Fri];
setB := [Wed, Thu, Fri, Sat];
resultSet := setA + setB; { Union }
PrintSet('Union', resultSet);
resultSet := setA * setB; { Intersection }
PrintSet('Intersection', resultSet);
resultSet := setA - setB; { Difference }
PrintSet('Difference', resultSet);
resultSet := setA >< setB; { Symmetric Difference }
PrintSet('Symmetric Difference', resultSet);
end.
writeln can’t print a whole set, so PrintSet loops over the elements and prints each one.
Chapter 6: Advanced Pascal Programming
This chapter covers some more advanced parts of Pascal: file handling, dynamic memory allocation, and pointers. You’ll use them when your programs need to manage more complex data and interactions.
File handling
Pascal has several built-in procedures and functions for file handling. A file stores data permanently, so you can read it back whenever you need it.
Types of files
Pascal supports different types of files, including:
- Text files, which store readable characters.
- Binary files, which store data in binary format.
Text files
Declaring text files
var
fileName: text;
Writing to a text file
program WriteTextFile;
var
fileName: text;
i: integer;
begin
assign(fileName, 'output.txt');
rewrite(fileName);
for i := 1 to 10 do
writeln(fileName, 'Line number ', i);
close(fileName);
end.
Reading from a text file
program ReadTextFile;
var
fileName: text;
line: string;
begin
assign(fileName, 'output.txt');
reset(fileName);
while not eof(fileName) do
begin
readln(fileName, line);
writeln(line);
end;
close(fileName);
end.
Binary files
Declaring binary files
var
binFile: file of dataType;
Writing to a binary file
program WriteBinaryFile;
type
Student = record
name: string[20];
age: integer;
end;
var
binFile: file of Student;
s: Student;
begin
assign(binFile, 'students.dat');
rewrite(binFile);
s.name := 'Alice';
s.age := 22;
write(binFile, s);
s.name := 'Bob';
s.age := 24;
write(binFile, s);
close(binFile);
end.
Reading from a binary file
program ReadBinaryFile;
type
Student = record
name: string[20];
age: integer;
end;
var
binFile: file of Student;
s: Student;
begin
assign(binFile, 'students.dat');
reset(binFile);
while not eof(binFile) do
begin
read(binFile, s);
writeln('Name: ', s.name, ', Age: ', s.age);
end;
close(binFile);
end.
Dynamic memory allocation
Dynamic memory allocation lets you allocate memory at runtime. That helps when you can’t know at compile time exactly how much memory the program will need.
Using the new and dispose procedures
program DynamicMemory;
var
p: ^integer;
begin
new(p); { Allocate memory }
p^ := 10;
writeln('Value: ', p^);
dispose(p); { Free memory }
end.
Here p is a pointer to an integer. The new procedure allocates memory for an integer, and dispose frees that memory again.
Dynamic arrays
With a dynamic array, you allocate and manage the array at runtime.
program DynamicArray;
var
arr: array of integer;
i, n: integer;
begin
writeln('Enter the size of the array: ');
readln(n);
setlength(arr, n);
for i := 0 to n - 1 do
arr[i] := i * 2;
for i := 0 to n - 1 do
writeln('arr[', i, '] = ', arr[i]);
end.
Pointers
A pointer is a variable that stores the memory address of another variable. Pointers are used for dynamic memory allocation and for accessing variables indirectly.
Declaring pointers
var
p: ^integer;
Using pointers
program PointerExample;
var
p, q: ^integer;
x: integer;
begin
new(p);
p^ := 10;
writeln('Value at p: ', p^); { Output: 10 }
x := 20;
q := @x;
writeln('Value at q: ', q^); { Output: 20 }
dispose(p);
end.
p and q are both pointers to integers. The new procedure allocates memory for an integer, and the @ operator gets the address of a variable.
Pointer arithmetic
Standard Pascal has no pointer arithmetic. Free Pascal adds it: Inc(p) on a typed pointer moves it forward by the size of the type it points to, as p++ does in C.
program PointerIncrement;
var
numbers: array[1..3] of integer = (10, 20, 30);
p: ^integer;
i: integer;
begin
p := @numbers[1];
for i := 1 to 3 do
begin
writeln(p^);
Inc(p); { Move to the next integer, not the next byte }
end;
end.
Most Pascal code doesn’t need this. It’s usually clearer to keep a pointer to a whole array and index into it, as this example does:
program PointerArithmetic;
type
IntArray = array[1..10] of integer;
PIntArray = ^IntArray;
var
arr: PIntArray;
i: integer;
begin
new(arr);
for i := 1 to 10 do
arr^[i] := i * 3;
for i := 1 to 10 do
writeln('arr[', i, '] = ', arr^[i]);
dispose(arr);
end.
arr points to an array allocated with new, and arr^[i] reaches each element through an index, so no pointer arithmetic is needed.
Chapter 7: Object-Oriented Programming in Pascal
Object-oriented programming (OOP) is a paradigm in which applications and programs are designed around “objects”. It helps you organize data and keep code modular. Pascal implements OOP with classes and objects, and this chapter introduces the basic concepts: classes, objects, inheritance, polymorphism, and encapsulation.
Free Pascal’s default mode doesn’t accept the class keyword. To use the examples in this chapter, put {$mode objfpc}{$H+} on the first line of the program file, before program, or compile with fpc -Mobjfpc. The {$H+} part makes string mean a long string with no 255-character limit.
{$mode objfpc}{$H+}
program ClassesExample;
Classes and objects
Defining a class
A class is a blueprint for creating objects. It defines the properties (fields) and methods (procedures and functions) that every object created from it can use.
type
TPerson = class
private
FName: string;
FAge: integer;
public
constructor Create(name: string; age: integer);
procedure Display;
end;
Implementing the class methods
constructor TPerson.Create(name: string; age: integer);
begin
FName := name;
FAge := age;
end;
procedure TPerson.Display;
begin
writeln('Name: ', FName);
writeln('Age: ', FAge);
end;
Creating objects
var
person: TPerson;
begin
person := TPerson.Create('Alice', 30);
person.Display;
person.Free;
end.
Inheritance
Inheritance lets one class take on the properties and methods of another. You can reuse code this way and arrange classes in a hierarchy.
Defining a base class and a derived class
type
TPerson = class
private
FName: string;
FAge: integer;
public
constructor Create(name: string; age: integer);
procedure Display; virtual;
end;
TEmployee = class(TPerson)
private
FSalary: real;
public
constructor Create(name: string; age: integer; salary: real);
procedure Display; override;
end;
Implementing the methods
constructor TPerson.Create(name: string; age: integer);
begin
FName := name;
FAge := age;
end;
procedure TPerson.Display;
begin
writeln('Name: ', FName);
writeln('Age: ', FAge);
end;
constructor TEmployee.Create(name: string; age: integer; salary: real);
begin
inherited Create(name, age);
FSalary := salary;
end;
procedure TEmployee.Display;
begin
inherited Display;
writeln('Salary: ', FSalary:0:2);
end;
Creating objects of derived class
var
employee: TEmployee;
begin
employee := TEmployee.Create('Bob', 25, 55000.0);
employee.Display;
employee.Free;
end.
Polymorphism
With polymorphism, the method that runs depends on the actual type of the object, even when you call it through a reference of the base class type. Virtual methods and method overriding make this work.
Example of polymorphism
var
person, someone: TPerson;
employee: TEmployee;
begin
person := TPerson.Create('Charlie', 40);
employee := TEmployee.Create('Dave', 28, 62000.0);
person.Display; { Calls TPerson's Display }
employee.Display; { Calls TEmployee's Display }
someone := employee; { Assigning derived class object to base class reference }
someone.Display; { Calls TEmployee's Display due to polymorphism }
person.Free;
employee.Free; { Also frees the object that someone refers to }
end.
someone and employee refer to the same object, so it’s freed only once. Assigning employee to person instead would lose the only reference to Charlie’s object, which could then never be freed, and person.Free followed by employee.Free would free Dave’s object twice.
Encapsulation
Encapsulation means hiding an object’s internal state so that all interaction has to go through its methods. This keeps code modular and protects the object’s integrity.
Example of encapsulation
type
TBankAccount = class
private
FBalance: real;
public
constructor Create(initialBalance: real);
procedure Deposit(amount: real);
procedure Withdraw(amount: real);
procedure DisplayBalance;
end;
constructor TBankAccount.Create(initialBalance: real);
begin
FBalance := initialBalance;
end;
procedure TBankAccount.Deposit(amount: real);
begin
if amount > 0 then
FBalance := FBalance + amount;
end;
procedure TBankAccount.Withdraw(amount: real);
begin
if (amount > 0) and (amount <= FBalance) then
FBalance := FBalance - amount;
end;
procedure TBankAccount.DisplayBalance;
begin
writeln('Balance: ', FBalance:0:2);
end;
Using the encapsulated class
var
account: TBankAccount;
begin
account := TBankAccount.Create(1000.0);
account.Deposit(500.0);
account.Withdraw(200.0);
account.DisplayBalance; { Output: Balance: 1300.00 }
account.Free;
end.
Chapter 8: Error Handling and Debugging
Good error handling lets your program deal with unexpected situations gracefully, and debugging helps you find and fix problems in your code. This chapter covers techniques and tools for both in Pascal.
Runtime errors
Runtime errors happen while the program is running. Division by zero, a missing file, and invalid input are common examples, and Pascal gives you ways to handle them.
Using the try...except block
A try...except block handles exceptions (errors) that occur while the program runs.
In Free Pascal, exceptions need two things at the top of the program. {$mode objfpc} (or {$mode delphi}) turns on the try, except, finally, and raise keywords, which the default mode doesn’t have. uses SysUtils brings in the exception classes, such as EDivByZero, and makes runtime errors like a division by zero raise them as exceptions. Every example in this chapter that uses try starts with both.
Example
{$mode objfpc}{$H+}
program TryExceptExample;
uses
SysUtils;
var
x, y, z: integer;
begin
x := 10;
y := 0;
try
z := x div y;
writeln('Result: ', z);
except
on E: EDivByZero do
writeln('Error: Division by zero');
end;
end.
The try...except block catches the division by zero error and displays a custom error message.
Using the try...finally block
A try...finally block makes sure certain code runs whether or not an exception occurs. It’s often used to clean up resources.
Example
{$mode objfpc}{$H+}
program TryFinallyExample;
uses
SysUtils;
var
fileVar: text;
begin
assign(fileVar, 'output.txt');
rewrite(fileVar);
try
writeln(fileVar, 'Hello, World!');
finally
close(fileVar);
end;
end.
The file is closed even if an error occurs while writing to it.
Compile-time errors
The compiler detects compile-time errors before the program runs. They include syntax errors, type mismatches, and undeclared variables. To fix them, read the compiler’s error messages carefully and correct the problems in your code.
Common compile-time errors
- Syntax errors break the rules of the language’s syntax, for example a missing semicolon (
;) or a misspelled keyword. - Type mismatches mix incompatible data types, such as assigning a string value to an integer variable.
- Undeclared variables are variables used without being declared first.
Debugging techniques
Debugging is the process of finding and fixing errors in your code. Pascal gives you several techniques and tools for it.
Using Writeln for debugging
The simplest way to debug a Pascal program is to add writeln statements that print variable values and show the flow of execution.
Example
program DebuggingExample;
var
x, y, z: integer;
begin
x := 10;
y := 5;
writeln('x = ', x);
writeln('y = ', y);
z := x + y;
writeln('z = x + y = ', z);
z := x div y;
writeln('z = x div y = ', z);
end.
Using the debugger
Many Pascal IDEs, Lazarus among them, have a built-in debugger for setting breakpoints, stepping through code, and inspecting variable values.
Setting breakpoints
A breakpoint is a marker you set in your code to pause execution at that point. While the program is paused, you can examine its state and see how it behaves.
- Open your Pascal IDE (for example, Lazarus).
- Open your Pascal source file.
- Click in the left margin next to the line where you want to set a breakpoint.
- Run your program in debug mode.
Stepping through code
Stepping through code runs your program one line at a time, so you can follow the flow of execution and find the exact point where an error occurs.
- Set a breakpoint as described above.
- Run your program in debug mode.
- Use the “Step Over” or “Step Into” commands to run your code line by line.
Inspecting variables
While debugging, you can inspect the values of variables at different points in your program to check that they’re being updated correctly.
- Pause your program at a breakpoint.
- Hover over a variable to see its current value.
- Use the “Watch” window to monitor specific variables.
Handling specific exceptions
The SysUtils unit defines several exception types that your code can handle. Common ones include:
EConvertError, raised when a string-to-number conversion fails.EInOutError, for errors during input/output operations (a file that can’t be found, for example).EOutOfMemory, which means the system has run out of memory.
Example: handling multiple exceptions
{$mode objfpc}{$H+}
program MultipleExceptionsExample;
uses
SysUtils;
var
x: integer;
str: string;
begin
str := 'abc';
try
x := StrToInt(str);
writeln('Converted value: ', x);
except
on E: EConvertError do
writeln('Error: Invalid number format');
on E: EOutOfMemory do
writeln('Error: Out of memory');
else
writeln('An unknown error occurred');
end;
end.
Converting a non-numeric string to an integer raises EConvertError, and the first handler catches it.
Chapter 9: Pascal Standard Libraries
Pascal comes with several standard libraries, also called units, that cover a wide range of tasks, from basic input/output to mathematical computation and string manipulation. Knowing them saves you a lot of work and lets your programs do more. This chapter covers some of the most commonly used ones: crt, SysUtils, and Math.
The CRT unit
The crt unit has functions and procedures for console input and output and for managing the screen.
Including the CRT unit
To use the crt unit, include it in the uses clause of your program.
uses crt;
Clearing the screen
begin
ClrScr;
writeln('Screen cleared!');
end.
Moving the cursor
begin
GotoXY(10, 5);
writeln('Cursor moved to position (10, 5)');
end.
Changing text color
begin
TextColor(Red);
writeln('This text is red.');
TextColor(White);
end.
Pausing the program
begin
writeln('Press any key to continue...');
ReadKey;
end.
The SysUtils unit
The SysUtils unit has system-related utilities for string manipulation, file handling, date and time operations, error handling, and more.
Including the SysUtils unit
Add the SysUtils unit to the uses clause of your program to use it.
uses SysUtils;
String manipulation
Converting strings to numbers
var
str: string;
num: integer;
begin
str := '123';
num := StrToInt(str);
writeln('Converted string to integer: ', num);
end.
Converting numbers to strings
var
num: integer;
str: string;
begin
num := 456;
str := IntToStr(num);
writeln('Converted integer to string: ', str);
end.
File handling
Checking if a file exists
var
fileName: string;
begin
fileName := 'test.txt';
if FileExists(fileName) then
writeln('File exists.')
else
writeln('File does not exist.');
end.
Deleting a file
var
fileName: string;
begin
fileName := 'test.txt';
if DeleteFile(fileName) then
writeln('File deleted.')
else
writeln('Failed to delete file.');
end.
Date and time operations
Getting the current date and time
var
currentTime: TDateTime;
begin
currentTime := Now;
writeln('Current date and time: ', DateTimeToStr(currentTime));
end.
Don’t name the variable now. Pascal ignores case, so a variable called now hides the Now function, and now := Now would copy the uninitialized variable into itself instead of reading the clock.
Formatting date and time
var
currentTime: TDateTime;
begin
currentTime := Now;
writeln('Formatted date: ', FormatDateTime('yyyy-mm-dd', currentTime));
writeln('Formatted time: ', FormatDateTime('hh:nn:ss', currentTime));
end.
The Math unit
The Math unit has mathematical functions and constants.
Including the Math unit
As with the other units, list Math in the uses clause of your program to use it.
uses Math;
Common mathematical functions
Calculating the square root
var
x, result: real;
begin
x := 16.0;
result := Sqrt(x);
writeln('Square root of ', x:0:2, ' is ', result:0:2);
end.
Sqrt is built in: it comes from the System unit, which every program uses automatically, so this example works without uses Math.
Calculating the power of a number
var
base, exponent, result: real;
begin
base := 2.0;
exponent := 3.0;
result := Power(base, exponent);
writeln(base:0:2, ' raised to the power of ', exponent:0:2, ' is ', result:0:2);
end.
Constants
Mathematical constants
begin
writeln('Value of Pi: ', Pi:0:10);
end.
Like Sqrt, Pi is built into the System unit. The Math unit adds constants such as Infinity, NaN, and MaxFloat.
Chapter 10: Practical Applications
This chapter puts the concepts from the earlier chapters to work in three practical programs: a calculator, a simple game, and a basic database application.
Building a calculator
This simple calculator performs the basic arithmetic operations: addition, subtraction, multiplication, and division.
Calculator program
program Calculator;
uses
crt, SysUtils;
var
num1, num2, result: real;
operation: char;
begin
ClrScr;
writeln('Simple Calculator');
writeln('-----------------');
writeln('Enter first number: ');
readln(num1);
writeln('Enter an operation (+, -, *, /): ');
readln(operation);
writeln('Enter second number: ');
readln(num2);
case operation of
'+': result := num1 + num2;
'-': result := num1 - num2;
'*': result := num1 * num2;
'/': if num2 <> 0 then
result := num1 / num2
else
begin
writeln('Error: Division by zero');
exit;
end;
else
writeln('Error: Invalid operation');
exit;
end;
writeln('Result: ', result:0:2);
end.
Explanation
- The program asks the user to enter two numbers and an operation.
- A
casestatement performs whichever arithmetic operation the user chose. - The program displays the result.
Simple game development
In this simple guessing game, the user has to guess a randomly generated number.
Guessing game program
program GuessingGame;
uses
crt, SysUtils;
var
secretNumber, guess: integer;
attempts: integer;
begin
ClrScr;
Randomize;
secretNumber := Random(100) + 1; { Generate a random number between 1 and 100 }
attempts := 0;
writeln('Guess the Number Game');
writeln('---------------------');
repeat
writeln('Enter your guess (1-100): ');
readln(guess);
attempts := attempts + 1;
if guess < secretNumber then
writeln('Too low!')
else if guess > secretNumber then
writeln('Too high!')
else
writeln('Congratulations! You guessed the number in ', attempts, ' attempts.');
until guess = secretNumber;
end.
Explanation
- The program generates a random number between 1 and 100.
- It asks the user for a guess and says whether the guess is too low, too high, or correct.
- The game continues until the user guesses the number.
Database applications
This simple database application stores and manages student records. Each record holds a name and an age.
Student record program
program StudentDatabase;
uses
crt, SysUtils;
type
Student = record
name: string[50];
age: integer;
end;
var
students: array[1..100] of Student;
studentCount: integer;
procedure AddStudent(name: string; age: integer);
begin
if studentCount < 100 then
begin
studentCount := studentCount + 1;
students[studentCount].name := name;
students[studentCount].age := age;
end
else
writeln('Error: Student database is full.');
end;
procedure ListStudents;
var
i: integer;
begin
writeln('Student List:');
writeln('-------------');
for i := 1 to studentCount do
writeln(i, ': ', students[i].name, ', Age: ', students[i].age);
end;
var
choice: char;
name: string;
age: integer;
begin
ClrScr;
studentCount := 0;
repeat
writeln('Student Database');
writeln('----------------');
writeln('1. Add Student');
writeln('2. List Students');
writeln('3. Exit');
writeln('Enter your choice: ');
readln(choice);
case choice of
'1':
begin
writeln('Enter student name: ');
readln(name);
writeln('Enter student age: ');
readln(age);
AddStudent(name, age);
end;
'2': ListStudents;
'3': writeln('Exiting...');
else
writeln('Invalid choice.');
end;
writeln;
until choice = '3';
end.
Explanation
- A record type defines a student, with a name and an age.
- The program keeps an array of student records and a count of how many students there are.
- The
AddStudentprocedure adds a new student to the array. ListStudentslists all the students in the database.- The main program shows a menu where the user can add students, list them, or exit.
Chapter 11: Appendix
This appendix collects reference material for Pascal programming: a list of reserved words, some common compiler directives, and tips and best practices for writing efficient, maintainable Pascal code.
Pascal reserved words
Reserved words are predefined, so you can’t use them as identifiers such as variable or procedure names. Here is a list of them:
and array begin case const
div do downto else end
file for function goto if
in label mod nil not
of or packed procedure program
record repeat set then to
type until var while with
Common compiler directives
Compiler directives are special instructions that change how the compiler behaves.
Syntax
A compiler directive is usually written between {$ and }.
Examples
-
$Iincludes a file.{$I filename.inc} -
$Rturns range checking on or off.{$R+} { Enable range checking } {$R-} { Disable range checking } -
$Qdoes the same for overflow checking.{$Q+} { Enable overflow checking } {$Q-} { Disable overflow checking } -
$Msets the stack and heap sizes.{$M 16384,0,655360} -
$DEFINEand$UNDEFdefine and undefine symbols.{$DEFINE DEBUG} {$UNDEF DEBUG}
Tips and best practices
Use meaningful variable names
Descriptive names for variables, procedures, functions, and other identifiers make your code easier to read and maintain.
var
totalAmount: real;
studentName: string;
Comment your code
Use comments to explain what your code is for, especially where the logic is complex. They help other people (and you) understand it.
{ Calculate the total amount after applying the discount }
totalAmount := price - (price * discount / 100);
Consistent indentation
Indent your code consistently so it’s easier to read. Two or four spaces per level of indentation is typical.
if condition then
begin
statement1;
statement2;
end;
Avoid magic numbers
Instead of hard-coded numbers (magic numbers), use constants with meaningful names.
const
MaxStudents = 50;
var
studentCount: integer;
begin
if studentCount > MaxStudents then
writeln('Error: Exceeds maximum number of students');
end;
Modularize your code
Break your code into smaller, reusable procedures and functions. Modular code like this is easier to test and maintain.
procedure PrintGreeting(name: string);
begin
writeln('Hello, ', name);
end;
begin
PrintGreeting('Alice');
end.
Handle errors gracefully
Use error handling to deal with exceptions and other unexpected situations in your code.
try
result := StrToInt(input);
except
on E: EConvertError do
writeln('Invalid number format');
end;
Optimize for performance
Avoid unnecessary calculations, and choose efficient algorithms and data structures.
for i := 1 to n do
result := result + values[i];
Test your code thoroughly
Test with both normal and edge cases to make sure your code works correctly in every situation.
procedure TestAddition;
begin
Assert(Add(2, 2) = 4);
Assert(Add(-1, 1) = 0);
Assert(Add(0, 0) = 0);
end;