Pascal

Introduction To Pascal

a

  1. Complete Pascal Programming Learning Roadmap
    1. Foundations
      1. Introduction: Why Pascal Still Matters in 2026
      2. Chapter 1: What Is Pascal, Really?
      3. Chapter 2: A Short but Important History
      4. Chapter 3: Pascal vs. C—What's Actually Different?
      5. Chapter 4: Why Learn Pascal?
      6. Chapter 5: What You Need Before Starting
      7. Chapter 6: Complete Setup Guide for Windows, Mac, and Linux
        1. Windows Setup
        2. macOS Setup
        3. Linux (Ubuntu) Setup
      8. Chapter 7: What Actually Happens When You Compile?
      9. Chapter 8: Using AI the Right Way While Learning Pascal
    2. Basic Syntax, Variables, Constants, and Data Types
      1. 1. Structure of a Pascal Program
        1. program, begin, end, and the period
      2. 2. Comments
        1. { } and (* *) delimiters
      3. 3. Case Insensitivity
        1. Pascal is not case-sensitive
      4. 4. Reserved Words
        1. begin, end, program, var, and more
      5. 5. Constants
        1. const keyword and constant declarations
      6. 6. Variables
        1. Declaration with var, assignment, and initialization
      7. 7. Primitive Data Types
        1. integer, real, char, boolean
      8. 8. Type Declarations
        1. type keyword and user-defined types
      9. 9. Operators
        1. Arithmetic, relational, logical operators
      10. 10. Expressions
        1. Value-producing combinations
      11. 11. Assignment
        1. The := assignment operator
      12. 12. Input and Output
        1. writeln()
        2. write()
        3. readln()
        4. read()
      13. 13. Format Specifiers
        1. :width:decimals for output formatting
    3. Control Structures
      1. 1. if Statement
        1. Simple conditional branching
      2. 2. if-else Statement
        1. Conditional branching with two paths
      3. 3. case Statement
        1. Multi-way branching
      4. 4. for Loop
        1. Counting loop
      5. 5. while Loop
        1. Conditional loop
      6. 6. repeat-until Loop
        1. Post-test loop
      7. 7. break and continue
        1. Loop control statements
    4. Procedures and Functions
      1. 1. Procedure Declaration
        1. Declaring subroutines
      2. 2. Procedure Definition
        1. The procedure body
      3. 3. Function Declaration
        1. Declaring functions with return values
      4. 4. Function Definition
        1. The function body
      5. 5. Parameters
        1. Value parameters and variable parameters
      6. 6. Function Results
        1. Returning values from functions
      7. 7. Recursion
        1. Functions that call themselves
      8. 8. Scope
        1. Local, global, and block scope
    5. Arrays and Strings
      1. 1. Arrays
        1. Fixed-size collections of same type
      2. 2. Multidimensional Arrays
        1. 2D, 3D, and beyond
      3. 3. Strings
        1. The string type and operations
      4. 4. Character Arrays
        1. Working with char arrays
      5. 5. String Library Functions
        1. length, concat, copy, pos, delete, insert
    6. Records, Sets, and Enumerations
      1. 1. Records
        1. Grouping related data
      2. 2. Nested Records
        1. Records within records
      3. 3. Sets
        1. The Pascal set type
      4. 4. Enumerations
        1. Named integer constants
      5. 5. Subranges
        1. Subsets of types
    7. Pointers and Dynamic Memory
      1. 1. Pointers
        1. Variables that store memory addresses
      2. 2. Dynamic Memory Allocation
        1. new and dispose
      3. 3. Linked Lists
        1. Dynamic data structures
    8. File Handling
      1. 1. Text Files
        1. Reading and writing text files
      2. 2. Typed Files
        1. Files with records
      3. 3. Standard Files
        1. input and output
    9. Modular Programming and Units
      1. 1. Program Structure
        1. program heading, declarations, execution
      2. 2. Units
        1. Separate compilation with units
      3. 3. Uses Clause
        1. Importing units
    10. Object-Oriented Programming (Modern Pascal)
      1. 1. Classes and Objects
        1. OOP in Pascal
      2. 2. Constructors and Destructors
        1. create and destroy
      3. 3. Inheritance
        1. Deriving classes
      4. 4. Interfaces
        1. Multiple inheritance alternative
    11. Practical Projects
      1. 1. Calculator
        1. Basic arithmetic calculator
      2. 2. Library Management System
        1. Managing books and members
    12. Final Advice

Complete Pascal Programming Learning Roadmap

Foundations

Introduction: Why Pascal Still Matters in 2026

When you open your laptop right now, you’re interacting with code influenced by Pascal. While not as visible as C or Python, Pascal’s legacy runs deep—it shaped how we teach programming, influenced modern languages like Ada and Modula-2, and still powers applications built with Delphi and Free Pascal. Despite being over five decades old, Pascal remains one of the most influential languages for teaching structured programming and continues to have a dedicated following.

Pascal has a reputation for being “easy to learn.” It’s not—it’s just logical, and this is a very different thing. C and C++ let you get away with dangerous memory tricks and cryptic syntax. Pascal refuses to do that. It forces you to actually structure your code properly, declare variables before using them, and think about data types explicitly. This discipline is precisely why Pascal remains one of the best languages for learning programming fundamentals—and why learning it properly makes you a noticeably stronger programmer in every other language you touch afterward.

The goal of this guide is comprehensive: to take you from complete beginner to confident, job-ready Pascal programmer, on Windows, Mac, or Linux. We provide plain-English explanations first, followed by a full professional-grade roadmap covering every stage from your very first “Hello, World!” to interview preparation and career readiness.

This comprehensive introduction covers everything you need to know before diving into actual coding. We explore what Pascal actually is and why it was created, the complete setup process for every major operating system, a detailed stage-by-stage roadmap of everything you’ll learn, how to use AI effectively without becoming dependent on it, what happens when you compile code through the four-stage pipeline, and common pitfalls and how to avoid them. By the end of this guide, you’ll have a crystal-clear understanding of Pascal and a complete learning path that will take you from absolute beginner to professional developer.

Chapter 1: What Is Pascal, Really?

Before you write a single line of Pascal code, you need to understand what this language actually is and why it was created. Let’s break down the definition slowly and carefully.

Pascal is a compiled, general-purpose programming language designed for teaching structured programming, with a clear syntax, strong typing, and a focus on program correctness.

Let’s unpack every word in that sentence, because each one matters.

Compiled vs. Interpreted Languages

Here’s the fundamental distinction that shapes everything about Pascal: Pascal is compiled, not interpreted. When you write a program in Python or JavaScript, an interpreter reads your code line-by-line and executes it, every single time the program runs. This is convenient for development because you can make changes and see results immediately, but it comes with a performance cost. The interpreter is essentially translating your code on the fly while it’s running, which adds overhead.

When you write Pascal, a separate tool called a compiler reads your human-readable code and translates it into machine code—raw binary instructions your CPU can execute directly. This happens as an extra step before you ever run your program. The payoff is speed: a compiled Pascal program typically runs many times faster than an equivalent interpreted program because the CPU is executing instructions meant exactly for it, with no translation happening on the fly.

Here’s what the process looks like at a high level:

Source Code (.pas) → Compiler → Machine Code (.exe or .out) → CPU Executes

Designed for Teaching Structured Programming

Pascal was specifically created as a teaching language. This means it was designed to enforce good programming habits. The language forces you to structure your code properly, declare variables before using them, and think carefully about data types. This discipline is what makes Pascal such an excellent first language.

Clear Syntax Using Common Words

Pascal uses English-like keywords such as begin, end, if, then, else, while, and do to express concepts. This makes code easier to read and understand than languages that use symbols for blocks. For example, where C uses { ... }, Pascal uses begin ... end. This clarity is one of Pascal’s greatest strengths for beginners.

Strong Typing and Program Correctness

Pascal is a strongly typed language, meaning every variable must be declared with a specific data type, and the compiler enforces type safety. This catches many errors at compile time rather than at runtime, making programs more reliable and easier to debug.

Chapter 2: A Short but Important History

Understanding where Pascal came from helps explain why it works the way it does today. Many of Pascal’s design choices—some brilliant, some frustrating—make perfect sense once you know the history.

The Birth of Pascal

Pascal was created by Niklaus Wirth in 1971 at the Polytechnic of Zurich, named after the 17th-century mathematician Blaise Pascal. Wirth designed Pascal as a simplified version of the ALGOL language for educational purposes. Wirth’s motivation was simple: the existing languages at the time were either too complex for teaching or too permissive. FORTRAN, COBOL, and C allowed programmers to write unstructured, hard-to-maintain code. Assembly was fast but incredibly tedious. Wirth wanted a language that would teach students how to program properly—with structure, discipline, and clarity. The key idea of Pascal was order, managed through a strong concept of data type, requiring declarations and structured program controls. This order is what makes Pascal programs easier to write correctly and harder to write incorrectly.

The Rise of Turbo Pascal

In the 1980s, Borland created Turbo Pascal, which gave programmers a full IDE and a lightning-fast compiler. Turbo Pascal became immensely popular, selling millions of copies and bringing Pascal to the mainstream. By version 7.0, Pascal had become object-oriented and could run in protected mode.

What Happened to Pascal’s Dominance?

Despite its popularity, Pascal took a heavy hit in the 1990s. Microsoft focused on Visual Basic and C. Apple gradually moved its software development from Pascal-based APIs toward C and eventually Objective-C. At the same time, AT&T made UNIX available to universities, which helped C gain widespread adoption among a generation of computer science students. However, Pascal did not die. It still retains a significant following through Delphi and Free Pascal. Many small-scale freeware, shareware, open-source, and commercial programs are still written in Pascal/Delphi today.

Standardization and Modern Versions

Original Pascal (1971) was Wirth’s original design. ISO Standard Pascal (1983) became the official standard. Turbo Pascal (1983-1997) was Borland’s extended version with object-oriented features. Delphi (1995-present) became modern Pascal with visual RAD environment. Free Pascal (1997-present) is an open-source, cross-platform Pascal compiler. Lazarus (2000-present) is an open-source IDE for Free Pascal. Each version adds features without breaking the huge amount of existing Pascal code already running in production systems worldwide.

Chapter 3: Pascal vs. C—What’s Actually Different?

Many beginners wonder whether to learn Pascal or C. Understanding the difference helps you appreciate what Pascal offers and when to use it.

The key differences between Pascal and C can be summarized as follows. Pascal uses a structured, disciplined programming style that enforces good habits, while C is flexible and allows shortcuts and dangerous tricks. Pascal uses English keywords like begin, end, if, and then, while C uses symbols like {, }, &&, and ||. Pascal variables must be declared in a strict order before use, while C variables can be declared anywhere. Pascal has strong typing and is very strict, while C has weak typing and allows many dangerous conversions. Pascal is safer with fewer dangerous operations, while C has many dangerous features that require caution. Pascal has extremely fast compilation, while C can be slow for large projects. Pascal has a gentle learning curve designed for beginners, while C has a steeper learning curve with many pitfalls.

The Psychological Difference

Pascal programs tend to be more explicit and readable than their C equivalents. C programs are more concise, but that conciseness can come at the cost of clarity. Pascal’s syntax uses familiar words (writeln instead of printf) and has fewer “tricky” constructs. Understanding this distinction early prevents a lot of confusion later, because Pascal tutorials constantly reference “structured programming” as if it’s obviously better. It’s worth knowing why that shift happened in the first place.

Chapter 4: Why Learn Pascal?

Beyond academic interest, why should you invest the time to learn Pascal in 2026? The answer lies in what Pascal offers and the careers it enables.

Pascal quietly exists in many places you might not expect. Delphi applications means many Windows desktop applications are built with Delphi (Pascal). Lazarus IDE is an open-source, cross-platform RAD environment. Education means Pascal remains preferred at many universities for teaching programming. Competitive programming means Pascal is an official language of the International Informatics Olympiad. Open-source projects means many freeware, shareware, and open-source projects use Free Pascal. Embedded systems means small-scale commercial and hobbyist projects.

Careers That Lean on Pascal

Delphi Developers build Windows desktop applications and cross-platform software. Educational Programmers teach programming fundamentals and structured programming. Legacy System Maintainers maintain and upgrade existing Pascal/Delphi codebases. Competitive Programmers use Pascal in programming contests and Olympiads.

Benefits Beyond Specific Careers

Pascal provides a gentle introduction to programming—it’s easier to learn than C/C++. It builds structured programming habits that transfer to any language. It’s less dangerous than C with fewer ways to shoot yourself in the foot. It teaches you to think like a programmer before worrying about syntax and machine details. It has faster compilation times than C for similar programs.

According to the Free Pascal wiki, Pascal has “less overhead and fewer ways for a student to get a program into trouble.” This makes it an excellent choice for learning basic procedural programming without distractions, understanding data types without type-bending tricks, mastering control structures before moving to more complex paradigms, and writing correct code by enforcing good habits from day one.

Chapter 5: What You Need Before Starting

Many people worry they lack the background to learn Pascal. Let’s address this directly.

You Do NOT Need a math or computer science degree. Pascal was designed for beginners. You also do not need prior programming experience. This is where you start. You do not need an expensive or powerful computer; Pascal compilers are lightweight.

You DO Need patience with compiler errors. Pascal errors are usually clear, but you still need to read them carefully. You need a willingness to follow rules. Pascal enforces discipline—embrace it. You need comfort navigating files and folders, and a willingness to use a terminal.

Chapter 6: Complete Setup Guide for Windows, Mac, and Linux

Every Pascal setup, regardless of operating system, needs exactly two things: a compiler which is the tool that translates your code into a runnable program, and an editor or IDE where you actually write your code. Below is a complete, step-by-step path for each operating system, ending in the same result: a working “Hello, World!” program.

Windows Setup

Step 1: Downloading and Installing Free Pascal and Lazarus

Go to https://www.lazarus-ide.org/ and download the Windows installer. Run the installer and follow the default installation steps. This will install both Free Pascal and Lazarus.

Step 2: Verifying Installation

Open Lazarus from your Start Menu. You should see the Lazarus IDE with a blank project.

Step 3: Creating Your First Project

Click “Project” → “New Project” → “Program” → “OK”. Replace the code with:

program HelloWorld;

begin
  writeln('Hello, World!');
end.

Step 4: Compiling and Running

Click “Run” → “Run” or press F9. You should see “Hello, World!” printed to the console window. Note: On Windows, the console may close immediately after the program finishes. Run it from a terminal or add readln; before end. to pause.

macOS Setup

Step 1: Installing Free Pascal

Open Terminal. Install Homebrew (if you don’t have it) with:

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

Install Free Pascal with:

brew install fpc

Step 2: Installing VS Code (Optional)

Download VS Code from https://code.visualstudio.com/. Open VS Code and install the “Pascal” extension.

Step 3: Creating Your First Program

Create a project folder with:

mkdir pascal-course
cd pascal-course

Create a new file with:

touch main.pas

Open VS Code with:

code .

Paste this code into main.pas:

program HelloWorld;

begin
  writeln('Hello, World!');
end.

Step 4: Compiling and Running

Open the integrated terminal. Compile with:

fpc main.pas

Run with:

./main

Expected output:

Hello, World!

Linux (Ubuntu) Setup

Step 1: Installing Free Pascal

Open a terminal and run:

sudo apt update
sudo apt install fpc lazarus

Step 2: Verifying Installation

Run:

fpc -h

You should see help information from the Free Pascal compiler.

Step 3: Creating Your First Program

Create a project folder with:

mkdir pascal-course
cd pascal-course

Create a new file with:

touch main.pas

Paste this code:

program HelloWorld;

begin
  writeln('Hello, World!');
end.

Step 4: Compiling and Running

Compile with:

fpc main.pas

Run with:

./main

Expected output:

Hello, World!

Common Problems and Solutions

If you get fpc: command not found, re-run the install command for your specific OS. If you get Unable to find file: hello.pas, check your filename and location. If the program window closes instantly, run from a terminal or add readln; before end..

Chapter 7: What Actually Happens When You Compile?

This is a step almost every tutorial skips, yet understanding it removes a huge amount of confusion later, especially around confusing error messages.

When you run fpc main.pas, four distinct stages happen behind the scenes.

Stage 1: Preprocessing
Pascal compilers handle uses and include directives, pulling in unit code and resources.

Stage 2: Compilation
Your Pascal source code is translated into assembly code. This is where syntax errors are caught—if your code has a mistake in the grammar of the language, the compiler will tell you here.

Stage 3: Assembly
That assembly code is converted into object code: pure binary, machine-readable, but not yet a complete, runnable program. The output is typically a .o file.

Stage 4: Linking
Your object code gets combined with pre-compiled library code (the Pascal runtime) to produce the final executable file. Linking is where “undefined reference” errors happen.

Chapter 8: Using AI the Right Way While Learning Pascal

AI assistants like ChatGPT, Claude, and GitHub Copilot are valuable for Pascal because compiler errors are usually clear but can still trip up beginners. Used correctly, these tools can accelerate your learning significantly.

Good Uses of AI include asking “Explain this Pascal compiler error in plain English.” Often, the compiler tells you what’s wrong but in a technical vocabulary you haven’t learned yet. AI can translate this into plain language. You can ask “Show me the difference between var and const in Pascal.” You can ask “Help me understand why I need to declare variables before begin.” Active recall is one of the best ways to learn.

Habits to Avoid include pasting AI-generated code without understanding it. Avoid trusting AI blindly about Pascal’s type system. Avoid skipping the “why” behind a fix. In Pascal, understanding structure and discipline is the actual skill you’re building; a working patch that you don’t understand teaches you nothing.

Basic Syntax, Variables, Constants, and Data Types

1. Structure of a Pascal Program

program, begin, end, and the period

The structure of a Pascal program follows a specific format that every program must adhere to. This includes the program heading, the declaration section, and the execution section.

The program keyword begins a Pascal program and is followed by the program name. The program heading tells the compiler what the program is called. The begin keyword marks the start of the executable code. The end. with a period marks the end of the program. The period after end is unique to Pascal and distinguishes program termination from a block end. The declaration section comes between the program heading and begin, where variables, constants, types, and subprograms are declared.

Code Example

program HelloWorld;
// This is the program heading - names the program

// Declaration section (empty here)
// Variables, constants, types would go here

begin
  // Execution section - code runs here
  writeln('Hello, World!');
end.  // Period marks end of program

The compiler reads the program heading to identify the program name. It then processes the declaration section, setting up variables and constants. The begin keyword signals the start of executable statements. The program executes each statement in order until it reaches end. with a period, which signals program completion. The period is required to mark the end of the program.

2. Comments

{ } and (* *) delimiters

Comments are text in your code that is ignored by the compiler, used for documentation. Pascal supports two comment styles.

Braces { } are the most common comment delimiters. Everything between { and } is ignored by the compiler. Parentheses with asterisks (* *) are an alternative comment style that can be nested. Comments are used to explain code, document functionality, and temporarily disable code. Best practices include commenting the purpose of the program, complex logic, and non-obvious code.

Code Example

program CommentDemo;

begin
  // Single-line style (Turbo Pascal extension)
  // This is a single-line comment
  
  { This is a comment using braces
    Everything between braces is ignored
    Even if it spans multiple lines }
  
  (* This is a comment using parentheses and asterisks
     It can also span multiple lines
     And it can be nested: (* like this *) *)
  
  writeln('Hello, World!');  { Inline comment }
end.

The compiler ignores everything between comment delimiters. Comments are not executed and do not affect program behavior. They exist only for human readers to understand the code.

3. Case Insensitivity

Pascal is not case-sensitive

Pascal is not case-sensitive, meaning uppercase and lowercase letters are treated as identical. This affects how you write variable names and keywords.

Case insensitivity means that myVariable, MYVARIABLE, and MyVariable all refer to the same identifier. This applies to keywords, variable names, and procedure names. Implications include that you must be consistent with spelling, not case. Best practices include using consistent casing for readability.

Code Example

program CaseInsensitive;

var
  myBird: Integer;
  MyBird: Integer;  // SAME as myBird - ERROR: duplicate identifier

begin
  MYBIRD := 10;  // Same as myBird and MyBird
  writeln(MyBiRd);  // Also same - outputs 10
  
  // Keywords are also case-insensitive
  Begin
    writeln('Inside begin...');
  End.  // 'End' works same as 'end'
end.

The compiler converts all identifiers to a canonical form (usually uppercase) before comparison. This means case differences don’t affect how the compiler interprets code. However, you cannot declare two variables that differ only by case.

4. Reserved Words

begin, end, program, var, and more

Reserved words are keywords that have special meaning in Pascal and cannot be used as identifiers.

Reserved words are part of the Pascal language syntax. They cannot be used as variable names, procedure names, or any other identifier. Keywords include program, begin, end, var, const, type, procedure, function, if, then, else, while, do, for, to, downto, case, of, repeat, until, and, or, not, div, mod, and more.

Code Example

program ReservedWords;

var
  // Valid variable names
  sparrow: Integer;
  eagleCount: Integer;
  
  // Invalid - cannot use reserved words as names
  // begin: Integer;    // ERROR: 'begin' is reserved
  // end: Integer;      // ERROR: 'end' is reserved
  // program: Integer;  // ERROR: 'program' is reserved
  // var: Integer;      // ERROR: 'var' is reserved

begin
  sparrow := 10;
  eagleCount := 5;
  
  writeln('Sparrows: ', sparrow);
  writeln('Eagles: ', eagleCount);
end.

The compiler recognizes reserved words as having special meaning. Attempting to use them as identifiers results in a compilation error. This prevents ambiguity in the language syntax.

5. Constants

const keyword and constant declarations

Constants are values that cannot be changed during program execution. They provide a way to give meaningful names to fixed values.

Constants are declared in the declaration section using the const keyword. They must be assigned a value when declared. Benefits include making code more readable, easier to maintain, and preventing accidental changes. Syntax uses const followed by the constant name, equals sign, and value.

Code Example

program ConstantsDemo;

const
  // Basic constants
  PI = 3.14159;
  MAX_BIRDS = 100;
  BIRD_NAME = 'Sparrow';
  IS_MIGRATORY = True;
  
  // Constants can be used in expressions
  HALF_PI = PI / 2;
  
var
  birdCount: Integer;

begin
  birdCount := 50;
  
  // Using constants in code
  if birdCount <= MAX_BIRDS then
    writeln('Bird count is within limit');
  
  writeln('Constant PI: ', PI:0:5);
  writeln('Bird name: ', BIRD_NAME);
  
  // Trying to change a constant would cause an error
  // PI := 3.14;  // ERROR: Cannot assign to constant
end.

Constants are evaluated at compile time. The compiler replaces constant references with their values during compilation. Constants are stored in read-only memory, preventing accidental modification during program execution.

6. Variables

Declaration with var, assignment, and initialization

Variables are named storage locations in memory that hold data that can change during program execution.

Variables are declared in the declaration section using the var keyword. The var keyword is followed by a list of variable names, a colon, and the data type. Assignment uses the := operator (colon-equals). Initialization can occur at declaration time or using assignment statements. Declaration must occur before begin in Pascal.

Code Example

program VariablesDemo;

var
  // Variable declarations
  sparrowCount: Integer;      // Integer variable
  eagleWeight: Real;          // Real (floating-point) variable
  birdName: String;           // String variable
  isMigratory: Boolean;       // Boolean variable
  
  // Multiple variables of same type
  hawkCount, falconCount: Integer;
  
  // Variables with initialization
  cardinalCount: Integer = 10;

begin
  // Variable assignment (:= operator)
  sparrowCount := 15;
  eagleWeight := 4500.5;
  birdName := 'Eagle';
  isMigratory := True;
  hawkCount := 5;
  falconCount := 3;
  
  // Display variable values
  writeln('Sparrows: ', sparrowCount);
  writeln('Eagle weight: ', eagleWeight:0:1);
  writeln('Bird name: ', birdName);
  writeln('Migratory: ', isMigratory);
  writeln('Total raptors: ', hawkCount + falconCount);
  
  // Modify variable
  sparrowCount := sparrowCount + 10;
  writeln('Updated sparrows: ', sparrowCount);
end.

When variables are declared, memory is allocated for them. Assignment stores a value in that memory location. The := operator is the assignment operator in Pascal, distinct from the equality operator =. Variables must be declared before they can be used, and declarations must appear before begin.

7. Primitive Data Types

integer, real, char, boolean

Primitive data types are the fundamental building blocks for storing data in Pascal. These are the most basic types provided by the language.

integer stores whole numbers without decimal points. Its range depends on the system but is typically -2,147,483,648 to 2,147,483,647. Memory size is 4 bytes on most systems. It is used for counting and indexing.

real stores numbers with decimal points. Its range is approximately -1.7e308 to 1.7e308. Memory size is 8 bytes with precision of about 15 decimal digits. It is used for scientific and precise calculations.

char stores a single character. Its range is 0 to 255 (ASCII). Memory size is 1 byte. Characters are enclosed in single quotes like ‘A’, ‘5’, or ‘$’.

boolean stores either True or False. Memory size is 1 byte. It is used for logical operations and conditions.

Code Example

program DataTypesDemo;

var
  // Integer - whole numbers
  sparrowCount: Integer;
  
  // Real - decimal numbers
  eagleWeight: Real;
  
  // Char - single character
  birdClass: Char;
  
  // Boolean - true/false
  isMigratory: Boolean;

begin
  // Assign values
  sparrowCount := 42;
  eagleWeight := 4500.5;
  birdClass := 'A';
  isMigratory := True;
  
  // Display values
  writeln('Integer value: ', sparrowCount);
  writeln('Real value: ', eagleWeight:0:1);
  writeln('Char value: ', birdClass);
  writeln('Boolean value: ', isMigratory);
  
  // Memory sizes (not standard Pascal, but FPC extension)
  writeln('Size of Integer: ', SizeOf(Integer));
  writeln('Size of Real: ', SizeOf(Real));
  writeln('Size of Char: ', SizeOf(Char));
  writeln('Size of Boolean: ', SizeOf(Boolean));
end.

Each data type reserves a specific amount of memory. Integer values are stored as signed integers. Real values use floating-point representation. Char values store ASCII codes. Boolean values store 0 (False) or 1 (True). The SizeOf function returns the memory size in bytes.

8. Type Declarations

type keyword and user-defined types

User-defined types allow you to create custom data types that make your code more expressive and type-safe.

Type declarations use the type keyword to define new types. Type aliases create new names for existing types. Enumerated types define a set of named values. Subranges define a subset of an existing type. Record types group related data together.

Code Example

program TypeDeclarationsDemo;

type
  // Type alias - creates a new name for an existing type
  BirdCount = Integer;
  BirdWeight = Real;
  
  // Enumerated type - named constants
  BirdColor = (Red, Blue, Green, Yellow, Black, White);
  
  // Subrange type - restricts values to a range
  BirdAge = 0..20;  // Age in years, 0-20
  
  // Enumerated type for months
  Month = (Jan, Feb, Mar, Apr, May, Jun,
           Jul, Aug, Sep, Oct, Nov, Dec);
  
  // Subrange for days in month
  Day = 1..31;

var
  // Using the custom types
  sparrows: BirdCount;
  eagleWeight: BirdWeight;
  cardinalColor: BirdColor;
  eagleAge: BirdAge;
  birthMonth: Month;
  birthDay: Day;

begin
  // Assign values
  sparrows := 100;
  eagleWeight := 4500.5;
  cardinalColor := Red;
  eagleAge := 5;
  birthMonth := Jan;
  birthDay := 15;
  
  // Display values
  writeln('Sparrows: ', sparrows);
  writeln('Eagle weight: ', eagleWeight:0:1);
  writeln('Cardinal color: ', Ord(cardinalColor));  // Ord gives numeric value
  writeln('Eagle age: ', eagleAge, ' years');
  writeln('Birth month: ', Ord(birthMonth) + 1);  // Ord + 1 for display
  writeln('Birth day: ', birthDay);
end.

Type declarations create new types that are checked by the compiler. Enumerated types assign integer values automatically (starting from 0). Subranges check values at runtime and compile time. Type aliases create new names without creating new types.

9. Operators

Arithmetic, relational, logical operators

Operators are symbols that perform specific operations on one or more operands (values or variables). Pascal provides a rich set of operators.

Arithmetic operators include + for addition, - for subtraction, * for multiplication, / for real division, div for integer division, and mod for modulo/remainder. The / operator always returns a real result. div performs integer division (discards remainder). mod gives the remainder.

Relational operators include = for equal to, <> for not equal to, < for less than, > for greater than, <= for less than or equal, and >= for greater than or equal. They return True or False.

Logical operators include and, or, and not. They combine or negate boolean expressions.

Code Example

program OperatorsDemo;

var
  a, b: Integer;
  c, d: Real;
  result: Boolean;

begin
  // ---- ARITHMETIC OPERATORS ----
  a := 10;
  b := 3;
  
  writeln('Arithmetic operators:');
  writeln('a + b = ', a + b);        // 13
  writeln('a - b = ', a - b);        // 7
  writeln('a * b = ', a * b);        // 30
  writeln('a / b = ', a / b:0:2);    // 3.33 (real division)
  writeln('a div b = ', a div b);    // 3 (integer division)
  writeln('a mod b = ', a mod b);    // 1 (remainder)
  writeln;
  
  // ---- RELATIONAL OPERATORS ----
  writeln('Relational operators:');
  writeln('a = b: ', a = b);          // False
  writeln('a <> b: ', a <> b);        // True
  writeln('a > b: ', a > b);          // True
  writeln('a < b: ', a < b);          // False
  writeln('a >= b: ', a >= b);        // True
  writeln('a <= b: ', a <= b);        // False
  writeln;
  
  // ---- LOGICAL OPERATORS ----
  writeln('Logical operators:');
  result := (a > b) and (b > 0);
  writeln('(a > b) and (b > 0): ', result);  // True
  
  result := (a < b) or (b > 0);
  writeln('(a < b) or (b > 0): ', result);   // True
  
  result := not (a < b);
  writeln('not (a < b): ', result);          // True
end.

Operators are evaluated according to precedence rules. Arithmetic operators perform mathematical calculations. Relational operators compare values and produce boolean results. Logical operators combine boolean expressions. The / operator always produces a real result, while div and mod work with integers.

10. Expressions

Value-producing combinations

Expressions are combinations of operators, variables, and literals that evaluate to a single value.

Expressions are formed by combining values or variables with operators to perform a calculation or evaluation that results in a value.Operator precedence determines the order of evaluation (parentheses first, then multiplication/division, then addition/subtraction). Common mistakes include mixing integer and real division, and forgetting parentheses.

Code Example

program ExpressionsDemo;

var
  a, b, c: Integer;
  x, y, z: Real;
  birdCount: Integer;
  averageWeight: Real;

begin
  a := 10;
  b := 5;
  c := 2;
  
  // ---- ARITHMETIC EXPRESSIONS ----
  writeln('Arithmetic expressions:');
  writeln('a + b * c = ', a + b * c);        // 20 (10 + 10)
  writeln('(a + b) * c = ', (a + b) * c);    // 30 (15 * 2)
  writeln('a / b + c = ', a / b + c:0:1);    // 4.0 (2.0 + 2)
  writeln('a div b * c = ', a div b * c);    // 8 (2 * 4)
  writeln;
  
  // ---- COMPLEX EXPRESSIONS ----
  x := 10.5;
  y := 3.2;
  z := 2.0;
  
  writeln('Complex expressions:');
  writeln('x + y * z = ', x + y * z:0:2);    // 16.9
  writeln('(x + y) * z = ', (x + y) * z:0:2); // 27.4
  writeln('x / y + z = ', x / y + z:0:2);     // 5.28
  writeln;
  
  // ---- BOOLEAN EXPRESSIONS ----
  writeln('Boolean expressions:');
  writeln('(a > b) = ', a > b);                // True
  writeln('(a > b) and (b > c) = ', (a > b) and (b > c)); // True
  writeln('(a < b) or (b > c) = ', (a < b) or (b > c));   // True
  writeln('not (a < b) = ', not (a < b));      // True
end.

Expressions are evaluated using operator precedence. Multiplication and division take precedence over addition and subtraction. Parentheses override the default precedence. Boolean expressions use short-circuit evaluation where possible.

11. Assignment

The := assignment operator

Assignment in Pascal uses the := operator to store a value in a variable. This is distinct from the equality operator = used for comparison.

Assignment operator := is used to assign a value to a variableIn an assignment statement, the variable on the left is given the result produced by the expression on the right. The = operator is used for assignment, while == is used to check whether two values are equal. Common mistakes include using = instead of := for assignment and using := for comparison.

Code Example

program AssignmentDemo;

var
  sparrowCount: Integer;
  eagleWeight: Real;
  birdName: String;
  isMigratory: Boolean;

begin
  // ---- CORRECT ASSIGNMENT ----
  sparrowCount := 10;
  eagleWeight := 4500.5;
  birdName := 'Eagle';
  isMigratory := True;
  
  // ---- COMPLEX ASSIGNMENTS ----
  sparrowCount := sparrowCount + 5;  // 15
  eagleWeight := eagleWeight / 2;    // 2250.25
  
  // ---- COMPARISON (using =) ----
  if sparrowCount = 15 then
    writeln('Sparrow count is 15');
  
  if eagleWeight > 1000 then
    writeln('Eagle is heavy');
  
  // ---- COMMON MISTAKES ----
  // sparrowCount = 20;      // ERROR: Using = instead of :=
  // if sparrowCount := 20 then  // ERROR: Using := instead of =
  
  writeln('Sparrows: ', sparrowCount);
  writeln('Eagle weight: ', eagleWeight:0:2);
end.

The := operator evaluates the right side and stores the result in the left side variable. The left side must be a variable that can accept the value (must be assignment-compatible). The = operator performs comparison and returns a boolean value. These two operators are distinct and cannot be used interchangeably.

12. Input and Output

writeln()

Output with newline

writeln() is used to display data to the console with automatic newline insertion.

writeln outputs data and automatically adds a newline after the output. write outputs data without adding a newline. Both can accept multiple arguments separated by commas. They work with strings, numbers, booleans, and other types.

Code Example

program WritelnDemo;

var
  sparrowCount: Integer;
  eagleWeight: Real;
  birdName: String;

begin
  sparrowCount := 10;
  eagleWeight := 4500.5;
  birdName := 'Eagle';
  
  // ---- BASIC OUTPUT ----
  writeln('Welcome to the Bird Sanctuary!');
  
  // ---- MULTIPLE VALUES ----
  writeln('Bird: ', birdName);
  writeln('Count: ', sparrowCount);
  writeln('Weight: ', eagleWeight:0:1, ' grams');
  
  // ---- SEPARATE ARGUMENTS ----
  writeln('Sparrows: ', sparrowCount, '  Eagles: 3');
  
  // ---- DIFFERENCE BETWEEN WRITE AND WRITELN ----
  write('This text is ');
  write('all on one line ');
  writeln('because of write()');
  writeln('This is on a new line because of writeln()');
end.

writeln converts its arguments to strings and sends them to the console. A newline character is automatically appended. Multiple arguments are output in order without separators unless you add spaces in strings.

write()

Output without newline

write() is used to display data to the console without adding a newline after output.

write outputs data without adding a newline. The next output continues on the same line. It accepts the same arguments as writeln. When to use includes building formatted output, prompting for input on the same line, and creating tables.

Code Example

program WriteDemo;

var
  birdName: String;
  birdCount: Integer;

begin
  birdName := 'Sparrow';
  birdCount := 10;
  
  // ---- USING WRITE FOR PROMPTS ----
  write('Enter bird name: ');
  readln(birdName);
  
  write('Enter bird count: ');
  readln(birdCount);
  
  // ---- BUILDING OUTPUT ----
  write('Bird: ');
  write(birdName);
  write('  Count: ');
  writeln(birdCount);
  
  // ---- CREATING COLUMNS ----
  write('Name    ');
  write('Count   ');
  writeln('Weight');
  write('----    ');
  write('-----   ');
  writeln('------');
  
  write('Sparrow ');
  write('10      ');
  writeln('25.5');
  
  write('Eagle   ');
  write('3       ');
  writeln('4500.0');
end.

write sends its arguments to the console without appending a newline. This allows careful control over output formatting. Subsequent write or writeln calls continue on the same line until a newline is explicitly added.

readln()

Input with newline handling

readln() is used to read data from the user, consuming the newline after reading.

readln reads data from standard input and handles the newline. read reads data without consuming the newline. Multiple values can be read with a single readln. Error handling requires checking if the input was valid.

Code Example

program ReadlnDemo;

var
  sparrowCount: Integer;
  eagleWeight: Real;
  birdName: String;
  isMigratory: Boolean;

begin
  // ---- READING SINGLE VALUE ----
  writeln('----- Bird Information -----');
  write('Enter bird species: ');
  readln(birdName);
  
  write('Enter bird count: ');
  readln(sparrowCount);
  
  write('Enter bird weight (grams): ');
  readln(eagleWeight);
  
  // ---- DISPLAY INPUT ----
  writeln;
  writeln('Bird Information:');
  writeln('Species: ', birdName);
  writeln('Count: ', sparrowCount);
  writeln('Weight: ', eagleWeight:0:1, ' grams');
  
  // ---- READING MULTIPLE VALUES ----
  writeln;
  write('Enter bird name, count, and weight (space separated): ');
  readln(birdName, sparrowCount, eagleWeight);
  
  writeln('Name: ', birdName, '  Count: ', sparrowCount, '  Weight: ', eagleWeight:0:1);
end.

readln reads data from the console and consumes the newline character. Multiple variables can be read in one call. The input is automatically converted to the variable’s type. If the input doesn’t match the expected type, a runtime error occurs.

read()

Input without newline handling

read() is used to read data from the user without consuming the newline.

read reads data without consuming the newline. The newline remains in the input buffer. Use cases include when you need to read characters one by one or when you want to skip newlines. Difference from readln is that read leaves the newline in the buffer.

Code Example

program ReadDemo;

var
  ch: Char;
  birdName: String;

begin
  // ---- READING CHARACTERS ----
  writeln('Enter a character: ');
  read(ch);  // Reads character, leaves newline
  writeln('You entered: ', ch);
  
  // ---- READING WITH NEWLINE ISSUES ----
  writeln('Enter your bird name: ');
  read(birdName);  // Reads until whitespace or newline
  writeln('Bird name: ', birdName);
  
  // ---- COMBINING READ AND READLN ----
  writeln('Enter a character: ');
  read(ch);
  writeln('Character: ', ch);
  
  // Skip the newline left in buffer
  readln;  // Consume the newline
  
  writeln('Enter a name: ');
  readln(birdName);
  writeln('Name: ', birdName);
end.

read reads data but doesn’t consume the newline character. This can cause issues when mixing read and readln. The next read may see the leftover newline. Using readln after read can consume the newline.

13. Format Specifiers

:width:decimals for output formatting

Format specifiers control how values are displayed, including field width and decimal places.

Width specifier :width sets the minimum field width for output. Decimal specifier :width:decimals sets both width and decimal places for real numbers. Right alignment is the default, with values padded with spaces on the left. Integer formatting uses :width only.

Code Example

program FormattingDemo;

var
  birdCount: Integer;
  birdWeight: Real;

begin
  birdCount := 42;
  birdWeight := 123.456;
  
  // ---- INTEGER FORMATTING ----
  writeln('--- Integer Formatting ---');
  writeln('Default: ', birdCount);
  writeln('Width 5: ', birdCount:5);
  writeln('Width 10: ', birdCount:10);
  writeln('Width 2: ', birdCount:2);    // Minimum width, no truncation
  
  // ---- REAL FORMATTING ----
  writeln('--- Real Formatting ---');
  writeln('Default: ', birdWeight);
  writeln('Width 10: ', birdWeight:10);
  writeln('2 decimals: ', birdWeight:0:2);
  writeln('Width 10, 2 decimals: ', birdWeight:10:2);
  writeln('Width 10, 4 decimals: ', birdWeight:10:4);
  
  // ---- CREATING TABLES ----
  writeln('--- Table Formatting ---');
  writeln('Bird    Count   Weight');
  writeln('----    -----   ------');
  writeln('Sparrow':8, birdCount:8, birdWeight:8:1);
  writeln('Eagle':8, '3':8, '4500.0':8);
  writeln('Hawk':8, '5':8, '1200.5':8);
  
  // ---- COMBINING FORMATTING ----
  writeln('--- Combined Formatting ---');
  writeln('Bird count: ', birdCount:3, '  Weight: ', birdWeight:6:2);
end.

The width specifier defines the minimum amount of space reserved for displaying a value; if the value does not fill that space, additional spaces are added. The decimal precision specifier determines how many digits are shown after the decimal point. For integers, only the width specifier is used. Values are right-aligned by default.

Control Structures

1. if Statement

Simple conditional branching

The if statement executes a block of code only if a condition is true. It allows your program to make decisions.

if ... then executes a single statement or block when the condition is true. Multiple statements require a begin ... end block. Syntax is if condition then statement. Common mistakes include forgetting begin for multiple statements and incorrect semicolon placement.

Code Example

program IfDemo;

var
  birdCount: Integer;
  eagleWeight: Real;

begin
  birdCount := 15;
  eagleWeight := 4500.5;
  
  // ---- SIMPLE IF ----
  if birdCount > 10 then
    writeln('We have many birds: ', birdCount);
  
  // ---- IF WITH BEGIN...END ----
  if eagleWeight > 1000 then
  begin
    writeln('Eagle is heavy: ', eagleWeight:0:1);
    writeln('This is a large bird');
  end;
  
  // ---- NESTED IF ----
  if birdCount > 0 then
    if birdCount > 5 then
      writeln('We have at least 5 birds');
  
  // ---- COMMON MISTAKES ----
  // Wrong: Semicolon before else
  // if birdCount > 5 then;
  //   writeln('This always executes');
  
  // Wrong: No begin for multiple statements
  // if birdCount > 10 then
  //   writeln('Many birds');
  //   writeln('This always executes');  // Not part of if!
end.

The condition is evaluated. If true, the statement(s) after then execute. If false, they’re skipped. Without begin, only the first statement after then is part of the conditional.

2. if-else Statement

Conditional branching with two paths

The if-else statement provides two execution paths based on a condition.

if ... then ... else executes one block when true, another when false. Nested if-else allows multiple conditions. Else binding binds to the nearest if without an else. No semicolon before else.

Code Example

program IfElseDemo;

var
  birdCount: Integer;
  eagleWeight: Real;

begin
  birdCount := 7;
  eagleWeight := 4500.5;
  
  // ---- BASIC IF-ELSE ----
  if birdCount > 10 then
    writeln('We have many birds')
  else
    writeln('We have few birds');
  
  // ---- MULTIPLE STATEMENTS ----
  if eagleWeight > 1000 then
  begin
    writeln('Eagle is heavy');
    writeln('Weight: ', eagleWeight:0:1);
  end
  else
  begin
    writeln('Eagle is light');
    writeln('Weight: ', eagleWeight:0:1);
  end;
  
  // ---- NESTED IF-ELSE ----
  if birdCount > 20 then
    writeln('Large flock')
  else if birdCount > 10 then
    writeln('Medium flock')
  else if birdCount > 5 then
    writeln('Small flock')
  else
    writeln('Just a few birds');
  
  // ---- ELSE BINDING (common mistake) ----
  // The else binds to the nearest if
  if birdCount > 5 then
    if birdCount > 10 then
      writeln('More than 10')
    else
      writeln('Between 5 and 10');  // This else binds to the inner if
  
  // To fix, use begin...end
  if birdCount > 5 then
  begin
    if birdCount > 10 then
      writeln('More than 10')
  end
  else
    writeln('5 or less');
end.

The condition is evaluated. If true, the then block executes. If false, the else block executes. In nested if-else, the else binds to the nearest unmatched if. Using begin ... end clarifies the structure.

3. case Statement

Multi-way branching

The case statement provides efficient multi-way branching based on a single value.

The case ... of statement evaluates an expression and selects the corresponding branch from several possible choices based on its result. else clause handles values not matched. Allowed types include integers, characters, enumerated types, and subranges. break is not needed in Pascal’s case.

Code Example

program CaseDemo;

var
  birdChoice: Integer;
  birdClass: Char;

begin
  birdChoice := 2;
  birdClass := 'A';
  
  // ---- CASE WITH INTEGER ----
  writeln('--- Integer Case ---');
  case birdChoice of
    1: writeln('You selected: Sparrow');
    2: writeln('You selected: Eagle');
    3: writeln('You selected: Hawk');
    4: writeln('You selected: Cardinal');
    else writeln('Invalid selection');
  end;
  
  // ---- CASE WITH CHARACTER ----
  writeln('--- Character Case ---');
  case birdClass of
    'A': writeln('Class A: Birds of prey');
    'B': writeln('Class B: Songbirds');
    'C': writeln('Class C: Water birds');
    else writeln('Unknown class');
  end;
  
  // ---- MULTIPLE VALUES ----
  writeln('--- Multiple Values ---');
  birdChoice := 2;
  case birdChoice of
    1, 2: writeln('Small bird');
    3, 4: writeln('Medium bird');
    5..8: writeln('Large bird');
    else writeln('Unknown size');
  end;
  
  // ---- CASE WITH RANGES ----
  writeln('--- Ranges in Case ---');
  case birdChoice of
    1..3: writeln('Common garden birds');
    4..6: writeln('Forest birds');
    7..10: writeln('Rare birds');
    else writeln('No information');
  end;
end.

The expression is evaluated once. The compiler jumps to the matching case label. The else clause executes when no match is found. Ranges and multiple values can be specified for a single case.

4. for Loop

Counting loop

The for loop executes a block a specific number of times, using a counter variable.

for ... to ... do counts upward from a starting value to an ending value. for ... downto ... do counts downward. Counter variable must be an integer type. The counter variable should not be changed manually from within the loop body.Nested for loops allow multiple dimensions.

Code Example

program ForLoopDemo;

var
  i, j: Integer;
  total: Integer;

begin
  // ---- BASIC FOR LOOP (TO) ----
  writeln('--- Counting Up ---');
  for i := 1 to 5 do
    writeln('Bird ', i, ' chirping');
  writeln('All birds chirped!');
  writeln;
  
  // ---- FOR LOOP (DOWNTO) ----
  writeln('--- Counting Down ---');
  for i := 5 downto 1 do
    writeln('Countdown: ', i);
  writeln('Take off!');
  writeln;
  
  // ---- NESTED FOR LOOPS ----
  writeln('--- Nested Loops ---');
  for i := 1 to 3 do
  begin
    write('Tree ', i, ': ');
    for j := 1 to 4 do
      write('Bird', j, ' ');
    writeln;
  end;
  writeln;
  
  // ---- USING LOOP VARIABLE ----
  writeln('--- Using Loop Variable ---');
  total := 0;
  for i := 1 to 10 do
    total := total + i;  // Sum 1 to 10
  writeln('Sum of 1..10 = ', total);
  
  // ---- COMMON MISTAKES ----
  // for i := 1 to 5 do
  //   i := i + 1;  // ERROR: Cannot modify loop variable
end.

The loop variable is initialized to the start value. Each iteration executes the loop body, then the variable is incremented (or decremented). The loop continues until the variable exceeds (or goes below) the end value. The loop variable is controlled by the loop structure and should not be changed directly within the loop body.

5. while Loop

Conditional loop

The while loop executes a block repeatedly while a condition remains true.

while ... do checks the condition before each iteration. Loop body executes only if the condition is true. Infinite loops occur when the condition never becomes false. Use while when the number of iterations is unknown before the loop starts.

Code Example

program WhileLoopDemo;

var
  birdCount: Integer;
  totalBirds: Integer;

begin
  birdCount := 1;
  totalBirds := 0;
  
  // ---- BASIC WHILE LOOP ----
  writeln('--- Basic While Loop ---');
  while birdCount <= 5 do
  begin
    writeln('Bird ', birdCount, ' chirping');
    birdCount := birdCount + 1;  // Must modify condition
  end;
  writeln('All birds chirped!');
  writeln;
  
  // ---- WHILE WITH UNKNOWN ITERATIONS ----
  writeln('--- Counting Birds ---');
  birdCount := 0;
  while birdCount < 10 do
  begin
    birdCount := birdCount + 1;
    writeln('Counting bird ', birdCount);
  end;
  writeln;
  
  // ---- INFINITE LOOP PREVENTION ----
  writeln('--- Safe Loop ---');
  birdCount := 1;
  while birdCount <= 3 do
  begin
    writeln('Bird ', birdCount);
    birdCount := birdCount + 1;  // Essential to avoid infinite loop
  end;
  
  // ---- COMMON MISTAKES ----
  // while birdCount <= 5 do
  //   writeln('This is infinite!');
  //   birdCount := birdCount + 1;  // Needs begin...end
end.

The condition is checked before each iteration. If true, the loop body executes. When the condition evaluates to false, the loop ends and program execution proceeds with the statements that follow it. The condition must eventually become false to prevent an infinite loop.The loop variable needs to be changed during each iteration so that the loop can eventually reach its stopping condition.

6. repeat-until Loop

Post-test loop

The repeat-until loop executes a block repeatedly until a condition becomes true.

repeat ... until executes the loop body at least once. Condition is checked at the end of each iteration. Unlike while, repeat-until continues until the condition is true. No begin ... end needed for multiple statements because repeat and until act as brackets.

Code Example

program RepeatUntilDemo;

var
  birdCount: Integer;
  input: Integer;

begin
  // ---- BASIC REPEAT-UNTIL ----
  writeln('--- Basic Repeat-Until ---');
  birdCount := 1;
  repeat
    writeln('Bird ', birdCount, ' chirping');
    birdCount := birdCount + 1;
  until birdCount > 5;
  writeln('All birds chirped!');
  writeln;
  
  // ---- AT LEAST ONE EXECUTION ----
  writeln('--- At Least One Execution ---');
  birdCount := 10;
  repeat
    writeln('This executes at least once');
    birdCount := birdCount + 1;
  until birdCount > 15;
  writeln;
  
  // ---- USER INPUT VALIDATION ----
  writeln('--- Input Validation ---');
  repeat
    write('Enter a number between 1 and 10: ');
    readln(input);
  until (input >= 1) and (input <= 10);
  writeln('Valid input: ', input);
  
  // ---- COMPARISON: WHILE VS REPEAT-UNTIL ----
  writeln('--- While vs Repeat-Until ---');
  writeln('while: checks condition first, may execute 0 times');
  writeln('repeat-until: executes at least once, then checks');
end.

The loop body executes first. Then the condition is checked. If true, the loop exits. If false, the loop repeats. The condition is the opposite of while—repeat-until continues until the condition is true, while while continues while the condition is true.

7. break and continue

Loop control statements

break exits a loop early, while continue skips to the next iteration.

break immediately terminates the loop containing it. Execution continues after the loop. continue skips the rest of the current iteration and jumps to the next iteration. Use break when you’ve found what you’re looking for. Use continue when you want to skip processing for certain conditions.

Code Example

program BreakContinueDemo;

var
  i: Integer;

begin
  // ---- BREAK EXAMPLE ----
  writeln('--- Break Example ---');
  writeln('Finding first eagle...');
  for i := 1 to 10 do
  begin
    if i = 4 then
    begin
      writeln('Eagle found at position ', i);
      break;  // Exit loop immediately
    end;
    writeln('Checking bird ', i);
  end;
  writeln('Search complete!');
  writeln;
  
  // ---- CONTINUE EXAMPLE ----
  writeln('--- Continue Example ---');
  writeln('Counting only odd birds...');
  for i := 1 to 10 do
  begin
    if i mod 2 = 0 then
      continue;  // Skip even numbers
    writeln('Bird ', i, ' counted');
  end;
  writeln('All odd birds counted!');
  writeln;
  
  // ---- BREAK IN NESTED LOOPS ----
  writeln('--- Break in Nested Loops ---');
  for i := 1 to 3 do
  begin
    writeln('Tree ', i);
    // Break only exits the inner loop
    if i = 2 then
      break;  // Exits outer loop completely
  end;
end.

The break statement terminates the loop immediately. When used inside nested loops, it exits only the innermost loop where it appears.

The continue statement skips the remaining statements in the current iteration and proceeds with the next iteration of the loop.

Procedures and Functions

1. Procedure Declaration

Declaring subroutines

Procedures are subroutines that perform specific tasks without returning a value.

Procedures are declared using the procedure keyword. Parameters can be passed to procedures. Local variables can be declared inside procedures. Procedure declaration specifies the name and parameters. Procedure definition provides the implementation.

Code Example

program ProcedureDemo;

// ---- PROCEDURE DECLARATIONS ----

// Procedure without parameters
procedure DisplayWelcome;
begin
  writeln('Welcome to the Bird Sanctuary!');
  writeln('We have many birds here.');
end;

// Procedure with one parameter
procedure DisplayBird(name: String);
begin
  writeln('Bird: ', name);
end;

// Procedure with multiple parameters
procedure DisplayBirdInfo(name: String; count: Integer; weight: Real);
begin
  writeln('Name: ', name);
  writeln('Count: ', count);
  writeln('Weight: ', weight:0:1, ' grams');
  writeln;
end;

// Procedure with parameters and local variable
procedure CountBirds(number: Integer);
var
  i: Integer;  // Local variable
begin
  for i := 1 to number do
    writeln('Bird ', i, ' counted');
end;

begin
  // ---- CALLING PROCEDURES ----
  DisplayWelcome;
  writeln;
  
  DisplayBird('Sparrow');
  DisplayBird('Eagle');
  writeln;
  
  DisplayBirdInfo('Hawk', 5, 1200.5);
  DisplayBirdInfo('Cardinal', 8, 45.2);
  
  CountBirds(3);
end.

Procedures are defined with the procedure keyword. They execute when called. Parameters receive values from the caller. Local variables exist only within the procedure. Procedures are called by name, followed by arguments in parentheses.

2. Procedure Definition

The procedure body

The procedure body contains the statements that execute when the procedure is called.

Procedure body is the code between begin and end in the procedure. Declarations can appear before begin in the procedure. Variables, constants, and types can be declared locally. Nested procedures are procedures declared inside other procedures.

Code Example

program ProcedureDefinitionDemo;

// ---- PROCEDURE WITH DECLARATIONS ----
procedure BirdCounter;
var
  i: Integer;        // Local variable
  BIRD_TYPES = 3;   // Local constant
begin
  writeln('Counting birds:');
  for i := 1 to BIRD_TYPES do
    writeln('Counting bird type ', i);
end;

// ---- PROCEDURE WITH LOCAL PROCEDURE ----
procedure BirdManager;
  // Nested procedure
  procedure AddBird(name: String);
  begin
    writeln('Adding bird: ', name);
  end;
begin
  AddBird('Sparrow');
  AddBird('Eagle');
  AddBird('Hawk');
end;

// ---- PROCEDURE WITH PARAMETERS ----
procedure BirdReporter(species: String; count: Integer);
var
  status: String;
begin
  if count > 10 then
    status := 'large flock'
  else
    status := 'small group';
  
  writeln('We have ', count, ' ', species, 's - ', status);
end;

begin
  writeln('--- Procedure with Declarations ---');
  BirdCounter;
  writeln;
  
  writeln('--- Procedure with Nested Procedure ---');
  BirdManager;
  writeln;
  
  writeln('--- Procedure with Parameters ---');
  BirdReporter('Sparrow', 15);
  BirdReporter('Eagle', 3);
end.

The procedure body contains the executable code. Local declarations are processed when the procedure is called. Nested procedures are accessible only within the containing procedure.

3. Function Declaration

Declaring functions with return values

Functions are subroutines that return a value to the caller.

Functions are declared with the function keyword followed by the return type. In Pascal, a function’s return value is assigned directly to the function’s name. Parameters work the same as procedures. Use functions when you need to compute and return a value.

Code Example

program FunctionDemo;

// ---- SIMPLE FUNCTION ----
function DoubleValue(x: Integer): Integer;
begin
  DoubleValue := x * 2;  // Assign return value to function name
end;

// ---- FUNCTION WITH REAL RETURN ----
function AverageWeight(w1, w2: Real): Real;
begin
  AverageWeight := (w1 + w2) / 2;
end;

// ---- FUNCTION WITH BOOLEAN RETURN ----
function IsHeavy(weight: Real): Boolean;
begin
  if weight > 1000 then
    IsHeavy := True
  else
    IsHeavy := False;
end;

// ---- FUNCTION WITH MULTIPLE PARAMETERS ----
function CalculatePopulation(initial: Integer; years: Integer; growthRate: Real): Real;
begin
  CalculatePopulation := initial * (1 + growthRate/100) ** years;
end;

var
  result: Integer;
  avg: Real;
  heavy: Boolean;

begin
  // ---- CALLING FUNCTIONS ----
  result := DoubleValue(10);
  writeln('Double of 10: ', result);
  
  avg := AverageWeight(25.5, 4500.0);
  writeln('Average weight: ', avg:0:1);
  
  heavy := IsHeavy(4500.0);
  writeln('Is eagle heavy? ', heavy);
  
  writeln('Population after 5 years: ', CalculatePopulation(100, 5, 10):0:1);
end.

Functions are defined with function and a return type. The function produces its result by assigning the desired value to the function’s name.Functions can be used in expressions.

4. Function Definition

The function body

The function body contains the statements that compute the return value.

Function body is the code between begin and end in the function. Return value is assigned to the function name or Result (in modern Pascal). Parameters can be passed by value or by reference. Before the function finishes execution, the function name must be assigned the value that will be returned to the caller.

Code Example

program FunctionDefinitionDemo;

// ---- BASIC FUNCTION DEFINITION ----
function AddBirds(a, b: Integer): Integer;
begin
  AddBirds := a + b;  // Assign to function name
end;

// ---- FUNCTION WITH LOCAL VARIABLE ----
function CalculateFlightTime(distance, speed: Real): Real;
var
  time: Real;  // Local variable
begin
  time := distance / speed;
  CalculateFlightTime := time;
end;

// ---- FUNCTION WITH MULTIPLE RETURN POINTS ----
function CompareBirds(a, b: Integer): String;
begin
  if a > b then
    CompareBirds := 'More birds'
  else if a < b then
    CompareBirds := 'Fewer birds'
  else
    CompareBirds := 'Same number';
end;

// ---- MODERN PASCAL: USING RESULT ----
function SquareValue(x: Integer): Integer;
begin
  Result := x * x;  // Using Result variable (Free Pascal)
end;

var
  sum: Integer;
  time: Real;
  comparison: String;

begin
  sum := AddBirds(10, 5);
  writeln('Total birds: ', sum);
  
  time := CalculateFlightTime(1000, 50);
  writeln('Flight time: ', time:0:2, ' hours');
  
  comparison := CompareBirds(10, 5);
  writeln('Comparison: ', comparison);
  
  writeln('Square of 5: ', SquareValue(5));
end.

The function body computes the return value. The return value is assigned to the function name (or Result). All execution paths must assign a value to the function. The function exits when end is reached.

5. Parameters

Value parameters and variable parameters

Parameters allow data to be passed to procedures and functions. Value parameters copy data, while variable parameters pass references.

Value parameters copy the argument’s value. Changes inside the procedure don’t affect the original. Variable parameters (var) pass a reference to the original. Changes affect the original. var allows modifying the original variable. Performance of value parameters can be slower for large types.

Code Example

program ParametersDemo;

// ---- VALUE PARAMETER (default) ----
procedure DoubleValue(x: Integer);
begin
  x := x * 2;  // Changes only the copy
  writeln('Inside procedure (value): ', x);
end;

// ---- VARIABLE PARAMETER (var) ----
procedure DoubleVariable(var x: Integer);
begin
  x := x * 2;  // Changes the original
  writeln('Inside procedure (var): ', x);
end;

// ---- VALUE PARAMETER WITH STRING ----
procedure DisplayBird(name: String);
begin
  name := 'Bird: ' + name;  // Changes only the copy
  writeln(name);
end;

// ---- VARIABLE PARAMETER WITH RECORD ----
type
  BirdRecord = record
    name: String;
    count: Integer;
  end;

procedure UpdateBirdCount(var bird: BirdRecord; newCount: Integer);
begin
  bird.count := newCount;  // Modifies the original
  writeln('Updated count: ', bird.count);
end;

var
  birdCount: Integer;
  myBird: BirdRecord;

begin
  // ---- VALUE PARAMETER EXAMPLE ----
  birdCount := 10;
  writeln('Before value call: ', birdCount);
  DoubleValue(birdCount);
  writeln('After value call: ', birdCount, ' (unchanged)');
  writeln;
  
  // ---- VARIABLE PARAMETER EXAMPLE ----
  birdCount := 10;
  writeln('Before var call: ', birdCount);
  DoubleVariable(birdCount);
  writeln('After var call: ', birdCount, ' (changed!)');
  writeln;
  
  // ---- RECORD WITH VAR ----
  myBird.name := 'Sparrow';
  myBird.count := 5;
  writeln('Before var record: ', myBird.count);
  UpdateBirdCount(myBird, 10);
  writeln('After var record: ', myBird.count);
end.

Value parameters copy the argument, protecting the original. Variable parameters (var) pass a reference, allowing modification. var is more efficient for large data types.

6. Function Results

Returning values from functions

Function results are the values produced by a function and passed back to the code that called it.

Return values can be any Pascal data type. Assigning to function name is the traditional way. Using Result is the modern Pascal way. A function call is an expression that executes a function and produces its resulting value.

Code Example

program FunctionResultsDemo;

// ---- RETURNING INTEGER ----
function AddNumbers(a, b: Integer): Integer;
begin
  AddNumbers := a + b;
end;

// ---- RETURNING REAL ----
function MultiplyNumbers(a, b: Real): Real;
begin
  Result := a * b;  // Using Result (modern Pascal)
end;

// ---- RETURNING BOOLEAN ----
function IsEven(number: Integer): Boolean;
begin
  IsEven := (number mod 2 = 0);
end;

// ---- RETURNING STRING ----
function GetBirdStatus(count: Integer): String;
begin
  if count > 10 then
    Result := 'Large flock'
  else if count > 5 then
    Result := 'Medium flock'
  else if count > 0 then
    Result := 'Small flock'
  else
    Result := 'No birds';
end;

// ---- RETURNING RECORD ----
type
  BirdType = record
    species: String;
    population: Integer;
    weight: Real;
  end;

function CreateBird(species: String; count: Integer; weight: Real): BirdType;
begin
  Result.species := species;
  Result.population := count;
  Result.weight := weight;
end;

var
  birdInfo: BirdType;
  result: Integer;

begin
  // ---- USING FUNCTIONS ----
  result := AddNumbers(10, 5);
  writeln('Addition result: ', result);
  
  writeln('Multiplication result: ', MultiplyNumbers(10.5, 3.2):0:2);
  writeln('Is 7 even? ', IsEven(7));
  writeln('Bird status: ', GetBirdStatus(8));
  
  // ---- FUNCTION RETURNING RECORD ----
  birdInfo := CreateBird('Eagle', 3, 4500.5);
  writeln('Species: ', birdInfo.species);
  writeln('Population: ', birdInfo.population);
  writeln('Weight: ', birdInfo.weight:0:1);
end.

Functions compute and return values. The return type is specified in the function declaration. The function name or Result is used to assign the return value. Functions can be used in expressions.

7. Recursion

Functions that call themselves

Recursion is a programming technique in which a function invokes itself to break a problem into smaller, more manageable instances.

Recursive functions call themselves with modified arguments. Base case stops the recursion. Recursive case calls itself. Without a base case, recursion continues indefinitely.

Code Example

program RecursionDemo;

// ---- FACTORIAL RECURSION ----
function Factorial(n: Integer): Integer;
begin
  if n <= 1 then
    Factorial := 1  // Base case
  else
    Factorial := n * Factorial(n - 1);  // Recursive case
end;

// ---- FIBONACCI RECURSION ----
function Fibonacci(n: Integer): Integer;
begin
  if n <= 1 then
    Fibonacci := n  // Base cases
  else
    Fibonacci := Fibonacci(n - 1) + Fibonacci(n - 2);  // Recursive case
end;

// ---- RECURSIVE SUM ----
function SumNumbers(n: Integer): Integer;
begin
  if n <= 0 then
    SumNumbers := 0
  else
    SumNumbers := n + SumNumbers(n - 1);
end;

// ---- RECURSIVE COUNTDOWN ----
procedure Countdown(n: Integer);
begin
  if n > 0 then
  begin
    writeln('Count: ', n);
    Countdown(n - 1);  // Recursive call
  end;
end;

var
  i: Integer;

begin
  // ---- RECURSIVE FUNCTIONS ----
  writeln('Factorial of 5: ', Factorial(5));
  writeln('Fibonacci(7): ', Fibonacci(7));
  writeln('Sum of 1..5: ', SumNumbers(5));
  writeln;
  
  // ---- RECURSIVE PROCEDURE ----
  writeln('Countdown:');
  Countdown(5);
  writeln;
  
  // ---- COMPARE RECURSION VS ITERATION ----
  writeln('--- Recursion vs Iteration ---');
  writeln('Recursion: Factorial(5) = ', Factorial(5));
  
  // Iterative factorial
  var result: Integer = 1;
  for i := 1 to 5 do
    result := result * i;
  writeln('Iteration: Factorial(5) = ', result);
end.

The function calls itself with a smaller value. The base case stops the recursion. Without a base case, the recursion continues indefinitely.

8. Scope

Local, global, and block scope

Scope defines the portion of a program in which a variable can be accessed and used.

Global variables are declared at the program level and accessible everywhere. Local variables are declared inside procedures/functions and accessible only there. Block scope is determined by begin ... end blocks. Strict ordering means declarations must be in a specific order.

Code Example

program ScopeDemo;

// ---- GLOBAL SCOPE ----
var
  globalBirdCount: Integer = 100;  // Accessible everywhere
  globalBirdName: String = 'Global Bird';

// ---- PROCEDURE WITH LOCAL SCOPE ----
procedure BirdCounter;
var
  localBirdCount: Integer;  // Local variable
begin
  localBirdCount := 50;  // Only accessible here
  
  // Can access global variables
  writeln('Global: ', globalBirdCount);
  writeln('Local: ', localBirdCount);
end;

// ---- PROCEDURE WITH NESTED SCOPE ----
procedure NestedScope;
var
  outerVar: Integer = 10;  // Accessible in entire procedure
begin
  writeln('Outer variable: ', outerVar);
  
  // ---- BLOCK SCOPE ----
  begin
    var blockVar: Integer = 20;  // Accessible only in this block
    writeln('Block variable: ', blockVar);
    
    // Nested block can access outer
    writeln('Outer from block: ', outerVar);
  end;
  
  // blockVar not accessible here
  // writeln(blockVar);  // ERROR: Not defined
end;

// ---- SHADOWING ----
procedure Shadowing;
var
  shadowVar: Integer = 5;
begin
  writeln('Local shadowVar: ', shadowVar);  // Shows local
  
  // Access global variable if needed
  // Use different names to avoid confusion
end;

var
  globalShadow: Integer = 100;  // Global variable

begin
  BirdCounter;
  NestedScope;
  Shadowing;
  
  // Can access global variables anywhere
  writeln('Global bird count: ', globalBirdCount);
end.

Global variables are accessible everywhere. Local variables are accessible only in their declaring block. Variables in inner blocks shadow variables in outer blocks.

Arrays and Strings

1. Arrays

Fixed-size collections of same type

Arrays store collections of elements of the same type in contiguous memory.

Arrays are declared with array[range] of type. Indexing starts at the specified lower bound (not necessarily 0). Memory layout is contiguous. Access uses the [index] notation. Array limits can be 1..10, 0..9, or any range.

Code Example

program ArraysDemo;

var
  // ---- 1D ARRAYS ----
  birdCount: array[1..5] of Integer;  // Indices 1-5
  birdWeight: array[0..4] of Real;    // Indices 0-4
  birdName: array[1..3] of String;    // Indices 1-3
  
  // ---- ARRAY WITH SUBRANGE ----
  type
    BirdRange = 1..10;
    BirdArray = array[BirdRange] of Integer;
  var
    myBirds: BirdArray;

begin
  // ---- ASSIGNING VALUES ----
  birdCount[1] := 10;
  birdCount[2] := 15;
  birdCount[3] := 12;
  birdCount[4] := 8;
  birdCount[5] := 20;
  
  birdWeight[0] := 25.5;
  birdWeight[1] := 4500.0;
  birdWeight[2] := 1200.5;
  
  // ---- DISPLAYING ARRAY ----
  writeln('Bird counts:');
  for var i := 1 to 5 do
    writeln('Bird ', i, ': ', birdCount[i]);
  
  writeln;
  writeln('Bird weights:');
  for var i := 0 to 2 do
    writeln('Weight ', i, ': ', birdWeight[i]:0:1);
  
  // ---- INITIALIZING ARRAY ----
  var birdNames: array[1..3] of String = ('Sparrow', 'Eagle', 'Hawk');
  writeln;
  writeln('Bird names:');
  for var i := 1 to 3 do
    writeln(i, '. ', birdNames[i]);
  
  // ---- ARRAY WITH SUBRANGE ----
  for var i := 1 to 10 do
    myBirds[i] := i * 10;
  writeln('My birds: ', myBirds[5]);
end.

Arrays store elements in contiguous memory. Indexing starts at the specified lower bound. Arrays can be initialized with literal values.

2. Multidimensional Arrays

2D, 3D, and beyond

Multidimensional arrays store data in grids or cubes.

2D arrays are array[rowRange, colRange] of type. Memory layout is row-major in Pascal. Access uses [row, col] notation. 3D arrays add another dimension.

Code Example

program MultiDimensionalDemo;

var
  // ---- 2D ARRAY (grid) ----
  birdSightings: array[1..3, 1..4] of Integer = (
    (10, 15, 12, 8),    // Location 1
    (5, 3, 7, 12),      // Location 2
    (20, 18, 15, 10)    // Location 3
  );
  
  // ---- 2D ARRAY WITH TYPE ----
  type
    Month = (Jan, Feb, Mar, Apr, May, Jun,
             Jul, Aug, Sep, Oct, Nov, Dec);
    Location = 1..3;
    Sightings = array[Location, Month] of Integer;
  var
    monthlySightings: Sightings;

begin
  // ---- DISPLAYING 2D ARRAY ----
  writeln('--- Bird Sightings Matrix ---');
  for var row := 1 to 3 do
  begin
    write('Location ', row, ': ');
    for var col := 1 to 4 do
      write(birdSightings[row, col]:3);
    writeln;
  end;
  writeln;
  
  // ---- ACCESSING 2D ARRAY ----
  writeln('Location 2, Column 3: ', birdSightings[2, 3]);
  writeln('Location 3, Column 2: ', birdSightings[3, 2]);
  
  // ---- 3D ARRAY (cube) ----
  var birdData: array[1..2, 1..3, 1..4] of Integer = (
    (  // Week 1
      (10, 15, 12, 8),
      (5, 3, 7, 12),
      (20, 18, 15, 10)
    ),
    (  // Week 2
      (12, 17, 14, 10),
      (7, 5, 9, 14),
      (22, 20, 17, 12)
    )
  );
  
  writeln('--- 3D Array ---');
  writeln('Week 2, Location 3, Column 2: ', birdData[2, 3, 2]);
  
  // ---- USING WITH SUBRANGES ----
  monthlySightings[1, Jan] := 10;
  monthlySightings[1, Feb] := 15;
  writeln('January sightings: ', monthlySightings[1, Jan]);
end.

Multidimensional arrays are stored in memory row-major order (the last index changes fastest). Access uses multiple indices in the same order as the declaration.

3. Strings

The string type and operations

Strings store text data as a sequence of characters.

Strings are variable-length character sequences. Declaration is var name: String. Maximum length can be specified: String[20]. Operations include assignment, concatenation (+), and comparison.

Code Example

program StringsDemo;

var
  // ---- STRING DECLARATIONS ----
  birdName: String;
  birdNameShort: String[10];  // Maximum 10 characters
  birdNameLong: String[50];
  
  // ---- STRING OPERATIONS ----
  fullName: String;
  greeting: String;

begin
  // ---- ASSIGNMENT ----
  birdName := 'Sparrow';
  birdNameShort := 'Eagle';
  birdNameLong := 'This is a very long bird name indeed';
  
  // ---- CONCATENATION ----
  fullName := birdName + ' ' + birdNameShort;
  writeln('Full name: ', fullName);
  
  greeting := 'Hello, ' + birdName + '!';
  writeln(greeting);
  
  // ---- STRING LENGTH ----
  writeln('Length of birdName: ', Length(birdName));
  writeln('Length of birdNameShort: ', Length(birdNameShort));
  writeln('Length of birdNameLong: ', Length(birdNameLong));
  
  // ---- STRING WITH DIMENSION ----
  var limitedStr: String[5];
  limitedStr := 'This is too long';  // Will be truncated
  writeln('Limited string: ', limitedStr);
  
  // ---- STRING LITERAL ----
  var emptyString: String = '';
  writeln('Empty string length: ', Length(emptyString));
end.

Strings are dynamic arrays of characters. String with length specifier limits the maximum length. Concatenation creates a new string. String literals use single quotes.

4. Character Arrays

Working with char arrays

Character arrays are arrays of characters, similar to C-style strings.

Char arrays are array[range] of Char. Compared to strings, they have fixed length. Access uses indexing. Conversion uses functions.

Code Example

program CharArraysDemo;

var
  // ---- CHAR ARRAY ----
  birdName: array[1..10] of Char;
  
  // ---- CHARACTERS ----
  ch: Char;
  i: Integer;

begin
  // ---- ASSIGNING CHARACTERS ----
  birdName[1] := 'S';
  birdName[2] := 'p';
  birdName[3] := 'a';
  birdName[4] := 'r';
  birdName[5] := 'r';
  birdName[6] := 'o';
  birdName[7] := 'w';
  // Remaining elements have undefined values
  
  // ---- DISPLAYING CHAR ARRAY ----
  writeln('--- Char Array ---');
  for i := 1 to 7 do
    write(birdName[i]);
  writeln;
  
  // ---- COMPARING CHAR ARRAY WITH STRING ----
  var myString: String = 'Sparrow';
  writeln('String: ', myString);
  
  // ---- CHARACTER CODES ----
  ch := 'A';
  writeln('Character: ', ch);
  writeln('ASCII code: ', Ord(ch));
  
  ch := Chr(65);  // Character from ASCII code
  writeln('Chr(65): ', ch);
end.

Char arrays store individual characters. They have fixed size. Characters use ASCII values. The Ord and Chr functions convert between characters and ASCII codes.

5. String Library Functions

length, concat, copy, pos, delete, insert

String functions provide common operations on strings.

length returns the number of characters. concat joins strings. copy extracts a substring. pos finds a substring. delete removes characters. insert inserts characters.

Code Example

program StringFunctionsDemo;

var
  birdName: String = 'Sparrow';
  birdList: String;
  result: String;
  position: Integer;

begin
  // ---- LENGTH ----
  writeln('--- Length ---');
  writeln('Length of "Sparrow": ', Length(birdName));
  writeln('Length of empty string: ', Length(''));
  writeln;
  
  // ---- CONCAT ----
  writeln('--- Concat ---');
  birdList := concat('Sparrow', ', ', 'Eagle', ', ', 'Hawk');
  writeln('Bird list: ', birdList);
  writeln('Using +: ', 'Sparrow' + ', ' + 'Eagle');
  writeln;
  
  // ---- COPY ----
  writeln('--- Copy ---');
  result := Copy(birdName, 2, 4);  // Copy from position 2, length 4
  writeln('Copy(2,4) from Sparrow: ', result);  // "parr"
  result := Copy(birdName, 1, 3);
  writeln('Copy(1,3): ', result);  // "Spa"
  writeln;
  
  // ---- POS ----
  writeln('--- Pos ---');
  position := Pos('row', birdName);
  writeln('Position of "row" in Sparrow: ', position);
  position := Pos('X', birdName);
  writeln('Position of "X" in Sparrow: ', position, ' (not found)');
  writeln;
  
  // ---- DELETE ----
  writeln('--- Delete ---');
  var modifyStr: String = birdName;
  Delete(modifyStr, 3, 2);  // Delete from position 3, length 2
  writeln('Delete(3,2) from Sparrow: ', modifyStr);  // "Srow"
  writeln;
  
  // ---- INSERT ----
  writeln('--- Insert ---');
  var insertStr: String = birdName;
  Insert('ow', insertStr, 3);  // Insert "ow" at position 3
  writeln('Insert("ow",3) into Sparrow: ', insertStr);  // "Spowarrow"
  writeln;
  
  // ---- COMBINED EXAMPLE ----
  writeln('--- Combined Example ---');
  var test: String = 'The Eagle bird';
  test := Copy(test, 5, 5);  // Extract "Eagle"
  writeln('Extract: ', test);
end.

String functions create new strings or modify existing ones. Pos returns 0 if the substring is not found. Delete modifies the original string. Insert modifies the original string.

Records, Sets, and Enumerations

1. Records

Records group related data of different types into a single unit.

Records are declared with record ... end. Fields are named members. Access uses the dot operator. with statement simplifies access to fields.

Code Example

program RecordsDemo;

type
  // ---- RECORD DECLARATION ----
  Bird = record
    species: String;
    count: Integer;
    weight: Real;
    isMigratory: Boolean;
  end;
  
  // ---- RECORD WITH ARRAY ----
  BirdData = record
    id: Integer;
    name: String;
    sightings: array[1..12] of Integer;
  end;

var
  // ---- RECORD VARIABLE ----
  sparrow: Bird;
  eagle: Bird;
  data: BirdData;

begin
  // ---- ASSIGNING FIELDS ----
  sparrow.species := 'Sparrow';
  sparrow.count := 10;
  sparrow.weight := 25.5;
  sparrow.isMigratory := True;
  
  eagle.species := 'Eagle';
  eagle.count := 3;
  eagle.weight := 4500.0;
  eagle.isMigratory := False;
  
  // ---- ACCESSING FIELDS ----
  writeln('--- Bird Records ---');
  writeln('Sparrow: ', sparrow.species, ' (', sparrow.count, ')');
  writeln('Eagle: ', eagle.species, ' (', eagle.count, ')');
  writeln;
  
  // ---- WITH STATEMENT ----
  writeln('--- Using With ---');
  with sparrow do
  begin
    writeln('Species: ', species);
    writeln('Count: ', count);
    writeln('Weight: ', weight:0:1);
    writeln('Migratory: ', isMigratory);
  end;
  writeln;
  
  // ---- RECORD WITH ARRAY ----
  data.id := 1;
  data.name := 'Bird Data';
  for var i := 1 to 12 do
    data.sightings[i] := i * 10;
  
  writeln('Data ID: ', data.id);
  writeln('Data Name: ', data.name);
  writeln('Sightings: ', data.sightings[5]);
end.

Records group different types of data. Fields are accessed with dot notation. The with statement simplifies access to fields. Records can contain arrays and other records.

2. Nested Records

Records within records

Nested records allow complex data structures by embedding records inside other records.

Nested records are records declared as fields of other records. Access uses multiple dot operators. Flexibility allows building complex structures.

Code Example

program NestedRecordsDemo;

type
  // ---- NESTED RECORDS ----
  Location = record
    city: String;
    country: String;
  end;
  
  BirdInfo = record
    species: String;
    count: Integer;
    weight: Real;
  end;
  
  Sighting = record
    bird: BirdInfo;     // Nested record
    location: Location; // Nested record
    date: String;
    observer: String;
  end;
  
  // ---- DEEPER NESTING ----
  BirdStats = record
    info: BirdInfo;
    habitat: String;
    sightings: array[1..5] of Sighting;
  end;

var
  sighting: Sighting;
  stats: BirdStats;

begin
  // ---- ASSIGNING NESTED RECORDS ----
  sighting.bird.species := 'Sparrow';
  sighting.bird.count := 10;
  sighting.bird.weight := 25.5;
  sighting.location.city := 'New York';
  sighting.location.country := 'USA';
  sighting.date := '2024-01-15';
  sighting.observer := 'John Doe';
  
  // ---- ACCESSING NESTED FIELDS ----
  writeln('--- Nested Records ---');
  writeln('Bird: ', sighting.bird.species);
  writeln('Count: ', sighting.bird.count);
  writeln('Location: ', sighting.location.city, ', ', sighting.location.country);
  writeln('Date: ', sighting.date);
  writeln('Observer: ', sighting.observer);
  writeln;
  
  // ---- DEEPER NESTING ----
  stats.info.species := 'Eagle';
  stats.info.count := 3;
  stats.info.weight := 4500.0;
  stats.habitat := 'Mountain';
  
  // Assign first sighting
  stats.sightings[1].bird := stats.info;
  stats.sightings[1].location.city := 'Rocky Mountains';
  stats.sightings[1].location.country := 'USA';
  stats.sightings[1].date := '2024-02-01';
  stats.sightings[1].observer := 'Jane Smith';
  
  writeln('--- Deeper Nesting ---');
  writeln('Species: ', stats.info.species);
  writeln('Habitat: ', stats.habitat);
  writeln('First sighting: ', stats.sightings[1].date, ' by ', stats.sightings[1].observer);
end.

Nested records allow structured data. Access uses multiple dot operators. Records can be nested to any depth.

3. Sets

The Pascal set type

Sets are collections of values of the same ordinal type.

Sets are declared with set of type. Elements must be ordinal types (integers, characters, enums). Operations include in (membership), + (union), - (difference), * (intersection). Limits typically max 256 elements.

Code Example

program SetsDemo;

type
  // ---- ENUMERATED TYPE FOR SETS ----
  BirdColor = (Red, Blue, Green, Yellow, Black, White);
  ColorSet = set of BirdColor;
  
  // ---- CHARACTER SET ----
  Vowels = set of Char;

var
  // ---- SET VARIABLES ----
  colors: ColorSet;
  vowels: Vowels;
  anotherColors: ColorSet;

begin
  // ---- SET INITIALIZATION ----
  colors := [Red, Blue, Green];
  vowels := ['a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U'];
  
  // ---- MEMBERSHIP TEST (IN) ----
  writeln('--- Membership Test ---');
  if Red in colors then
    writeln('Red is in the set');
  
  if Yellow in colors then
    writeln('Yellow is in the set')
  else
    writeln('Yellow is not in the set');
  
  if 'a' in vowels then
    writeln('"a" is a vowel');
  writeln;
  
  // ---- SET OPERATIONS ----
  writeln('--- Set Operations ---');
  
  // Union (+)
  anotherColors := [Yellow, Black];
  var allColors: ColorSet = colors + anotherColors;
  writeln('Union: ', allColors);
  
  // Difference (-)
  var someColors: ColorSet = colors - [Blue];
  writeln('Colors without Blue: ', someColors);
  
  // Intersection (*)
  var commonColors: ColorSet = colors * anotherColors;
  writeln('Common colors: ', commonColors);
  writeln;
  
  // ---- CHECKING EMPTY SET ----
  var emptySet: ColorSet = [];
  if emptySet = [] then
    writeln('Set is empty');
  
  // ---- PRACTICAL: CHARACTER CLASSIFICATION ----
  writeln('--- Character Classification ---');
  var testChar: Char = 'a';
  if testChar in vowels then
    writeln(testChar, ' is a vowel')
  else
    writeln(testChar, ' is a consonant');
end.

Sets contain ordinal values. The in operator checks membership. Set operations create new sets. Sets must have a limited number of elements.

4. Enumerations

Named integer constants

Enumerations are named values representing integers.

Enumerations are declared with type name = (value1, value2, ...). Values start at 0 by default. Order determines integer value. Ord returns the integer value.

Code Example

program EnumerationsDemo;

type
  // ---- BASIC ENUMERATION ----
  BirdType = (Sparrow, Eagle, Hawk, Cardinal, Finch);
  
  // ---- ENUMERATION WITH SPECIFIC VALUES ----
  BirdSize = (Tiny = 1, Small = 2, Medium = 3, Large = 4, Giant = 5);
  
  // ---- ENUMERATION FOR DAYS ----
  Day = (Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday);

var
  myBird: BirdType;
  size: BirdSize;
  today: Day;

begin
  // ---- USING ENUMERATIONS ----
  myBird := Eagle;
  size := Large;
  today := Sunday;
  
  // ---- DISPLAYING ENUMERATION VALUES ----
  writeln('--- Enumeration Values ---');
  writeln('My bird: ', myBird);
  writeln('Ordinal value: ', Ord(myBird));  // 1
  writeln('Size: ', size);
  writeln('Ordinal value: ', Ord(size));    // 4
  writeln('Day: ', today);
  writeln('Ordinal value: ', Ord(today));   // 6
  writeln;
  
  // ---- ENUMERATION AS INDEX ----
  var counts: array[BirdType] of Integer;
  counts[Sparrow] := 10;
  counts[Eagle] := 3;
  counts[Hawk] := 5;
  
  writeln('--- Enumeration as Index ---');
  writeln('Sparrows: ', counts[Sparrow]);
  writeln('Eagles: ', counts[Eagle]);
  writeln;
  
  // ---- LOOPING THROUGH ENUMERATION ----
  writeln('--- Looping Through Enumeration ---');
  for var b := Sparrow to Finch do
    writeln('Bird type: ', b);
  
  // ---- ENUMERATION WITH CASE ----
  writeln('--- Enumeration with Case ---');
  case myBird of
    Sparrow: writeln('It is a Sparrow');
    Eagle: writeln('It is an Eagle');
    Hawk: writeln('It is a Hawk');
    Cardinal: writeln('It is a Cardinal');
    Finch: writeln('It is a Finch');
  end;
end.

Enumerations assign integer values to names. The first value is 0 unless specified. Ord returns the integer value. Enumerations can be used in arrays and loops.

5. Subranges

Subsets of types

Subranges restrict values to a subset of a type.

Subranges are declared with lower .. upper. Values must be in the specified range. Usage includes array indices, validation, and type safety.

Code Example

program SubrangesDemo;

type
  // ---- SUBRANGE DECLARATIONS ----
  BirdAge = 0..20;           // Age in years, 0-20
  BirdCount = 1..100;        // Count must be 1-100
  MonthDay = 1..31;          // Day of month
  MonthNumber = 1..12;       // Month of year
  ScoreRange = 0..100;       // Score 0-100
  
  // ---- SUBRANGE WITH ENUMERATION ----
  Day = (Mon, Tue, Wed, Thu, Fri, Sat, Sun);
  WorkDay = Mon..Fri;        // Subrange of Day

var
  age: BirdAge;
  count: BirdCount;
  day: WorkDay;

begin
  // ---- ASSIGNING VALUES ----
  age := 5;        // Valid
  count := 50;     // Valid
  day := Wed;      // Valid
  
  // ---- INVALID ASSIGNMENTS ----
  // age := 25;     // ERROR: Outside range 0..20
  // count := 0;    // ERROR: Outside range 1..100
  // day := Sun;    // ERROR: Outside range Mon..Fri
  
  // ---- DISPLAY ----
  writeln('--- Subranges ---');
  writeln('Age: ', age);
  writeln('Count: ', count);
  writeln('Day: ', day);
  
  // ---- SUBRANGE WITH ARRAY ----
  var monthlyData: array[MonthNumber] of Integer;
  for var i := 1 to 12 do
    monthlyData[i] := i * 10;
  writeln('January data: ', monthlyData[1]);
  
  // ---- SUBRANGE VALIDATION ----
  writeln('--- Subrange Validation ---');
  var input: Integer = 75;
  if (input >= 0) and (input <= 100) then
    writeln('Score ', input, ' is valid')
  else
    writeln('Score invalid');
end.

Subranges restrict values to a specified range. They provide type safety and validation. Subranges can be used for array indices and other range-based operations.

Pointers and Dynamic Memory

1. Pointers

Variables that store memory addresses

Pointers store memory addresses of variables or dynamically allocated memory.

Pointers are declared with ^type. @ gets the address of a variable. ^ dereferences a pointer. nil is a null pointer. Dynamic memory is allocated with new.

Code Example

program PointersDemo;

type
  // ---- POINTER TO A RECORD ----
  BirdPtr = ^Bird;
  Bird = record
    species: String;
    count: Integer;
  end;

var
  // ---- BASIC POINTERS ----
  intPtr: ^Integer;
  strPtr: ^String;
  birdPtr: BirdPtr;
  
  // ---- VARIABLES TO POINT TO ----
  sparrow: Bird;

begin
  // ---- GETTING ADDRESS (@) ----
  new(intPtr);  // Allocate memory
  intPtr^ := 10;  // Dereference to assign value
  
  writeln('--- Basic Pointers ---');
  writeln('intPtr^ = ', intPtr^);
  writeln('intPtr = ', intPtr);
  
  // ---- POINTER TO RECORD ----
  new(birdPtr);
  birdPtr^.species := 'Sparrow';
  birdPtr^.count := 10;
  
  writeln('--- Record Pointer ---');
  writeln('Bird: ', birdPtr^.species);
  writeln('Count: ', birdPtr^.count);
  
  // ---- USING nil ----
  if birdPtr <> nil then
    writeln('birdPtr is valid');
  
  // ---- FREEING MEMORY ----
  dispose(intPtr);
  dispose(birdPtr);
  intPtr := nil;
  birdPtr := nil;
  
  // ---- COMMON USES ----
  writeln('--- Pointers in Use ---');
  var x: Integer = 5;
  var ptr: ^Integer = @x;  // Get address of existing variable
  
  ptr^ := 10;  // Change x through pointer
  writeln('x = ', x);
end.

Pointers store addresses. new allocates memory. dispose frees memory. ^ accesses the value at the address.

2. Dynamic Memory Allocation

new and dispose

Dynamic memory allocation creates variables at runtime.

new allocates memory on the heap. dispose frees memory. Memory leaks occur when you don’t dispose. Nil pointers prevent dangling pointers. Double dispose causes errors.

Code Example

program DynamicMemoryDemo;

type
  BirdPtr = ^Bird;
  Bird = record
    species: String;
    count: Integer;
    weight: Real;
  end;

var
  // ---- DYNAMIC VARIABLES ----
  birdPtr: BirdPtr;
  intPtr: ^Integer;
  strPtr: ^String;

begin
  // ---- ALLOCATING MEMORY ----
  writeln('--- Allocating Memory ---');
  new(birdPtr);
  new(intPtr);
  new(strPtr);
  
  // ---- USING DYNAMIC VARIABLES ----
  birdPtr^.species := 'Eagle';
  birdPtr^.count := 3;
  birdPtr^.weight := 4500.5;
  
  intPtr^ := 100;
  strPtr^ := 'Sparrow';
  
  writeln('Bird: ', birdPtr^.species);
  writeln('Count: ', birdPtr^.count);
  writeln('Weight: ', birdPtr^.weight:0:1);
  writeln('Integer: ', intPtr^);
  writeln('String: ', strPtr^);
  
  // ---- FREEING MEMORY ----
  writeln;
  writeln('--- Freeing Memory ---');
  dispose(birdPtr);
  dispose(intPtr);
  dispose(strPtr);
  
  // ---- SAFE PRACTICE ----
  birdPtr := nil;
  intPtr := nil;
  strPtr := nil;
  
  writeln('Memory freed, pointers set to nil');
  
  // ---- MEMORY LEAK EXAMPLE ----
  writeln;
  writeln('--- Memory Leak Warning ---');
  writeln('Forgetting to dispose() causes memory leaks');
  writeln('Always dispose() dynamic memory when done');
end.

new allocates memory. dispose frees it. Setting to nil prevents dangling pointers.

3. Linked Lists

Dynamic data structures

Linked lists are dynamic data structures where each element points to the next.

Linked lists consist of nodes with data and next pointer. Insertion adds nodes at any position. Deletion removes nodes. Traversal follows links from head to tail.

Code Example

program LinkedListsDemo;

type
  NodePtr = ^Node;
  Node = record
    data: Integer;
    next: NodePtr;
  end;

var
  head: NodePtr;
  current: NodePtr;
  newNode: NodePtr;
  i: Integer;

// ---- PROCEDURE TO ADD NODE ----
procedure AddNode(var head: NodePtr; value: Integer);
var
  newNode: NodePtr;
  current: NodePtr;
begin
  new(newNode);
  newNode^.data := value;
  newNode^.next := nil;
  
  if head = nil then
    head := newNode
  else
  begin
    current := head;
    while current^.next <> nil do
      current := current^.next;
    current^.next := newNode;
  end;
end;

// ---- PROCEDURE TO DISPLAY LIST ----
procedure DisplayList(head: NodePtr);
var
  current: NodePtr;
  position: Integer;
begin
  current := head;
  position := 1;
  
  while current <> nil do
  begin
    writeln('Node ', position, ': ', current^.data);
    current := current^.next;
    position := position + 1;
  end;
end;

// ---- FUNCTION TO SEARCH ----
function SearchNode(head: NodePtr; value: Integer): NodePtr;
var
  current: NodePtr;
begin
  current := head;
  while current <> nil do
  begin
    if current^.data = value then
    begin
      SearchNode := current;
      exit;
    end;
    current := current^.next;
  end;
  SearchNode := nil;
end;

// ---- PROCEDURE TO DELETE NODE ----
procedure DeleteNode(var head: NodePtr; value: Integer);
var
  current: NodePtr;
  prev: NodePtr;
begin
  if head = nil then
    exit;
  
  if head^.data = value then
  begin
    current := head;
    head := head^.next;
    dispose(current);
    exit;
  end;
  
  current := head;
  while current <> nil do
  begin
    if current^.data = value then
    begin
      prev^.next := current^.next;
      dispose(current);
      exit;
    end;
    prev := current;
    current := current^.next;
  end;
end;

begin
  // ---- CREATING LINKED LIST ----
  head := nil;
  writeln('--- Creating Linked List ---');
  
  for i := 1 to 5 do
    AddNode(head, i * 10);
  
  writeln('Initial list:');
  DisplayList(head);
  writeln;
  
  // ---- SEARCHING ----
  writeln('--- Searching ---');
  var found: NodePtr = SearchNode(head, 30);
  if found <> nil then
    writeln('Found node with value 30')
  else
    writeln('Node with value 30 not found');
  writeln;
  
  // ---- DELETING ----
  writeln('--- Deleting Node ---');
  DeleteNode(head, 20);
  writeln('After deleting 20:');
  DisplayList(head);
  writeln;
  
  // ---- FREEING LIST ----
  writeln('--- Freeing Memory ---');
  current := head;
  while current <> nil do
  begin
    var next := current^.next;
    dispose(current);
    current := next;
  end;
  head := nil;
  writeln('List freed');
end.

Nodes contain data and a pointer to the next node. AddNode creates a new node. SearchNode finds a node. DeleteNode removes a node. Traversal follows the next pointers.

File Handling

1. Text Files

Reading and writing text files

Text files store human-readable data in text format.

Text files use Text type. assign connects a variable to a file. rewrite creates a new file. reset opens an existing file. append adds to the end. close closes the file.

Code Example

program TextFileDemo;

var
  // ---- FILE VARIABLES ----
  outFile: Text;
  inFile: Text;
  
  // ---- DATA ----
  birdName: String;
  birdCount: Integer;
  i: Integer;

begin
  // ---- WRITING TO TEXT FILE ----
  writeln('--- Writing to File ---');
  assign(outFile, 'birds.txt');
  rewrite(outFile);  // Create new file
  
  // Write data to file
  writeln(outFile, 'Bird Sanctuary Data');
  writeln(outFile, '==================');
  writeln(outFile, 'Sparrow,10,25.5');
  writeln(outFile, 'Eagle,3,4500.0');
  writeln(outFile, 'Hawk,5,1200.5');
  
  close(outFile);  // Important!
  writeln('Data written to birds.txt');
  writeln;
  
  // ---- APPENDING TO FILE ----
  writeln('--- Appending to File ---');
  assign(outFile, 'birds.txt');
  append(outFile);  // Open for appending
  writeln(outFile, 'Cardinal,8,45.2');
  close(outFile);
  writeln('Data appended');
  writeln;
  
  // ---- READING FROM TEXT FILE ----
  writeln('--- Reading from File ---');
  assign(inFile, 'birds.txt');
  reset(inFile);  // Open for reading
  
  // Read and display each line
  while not EOF(inFile) do
  begin
    readln(inFile, birdName);
    writeln('Line: ', birdName);
  end;
  
  close(inFile);
  
  // ---- READING STRUCTURED DATA ----
  writeln;
  writeln('--- Reading Structured Data ---');
  reset(inFile);  // Reopen from start
  
  // Skip header lines
  for i := 1 to 2 do
    readln(inFile);
  
  while not EOF(inFile) do
  begin
    readln(inFile, birdName);
    // Parse comma-separated data
    writeln('Data: ', birdName);
  end;
  
  close(inFile);
end.

Text files use Text type. assign connects variable to file. rewrite creates new file. reset opens for reading. append adds to end. Always close files when done.

2. Typed Files

Files with records

Typed files store data in the native format of the type, often records.

Typed files are declared with file of type. rewrite creates new file. reset opens existing file. write writes records. read reads records. Seek moves to a position.

Code Example

program TypedFileDemo;

type
  BirdRecord = record
    id: Integer;
    species: String[20];
    count: Integer;
    weight: Real;
    isMigratory: Boolean;
  end;
  
  BirdFile = file of BirdRecord;

var
  // ---- FILE VARIABLE ----
  birdFile: BirdFile;
  
  // ---- RECORD VARIABLES ----
  bird: BirdRecord;
  birds: array[1..5] of BirdRecord;

begin
  // ---- CREATING TYPED FILE ----
  writeln('--- Creating Typed File ---');
  assign(birdFile, 'birds.dat');
  rewrite(birdFile);
  
  // Prepare data
  birds[1].id := 1;
  birds[1].species := 'Sparrow';
  birds[1].count := 10;
  birds[1].weight := 25.5;
  birds[1].isMigratory := True;
  
  birds[2].id := 2;
  birds[2].species := 'Eagle';
  birds[2].count := 3;
  birds[2].weight := 4500.0;
  birds[2].isMigratory := False;
  
  birds[3].id := 3;
  birds[3].species := 'Hawk';
  birds[3].count := 5;
  birds[3].weight := 1200.5;
  birds[3].isMigratory := True;
  
  // Write records to file
  for var i := 1 to 3 do
    write(birdFile, birds[i]);
  
  close(birdFile);
  writeln('Records written to birds.dat');
  writeln;
  
  // ---- READING TYPED FILE ----
  writeln('--- Reading Typed File ---');
  assign(birdFile, 'birds.dat');
  reset(birdFile);
  
  while not EOF(birdFile) do
  begin
    read(birdFile, bird);
    writeln('ID: ', bird.id);
    writeln('Species: ', bird.species);
    writeln('Count: ', bird.count);
    writeln('Weight: ', bird.weight:0:1);
    writeln('Migratory: ', bird.isMigratory);
    writeln;
  end;
  
  close(birdFile);
  
  // ---- RANDOM ACCESS ----
  writeln('--- Random Access ---');
  reset(birdFile);
  seek(birdFile, 1);  // Move to second record (0-based)
  read(birdFile, bird);
  writeln('Second record: ', bird.species);
  close(birdFile);
end.

Typed files store records directly. rewrite creates new file. reset opens existing file. write writes records. read reads records.

3. Standard Files

input and output

Standard files are predefined file variables connected to console input and output.

input is the standard input file (keyboard). output is the standard output file (screen). Program heading can declare them. Usage is typically implicit.

Code Example

program StandardFiles(input, output);

var
  birdName: String;
  birdCount: Integer;

begin
  // ---- INPUT AND OUTPUT ARE STANDARD ----
  writeln('--- Using Standard Files ---');
  
  // Reading from input (keyboard)
  writeln('Enter bird name: ');
  readln(input, birdName);
  
  writeln('Enter bird count: ');
  readln(input, birdCount);
  
  // Writing to output (screen)
  writeln(output, 'Bird: ', birdName);
  writeln(output, 'Count: ', birdCount);
  
  // ---- WITHOUT SPECIFYING ----
  // writeln and readln implicitly use output and input
  writeln('This uses the standard output');
  
  // ---- REDIRECTING ----
  // In theory, input and output can be redirected
  // from the command line
end.

input and output are standard files. readln without a file variable uses input. writeln without a file variable uses output. They can be explicitly used in the program heading.

Modular Programming and Units

1. Program Structure

program heading, declarations, execution

The structure of a Pascal program has three sections: heading, declarations, and execution.

Program heading starts with program and the program name. Declaration section contains uses, const, type, var, procedure, and function declarations in strict order. Execution section begins with begin and ends with end. with a period.

Code Example

program ProgramStructure(input, output);

uses
  Crt;  // Unit declarations (optional)

const
  MAX_BIRDS = 100;  // Constant declarations
  PI = 3.14159;

type
  BirdRecord = record  // Type declarations
    species: String[20];
    count: Integer;
  end;

var
  sparrow: BirdRecord;  // Variable declarations
  eagleCount: Integer;

// Procedure declaration (optional)
procedure DisplayBird(bird: BirdRecord);
begin
  writeln('Species: ', bird.species);
  writeln('Count: ', bird.count);
end;

// Function declaration (optional)
function GetTotal(birds: array of BirdRecord): Integer;
var
  total: Integer;
  i: Integer;
begin
  total := 0;
  for i := 0 to High(birds) do
    total := total + birds[i].count;
  Result := total;
end;

// ---- EXECUTION SECTION ----
begin
  sparrow.species := 'Sparrow';
  sparrow.count := 10;
  
  DisplayBird(sparrow);
  writeln('Program execution complete.');
end.

The program structure enforces a strict order of declarations. The execution section is the main program body. Units provide external functionality.

2. Units

Separate compilation with units

Units are separate modules that can be compiled independently and reused.

Units have unit keyword. Interface section declares what’s visible. Implementation section contains the code. Initialization and finalization sections run when loading/unloading.

Code Example

unit BirdUnit;

interface
  // ---- PUBLIC SECTION ----
  type
    BirdRecord = record
      species: String[20];
      count: Integer;
      weight: Real;
    end;
  
  procedure DisplayBird(bird: BirdRecord);
  function TotalBirds(birds: array of BirdRecord): Integer;

implementation
  // ---- PRIVATE SECTION ----
  procedure DisplayBird(bird: BirdRecord);
  begin
    writeln('Species: ', bird.species);
    writeln('Count: ', bird.count);
    writeln('Weight: ', bird.weight:0:1);
  end;
  
  function TotalBirds(birds: array of BirdRecord): Integer;
  var
    i: Integer;
    total: Integer;
  begin
    total := 0;
    for i := 0 to High(birds) do
      total := total + birds[i].count;
    Result := total;
  end;

initialization
  writeln('BirdUnit initialized');

finalization
  writeln('BirdUnit finalized');

end.

Units have interface and implementation sections. The interface section declares public functions and types. The implementation section contains the code. initialization and finalization run at program start and end.

3. Uses Clause

Importing units

The uses clause imports units into your program.

uses clause lists unit names. Units are compiled separately. Standard units include crt (console), sysutils (system utilities), and dos (DOS functions). Order can affect identifier resolution.

Code Example

program UsesDemo;

uses
  Crt,         // Console functions
  SysUtils;    // System utilities

var
  currentTime: TDateTime;
  birdName: String;

begin
  // ---- USING CRT UNIT ----
  ClrScr;  // Clear screen
  writeln('--- Using CRT Unit ---');
  writeln('Press any key to continue...');
  ReadKey;  // Wait for key press
  
  // ---- USING SYSUTILS UNIT ----
  currentTime := Now;
  writeln('Current time: ', TimeToStr(currentTime));
  
  birdName := 'Sparrow';
  writeln('Uppercase: ', UpperCase(birdName));
  writeln('Lowercase: ', LowerCase(birdName));
  
  // ---- OTHER COMMON UNITS ----
  // Dos - DOS functions
  // Math - mathematical functions
  // Strings - string utilities
  // System - core functions (always included)
end.

The uses clause imports units. Standard units provide additional functionality. crt provides console control. sysutils provides system utilities.

Object-Oriented Programming (Modern Pascal)

1. Classes and Objects

OOP in Pascal

Classes are object-oriented constructs that encapsulate data and methods.

Classes are declared with class keyword. Objects are instances of classes. Fields are data members. Methods are procedures and functions.

Code Example

program ClassDemo;

type
  // ---- CLASS DECLARATION ----
  TBird = class
  private
    FSpecies: String;
    FCount: Integer;
    FWeight: Real;
  public
    // Constructor
    constructor Create(Species: String; Count: Integer; Weight: Real);
    
    // Methods
    procedure Display;
    function GetDescription: String;
    procedure AddBirds(Number: Integer);
    
    // Properties
    property Species: String read FSpecies write FSpecies;
    property Count: Integer read FCount write FCount;
    property Weight: Real read FWeight write FWeight;
  end;

// ---- CLASS IMPLEMENTATION ----
constructor TBird.Create(Species: String; Count: Integer; Weight: Real);
begin
  FSpecies := Species;
  FCount := Count;
  FWeight := Weight;
end;

procedure TBird.Display;
begin
  writeln('Species: ', FSpecies);
  writeln('Count: ', FCount);
  writeln('Weight: ', FWeight:0:1);
  writeln('Description: ', GetDescription);
end;

function TBird.GetDescription: String;
begin
  Result := FSpecies + ' (' + IntToStr(FCount) + ' birds)';
end;

procedure TBird.AddBirds(Number: Integer);
begin
  if Number > 0 then
    FCount := FCount + Number;
end;

var
  sparrow: TBird;
  eagle: TBird;

begin
  // ---- CREATING OBJECTS ----
  sparrow := TBird.Create('Sparrow', 10, 25.5);
  eagle := TBird.Create('Eagle', 3, 4500.0);
  
  // ---- USING OBJECTS ----
  writeln('--- Sparrow ---');
  sparrow.Display;
  sparrow.AddBirds(5);
  writeln('After adding:');
  sparrow.Display;
  writeln;
  
  writeln('--- Eagle ---');
  eagle.Display;
  
  // ---- PROPERTY ACCESS ----
  sparrow.Count := 20;
  writeln('Updated sparrow count: ', sparrow.Count);
  
  // ---- FREEING MEMORY ----
  sparrow.Free;
  eagle.Free;
end.

Classes encapsulate data and methods. Constructors (Create) initialize objects. Methods operate on the object. Properties provide controlled access to fields. Free destroys objects.

2. Constructors and Destructors

create and destroy

Constructors initialize objects; destructors clean up resources.

Constructors are methods named Create. Destructors are methods named Destroy. Inherited calls parent constructor. Free destroys object safely.

Code Example

program ConstructorDemo;

type
  TBird = class
  private
    FSpecies: String;
    FCount: Integer;
    FWeight: Real;
    FData: ^Integer;  // Dynamic data
  public
    // Constructor
    constructor Create(Species: String; Count: Integer; Weight: Real);
    
    // Destructor
    destructor Destroy; override;
    
    // Methods
    procedure Display;
  end;

// ---- IMPLEMENTATION ----
constructor TBird.Create(Species: String; Count: Integer; Weight: Real);
begin
  inherited Create;  // Call parent constructor
  FSpecies := Species;
  FCount := Count;
  FWeight := Weight;
  
  // Allocate dynamic memory
  New(FData);
  FData^ := Count * 2;
  
  writeln('Constructor: ', FSpecies, ' created');
end;

destructor TBird.Destroy;
begin
  // Clean up dynamic memory
  Dispose(FData);
  
  writeln('Destructor: ', FSpecies, ' destroyed');
  
  inherited Destroy;  // Call parent destructor
end;

procedure TBird.Display;
begin
  writeln('Species: ', FSpecies);
  writeln('Count: ', FCount);
  writeln('Weight: ', FWeight:0:1);
  writeln('Data: ', FData^);
end;

var
  sparrow: TBird;

begin
  writeln('--- Constructor and Destructor ---');
  
  sparrow := TBird.Create('Sparrow', 10, 25.5);
  sparrow.Display;
  
  // Clean up
  sparrow.Free;  // Calls Destroy
  sparrow := nil;
end.

Create initializes objects and allocates resources. Destroy cleans up resources. Free is the safe way to destroy objects. inherited calls parent methods.

3. Inheritance

Deriving classes

Inheritance is a feature that enables a new class to be built from an existing class, allowing it to reuse and extend its properties and behavior.

Derived classes inherit from base classes. inherited calls parent methods. Methods can be overridden. override marks overridden methods.

Code Example

program InheritanceDemo;

type
  // ---- BASE CLASS ----
  TBird = class
  protected
    FSpecies: String;
    FCount: Integer;
  public
    constructor Create(Species: String; Count: Integer);
    procedure Display; virtual;
    function GetDescription: String; virtual;
  end;
  
  // ---- DERIVED CLASS 1 ----
  TEagle = class(TBird)
  private
    FWingspan: Real;
  public
    constructor Create(Species: String; Count: Integer; Wingspan: Real);
    procedure Display; override;
    function GetDescription: String; override;
  end;
  
  // ---- DERIVED CLASS 2 ----
  TSparrow = class(TBird)
  private
    FIsMigratory: Boolean;
  public
    constructor Create(Species: String; Count: Integer; IsMigratory: Boolean);
    procedure Display; override;
  end;

// ---- BASE CLASS IMPLEMENTATION ----
constructor TBird.Create(Species: String; Count: Integer);
begin
  inherited Create;
  FSpecies := Species;
  FCount := Count;
end;

procedure TBird.Display;
begin
  writeln('Species: ', FSpecies);
  writeln('Count: ', FCount);
end;

function TBird.GetDescription: String;
begin
  Result := FSpecies + ' (' + IntToStr(FCount) + ')';
end;

// ---- DERIVED CLASS 1 IMPLEMENTATION ----
constructor TEagle.Create(Species: String; Count: Integer; Wingspan: Real);
begin
  inherited Create(Species, Count);
  FWingspan := Wingspan;
end;

procedure TEagle.Display;
begin
  inherited Display;  // Call base method
  writeln('Wingspan: ', FWingspan:0:1);
end;

function TEagle.GetDescription: String;
begin
  Result := inherited GetDescription + ', Wingspan: ' + FloatToStr(FWingspan);
end;

// ---- DERIVED CLASS 2 IMPLEMENTATION ----
constructor TSparrow.Create(Species: String; Count: Integer; IsMigratory: Boolean);
begin
  inherited Create(Species, Count);
  FIsMigratory := IsMigratory;
end;

procedure TSparrow.Display;
begin
  inherited Display;
  writeln('Migratory: ', FIsMigratory);
end;

var
  bird1: TBird;
  bird2: TBird;

begin
  writeln('--- Inheritance ---');
  
  bird1 := TEagle.Create('Golden Eagle', 3, 230.5);
  bird2 := TSparrow.Create('House Sparrow', 10, True);
  
  writeln('Eagle:');
  bird1.Display;
  writeln;
  
  writeln('Sparrow:');
  bird2.Display;
  writeln;
  
  // ---- POLYMORPHISM ----
  writeln('--- Polymorphism ---');
  writeln('Eagle description: ', bird1.GetDescription);
  writeln('Sparrow description: ', bird2.GetDescription);
  
  bird1.Free;
  bird2.Free;
end.

Derived classes inherit from base classes. inherited calls parent methods. virtual and override enable polymorphism. Derived classes can extend or override behavior.

4. Interfaces

Multiple inheritance alternative

Interfaces define contracts that classes can implement.

Interfaces are declared with interface keyword. Methods are declared but not implemented. Classes implement interfaces. Multiple interfaces can be implemented.

Code Example

program InterfaceDemo;

type
  // ---- INTERFACE DECLARATIONS ----
  IFlyable = interface
    procedure Fly;
    function CanFly: Boolean;
  end;
  
  IBird = interface
    function GetSpecies: String;
    procedure Display;
  end;
  
  // ---- CLASS IMPLEMENTING INTERFACES ----
  TBird = class(TInterfacedObject, IFlyable, IBird)
  private
    FSpecies: String;
    FCount: Integer;
    FCanFly: Boolean;
  public
    constructor Create(Species: String; Count: Integer; CanFly: Boolean);
    
    // IFlyable methods
    procedure Fly;
    function CanFly: Boolean;
    
    // IBird methods
    function GetSpecies: String;
    procedure Display;
  end;

// ---- CLASS IMPLEMENTATION ----
constructor TBird.Create(Species: String; Count: Integer; CanFly: Boolean);
begin
  inherited Create;
  FSpecies := Species;
  FCount := Count;
  FCanFly := CanFly;
end;

procedure TBird.Fly;
begin
  if FCanFly then
    writeln(FSpecies, ' is flying!')
  else
    writeln(FSpecies, ' cannot fly.');
end;

function TBird.CanFly: Boolean;
begin
  Result := FCanFly;
end;

function TBird.GetSpecies: String;
begin
  Result := FSpecies;
end;

procedure TBird.Display;
begin
  writeln('Species: ', FSpecies);
  writeln('Count: ', FCount);
  writeln('Can fly: ', FCanFly);
end;

var
  bird: TBird;
  flyable: IFlyable;
  birdInterface: IBird;

begin
  writeln('--- Interfaces ---');
  
  bird := TBird.Create('Sparrow', 10, True);
  
  // ---- USING INTERFACES ----
  bird.Display;
  writeln;
  
  bird.Fly;
  writeln('Can fly? ', bird.CanFly);
  writeln;
  
  // ---- INTERFACE VARIABLES ----
  flyable := bird as IFlyable;
  flyable.Fly;
  
  birdInterface := bird as IBird;
  writeln('Interface species: ', birdInterface.GetSpecies);
  
  // Free
  bird.Free;
end.

Interfaces define method signatures. Classes implement interfaces. Multiple interfaces can be implemented. as casts to an interface.

Practical Projects

1. Calculator

Basic arithmetic calculator

A simple calculator program that performs arithmetic operations.

Calculator handles addition, subtraction, multiplication, and division. User menu provides a simple interface. Error handling prevents division by zero and invalid input.

Code Example

program Calculator;

var
  num1, num2: Real;
  operation: Char;
  result: Real;

// ---- DISPLAY MENU ----
procedure ShowMenu;
begin
  writeln('--- Simple Calculator ---');
  writeln('Select operation:');
  writeln('+  Addition');
  writeln('-  Subtraction');
  writeln('*  Multiplication');
  writeln('/  Division');
  writeln('Q  Quit');
  writeln;
end;

// ---- GET INPUT ----
procedure GetInput(var a, b: Real);
begin
  write('Enter first number: ');
  readln(a);
  write('Enter second number: ');
  readln(b);
end;

// ---- PERFORM OPERATION ----
function PerformOperation(op: Char; a, b: Real): Real;
begin
  case op of
    '+': Result := a + b;
    '-': Result := a - b;
    '*': Result := a * b;
    '/': 
      if b <> 0 then
        Result := a / b
      else
      begin
        writeln('Error: Division by zero!');
        Result := 0;
      end;
    else
    begin
      writeln('Invalid operation!');
      Result := 0;
    end;
  end;
end;

begin
  writeln('Welcome to the Calculator!');
  
  repeat
    ShowMenu;
    write('Enter operation: ');
    readln(operation);
    
    // Check for quit
    if (operation = 'Q') or (operation = 'q') then
      break;
    
    // Get numbers
    GetInput(num1, num2);
    
    // Calculate result
    result := PerformOperation(operation, num1, num2);
    
    // Display result
    writeln;
    writeln('Result: ', num1:0:2, ' ', operation, ' ', num2:0:2, ' = ', result:0:2);
    writeln;
    
  until false;
  
  writeln('Goodbye!');
end.

The program displays a menu, gets user input, performs the operation, and displays the result. It continues until the user quits.

2. Library Management System

Managing books and members

A library management system tracks books, members, and borrowing transactions.

Books have title, author, and availability status. Members have name, ID, and borrowed books. Operations include adding, searching, borrowing, and returning.

Code Example

program LibrarySystem;

type
  Book = record
    id: Integer;
    title: String;
    author: String;
    isAvailable: Boolean;
  end;
  
  Member = record
    id: Integer;
    name: String;
    borrowedBookId: Integer;
  end;

var
  books: array[1..100] of Book;
  members: array[1..100] of Member;
  bookCount: Integer = 0;
  memberCount: Integer = 0;

// ---- ADD BOOK ----
procedure AddBook;
begin
  bookCount := bookCount + 1;
  with books[bookCount] do
  begin
    id := bookCount;
    write('Enter title: ');
    readln(title);
    write('Enter author: ');
    readln(author);
    isAvailable := True;
  end;
  writeln('Book added successfully!');
end;

// ---- ADD MEMBER ----
procedure AddMember;
begin
  memberCount := memberCount + 1;
  with members[memberCount] do
  begin
    id := memberCount;
    write('Enter name: ');
    readln(name);
    borrowedBookId := 0;
  end;
  writeln('Member added successfully!');
end;

// ---- SEARCH BOOKS ----
procedure SearchBooks;
var
  searchTitle: String;
  i: Integer;
  found: Boolean;
begin
  write('Enter title to search: ');
  readln(searchTitle);
  
  found := False;
  for i := 1 to bookCount do
  begin
    if Pos(searchTitle, books[i].title) > 0 then
    begin
      writeln('ID: ', books[i].id);
      writeln('Title: ', books[i].title);
      writeln('Author: ', books[i].author);
      writeln('Available: ', books[i].isAvailable);
      writeln;
      found := True;
    end;
  end;
  
  if not found then
    writeln('Book not found');
end;

// ---- BORROW BOOK ----
procedure BorrowBook;
var
  bookId, memberId: Integer;
begin
  write('Enter book ID: ');
  readln(bookId);
  write('Enter member ID: ');
  readln(memberId);
  
  if (bookId < 1) or (bookId > bookCount) or
     (memberId < 1) or (memberId > memberCount) then
  begin
    writeln('Invalid ID!');
    exit;
  end;
  
  if not books[bookId].isAvailable then
  begin
    writeln('Book is already borrowed!');
    exit;
  end;
  
  if members[memberId].borrowedBookId <> 0 then
  begin
    writeln('Member already has a borrowed book!');
    exit;
  end;
  
  books[bookId].isAvailable := False;
  members[memberId].borrowedBookId := bookId;
  writeln('Book borrowed successfully!');
end;

// ---- RETURN BOOK ----
procedure ReturnBook;
var
  memberId: Integer;
begin
  write('Enter member ID: ');
  readln(memberId);
  
  if (memberId < 1) or (memberId > memberCount) then
  begin
    writeln('Invalid ID!');
    exit;
  end;
  
  if members[memberId].borrowedBookId = 0 then
  begin
    writeln('Member has no borrowed book!');
    exit;
  end;
  
  books[members[memberId].borrowedBookId].isAvailable := True;
  writeln('Book returned successfully!');
  members[memberId].borrowedBookId := 0;
end;

// ---- MAIN MENU ----
procedure ShowMenu;
begin
  writeln;
  writeln('--- Library Management System ---');
  writeln('1. Add Book');
  writeln('2. Add Member');
  writeln('3. Search Books');
  writeln('4. Borrow Book');
  writeln('5. Return Book');
  writeln('6. Exit');
  write('Select option: ');
end;

var
  choice: Integer;

begin
  writeln('Welcome to Library Management System!');
  
  repeat
    ShowMenu;
    readln(choice);
    
    case choice of
      1: AddBook;
      2: AddMember;
      3: SearchBooks;
      4: BorrowBook;
      5: ReturnBook;
      6: writeln('Goodbye!');
      else writeln('Invalid option!');
    end;
    
  until choice = 6;
end.

The system manages books and members with CRUD operations. Books have availability status. Members can borrow and return books.

Final Advice

Pascal feels structured at first because it forces you to follow the rules—but that discipline is exactly what makes it such a powerful language to learn. Every other language you learn after this one will feel easier, because you’ll already understand the fundamental concepts: variables, data types, control structures, procedures, functions, and pointers.

Begin with the first stage today by running “Hello, World!” yourself in a terminal on your own computer.Then break it on purpose—remove a semicolon, misspell writeln, forget a begin—and read the resulting error message slowly and carefully instead of panicking.

That single habit, repeated consistently over weeks and months, is genuinely how every strong Pascal programmer built their foundation.

Use the AI prompts provided for each concept. They’re designed to give you code examples, clear explanations, and practical exercises. Copy them into your favorite AI assistant and work through the examples yourself. Write the code, run it, modify it, break it, and fix it.

Good luck, and welcome to Pascal. The journey is structured, but the destination is worth it.

Scroll to Top