Data Objects

Alice Pascal recognizes many kinds of data. These include simple variables of various types, as well as arrays, pointers, records, files, sets, and scalar types. This chapter describes the nature of these objects and indicates simple ways in which they may be declared.

Identifiers

Data objects in Pascal are referenced using names or identifiers. Identifiers may be formed from the upper case letters 'A'-'Z', the lower case letters 'a'-'z', the digits '0'-'9', and the underscore _. Identifiers may not begin with a digit or underscore. Alice Pascal does not put any limits on how long an identifier can be; however, the maximum length of an input line is 255 characters, so the length of an identifier is restricted by the amount of space available in declarations and statements where the identifier is used.

Alice Pascal does not pay attention to the case of letters in identifiers. Thus the identifiers SUM, sum, and Sum all refer to the same thing. This is not true of some other versions of Pascal.

Keywords

Alice Pascal recognizes a number of keywords that may not be used as identifiers. If you try to use them as identifiers, Alice will either give you an error message or lay out the template associated with the keyword (which should tell you something has gone wrong). Recognized keywords are listed below. (Those marked with a * are Turbo Pascal keywords.)

and       array     begin     case
const     div       do        downto
else      end       file      for
function  goto      if        in
label     mod       nil       not
of        or        packed    procedure
program   record    repeat    set
shl*      shr*      string*   then
to        type      until     var
while     with      xor*

In the rest of this manual, keywords will usually be written in a special type face to remind users that they are reserved.

Declarations

Every identifier in a Pascal program must be declared. Declarations are made in special declaration sections that appear at the beginning of the function, procedure, or program mainline where the identifier appears. Declarations made inside a subprogram (a function or a procedure) are local to the subprogram in which they appear and to other subprograms declared inside the first subprogram. Declarations that are not inside any subprogram apply to the entire program.

There are four major declaration sections.

The Label Section

This section begins with the keyword label. It declares numbers that will be used as statement labels.

The Constant Section

This section begins with the keyword const. It declares identifiers that will represent constant values.

The Type Section

This section begins with the keyword type. It declares identifiers that will stand for specific data types.

The Variable Section

This section begins with the keyword var. It declares identifiers that will be used as variables.

Any or all of these declaration sections may be omitted at the beginning of a function, procedure, or program mainline. Alice lets you give these sections in any order, and lets you have more than one of each kind of section (e.g. a type section followed by a var section followed by another type section). However, other versions of Pascal usually demand that you only have one of each kind of section and that the sections appear in the order listed above.

Programs and subprograms may also contain subprogram declarations. function declarations describe subprograms that return a value. The declaration gives the type of value returned, as well as the type of each argument to the function. Arguments are explained in Chapter 6.

The other type of subprogram declaration is a procedure declaration. A procedure is exactly like a function except that it does not return a value.

In other versions of Pascal, subprogram declarations come after the other declaration sections, but procedure and function declarations may be intermixed. For example, you could have a procedure declaration, then a function declaration, then another procedure, and so on. Again, Alice lets you put declarations in any order at all.

In the rest of this chapter, we will describe the first four declaration sections. Subprogram declarations will be described in Chapter 6.

Label Declarations

Statement labels are rarely used in Pascal. Only the goto statement references such labels, and Pascal has a number of program structures like while loops and for statements that make goto statements unnecessary.

If a program is going to use statement labels, the labels must be declared in the label declaration section. Most versions of Pascal use unsigned integers for statement labels. These must be integer constants (just sequences of digits) -- they cannot be variables or constant expressions.

Turbo Pascal lets you use identifier names as labels, in addition to numbers. While Aliceinternally supports alphanumeric identifers, and the APIN program allows them as well, Alice won't allow you to type in non-numeric labels in order to encourage portability.

The label declaration section consists of the keyword label followed by a list of the integers that will be used as labels. Integers in the list are separated by commas. The end of the list is marked with a semicolon. Therefore, you might have

label
    5,68,23;

Once a label has been declared, it may be used to label a statement, as in

68: writeln('The label is 68');

A single statement may have more than one label. Each such label is followed by a colon.

The Constant Declaration Section

A constant declaration section assigns names to certain constants. These names may then be used in exactly the same way as the constants that the names represent. Constants declared in the constant section are sometimes called manifest constants.

A constant declaration section begins with the keyword const. Following this are lines of the form

identifier = constant;

For example, here is a typical constant declaration section.

const
    VECSIZE = 100;
    FILENAME = 'myfile';
    E = 2.71828;

In the declarations above, we have followed the widely-used convention of writing manifest constants in upper case so they stand out in the source code.

Any valid Pascal constant may be associated with a name in a const section. Named constants are frequently used for constants that may have to be changed if modifications are made in the program. For example, the VECSIZE constant shown above may give the size of a particular vector. The source code can then use VECSIZE to refer to the vector's size instead of stating the size as an explicit constant. If it later becomes necessary to change the vector's size, you just have to change the definition of VECSIZE and run the program again.

Typed Constants

Alice supports the use of typed constants, which are equivalent to variables with a constant value. They can be used anywhere a variable is used; they should not be assigned to, however.

A typed constant is declared as follows:

const
    identifier : type = value;

For example,

const
    HoursPerDay : integer = 24;
    SalesTaxRate : real = 1.07;

In addition, structured typed constants are supported. They are declared the same way as unstructured typed constants, but their type specifier is a structured type and the initializer provides values for all the elements of the structured type.

Array constant initializers are enclosed in parentheses and separated by commas, as in the following examples:

const
    punctuation : array [0..5] of char = ( '!', '@', '%', '&', '<', '> );
    tictacgrid : array[0..2,0..2] of integer = ((0, 1, 2),(3,4,5),(6,7,8));

Record constant initializers are enclosed in parentheses, separated by commas, and consist of a field name, a colon, and a value for that field. The initializers need not all be present, and do not need to be in any particular order. For example

type
    Emp record = record
        empname : string[15];
        empwage : real;
        empnum : integer;
        end;
const
    Best emp : Emp record = (empnum : 15, empname = 'J.Q. Public');

Set constant initializers consist of a list of set elements enclosed in square brackets and separated by commas. In addition, the form val..val may be used to denote a range of values. For example

const
    lowprimes : set of integer = [1, 3, 5, 7, 11, 13, 17, 19, 23];

Note that only a limited number of initializers is supported in Alice.

The Type Declaration Section

A type declaration section gives names to certain data types. These names may then be used as data types in variable and subprogram declarations.

A type declaration section begins with the keyword type. After this comes lines of the form

identifier = type;

For example, we might have

type
    PRICE = real;
    vec = array[1..10] of integer;

We will explain the various types of Pascal in the description of the Variable Declaration Section.

Pascal is much more rigorous than other languages when it comes to type-checking. For example, a variable declared with the type vec (declared above) would not be compatible with a variable declared with the unnamed type

array[1..10] of integer

even though the two look the same to the human eye. Pascal's rules of data compatibility are given later in this chapter.

The Variable Declaration Section

A variable declaration section declares one or more variables that will be used in the program. Every declaration gives the variable's name and its type.

A variable declaration section begins with the keyword var. After this come lines of the form

identifier : type;

or

identifier, identifier, identifier, ... : type;

For example, here is a typical var section.

var
    x,y,z : real;
    i,j : integer;
    c : char;
    switcher : Boolean;

In the paragraphs to come, we will describe the various types of variables that can be declared in a var section.

The integer Type

To declare a variable of the integer type, use

var
    NAME : integer;

where NAME is the name of the variable. Integer variables may (naturally) be assigned integer values.

Chapter 4 describes the various operations that may be performed on integers. These include standard addition, subtraction, multiplication, and integer division, plus a division remainder operation.

Pascal does not let you assign real values directly to integer variables. Instead, the real value must be converted with either the round or trunc function. The round function converts a real value to integer by rounding up or down to the nearest whole number. For example, if i is an integer variable

i := round(3.7);

will round 3.7 to 4 and assign this to i.

The trunc function converts a real value to integer by truncating the fractional part. You can think of this truncation as ``erasing'' everything that comes after the decimal point. For example,

i := trunc(3.7);

will truncate 3.7 to 3.

i := trunc(-3.7);

will truncate -3.7 to -3.

The real Type

To declare a variable of the real type, use

var
    NAME : real;

where NAME is the name of the variable. Real variables may (naturally) be assigned real values.

Chapter 4 contains descriptions of the various operations that may be performed on real values. These include standard addition, subtraction, multiplication, and real division.

If an integer value is assigned to a real variable, the integer will automatically be converted to real first. For example, if x is a real variable

x := 3;

will first convert 3 to 3.0, then assign this to x.

The char Type

To declare a variable of the char type, use

var
    NAME : char;

where NAME is the name of the variable. Variables of this type can hold a single character.

Unlike some other languages, Pascal will not allow char data to be used numerically. To treat characters as integers, you must use the transfer functions ord and chr. If c is a char variable,

ord(c)

is an integer that is equal to the numeric value of the representation of the character in c. Similarly, if i is an integer,

chr(i)

is the character whose representation is equal to i. ord can be used on any character; chr can only be used on integers in the range 0 to 255.

The Byte Type

To declare a variable of Byte, use

var
    NAME : byte;

where NAME is the name of the variable.

Byte variables may contain any value in the range 0..255. They are compatible with integers, except when used as parameters.

The Boolean Type

To declare a Boolean variable, use

var
    NAME : Boolean;

where NAME is the name of the variable.

Boolean variables may only take on the two values true and false. Boolean values most commonly arise as the result of a relational comparison. For example,

A > B

returns a Boolean value that is true if A is greater than B, and false otherwise. As noted previously, true and false are predefined identifiers.

Enumerated Types

An enumerated type is defined by listing the values that belong to the type. Each value is represented by a normal identifier. For example,

type
    days = (sun,mon,tue,wed,thu,fri,sat);

might appear in the type declaration section to define an enumerated type. The values in the list may be assigned to variables with the days type. For example, we might define

var
    today : days;

and then make an assignment like

today := tue;

The most practical way to declare a variable of a given enumerated type is to name the type in the type declaration section, as in

type
    ENAME = (identifier,identifier,...);

where ENAME is a valid Pascal identifier. The enumerated type will then have the name ENAME. You can then declare a variable with that type, as in

var
    NAME : ENAME;

You can also use unnamed enumerated types right in a variable declaration, as in

var
   x,y,z : (red,green,blue);

but this tends to be less useful.

The identifiers given as values for the enumerated type must be unique -- they cannot be used as names for other data objects or as values in other enumerated types.

The standard comparison operators (e.g., < and >) can be used with values of the same enumerated type. When the enumerated type is defined, the order in which values are listed indicates which values are ``smaller'' and which are ``larger''. For example, in our definition of the type days, we would have sun less than mon, mon less than tue, and so on.

The functions pred and succ are also related to the order of enumerated types. If etv is an enumerated type value, then

pred(etv)

is the enumerated type value that precedes etv in the list, and

succ(etv)

is the enumerated type value that follows etv in the list. Therefore, pred(tue) is mon and succ(tue) is wed.

The ord function will convert an enumerated type value to an integer that represents the value's position in the list. The first item in an enumerated type list has a value of 0, the next has a value of 1, and so on. Therefore, ord(tue) is 2.

Note that the Boolean type is actually predefined as an enumerated type, with

type
    Boolean = (false,true);

This means that false is less than true.

Unlike most versions of Pascal, the Alice version of the write subprogram can write out all enumerated values by name. See Chapter 7.

Scalar Types

Enumerated types, the char type, and the integer type are commonly called scalar types. The functions pred and succ can be used with integer and character data as well as with scalar types created by the user.

The real type is sometimes called a scalar type by other Pascal manuals. However, it is illegal to use pred and succ on real values, and there are other ways in which reals differ from scalar types. For this reason, we will not group real values with scalar types in this manual.

Note that books which include real as one of the scalar types sometimes use the term ordinal type to refer to the things we call scalar types.

Subrange Types

A subrange is a subset of an existing scalar type. Subranges are represented by the construct

lowest..highest

where lowest and highest are values of the same scalar type, and

lowest <= highest

For example, here are some valid subranges.

1..100
'a'..'z'
mon..fri

Subranges can be set up in the type declaration section, as in

type
    lowercase = 'a'..'z';

or they may be set up in the var declaration section, as in

var
    index : 1..100;

If a variable has a subrange type, it is an error to assign that variable a value which is outside the subrange, even if the value has the same scalar type. For example, if index is declared as above,

index := 101;

is an error. Variables with a subrange type can only take on values inside the range.

Apart from the limited range, variables with a subrange type can generally be used in any way that is valid for variables of the same scalar type. Because of this, the rest of this manual will use the term ``scalar type'' to refer to subranges of scalar types as well as full scalar types.

Note, however, that any subrange type which is a subset of the range 0..255 will cause variables of that type to occupy a single byte of storage.

The Array Type

An array is made up of a fixed number of elements that all have the same type. For example, we can have an array of integers, an array of characters, etc.

The elements of an array are referenced using subscripts. At the time that the array is declared, you must also declare what type of values will be used as subscripts. To declare an array variable, use

var
    NAME : array[type1] of type2;

type1 gives the type of the subscripts that will be used for the array. type2 gives the type of the elements that will be contained in the array. Below we give some examples of array declarations.

var
    intarr : array [1..100] of integer;
    weekarr : array [mon..fri] of integer;
    carr : array [char] of array [1..10] of real;

These declarations indicate the versatility of Pascal arrays. The type of the elements in the array (type2) can be any valid type. The subscripts can have any scalar type with one exception. The declared type of the subscripts can be a subrange of the integers, but it cannot be the type integer itself, since this would imply an array of virtually infinite size.

When an array variable is declared, Pascal will create the array with one element for every value in the scalar type of the subscripts. Therefore,

var
    intarr : array [1..10] of integer;

means an array of ten integers.

var
    table : array [char] of char;

means an array with one character element for every possible char value, i.e., an element for every possible character. Of course, the most common type for subscripts will be a subrange of integers beginning at 0 or 1.

Once an array has been declared in this way, elements of the array may be referenced by giving a subscript value of the correct scalar type inside square brackets following the name of the array. For example,

intarr[5]

refers to the element of intarr that has the subscript 5.

table['A']

refers to the element of table that has the subscript 'A'.

Pascal also supports multi-dimensional arrays. One way of declaring a multi-dimensional array is to declare an array of arrays, as in

var
    multi : array [1..10] of array [1..20] of real;

The construct

multi[N]

will refer to one of the sub-arrays, so

multi[N][M]

will refer to one of the real elements of the sub-array.

Alice also lets you talk about multi-dimensional arrays using the form

var
    multi : array [1..10,1..20] of real;

and

multi[N,M]

These will be converted to

var
    multi : array [1..10] of array [1..20] of real;

and

multi[N][M]

on your display screen, as you enter them.

Packed Arrays

A packed array is declared in the same way as a normal array, except that the keyword packed is placed before the keyword array, as in

var
    arr1 : array [1..20] of char;
    arr2 : packed array of [1..20] of char;

Traditionally, the packed keyword asks Pascal to compress the elements of the array into the smallest space possible. Since Alice already does this, packed has no effect on how arrays are stored.

String Types

String constants that contain more than one character are considered to have the type

packed array [1..N] of char;

where N is the number of characters in the string. For this reason, variables that are to contain strings are usually declared to be packed arrays as well. We use the term string types to refer to all packed character array types whose starting index is 1.

At times, a string type variable may be longer than the string it holds. For example, suppose we have

var
    str : packed array [1..10] of char;

and then assign the value "abc" to str. It is fairly clear that

str[1] = 'a';
str[2] = 'b';
str[3] = 'c';

but what about the rest of the str array?

In situations like this, the special character StrEnd is used to mark the end of the string that is stored in str. StrEnd is put into str[4] and the other elements in str remain undefined.

The StrEnd character is actually the ASCII null character, so

ord(StrEnd) = 0

The packed array of char method for representing strings is common to all versions of Pascal. Alice and Turbo Pascal also support a special type named String. A declaration of the form

var
    name : String[N];

is equivalent to

var
    name : packed array[1..N] of char;

Note that the + operator can be used to concatenate strings together; its use is detailed in the second on the binary + operator.

Record Types

The Pascal record type is similar to a COBOL record or a C structure. A record is a structure that contains one or more sub-elements called fields. These fields can contain data of practically any type. For example, here is a typical record type.

type
    personnel = record
                    name : packed array [1..40] of char;
                    empno, age : integer;
                    income : real
                end;

The name of this type is personnel. Each personnel record contains four fields: a name, an employee number (called empno), an age, and an income. A name and type are given for each field in the record. The end of the list of record fields is indicated by the keyword end. Once we have defined this type in the type declaration section, we could use this type in variable declarations.

var
    Bob, Rick, Nancy : personnel;
    department : array [1..10] of personnel;

Record variables can also be declared directly in the var section.

var
    z1, z2, z3 : record
                     re, im : real
                 end;

This sort of record might represent a complex number.

If a variable has a record type, the fields of the record can be referenced by giving the name of the variable, followed by a dot, followed by the field name. For example, z1.re refers to the re field in the record z1 declared above.

A record type can contain a variant part as well as a fixed part. This means that part of the record contains a set of fields that can change from record to record. The variant part must always appear at the end of the record; the first part of the record contains the fields that are found in every record of the type.

Below we give a typical record structure that contains a variant part.

type
    person = record
                 name : packed array [1..40] of char;
                 case age : integer of
                   1..16 : (grades:integer);
                   17..24 : (inschool:Boolean);
             end;

The name field is the fixed part of the record. The variant part is introduced by the keyword case. After this comes a declaration of the tag field. The tag field dictates what form the variant part will take.

In the above example, the tag field is age. If the value of this field is in the range 1..16, the variant part takes the form given in parentheses after the 1..16; the variant part consists of a single integer field called grades. If the value of age is in the range 17..24, the variant part consists of a single Boolean field called inschool.

It is not necessary to give a range of values for the possible forms of the variant part; for example, one might have the following record form.

type
    days = (sun,mon,tue,wed,thu,fri,sat);
    work = record
               case d : day of
                 mon..fri : (hours : integer);
                 sat : (holiday : Boolean,
                        sathours : integer);
                 sun : ()
           end;

This example demonstrates several points. A record does not have to have a fixed part. It can begin directly with the keyword case and the variant part. The tag field can have any scalar type. Some forms of the variant part may be empty; in the above case, if d has the value sun, the variant part contains no fields.

The field names of a record must all be different. In particular, two different forms of the variant part cannot contain fields with the same name.

Now that we have seen examples of record types, we can give a complete description of a record declaration.

record
    field-name : type;
    field-name : type;
              ...
    case tag-field : type of
      tag-value : (variant-field : type;
                   variant-field : type);
                          ...
      tag-value,tag-value : (variant-field : type);
                                         ...
      tag-value..tag-value : (variant-field : type);
                                         ...
      {end variant}
      end;

Either the fixed part before case or the variant part after case may be omitted (but not both). It doesn't matter what order you use to list the cases in the variant part of the record. The tag-field name placeholder may be left blank but the type must be filled in before running the program. Note that Alice puts in a comment to mark the end of the variant part of the record.

Records may be designated as packed if desired, but this has no effect.

Pointer Types

A pointer variable contains a pointer to another data object. At the time that the pointer type is declared, you must also declare the type of object that the pointer will point at. The general form of a pointer type declaration is

type
    NAME = ^ datatype ;

where NAME is the name of the pointer type, datatype is the type of data that the pointer points to, and ^ is the caret (uparrow) character. For example,

type
    intp = ^ integer;

declares a pointer to an integer. You can also declare pointers in the var section without declaring the pointer type in the type section.

var
    charp : ^ char;

declares charp to be a variable that contains a pointer to a character.

Pascal lets you define a pointer to a particular type of value before the actual type is defined. This allows you to make interlinked records, as in

type
    man = record
              name : packed array[1..40] of char;
              wife : ^ woman
              end;
    woman = record
              name : packed array[1..40] of char;
              husband : ^ man
              end;

If ptr was a pointer to the record type man, we would refer to elements of the record as

ptr^ . name
ptr^ . wife

It is an error to declare a circular list of pointer types, as in

type
    x = ^ y;
    y = ^ x;

In standard Pascal, pointers can only be used when memory is allocated dynamically; see the description of the new function in Chapter 8. In addition, Alice has two routines (MakePointer and RawPointer) which can reate pointers to objects that have not been allocated dynamically.

The Universal Pointer Type

In addition to pointers to specific types, there is a built-in Pointer type that can hold a pointer to any kind of data. For example, if we define

var
    uptr : Pointer;
    iptr : ^ integer;
    cptr : ^ char;

we may make assignments like

uptr := iptr;
uptr := cptr;

Pointer types may also be assigned string values.

One example of a Pointer type is the keyword nil. This is used as the null pointer, i.e. a pointer that doesn't point to anything. The value nil can be assigned to a pointer variable of any type.

File Types

File types are similar to arrays -- a file is viewed as a collection of elements that all have the same type. The difference is that arrays have a fixed length, while files do not. Also, files can only be accessed sequentially, while arrays allow random access to elements.

To declare a file type, use

type
    identifier = file of type;

The identifier is the name that will be used for this particular file type; the type is the type of elements that will be contained in data objects of this type. For example,

type
    intfile = file of integer;

defines a file type intfile that contains normal integers.

There is a predefined file type named text that can be used for files containing normal text. text is much like file of char, but it has a few extra properties. In particular, you can apply the eoln, readln, writeln, and page subprograms to text files. These subprograms are described in Chapters 7 and 8.

Naturally, there is a close association between the file type and files stored on peripheral devices. For every file that the program uses, there must be a corresponding file variable having an appropriate file type. The text type is one of the most common file types. For data processing, files of record data are often common. However, you can set up files of any type of data object.

The I/O functions of the Pascal library are set up to read, write, and perform other operations on file type data objects. These are explained in Chapter 7.

Every declaration of a file variable automatically creates an associated buffer variable for the file. The name of this variable is the name of the file variable followed by a caret (^). For example, if f has been declared as a file variable, it will have an associated buffer variable named f^. The buffer variable has the same type as the elements contained by the file. Chapter 7 gives a much fuller description of how Pascal I/O works.

Set Types

A value of the set type can be thought of as a subset of all the values that belong to a given scalar type. This scalar type is called the base type of the set. A type declaration for a set type has the form

type
    NAME = set of base-type;

For example, we might have

type
    intset = set of 1..30;

Variables of this type would be sets whose elements were integers in the range 1 to 30.

Set values are represented by values of the base type enclosed in square brackets. For example, suppose s is a variable of the intset type declared above. Then

s := [10,20,30];

makes s into a set that contains the integers 10, 20, and 30.

There are a few restrictions on the base type of a set. The base type must be a scalar type. If the base type is a subrange of the integers, the subrange must fall in the range 0..255. If the base type is an enumerated class, the class can have a maximum of 256 elements.

Pascal allows all the usual set operations: intersection, union, and ``set difference''. You can also determine if one set is equal to another, or a subset of another. Lastly, you can determine if a particular value of the base type is an element of a set.

Set types can be declared as packed if you wish. However, this makes no difference to memory requirements.

Unlike other Pascals, Alice can Write set types.

Generic Type

A special type called the generic type is available for declaring parameters of a function or procedure which may be of any of a number of different types. See the section on Procedure Declarations for more details on generic types.

Compatibility

One of Pascal's most important characteristics is its strict type-checking. In general, Pascal tries to prevent the user from performing operations that mix data of two different types. However, it makes some exceptions when the types are ``compatible enough'' to be mixed. For example, suppose we declare

var
    A : 1..10;
    B : 1..9;

A and B have different types because they are different subranges of the integers. However, it is obvious that the assignment A:=B makes sense since the value of B must be in the allowed range for A.

There are two definitions of compatibility: assignment compatibility (which means that a value of one type may be assigned to a variable of a different type) and general compatibility (which means that two types are compatible enough for expressions like comparison tests).

General Compatibility

Two data types T1 and T2 are compatible if any of the following statements are true.

T1 and T2 are the same type.

T1 is a subrange of T2, or T2 is a subrange of T1, or both are subranges of the same scalar type. Thus the following pairs of declarations would all be compatible under this rule.

T1: 1..10;  T2: 1..12;
T1: 1..10;  T2: 2..8;
T1: 1..10;  T2: 3..12;
T1: 1..10;  T2: 100..1000;

T1 and T2 are set types of compatible base types, and either both are packed or neither are packed. For example,

T1: set of 1..10;   T2: set of 1..12;

would be compatible according to this rule.

T1 and T2 are both string types. For example,

T1: packed array [1..12] of char;
T2: packed array [1..15] of char;

would be compatible under this definition. Note that a packed character array must have a starting index of 1 if it is to be a string type.

One of T1 or T2 is a pointer type and the other is the universal Pointer type.

One of T1 or T2 is a string type and the other is a char type.

If two types are generally compatible, variables of those types may be used together in expressions, e.g. arithmetic operations and comparison tests.

Assignment Compatibility

Assignment compatibility dictates whether or not a value of one type may be assigned to a variable of another type using :=. If VT1 is a variable of type T1 and VT2 is a variable of type T2, the statement

VT1 := VT2;

is valid when any one of the following is true.

T1 and T2 are the same type. This type may not be a file type or a structured type that has a file component.

T1 is the real type and T2 is the integer type. This means that assignment automatically converts integers to reals.

T1 and T2 are generally compatible subrange types and the value of VT2 is in the subrange specified by the type T1. General compatibility was described in the last section. For example, if we define

type
    T1: 1..10;  T2: 3..12;

the operation VT1:=VT2 is valid provided that VT2 is in the range 3..10.

T1 and T2 are generally compatible set types and all the members of VT2 are in the base type of T1. This is similar to point (c).

T1 and T2 are both string types.

T1 is a string type and T2 is the char type.

One of T1 and T2 is a pointer type and the other is the universal Pointer type.

T1 is the Pointer type and T2 is a string type.

The above rules give the only conditions in which VT1:=VT2 is valid. For example, consider

var
    x : ^ char;
    y : ^ char;

With these declarations, it would be valid to say

x^ := y^ ;

since both pointers point to the type char. However, it would not be valid to say

x := y;

because x and y are not assignment compatible. Appearances to the contrary, x and y are not the same type in the eyes of Pascal. To be the same type, you need something like

type
    charp : ^ char;
var
    x : charp;
    y : charp;

Compatibility for Subprogram Arguments

Compatibility is important when passing arguments to a function or procedure. Functions, procedures, and their arguments will be described in more detail in Chapter 6.

If an argument is a var argument, the argument must have the same type in both the caller and the called subprogram. It is not enough for them to be compatible or assignment compatible.

If an argument is a normal ``value'' argument, the caller's argument must be assignment compatible with the called function's argument. Certain built-in subprograms (e.g. get or put) are exceptions to this rule, in that specific arguments may have many different types.

When passing a subprogram as an argument to another subprogram, the argument lists of the argument subprogram must match exactly in both caller and callee. It is not enough for the argument lists to be compatible or assignment compatible. This means that many built-in subprograms may not be passed as arguments to other subprograms, because they do not always take the same type of arguments.