Troubleshooters.Com® and Code Corner Present:

Pascal Intro

See the Troubleshooters.Com Bookstore.

Articles:

(Grayed out articles are not yet complete)

Hello World

Create the following hello.pas:

program hello;
begin
   writeln('Hello world.');
end.

Now compile it:

[slitt@mydesk pascal]$ fpc hello.pas
Free Pascal Compiler version 3.2.0 [2020/09/28] for x86_64
Copyright (c) 1993-2020 by Florian Klaempfl and others
Target OS: Linux for x86-64
Compiling hello.pas
Linking hello
4 lines compiled, 0.1 sec
[slitt@mydesk pascal]$ 

Now run the program you just compiled:

[slitt@mydesk pascal]$ ./hello 
Hello world.
[slitt@mydesk pascal]$

fp, The Free Pascal IDE

Just like ancient Turbo Pascal, Free Pascal comes with an IDE (Integrated Development Environment) that runs in a terminal. It is not a GUI program. To invoke it, just issue the fp <sourcefile> command. If this is the first time you've run fp in this particular directory, you'll be presented with a choice screen as shown in the following screenshot:

fp screen when fp is run for the first time in a given directory

The IDE is mouse-aware, so click the "Yes" button.

Next you're asked whether you want to copy the existing configuration or create a new one. Unless you really know what you're doing, choose to copy the existing one.

From this point on, you can do things like make, compile, run, link, etc. It has a built in editor resembling the old Turbo Pascal editor which itself resembled the ancient Wordstar word processor. Just like Turbo Pascal and Delphi before it, this IDE is very productive, but because it's so different from Vim, emacs and other modern editors (yeah, I called Vim and Emacs modern), the IDE has a learning curve.

One downside of the IDE is it assumes a directory contains a project, so only one main Pascal program can be in the directory: Other Pascal programs would be linked in unit files, etc.

If you're going to make big Free Pascal projects, and especially if you'll be using the Lazarus Delphi clone, you should familiarize yourself with this IDE and its high productivity. But for the purpose of this document, this will be the last time the fp IDE is mentioned.

Pascal Comments

Comments are either old-style Paren and asterisk, as in (*This is is a  comment*), or the newer curly brace comments as in {This is is a  comment}. Comments of one type can be nested within the other, but trying to nest a comment of one type within a comment of that same type produces a warning. Personally, I use old-style paren and star comments to comment out large chunks temporarily, and curly brace comments to comment out shorter stuff. This way I can use commenting both as a comment and as a diagnostic technique.

C Developers Beware!

When switching between C and Pascal you might temporarily forget by accident that in Pascal curly braces are comments and don't begin and end a block. In Pascal, beginning and ending a block are done with the begin and end keywords.

Under certain conditions, a double slash (// can be used to comment out everything to the end of one line.The ability to use these doubleslash comments depends on which Compiler Directives you're using.

Compiler Directives

Compiler Directives take the following form:

{$DIRECTIVENAME VALUE}

Specifically, a directive name starts with a left curly brace, then immediately a dollar sign, and then immediately a valid directive name. The directive name might or might not be followed by a space and a value, depending on the directive name. Finally, the compiler directive closes with a right curly brace.

Compiler Directives Aren't Comments!

Most Pascal compilers, in particular Turbo Pascal and Free Pascal, have Compiler Directives to tell how to compile. They look like {$DIRECTIVENAME VALUE}, where "DIRECTIVENAME" is the name of a valid directive, and "VALUE" is a value for the directive. A compiler directive must start with a left curly brace, then a dollar sign, then the name of a directive, with no intervening spaces. Intervening spaces would make it a comment and therefore disable it.

Structure of a Pascal Program

First things first: Pascal keywords and variable names are case insensitive. The language is not indentation dependent, a fact that Python haters will love. It's also space independent as long as two identifier names aren't put together, so the following two lines compile to the exact same executed binary:

Pascal programs have a more definite structure than C, Perl, Python, Ruby, and Lua. You can think of a Pascal program as a single sentence, that begins with the word program and ends with a period. Within this sentence there are many, many clauses that end in semicolons, similar to C.

More than modern languages, Pascal tends to do things in a specific order, something like the following:

The preceding doesn't include all sections you can have, but it includes the most common. Note that every function and procedure has its own sections for its local variables and perhaps its local constants and types, although constants and types are probably best left global. Global variables, on the other hand, are often a bad idea in any programming language.

The following is a trivial example program showcasing the typical Pascal structure:

program structure;       {start of program}
uses getopts, sysutils;  {uses section}
const                    {constants section}
    phone_number_size = 10;
type                     {type section}
    phone_number = array[1..phone_number_size] of char;
    personrecord = record
    name: string;
    phone: phone_number;
    end;
var                      {variables section}
    person: personrecord;

{DECLARE FUNCTIONS AND PROCEDURES}
function modperson(person: personrecord;
    name: string;
    phone: phone_number ): personrecord;
begin
    person.name := name;
    person.phone := phone;
    modperson := person;
end;

procedure writeperson(person: personrecord);
begin
    writeln(person.name, ' has phone number ', person.phone, '.');
end;
{END OF FUNCTION AND PROCEDURE DECLARATIONS}


begin     {Start of main program execution}
    person := default(personrecord);
    person := modperson(person, 'Wilson Pickett', '5556345789');
    writeperson(person);
end.      {end of entire program}

The following are a few observations about the preceding program:

Enabling Range Checking

Some of the most time consuming intermittent problems with a program are caused by the program reading or writing memory that doesn't belong to it. For instance, reading or writing past the allocated size of an array. Free Pascal has a Compiler Directive, called $rangechecks, to enable run time range checking. With such range checking, the program errors instantly when such a read or write occurs, instead of waiting to error out somewhere not even remotely near the illegal memory access, or worse, intermittently erroring out, or worst of all not erroring out at all but delivering wrong answers. In order to save huge amounts of time, I suggest you always develop with range checking on, always test with it on, always beta test with it still on, and even deploy with it on, at first.

All you need to do to enable range checking is include the following line at or near the top of the program:

{$rangechecks on}

Once you're confident that there are no illegal memory accesses, you can turn range checking off (change "on" to "off") to speed up the program to a degree. This being said, if a program runs "fast enough" with range checking on, I just leave it on. By "fast enough", I typically mean an interactive program in which the bottleneck is human input, and the time to complete action on the human input seems "instantaneous" (less than 1/4 second).

Intermission: About Steve Litt

I'm Steve Litt. I took a Pascal course at Santa Monica College (SMC) in the fall of 1982, and also took several Cobol courses in 1982, 83 and 84. During this time I operated Steve's Stereo Repair out of my apartment.

In March 1984 I saw a hand-written sign in the SMC computer lab, asking for a Pascal programmer. I called the number and got the job, which turned out to be programming Whitesmith Pascal on a PDP11 computer. Whitesmith Pascal took ages to compile and intermittently bombed out of compilation on files that would other times compile perfectly.

In December 1984 I purchased a Kaypro 2x CPM computer with a Juki daisy wheel printer, and also bought Turbo Pascal 2.0 for CPM. On the PDP11 at work Pascal compilation took at least a minute, often several minutes. At home on my dual-floppy-no-hard-disk Kaypro it typically took less than 10 seconds. Turbo Pascal compiled correct code every time, and Turbo Pascal allowed you to access individual memory locations, which was a must back in the days of CPM and MS-DOS (remember B800?). Turbo Pascal was spectacular.

In 1985 I started doing rudimentary C coding, wanting to learn C because it was much easier to make money with C. For me, C was much more difficult to learn and to program than Pascal. Pascal's extremely strict typing meant mistakes were found at compile time, not found out in a bug report. Turbo Pascal was everything I could want in a compiled computer language.

In early 1986 my client decided to replace their PDP11 based medical management software on IBM AT clones. Unfortunately for them, the language they chose was PC-FOCUS, a low performance, very restrictive 4th Generation Language (4GL) that was trivial to do simple things with and a monstrosity to do semi-difficult stuff. Our programmers couldn't make PC-FOCUS create the sophisticated reports our clients demanded, so I wrote Turbo Pascal programs to format PC-FOCUS data into the needed report. Later that year they called on me to create the client of what a few years later would be called a client/server system, using Turbo Pascal, Turbo Async, Kermit, and Opttech Sort.

The next few years saw me solving my various clients' problems with Turbo Pascal. In the meantime my professional development expanded into C, C++, Clarion, Powerbuilder, WordPerfect macros, Perl, Ruby, and even a little Microsoft Assembler. My final professional Turbo Pascal project was with Object Oriented Turbo Pascal 5.5 in March, April and May of 1993, after which I took off 11 months to help care for our very premature and very sick triplets.

Upon getting back to work in April 1994 it was obvious the development world had changed. Everything was C, C++, and some 4GLs and report writer packages. You did C or C++ if you wanted to make money in the microcomputer sphere. MS-DOS was no longer a thing: Windows was now the corporate operating system. Admitting you did Turbo Pascal was bad for the resume: Almost as bad as admitting you'd worked with Cobol. I kept my mouth shut about Pascal.

The C language improved, the C compilers improved, and I improved. My C productivity was recognized far and wide. Slowly I forgot my precious Turbo Pascal. Around the turn of the century, my business slowly transitioned from free lance analysis and programming to teaching of the Universal Troubleshooting Process, and my website, Troubleshooters.Com. Most of my Troubleshooters.Com programming was done with loosely typed languages: Perl, Python, Ruby and Lua, along with Linux shellscripts. I dabbled in Lazarus, a Borland Delphi clone based on Pascal, but so much of my Pascal was forgotten that only a few Lazarus-based programs were completed.

In 2023 I got interested in Lazarus again, leading to an intense nostalgic longing for Turbo Pascal and therefore an interest in FreePascal. So I'm relearning Pascal, using this web page as a storehouse for what I've relearned. Hopefully others will benefit from this web page as much as I do.

If Statements

Every major computer language has a way to accomplish branching. The word "branching" refers to making the order of commands performed depending on a condition: Typically the value of a variable. The main way Pascal implements branching is with the if statement. The simplest form of the if statement follows:

if <condition> then
   <command>;

In the preceding the command runs only if the condition is true. Note that the preceding code could have been put on one line.

With its begin and end keywords Pascal can package several commands into one command. So considering this, the following code follows the same principles as the previous if statement code:

if <condition> then
    begin
    <command1>;
    <command2>;
    <command3>;
    end;

Like most languages, Pascal gives you an else to go along with your if, as follows:

if <condition> then
    <command1>
else
    <command2>;

Landmine alert!

Note the lack of a semicolon after command1. Adding a semicolon there causes a compilation error. If you're accustomed to C, this is very unexpected. In C you almost can't use too many semicolons. Pascal is very picky about where you do and don't use semicolons.

My best guess on the motivation for this pickiness is that the entire if/else statement is considered one phrase to be ended with a semicolon, but the else part is integral to the phrase.

Note that if command1 is changed to a compound statement with a begin and an end statement, the end does not end with a semicolon. There's a logic to all of this, but it takes some thought.

Pascal gives a method to choose between several alternatives, as follows:

if <condition1> then
    <command1>
else if <condition2> then
    <command2>
else if <condition3> then
    <command3>
else
    <command4>;

Once again notice that the commands following the if and else if conditions don't end in semicolons, but the one for the else condition does. This isn't a function of else, but instead is a function of what ends the entire compound if. For instance, you could leave off the final else section, in which case it would be as follows:

if <condition1> then
    <command1>
else if <condition2> then
    <command2>
else if <condition3> then
    <command3>;

In the preceding, there's a semicolon after command 3 because command 3 ends the entire if assembly.

Just so we're all on the same page, let's change command1 to a compound command and illustrate that there's no semicolon after the compound command's end:

if <condition1> then
    begin
    <command1a>
    <command1b>
    <command1c>
    end
else if <condition2> then
    <command2>
else if <condition3> then
    <command3>;

The Case Statement

To help with readability, Pascal offers the case statement, shown in the following code of a full program:

program casehello;
var mychar: char;

begin
mychar := 'b';
case mychar of
    'a': writeln('a');
    'b': writeln('b');
    'c': writeln('c');
end;
end.    

Bizarre terminology alert!

The stuff appearing before the colon in each choice is called a label, not only in documentation, but in error messages. Is it just me, or is this a bizarre word for it? In just about every other language, a label is an unquoted string that a goto jumps to. For those whose first language isn't Pascal, I'd call this misleading. Personally I'd call it a value or a condition set or something. However, to keep in step with the rest of the Pascal world, I'll call these things "labels".

A few things to notice. First, unlike the C switch statement, you don't need to insert all those lame break; statements. If each command is simple, it's ultimately readable. The case keyword is ended by an end;, without any begin. If I'd had my druthers, they would have had a endcase; keyword to bookend the case, but no language is perfect. Every alternative consists of a value, a colon, and a command (which can be a compound command bookended by begin and end, and finally a semicolon. If there are too many compound commands, it starts eroding the readability of case over if. And remember, the preceding is a whole program, which is why it's bookended by program and a period.

To showcase the versatility of Pascal case statements and also to reveal some other Pascalisms, take a long look at the following full program:

program casehello2;
var mychar: char;

begin
mychar := '`';
case mychar of
    'a': writeln('a');
    'b': writeln('b');
    'c': writeln('c');
    'd' .. 'z': writeln('Lower case, not a through c');
    '0' .. '9': writeln('Decimal numeral');
    chr(0) .. chr(31): writeln('Control character or tab');
    chr(32): writeln('Space');
    '"', '`', '''': writeln('One of the many quote characters');
    else writeln('Probably punctuation');
end;
end.

The preceding shows that case labels can be values, ranges, or comma delimited lists. This fact makes them extremely versatile. Also notice that .. is used to denote a range, in the comma delimited list in the final label, the commas act logically like or, but replacing them by or creates a compiler error. Finally notice that to include a single quote in a string, you use a double single quote, as in ''''. Unlike Python, you can't use double quotes to delineate a string in Pascal.

Surprising syntax:

Every label is terminated by a colon, but the else is not terminated by a colon and terminating it with a colon creates a compiler error.

Arrays

Like most other computer languages, Pascal has arrays. An array is an ordered list of things of the same type. An ordered list of integers, or of strings, or of same-type records, or objects made from the same class.

The strong typing of array elements can be an inconvenience, compared to the loosy-goosy "anything goes" non-restrictions on array elements in Python. If you really need the convenience of anything goes array elements, you'd need to make each element a pointer and then use type casting and then grab the memory for each out of the heap. Ugh!

The following program loads an array of strings and then uses a loop to iterate through that array:

program temp;

var
    arr: array[1..3] of string;
    numstring: string;

begin
arr[1] := 'One';
arr[2] := 'Two';
arr[3] := 'Three';

for numstring in arr do
    writeln(numstring);
end.    

The preceding code produces the following output:

[slitt@mydesk pascal]$ ./temp
One
Two
Three
[slitt@mydesk pascal]$

The for-in loop is a great way to iterate through arrays.

The preceding code shows how to set the value of an array element:

arr[2] := 'Two';

You can also read the value of a specific element, as follows:

mystring := arr[2];

Loops

Computers excel in doing lots of repetitive things fast, and that means loops. All Turing Complete languages have some form of looping. Pascal can loop in the following ways:

while do loops

while do loops are test at top loops. The following is a flowchart of a while do loop:

A while do loop, which tests at the top

As you can see from the preceding flowchart, the test is done before the loop body. Upon a little thought it becomes apparent that if the test is false going in, the loop body might never be done, which might or might not be what you want. The following is a simple sample of the while do loop:

program whiledo;
var num: integer;

begin
num := 4;
while num > 0 do
    begin
    write(num, '   ');
    num := num -1;
    end;
writeln('');
end.  

As you'd expect, the preceding program outputs the following:

[slitt@mydesk pascal]$ ./whiledo
4   3   2   1   
[slitt@mydesk pascal]$

If num had started out zero or negative, the loop body would have been skipped, so absolutely nothing would have printed.

repeat until loops

repeat until loops are test at bottom loops. The following is a flowchart of a repeat until loop:

A repeat until loop, which tests at the bottom

As you can see from the preceding flowchart, the test is done after the loop body. Upon a little thought it becomes apparent that if the test is false going in, the loop body still gets done, which might or might not be what you want.

To demonstrate a repeat until loop, consider the following program:

program repeatuntil;
var num: integer;

begin
num := 4;
repeat
    begin
    write(num, '   ');
    num := num - 1;
    end;
until num < 1;
writeln('');
end.    

The preceding program prints out the same numbers as the while do based program:

[slitt@mydesk pascal]$ ./repeatuntil 
4   3   2   1   
[slitt@mydesk pascal]$

However, if num had been initialized to, let's say, -5, then the while do version would have printed nothing, whereas the repeat until version would have printed out -5.

Without going into details, anything you can do with a while do loop you can do with a repeat until, and vice versa. Sometimes one is more convenient than the other. Personally, I use while do loops unless I have a good reason to use a repeat until loop.

for do loops

DANGER!

Never, ever tamper with the loop control integer within the loop body. I know doing so works just fine in a C for loop, but with a Pascal for do, is either a compiler error, or much worse, on old Pascal compilers (not FreePascal), the result is undefined and will almost certainly lead to runtime bugs.

The following is a nobrainer simple example of two for do loops, one counting up, and the following counting down:

program fordo;
var num: integer;

begin
for num := 1 to 4 do
    write(num, '    ');
writeln;
for num := 4 downto 1 do
    write(num, '    ');
writeln;
end.    

The unsurprising output follows:

[slitt@mydesk pascal]$ ./fordo
1    2    3    4    
4    3    2    1    
[slitt@mydesk pascal]$

It's trivial to loop through a string with a for do loop, as illustrated with the following short program:

program fordo;
var strng: string = 'Pascal is great!';
var letter: char;

begin
for letter in strng do
    write(letter, '_');
writeln;
end.

See how simple that was? The output follows:

[slitt@mydesk pascal]$ ./fordo
P_a_s_c_a_l_ _i_s_ _g_r_e_a_t_!_
[slitt@mydesk pascal]$ 

Same with arrays:

program fordo;
var words: array[1..6] of string;
var ss: integer; 

begin
for ss := 1 to 6 do
    words[ss] := '~';
words[1] := 'One';
words[2] := 'Two';
words[3] := 'Three';
for ss := 1 to 6 do
    if words[ss] = '~' then
        break
    else
        write(words[ss], '    ');
writeln;
end.
    

for in loops

for in loops

The following is a nobrainer simple example of two for do loops, one counting up, and the following counting down:

program fordo;
var num: integer;

begin
for num := 1 to 4 do
    write(num, '    ');
writeln;
for num := 4 downto 1 do
    write(num, '    ');
writeln;
end.    

The unsurprising output follows:

[slitt@mydesk pascal]$ ./fordo
1    2    3    4    
4    3    2    1    
[slitt@mydesk pascal]$

It's trivial to loop through a string with a for do loop, as illustrated with the following short program:

program fordo;
var strng: string = 'Pascal is great!';
var letter: char;

begin
for letter in strng do
    write(letter, '_');
writeln;
end.

See how simple that was? The output follows:

[slitt@mydesk pascal]$ ./fordo
P_a_s_c_a_l_ _i_s_ _g_r_e_a_t_!_
[slitt@mydesk pascal]$ 

Same with arrays:

program fordo;
var words: array[1..6] of string;
var ss: integer; 

begin
for ss := 1 to 6 do
    words[ss] := '~';
words[1] := 'One';
words[2] := 'Two';
words[3] := 'Three';
for ss := 1 to 6 do
    if words[ss] = '~' then
        break
    else
        write(words[ss], '    ');
writeln;
end.
    

Recursion loops

Loops can be performed by recursion, although loops iterated a huge number of times through recursion tend to evaporate the stack and crash unless you use optimized tail recursion.

Tail recursion loops are explained in the Tail Recursion section. Before reading that section, please first read the section titled Recursion Hello World.

Nested Loops

Most computer programs have loops within loops. The following is a trivial nested loop that actually has some practical applications making test data sets:

program nestedloop;
type
    threestring = array[1..3] of string;

var
    firstnames, lastnames: threestring;
    fss, lss: integer;

begin
firstnames[1] := 'Albert';
firstnames[2] := 'Betty';
firstnames[3] := 'Carlos';
lastnames[1] := 'Anderson';
lastnames[2] := 'Barnett';
lastnames[3] := 'Cederberg';
for fss := 1 to 3 do
    begin
    for lss := 1 to 3 do
        writeln(firstnames[fss], ' ', lastnames[lss]);
    end;
end.   

The output follows:

[slitt@mydesk pascal]$ ./nestedloop 
Albert Anderson
Albert Barnett
Albert Cederberg
Betty Anderson
Betty Barnett
Betty Cederberg
Carlos Anderson
Carlos Barnett
Carlos Cederberg
[slitt@mydesk pascal]$

For every first name it iterates through all last names.

Let's add a little more. Let's make a two column report, and add an employee number to be listed in front of the name:

program nestedloop;
type
    threestring = array[1..3] of string;

var
    firstnames, lastnames: threestring;
    fss, lss: integer;
    empno: integer = 1000;
    sstemp: integer;

begin
firstnames[1] := 'Albert';
firstnames[2] := 'Betty';
firstnames[3] := 'Carlos';
lastnames[1] := 'Anderson';
lastnames[2] := 'Barnett';
lastnames[3] := 'Cederberg';
for fss := 1 to 3 do
    begin
    for lss := 1 to 3 do
        begin
        empno := empno + 1;
        write(empno, ': ', firstnames[fss], ' ', lastnames[lss]);
        if odd(empno) then
            for sstemp := 24 downto length(firstnames[fss]) + length(lastnames[lss]) + 5 do
            write(' ')
        else
            writeln;
        end;
    end;
writeln;
end.

The preceding program contains a triply nested loop. The outermost iterates over first name, the next one in iterates over last name, and the innermost iterates the writing of spaces to keep the rightmost column in line. Each person gets an employee number that goes in front of his or her name.

Did you notice that I declared and initialized integer empno with a line like the following:

empno: integer = 1000;

You can do that in Pascal, but be sure to use just an equal sign, and not a colon equal when initializing in the declaration of a variable. The preceding program prints out the following output:

[slitt@mydesk pascal]$ ./nestedloop 
1001: Albert Anderson      1002: Albert Barnett
1003: Albert Cederberg     1004: Betty Anderson
1005: Betty Barnett        1006: Betty Cederberg
1007: Carlos Anderson      1008: Carlos Barnett
1009: Carlos Cederberg     
[slitt@mydesk pascal]$

Multidimensional Arrays

A two dimensional array is an array of arrays. A three dimensional array is an array of arrays of arrays.

This section gives an example of a theater that holds sixteen people. There are four rows with four seats each. This theater is represented by a two dimensional array of rows, and then seats within each row. It outputs a seating chart of the ticket holders, with both their names and their seat assignments. Seat assignments are designated by a letter representing the row and a number representing the seat. They go from A1 through D4.

The theater is declared in the type and var sections. The executable section starts by filling every seat one by one with attendees' names.

Then it iterates through rows and then seats in order to print the seating chart. The end result looks like the following:

[slitt@mydesk pascal]$ ./multidim 
A1: Albert     A2: Betty      A3: Carlos     A4: Denise
B1: Edward     B2: Felise     B3: George     B4: Helen
C1: Ian        C2: Janice     C3: Ken        C4: Lisa
D1: Mark       D2: Nancy      D3: Oscar      D4: Patricia
[slitt@mydesk pascal]$

The program that created the preceding seating chart follows:

program multidim;
type
    trow = array[1..4] of string;
    ttheater = array[1..4] of trow;

var
    theater: ttheater;
    row: integer;
    seat: integer;
    sstemp: integer;

begin
theater[1,1] := 'Albert';
theater[1,2] := 'Betty';
theater[1,3] := 'Carlos';
theater[1,4] := 'Denise';
theater[2,1] := 'Edward';
theater[2,2] := 'Felise';
theater[2,3] := 'George';
theater[2,4] := 'Helen';
theater[3,1] := 'Ian';
theater[3,2] := 'Janice';
theater[3,3] := 'Ken';
theater[3,4] := 'Lisa';
theater[4,1] := 'Mark';
theater[4,2] := 'Nancy';
theater[4,3] := 'Oscar';
theater[4,4] := 'Patricia';

for row := 1 to 4 do
    begin
    for seat := 1 to 4 do
        begin
        write(chr(ord('A') + row - 1));
        write(seat, ': ');
        write(theater[row, seat]);
        if seat = 4 then
            writeln
        else
            for sstemp := 10 downto length(theater[row, seat]) do
            write(' ');
        end;
    end;
end.

A few things to keep in mind about the preceding code:

Functions and Procedures

One of the many ways modern (the past half century) developers create reliable, robust and maintainable software is by successively splitting the software into smaller, simpler and more modular pieces. One great way to do this is by splitting the software into successively simpler functions and procedures.

Short Definition of a Procedure

A procedure is a hunk of source code that gets called by other source code to perform a task. Upon completion of that task, control automatically returns to the line of source code after the one that called the procedure. Procedures can be coded such that they take arguments, which are values or other data the procedure needs in order to do its job. Procedures can also look at global variables in order to do their jobs, but global variables reduce modularity, make debugging more difficult, and are usually a bad idea.

The following is a tiny example of a procedure with no arguments:

program temp;

procedure printmessage;
    begin
    writeln('This was printed by printmessage.');   
    end;

begin
    printmessage;
    printmessage();
end.    

The preceding code prints out the following output:

[slitt@mydesk pascal]$ ./temp
This was printed by printmessage.
This was printed by printmessage.
[slitt@mydesk pascal]$ 

The following are some takeaways from the preceding program and output:

Procedures With Arguments

Note:

In Pascal, the words "argument" and "parameter" are pretty much synonymous. In this document I call them "arguments" because many readers use the word "arguments" because they're familiar with other languages, but please be aware that either is correct.

The following is a trivial example of passing an argument to a procedure:

program temp;
var i: integer;

procedure printnumber(num: integer);
    begin
    writeln('The number is ', num, '.');   
    end;
begin
for i := 1 to 3 do
    printnumber(i);
end.    

The preceding code yielded the following output:

[slitt@mydesk pascal]$ ./temp
The number is 1.
The number is 2.
The number is 3.
[slitt@mydesk pascal]$

So in the preceding program and output, the action of the procedure depended on the argument. Note that there could have been loops or if statements that would have totally changed the actions of the procedure depending on the argument(s).

There are two ways to send an argument to a Pascal procedure:

These are discussed later in this document.

Functions

In Pascal, a function is a procedure that returns a result via a function return. The following is a trivial example:

program temp;
var i, n: integer;

function multiply_by_two(num: integer): integer;
    begin
    multiply_by_two := num * 2;
    end;

procedure printnumber(num1, num2: integer);
begin
    writeln(num1, ' times two equals ', num2, '.');
end;

begin
for i := 1 to 3 do
    printnumber(i, multiply_by_two(i));
writeln;
for i := 1 to 3 do
    begin
    n := multiply_by_two(i);
    printnumber(i, n);
    end;
end. 

The preceding code yields the following output:

[slitt@mydesk pascal]$ ./temp
1 times two equals 2.
2 times two equals 4.
3 times two equals 6.

1 times two equals 2.
2 times two equals 4.
3 times two equals 6.
[slitt@mydesk pascal]$

In the preceding program and output, notice the following:

Mathematical Definition Of a Function

In math, the definition of a function is an algorithm that takes input and outputs output. It does nothing else except output the output, and its actions depend only on its inputs. For example, take the math function x2. Its output depends exclusively on its input. If x is 1 the value of the function is 1. Here are some others:

x    x2
11
24
39
416

Pascal functions can resemble math functions if their function return depends only on their input and they do nothing but return an output value. Such functions are called pure functions. Observe the following example:

function mysquare(x:int64): int64;
   begin
   mysquare := x*x;
   end;

Observe the preceding function. If it were to write to the screen or a file or a printer or a website it wouldn't have been pure. Likewise, if it had depended on a global variable or the time of day it wouldn't be a pure function. For further information see this detailed treatise of pure functions, as well as a suggested language enhancement that never got done.

In my opinion, all other things being equal, a pure function is better than an impure function. If it's going to read or write to file, screen, stdout or socket, why not make it a procedure? Please remember that procedures can return a value through a pass-by-reference argument, which is discussed later in this document. Notwithstanding this paragraph, if you feel a need to make a function impure, that's not a crime, do it.

Pure functions are necessary for Functional Programming, which is beyond the scope of this document.

Local Functions and Procedures

Pascal allows you to declare functions or procedures inside of functions or procedures. See the following example:

program temp;

procedure printnames(fname, lname: string);
   procedure print_last_name(lname: string);
      begin
      writeln(lname);
      end;
   begin
   write(fname, ' ');
   print_last_name(lname);
   end;
begin
printnames('Abe', 'Lincoln');
{An "identifier not found" is produced}
{if you uncomment the next line}
{print_last_name('Washington');}
end.

Observe the preceding code carefully. Notice that procedure print_last_name is declared inside procedure printnames. This has the effect of making print_last_name callable only from within printnames. If it's called from outside of printnames the "identifier not found" error results, because identifier print_last_name is known only inside of procedure printnames. This can be done with functions too, or with combinations of functions and procedures. The preceding program outputs the following:

[slitt@mydesk pascal]$ ./temp
Abe Lincoln
[slitt@mydesk pascal]$

The outer procedure printed the first name, and the inner procedure printed the last name.

So why would anyone want to declare a local procedure or function within a procedure or function? One reason is the local procedure or function is called only from the outer procedure or function, and you don't want a future maintenance programmer calling the inner function or procedure directly. Similar to the rationale behind local variables, although global procedures and functions aren't nearly as harmful as global variables.

Another reason is to perform a closure, but this is beyond the scope of this document.

Passing By Reference and By Value

Pascal has two methods of passing arguments into a function or procedure:

  1. By value, the default.
  2. By reference, requiring the var keyword.

Passing by value is what's been done in this document up to this point. When you pass by value, the procedure or makes its own copy of the argument, and can change that argument to its heart's content, but after the call to the procedure or function, the value of the argument remains what it was before the procedure or function was called. However, when you pass by reference, the called procedure or function works on the actual argument, not a copy thereof. This means that after return from the procedure or function, the value of the passed in argument is what the procedure or function changed it to. There will be code examples later in this section.

I can think of three reasons why you'd pass by reference:

  1. Performance concerns.
  2. You want to return a value via the passed by reference argument, rather than by function return.
  3. You want to return two or more different data pieces and don't want to return them as a record or array.

Let's say you had loop calling a function with a pass-by-value argument of a huge data record. Because every invocation of the procedure or function makes another copy of the huge data record, your RAM and CPU are taxed, and the program takes longer to run. If you passed by reference, performance would be enhanced.

Then there's the case in which you have one huge data record that must be modified by many different procedures or functions. Sometimes it's easier to simply have a procedure change a component of the referenced record.

Intermission: I Don't Like Pointers

When learning C after using Pascal professionally for a few years, my biggest annoyance with C was pointers, and all the baggage they carried with them. The off by one errors. Having to either count very carefully or assign another pointer to the final location you wanted to process, and then later forgetting whether it was the last location to process or the first one that was out of bounds. Having to match types just right or else having to increment a pointer by something other than 1. Can you spell "messy"?

If you forget to initialize a pointer, all hell breaks loose, usually at runtime, often on Friday afternoon when you've started your camping trip but 100 highly paid contractors hired for the weekend are sitting on their hands because your program crashed.

When I first learned C, pointers were a C thing. Thankfully, the Whitesmith Pascal and Turbo Pascal I used didn't have them. Neither did Cobol, or Basic (well, they had peek and poke, but you know what I mean). About the only place you could find pointers other than C was in the various assemblers, where it was called "indirect addressing". And that fact kind of gives you the feel for the priorities of Dennis Ritchie when creating C: A portable assembler.

Hey, I'm not going to gripe about C, having made a heck of a lot more money from C than from any form of Pascal. And if you use C enough, pointers start seeming like a natural part of life, and not an obstacle course of runtime intermittent causing security risks. So why was it, at the height of my very well respected C career, did I long for the Turbo Pascal of my youth? Turbo Pascal's non-reliance on pointers, and the baggage they carried with them, was a big part of that longing.

Today's Pascal enables you to use pointers. And I suppose under certain conditions using those pointers might save a lot of trouble. But for those who really like pointers, why use Pascal? If you're really a pointer fan, why not use C, the language built from the ground up centered on pointers? Or C++, the OOP version of C?

Speaking for myself, when programming Pascal I'll go out of my way to avoid pointers.

The With Statement

I don't use Pascal's with statement often, because in my opinion in long sequences of code it's less readable. This being said, a lot of people like it because it cuts down on the length of chained variables and objects.

Recursion Hello World

Recursion occurs when a function repeatedly calls invokes itself, either directly or indirectly through other functions. Pascal isn't optimized for recursion the way lisp and Guile are, but it can still do recursion. See, compile and run the following trivial recursion example program:

program recursion_hello;

function nextt(num: int64): int64;
begin
   writeln('Going deeper=>   ', num);
   if (num <= 0) then
      begin
      nextt := num;
      end
   else
      begin
      nextt(num - 1);
      nextt := num;
      end;
   writeln('Coming back up=> ', num);
end;

begin
  nextt(10);
end.

Pretty nice, right? Obviously the right way to do this would have been iteratively (iterative means non-recursive), but it makes for a good example.

Tail Recursion In Free Pascal

In the example example in the preceding section, when instead of setting the original nextt() call to 10, I set it to ten million, the compiled program segfaulted. I assume this is because I ran out of stack with all the variables and parameters in each recursion level having their own stack locations.

Some programming languages, such as Scheme/Guile, get around this segfaulting behavior by optimising tail recursion. Tail recursion is recursion in which absolutely nothing is executed after the recursive function's return statement. In a language set up to optimize for tail recursion, a tail-recursive algorithm does not consume extra stack for each level of recursion, so it can go infinitely deep.

It turns out that on modern Free Pascal versions, there's a way to get the compiler to optimize for tail recursion. You simply use the {$optimization tailrec} or {$optimization on} compiler directive at the top of the program. Consider the following program with tail recursion optimized:

{$optimization tailrec}
program tail_recursion_hello;

function nextt(num: int64): int64;
    begin
    writeln('Going deeper=>   ', num);
    nextt := num;
    if (num > 0) then
        begin
        nextt(num - 1);
        end;
    end;

begin
nextt(100000000);
end.    

In the preceding code, notice the following:

You test it as follows:

[slitt@mydesk pascal]$ fpc tail_recursion_hello.pas
Free Pascal Compiler version 3.2.0 [2020/09/28] for x86_64
Copyright (c) 1993-2020 by Florian Klaempfl and others
Target OS: Linux for x86-64
Compiling tail_recursion_hello.pas
Linking tail_recursion_hello
16 lines compiled, 0.1 sec
[slitt@mydesk pascal]$ ./tail_recursion_hello | head -n 5
Going deeper=>   100000000
Going deeper=>   99999999
Going deeper=>   99999998
Going deeper=>   99999997
Going deeper=>   99999996
[slitt@mydesk pascal]$ ./tail_recursion_hello | tail -n 5
Going deeper=>   4
Going deeper=>   3
Going deeper=>   2
Going deeper=>   1
Going deeper=>   0
[slitt@mydesk pascal]$ time ./tail_recursion_hello > /dev/null

real	0m6.121s
user	0m5.595s
sys	0m0.490s
[slitt@mydesk pascal]$

In the preceding tests, note the following:

Of course, substituting recursion for iteration (e.g. a while statement) is worthless unless the recursive performance is somewhat comparable to the performance of the iterative solution. To test this, I made a tiny iterative program, with a while statement, that counted down over the exact same interval and output the exact same information. I timed them both, redirecting their output to /dev/null so as not to hide performance differences in the immense time consumed with output. The iterative solution was about 5 or 6% faster, which means in all but the most performance critical situations, the tail recursive solution can be substituted for the iterative solution. But wait, there's more...

On the recursive solution, I changed {$optimization tailrec} to {$optimization on}, and added {$optimization on} to the iterative solution. This yielded about a 10% performance increase for the recursive solution, and about a 5% increase for the iterative. The recursive solution subjectively seemed a hair faster than the iterative, although I don't have facilities to measure such small differences objectively.

I don't think Pascal was originally made thinking about tail recursion optimization or functional program, which requires tail recursion optimization. But by adding tail recursion optimization to Free Pascal, the Free Pascal team made functional programming at least possible in Pascal.

Records in Pascal

Pascal records are pretty much the same thing as C structs.

A record is used to keep several types of data in one container. For instance, you might want to keep an employee's first and last names, job title and salary in one container. You'd use a record, as shown:

program test;
var jim : record
    name: string;
    job: string;
    salary: longint;
    end;

begin
jim.name := 'Jim Shucks';
jim.job := 'Project Manager';
jim.salary := 72000;
writeln('Jim''s employee info follows:');
writeln(jim.name, ' is a ', jim.job);
writeln('who makes ', jim.salary, ' per year.');
end.    

That's it. You define the record as a var, populate or modify it in the code using the dot operator, and then access it using the dot operator. The preceding code outputs the following:

[slitt@mydesk pascal]$ ./test
Jim's employee info follows:
Jim Shucks is a Project Manager
who makes 72000 per year.
[slitt@mydesk pascal]$

Records are rarely used the preceding way. Instead, a specific record type is defined, then a variable is set to that type. The following is a modification of the preceding, but done with a type, as follows:

program test;
type TEmployee = record
    name: string;
    job: string;
    salary: longint;
    end;

var jim: TEmployee;

begin
jim.name := 'Jim Shucks';
jim.job := 'Project Manager';
jim.salary := 72000;
writeln('Jim''s employee info follows:');
writeln(jim.name, ' is a ', jim.job);
writeln('who makes ', jim.salary, ' per year');
end.    

The

Classes and OOP

Pascal Pointers, Addresses, Referencing and Dereferencing

ScreenPtr := Ptr($B800, 0);

Callback Functions


[ Training | Troubleshooters.Com | Email Steve Litt ]