Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Programming for All: Pascal Edition

Table of contents

  1. Introduction to Pascal

  2. Basic Pascal Syntax

  3. Control Structures

  4. Procedures and Functions

  5. Data Structures

  6. Advanced Pascal Programming

  7. Object-Oriented Programming in Pascal

  8. Error Handling and Debugging

  9. Pascal Standard Libraries

  10. Practical Applications

  11. Appendix

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

  1. Download the Free Pascal installer from the official website.
  2. Run the installer and follow the on-screen instructions.
  3. When the installer finishes, open the command prompt and type fpc to check that the installation worked.

macOS

  1. Download the Free Pascal installer from the official website.
  2. Open the downloaded .dmg file and drag the Free Pascal application to your Applications folder.
  3. Open Terminal and type fpc to verify the installation.

Linux

  1. Open your terminal.
  2. Install Free Pascal with your package manager. On Debian-based systems, for example:
    sudo apt-get install fpc
    
  3. Type fpc in 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.
  • begin and end. 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

  1. Open your command line interface (CLI).
  2. Go to the directory where you saved hello.pas.
  3. Compile the program with the Free Pascal Compiler:
    fpc hello.pas
    
  4. If there are no errors, the compiler creates an executable file named hello.
  5. 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:

  1. Program heading: gives the program its name. It’s optional but recommended.
  2. Uses clause: lists the libraries or units the program uses. This is optional too.
  3. Declaration part: declares the program’s variables, constants, types, and procedures or functions.
  4. 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

  1. Syntax errors break the rules of the language’s syntax, for example a missing semicolon (;) or a misspelled keyword.
  2. Type mismatches mix incompatible data types, such as assigning a string value to an integer variable.
  3. 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.

  1. Open your Pascal IDE (for example, Lazarus).
  2. Open your Pascal source file.
  3. Click in the left margin next to the line where you want to set a breakpoint.
  4. 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.

  1. Set a breakpoint as described above.
  2. Run your program in debug mode.
  3. 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.

  1. Pause your program at a breakpoint.
  2. Hover over a variable to see its current value.
  3. 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

  1. The program asks the user to enter two numbers and an operation.
  2. A case statement performs whichever arithmetic operation the user chose.
  3. 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

  1. The program generates a random number between 1 and 100.
  2. It asks the user for a guess and says whether the guess is too low, too high, or correct.
  3. 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

  1. A record type defines a student, with a name and an age.
  2. The program keeps an array of student records and a count of how many students there are.
  3. The AddStudent procedure adds a new student to the array.
  4. ListStudents lists all the students in the database.
  5. 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

  • $I includes a file.

    {$I filename.inc}
    
  • $R turns range checking on or off.

    {$R+}  { Enable range checking }
    {$R-}  { Disable range checking }
    
  • $Q does the same for overflow checking.

    {$Q+}  { Enable overflow checking }
    {$Q-}  { Disable overflow checking }
    
  • $M sets the stack and heap sizes.

    {$M 16384,0,655360}
    
  • $DEFINE and $UNDEF define 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;