Data Objects

# 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.