Developed, 1968-70.
Revised, 1972.
UCSD produces a portable Pascal system with a pseudo-code (P-code) interpreter, 1973 (cf. Java's bytecode and virtual machine).
International standard, 1982.
Suitable for teaching programming.
Implementation should be reliable and efficient, at compile-time and run-time, on available computers.
Although Pascal was designed as a teaching language, it has been used successfully as a production language, due both to its strong typing and to its support for both commercial and scientific programming through its rich collection of data structuring constructs.
Algol-like syntax (begin/end, :=, nested structures).
Reserved words.
Recursion.
Syntax for declaring new types.
Parts of a program must be written in a specific order to facilitate a one-pass compiler:
Constant declarations
Type declarations
Variable declarations
Procedure and function declarations (including forward declarations for procedures which must be used before they are defined)
Body of program (in begin/end pair)
These ordering requirements were relaxed in Extended Pascal (1990).
Includes support for labels and goto, including nonlocal transfers.
Includes simple I/O statements.
Pascal adds character (char) and a secure (typed) pointer type to Algol's real, integer, and Boolean types.
A new type can be declared by listing (enumerating) all its possible values.
E.g.,
type
DayOfWeek = (Sun, Mon, Tue, Wed, Thu, Fri, Sat);
secure
range
operators -- assignment, relational, successor (succ), predecessor (pred) and ordinal value (ord).
high-level and application-oriented
efficient
Pascal permits the programmer to define subranges of any discrete (scalar) type.
E.g.,
type WeekDay = Mon .. Fri; var dayOfMonth: 1 .. 31;
secure -- catches programming errors early
efficient -- range checking is done at compile time rather than run time
Pascal introduced support for small finite sets.
E.g.,
var S, T, U: set of 1 .. 10;
:
S := [1, 2, 3, 5, 7];
T := [1 .. 6];
U := S + T; { set union }
if 6 in S * T { set intersection }
then . . .
high-level -- allows writing a program in a natural notation
efficient -- stored as a bit string; bitwise operators
secure -- avoids error-prone low-level bitwise operations (cf. C's |, &, ~, <<, >>)
Pascal's arrays are both more general and more restrictive than Algol's.
E.g.,
var hoursWorked : array [Mon .. Fri] of 0 .. 24;
user-specified lower and upper bounds
indexed by any finite discrete type
any base type
any number of dimensions
E.g.,
var M : array [Mon .. Fri] of array [char] of real;
:
for day := Mon to Fri do
M[ day ][ 'e' ] := 0.0001;
syntactic sugar -- equivalent notation:
var M : array [Mon .. Fri, char] of real; : M[ day, 'e' ] := 0.0001;
static
dimensions are part of the array type
all types must be determinable at compile time
strong typing requires that the dimensions of an actual array parameter agree with the dimensions of the corresponding formal array parameter
a separate procedure is needed for every array size we want to pass
conformant array schema developed for Standard Pascal
Pascal provides a heterogeneous data constructor (as in COBOL, Algol-W), with the "dot" operator as a field selector.
E.g.,
type Date =
record
mon : month;
day : 1 .. 31;
year : 1900 .. 2100;
end;
:
var today : Date;
:
today.mon := Jan;
A record defines a scope: field names are only visible with the dot selector, unless a with
statement is used to "open up" the record.
E.g.,
with today do
begin
mon := Oct;
day := 18;
year := 2001;
end;
Variant records allow different fields to be used for subtypes (variants) of a basic heterogeneous type.
A tag field indicates which variant is currently in use.
E.g.,
type Employee = record
name : NameType;
position : (executive, manager, sales, clerical);
birthDate : Date;
hireDate : Date;
case payType : ( salary, commission, wage ) of
salary: ( perMonth : real ); { paid monthly }
commission: (
base : real;
rate : real );
wage: (
regularHours: 0 .. 40; { paid weekly }
overtimeHours: 0 .. 20; { time-and-a-half }
perHour: real );
end {Employee}
economical -- the compiler allocates enough space for the largest variant, not for the union of the variants
secure -- e.g, one cannot access the perHour field for a sales associate on commission
insecure -- type-checking loophole because Pascal does not require that the fields of a variant
be initialized when the value of the tag field is changed.
E.g.,
type Value = record
case numType : ( float, byte ) of
float: ( val : real );
byte: ( parts : packed array [1 .. 4] of char );
end
var
x : Value;
i : integer
:
x.numType := float;
x.val := 3.14159;
x.numType := byte;
for i := 1 to 4 do
writeln( x.parts[ i ]);
Instead of using PL/I's primitive pointer type, Wirth introduced typed pointers in Pascal.
E.g.,
var
p : ^real;
x : real;
c : char;
begin
new( p );
p^ := 3.14159;
x := p^; { OK }
c := p^; { Illegal }
end.
A compiler often has to determine if two types are equivalent. For example,
An expression can be assigned to a variable if the expression and variable have "identical type," according to the Revised Pascal Report.
The types of actual parameters must agree with the types of the corresponding formal parameters.
There are two general ways (and many variants of these approaches) to determine if two types are equivalent:
E.g.,
type Example1 = record
x : real;
y : integer;
end;
type Example2 = record
y : integer;
x : real;
end;
type Example3 = record
a : real;
b : integer;
end;
The first and second examples are the same if we ignore the order of the fields; the first and third are the same if we ignore the names of the fields. The variant of structural equivalence which requires field names to be the same is called structural equivalence under naming.
Two basic problems with structural equivalence:
It is ill-defined, as illustrated by the examples above.
It is difficult to implement, as it requires a recursive function to compare the data structures of the two types.
Furthermore, it is not clear that two types which are structurally equivalent should be considered equivalent. Consider these two types:
type
Person = record
id : integer;
age : integer;
weight : real
end;
Auto = record
id : integer;
age : integer;
weight : real
end;
var
mother : Person;
car : Auto;
begin
:
mother := car;
This example suggests that name equivalence is more secure, on the assumption that if a programmer declares two types with different names, they probably represent different things which would not be considered equivalent in the "real world".
There are problems with name equivalence too:
Anonymous types
E.g.,
var
x : record id : integer; weight : real end;
y : record id : integer; weight : real end;
Should a language designer specify that these two variables are of the same type or of different types?
Subrange types
E.g.,
const
max_age = 150;
type
age_type = 0 .. max_age;
age_data = array [ age_type ] of integer;
new_int_type = integer;
var
age : age_type;
m : integer;
n : new_int_type;
ages1 : age_data;
ages2 : array [ age_type ] of integer;
ages3 : array [ 0 .. max_age ] of integer;
ages4 : array [ 0 .. 150 ] of integer;
Should the assignment m := n be legal? What about n := a? If it is specified that anonymous types of the same structure are equivalent, should the types ages1, ages2, ages3 and ages4 be equivalent?
Pascal uses a variant of name equivalence which might be called declaration equivalence, with some exceptions:
A type declared as being '=' another type is equivalent to that type (i.e, m and n are of the same type).
A subrange is compatible with its host-type and with other subranges of the same host-type (i.e., age is compatible with m and n). It is an error to assign an out-of-range value to a variable of a subrange type.
Integers are assignment compatible with reals: there is an implicit widening conversion.
Actual parameters passed by value need only be assignment compatible with their corresponding formal parameters. Variable (var) parameters (passed by reference) must be of the same type.
An anonymous type is a different type from all other types. This implies that a variable of an anonymous type cannot be passed as a variable parameter to any procedure or function.
The ability to define constants improves readability and maintainability.
E.g.,
const
DaysIn Week = 7;
WondersOfTheWorld = 7;
Without these constant declarations, there might be code such as the following scattered throughout the program:
for i := 1 to 7 do
If someone were to declare an eighth wonder of the world, the program maintainer would have to inspect each for loop using 7 as a bound to see if it referred to days of the week or wonders of the world. This maintenance problem would have been avoided if the loop had used a named constant instead:
for i := 1 to WondersOfTheWorld do
The requirement in most Pascal dialects that the elements of a program be written in a strict order to facilitate one-pass compilers -- every name must be bound before it is used, where "before" is defined in terms of the textual order of declarations -- and Pascal's lack of support for separate compilation create two problems:
Structured programming encourages top-down decomposition of a problem, but Pascal requires the program to be in bottom-up order.
Programmers are prevented from grouping related declarations together, either within a single file or in separate files.
A begin/end pair in Pascal defines a compound statement. Pascal does not support blocks. New declarations can only be made in the declaration section of the main program or in the declaration sections of procedures and functions, so storage can only be shared between disjoint procedures.
procedure name ( formals );
declarations
begin
statements
end;
Pascal has more control constructs than Algol-60, but they are simpler and more structured.
Pascal's for loop is restricted to definite iteration -- it steps only by increments of 1, upward or downward.
for name := expression to expression do
statement
or
for name := expression downto expression do
statement
The loop bounds are computed only on loop entry, not on every iteration. The loop control variable may not be modified in the body of the loop.
Pascal provides two constructs for indefinite iteration: a leading-decision iterator and a trailing-decision iterator.
while condition do statement repeat statements until condition
Pascal introduced C.A.R. Hoare's labeled case-statement.
E.g.,
case myValue of
1 : writeln( "Move forward");
2, 6 : begin myValue := myValue - 1; writeln("Go back 1 square") end;
3 .. 5 : writeln("Move forward ", myValue );
end
This construct is:
Pascal supports only pass by value and pass by reference.
Originally, Pascal provided another in-mode parameter passing method -- pass as constant -- which allowed the compiler to pass a parameter as either a value (e.g., for a simple variable) or an address (e.g., for an array), but disallowed assignment to the parameter in the body of the procedure or function.
The restriction to only pass by value and pass by reference causes programmers to pass large data structures such as arrays and large records as variable (var) parameters, even if they are actually being passed as in-mode parameters only (i.e., they will not be altered by the procedure). This undermines security and readability by confusing two orthogonal issues:
Pascal allows procedures and functions to be passed as parameters to other procedures and functions. This allows procedures to be more general; for example, a compare function could be passed to a sort procedure to allow it to sort an array according to a variety of sort orders.
According to the Revised Report, the syntax for passing a function which accepted a real parameter and returned a real result was:
function name (function f : real; x : real) : real;
However, this created a type loophole, since the parameters of f are not specified. The ISO Pascal Standard requires:
function name (function f( y : real) : real; x : real) : real;
Note the correction relative to the textbook, pp. 302-302.
As in Algol, the value to be returned by a function is specified by an assignment to the name of the function within the body of the function. This syntax causes programmers to think of the name of the function as a local variable, which it is not; it is illegal to use the name of the function on the right-hand side of an assignment statement, for example. C's function return syntax -- return expr -- is preferable.
Although arrays, procedures, and functions may be passed to a procedure or function as parameters, a Pascal function can only return values of type real, ordinal types (integer, char, boolean or subranges of these types), and pointers. Thus, arrays, procedures, and functions are not first-class citizens in Pascal.
Copyright © 2001 Jonathan Mohr