Pascal
Pascal is a structured, high-level programming language developed to promote clear program design, disciplined coding practices, and efficient problem-solving. It became widely known for its strong emphasis on structured programming and type safety, making it particularly useful for teaching fundamental programming concepts. Pascal also influenced the development of later programming languages and played an important role in the history of software development.
This section explores the core features of Pascal, including its syntax, data types, control structures, procedures, functions, and structured programming approach. It also examines Pascal’s strengths, limitations, historical significance, and common applications, providing a foundation for understanding how the language contributed to the evolution of modern programming practices.

Introduction To Pascal
a
- Complete Pascal Programming Learning Roadmap
- Foundations
- Introduction: Why Pascal Still Matters in 2026
- Chapter 1: What Is Pascal, Really?
- Chapter 2: A Short but Important History
- Chapter 3: Pascal vs. C—What's Actually Different?
- Chapter 4: Why Learn Pascal?
- Chapter 5: What You Need Before Starting
- Chapter 6: Complete Setup Guide for Windows, Mac, and Linux
- Chapter 7: What Actually Happens When You Compile?
- Chapter 8: Using AI the Right Way While Learning Pascal
- Basic Syntax, Variables, Constants, and Data Types
- Control Structures
- Procedures and Functions
- Arrays and Strings
- Records, Sets, and Enumerations
- Pointers and Dynamic Memory
- File Handling
- Modular Programming and Units
- Object-Oriented Programming (Modern Pascal)
- Practical Projects
- Final Advice
- Foundations
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
Grouping related data
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.